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 };