mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-07 00:19:27 +00:00
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:
parent
12ec55ee74
commit
d02238af48
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue