From 3cfb476f1b767c32dd25508279d40d724b177d17 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 --- src/ndbl/gui/Event.h | 9 ++-- src/ndbl/gui/GraphView.cpp | 85 +++++++++++++++--------------------- src/ndbl/gui/GraphView.h | 7 ++- src/ndbl/gui/Nodable.cpp | 12 ++--- src/ndbl/gui/NodableView.cpp | 2 +- src/ndbl/gui/ScopeView.cpp | 21 ++++----- src/ndbl/gui/ScopeView.h | 6 ++- src/ndbl/gui/Selection.h | 85 ++++++++++++++++++++++++++++++++++++ 8 files changed, 151 insertions(+), 76 deletions(-) create mode 100644 src/ndbl/gui/Selection.h diff --git a/src/ndbl/gui/Event.h b/src/ndbl/gui/Event.h index 01f28fe80..7f82cebd0 100644 --- a/src/ndbl/gui/Event.h +++ b/src/ndbl/gui/Event.h @@ -9,6 +9,7 @@ #include "Event.h" #include "FrameMode.h" #include "SlotView.h" +#include "Selection.h" namespace ndbl { @@ -79,12 +80,12 @@ namespace ndbl }; using Event_ToggleFolding = tools::Event; - struct EventPayload_NodeViewSelectionChange + struct EventPayload_SelectionChange { - std::vector new_selection; - std::vector old_selection; + Selection new_selection; + Selection old_selection; }; - using Event_SelectionChange = tools::Event; + using Event_SelectionChange = tools::Event; struct EventPayload_CreateNode { diff --git a/src/ndbl/gui/GraphView.cpp b/src/ndbl/gui/GraphView.cpp index 1f9715793..6512c6fda 100644 --- a/src/ndbl/gui/GraphView.cpp +++ b/src/ndbl/gui/GraphView.cpp @@ -178,13 +178,11 @@ bool GraphView::draw(float dt) auto low_to_high_depth = [](Scope* s1, Scope* s2) { return s1->depth() < s2->depth(); }; std::sort(scopes_to_draw.begin(), scopes_to_draw.end(), low_to_high_depth); - const ScopeView* focused_scope_view = m_focused.type == ViewItemType_SCOPE ? m_focused.scopeview : nullptr; for( Scope* scope : scopes_to_draw ) { if (ScopeView* view = scope->view()) { - bool highlight = view == focused_scope_view; - view->draw(dt, highlight); + view->draw(dt); } } @@ -627,8 +625,8 @@ void GraphView::frame_nodes(FrameMode mode ) case FRAME_SELECTION_ONLY: { - if ( !m_selected_nodeview.empty()) - frame_views(m_selected_nodeview, CENTER); + if ( !m_selected.node.empty()) + frame_views(m_selected.node, CENTER); break; } default: @@ -638,37 +636,33 @@ void GraphView::frame_nodes(FrameMode mode ) void GraphView::set_selected(const Selection& views, SelectionMode mode ) { - Selection curr_selection = m_selected_nodeview; + Selection curr_selection = m_selected; if ( mode == SelectionMode_REPLACE ) { - m_selected_nodeview.clear(); - for(auto& each : curr_selection ) + m_selected.remove_all(); + for(auto& each : curr_selection.node ) each->set_selected(false); } - for(auto& each : views) - { - m_selected_nodeview.emplace_back(each); - each->set_selected(); - } + m_selected.add( views ) ; - EventPayload_NodeViewSelectionChange event{ m_selected_nodeview, curr_selection }; + EventPayload_SelectionChange event{m_selected, curr_selection }; get_event_manager()->dispatch(event); } -const GraphView::Selection& GraphView::get_selected() const +const Selection& GraphView::selected() const { - return m_selected_nodeview; + return m_selected; } bool GraphView::is_selected(NodeView* view) const { - return std::find( m_selected_nodeview.begin(), m_selected_nodeview.end(), view) != m_selected_nodeview.end(); + return m_selected.has(view); } bool GraphView::selection_empty() const { - return m_selected_nodeview.empty(); + return m_selected.empty(); } void GraphView::_on_graph_change() @@ -688,8 +682,8 @@ void GraphView::reset() Vec2 far_outside = Vec2(-1000.f, -1000.0f); for( Node* node : graph()->nodes() ) - if ( NodeView* v = node->get_component() ) - v->spatial_node().translate( far_outside ); + if ( auto* view = node->get_component() ) + view->spatial_node().translate( far_outside ); // physics m_physics_dirty = true; @@ -735,20 +729,10 @@ void GraphView::draw_create_node_context_menu(CreateNodeCtxMenu& menu, SlotView* void GraphView::drag_state_enter() { - switch ( m_focused.type ) - { - case ViewItemType_SCOPE: - { - m_focused.scopeview->set_pinned(); - break; - } - default: - { - for ( NodeView* node_view : get_selected() ) - node_view->set_pinned(); - break; - } - } + for ( NodeView* view : m_selected.node ) + view->set_pinned(); + for ( ScopeView* view : m_selected.scope ) + view->set_pinned(); } void GraphView::drag_state_tick() @@ -766,7 +750,7 @@ void GraphView::drag_state_tick() default: { - for (NodeView* view: get_selected() ) + for (NodeView* view : m_selected.node ) view->spatial_node().translate( delta ); break; } @@ -836,20 +820,20 @@ void GraphView::cursor_state_tick() Scope::get_descendent( children, m_focused.scopeview->scope(), ScopeFlags_INCLUDE_SELF ); // Extract node views from each descendent - std::set views; + std::vector views; for(Scope* child : children) { // Include scope owner's view too - if ( NodeView* view = child->node()->get_component()) - views.insert( view ); + if ( auto* view = child->node()->get_component()) + views.push_back( view ); // and every other child's for(Node* child_node : child->child()) - if ( NodeView* view = child_node->get_component()) - views.insert(view); + if ( auto* view = child_node->get_component()) + views.push_back(view); } // Replace selection - set_selected({views.begin(), views.end()}); + set_selected(views); } ImGui::Separator(); @@ -911,6 +895,12 @@ 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: @@ -937,12 +927,7 @@ void GraphView::cursor_state_tick() } else if (ImGui::IsMouseReleased(0) ) { - // TODO: handle remove! - // Add/Remove/Replace selection - SelectionMode flags = SelectionMode_REPLACE; - if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) - flags = SelectionMode_ADD; - set_selected({m_hovered.nodeview}, flags); + set_selected(m_hovered.nodeview, selection_flags); m_focused = m_hovered; } else if (ImGui::IsMouseDoubleClicked(0)) @@ -954,7 +939,7 @@ void GraphView::cursor_state_tick() { m_focused = m_hovered; if (!m_hovered.nodeview->selected()) - set_selected({m_hovered.nodeview}); + set_selected(m_hovered.nodeview); m_state_machine.change_state(DRAG_STATE); } break; @@ -976,8 +961,9 @@ void GraphView::cursor_state_tick() case ViewItemType_SCOPE: { - if (ImGui::IsMouseClicked(0)) + if (ImGui::IsMouseReleased(0) ) { + set_selected(m_hovered.scopeview, selection_flags); m_focused = m_hovered; } else if (ImGui::IsMouseClicked(1)) @@ -988,6 +974,7 @@ void GraphView::cursor_state_tick() else if ( ImGui::IsMouseDragging(0) ) { m_focused = m_hovered; + set_selected(m_hovered.scopeview); m_state_machine.change_state(DRAG_STATE); } break; diff --git a/src/ndbl/gui/GraphView.h b/src/ndbl/gui/GraphView.h index f1a924c12..510582a16 100644 --- a/src/ndbl/gui/GraphView.h +++ b/src/ndbl/gui/GraphView.h @@ -11,7 +11,7 @@ #include "ndbl/core/Scope.h" #include "Action.h" - +#include "Selection.h" #include "NodeView.h" #include "SlotView.h" #include "types.h" @@ -39,7 +39,6 @@ namespace ndbl public: DECLARE_REFLECT - typedef std::vector Selection; typedef tools::StateMachine StateMachine; explicit GraphView(Graph* graph); @@ -55,7 +54,7 @@ namespace ndbl void reset(); // unfold and frame the whole graph bool has_an_active_tool() const; void set_selected(const Selection&, SelectionMode = SelectionMode_REPLACE); - const Selection& get_selected() const; + const Selection& selected() const; void reset_all_properties(); Graph* graph() const; void add_child(NodeView*); @@ -66,7 +65,7 @@ namespace ndbl CreateNodeCtxMenu m_create_node_menu; ViewItem m_hovered; ViewItem m_focused; - std::vector m_selected_nodeview; + Selection m_selected; tools::ViewState m_view_state; Graph* m_graph; bool m_physics_dirty = false; diff --git a/src/ndbl/gui/Nodable.cpp b/src/ndbl/gui/Nodable.cpp index 96ed7d820..52c563053 100644 --- a/src/ndbl/gui/Nodable.cpp +++ b/src/ndbl/gui/Nodable.cpp @@ -212,7 +212,7 @@ void Nodable::update() case Event_DeleteNode::id: { if ( graph_view ) - for(NodeView* view : graph_view->get_selected()) + for(NodeView* view : graph_view->selected().node) graph_view->graph()->destroy_next_frame(view->node()); break; } @@ -220,18 +220,18 @@ void Nodable::update() case Event_ArrangeNode::id: { if ( graph_view ) - for(NodeView* view : graph_view->get_selected()) + for(NodeView* view : graph_view->selected().node) view->arrange_recursively(); break; } case Event_SelectNext::id: { - if (graph_view && !graph_view->get_selected().empty()) + if (graph_view && !graph_view->selected().empty()) { std::vector successors; - if (!graph_view->get_selected().empty()) - for(NodeView* view : graph_view->get_selected() ) + if (!graph_view->selected().empty()) + for(NodeView* view : graph_view->selected().node ) for (NodeView* successor : Utils::get_components( view->node()->flow_outputs() ) ) successors.push_back( successor ); @@ -244,7 +244,7 @@ void Nodable::update() case Event_ToggleFolding::id: { if ( graph_view && !graph_view->selection_empty() ) - for(NodeView* view : graph_view->get_selected()) + for(NodeView* view : graph_view->selected().node) { auto _event = reinterpret_cast(event); _event->data.mode == RECURSIVELY ? view->expand_toggle_rec() diff --git a/src/ndbl/gui/NodableView.cpp b/src/ndbl/gui/NodableView.cpp index a8892abad..98e5d3c90 100644 --- a/src/ndbl/gui/NodableView.cpp +++ b/src/ndbl/gui/NodableView.cpp @@ -527,7 +527,7 @@ bool NodableView::draw_node_properties_window() { GraphView* graph_view = current_file->graph().view(); // Graph can't be null ASSERT(graph_view != nullptr); - std::vector selected_nodeviews = graph_view->get_selected(); + const std::vector& selected_nodeviews = graph_view->selected().node; if (selected_nodeviews.size() == 1) { diff --git a/src/ndbl/gui/ScopeView.cpp b/src/ndbl/gui/ScopeView.cpp index 5fab99959..eddce0432 100644 --- a/src/ndbl/gui/ScopeView.cpp +++ b/src/ndbl/gui/ScopeView.cpp @@ -132,7 +132,7 @@ bool ScopeView::must_be_draw() const } } -void ScopeView::draw(float dt, bool highlight) +void ScopeView::draw(float dt) { if ( must_be_draw() ) { @@ -142,13 +142,15 @@ void ScopeView::draw(float dt, bool highlight) const Vec4& fill_col = m_theme == Theme_DARK ? config->ui_scope_fill_col_light : config->ui_scope_fill_col_dark; draw_list->AddRectFilled(r.min, r.max, ImGui::GetColorU32(fill_col), config->ui_scope_border_radius ); - if ( highlight ) + if ( m_state.selected ) { draw_list->AddRect(r.min, r.max, ImGui::GetColorU32( config->ui_scope_border_col ) , config->ui_scope_border_radius, 0, config->ui_scope_border_thickness ); } if ( ImGui::IsMouseHoveringRect(r.min, r.max) ) + { on_hover.emit(this); + } } } @@ -156,7 +158,7 @@ void ScopeView::on_add_node(Node* node) { if( NodeView* view = node->get_component()) { - m_spatial_node.add_child( &view->spatial_node() ); + m_state.spatial_node().add_child( &view->spatial_node() ); } } @@ -164,18 +166,18 @@ void ScopeView::on_remove_node(Node* node) { if( NodeView* view = node->get_component()) { - m_spatial_node.remove_child( &view->spatial_node() ); + m_state.spatial_node().remove_child( &view->spatial_node() ); } } void ScopeView::on_reset_parent(Scope* scope) { - if( m_spatial_node.has_parent() ) - m_spatial_node.parent()->remove_child(&m_spatial_node ); + if( m_state.spatial_node().has_parent() ) + m_state.spatial_node().parent()->remove_child(&m_state.spatial_node() ); // this view must move when scope's owner view moves if( scope ) - scope->view()->m_spatial_node.add_child( &m_spatial_node ); + scope->view()->m_state.spatial_node().add_child( &m_state.spatial_node() ); } void ScopeView::translate(const tools::Vec2 &delta) @@ -184,7 +186,7 @@ void ScopeView::translate(const tools::Vec2 &delta) if ( node()->internal_scope() == m_scope ) node()->get_component()->spatial_node().translate( delta ); // translate view (and children...) - m_spatial_node.translate( delta ); + m_state.spatial_node().translate( delta ); } void ScopeView::set_pinned(bool b) @@ -199,10 +201,9 @@ bool ScopeView::pinned() const void ScopeView::set_position(const tools::Vec2& pos, tools::Space space) { - m_spatial_node.set_position( pos, space ); + m_state.spatial_node().set_position( pos, space ); } - void ScopeView::draw_scope_tree(Scope *scope) { if ( ImGui::TreeNode("Scope Tree" ) ) diff --git a/src/ndbl/gui/ScopeView.h b/src/ndbl/gui/ScopeView.h index 550760703..f014b7ce9 100644 --- a/src/ndbl/gui/ScopeView.h +++ b/src/ndbl/gui/ScopeView.h @@ -3,6 +3,7 @@ #include "tools/gui/geometry/BoxShape2D.h" #include "ndbl/core/NodeComponent.h" #include "ndbl/core/Scope.h" +#include "tools/gui/ViewState.h" namespace ndbl { @@ -34,7 +35,8 @@ namespace ndbl void init(Scope*); void update(float nodeview, ScopeViewFlags flags = ScopeViewFlags_NONE ); - void draw(float dt, bool highlight); + void draw(float dt); + tools::ViewState& state() { return m_state; } ScopeView* parent() const; Scope* scope() const { return m_scope; } size_t depth() const { return m_scope->depth(); } @@ -51,8 +53,8 @@ namespace ndbl void on_add_node(Node*); void on_remove_node(Node*); + tools::ViewState m_state; Scope* m_scope = nullptr; - tools::SpatialNode2D m_spatial_node; Rect m_content_rect; std::vector m_wrapped_node_view; Theme m_theme; diff --git a/src/ndbl/gui/Selection.h b/src/ndbl/gui/Selection.h new file mode 100644 index 000000000..1926527f5 --- /dev/null +++ b/src/ndbl/gui/Selection.h @@ -0,0 +1,85 @@ +#pragma once +#include "NodeView.h" +#include "ScopeView.h" +#include + +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) + { + node.push_back(view); + view->state().selected = true; + } + + void add(ScopeView* 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) ); + } + + void remove(ScopeView* view) + { + view->state().selected = false; + scope.erase( std::find(scope.begin(), scope.end(), view) ); + } + + [[nodiscard]] bool empty() const + { + return node.empty() && scope.empty(); + } + + bool has(NodeView* view) const + { + return std::find(node.begin(), node.end(), view) != node.end(); + } + + std::vector node; + std::vector scope; + + }; +} \ No newline at end of file