@@ -6,7 +6,7 @@ use crate::messages::layout::utility_types::widget_prelude::*;
66use crate :: messages:: portfolio:: document:: document_message_handler:: navigation_controls;
77use crate :: messages:: portfolio:: document:: graph_operation:: utility_types:: ModifyInputsContext ;
88use crate :: messages:: portfolio:: document:: node_graph:: document_node_definitions:: NodePropertiesContext ;
9- use crate :: messages:: portfolio:: document:: node_graph:: utility_types:: { ContextMenuData , Direction , FrontendGraphDataType } ;
9+ use crate :: messages:: portfolio:: document:: node_graph:: utility_types:: { ContextMenuData , Direction , FrontendGraphDataType , LassoSelection } ;
1010use crate :: messages:: portfolio:: document:: utility_types:: document_metadata:: LayerNodeIdentifier ;
1111use crate :: messages:: portfolio:: document:: utility_types:: misc:: GroupFolderType ;
1212use crate :: messages:: portfolio:: document:: utility_types:: network_interface:: {
@@ -26,8 +26,9 @@ use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
2626use graph_craft:: proto:: GraphErrors ;
2727use graphene_std:: math:: math_ext:: QuadExt ;
2828use graphene_std:: vector:: algorithms:: bezpath_algorithms:: bezpath_is_inside_bezpath;
29+ use graphene_std:: vector:: misc:: dvec2_to_point;
2930use graphene_std:: * ;
30- use kurbo:: { DEFAULT_ACCURACY , Shape } ;
31+ use kurbo:: { DEFAULT_ACCURACY , Line , PathSeg , Shape } ;
3132use renderer:: Quad ;
3233use std:: cmp:: Ordering ;
3334
@@ -66,6 +67,9 @@ pub struct NodeGraphMessageHandler {
6667 /// If dragging the background to create a box selection, this stores its starting point in node graph coordinates,
6768 /// plus a flag indicating if it has been dragged since the mousedown began.
6869 box_selection_start : Option < ( DVec2 , bool ) > ,
70+ /// If dragging the background to create a lasso selection, this stores its current lasso polygon in node graph coordinates,
71+ /// plus a flag indicating if it has been dragged since the mousedown began.
72+ lasso_selection_curr : Option < ( Vec < DVec2 > , bool ) > ,
6973 /// Restore the selection before box selection if it is aborted
7074 selection_before_pointer_down : Vec < NodeId > ,
7175 /// If the grip icon is held during a drag, then shift without pushing other nodes
@@ -767,6 +771,15 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
767771 responses. add ( FrontendMessage :: UpdateBox { box_selection : None } ) ;
768772 return ;
769773 }
774+ // Abort a lasso selection
775+ if self . lasso_selection_curr . is_some ( ) {
776+ self . lasso_selection_curr = None ;
777+ responses. add ( NodeGraphMessage :: SelectedNodesSet {
778+ nodes : self . selection_before_pointer_down . clone ( ) ,
779+ } ) ;
780+ responses. add ( FrontendMessage :: UpdateLasso { lasso_selection : None } ) ;
781+ return ;
782+ }
770783 // Abort dragging a wire
771784 if self . wire_in_progress_from_connector . is_some ( ) {
772785 self . wire_in_progress_from_connector = None ;
@@ -972,7 +985,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
972985 if !shift_click && !alt_click {
973986 responses. add ( NodeGraphMessage :: SelectedNodesSet { nodes : Vec :: new ( ) } )
974987 }
975- self . box_selection_start = Some ( ( node_graph_point, false ) ) ;
988+
989+ if control_click {
990+ self . lasso_selection_curr = Some ( ( vec ! [ node_graph_point] , false ) ) ;
991+ } else {
992+ self . box_selection_start = Some ( ( node_graph_point, false ) ) ;
993+ }
994+
976995 self . update_node_graph_hints ( responses) ;
977996 }
978997 NodeGraphMessage :: PointerMove { shift } => {
@@ -1106,6 +1125,10 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
11061125 * box_selection_dragged = true ;
11071126 responses. add ( NodeGraphMessage :: UpdateBoxSelection ) ;
11081127 self . update_node_graph_hints ( responses) ;
1128+ } else if let Some ( ( _, lasso_selection_dragged) ) = & mut self . lasso_selection_curr {
1129+ * lasso_selection_dragged = true ;
1130+ responses. add ( NodeGraphMessage :: UpdateLassoSelection ) ;
1131+ self . update_node_graph_hints ( responses) ;
11091132 } else if self . reordering_import . is_some ( ) {
11101133 let Some ( modify_import_export) = network_interface. modify_import_export ( selection_network_path) else {
11111134 log:: error!( "Could not get modify import export in PointerMove" ) ;
@@ -1392,6 +1415,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
13921415 self . drag_start = None ;
13931416 self . begin_dragging = false ;
13941417 self . box_selection_start = None ;
1418+ self . lasso_selection_curr = None ;
13951419 self . wire_in_progress_from_connector = None ;
13961420 self . wire_in_progress_type = FrontendGraphDataType :: General ;
13971421 self . wire_in_progress_to_connector = None ;
@@ -1400,12 +1424,17 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
14001424 responses. add ( DocumentMessage :: EndTransaction ) ;
14011425 responses. add ( FrontendMessage :: UpdateWirePathInProgress { wire_path : None } ) ;
14021426 responses. add ( FrontendMessage :: UpdateBox { box_selection : None } ) ;
1427+ responses. add ( FrontendMessage :: UpdateLasso { lasso_selection : None } ) ;
14031428 responses. add ( FrontendMessage :: UpdateImportReorderIndex { index : None } ) ;
14041429 responses. add ( FrontendMessage :: UpdateExportReorderIndex { index : None } ) ;
14051430 self . update_node_graph_hints ( responses) ;
14061431 }
14071432 NodeGraphMessage :: PointerOutsideViewport { shift } => {
1408- if self . drag_start . is_some ( ) || self . box_selection_start . is_some ( ) || ( self . wire_in_progress_from_connector . is_some ( ) && self . context_menu . is_none ( ) ) {
1433+ if self . drag_start . is_some ( )
1434+ || self . box_selection_start . is_some ( )
1435+ || self . lasso_selection_curr . is_some ( )
1436+ || ( self . wire_in_progress_from_connector . is_some ( ) && self . context_menu . is_none ( ) )
1437+ {
14091438 let _ = self . auto_panning . shift_viewport ( ipp, viewport, responses) ;
14101439 } else {
14111440 // Auto-panning
@@ -1959,6 +1988,88 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
19591988 responses. add ( FrontendMessage :: UpdateBox { box_selection } )
19601989 }
19611990 }
1991+ NodeGraphMessage :: UpdateLassoSelection => {
1992+ if let Some ( ( lasso_selection_curr, _) ) = & mut self . lasso_selection_curr {
1993+ // WARNING WARNING WARNING: this commented-out code is copy pasted from UpdateBoxSelection above and has not been edited for lasso
1994+ // The mouse button was released but we missed the pointer up event
1995+ // if ((e.buttons & 1) === 0) {
1996+ // completeBoxSelection();
1997+ // boxSelection = undefined;
1998+ // } else if ((e.buttons & 2) !== 0) {
1999+ // editor.handle.selectNodes(new BigUint64Array(previousSelection));
2000+ // boxSelection = undefined;
2001+ // }
2002+
2003+ let Some ( network_metadata) = network_interface. network_metadata ( selection_network_path) else {
2004+ log:: error!( "Could not get network metadata in UpdateBoxSelection" ) ;
2005+ return ;
2006+ } ;
2007+
2008+ {
2009+ let node_graph_point = network_metadata
2010+ . persistent_metadata
2011+ . navigation_metadata
2012+ . node_graph_to_viewport
2013+ . inverse ( )
2014+ . transform_point2 ( ipp. mouse . position ) ;
2015+
2016+ lasso_selection_curr. push ( node_graph_point) ;
2017+ }
2018+
2019+ let lasso_selection_viewport: Vec < DVec2 > = lasso_selection_curr
2020+ . iter ( )
2021+ . map ( |selection_point| network_metadata. persistent_metadata . navigation_metadata . node_graph_to_viewport . transform_point2 ( * selection_point) )
2022+ . collect ( ) ;
2023+
2024+ let shift = ipp. keyboard . get ( Key :: Shift as usize ) ;
2025+ let alt = ipp. keyboard . get ( Key :: Alt as usize ) ;
2026+ let Some ( selected_nodes) = network_interface. selected_nodes_in_nested_network ( selection_network_path) else {
2027+ log:: error!( "Could not get selected nodes in UpdateBoxSelection" ) ;
2028+ return ;
2029+ } ;
2030+ let previous_selection = selected_nodes. selected_nodes_ref ( ) . iter ( ) . cloned ( ) . collect :: < HashSet < _ > > ( ) ;
2031+ let mut nodes = if shift || alt {
2032+ selected_nodes. selected_nodes_ref ( ) . iter ( ) . cloned ( ) . collect :: < HashSet < _ > > ( )
2033+ } else {
2034+ HashSet :: new ( )
2035+ } ;
2036+ let all_nodes = network_metadata. persistent_metadata . node_metadata . keys ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
2037+ let path: Vec < PathSeg > = {
2038+ fn points_to_polygon ( points : & [ DVec2 ] ) -> Vec < PathSeg > {
2039+ points
2040+ . windows ( 2 )
2041+ . map ( |w| PathSeg :: Line ( Line :: new ( dvec2_to_point ( w[ 0 ] ) , dvec2_to_point ( w[ 1 ] ) ) ) )
2042+ . chain ( std:: iter:: once ( PathSeg :: Line ( Line :: new (
2043+ dvec2_to_point ( * points. last ( ) . unwrap ( ) ) ,
2044+ dvec2_to_point ( * points. first ( ) . unwrap ( ) ) ,
2045+ ) ) ) )
2046+ . collect ( )
2047+ }
2048+ points_to_polygon ( lasso_selection_curr)
2049+ } ;
2050+ for node_id in all_nodes {
2051+ let Some ( click_targets) = network_interface. node_click_targets ( & node_id, selection_network_path) else {
2052+ log:: error!( "Could not get transient metadata for node {node_id}" ) ;
2053+ continue ;
2054+ } ;
2055+ if click_targets. node_click_target . intersect_path ( || path. iter ( ) . cloned ( ) , DAffine2 :: IDENTITY ) {
2056+ if alt {
2057+ nodes. remove ( & node_id) ;
2058+ } else {
2059+ nodes. insert ( node_id) ;
2060+ }
2061+ }
2062+ }
2063+ if nodes != previous_selection {
2064+ responses. add ( NodeGraphMessage :: SelectedNodesSet {
2065+ nodes : nodes. into_iter ( ) . collect :: < Vec < _ > > ( ) ,
2066+ } ) ;
2067+ }
2068+ responses. add ( FrontendMessage :: UpdateLasso {
2069+ lasso_selection : Some ( LassoSelection :: from_iter ( lasso_selection_viewport. into_iter ( ) ) ) ,
2070+ } )
2071+ }
2072+ }
19622073 NodeGraphMessage :: UpdateImportsExports => {
19632074 let imports = network_interface. frontend_imports ( breadcrumb_network_path) ;
19642075 let exports = network_interface. frontend_exports ( breadcrumb_network_path) ;
@@ -2729,11 +2840,12 @@ impl NodeGraphMessageHandler {
27292840 // Node gragging is in progress (having already moved at least one pixel from the mouse down position)
27302841 let dragging_nodes = self . drag_start . as_ref ( ) . is_some_and ( |( _, dragged) | * dragged) ;
27312842
2732- // A box selection is in progress
2733- let dragging_box_selection = self . box_selection_start . is_some_and ( |( _, box_selection_dragged) | box_selection_dragged) ;
2843+ // A box or lasso selection is in progress
2844+ let dragging_selection = self . box_selection_start . as_ref ( ) . is_some_and ( |( _, box_selection_dragged) | * box_selection_dragged)
2845+ || self . lasso_selection_curr . as_ref ( ) . is_some_and ( |( _, lasso_selection_dragged) | * lasso_selection_dragged) ;
27342846
27352847 // Cancel the ongoing action
2736- if wiring || dragging_nodes || dragging_box_selection {
2848+ if wiring || dragging_nodes || dragging_selection {
27372849 let hint_data = HintData ( vec ! [ HintGroup ( vec![ HintInfo :: mouse( MouseMotion :: Rmb , "" ) , HintInfo :: keys( [ Key :: Escape ] , "Cancel" ) . prepend_slash( ) ] ) ] ) ;
27382850 responses. add ( FrontendMessage :: UpdateInputHints { hint_data } ) ;
27392851 return ;
@@ -2779,6 +2891,7 @@ impl Default for NodeGraphMessageHandler {
27792891 node_has_moved_in_drag : false ,
27802892 shift_without_push : false ,
27812893 box_selection_start : None ,
2894+ lasso_selection_curr : None ,
27822895 drag_start_chain_nodes : Vec :: new ( ) ,
27832896 selection_before_pointer_down : Vec :: new ( ) ,
27842897 disconnecting : None ,
@@ -2810,6 +2923,7 @@ impl PartialEq for NodeGraphMessageHandler {
28102923 && self . begin_dragging == other. begin_dragging
28112924 && self . node_has_moved_in_drag == other. node_has_moved_in_drag
28122925 && self . box_selection_start == other. box_selection_start
2926+ && self . lasso_selection_curr == other. lasso_selection_curr
28132927 && self . initial_disconnecting == other. initial_disconnecting
28142928 && self . select_if_not_dragged == other. select_if_not_dragged
28152929 && self . wire_in_progress_from_connector == other. wire_in_progress_from_connector
0 commit comments