LibGUI: Expand GModelIndex a bit, adding internal data and model pointers.

This will be useful for implementing more complicated models.
This commit is contained in:
Andreas Kling 2019-03-29 04:58:15 +01:00
parent 12ec55ee74
commit d02238af48
10 changed files with 94 additions and 34 deletions

View file

@ -282,7 +282,7 @@ void DirectoryModel::open(const String& a_path)
closedir(dirp);
m_path = path;
update();
set_selected_index({ 0, 0 });
set_selected_index(index(0, 0));
}
void DirectoryModel::activate(const GModelIndex& index)

View file

@ -11,6 +11,7 @@
#include <LibGUI/GInputBox.h>
#include <LibGUI/GMessageBox.h>
#include <LibGUI/GProgressBar.h>
#include <LibGUI/GTreeView.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
@ -48,7 +49,12 @@ int main(int argc, char** argv)
auto* location_textbox = new GTextEditor(GTextEditor::SingleLine, location_toolbar);
auto* directory_view = new DirectoryView(widget);
// FIXME: Implement an actual GSplitter widget.
auto* splitter = new GWidget(widget);
splitter->set_layout(make<GBoxLayout>(Orientation::Horizontal));
auto* tree_view = new GTreeView(splitter);
auto* directory_view = new DirectoryView(splitter);
auto* statusbar = new GStatusBar(widget);
auto* progressbar = new GProgressBar(statusbar);

View file

@ -33,5 +33,5 @@ pid_t ProcessTableView::selected_pid() const
{
if (!model()->selected_index().is_valid())
return -1;
return model()->data({ model()->selected_index().row(), ProcessModel::Column::PID }, GModel::Role::Sort).as_int();
return model()->data(model()->index(model()->selected_index().row(), ProcessModel::Column::PID), GModel::Role::Sort).as_int();
}

View file

@ -74,7 +74,7 @@ void GItemView::mousedown_event(GMouseEvent& event)
auto adjusted_position = event.position().translated(0, vertical_scrollbar().value());
for (int i = 0; i < item_count(); ++i) {
if (item_rect(i).contains(adjusted_position)) {
model()->set_selected_index({ i, 0 });
model()->set_selected_index(model()->index(i, 0));
update();
return;
}
@ -117,7 +117,7 @@ void GItemView::paint_event(GPaintEvent& event)
}
Rect item_rect = this->item_rect(item_index);
GModelIndex model_index(item_index, m_model_column);
auto model_index = model()->index(item_index, m_model_column);
auto icon = model()->data(model_index, GModel::Role::Icon);
auto item_text = model()->data(model_index, GModel::Role::Display);
@ -165,7 +165,7 @@ void GItemView::keydown_event(GKeyEvent& event)
return;
}
if (event.key() == KeyCode::Key_Home) {
GModelIndex new_index { 0, 0 };
auto new_index = model.index(0, 0);
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);
@ -174,7 +174,7 @@ void GItemView::keydown_event(GKeyEvent& event)
return;
}
if (event.key() == KeyCode::Key_End) {
GModelIndex new_index { model.row_count() - 1, 0 };
auto new_index = model.index(model.row_count() - 1, 0);
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);
@ -185,9 +185,9 @@ void GItemView::keydown_event(GKeyEvent& event)
if (event.key() == KeyCode::Key_Up) {
GModelIndex new_index;
if (model.selected_index().is_valid())
new_index = { model.selected_index().row() - m_visual_column_count, model.selected_index().column() };
new_index = model.index(model.selected_index().row() - m_visual_column_count, model.selected_index().column());
else
new_index = { 0, 0 };
new_index = model.index(0, 0);
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);
@ -198,9 +198,9 @@ void GItemView::keydown_event(GKeyEvent& event)
if (event.key() == KeyCode::Key_Down) {
GModelIndex new_index;
if (model.selected_index().is_valid())
new_index = { model.selected_index().row() + m_visual_column_count, model.selected_index().column() };
new_index = model.index(model.selected_index().row() + m_visual_column_count, model.selected_index().column());
else
new_index = { 0, 0 };
new_index = model.index(0, 0);
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);
@ -211,9 +211,9 @@ void GItemView::keydown_event(GKeyEvent& event)
if (event.key() == KeyCode::Key_Left) {
GModelIndex new_index;
if (model.selected_index().is_valid())
new_index = { model.selected_index().row() - 1, model.selected_index().column() };
new_index = model.index(model.selected_index().row() - 1, model.selected_index().column());
else
new_index = { 0, 0 };
new_index = model.index(0, 0);
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);
@ -224,9 +224,9 @@ void GItemView::keydown_event(GKeyEvent& event)
if (event.key() == KeyCode::Key_Right) {
GModelIndex new_index;
if (model.selected_index().is_valid())
new_index = { model.selected_index().row() + 1, model.selected_index().column() };
new_index = model.index(model.selected_index().row() + 1, model.selected_index().column());
else
new_index = { 0, 0 };
new_index = model.index(0, 0);
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);
@ -236,7 +236,7 @@ void GItemView::keydown_event(GKeyEvent& event)
}
if (event.key() == KeyCode::Key_PageUp) {
int items_per_page = (visible_content_rect().height() / effective_item_size().height()) * m_visual_column_count;
GModelIndex new_index(max(0, model.selected_index().row() - items_per_page), model.selected_index().column());
auto new_index = model.index(max(0, model.selected_index().row() - items_per_page), model.selected_index().column());
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);
@ -246,7 +246,7 @@ void GItemView::keydown_event(GKeyEvent& event)
}
if (event.key() == KeyCode::Key_PageDown) {
int items_per_page = (visible_content_rect().height() / effective_item_size().height()) * m_visual_column_count;
GModelIndex new_index(min(model.row_count() - 1, model.selected_index().row() + items_per_page), model.selected_index().column());
auto new_index = model.index(min(model.row_count() - 1, model.selected_index().row() + items_per_page), model.selected_index().column());
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);

