Bläddra i källkod

LibGUI: Make model sorting imperative and move order to AbstractView

Instead of SortingProxyModel having a column+order, we move that state
to AbstractView. When you click on a column header, the view tells the
model to resort the relevant column with the new order.

This is implemented in SortingProxyModel by simply walking all the
reified source/proxy mappings and resorting their row indexes.
Andreas Kling 5 år sedan
förälder
incheckning
e1ed71ef9e

+ 1 - 1
Applications/FileManager/DirectoryView.cpp

@@ -127,7 +127,7 @@ DirectoryView::DirectoryView()
     m_table_view = add<GUI::TableView>();
     m_table_view->set_model(m_sorting_model);
 
-    m_table_view->model()->set_key_column_and_sort_order(GUI::FileSystemModel::Column::Name, GUI::SortOrder::Ascending);
+    m_table_view->set_key_column_and_sort_order(GUI::FileSystemModel::Column::Name, GUI::SortOrder::Ascending);
 
     m_icon_view->set_model_column(GUI::FileSystemModel::Column::Name);
     m_columns_view->set_model_column(GUI::FileSystemModel::Column::Name);

+ 1 - 1
Applications/SystemMonitor/ProcessMemoryMapWidget.cpp

@@ -120,7 +120,7 @@ ProcessMemoryMapWidget::ProcessMemoryMapWidget()
 
     m_table_view->set_cell_painting_delegate(7, make<PagemapPaintingDelegate>());
 
-    m_table_view->model()->set_key_column_and_sort_order(0, GUI::SortOrder::Ascending);
+    m_table_view->set_key_column_and_sort_order(0, GUI::SortOrder::Ascending);
     m_timer = add<Core::Timer>(1000, [this] { refresh(); });
 }
 

+ 1 - 1
Applications/SystemMonitor/main.cpp

@@ -179,7 +179,7 @@ int main(int argc, char** argv)
     auto& process_table_view = process_table_container.add<GUI::TableView>();
     process_table_view.set_headers_visible(true);
     process_table_view.set_model(GUI::SortingProxyModel::create(ProcessModel::create()));
-    process_table_view.model()->set_key_column_and_sort_order(ProcessModel::Column::CPU, GUI::SortOrder::Descending);
+    process_table_view.set_key_column_and_sort_order(ProcessModel::Column::CPU, GUI::SortOrder::Descending);
     process_table_view.model()->update();
 
     auto& refresh_timer = window->add<Core::Timer>(

+ 7 - 9
Libraries/LibGUI/AbstractTableView.cpp

@@ -65,13 +65,12 @@ void AbstractTableView::update_column_sizes()
     auto& model = *this->model();
     int column_count = model.column_count();
     int row_count = model.row_count();
-    int key_column = model.key_column();
 
     for (int column = 0; column < column_count; ++column) {
         if (is_column_hidden(column))
             continue;
         int header_width = header_font().width(model.column_name(column));
-        if (column == key_column && model.is_column_sortable(column))
+        if (column == m_key_column && model.is_column_sortable(column))
             header_width += font().width(" \xE2\xAC\x86"); // UPWARDS BLACK ARROW
         int column_width = header_width;
         for (int row = 0; row < row_count; ++row) {
@@ -146,7 +145,7 @@ void AbstractTableView::paint_headers(Painter& painter)
         if (is_column_hidden(column_index))
             continue;
         int column_width = this->column_width(column_index);
-        bool is_key_column = model()->key_column() == column_index;
+        bool is_key_column = m_key_column == column_index;
         Gfx::IntRect cell_rect(x_offset, 0, column_width + horizontal_padding() * 2, header_height());
         bool pressed = column_index == m_pressed_column_header_index && m_pressed_column_header_is_pressed;
         bool hovered = column_index == m_hovered_column_header_index && model()->is_column_sortable(column_index);
@@ -155,10 +154,9 @@ void AbstractTableView::paint_headers(Painter& painter)
         if (is_key_column) {
             StringBuilder builder;
             builder.append(model()->column_name(column_index));
-            auto sort_order = model()->sort_order();
-            if (sort_order == SortOrder::Ascending)
+            if (m_sort_order == SortOrder::Ascending)
                 builder.append(" \xE2\xAC\x86"); // UPWARDS BLACK ARROW
-            else if (sort_order == SortOrder::Descending)
+            else if (m_sort_order == SortOrder::Descending)
                 builder.append(" \xE2\xAC\x87"); // DOWNWARDS BLACK ARROW
             text = builder.to_string();
         } else {
@@ -327,11 +325,11 @@ void AbstractTableView::mouseup_event(MouseEvent& event)
             auto header_rect = this->header_rect(m_pressed_column_header_index);
             if (header_rect.contains(horizontally_adjusted_position)) {
                 auto new_sort_order = SortOrder::Ascending;
-                if (model()->key_column() == m_pressed_column_header_index)
-                    new_sort_order = model()->sort_order() == SortOrder::Ascending
+                if (m_key_column == m_pressed_column_header_index)
+                    new_sort_order = m_sort_order == SortOrder::Ascending
                         ? SortOrder::Descending
                         : SortOrder::Ascending;
-                model()->set_key_column_and_sort_order(m_pressed_column_header_index, new_sort_order);
+                set_key_column_and_sort_order(m_pressed_column_header_index, new_sort_order);
             }
             m_pressed_column_header_index = -1;
             m_pressed_column_header_is_pressed = false;

+ 11 - 1
Libraries/LibGUI/AbstractView.cpp

@@ -37,7 +37,8 @@
 namespace GUI {
 
 AbstractView::AbstractView()
-    : m_selection(*this)
+    : m_sort_order(SortOrder::Ascending)
+    , m_selection(*this)
 {
 }
 
@@ -400,4 +401,13 @@ void AbstractView::set_multi_select(bool multi_select)
     }
 }
 
+void AbstractView::set_key_column_and_sort_order(int column, SortOrder sort_order)
+{
+    m_key_column = column;
+    m_sort_order = sort_order;
+
+    if (model())
+        model()->sort(column, sort_order);
+}
+
 }

+ 5 - 0
Libraries/LibGUI/AbstractView.h

@@ -76,6 +76,8 @@ public:
 
     void set_last_valid_hovered_index(const ModelIndex&);
 
+    void set_key_column_and_sort_order(int column, SortOrder);
+
 protected:
     AbstractView();
     virtual ~AbstractView() override;
@@ -111,6 +113,9 @@ protected:
     ModelIndex m_hovered_index;
     ModelIndex m_last_valid_hovered_index;
 
+    int m_key_column { 0 };
+    SortOrder m_sort_order;
+
 private:
     RefPtr<Model> m_model;
     OwnPtr<ModelEditingDelegate> m_editing_delegate;

+ 1 - 1
Libraries/LibGUI/FilePicker.cpp

@@ -127,7 +127,7 @@ FilePicker::FilePicker(Window* parent_window, Mode mode, Options options, const
     m_view->set_multi_select(m_mode == Mode::OpenMultiple);
     m_view->set_model(SortingProxyModel::create(*m_model));
     m_view->set_model_column(FileSystemModel::Column::Name);
-    m_view->model()->set_key_column_and_sort_order(GUI::FileSystemModel::Column::Name, GUI::SortOrder::Ascending);
+    m_view->set_key_column_and_sort_order(GUI::FileSystemModel::Column::Name, GUI::SortOrder::Ascending);
     m_view->set_column_hidden(FileSystemModel::Column::Owner, true);
     m_view->set_column_hidden(FileSystemModel::Column::Group, true);
     m_view->set_column_hidden(FileSystemModel::Column::Permissions, true);

+ 2 - 0
Libraries/LibGUI/Forward.h

@@ -92,4 +92,6 @@ class Widget;
 class Window;
 class WindowServerConnection;
 
+enum class SortOrder;
+
 }

+ 1 - 4
Libraries/LibGUI/Model.h

@@ -87,6 +87,7 @@ public:
     virtual bool accepts_drag(const ModelIndex&, const StringView& data_type);
 
     virtual bool is_column_sortable([[maybe_unused]] int column_index) const { return true; }
+    virtual void sort([[maybe_unused]] int column, SortOrder) { }
 
     bool is_valid(const ModelIndex& index) const
     {
@@ -94,10 +95,6 @@ public:
         return index.row() >= 0 && index.row() < row_count(parent_index) && index.column() >= 0 && index.column() < column_count(parent_index);
     }
 
-    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 StringView drag_data_type() const { return {}; }
 
     void register_view(Badge<AbstractView>, AbstractView&);

+ 7 - 0
Libraries/LibGUI/MultiView.cpp

@@ -212,4 +212,11 @@ void MultiView::set_multi_select(bool multi_select)
     apply_multi_select();
 }
 
+void MultiView::set_key_column_and_sort_order(int column, SortOrder sort_order)
+{
+    for_each_view_implementation([&](auto& view) {
+        view.set_key_column_and_sort_order(column, sort_order);
+    });
+}
+
 }

