From e9dac646cf6e2f02d757d9dd7e56dd1723be0b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9renger=20Dalle-Cort?= Date: Wed, 27 Nov 2024 21:52:21 -0500 Subject: [PATCH] refactor(GraphView): extract struct Selection.h 2/2 --- src/ndbl/gui/Action.h | 2 +- src/ndbl/gui/Event.h | 6 +-- src/ndbl/gui/FileView.cpp | 5 ++ src/ndbl/gui/GraphView.cpp | 98 +++++++++++++++--------------------- src/ndbl/gui/GraphView.h | 9 ++-- src/ndbl/gui/Nodable.cpp | 52 ++++++++----------- src/ndbl/gui/NodableView.cpp | 16 +++--- src/ndbl/gui/NodeView.cpp | 5 +- src/ndbl/gui/Selection.h | 79 +++++++++++------------------ 9 files changed, 115 insertions(+), 157 deletions(-) diff --git a/src/ndbl/gui/Action.h b/src/ndbl/gui/Action.h index e0edccd2d..e9febda4c 100644 --- a/src/ndbl/gui/Action.h +++ b/src/ndbl/gui/Action.h @@ -17,7 +17,7 @@ namespace ndbl using Action_ToggleFolding = Action; using Action_SelectNext = Action; using Action_ToggleIsolate = Action; - using Action_SelectionChange = Action; + using Action_SelectionChange = Action; using Action_MoveGraph = Action; // 2) Advanced actions (custom events) diff --git a/src/ndbl/gui/Event.h b/src/ndbl/gui/Event.h index 7f82cebd0..f104d3548 100644 --- a/src/ndbl/gui/Event.h +++ b/src/ndbl/gui/Event.h @@ -82,10 +82,10 @@ namespace ndbl struct EventPayload_SelectionChange { - Selection new_selection; - Selection old_selection; + GraphView* graph_view; + // Selection old_selection; was unused }; - using Event_SelectionChange = tools::Event; + using Event_GraphViewSelectionChanged = tools::Event; struct EventPayload_CreateNode { diff --git a/src/ndbl/gui/FileView.cpp b/src/ndbl/gui/FileView.cpp index f8fd6cf96..8da64cb79 100644 --- a/src/ndbl/gui/FileView.cpp +++ b/src/ndbl/gui/FileView.cpp @@ -57,6 +57,11 @@ void FileView::draw(float dt) // 1) Draw History Bar // 2) Draw Text and Graph Editors + clear_overlay(); + Condition condition_flags = m_file->graph().view()->selection().empty() + ? Condition_ENABLE_IF_HAS_NO_SELECTION + : Condition_ENABLE_IF_HAS_SELECTION; + refresh_overlay( condition_flags ); // 1) if (ImGui::IsMouseReleased(0)) diff --git a/src/ndbl/gui/GraphView.cpp b/src/ndbl/gui/GraphView.cpp index 6512c6fda..e43207cb1 100644 --- a/src/ndbl/gui/GraphView.cpp +++ b/src/ndbl/gui/GraphView.cpp @@ -294,8 +294,7 @@ bool GraphView::draw(float dt) // Animate style ImGuiEx::WireStyle style = default_wire_style; - if (is_selected(node_view_out) || - is_selected(node_view_in)) + if ( m_selection.contains( node_view_out ) || m_selection.contains( node_view_in ) ) { style.color.w *= wave(0.5f, 1.f, (float) App::get_time(), 10.f); } @@ -625,8 +624,8 @@ void GraphView::frame_nodes(FrameMode mode ) case FRAME_SELECTION_ONLY: { - if ( !m_selected.node.empty()) - frame_views(m_selected.node, CENTER); + if ( !m_selection.node().empty()) + frame_views(m_selection.node(), CENTER); break; } default: @@ -634,37 +633,6 @@ void GraphView::frame_nodes(FrameMode mode ) } } -void GraphView::set_selected(const Selection& views, SelectionMode mode ) -{ - Selection curr_selection = m_selected; - if ( mode == SelectionMode_REPLACE ) - { - m_selected.remove_all(); - for(auto& each : curr_selection.node ) - each->set_selected(false); - } - - m_selected.add( views ) ; - - EventPayload_SelectionChange event{m_selected, curr_selection }; - get_event_manager()->dispatch(event); -} - -const Selection& GraphView::selected() const -{ - return m_selected; -} - -bool GraphView::is_selected(NodeView* view) const -{ - return m_selected.has(view); -} - -bool GraphView::selection_empty() const -{ - return m_selected.empty(); -} - void GraphView::_on_graph_change() { m_physics_dirty = true; @@ -729,9 +697,9 @@ void GraphView::draw_create_node_context_menu(CreateNodeCtxMenu& menu, SlotView* void GraphView::drag_state_enter() { - for ( NodeView* view : m_selected.node ) + for ( NodeView* view : m_selection.node() ) view->set_pinned(); - for ( ScopeView* view : m_selected.scope ) + for ( ScopeView* view : m_selection.scope() ) view->set_pinned(); } @@ -750,7 +718,7 @@ void GraphView::drag_state_tick() default: { - for (NodeView* view : m_selected.node ) + for (NodeView* view : m_selection.node() ) view->spatial_node().translate( delta ); break; } @@ -833,7 +801,10 @@ void GraphView::cursor_state_tick() views.push_back(view); } // Replace selection - set_selected(views); + m_selection.clear(); + for(NodeView* view : views) + m_selection.append(view ) ; + get_event_manager()->dispatch({this}); } ImGui::Separator(); @@ -895,12 +866,6 @@ void GraphView::cursor_state_tick() return; } - // TODO: handle remove! - // Add/Remove/Replace selection - SelectionMode selection_flags = SelectionMode_REPLACE; - if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) - selection_flags = SelectionMode_ADD; - switch (m_hovered.type) { case ViewItemType_SLOT: @@ -920,6 +885,7 @@ void GraphView::cursor_state_tick() case ViewItemType_NODE: { + bool select_hovered = false; if ( ImGui::IsMouseClicked(1) ) { m_focused = m_hovered; @@ -927,7 +893,7 @@ void GraphView::cursor_state_tick() } else if (ImGui::IsMouseReleased(0) ) { - set_selected(m_hovered.nodeview, selection_flags); + select_hovered = true; m_focused = m_hovered; } else if (ImGui::IsMouseDoubleClicked(0)) @@ -937,11 +903,19 @@ void GraphView::cursor_state_tick() } else if (ImGui::IsMouseDragging(0)) { + select_hovered = true; m_focused = m_hovered; - if (!m_hovered.nodeview->selected()) - set_selected(m_hovered.nodeview); m_state_machine.change_state(DRAG_STATE); } + + if ( select_hovered ) + { + const bool ctrl_pressed = ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl); + if ( !ctrl_pressed ) // replace when Ctrl is pressed + m_selection.clear(); + m_selection.append( m_hovered.nodeview ) ; + get_event_manager()->dispatch({this}); + } break; } @@ -961,9 +935,10 @@ void GraphView::cursor_state_tick() case ViewItemType_SCOPE: { + bool select_hovered = false; if (ImGui::IsMouseReleased(0) ) { - set_selected(m_hovered.scopeview, selection_flags); + select_hovered = true; m_focused = m_hovered; } else if (ImGui::IsMouseClicked(1)) @@ -974,9 +949,18 @@ void GraphView::cursor_state_tick() else if ( ImGui::IsMouseDragging(0) ) { m_focused = m_hovered; - set_selected(m_hovered.scopeview); + select_hovered = true; m_state_machine.change_state(DRAG_STATE); } + + if ( select_hovered ) + { + const bool ctrl_pressed = ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl); + if ( !ctrl_pressed ) // replace when Ctrl is pressed + m_selection.clear(); + m_selection.append( m_hovered.scopeview ) ; + get_event_manager()->dispatch({this}); + } break; } @@ -985,7 +969,7 @@ void GraphView::cursor_state_tick() if ( ImGui::IsWindowHovered(ImGuiFocusedFlags_ChildWindows) ) { if (ImGui::IsMouseClicked(0)) - set_selected({}, SelectionMode_REPLACE); // Deselect All (Click on the background) + m_selection.clear(); // Deselect All (Click on the background) else if (ImGui::IsMouseReleased(0)) m_focused = {}; else if (ImGui::IsMouseClicked(1)) @@ -1098,17 +1082,17 @@ void GraphView::roi_state_tick() if (ImGui::IsMouseReleased(0)) { + const bool ctrl_pressed = ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl); + if ( !ctrl_pressed ) + m_selection.clear(); + // Select the views included in the ROI - std::vector nodeview_in_roi; for ( Node* node : graph()->nodes() ) if ( auto v = node->get_component() ) if (Rect::contains(roi, v->get_rect())) - nodeview_in_roi.emplace_back(v); + m_selection.append(v); - bool control_pressed = ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || - ImGui::IsKeyDown(ImGuiKey_RightCtrl); - SelectionMode flags = control_pressed ? SelectionMode_ADD : SelectionMode_REPLACE; - set_selected(nodeview_in_roi, flags); + get_event_manager()->dispatch({this}); m_state_machine.exit_state(); } diff --git a/src/ndbl/gui/GraphView.h b/src/ndbl/gui/GraphView.h index 510582a16..a62c45171 100644 --- a/src/ndbl/gui/GraphView.h +++ b/src/ndbl/gui/GraphView.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "tools/gui/ViewState.h" // base class #include "tools/core/reflection/reflection" @@ -50,11 +51,10 @@ namespace ndbl bool draw(float dt); void add_action_to_node_menu(Action_CreateNode* _action); void frame_nodes(FrameMode mode ); - bool selection_empty() const; void reset(); // unfold and frame the whole graph bool has_an_active_tool() const; - void set_selected(const Selection&, SelectionMode = SelectionMode_REPLACE); - const Selection& selected() const; + Selection& selection() { return m_selection; } + const Selection& selection() const { return m_selection; } void reset_all_properties(); Graph* graph() const; void add_child(NodeView*); @@ -65,7 +65,7 @@ namespace ndbl CreateNodeCtxMenu m_create_node_menu; ViewItem m_hovered; ViewItem m_focused; - Selection m_selected; + Selection m_selection; tools::ViewState m_view_state; Graph* m_graph; bool m_physics_dirty = false; @@ -75,7 +75,6 @@ namespace ndbl void _update(float dt, u16_t iterations); void _update(float dt); void _on_graph_change(); - bool is_selected(NodeView*) const; void frame_views(const std::vector&, const Vec2& pivot ); void draw_create_node_context_menu(CreateNodeCtxMenu& menu, SlotView* dragged_slotview = nullptr ); void create_constraints__align_top_recursively(const std::vector& unfiltered_follower, ndbl::Node *leader); diff --git a/src/ndbl/gui/Nodable.cpp b/src/ndbl/gui/Nodable.cpp index 52c563053..b94c874a5 100644 --- a/src/ndbl/gui/Nodable.cpp +++ b/src/ndbl/gui/Nodable.cpp @@ -190,18 +190,6 @@ void Nodable::update() break; } - case Event_SelectionChange::id: - { - if (m_current_file == nullptr ) - break; - auto _event = reinterpret_cast( event ); - - Condition_ condition = _event->data.new_selection.empty() ? Condition_ENABLE_IF_HAS_NO_SELECTION - : Condition_ENABLE_IF_HAS_SELECTION; - m_current_file->view.clear_overlay(); - m_current_file->view.refresh_overlay(condition ); - break; - } case EventID_FILE_OPENED: { ASSERT(m_current_file != nullptr ); @@ -212,7 +200,7 @@ void Nodable::update() case Event_DeleteNode::id: { if ( graph_view ) - for(NodeView* view : graph_view->selected().node) + for(NodeView* view : graph_view->selection().node() ) graph_view->graph()->destroy_next_frame(view->node()); break; } @@ -220,22 +208,20 @@ void Nodable::update() case Event_ArrangeNode::id: { if ( graph_view ) - for(NodeView* view : graph_view->selected().node) + for(NodeView* view : graph_view->selection().node() ) view->arrange_recursively(); break; } case Event_SelectNext::id: { - if (graph_view && !graph_view->selected().empty()) + if (graph_view && !graph_view->selection().empty()) { - std::vector successors; - if (!graph_view->selected().empty()) - for(NodeView* view : graph_view->selected().node ) + graph_view->selection().clear(); + if (!graph_view->selection().empty()) + for(NodeView* view : graph_view->selection().node() ) for (NodeView* successor : Utils::get_components( view->node()->flow_outputs() ) ) - successors.push_back( successor ); - - graph_view->set_selected(successors, SelectionMode_REPLACE); + graph_view->selection().append( successor ); } break; @@ -243,14 +229,15 @@ void Nodable::update() case Event_ToggleFolding::id: { - if ( graph_view && !graph_view->selection_empty() ) - for(NodeView* view : graph_view->selected().node) - { - auto _event = reinterpret_cast(event); - _event->data.mode == RECURSIVELY ? view->expand_toggle_rec() - : view->expand_toggle(); - } + if ( graph_view ) + break; + for(NodeView* view : graph_view->selection().node()) + { + auto _event = reinterpret_cast(event); + _event->data.mode == RECURSIVELY ? view->expand_toggle_rec() + : view->expand_toggle(); + } break; } @@ -389,7 +376,8 @@ void Nodable::update() if ( auto view = new_node->get_component() ) { view->spatial_node().set_position(_event->data.desired_screen_pos, WORLD_SPACE); - graph->view()->set_selected({view}); + graph->view()->selection().clear(); + graph->view()->selection().append(view); } break; } @@ -546,7 +534,7 @@ void Nodable::step_over_program() if (!m_interpreter->is_there_a_next_instr() ) { - graph_view->set_selected({}, SelectionMode_REPLACE); + graph_view->selection().clear(); return; } @@ -554,8 +542,8 @@ void Nodable::step_over_program() if ( !next_node ) return; - NodeView* view = next_node->get_component(); - graph_view->set_selected({view}); + graph_view->selection().clear(); + graph_view->selection().append( next_node->get_component() ); } void Nodable::stop_program() diff --git a/src/ndbl/gui/NodableView.cpp b/src/ndbl/gui/NodableView.cpp index 98e5d3c90..bbe82cc1d 100644 --- a/src/ndbl/gui/NodableView.cpp +++ b/src/ndbl/gui/NodableView.cpp @@ -77,7 +77,7 @@ void NodableView::init(Nodable * _app) action_manager->new_action("Undo", Shortcut{SDLK_z, KMOD_CTRL } ); action_manager->new_action("Redo", Shortcut{SDLK_y, KMOD_CTRL } ); action_manager->new_action("Isolation", Shortcut{SDLK_i, KMOD_CTRL }, Condition_ENABLE | Condition_HIGHLIGHTED_IN_TEXT_EDITOR ); - action_manager->new_action("Deselect", Shortcut{0, KMOD_NONE, "Click on background" }, Condition_ENABLE_IF_HAS_SELECTION | Condition_HIGHLIGHTED_IN_GRAPH_EDITOR ); + action_manager->new_action("Deselect", Shortcut{0, KMOD_NONE, "Click on background" }, Condition_ENABLE_IF_HAS_SELECTION | Condition_HIGHLIGHTED_IN_GRAPH_EDITOR ); action_manager->new_action("Drag whole graph", Shortcut{SDLK_SPACE, KMOD_NONE, "Space + Drag" }, Condition_ENABLE | Condition_HIGHLIGHTED_IN_GRAPH_EDITOR ); action_manager->new_action("Frame Selection", Shortcut{SDLK_f, KMOD_NONE }, EventPayload_FrameNodeViews{FRAME_SELECTION_ONLY }, Condition_ENABLE_IF_HAS_SELECTION | Condition_HIGHLIGHTED_IN_GRAPH_EDITOR ); action_manager->new_action("Frame All", Shortcut{SDLK_f, KMOD_LCTRL }, EventPayload_FrameNodeViews{FRAME_ALL } ); @@ -185,7 +185,7 @@ void NodableView::draw() if (ImGui::BeginMenuBar()) { History* current_file_history = current_file ? ¤t_file->history : nullptr; - auto has_selection = current_file != nullptr ? !current_file->graph().view()->selection_empty() + auto has_selection = current_file != nullptr ? !current_file->graph().view()->selection().empty() : false; if (ImGui::BeginMenu("File")) @@ -525,17 +525,15 @@ bool NodableView::draw_node_properties_window() { if( File* current_file = m_app->get_current_file() ) { - GraphView* graph_view = current_file->graph().view(); // Graph can't be null - ASSERT(graph_view != nullptr); - const std::vector& selected_nodeviews = graph_view->selected().node; - - if (selected_nodeviews.size() == 1) + const GraphView* graph_view = current_file->graph().view(); // Graph can't be null + const size_t selection_size = graph_view->selection().node().size(); + if ( selection_size == 1) { ImGui::Indent(10.0f); - NodeView *first_node_view = selected_nodeviews.front(); + NodeView* first_node_view = graph_view->selection().node().front(); changed |= NodeView::draw_as_properties_panel(first_node_view, &m_show_advanced_node_properties); } - else if (selected_nodeviews.size() > 1) + else if ( selection_size > 1) { ImGui::Indent(10.0f); ImGui::Text("Multi-Selection"); diff --git a/src/ndbl/gui/NodeView.cpp b/src/ndbl/gui/NodeView.cpp index e53e0dfd6..87fe3eead 100644 --- a/src/ndbl/gui/NodeView.cpp +++ b/src/ndbl/gui/NodeView.cpp @@ -708,7 +708,10 @@ bool NodeView::draw_as_properties_panel(NodeView *_view, bool* _show_advanced) Node* scope_owner = scope->node(); label.append_fmt("%s %p (%s %p)", scope->name(), scope, scope_owner->name().c_str(), scope_owner); if ( ImGui::Button(label.c_str()) ) - node->graph()->view()->set_selected({ scope_owner->get_component() }); + { + node->graph()->view()->selection().clear(); + node->graph()->view()->selection().append( scope_owner->get_component() ); + } } else { diff --git a/src/ndbl/gui/Selection.h b/src/ndbl/gui/Selection.h index 1926527f5..431938cbd 100644 --- a/src/ndbl/gui/Selection.h +++ b/src/ndbl/gui/Selection.h @@ -7,79 +7,60 @@ namespace ndbl { struct Selection { - Selection() - {} - - Selection(ScopeView* view) - { add(view); } - - Selection(NodeView* view) - { add(view); } - - Selection(const std::vector& views) - { - for ( auto view : views ) - add( view ); - } - - void remove_all() - { - for( NodeView* view : node ) - { - view->state().selected = false; - } - - for( ScopeView* view : scope ) - { - view->state().selected = false; - } - - node.clear(); - scope.clear(); - } - - void add(const Selection& selection) - { - for ( auto each : selection.node ) add( each ); - for ( auto each : selection.scope ) add( each ); - } - - void add(NodeView* view) + void append(NodeView* view) { - node.push_back(view); + _node.push_back(view); view->state().selected = true; } - void add(ScopeView* view) + void append(ScopeView* view) { - scope.push_back(view); + _scope.push_back(view); view->state().selected = true; } void remove(NodeView* view) { view->state().selected = false; - node.erase( std::find(node.begin(), node.end(), view) ); + _node.erase( std::find(_node.begin(), _node.end(), view) ); } void remove(ScopeView* view) { view->state().selected = false; - scope.erase( std::find(scope.begin(), scope.end(), view) ); + _scope.erase( std::find(_scope.begin(), _scope.end(), view) ); } - [[nodiscard]] bool empty() const + bool empty() const { - return node.empty() && scope.empty(); + return _node.empty() && _scope.empty(); } - bool has(NodeView* view) const + bool contains(NodeView* view) const { - return std::find(node.begin(), node.end(), view) != node.end(); + return std::find(_node.begin(), _node.end(), view) != _node.end(); } - std::vector node; - std::vector scope; + void clear() + { + for( NodeView* view : _node ) + { + view->state().selected = false; + } + + for( ScopeView* view : _scope ) + { + view->state().selected = false; + } + + _node.clear(); + _scope.clear(); + } + const std::vector& node() const { return _node; }; + const std::vector& scope() const { return _scope; }; + private: + std::vector _node; + std::vector _scope; }; } \ No newline at end of file