From 0e6b27362045c8f6511cec27c2e2108dd95c105a Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 18 Apr 2019 22:27:14 +0200 Subject: [PATCH] LibGUI: Start working on GTableView inline editing. This is pretty shaky still, but the basic idea is that you subclass GModel and return true for editable indices. The table view also needs to have its editable flag set. --- .../VisualBuilder/VBPropertiesWindow.cpp | 1 + .../VisualBuilder/VBWidgetPropertyModel.cpp | 16 +++++ .../VisualBuilder/VBWidgetPropertyModel.h | 2 + Kernel/init.cpp | 4 +- LibGUI/GAbstractView.cpp | 2 + LibGUI/GAbstractView.h | 9 +++ LibGUI/GModel.h | 2 + LibGUI/GTableView.cpp | 60 +++++++++++++++++-- LibGUI/GTableView.h | 5 ++ 9 files changed, 95 insertions(+), 6 deletions(-) diff --git a/Applications/VisualBuilder/VBPropertiesWindow.cpp b/Applications/VisualBuilder/VBPropertiesWindow.cpp index 5b52afc448..a2a9cc082c 100644 --- a/Applications/VisualBuilder/VBPropertiesWindow.cpp +++ b/Applications/VisualBuilder/VBPropertiesWindow.cpp @@ -19,6 +19,7 @@ VBPropertiesWindow::VBPropertiesWindow() m_text_box->set_preferred_size({ 0, 21 }); m_table_view = new GTableView(widget); + m_table_view->set_editable(true); } VBPropertiesWindow::~VBPropertiesWindow() diff --git a/Applications/VisualBuilder/VBWidgetPropertyModel.cpp b/Applications/VisualBuilder/VBWidgetPropertyModel.cpp index 9e2d61c819..922f568a95 100644 --- a/Applications/VisualBuilder/VBWidgetPropertyModel.cpp +++ b/Applications/VisualBuilder/VBWidgetPropertyModel.cpp @@ -52,3 +52,19 @@ GVariant VBWidgetPropertyModel::data(const GModelIndex& index, Role role) const } return { }; } + +void VBWidgetPropertyModel::set_data(const GModelIndex& index, const GVariant& value) +{ + ASSERT(index.column() == Column::Value); + auto& property = *m_widget.m_properties[index.row()]; + ASSERT(!property.is_readonly()); + property.set_value(value); +} + +bool VBWidgetPropertyModel::is_editable(const GModelIndex& index) const +{ + if (index.column() != Column::Value) + return false; + auto& property = *m_widget.m_properties[index.row()]; + return !property.is_readonly(); +} diff --git a/Applications/VisualBuilder/VBWidgetPropertyModel.h b/Applications/VisualBuilder/VBWidgetPropertyModel.h index 0d7ee5909b..b84bffc697 100644 --- a/Applications/VisualBuilder/VBWidgetPropertyModel.h +++ b/Applications/VisualBuilder/VBWidgetPropertyModel.h @@ -22,6 +22,8 @@ public: virtual ColumnMetadata column_metadata(int column) const override; virtual GVariant data(const GModelIndex&, Role = Role::Display) const override; virtual void update() override { did_update(); } + virtual bool is_editable(const GModelIndex&) const override; + virtual void set_data(const GModelIndex&, const GVariant&) override; private: explicit VBWidgetPropertyModel(VBWidget&); diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 0081b88ece..f9636cfdef 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -26,14 +26,14 @@ #include #include -#define SPAWN_TERMINAL +//#define SPAWN_TERMINAL //#define SPAWN_LAUNCHER //#define SPAWN_GUITEST2 //#define SPAWN_FILE_MANAGER //#define SPAWN_PROCESS_MANAGER //#define SPAWN_TEXT_EDITOR //#define SPAWN_FONTEDITOR -//#define SPAWN_VISUAL_BUILDER +#define SPAWN_VISUAL_BUILDER //#define SPAWN_MULTIPLE_SHELLS //#define STRESS_TEST_SPAWNING diff --git a/LibGUI/GAbstractView.cpp b/LibGUI/GAbstractView.cpp index 3db6b00852..0187225368 100644 --- a/LibGUI/GAbstractView.cpp +++ b/LibGUI/GAbstractView.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include GAbstractView::GAbstractView(GWidget* parent) @@ -11,6 +12,7 @@ GAbstractView::GAbstractView(GWidget* parent) GAbstractView::~GAbstractView() { + delete m_edit_widget; } void GAbstractView::set_model(RetainPtr&& model) diff --git a/LibGUI/GAbstractView.h b/LibGUI/GAbstractView.h index 5048d26048..f0c33c5bd9 100644 --- a/LibGUI/GAbstractView.h +++ b/LibGUI/GAbstractView.h @@ -4,6 +4,8 @@ #include #include +class GTextBox; + class GAbstractView : public GScrollableWidget { friend class GModel; public: @@ -14,6 +16,9 @@ public: GModel* model() { return m_model.ptr(); } const GModel* model() const { return m_model.ptr(); } + bool is_editable() const { return m_editable; } + void set_editable(bool editable) { m_editable = editable; } + virtual bool accepts_focus() const override { return true; } virtual void did_update_model(); virtual void did_update_selection(); @@ -25,6 +30,10 @@ public: protected: virtual void model_notification(const GModelNotification&); + bool m_editable { false }; + GModelIndex m_edit_index; + GTextBox* m_edit_widget { nullptr }; + private: RetainPtr m_model; }; diff --git a/LibGUI/GModel.h b/LibGUI/GModel.h index 1d355e4f27..25d80b35fb 100644 --- a/LibGUI/GModel.h +++ b/LibGUI/GModel.h @@ -57,6 +57,8 @@ public: virtual GModelIndex index(int row, int column = 0, const GModelIndex& = GModelIndex()) const { return create_index(row, column); } virtual void activate(const GModelIndex&) { } virtual GModelIndex sibling(int row, int column, const GModelIndex& parent) const; + virtual bool is_editable(const GModelIndex&) const { return false; } + virtual void set_data(const GModelIndex&, const GVariant&) { } bool is_valid(const GModelIndex& index) const { diff --git a/LibGUI/GTableView.cpp b/LibGUI/GTableView.cpp index a0bf2af1b7..85b54cc2b6 100644 --- a/LibGUI/GTableView.cpp +++ b/LibGUI/GTableView.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include GTableView::GTableView(GWidget* parent) @@ -38,6 +39,21 @@ void GTableView::did_update_model() update(); } +Rect GTableView::cell_content_rect(int row, int column) const +{ + auto row_rect = this->row_rect(row); + int x = 0; + for (int i = 0; i < column; ++i) + x += column_width(i) + horizontal_padding() * 2; + + return { horizontal_padding() + row_rect.x() + x, row_rect.y(), column_width(column), item_height() }; +} + +Rect GTableView::cell_content_rect(const GModelIndex& index) const +{ + return cell_content_rect(index.row(), index.column()); +} + Rect GTableView::row_rect(int item_index) const { return { 0, header_height() + (item_index * item_height()), max(content_size().width(), width()), item_height() }; @@ -87,9 +103,13 @@ void GTableView::mousedown_event(GMouseEvent& event) if (event.button() == GMouseButton::Left) { 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(model()->index(i, 0)); + for (int row = 0, row_count = model()->row_count(); row < row_count; ++row) { + if (!row_rect(row).contains(adjusted_position)) + continue; + for (int column = 0, column_count = model()->column_count(); column < column_count; ++column) { + if (!cell_content_rect(row, column).contains(adjusted_position)) + continue; + model()->set_selected_index(model()->index(row, column)); update(); return; } @@ -300,6 +320,38 @@ void GTableView::doubleclick_event(GMouseEvent& event) return; if (event.button() == GMouseButton::Left) { mousedown_event(event); - model()->activate(model()->selected_index()); + if (is_editable()) + begin_editing(model()->selected_index()); + else + model()->activate(model()->selected_index()); } } + +void GTableView::begin_editing(const GModelIndex& index) +{ + ASSERT(is_editable()); + ASSERT(model()); + if (m_edit_index == index) + return; + if (!model()->is_editable(index)) + return; + if (m_edit_widget) + delete m_edit_widget; + m_edit_index = index; + m_edit_widget = new GTextBox(this); + m_edit_widget->set_text(model()->data(index, GModel::Role::Display).to_string()); + m_edit_widget->set_relative_rect(cell_content_rect(index)); + m_edit_widget->set_focus(true); + m_edit_widget->on_return_pressed = [this] { + ASSERT(model()); + model()->set_data(m_edit_index, m_edit_widget->text()); + stop_editing(); + }; +} + +void GTableView::stop_editing() +{ + m_edit_index = { }; + m_edit_widget->delete_later(); + m_edit_widget = nullptr; +} diff --git a/LibGUI/GTableView.h b/LibGUI/GTableView.h index ea75381056..3900e164a5 100644 --- a/LibGUI/GTableView.h +++ b/LibGUI/GTableView.h @@ -30,6 +30,9 @@ public: bool is_column_hidden(int) const; void set_column_hidden(int, bool); + void begin_editing(const GModelIndex&); + void stop_editing(); + virtual const char* class_name() const override { return "GTableView"; } private: @@ -45,6 +48,8 @@ private: Rect header_rect(int) const; int column_width(int) const; void update_content_size(); + Rect cell_content_rect(const GModelIndex&) const; + Rect cell_content_rect(int row, int column) const; Vector m_column_visibility; int m_horizontal_padding { 5 };