From d5f735ecec90e25d423f0f2bc1b5476e6d7c4196 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 11 Nov 2019 19:13:36 +0100 Subject: [PATCH] HackStudio: Show the edited form widget's widget tree in the tree view This patch introduces a simple WidgetTreeModel that models the widget tree inside of a given root GWidget. --- DevTools/HackStudio/CursorTool.cpp | 2 + DevTools/HackStudio/FormEditorWidget.cpp | 7 +++ DevTools/HackStudio/FormEditorWidget.h | 5 ++ DevTools/HackStudio/Makefile | 1 + DevTools/HackStudio/WidgetTreeModel.cpp | 77 ++++++++++++++++++++++++ DevTools/HackStudio/WidgetTreeModel.h | 23 +++++++ DevTools/HackStudio/main.cpp | 7 ++- 7 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 DevTools/HackStudio/WidgetTreeModel.cpp create mode 100644 DevTools/HackStudio/WidgetTreeModel.h diff --git a/DevTools/HackStudio/CursorTool.cpp b/DevTools/HackStudio/CursorTool.cpp index 643f9ef701..98742d448b 100644 --- a/DevTools/HackStudio/CursorTool.cpp +++ b/DevTools/HackStudio/CursorTool.cpp @@ -1,6 +1,7 @@ #include "CursorTool.h" #include "FormEditorWidget.h" #include "FormWidget.h" +#include "WidgetTreeModel.h" #include void CursorTool::on_mousedown(GMouseEvent& event) @@ -78,6 +79,7 @@ void CursorTool::on_mousemove(GMouseEvent& event) widget.set_relative_rect(new_rect); return IterationDecision::Continue; }); + m_editor.model().update(); return; } } diff --git a/DevTools/HackStudio/FormEditorWidget.cpp b/DevTools/HackStudio/FormEditorWidget.cpp index 894454b2d9..c1e382566b 100644 --- a/DevTools/HackStudio/FormEditorWidget.cpp +++ b/DevTools/HackStudio/FormEditorWidget.cpp @@ -1,6 +1,7 @@ #include "FormEditorWidget.h" #include "CursorTool.h" #include "FormWidget.h" +#include "WidgetTreeModel.h" #include FormEditorWidget::FormEditorWidget(GWidget* parent) @@ -15,6 +16,7 @@ FormEditorWidget::FormEditorWidget(GWidget* parent) set_frame_thickness(2); m_form_widget = FormWidget::construct(*this); + m_widget_tree_model = WidgetTreeModel::create(*m_form_widget); } FormEditorWidget::~FormEditorWidget() @@ -35,3 +37,8 @@ void FormEditorWidget::set_tool(NonnullOwnPtr tool) m_tool = move(tool); m_tool->attach(); } + +WidgetTreeModel& FormEditorWidget::model() +{ + return *m_widget_tree_model; +} diff --git a/DevTools/HackStudio/FormEditorWidget.h b/DevTools/HackStudio/FormEditorWidget.h index cc2d9e66cd..04758ab263 100644 --- a/DevTools/HackStudio/FormEditorWidget.h +++ b/DevTools/HackStudio/FormEditorWidget.h @@ -4,6 +4,7 @@ class FormWidget; class Tool; +class WidgetTreeModel; class FormEditorWidget final : public GScrollableWidget { C_OBJECT(FormEditorWidget) @@ -18,6 +19,8 @@ public: void set_tool(NonnullOwnPtr); + WidgetTreeModel& model(); + class WidgetSelection { public: bool is_empty() const @@ -70,6 +73,7 @@ public: } WidgetSelection() {} + private: HashTable m_widgets; }; @@ -82,6 +86,7 @@ private: explicit FormEditorWidget(GWidget* parent); RefPtr m_form_widget; + RefPtr m_widget_tree_model; NonnullOwnPtr m_tool; WidgetSelection m_selection; }; diff --git a/DevTools/HackStudio/Makefile b/DevTools/HackStudio/Makefile index ca2c4fabec..e4bc07f538 100644 --- a/DevTools/HackStudio/Makefile +++ b/DevTools/HackStudio/Makefile @@ -15,6 +15,7 @@ OBJS = \ Tool.o \ CursorTool.o \ WidgetTool.o \ + WidgetTreeModel.o \ main.o APP = HackStudio diff --git a/DevTools/HackStudio/WidgetTreeModel.cpp b/DevTools/HackStudio/WidgetTreeModel.cpp new file mode 100644 index 0000000000..a2740da185 --- /dev/null +++ b/DevTools/HackStudio/WidgetTreeModel.cpp @@ -0,0 +1,77 @@ +#include "WidgetTreeModel.h" +#include +#include +#include + +WidgetTreeModel::WidgetTreeModel(GWidget& root) + : m_root(root) +{ + m_widget_icon.set_bitmap_for_size(16, GraphicsBitmap::load_from_file("/res/icons/16x16/inspector-object.png")); +} + +WidgetTreeModel::~WidgetTreeModel() +{ +} + +GModelIndex WidgetTreeModel::index(int row, int column, const GModelIndex& parent) const +{ + if (!parent.is_valid()) { + return create_index(row, column, m_root.ptr()); + } + auto& parent_node = *static_cast(parent.internal_data()); + return create_index(row, column, parent_node.child_widgets().at(row)); +} + +GModelIndex WidgetTreeModel::parent_index(const GModelIndex& index) const +{ + if (!index.is_valid()) + return {}; + auto& widget = *static_cast(index.internal_data()); + if (&widget == m_root.ptr()) + return {}; + + if (widget.parent_widget() == m_root.ptr()) + return create_index(0, 0, m_root.ptr()); + + // Walk the grandparent's children to find the index of widget's parent in its parent. + // (This is needed to produce the row number of the GModelIndex corresponding to widget's parent.) + int grandparent_child_index = 0; + for (auto& grandparent_child : widget.parent_widget()->parent_widget()->child_widgets()) { + if (grandparent_child == widget.parent_widget()) + return create_index(grandparent_child_index, 0, widget.parent_widget()); + ++grandparent_child_index; + } + + ASSERT_NOT_REACHED(); + return {}; +} + +int WidgetTreeModel::row_count(const GModelIndex& index) const +{ + if (!index.is_valid()) + return 1; + auto& widget = *static_cast(index.internal_data()); + return widget.child_widgets().size(); +} + +int WidgetTreeModel::column_count(const GModelIndex&) const +{ + return 1; +} + +GVariant WidgetTreeModel::data(const GModelIndex& index, Role role) const +{ + auto* widget = static_cast(index.internal_data()); + if (role == Role::Icon) { + return m_widget_icon; + } + if (role == Role::Display) { + return String::format("%s (%s)", widget->class_name(), widget->relative_rect().to_string().characters()); + } + return {}; +} + +void WidgetTreeModel::update() +{ + did_update(); +} diff --git a/DevTools/HackStudio/WidgetTreeModel.h b/DevTools/HackStudio/WidgetTreeModel.h new file mode 100644 index 0000000000..10a81da3fa --- /dev/null +++ b/DevTools/HackStudio/WidgetTreeModel.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +class WidgetTreeModel final : public GModel { +public: + static NonnullRefPtr create(GWidget& root) { return adopt(*new WidgetTreeModel(root)); } + virtual ~WidgetTreeModel() override; + + virtual int row_count(const GModelIndex& = GModelIndex()) const override; + virtual int column_count(const GModelIndex& = GModelIndex()) const override; + virtual GVariant data(const GModelIndex&, Role = Role::Display) const override; + virtual GModelIndex index(int row, int column, const GModelIndex& parent = GModelIndex()) const override; + virtual GModelIndex parent_index(const GModelIndex&) const override; + virtual void update() override; + +private: + explicit WidgetTreeModel(GWidget&); + + NonnullRefPtr m_root; + GIcon m_widget_icon; +}; diff --git a/DevTools/HackStudio/main.cpp b/DevTools/HackStudio/main.cpp index 0391ee3dc3..e3f870d7d4 100644 --- a/DevTools/HackStudio/main.cpp +++ b/DevTools/HackStudio/main.cpp @@ -9,6 +9,7 @@ #include "Project.h" #include "TerminalWrapper.h" #include "WidgetTool.h" +#include "WidgetTreeModel.h" #include #include #include @@ -149,6 +150,7 @@ int main(int argc, char** argv) g_form_editor_widget->set_tool(make(*g_form_editor_widget, reg)); auto widget = reg.construct(&g_form_editor_widget->form_widget()); widget->set_relative_rect(30, 30, 30, 30); + g_form_editor_widget->model().update(); }); action->set_checkable(true); action->set_checked(false); @@ -177,7 +179,10 @@ int main(int argc, char** argv) wrapper->add_child(pane_widget); }; - add_properties_pane("Form widget tree:", GTreeView::construct(nullptr)); + auto form_widget_tree_view = GTreeView::construct(nullptr); + form_widget_tree_view->set_model(g_form_editor_widget->model()); + + add_properties_pane("Form widget tree:", form_widget_tree_view); add_properties_pane("Widget properties:", GTableView::construct(nullptr)); g_text_inner_splitter = GSplitter::construct(Orientation::Vertical, g_right_hand_stack);