Serenity Operating System
1/*
2 * Copyright (c) 2019-2020, Jesse Buhgaiar <jooster669@gmail.com>
3 * Copyright (c) 2022, the SerenityOS developers.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#pragma once
9
10#include <AK/NonnullRefPtr.h>
11#include <AK/Vector.h>
12#include <LibGUI/Model.h>
13
14namespace GUI {
15
16template<typename T, typename Container = Vector<T>, typename ColumnNameListType = void>
17class ItemListModel : public Model {
18public:
19 static constexpr auto IsTwoDimensional = requires(Container data) {
20 requires !IsVoid<ColumnNameListType>;
21 data.at(0).at(0);
22 data.at(0).size();
23 };
24
25 // Substitute 'void' for a dummy u8.
26 using ColumnNamesT = Conditional<IsVoid<ColumnNameListType>, u8, ColumnNameListType>;
27
28 static NonnullRefPtr<ItemListModel> create(Container const& data, ColumnNamesT const& column_names, Optional<size_t> const& row_count = {})
29 requires(IsTwoDimensional)
30 {
31 return adopt_ref(*new ItemListModel<T, Container, ColumnNameListType>(data, column_names, row_count));
32 }
33 static NonnullRefPtr<ItemListModel> create(Container const& data, Optional<size_t> const& row_count = {})
34 requires(!IsTwoDimensional)
35 {
36 return adopt_ref(*new ItemListModel<T, Container>(data, row_count));
37 }
38
39 virtual ~ItemListModel() override = default;
40
41 virtual int row_count(ModelIndex const& index) const override
42 {
43 if (!index.is_valid())
44 return m_provided_row_count.has_value() ? *m_provided_row_count : m_data.size();
45 return 0;
46 }
47
48 virtual int column_count(ModelIndex const& index) const override
49 {
50 // if it's 2D (e.g. Vector<Vector<T>>)
51 if constexpr (IsTwoDimensional) {
52 if (index.is_valid())
53 return m_data.at(index.row()).size();
54 if (m_data.size())
55 return m_data.at(0).size();
56 return 0;
57 }
58
59 // Otherwise, let's just assume it's 1D.
60 return 1;
61 }
62
63 virtual DeprecatedString column_name(int index) const override
64 {
65 if constexpr (IsTwoDimensional)
66 return m_column_names[index];
67 return "Data";
68 }
69
70 virtual Variant data(ModelIndex const& index, ModelRole role) const override
71 {
72 if (role == ModelRole::TextAlignment)
73 return Gfx::TextAlignment::CenterLeft;
74 if (role == ModelRole::Display) {
75 if constexpr (IsTwoDimensional)
76 return m_data.at(index.row()).at(index.column());
77 else
78 return m_data.at(index.row());
79 }
80
81 return {};
82 }
83
84 virtual TriState data_matches(GUI::ModelIndex const& index, GUI::Variant const& term) const override
85 {
86 if (index.data().as_string().contains(term.as_string(), CaseSensitivity::CaseInsensitive))
87 return TriState::True;
88 return TriState::False;
89 }
90
91 virtual bool is_searchable() const override { return true; }
92 virtual Vector<GUI::ModelIndex> matches(StringView searching, unsigned flags, GUI::ModelIndex const&) override
93 {
94 Vector<GUI::ModelIndex> found_indices;
95 if constexpr (IsTwoDimensional) {
96 for (auto it = m_data.begin(); it != m_data.end(); ++it) {
97 for (auto it2d = (*it).begin(); it2d != (*it).end(); ++it2d) {
98 GUI::ModelIndex index = this->index(it.index(), it2d.index());
99 if (!string_matches(data(index, ModelRole::Display).to_deprecated_string(), searching, flags))
100 continue;
101
102 found_indices.append(index);
103 if (flags & FirstMatchOnly)
104 return found_indices;
105 }
106 }
107 } else {
108 for (auto it = m_data.begin(); it != m_data.end(); ++it) {
109 GUI::ModelIndex index = this->index(it.index());
110 if (!string_matches(data(index, ModelRole::Display).to_deprecated_string(), searching, flags))
111 continue;
112
113 found_indices.append(index);
114 if (flags & FirstMatchOnly)
115 return found_indices;
116 }
117 }
118
119 return found_indices;
120 }
121
122protected:
123 explicit ItemListModel(Container const& data, Optional<size_t> row_count = {})
124 requires(!IsTwoDimensional)
125 : m_data(data)
126 , m_provided_row_count(move(row_count))
127 {
128 }
129
130 explicit ItemListModel(Container const& data, ColumnNamesT const& column_names, Optional<size_t> row_count = {})
131 requires(IsTwoDimensional)
132 : m_data(data)
133 , m_column_names(column_names)
134 , m_provided_row_count(move(row_count))
135 {
136 }
137
138 Container const& m_data;
139 ColumnNamesT m_column_names;
140 Optional<size_t> m_provided_row_count;
141};
142
143}