diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-04-19 22:46:16 +0200 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-04-19 22:46:16 +0200 |
commit | 440700b4cba12514f07b7247631ca66ec281c9d8 (patch) | |
tree | e815940cb04b390b4c6681f3435af6636b5085e8 | |
parent | e0019541934e6b96ac05a4d32d7a29d45cbb575c (diff) | |
download | serenity-440700b4cba12514f07b7247631ca66ec281c9d8.zip |
VisualBuilder: Multiple-widget selection support.
This is pretty damn nice, now I can move and resize entire groups of widgets
together. Diagonal group resizing feels a bit strange but I wasn't expecting
it not to. :^)
-rw-r--r-- | Applications/VisualBuilder/VBForm.cpp | 155 | ||||
-rw-r--r-- | Applications/VisualBuilder/VBForm.h | 11 | ||||
-rw-r--r-- | Applications/VisualBuilder/VBWidget.cpp | 7 | ||||
-rw-r--r-- | Applications/VisualBuilder/VBWidget.h | 4 | ||||
-rw-r--r-- | Applications/VisualBuilder/VBWidgetPropertyModel.cpp | 1 |
5 files changed, 123 insertions, 55 deletions
diff --git a/Applications/VisualBuilder/VBForm.cpp b/Applications/VisualBuilder/VBForm.cpp index 3b9c638d28..a0e4ae827d 100644 --- a/Applications/VisualBuilder/VBForm.cpp +++ b/Applications/VisualBuilder/VBForm.cpp @@ -37,12 +37,15 @@ VBForm::VBForm(const String& name, GWidget* parent) m_context_menu = make<GMenu>("Context menu"); m_context_menu->add_action(GAction::create("Move to front", [this] (auto&) { - if (m_selected_widget) - m_selected_widget->gwidget()->move_to_front(); + if (auto* widget = single_selected_widget()) + widget->gwidget()->move_to_front(); })); m_context_menu->add_action(GAction::create("Move to back", [this] (auto&) { - if (m_selected_widget) - m_selected_widget->gwidget()->move_to_back(); + if (auto* widget = single_selected_widget()) + widget->gwidget()->move_to_back(); + })); + m_context_menu->add_action(GAction::create("Delete", [this] (auto&) { + delete_selected_widgets(); })); } @@ -91,7 +94,8 @@ void VBForm::second_paint_event(GPaintEvent& event) bool VBForm::is_selected(const VBWidget& widget) const { - return &widget == m_selected_widget; + // FIXME: Fix HashTable and remove this const_cast. + return m_selected_widgets.contains(const_cast<VBWidget*>(&widget)); } VBWidget* VBForm::widget_at(const Point& position) @@ -105,100 +109,130 @@ VBWidget* VBForm::widget_at(const Point& position) void VBForm::grabber_mousedown_event(GMouseEvent& event, VBWidget& widget, Direction grabber) { m_transform_event_origin = event.position(); - m_transform_widget_origin_rect = widget.rect(); + for_each_selected_widget([] (auto& widget) { widget.capture_transform_origin_rect(); }); m_resize_direction = grabber; } void VBForm::keydown_event(GKeyEvent& event) { + if (event.key() == KeyCode::Key_Delete) { + delete_selected_widgets(); + return; + } if (event.key() == KeyCode::Key_Tab) { if (m_widgets.is_empty()) return; - if (!m_selected_widget) { - set_selected_widget(m_widgets.first()); + if (m_selected_widgets.is_empty()) { + set_single_selected_widget(m_widgets.first()); update(); return; } int selected_widget_index = 0; for (; selected_widget_index < m_widgets.size(); ++selected_widget_index) { - if (m_widgets[selected_widget_index] == m_selected_widget) + if (m_widgets[selected_widget_index] == *m_selected_widgets.begin()) break; } ++selected_widget_index; if (selected_widget_index == m_widgets.size()) selected_widget_index = 0; - set_selected_widget(m_widgets[selected_widget_index]); + set_single_selected_widget(m_widgets[selected_widget_index]); update(); + return; } - if (m_selected_widget) { + if (!m_selected_widgets.is_empty()) { switch (event.key()) { case KeyCode::Key_Up: update(); - m_selected_widget->gwidget()->move_by(0, -m_grid_size); + for_each_selected_widget([this] (auto& widget) { widget.gwidget()->move_by(0, -m_grid_size); }); break; case KeyCode::Key_Down: update(); - m_selected_widget->gwidget()->move_by(0, m_grid_size); + for_each_selected_widget([this] (auto& widget) { widget.gwidget()->move_by(0, m_grid_size); }); break; case KeyCode::Key_Left: update(); - m_selected_widget->gwidget()->move_by(-m_grid_size, 0); + for_each_selected_widget([this] (auto& widget) { widget.gwidget()->move_by(-m_grid_size, 0); }); break; case KeyCode::Key_Right: update(); - m_selected_widget->gwidget()->move_by(m_grid_size, 0); + for_each_selected_widget([this] (auto& widget) { widget.gwidget()->move_by(m_grid_size, 0); }); break; } return; } } -void VBForm::set_selected_widget(VBWidget* widget) +void VBForm::set_single_selected_widget(VBWidget* widget) { if (!widget) { - if (m_selected_widget) { - m_selected_widget = nullptr; - if (on_widget_selected) - on_widget_selected(nullptr); + if (!m_selected_widgets.is_empty()) { + m_selected_widgets.clear(); + on_widget_selected(nullptr); update(); } return; } - m_selected_widget = widget->make_weak_ptr(); - if (on_widget_selected) - on_widget_selected(widget); + m_selected_widgets.clear(); + m_selected_widgets.set(widget); + on_widget_selected(m_selected_widgets.size() == 1 ? widget : nullptr); + update(); +} + +void VBForm::add_to_selection(VBWidget& widget) +{ + m_selected_widgets.set(&widget); + update(); +} + +void VBForm::remove_from_selection(VBWidget& widget) +{ + m_selected_widgets.remove(&widget); update(); } void VBForm::mousedown_event(GMouseEvent& event) { - if (m_selected_widget && m_resize_direction == Direction::None) { - auto grabber = m_selected_widget->grabber_at(event.position()); - if (grabber != Direction::None) - return grabber_mousedown_event(event, *m_selected_widget, grabber); + if (m_resize_direction == Direction::None) { + bool hit_grabber = false; + for_each_selected_widget([&] (auto& widget) { + auto grabber = widget.grabber_at(event.position()); + if (grabber != Direction::None) { + hit_grabber = true; + return grabber_mousedown_event(event, widget, grabber); + } + }); + if (hit_grabber) + return; } auto* widget = widget_at(event.position()); if (!widget) { - set_selected_widget(nullptr); + set_single_selected_widget(nullptr); return; } if (event.button() == GMouseButton::Left || event.button() == GMouseButton::Right) { m_transform_event_origin = event.position(); - m_transform_widget_origin_rect = widget->rect(); - set_selected_widget(widget); + if (event.modifiers() == Mod_Ctrl) + remove_from_selection(*widget); + else if (event.modifiers() == Mod_Shift) + add_to_selection(*widget); + else if (!m_selected_widgets.contains(widget)) + set_single_selected_widget(widget); + for_each_selected_widget([] (auto& widget) { widget.capture_transform_origin_rect(); }); } } void VBForm::mousemove_event(GMouseEvent& event) { - if (event.buttons() & GMouseButton::Left && m_selected_widget) { + if (event.buttons() & GMouseButton::Left) { if (m_resize_direction == Direction::None) { - 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(); + auto delta = event.position() - m_transform_event_origin; + for_each_selected_widget([&] (auto& widget) { + auto new_rect = widget.transform_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)); + widget.set_rect(new_rect); + }); return; } int diff_x = event.x() - m_transform_event_origin.x(); @@ -248,21 +282,20 @@ void VBForm::mousemove_event(GMouseEvent& event) ASSERT_NOT_REACHED(); } - auto new_rect = m_transform_widget_origin_rect; - Size minimum_size { 5, 5 }; - - new_rect.set_x(new_rect.x() + change_x); - new_rect.set_y(new_rect.y() + change_y); - new_rect.set_width(max(minimum_size.width(), new_rect.width() + change_w)); - new_rect.set_height(max(minimum_size.height(), new_rect.height() + change_h)); - - 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)); - new_rect.set_width(new_rect.width() - (new_rect.width() % m_grid_size) + 1); - new_rect.set_height(new_rect.height() - (new_rect.height() % m_grid_size) + 1); - - m_selected_widget->set_rect(new_rect); update(); + for_each_selected_widget([&] (auto& widget) { + auto new_rect = widget.transform_origin_rect(); + Size minimum_size { 5, 5 }; + new_rect.set_x(new_rect.x() + change_x); + new_rect.set_y(new_rect.y() + change_y); + new_rect.set_width(max(minimum_size.width(), new_rect.width() + change_w)); + new_rect.set_height(max(minimum_size.height(), new_rect.height() + change_h)); + 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)); + new_rect.set_width(new_rect.width() - (new_rect.width() % m_grid_size) + 1); + new_rect.set_height(new_rect.height() - (new_rect.height() % m_grid_size) + 1); + widget.set_rect(new_rect); + }); } } @@ -270,7 +303,27 @@ void VBForm::mouseup_event(GMouseEvent& event) { if (event.button() == GMouseButton::Left) { m_transform_event_origin = { }; - m_transform_widget_origin_rect = { }; m_resize_direction = Direction::None; } } + +void VBForm::delete_selected_widgets() +{ + for_each_selected_widget([this] (auto& widget) { + m_widgets.remove_first_matching([&widget] (auto& entry) { return entry == &widget; } ); + }); +} + +template<typename Callback> +void VBForm::for_each_selected_widget(Callback callback) +{ + for (auto& widget : m_selected_widgets) + callback(*widget); +} + +VBWidget* VBForm::single_selected_widget() +{ + if (m_selected_widgets.size() != 1) + return nullptr; + return *m_selected_widgets.begin(); +} diff --git a/Applications/VisualBuilder/VBForm.h b/Applications/VisualBuilder/VBForm.h index e28f133a71..008272965f 100644 --- a/Applications/VisualBuilder/VBForm.h +++ b/Applications/VisualBuilder/VBForm.h @@ -36,16 +36,21 @@ protected: private: void grabber_mousedown_event(GMouseEvent&, VBWidget&, Direction grabber); - void set_selected_widget(VBWidget*); + void set_single_selected_widget(VBWidget*); + void add_to_selection(VBWidget&); + void remove_from_selection(VBWidget&); + void delete_selected_widgets(); + template<typename Callback> void for_each_selected_widget(Callback); + + VBWidget* single_selected_widget(); String m_name; int m_grid_size { 5 }; bool m_should_snap_to_grid { true }; Vector<Retained<VBWidget>> m_widgets; HashMap<GWidget*, VBWidget*> m_gwidget_map; - WeakPtr<VBWidget> m_selected_widget; + HashTable<VBWidget*> m_selected_widgets; Point m_transform_event_origin; - Rect m_transform_widget_origin_rect; Point m_next_insertion_position; Direction m_resize_direction { Direction::None }; OwnPtr<GMenu> m_context_menu; diff --git a/Applications/VisualBuilder/VBWidget.cpp b/Applications/VisualBuilder/VBWidget.cpp index 0ea3a8f77e..cb52e184a5 100644 --- a/Applications/VisualBuilder/VBWidget.cpp +++ b/Applications/VisualBuilder/VBWidget.cpp @@ -25,6 +25,8 @@ VBWidget::VBWidget(VBWidgetType type, VBForm& form) VBWidget::~VBWidget() { m_form.m_gwidget_map.remove(m_gwidget); + m_form.m_selected_widgets.remove(this); + delete m_gwidget; } Rect VBWidget::rect() const @@ -174,3 +176,8 @@ void VBWidget::property_did_change() { m_form.update(); } + +void VBWidget::capture_transform_origin_rect() +{ + m_transform_origin_rect = rect(); +} diff --git a/Applications/VisualBuilder/VBWidget.h b/Applications/VisualBuilder/VBWidget.h index b865296209..afd240b081 100644 --- a/Applications/VisualBuilder/VBWidget.h +++ b/Applications/VisualBuilder/VBWidget.h @@ -56,6 +56,9 @@ public: void property_did_change(); + Rect transform_origin_rect() const { return m_transform_origin_rect; } + void capture_transform_origin_rect(); + private: VBWidget(VBWidgetType, VBForm&); @@ -66,4 +69,5 @@ private: GWidget* m_gwidget { nullptr }; Vector<OwnPtr<VBProperty>> m_properties; Retained<VBWidgetPropertyModel> m_property_model; + Rect m_transform_origin_rect; }; diff --git a/Applications/VisualBuilder/VBWidgetPropertyModel.cpp b/Applications/VisualBuilder/VBWidgetPropertyModel.cpp index 46d9f90575..0a4935c660 100644 --- a/Applications/VisualBuilder/VBWidgetPropertyModel.cpp +++ b/Applications/VisualBuilder/VBWidgetPropertyModel.cpp @@ -10,7 +10,6 @@ VBWidgetPropertyModel::VBWidgetPropertyModel(VBWidget& widget) VBWidgetPropertyModel::~VBWidgetPropertyModel() { - ASSERT_NOT_REACHED(); } int VBWidgetPropertyModel::row_count(const GModelIndex&) const |