+ 2 - 0
Libraries/LibGUI/MultiView.h

@@ -63,6 +63,8 @@ public:
 
     void set_column_hidden(int column_index, bool hidden);
 
+    void set_key_column_and_sort_order(int column, SortOrder);
+
     GUI::AbstractView& current_view()
     {
         switch (m_view_mode) {

+ 1 - 1
Libraries/LibGUI/ProcessChooser.cpp

@@ -58,7 +58,7 @@ ProcessChooser::ProcessChooser(const StringView& window_title, const StringView&
     m_table_view = widget.add<GUI::TableView>();
     auto sorting_model = GUI::SortingProxyModel::create(RunningProcessesModel::create());
     sorting_model->set_sort_role(GUI::Model::Role::Display);
-    sorting_model->set_key_column_and_sort_order(RunningProcessesModel::Column::PID, GUI::SortOrder::Descending);
+    m_table_view->set_key_column_and_sort_order(RunningProcessesModel::Column::PID, GUI::SortOrder::Descending);
     m_table_view->set_model(sorting_model);
 
     m_table_view->on_activation = [this](const ModelIndex& index) { set_pid_from_index_and_close(index); };

+ 38 - 30
Libraries/LibGUI/SortingProxyModel.cpp

@@ -32,7 +32,6 @@ namespace GUI {
 
 SortingProxyModel::SortingProxyModel(NonnullRefPtr<Model> source)
     : m_source(move(source))
-    , m_key_column(-1)
 {
     m_source->register_client(*this);
     invalidate();
@@ -126,17 +125,6 @@ StringView SortingProxyModel::drag_data_type() const
     return source().drag_data_type();
 }
 
-void SortingProxyModel::set_key_column_and_sort_order(int column, SortOrder sort_order)
-{
-    if (column == m_key_column && sort_order == m_sort_order)
-        return;
-
-    ASSERT(column >= 0 && column < column_count());
-    m_key_column = column;
-    m_sort_order = sort_order;
-    invalidate();
-}
-
 bool SortingProxyModel::less_than(const ModelIndex& index1, const ModelIndex& index2) const
 {
     auto data1 = index1.model() ? index1.model()->data(index1, m_sort_role) : Variant();
@@ -177,6 +165,43 @@ ModelIndex SortingProxyModel::parent_index(const ModelIndex& proxy_index) const
     return map_to_proxy(it->value->source_parent);
 }
 
+void SortingProxyModel::sort_mapping(Mapping& mapping, int column, SortOrder sort_order)
+{
+    if (column == -1) {
+        int row_count = source().row_count(mapping.source_parent);
+        for (int i = 0; i < row_count; ++i) {
+            mapping.source_rows[i] = i;
+            mapping.proxy_rows[i] = i;
+        }
+        return;
+    }
+
+    int row_count = source().row_count(mapping.source_parent);
+    for (int i = 0; i < row_count; ++i)
+        mapping.source_rows[i] = i;
+
+    quick_sort(mapping.source_rows, [&](auto row1, auto row2) -> bool {
+        bool is_less_than = less_than(source().index(row1, column, mapping.source_parent), source().index(row2, column, mapping.source_parent));
+        return sort_order == SortOrder::Ascending ? is_less_than : !is_less_than;
+    });
+
+    for (int i = 0; i < row_count; ++i)
+        mapping.proxy_rows[mapping.source_rows[i]] = i;
+}
+
+void SortingProxyModel::sort(int column, SortOrder sort_order)
+{
+    for (auto& it : m_mappings) {
+        auto& mapping = *it.value;
+        sort_mapping(mapping, column, sort_order);
+    }
+
+    m_last_key_column = column;
+    m_last_sort_order = sort_order;
+
+    did_update();
+}
+
 SortingProxyModel::InternalMapIterator SortingProxyModel::build_mapping(const ModelIndex& source_parent)
 {
     auto it = m_mappings.find(source_parent);
@@ -191,24 +216,7 @@ SortingProxyModel::InternalMapIterator SortingProxyModel::build_mapping(const Mo
     mapping->source_rows.resize(row_count);
     mapping->proxy_rows.resize(row_count);
 
-    for (int i = 0; i < row_count; ++i) {
-        mapping->source_rows[i] = i;
-    }
-
-    // If we don't have a key column, we're not sorting.
-    if (m_key_column == -1) {
-        m_mappings.set(source_parent, move(mapping));
-        return m_mappings.find(source_parent);
-    }
-
-    quick_sort(mapping->source_rows, [&](auto row1, auto row2) -> bool {
-        bool is_less_than = less_than(source().index(row1, m_key_column, source_parent), source().index(row2, m_key_column, source_parent));
-        return m_sort_order == SortOrder::Ascending ? is_less_than : !is_less_than;
-    });
-
-    for (int i = 0; i < row_count; ++i) {
-        mapping->proxy_rows[mapping->source_rows[i]] = i;
-    }
+    sort_mapping(*mapping, m_last_key_column, m_last_sort_order);
 
     if (source_parent.is_valid()) {
         auto source_grand_parent = source_parent.parent();

+ 6 - 5
Libraries/LibGUI/SortingProxyModel.h

@@ -46,9 +46,6 @@ public:
     virtual ModelIndex parent_index(const ModelIndex&) const override;
     virtual ModelIndex index(int row, int column, const ModelIndex& parent) const override;
 
-    virtual int key_column() const override { return m_key_column; }
-    virtual SortOrder sort_order() const override { return m_sort_order; }
-    virtual void set_key_column_and_sort_order(int, SortOrder) override;
     virtual bool is_column_sortable(int column_index) const override;
 
     virtual bool less_than(const ModelIndex&, const ModelIndex&) const;
@@ -59,6 +56,8 @@ public:
     Role sort_role() const { return m_sort_role; }
     void set_sort_role(Role role) { m_sort_role = role; }
 
+    virtual void sort(int column, SortOrder) override;
+
 private:
     explicit SortingProxyModel(NonnullRefPtr<Model> source);
 
@@ -71,6 +70,8 @@ private:
 
     using InternalMapIterator = HashMap<ModelIndex, NonnullOwnPtr<Mapping>>::IteratorType;
 
+    void sort_mapping(Mapping&, int column, SortOrder);
+
     // ^ModelClient
     virtual void model_did_update(unsigned) override;
 
@@ -83,9 +84,9 @@ private:
     NonnullRefPtr<Model> m_source;
 
     HashMap<ModelIndex, NonnullOwnPtr<Mapping>> m_mappings;
-    int m_key_column { -1 };
-    SortOrder m_sort_order { SortOrder::Ascending };
     Role m_sort_role { Role::Sort };
+    int m_last_key_column { -1 };
+    SortOrder m_last_sort_order { SortOrder::Ascending };
 };
 
 }

+ 1 - 1
Libraries/LibGUI/TableView.cpp

@@ -103,7 +103,7 @@ void TableView::paint_event(PaintEvent& event)
             if (is_column_hidden(column_index))
                 continue;
             int column_width = this->column_width(column_index);
-            bool is_key_column = model()->key_column() == column_index;
+            bool is_key_column = m_key_column == column_index;
             Gfx::IntRect cell_rect(horizontal_padding() + x_offset, y, column_width, item_height());
             auto cell_rect_for_fill = cell_rect.inflated(horizontal_padding() * 2, 0);
             if (is_key_column)