diff options
author | AnotherTest <ali.mpfard@gmail.com> | 2020-07-04 22:36:23 +0430 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-08-01 08:39:26 +0200 |
commit | d04c833002331d868e95a1036426609b219ceb79 (patch) | |
tree | ba20b55c3328283c58b4dc5a58f921df2bb78f18 /Libraries/LibGUI | |
parent | c35493c1564222e0019ff8a69bc01aea9f650692 (diff) | |
download | serenity-d04c833002331d868e95a1036426609b219ceb79.zip |
LibGUI: Add FilteringProxyModel
This model does not support nested indices well, in that it flattens
them all out.
That's a FIXME for the future as it does its job for now.
Diffstat (limited to 'Libraries/LibGUI')
-rw-r--r-- | Libraries/LibGUI/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Libraries/LibGUI/FilteringProxyModel.cpp | 122 | ||||
-rw-r--r-- | Libraries/LibGUI/FilteringProxyModel.h | 71 | ||||
-rw-r--r-- | Libraries/LibGUI/Model.h | 6 |
4 files changed, 198 insertions, 2 deletions
diff --git a/Libraries/LibGUI/CMakeLists.txt b/Libraries/LibGUI/CMakeLists.txt index 88ab51cffe..d7cace8e85 100644 --- a/Libraries/LibGUI/CMakeLists.txt +++ b/Libraries/LibGUI/CMakeLists.txt @@ -26,6 +26,7 @@ set(SOURCES Event.cpp FilePicker.cpp FileSystemModel.cpp + FilteringProxyModel.cpp FontDatabase.cpp Frame.cpp GroupBox.cpp diff --git a/Libraries/LibGUI/FilteringProxyModel.cpp b/Libraries/LibGUI/FilteringProxyModel.cpp new file mode 100644 index 0000000000..7d14b29a13 --- /dev/null +++ b/Libraries/LibGUI/FilteringProxyModel.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <LibGUI/FilteringProxyModel.h> + +namespace GUI { + +ModelIndex FilteringProxyModel::index(int row, int column, const ModelIndex& parent_index) const +{ + int parent_row = parent_index.row(); + if (!parent_index.is_valid()) + parent_row = 0; + + return create_index(parent_row + row, column); +} + +int FilteringProxyModel::row_count(const ModelIndex&) const +{ + return m_matching_indices.size(); +} + +int FilteringProxyModel::column_count(const ModelIndex& index) const +{ + if (!index.is_valid()) + return {}; + + if ((size_t)index.row() > m_matching_indices.size() || index.row() < 0) + return 0; + + return m_model.column_count(m_matching_indices[index.row()]); +} + +Variant FilteringProxyModel::data(const ModelIndex& index, Role role) const +{ + if (!index.is_valid()) + return {}; + + if ((size_t)index.row() > m_matching_indices.size() || index.row() < 0) + return 0; + + return m_model.data(m_matching_indices[index.row()], role); +} + +void FilteringProxyModel::update() +{ + m_model.update(); + filter(); + did_update(); +} + +void FilteringProxyModel::filter() +{ + m_matching_indices.clear(); + + Function<void(ModelIndex&)> add_matching = [&](ModelIndex& parent_index) { + for (auto i = 0; i < m_model.row_count(parent_index); ++i) { + auto index = m_model.index(i, 0, parent_index); + if (!index.is_valid()) + continue; + + auto filter_matches = m_model.data_matches(index, m_filter_term); + bool matches = filter_matches == TriState::True; + if (filter_matches == TriState::Unknown) { + auto data = m_model.data(index, Role::Display); + if (data.is_string() && data.as_string().contains(m_filter_term)) + matches = true; + } + if (matches) + m_matching_indices.append(index); + + add_matching(index); + } + }; + + ModelIndex parent_index; + add_matching(parent_index); +} + +void FilteringProxyModel::set_filter_term(const StringView& term) +{ + if (m_filter_term == term) + return; + m_filter_term = term; + update(); +} + +ModelIndex FilteringProxyModel::map(const ModelIndex& index) const +{ + if (!index.is_valid()) + return {}; + + auto row = index.row(); + if (m_matching_indices.size() > (size_t)row) + return m_matching_indices[row]; + + return {}; +} + +} diff --git a/Libraries/LibGUI/FilteringProxyModel.h b/Libraries/LibGUI/FilteringProxyModel.h new file mode 100644 index 0000000000..f5296c3e6b --- /dev/null +++ b/Libraries/LibGUI/FilteringProxyModel.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/NonnullRefPtr.h> +#include <AK/Optional.h> +#include <AK/String.h> +#include <LibGUI/Model.h> +#include <LibGUI/TextBox.h> + +namespace GUI { + +class FilteringProxyModel final : public Model { +public: + static NonnullRefPtr<FilteringProxyModel> construct(Model& model) + { + return adopt(*new FilteringProxyModel(model)); + } + + virtual ~FilteringProxyModel() override {}; + + virtual int row_count(const ModelIndex& = ModelIndex()) const override; + virtual int column_count(const ModelIndex& = ModelIndex()) const override; + virtual Variant data(const ModelIndex&, Role = Role::Display) const override; + virtual void update() override; + virtual ModelIndex index(int row, int column = 0, const ModelIndex& parent = ModelIndex()) const override; + + void set_filter_term(const StringView& term); + + ModelIndex map(const ModelIndex&) const; + +private: + void filter(); + explicit FilteringProxyModel(Model& model) + : m_model(model) + { + } + + Model& m_model; + + // Maps row to actual model index. + Vector<ModelIndex> m_matching_indices; + + String m_filter_term; +}; + +} diff --git a/Libraries/LibGUI/Model.h b/Libraries/LibGUI/Model.h index 29933023ba..d12a0f2afc 100644 --- a/Libraries/LibGUI/Model.h +++ b/Libraries/LibGUI/Model.h @@ -67,6 +67,7 @@ public: Font, DragData, TextAlignment, + Search, Custom = 0x100, // Applications are free to use roles above this number as they please }; @@ -76,12 +77,13 @@ public: virtual int column_count(const ModelIndex& = ModelIndex()) const = 0; virtual String column_name(int) const { return {}; } virtual Variant data(const ModelIndex&, Role = Role::Display) const = 0; + virtual TriState data_matches(const ModelIndex&, Variant) const { return TriState::Unknown; } virtual void update() = 0; virtual ModelIndex parent_index(const ModelIndex&) const { return {}; } virtual ModelIndex index(int row, int column = 0, const ModelIndex& = ModelIndex()) const { return create_index(row, column); } virtual ModelIndex sibling(int row, int column, const ModelIndex& parent) const; virtual bool is_editable(const ModelIndex&) const { return false; } - virtual void set_data(const ModelIndex&, const Variant&) {} + virtual void set_data(const ModelIndex&, const Variant&) { } virtual int tree_column() const { return 0; } virtual bool accepts_drag(const ModelIndex&, const StringView& data_type); @@ -95,7 +97,7 @@ public: virtual int key_column() const { return -1; } virtual SortOrder sort_order() const { return SortOrder::None; } - virtual void set_key_column_and_sort_order(int, SortOrder) {} + virtual void set_key_column_and_sort_order(int, SortOrder) { } virtual StringView drag_data_type() const { return {}; } |