mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-07 16:40:59 +00:00
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.
This commit is contained in:
parent
370624bc37
commit
e1ed71ef9e
|
@ -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);
|
||||
|
|
|
@ -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(); });
|
||||
}
|
||||
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -92,4 +92,6 @@ class Widget;
|
|||
class Window;
|
||||
class WindowServerConnection;
|
||||
|
||||
enum class SortOrder;
|
||||
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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); };
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue