Jelajahi Sumber

HackStudio: Allow moving widgets around using the CursorTool

You can now move the widgets around, either by themselves or in group
selections, by dragging them with the cursor tool. :^)
Andreas Kling 5 tahun lalu
induk
melakukan
567769eb2f

+ 59 - 7
DevTools/HackStudio/CursorTool.cpp

@@ -8,11 +8,27 @@ void CursorTool::on_mousedown(GMouseEvent& event)
     dbg() << "CursorTool::on_mousedown";
     dbg() << "CursorTool::on_mousedown";
     auto& form_widget = m_editor.form_widget();
     auto& form_widget = m_editor.form_widget();
     auto result = form_widget.hit_test(event.position(), GWidget::ShouldRespectGreediness::No);
     auto result = form_widget.hit_test(event.position(), GWidget::ShouldRespectGreediness::No);
-    if (result.widget && result.widget != &form_widget) {
-        if (event.modifiers() & Mod_Ctrl)
-            m_editor.selection().toggle(*result.widget);
-        else
-            m_editor.selection().set(*result.widget);
+
+    if (event.button() == GMouseButton::Left) {
+        if (result.widget && result.widget != &form_widget) {
+            if (event.modifiers() & Mod_Ctrl) {
+                m_editor.selection().toggle(*result.widget);
+            } else if (!event.modifiers()) {
+                if (!m_editor.selection().contains(*result.widget)) {
+                    dbg() << "Selection didn't contain " << *result.widget << ", making it the only selected one";
+                    m_editor.selection().set(*result.widget);
+                }
+
+                m_drag_origin = event.position();
+                m_positions_before_drag.clear();
+                m_editor.selection().for_each([&](auto& widget) {
+                    m_positions_before_drag.set(&widget, widget.relative_position());
+                    return IterationDecision::Continue;
+                });
+            }
+        } else {
+            m_editor.selection().clear();
+        }
         // FIXME: Do we need to update any part of the FormEditorWidget outside the FormWidget?
         // FIXME: Do we need to update any part of the FormEditorWidget outside the FormWidget?
         form_widget.update();
         form_widget.update();
     }
     }
@@ -20,12 +36,48 @@ void CursorTool::on_mousedown(GMouseEvent& event)
 
 
 void CursorTool::on_mouseup(GMouseEvent& event)
 void CursorTool::on_mouseup(GMouseEvent& event)
 {
 {
-    (void)event;
     dbg() << "CursorTool::on_mouseup";
     dbg() << "CursorTool::on_mouseup";
+    if (event.button() == GMouseButton::Left) {
+        auto& form_widget = m_editor.form_widget();
+        auto result = form_widget.hit_test(event.position(), GWidget::ShouldRespectGreediness::No);
+        if (!m_dragging && !(event.modifiers() & Mod_Ctrl)) {
+            if (result.widget && result.widget != &form_widget) {
+                m_editor.selection().set(*result.widget);
+                // FIXME: Do we need to update any part of the FormEditorWidget outside the FormWidget?
+                form_widget.update();
+            }
+        }
+        m_dragging = false;
+    }
 }
 }
 
 
 void CursorTool::on_mousemove(GMouseEvent& event)
 void CursorTool::on_mousemove(GMouseEvent& event)
 {
 {
-    (void)event;
     dbg() << "CursorTool::on_mousemove";
     dbg() << "CursorTool::on_mousemove";
+
+    if (!m_dragging && event.buttons() & GMouseButton::Left && event.position() != m_drag_origin) {
+        auto& form_widget = m_editor.form_widget();
+        auto result = form_widget.hit_test(event.position(), GWidget::ShouldRespectGreediness::No);
+        if (result.widget && result.widget != &form_widget) {
+            if (!m_editor.selection().contains(*result.widget)) {
+                m_editor.selection().set(*result.widget);
+                // FIXME: Do we need to update any part of the FormEditorWidget outside the FormWidget?
+                form_widget.update();
+            }
+        }
+        m_dragging = true;
+    }
+
+    if (m_dragging) {
+        auto movement_delta = event.position() - m_drag_origin;
+        m_editor.selection().for_each([&](auto& widget) {
+            auto new_rect = widget.relative_rect();
+            new_rect.set_location(m_positions_before_drag.get(&widget).value_or({}).translated(movement_delta));
+            new_rect.set_x(new_rect.x() - (new_rect.x() % m_editor.form_widget().grid_size()));
+            new_rect.set_y(new_rect.y() - (new_rect.y() % m_editor.form_widget().grid_size()));
+            widget.set_relative_rect(new_rect);
+            return IterationDecision::Continue;
+        });
+        return;
+    }
 }
 }

+ 8 - 0
DevTools/HackStudio/CursorTool.h

@@ -1,6 +1,10 @@
 #pragma once
 #pragma once
 
 
 #include "Tool.h"
 #include "Tool.h"
+#include <AK/HashMap.h>
+#include <LibDraw/Point.h>
+
+class GWidget;
 
 
 class CursorTool final : public Tool {
 class CursorTool final : public Tool {
 public:
 public:
@@ -15,4 +19,8 @@ private:
     virtual void on_mousedown(GMouseEvent&) override;
     virtual void on_mousedown(GMouseEvent&) override;
     virtual void on_mouseup(GMouseEvent&) override;
     virtual void on_mouseup(GMouseEvent&) override;
     virtual void on_mousemove(GMouseEvent&) override;
     virtual void on_mousemove(GMouseEvent&) override;
+
+    Point m_drag_origin;
+    HashMap<GWidget*, Point> m_positions_before_drag;
+    bool m_dragging { false };
 };
 };

+ 9 - 0
DevTools/HackStudio/FormEditorWidget.h

@@ -60,6 +60,15 @@ public:
             m_widgets.clear();
             m_widgets.clear();
         }
         }
 
 
+        template<typename Callback>
+        void for_each(Callback callback)
+        {
+            for (auto& it : m_widgets) {
+                if (callback(*it) == IterationDecision::Break)
+                    break;
+            }
+        }
+
         WidgetSelection() {}
         WidgetSelection() {}
     private:
     private:
         HashTable<GWidget*> m_widgets;
         HashTable<GWidget*> m_widgets;

+ 3 - 1
DevTools/HackStudio/FormWidget.h

@@ -12,6 +12,9 @@ public:
     FormEditorWidget& editor();
     FormEditorWidget& editor();
     const FormEditorWidget& editor() const;
     const FormEditorWidget& editor() const;
 
 
+    // FIXME: This should be an app-wide preference instead.
+    int grid_size() const { return m_grid_size; }
+
 private:
 private:
     virtual void paint_event(GPaintEvent&) override;
     virtual void paint_event(GPaintEvent&) override;
     virtual void second_paint_event(GPaintEvent&) override;
     virtual void second_paint_event(GPaintEvent&) override;
@@ -21,6 +24,5 @@ private:
 
 
     explicit FormWidget(FormEditorWidget& parent);
     explicit FormWidget(FormEditorWidget& parent);
 
 
-    // FIXME: This should be an app-wide preference instead.
     int m_grid_size { 5 };
     int m_grid_size { 5 };
 };
 };