View file

@ -44,3 +44,8 @@ void GModel::set_selected_index(const GModelIndex& index)
if (m_activates_on_selection && is_valid(index))
activate(index);
}
GModelIndex GModel::create_index(int row, int column, void* data) const
{
return GModelIndex(*this, row, column, data);
}

View file

@ -76,12 +76,16 @@ public:
Function<void(GModel&)> on_model_update;
Function<void(const GModelIndex&)> on_selection_changed;
virtual GModelIndex index(int row, int column) const { return create_index(row, column); }
protected:
GModel();
void for_each_view(Function<void(GAbstractView&)>);
void did_update();
GModelIndex create_index(int row, int column, void* data = nullptr) const;
private:
HashTable<GAbstractView*> m_views;
GModelIndex m_selected_index;

View file

@ -1,21 +1,31 @@
#pragma once
class GModel;
class GModelIndex {
friend class GModel;
public:
GModelIndex() { }
GModelIndex(int row, int column)
: m_row(row)
, m_column(column)
{
}
bool is_valid() const { return m_row != -1 && m_column != -1; }
int row() const { return m_row; }
int column() const { return m_column; }
void* internal_data() const { return m_internal_data; }
bool operator==(const GModelIndex& other) const { return m_row == other.m_row && m_column == other.m_column; }
private:
GModelIndex(const GModel& model, int row, int column, void* internal_data)
: m_model(&model)
, m_row(row)
, m_column(column)
, m_internal_data(internal_data)
{
}
const GModel* m_model { nullptr };
int m_row { -1 };
int m_column { -1 };
void* m_internal_data { nullptr };
};

View file

