Browse Source

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.
Andreas Kling 5 years ago
parent
commit
d5f735ecec

+ 2 - 0
DevTools/HackStudio/CursorTool.cpp

@@ -1,6 +1,7 @@
 #include "CursorTool.h"
 #include "FormEditorWidget.h"
 #include "FormWidget.h"
+#include "WidgetTreeModel.h"
 #include <AK/LogStream.h>
 
 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;
     }
 }

+ 7 - 0
DevTools/HackStudio/FormEditorWidget.cpp

@@ -1,6 +1,7 @@
 #include "FormEditorWidget.h"
 #include "CursorTool.h"
 #include "FormWidget.h"
+#include "WidgetTreeModel.h"
 #include <LibGUI/GPainter.h>
 
 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> tool)
     m_tool = move(tool);
     m_tool->attach();
 }
+
+WidgetTreeModel& FormEditorWidget::model()
+{
+    return *m_widget_tree_model;
+}

+ 5 - 0
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<Tool>);
 
+    WidgetTreeModel& model();
+
     class WidgetSelection {
     public:
         bool is_empty() const
@@ -70,6 +73,7 @@ public:
         }
 
         WidgetSelection() {}
+
     private:
         HashTable<GWidget*> m_widgets;
     };
@@ -82,6 +86,7 @@ private:
     explicit FormEditorWidget(GWidget* parent);
 
     RefPtr<FormWidget> m_form_widget;
+    RefPtr<WidgetTreeModel> m_widget_tree_model;
     NonnullOwnPtr<Tool> m_tool;
     WidgetSelection m_selection;
 };

+ 1 - 0
DevTools/HackStudio/Makefile

@@ -15,6 +15,7 @@ OBJS = \
     Tool.o \
     CursorTool.o \
     WidgetTool.o \
+    WidgetTreeModel.o \
     main.o
 
 APP = HackStudio

+ 77 - 0
DevTools/HackStudio/WidgetTreeModel.cpp

@@ -0,0 +1,77 @@
+#include "WidgetTreeModel.h"
+#include <AK/StringBuilder.h>
+#include <LibGUI/GWidget.h>
+#include <stdio.h>
+
+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<GWidget*>(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<GWidget*>(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<GWidget*>(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<GWidget*>(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();
+}

+ 23 - 0
DevTools/HackStudio/WidgetTreeModel.h

@@ -0,0 +1,23 @@
+#pragma once
+
+#include <LibGUI/GModel.h>
+#include <LibGUI/GPainter.h>
+
+class WidgetTreeModel final : public GModel {
+public:
+    static NonnullRefPtr<WidgetTreeModel> 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<GWidget> m_root;
+    GIcon m_widget_icon;
+};

+ 6 - 1
DevTools/HackStudio/main.cpp

@@ -9,6 +9,7 @@
 #include "Project.h"
 #include "TerminalWrapper.h"
 #include "WidgetTool.h"
+#include "WidgetTreeModel.h"
 #include <LibCore/CFile.h>
 #include <LibGUI/GAboutDialog.h>
 #include <LibGUI/GAction.h>
@@ -149,6 +150,7 @@ int main(int argc, char** argv)
             g_form_editor_widget->set_tool(make<WidgetTool>(*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);