diff --git a/Applications/VisualBuilder/.gitignore b/Applications/VisualBuilder/.gitignore index bc36e52322..f1b8cdb6c9 100644 --- a/Applications/VisualBuilder/.gitignore +++ b/Applications/VisualBuilder/.gitignore @@ -1,3 +1,3 @@ *.o *.d -InterfaceEditor +VisualBuilder diff --git a/Applications/VisualBuilder/VBForm.cpp b/Applications/VisualBuilder/VBForm.cpp index 5f40d8263e..4c50a9e8e0 100644 --- a/Applications/VisualBuilder/VBForm.cpp +++ b/Applications/VisualBuilder/VBForm.cpp @@ -1,4 +1,5 @@ #include "VBForm.h" +#include "VBWidget.h" #include VBForm::VBForm(const String& name, GWidget* parent) @@ -6,6 +7,14 @@ VBForm::VBForm(const String& name, GWidget* parent) { set_fill_with_background_color(true); set_background_color(Color::LightGray); + + auto box1 = VBWidget::create(*this); + box1->set_rect({ 10, 10, 61, 41 }); + m_widgets.append(move(box1)); + + auto box2 = VBWidget::create(*this); + box2->set_rect({ 100, 100, 161, 141 }); + m_widgets.append(move(box2)); } VBForm::~VBForm() @@ -22,4 +31,61 @@ void VBForm::paint_event(GPaintEvent& event) painter.set_pixel({ x, y }, Color::Black); } } + + for (auto& widget : m_widgets) { + widget->paint(painter); + } +} + +bool VBForm::is_selected(const VBWidget& widget) const +{ + return &widget == m_selected_widget.ptr(); +} + +VBWidget* VBForm::widget_at(const Point& position) +{ + for (int i = m_widgets.size() - 1; i >= 0; --i) { + auto& widget = *m_widgets[i]; + if (widget.rect().contains(position)) + return &widget; + } + return nullptr; +} + +void VBForm::mousedown_event(GMouseEvent& event) +{ + auto* widget = widget_at(event.position()); + if (!widget) { + if (m_selected_widget) { + m_selected_widget = nullptr; + update(); + } + return; + } + if (event.button() == GMouseButton::Left) { + m_selected_widget = widget->make_weak_ptr(); + m_transform_event_origin = event.position(); + m_transform_widget_origin_rect = widget->rect(); + update(); + } +} + +void VBForm::mousemove_event(GMouseEvent& event) +{ + if (event.buttons() & GMouseButton::Left && m_selected_widget) { + auto delta = event.position() - m_transform_event_origin; + auto new_rect = m_transform_widget_origin_rect.translated(delta); + new_rect.set_x(new_rect.x() - (new_rect.x() % m_grid_size)); + new_rect.set_y(new_rect.y() - (new_rect.y() % m_grid_size)); + m_selected_widget->set_rect(new_rect); + update(); + } +} + +void VBForm::mouseup_event(GMouseEvent& event) +{ + if (event.button() == GMouseButton::Left) { + m_transform_event_origin = { }; + m_transform_widget_origin_rect = { }; + } } diff --git a/Applications/VisualBuilder/VBForm.h b/Applications/VisualBuilder/VBForm.h index 64bc69dd9e..64e2d168ed 100644 --- a/Applications/VisualBuilder/VBForm.h +++ b/Applications/VisualBuilder/VBForm.h @@ -9,12 +9,24 @@ public: explicit VBForm(const String& name, GWidget* parent = nullptr); virtual ~VBForm() override; + bool is_selected(const VBWidget&) const; + VBWidget* widget_at(const Point&); + + void set_should_snap_to_grip(bool snap) { m_should_snap_to_grid = snap; } + bool should_snap_to_grid() const { return m_should_snap_to_grid; } + protected: virtual void paint_event(GPaintEvent&) override; + virtual void mousedown_event(GMouseEvent&) override; + virtual void mousemove_event(GMouseEvent&) override; + virtual void mouseup_event(GMouseEvent&) override; private: String m_name; int m_grid_size { 5 }; - - Vector m_widgets; + bool m_should_snap_to_grid { true }; + Vector> m_widgets; + WeakPtr m_selected_widget; + Point m_transform_event_origin; + Rect m_transform_widget_origin_rect; }; diff --git a/Applications/VisualBuilder/VBWidget.cpp b/Applications/VisualBuilder/VBWidget.cpp index e69de29bb2..75359f76ef 100644 --- a/Applications/VisualBuilder/VBWidget.cpp +++ b/Applications/VisualBuilder/VBWidget.cpp @@ -0,0 +1,55 @@ +#include "VBWidget.h" +#include "VBForm.h" +#include + +VBWidget::VBWidget(VBForm& form) + : m_form(form) +{ +} + +VBWidget::~VBWidget() +{ +} + +bool VBWidget::is_selected() const +{ + return m_form.is_selected(*this); +} + +Rect VBWidget::grabber_rect(Direction direction) const +{ + int grabber_size = 5; + int half_grabber_size = grabber_size / 2; + switch (direction) { + case Direction::Left: + return { m_rect.x() - half_grabber_size, m_rect.center().y() - half_grabber_size, grabber_size, grabber_size }; + case Direction::UpLeft: + return { m_rect.x() - half_grabber_size, m_rect.y() - half_grabber_size, grabber_size, grabber_size }; + case Direction::Up: + return { m_rect.center().x() - half_grabber_size, m_rect.y() - half_grabber_size, grabber_size, grabber_size }; + case Direction::UpRight: + return { m_rect.right() - half_grabber_size, m_rect.y() - half_grabber_size, grabber_size, grabber_size }; + case Direction::Right: + return { m_rect.right() - half_grabber_size, m_rect.center().y() - half_grabber_size, grabber_size, grabber_size }; + case Direction::DownLeft: + return { m_rect.x() - half_grabber_size, m_rect.bottom() - half_grabber_size, grabber_size, grabber_size }; + case Direction::Down: + return { m_rect.center().x() - half_grabber_size, m_rect.bottom() - half_grabber_size, grabber_size, grabber_size }; + case Direction::DownRight: + return { m_rect.right() - half_grabber_size, m_rect.bottom() - half_grabber_size, grabber_size, grabber_size }; + default: + ASSERT_NOT_REACHED(); + } +} + +void VBWidget::paint(GPainter& painter) +{ + painter.fill_rect(m_rect, Color::White); + painter.draw_rect(m_rect, Color::Black); + + if (is_selected()) { + for_each_direction([&] (Direction direction) { + painter.fill_rect(grabber_rect(direction), Color::Black); + }); + } +} diff --git a/Applications/VisualBuilder/VBWidget.h b/Applications/VisualBuilder/VBWidget.h index 231336621c..6ab7a1011f 100644 --- a/Applications/VisualBuilder/VBWidget.h +++ b/Applications/VisualBuilder/VBWidget.h @@ -1,15 +1,44 @@ #pragma once #include +#include +#include +#include -class VBWidget { +class GPainter; +class VBForm; + +enum class Direction { None, Left, UpLeft, Up, UpRight, Right, DownRight, Down, DownLeft }; +template +inline void for_each_direction(Callback callback) +{ + callback(Direction::Left); + callback(Direction::UpLeft); + callback(Direction::Up); + callback(Direction::UpRight); + callback(Direction::Right); + callback(Direction::DownRight); + callback(Direction::Down); + callback(Direction::DownLeft); +} + +class VBWidget : public Retainable, public Weakable { public: - VBWidget(); + static Retained create(VBForm& form) { return adopt(*new VBWidget(form)); } virtual ~VBWidget(); + bool is_selected() const; + Rect rect() const { return m_rect; } void set_rect(const Rect& rect) { m_rect = rect; } + Rect grabber_rect(Direction) const; + + void paint(GPainter&); + private: + VBWidget(VBForm&); + + VBForm& m_form; Rect m_rect; }; diff --git a/Applications/VisualBuilder/VisualBuilder b/Applications/VisualBuilder/VisualBuilder deleted file mode 100755 index 4381e1052a..0000000000 Binary files a/Applications/VisualBuilder/VisualBuilder and /dev/null differ