@ -32,7 +32,7 @@ GModelIndex GSortingProxyModel::map_to_target(const GModelIndex& index) const
return { };
if (index.row() >= row_count() || index.column() >= column_count())
return { };
return { m_row_mappings[index.row()], index.column() };
return target().index(m_row_mappings[index.row()], index.column());
}
String GSortingProxyModel::row_name(int index) const
@ -86,8 +86,8 @@ void GSortingProxyModel::resort()
if (m_key_column == -1)
return;
quick_sort(m_row_mappings.begin(), m_row_mappings.end(), [&] (auto row1, auto row2) -> bool {
auto data1 = target().data({ row1, m_key_column }, GModel::Role::Sort);
auto data2 = target().data({ row2, m_key_column }, GModel::Role::Sort);
auto data1 = target().data(target().index(row1, m_key_column), GModel::Role::Sort);
auto data2 = target().data(target().index(row2, m_key_column), GModel::Role::Sort);
if (data1 == data2)
return 0;
bool is_less_than = data1 < data2;
@ -97,7 +97,7 @@ void GSortingProxyModel::resort()
// Preserve selection.
for (int i = 0; i < row_count; ++i) {
if (m_row_mappings[i] == previously_selected_target_row) {
set_selected_index({ i, 0 });
set_selected_index(index(i, 0));
break;
}
}

View file

@ -86,7 +86,7 @@ void GTableView::mousedown_event(GMouseEvent& event)
auto adjusted_position = event.position().translated(0, vertical_scrollbar().value());
for (int i = 0; i < item_count(); ++i) {
if (row_rect(i).contains(adjusted_position)) {
model()->set_selected_index({ i, 0 });
model()->set_selected_index(model()->index(i, 0));
update();
return;
}
@ -143,7 +143,7 @@ void GTableView::paint_event(GPaintEvent& event)
auto cell_rect_for_fill = cell_rect.inflated(horizontal_padding() * 2, 0);
painter.fill_rect(cell_rect_for_fill, key_column_background_color);
}
GModelIndex cell_index(row_index, column_index);
auto cell_index = model()->index(row_index, column_index);
auto data = model()->data(cell_index);
if (data.is_bitmap()) {
painter.blit(cell_rect.location(), data.as_bitmap(), data.as_bitmap().rect());
@ -228,9 +228,9 @@ void GTableView::keydown_event(GKeyEvent& event)
if (event.key() == KeyCode::Key_Up) {
GModelIndex new_index;
if (model.selected_index().is_valid())
new_index = { model.selected_index().row() - 1, model.selected_index().column() };
new_index = model.index(model.selected_index().row() - 1, model.selected_index().column());
else
new_index = { 0, 0 };
new_index = model.index(0, 0);
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);
@ -241,9 +241,9 @@ void GTableView::keydown_event(GKeyEvent& event)
if (event.key() == KeyCode::Key_Down) {
GModelIndex new_index;
if (model.selected_index().is_valid())
new_index = { model.selected_index().row() + 1, model.selected_index().column() };
new_index = model.index(model.selected_index().row() + 1, model.selected_index().column());
else
new_index = { 0, 0 };
new_index = model.index(0, 0);
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);
@ -253,7 +253,7 @@ void GTableView::keydown_event(GKeyEvent& event)
}
if (event.key() == KeyCode::Key_PageUp) {
int items_per_page = visible_content_rect().height() / item_height();
GModelIndex new_index(max(0, model.selected_index().row() - items_per_page), model.selected_index().column());
auto new_index = model.index(max(0, model.selected_index().row() - items_per_page), model.selected_index().column());
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);
@ -263,7 +263,7 @@ void GTableView::keydown_event(GKeyEvent& event)
}
if (event.key() == KeyCode::Key_PageDown) {
int items_per_page = visible_content_rect().height() / item_height();
GModelIndex new_index(min(model.row_count() - 1, model.selected_index().row() + items_per_page), model.selected_index().column());
auto new_index = model.index(min(model.row_count() - 1, model.selected_index().row() + items_per_page), model.selected_index().column());
if (model.is_valid(new_index)) {
model.set_selected_index(new_index);
scroll_into_view(new_index, Orientation::Vertical);

View file

@ -1,9 +1,44 @@
#include <LibGUI/GTreeView.h>
#include <LibGUI/GPainter.h>
class TestModel : public GModel {
public:
static Retained<TestModel> create() { return adopt(*new TestModel); }
virtual int row_count(const GModelIndex& = GModelIndex()) const;
virtual int column_count(const GModelIndex& = GModelIndex()) const;
virtual GVariant data(const GModelIndex&, Role = Role::Display) const;
virtual void update();
virtual ColumnMetadata column_metadata(int) const { return { 100 }; }
};
int TestModel::row_count(const GModelIndex& index) const
{
return 0;
}
int TestModel::column_count(const GModelIndex&) const
{
return 1;
}
void TestModel::update()
{
}
GVariant TestModel::data(const GModelIndex&, Role) const
{
return { };
}
GTreeView::GTreeView(GWidget* parent)
: GAbstractView(parent)
{
set_frame_shape(GFrame::Shape::Container);
set_frame_shadow(GFrame::Shadow::Sunken);
set_frame_thickness(2);
set_model(TestModel::create());
}
GTreeView::~GTreeView()