@@ -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 , NodeGraphErrorDiagnostic } ;
9+ use crate :: messages:: portfolio:: document:: node_graph:: utility_types:: { ContextMenuData , Direction , FrontendGraphDataType , LassoSelection , NodeGraphErrorDiagnostic } ;
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:: {
@@ -25,8 +25,9 @@ use glam::{DAffine2, DVec2, IVec2};
2525use graph_craft:: document:: { DocumentNodeImplementation , NodeId , NodeInput } ;
2626use graphene_std:: math:: math_ext:: QuadExt ;
2727use graphene_std:: vector:: algorithms:: bezpath_algorithms:: bezpath_is_inside_bezpath;
28+ use graphene_std:: vector:: misc:: dvec2_to_point;
2829use graphene_std:: * ;
29- use kurbo:: { DEFAULT_ACCURACY , Shape } ;
30+ use kurbo:: { DEFAULT_ACCURACY , Line , PathSeg , Shape } ;
3031use renderer:: Quad ;
3132use std:: cmp:: Ordering ;
3233
@@ -64,6 +65,9 @@ pub struct NodeGraphMessageHandler {
6465 /// If dragging the background to create a box selection, this stores its starting point in node graph coordinates,
6566 /// plus a flag indicating if it has been dragged since the mousedown began.
6667 box_selection_start : Option < ( DVec2 , bool ) > ,
68+ /// If dragging the background to create a lasso selection, this stores its current lasso polygon in node graph coordinates,
69+ /// plus a flag indicating if it has been dragged since the mousedown began.
70+ lasso_selection_curr : Option < ( Vec < DVec2 > , bool ) > ,
6771 /// Restore the selection before box selection if it is aborted
6872 selection_before_pointer_down : Vec < NodeId > ,
6973 /// If the grip icon is held during a drag, then shift without pushing other nodes
@@ -765,6 +769,15 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
765769 responses. add ( FrontendMessage :: UpdateBox { box_selection : None } ) ;
766770 return ;
767771 }
772+ // Abort a lasso selection
773+ if self . lasso_selection_curr . is_some ( ) {
774+ self . lasso_selection_curr = None ;
775+ responses. add ( NodeGraphMessage :: SelectedNodesSet {
776+ nodes : self . selection_before_pointer_down . clone ( ) ,
777+ } ) ;
778+ responses. add ( FrontendMessage :: UpdateLasso { lasso_selection : None } ) ;
779+ return ;
780+ }
768781 // Abort dragging a wire
769782 if self . wire_in_progress_from_connector . is_some ( ) {
770783 self . wire_in_progress_from_connector = None ;
@@ -974,7 +987,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
974987 if !shift_click && !alt_click {
975988 responses. add ( NodeGraphMessage :: SelectedNodesSet { nodes : Vec :: new ( ) } )
976989 }
977- self . box_selection_start = Some ( ( node_graph_point, false ) ) ;
990+
991+ if control_click {
992+ self . lasso_selection_curr = Some ( ( vec ! [ node_graph_point] , false ) ) ;
993+ } else {
994+ self . box_selection_start = Some ( ( node_graph_point, false ) ) ;
995+ }
996+
978997 self . update_node_graph_hints ( responses) ;
979998 }
980999 NodeGraphMessage :: PointerMove { shift } => {
@@ -1109,6 +1128,10 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
11091128 * box_selection_dragged = true ;
11101129 responses. add ( NodeGraphMessage :: UpdateBoxSelection ) ;
11111130 self . update_node_graph_hints ( responses) ;
1131+ } else if let Some ( ( _, lasso_selection_dragged) ) = & mut self . lasso_selection_curr {
1132+ * lasso_selection_dragged = true ;
1133+ responses. add ( NodeGraphMessage :: UpdateLassoSelection ) ;
1134+ self . update_node_graph_hints ( responses) ;
11121135 } else if self . reordering_import . is_some ( ) {
11131136 let Some ( modify_import_export) = network_interface. modify_import_export ( selection_network_path) else {
11141137 log:: error!( "Could not get modify import export in PointerMove" ) ;
@@ -1391,6 +1414,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
13911414 self . drag_start = None ;
13921415 self . begin_dragging = false ;
13931416 self . box_selection_start = None ;
1417+ self . lasso_selection_curr = None ;
13941418 self . wire_in_progress_from_connector = None ;
13951419 self . wire_in_progress_type = FrontendGraphDataType :: General ;
13961420 self . wire_in_progress_to_connector = None ;
@@ -1399,12 +1423,17 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
13991423 responses. add ( DocumentMessage :: EndTransaction ) ;
14001424 responses. add ( FrontendMessage :: UpdateWirePathInProgress { wire_path : None } ) ;
14011425 responses. add ( FrontendMessage :: UpdateBox { box_selection : None } ) ;
1426+ responses. add ( FrontendMessage :: UpdateLasso { lasso_selection : None } ) ;
14021427 responses. add ( FrontendMessage :: UpdateImportReorderIndex { index : None } ) ;
14031428 responses. add ( FrontendMessage :: UpdateExportReorderIndex { index : None } ) ;
14041429 self . update_node_graph_hints ( responses) ;
14051430 }
14061431 NodeGraphMessage :: PointerOutsideViewport { shift } => {
1407- 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 ( ) ) {
1432+ if self . drag_start . is_some ( )
1433+ || self . box_selection_start . is_some ( )
1434+ || self . lasso_selection_curr . is_some ( )
1435+ || ( self . wire_in_progress_from_connector . is_some ( ) && self . context_menu . is_none ( ) )
1436+ {
14081437 let _ = self . auto_panning . shift_viewport ( ipp, viewport, responses) ;
14091438 } else {
14101439 // Auto-panning
@@ -1956,6 +1985,88 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
19561985 responses. add ( FrontendMessage :: UpdateBox { box_selection } )
19571986 }
19581987 }
1988+ NodeGraphMessage :: UpdateLassoSelection => {
1989+ if let Some ( ( lasso_selection_curr, _) ) = & mut self . lasso_selection_curr {
1990+ // WARNING WARNING WARNING: this commented-out code is copy pasted from UpdateBoxSelection above and has not been edited for lasso
1991+ // The mouse button was released but we missed the pointer up event
1992+ // if ((e.buttons & 1) === 0) {
1993+ // completeBoxSelection();
1994+ // boxSelection = undefined;
1995+ // } else if ((e.buttons & 2) !== 0) {
1996+ // editor.handle.selectNodes(new BigUint64Array(previousSelection));
1997+ // boxSelection = undefined;
1998+ // }
1999+
2000+ let Some ( network_metadata) = network_interface. network_metadata ( selection_network_path) else {
2001+ log:: error!( "Could not get network metadata in UpdateBoxSelection" ) ;
2002+ return ;
2003+ } ;
2004+
2005+ {
2006+ let node_graph_point = network_metadata
2007+ . persistent_metadata
2008+ . navigation_metadata
2009+ . node_graph_to_viewport
2010+ . inverse ( )
2011+ . transform_point2 ( ipp. mouse . position ) ;
2012+
2013+ lasso_selection_curr. push ( node_graph_point) ;
2014+ }
2015+
2016+ let lasso_selection_viewport: Vec < DVec2 > = lasso_selection_curr
2017+ . iter ( )
2018+ . map ( |selection_point| network_metadata. persistent_metadata . navigation_metadata . node_graph_to_viewport . transform_point2 ( * selection_point) )
2019+ . collect ( ) ;
2020+
2021+ let shift = ipp. keyboard . get ( Key :: Shift as usize ) ;
2022+ let alt = ipp. keyboard . get ( Key :: Alt as usize ) ;
2023+ let Some ( selected_nodes) = network_interface. selected_nodes_in_nested_network ( selection_network_path) else {
2024+ log:: error!( "Could not get selected nodes in UpdateBoxSelection" ) ;
2025+ return ;
2026+ } ;
2027+ let previous_selection = selected_nodes. selected_nodes_ref ( ) . iter ( ) . cloned ( ) . collect :: < HashSet < _ > > ( ) ;
2028+ let mut nodes = if shift || alt {
2029+ selected_nodes. selected_nodes_ref ( ) . iter ( ) . cloned ( ) . collect :: < HashSet < _ > > ( )
2030+ } else {
2031+ HashSet :: new ( )
2032+ } ;
2033+ let all_nodes = network_metadata. persistent_metadata . node_metadata . keys ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
2034+ let path: Vec < PathSeg > = {
2035+ fn points_to_polygon ( points : & [ DVec2 ] ) -> Vec < PathSeg > {
2036+ points
2037+ . windows ( 2 )
2038+ . map ( |w| PathSeg :: Line ( Line :: new ( dvec2_to_point ( w[ 0 ] ) , dvec2_to_point ( w[ 1 ] ) ) ) )
2039+ . chain ( std:: iter:: once ( PathSeg :: Line ( Line :: new (
2040+ dvec2_to_point ( * points. last ( ) . unwrap ( ) ) ,
2041+ dvec2_to_point ( * points. first ( ) . unwrap ( ) ) ,
2042+ ) ) ) )
2043+ . collect ( )
2044+ }
2045+ points_to_polygon ( lasso_selection_curr)
2046+ } ;
2047+ for node_id in all_nodes {
2048+ let Some ( click_targets) = network_interface. node_click_targets ( & node_id, selection_network_path) else {
2049+ log:: error!( "Could not get transient metadata for node {node_id}" ) ;
2050+ continue ;
2051+ } ;
2052+ if click_targets. node_click_target . intersect_path ( || path. iter ( ) . cloned ( ) , DAffine2 :: IDENTITY ) {
2053+ if alt {
2054+ nodes. remove ( & node_id) ;
2055+ } else {
2056+ nodes. insert ( node_id) ;
2057+ }
2058+ }
2059+ }
2060+ if nodes != previous_selection {
2061+ responses. add ( NodeGraphMessage :: SelectedNodesSet {
2062+ nodes : nodes. into_iter ( ) . collect :: < Vec < _ > > ( ) ,
2063+ } ) ;
2064+ }
2065+ responses. add ( FrontendMessage :: UpdateLasso {
2066+ lasso_selection : Some ( LassoSelection :: from_iter ( lasso_selection_viewport. into_iter ( ) ) ) ,
2067+ } )
2068+ }
2069+ }
19592070 NodeGraphMessage :: UpdateImportsExports => {
19602071 let imports = network_interface. frontend_imports ( breadcrumb_network_path) ;
19612072 let exports = network_interface. frontend_exports ( breadcrumb_network_path) ;
@@ -2711,11 +2822,12 @@ impl NodeGraphMessageHandler {
27112822 // Node gragging is in progress (having already moved at least one pixel from the mouse down position)
27122823 let dragging_nodes = self . drag_start . as_ref ( ) . is_some_and ( |( _, dragged) | * dragged) ;
27132824
2714- // A box selection is in progress
2715- let dragging_box_selection = self . box_selection_start . is_some_and ( |( _, box_selection_dragged) | box_selection_dragged) ;
2825+ // A box or lasso selection is in progress
2826+ let dragging_selection = self . box_selection_start . as_ref ( ) . is_some_and ( |( _, box_selection_dragged) | * box_selection_dragged)
2827+ || self . lasso_selection_curr . as_ref ( ) . is_some_and ( |( _, lasso_selection_dragged) | * lasso_selection_dragged) ;
27162828
27172829 // Cancel the ongoing action
2718- if wiring || dragging_nodes || dragging_box_selection {
2830+ if wiring || dragging_nodes || dragging_selection {
27192831 let hint_data = HintData ( vec ! [ HintGroup ( vec![ HintInfo :: mouse( MouseMotion :: Rmb , "" ) , HintInfo :: keys( [ Key :: Escape ] , "Cancel" ) . prepend_slash( ) ] ) ] ) ;
27202832 responses. add ( FrontendMessage :: UpdateInputHints { hint_data } ) ;
27212833 return ;
@@ -2760,6 +2872,7 @@ impl Default for NodeGraphMessageHandler {
27602872 node_has_moved_in_drag : false ,
27612873 shift_without_push : false ,
27622874 box_selection_start : None ,
2875+ lasso_selection_curr : None ,
27632876 drag_start_chain_nodes : Vec :: new ( ) ,
27642877 selection_before_pointer_down : Vec :: new ( ) ,
27652878 disconnecting : None ,
@@ -2790,6 +2903,7 @@ impl PartialEq for NodeGraphMessageHandler {
27902903 && self . begin_dragging == other. begin_dragging
27912904 && self . node_has_moved_in_drag == other. node_has_moved_in_drag
27922905 && self . box_selection_start == other. box_selection_start
2906+ && self . lasso_selection_curr == other. lasso_selection_curr
27932907 && self . initial_disconnecting == other. initial_disconnecting
27942908 && self . select_if_not_dragged == other. select_if_not_dragged
27952909 && self . wire_in_progress_from_connector == other. wire_in_progress_from_connector
0 commit comments