mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 01:07:35 +00:00
Make it possible to sort a GTableModel by column+order.
This is accomplished by putting a GSortingProxyTableModel between the model and the view. It's pretty simplistic but it works for this use case. :^)
This commit is contained in:
parent
0680fe2383
commit
7d1142c7d9
16 changed files with 278 additions and 40 deletions
116
LibGUI/GSortingProxyTableModel.cpp
Normal file
116
LibGUI/GSortingProxyTableModel.cpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
#include <LibGUI/GSortingProxyTableModel.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
GSortingProxyTableModel::GSortingProxyTableModel(OwnPtr<GTableModel>&& target)
|
||||
: m_target(move(target))
|
||||
, m_key_column(-1)
|
||||
{
|
||||
m_target->on_model_update = [this] (GTableModel&) {
|
||||
resort();
|
||||
};
|
||||
}
|
||||
|
||||
GSortingProxyTableModel::~GSortingProxyTableModel()
|
||||
{
|
||||
}
|
||||
|
||||
int GSortingProxyTableModel::row_count() const
|
||||
{
|
||||
return target().row_count();
|
||||
}
|
||||
|
||||
int GSortingProxyTableModel::column_count() const
|
||||
{
|
||||
return target().column_count();
|
||||
}
|
||||
|
||||
GModelIndex GSortingProxyTableModel::map_to_target(const GModelIndex& index) const
|
||||
{
|
||||
ASSERT(!m_row_mappings.is_empty());
|
||||
if (!index.is_valid()) {
|
||||
ASSERT_NOT_REACHED();
|
||||
return { };
|
||||
}
|
||||
if (index.row() >= row_count() || index.column() >= column_count()) {
|
||||
ASSERT_NOT_REACHED();
|
||||
return { };
|
||||
}
|
||||
return { m_row_mappings[index.row()], index.column() };
|
||||
}
|
||||
|
||||
String GSortingProxyTableModel::row_name(int index) const
|
||||
{
|
||||
return target().row_name(index);
|
||||
}
|
||||
|
||||
String GSortingProxyTableModel::column_name(int index) const
|
||||
{
|
||||
return target().column_name(index);
|
||||
}
|
||||
|
||||
GTableModel::ColumnMetadata GSortingProxyTableModel::column_metadata(int index) const
|
||||
{
|
||||
return target().column_metadata(index);
|
||||
}
|
||||
|
||||
GVariant GSortingProxyTableModel::data(const GModelIndex& index) const
|
||||
{
|
||||
return target().data(map_to_target(index));
|
||||
}
|
||||
|
||||
void GSortingProxyTableModel::activate(const GModelIndex& index)
|
||||
{
|
||||
target().activate(map_to_target(index));
|
||||
}
|
||||
|
||||
void GSortingProxyTableModel::update()
|
||||
{
|
||||
target().update();
|
||||
}
|
||||
|
||||
void GSortingProxyTableModel::set_key_column_and_sort_order(int column, GSortOrder 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;
|
||||
resort();
|
||||
}
|
||||
|
||||
void GSortingProxyTableModel::resort()
|
||||
{
|
||||
int row_count = target().row_count();
|
||||
m_row_mappings.resize(row_count);
|
||||
for (int i = 0; i < row_count; ++i)
|
||||
m_row_mappings[i] = i;
|
||||
|
||||
if (m_key_column == -1)
|
||||
return;
|
||||
|
||||
struct Context {
|
||||
GTableModel* target;
|
||||
int key_column;
|
||||
GSortOrder sort_order;
|
||||
};
|
||||
Context context { m_target.ptr(), m_key_column, m_sort_order };
|
||||
qsort_r(m_row_mappings.data(), m_row_mappings.size(), sizeof(int), [] (const void* a, const void* b, void* ctx) -> int {
|
||||
int row1 = *(const int*)(a);
|
||||
int row2 = *(const int*)(b);
|
||||
auto& context = *(Context*)(ctx);
|
||||
GModelIndex index1 { row1, context.key_column };
|
||||
GModelIndex index2 { row2, context.key_column };
|
||||
auto data1 = context.target->data(index1);
|
||||
auto data2 = context.target->data(index2);
|
||||
if (data1 == data2)
|
||||
return 0;
|
||||
bool is_less_than = data1 < data2;
|
||||
if (context.sort_order == GSortOrder::Ascending)
|
||||
return is_less_than ? -1 : 1;
|
||||
return is_less_than ? 1 : -1;
|
||||
}, &context);
|
||||
|
||||
did_update();
|
||||
}
|
35
LibGUI/GSortingProxyTableModel.h
Normal file
35
LibGUI/GSortingProxyTableModel.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibGUI/GTableModel.h>
|
||||
|
||||
class GSortingProxyTableModel final : public GTableModel {
|
||||
public:
|
||||
explicit GSortingProxyTableModel(OwnPtr<GTableModel>&&);
|
||||
virtual ~GSortingProxyTableModel() override;
|
||||
|
||||
virtual int row_count() const override;
|
||||
virtual int column_count() const override;
|
||||
virtual String row_name(int) const override;
|
||||
virtual String column_name(int) const override;
|
||||
virtual ColumnMetadata column_metadata(int) const override;
|
||||
virtual GVariant data(const GModelIndex&) const override;
|
||||
virtual void update() override;
|
||||
virtual void activate(const GModelIndex&) override;
|
||||
|
||||
virtual int key_column() const override { return m_key_column; }
|
||||
virtual GSortOrder sort_order() const override { return m_sort_order; }
|
||||
virtual void set_key_column_and_sort_order(int, GSortOrder) override;
|
||||
|
||||
GModelIndex map_to_target(const GModelIndex&) const;
|
||||
|
||||
private:
|
||||
GTableModel& target() { return *m_target; }
|
||||
const GTableModel& target() const { return *m_target; }
|
||||
|
||||
void resort();
|
||||
|
||||
OwnPtr<GTableModel> m_target;
|
||||
Vector<int> m_row_mappings;
|
||||
int m_key_column { -1 };
|
||||
GSortOrder m_sort_order { GSortOrder::Ascending };
|
||||
};
|
|
@ -27,6 +27,8 @@ void GTableModel::for_each_view(Function<void(GTableView&)> callback)
|
|||
|
||||
void GTableModel::did_update()
|
||||
{
|
||||
if (on_model_update)
|
||||
on_model_update(*this);
|
||||
for_each_view([] (GTableView& view) {
|
||||
view.did_update_model();
|
||||
});
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
class GTableView;
|
||||
|
||||
enum class GSortOrder { None, Ascending, Descending };
|
||||
|
||||
class GModelNotification {
|
||||
public:
|
||||
enum Type {
|
||||
|
@ -57,9 +59,15 @@ public:
|
|||
void set_selected_index(const GModelIndex& index) { m_selected_index = index; }
|
||||
GModelIndex selected_index() const { return m_selected_index; }
|
||||
|
||||
virtual int key_column() const { return -1; }
|
||||
virtual GSortOrder sort_order() const { return GSortOrder::None; }
|
||||
virtual void set_key_column_and_sort_order(int, GSortOrder) { }
|
||||
|
||||
void register_view(Badge<GTableView>, GTableView&);
|
||||
void unregister_view(Badge<GTableView>, GTableView&);
|
||||
|
||||
Function<void(GTableModel&)> on_model_update;
|
||||
|
||||
protected:
|
||||
GTableModel();
|
||||
|
||||
|
|
|
@ -48,6 +48,49 @@ GVariant::GVariant(const GraphicsBitmap& value)
|
|||
AK::retain_if_not_null(m_value.as_bitmap);
|
||||
}
|
||||
|
||||
bool GVariant::operator==(const GVariant& other) const
|
||||
{
|
||||
if (m_type != other.m_type)
|
||||
return to_string() == other.to_string();
|
||||
switch (m_type) {
|
||||
case Type::Bool:
|
||||
return as_bool() == other.as_bool();
|
||||
case Type::Int:
|
||||
return as_int() == other.as_int();
|
||||
case Type::Float:
|
||||
return as_float() == other.as_float();
|
||||
case Type::String:
|
||||
return as_string() == other.as_string();
|
||||
case Type::Bitmap:
|
||||
return m_value.as_bitmap == other.m_value.as_bitmap;
|
||||
case Type::Invalid:
|
||||
break;
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
bool GVariant::operator<(const GVariant& other) const
|
||||
{
|
||||
if (m_type != other.m_type)
|
||||
return to_string() < other.to_string();
|
||||
switch (m_type) {
|
||||
case Type::Bool:
|
||||
return as_bool() < other.as_bool();
|
||||
case Type::Int:
|
||||
return as_int() < other.as_int();
|
||||
case Type::Float:
|
||||
return as_float() < other.as_float();
|
||||
case Type::String:
|
||||
return as_string() < other.as_string();
|
||||
case Type::Bitmap:
|
||||
// FIXME: Maybe compare bitmaps somehow differently?
|
||||
return m_value.as_bitmap < other.m_value.as_bitmap;
|
||||
case Type::Invalid:
|
||||
break;
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
String GVariant::to_string() const
|
||||
{
|
||||
switch (m_type) {
|
||||
|
|
|
@ -62,6 +62,9 @@ public:
|
|||
|
||||
String to_string() const;
|
||||
|
||||
bool operator==(const GVariant&) const;
|
||||
bool operator<(const GVariant&) const;
|
||||
|
||||
private:
|
||||
union {
|
||||
StringImpl* as_string;
|
||||
|
|
|
@ -34,6 +34,7 @@ LIBGUI_OBJS = \
|
|||
GShortcut.o \
|
||||
GTextEditor.o \
|
||||
GClipboard.o \
|
||||
GSortingProxyTableModel.o \
|
||||
GWindow.o
|
||||
|
||||
OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue