11use super :: tool_prelude:: * ;
22use crate :: consts:: { BOUNDS_SELECT_THRESHOLD , DEFAULT_STROKE_WIDTH , SNAP_POINT_TOLERANCE } ;
33use crate :: messages:: portfolio:: document:: graph_operation:: utility_types:: TransformIn ;
4+ use crate :: messages:: portfolio:: document:: node_graph:: document_node_definitions:: DefinitionIdentifier ;
45use crate :: messages:: portfolio:: document:: overlays:: utility_types:: OverlayContext ;
56use crate :: messages:: portfolio:: document:: utility_types:: document_metadata:: LayerNodeIdentifier ;
67use crate :: messages:: tool:: common_functionality:: auto_panning:: AutoPanning ;
@@ -22,9 +23,10 @@ use crate::messages::tool::common_functionality::snapping::{self, SnapCandidateP
2223use crate :: messages:: tool:: common_functionality:: transformation_cage:: { BoundingBoxManager , EdgeBool } ;
2324use crate :: messages:: tool:: common_functionality:: utility_functions:: { closest_point, resize_bounds, rotate_bounds, skew_bounds, transforming_transform_cage} ;
2425use graph_craft:: document:: NodeId ;
25- use graphene_std :: Color ;
26+ use graph_craft :: document :: value :: TaggedValue ;
2627use graphene_std:: renderer:: Quad ;
2728use graphene_std:: vector:: misc:: { ArcType , GridType , SpiralType } ;
29+ use graphene_std:: { Color , NodeInputDecleration } ;
2830use std:: vec;
2931
3032#[ derive( Default , ExtractField ) ]
@@ -127,6 +129,7 @@ fn create_sides_widget(vertices: u32) -> WidgetInstance {
127129 }
128130 . into ( )
129131 } )
132+ . on_commit ( |_| DocumentMessage :: StartTransaction . into ( ) )
130133 . widget_instance ( )
131134}
132135
@@ -141,6 +144,7 @@ fn create_turns_widget(turns: f64) -> WidgetInstance {
141144 }
142145 . into ( )
143146 } )
147+ . on_commit ( |_| DocumentMessage :: StartTransaction . into ( ) )
144148 . widget_instance ( )
145149}
146150
@@ -243,6 +247,7 @@ fn create_arrow_shaft_width_widget(shaft_width: f64) -> WidgetInstance {
243247 }
244248 . into ( )
245249 } )
250+ . on_commit ( |_| DocumentMessage :: StartTransaction . into ( ) )
246251 . widget_instance ( )
247252}
248253
@@ -258,6 +263,7 @@ fn create_arrow_head_width_widget(head_width: f64) -> WidgetInstance {
258263 }
259264 . into ( )
260265 } )
266+ . on_commit ( |_| DocumentMessage :: StartTransaction . into ( ) )
261267 . widget_instance ( )
262268}
263269
@@ -273,6 +279,7 @@ fn create_arrow_head_length_widget(head_length: f64) -> WidgetInstance {
273279 }
274280 . into ( )
275281 } )
282+ . on_commit ( |_| DocumentMessage :: StartTransaction . into ( ) )
276283 . widget_instance ( )
277284}
278285
@@ -312,6 +319,118 @@ fn create_grid_type_widget(grid_type: GridType) -> WidgetInstance {
312319 RadioInput :: new ( entries) . selected_index ( Some ( grid_type as u32 ) ) . widget_instance ( )
313320}
314321
322+ /// Mirrors the per-shape parameters (and `shape_type` itself) from the first selected non-artboard layer into the
323+ /// control bar's option state. Detects the layer's shape by trying each generator's proto node, then reads only the
324+ /// inputs relevant to that shape. Returns whether anything in `options` (or `tool_data.current_shape`) changed.
325+ /// The caller decides whether to dispatch a layout refresh.
326+ fn sync_shape_options_from_selection ( options : & mut ShapeToolOptions , tool_data : & mut ShapeToolData , document : & DocumentMessageHandler ) -> bool {
327+ use graphene_std:: vector:: generator_nodes:: * ;
328+
329+ let Some ( layer) = document. network_interface . selected_nodes ( ) . selected_layers_except_artboards ( & document. network_interface ) . next ( ) else {
330+ return false ;
331+ } ;
332+ let layer_view = graph_modification_utils:: NodeGraphLayer :: new ( layer, & document. network_interface ) ;
333+ let proto = DefinitionIdentifier :: ProtoNode ;
334+
335+ // Map each generator's proto node to the corresponding `ShapeType`.
336+ // First match wins. Only includes modes from the Shape tool's mode dropdown.
337+ let Some ( shape_type) = [
338+ ( regular_polygon:: IDENTIFIER , ShapeType :: Polygon ) ,
339+ ( star:: IDENTIFIER , ShapeType :: Star ) ,
340+ ( circle:: IDENTIFIER , ShapeType :: Circle ) ,
341+ ( arc:: IDENTIFIER , ShapeType :: Arc ) ,
342+ ( spiral:: IDENTIFIER , ShapeType :: Spiral ) ,
343+ ( grid:: IDENTIFIER , ShapeType :: Grid ) ,
344+ ( arrow:: IDENTIFIER , ShapeType :: Arrow ) ,
345+ ]
346+ . into_iter ( )
347+ . find_map ( |( id, shape) | layer_view. upstream_node_id_from_name ( & proto ( id) ) . map ( |_| shape) ) else {
348+ return false ;
349+ } ;
350+
351+ let mut changed = false ;
352+
353+ if options. shape_type != shape_type {
354+ options. shape_type = shape_type;
355+ tool_data. current_shape = shape_type;
356+ changed = true ;
357+ }
358+
359+ // Only the shapes whose control bar exposes per-shape parameters need a sync below.
360+ // The rest (Ellipse, Rectangle, Line) just keep `shape_type` in step and rely on the shared Stroke/Fill controls.
361+ match shape_type {
362+ ShapeType :: Polygon | ShapeType :: Star => {
363+ let id = if shape_type == ShapeType :: Polygon { regular_polygon:: IDENTIFIER } else { star:: IDENTIFIER } ;
364+ // Both `regular_polygon` and `star` are generic over `T: AsU64`, but the control bar widget always writes `u32`,
365+ // and existing call sites (e.g. `polygon_shape.rs`) read it back as `TaggedValue::U32`.
366+ let index = if shape_type == ShapeType :: Polygon {
367+ regular_polygon:: SidesInput :: < u32 > :: INDEX
368+ } else {
369+ star:: SidesInput :: < u32 > :: INDEX
370+ } ;
371+ if let Some ( & TaggedValue :: U32 ( sides) ) = layer_view. find_input ( & proto ( id) , index)
372+ && options. vertices != sides
373+ {
374+ options. vertices = sides;
375+ changed = true ;
376+ }
377+ }
378+ ShapeType :: Arc => {
379+ if let Some ( & TaggedValue :: ArcType ( arc_type) ) = layer_view. find_input ( & proto ( arc:: IDENTIFIER ) , arc:: ArcTypeInput :: INDEX )
380+ && options. arc_type != arc_type
381+ {
382+ options. arc_type = arc_type;
383+ changed = true ;
384+ }
385+ }
386+ ShapeType :: Spiral => {
387+ if let Some ( & TaggedValue :: SpiralType ( spiral_type) ) = layer_view. find_input ( & proto ( spiral:: IDENTIFIER ) , spiral:: SpiralTypeInput :: INDEX )
388+ && options. spiral_type != spiral_type
389+ {
390+ options. spiral_type = spiral_type;
391+ changed = true ;
392+ }
393+ if let Some ( & TaggedValue :: F64 ( turns) ) = layer_view. find_input ( & proto ( spiral:: IDENTIFIER ) , spiral:: TurnsInput :: INDEX )
394+ && options. turns != turns
395+ {
396+ options. turns = turns;
397+ changed = true ;
398+ }
399+ }
400+ ShapeType :: Grid => {
401+ if let Some ( & TaggedValue :: GridType ( grid_type) ) = layer_view. find_input ( & proto ( grid:: IDENTIFIER ) , grid:: GridTypeInput :: INDEX )
402+ && options. grid_type != grid_type
403+ {
404+ options. grid_type = grid_type;
405+ changed = true ;
406+ }
407+ }
408+ ShapeType :: Arrow => {
409+ if let Some ( & TaggedValue :: F64 ( shaft) ) = layer_view. find_input ( & proto ( arrow:: IDENTIFIER ) , arrow:: ShaftWidthInput :: INDEX )
410+ && options. arrow_shaft_width != shaft
411+ {
412+ options. arrow_shaft_width = shaft;
413+ changed = true ;
414+ }
415+ if let Some ( & TaggedValue :: F64 ( head_w) ) = layer_view. find_input ( & proto ( arrow:: IDENTIFIER ) , arrow:: HeadWidthInput :: INDEX )
416+ && options. arrow_head_width != head_w
417+ {
418+ options. arrow_head_width = head_w;
419+ changed = true ;
420+ }
421+ if let Some ( & TaggedValue :: F64 ( head_l) ) = layer_view. find_input ( & proto ( arrow:: IDENTIFIER ) , arrow:: HeadLengthInput :: INDEX )
422+ && options. arrow_head_length != head_l
423+ {
424+ options. arrow_head_length = head_l;
425+ changed = true ;
426+ }
427+ }
428+ ShapeType :: Ellipse | ShapeType :: Rectangle | ShapeType :: Line | ShapeType :: Circle => { }
429+ }
430+
431+ changed
432+ }
433+
315434impl LayoutHolder for ShapeTool {
316435 fn layout ( & self ) -> Layout {
317436 let mut widgets = vec ! [ ] ;
@@ -416,11 +535,28 @@ impl LayoutHolder for ShapeTool {
416535#[ message_handler_data]
417536impl < ' a > MessageHandler < ToolMessage , & mut ToolActionMessageContext < ' a > > for ShapeTool {
418537 fn process_message ( & mut self , message : ToolMessage , responses : & mut VecDeque < Message > , context : & mut ToolActionMessageContext < ' a > ) {
538+ use graphene_std:: vector:: generator_nodes:: * ;
539+
419540 if matches ! ( & message, ToolMessage :: Shape ( ShapeToolMessage :: SelectionChanged ) ) {
541+ if !matches ! ( self . fsm_state, ShapeToolFsmState :: Ready ( _) ) {
542+ return ;
543+ }
544+
545+ let mut needs_refresh = false ;
546+
547+ // Stroke weight is shape-agnostic. Sync it regardless of which (if any) shape proto node the layer has.
420548 if let Some ( weight) = graph_modification_utils:: first_selected_stroke_weight ( context. document )
421549 && self . options . line_weight != weight
422550 {
423551 self . options . line_weight = weight;
552+ needs_refresh = true ;
553+ }
554+
555+ // Detect which shape the first selected layer is by checking for each generator's proto node, then mirror
556+ // the control bar's `shape_type` into that and pull the shape's parameters into the matching control bar fields.
557+ needs_refresh |= sync_shape_options_from_selection ( & mut self . options , & mut self . tool_data , context. document ) ;
558+
559+ if needs_refresh {
424560 self . send_layout ( responses, LayoutTarget :: ToolOptions ) ;
425561 }
426562 return ;
@@ -461,27 +597,48 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Shap
461597 }
462598 ShapeOptionsUpdate :: Vertices ( vertices) => {
463599 self . options . vertices = vertices;
600+ // Push to whichever sides-bearing shape (Polygon or Star) the control bar's `shape_type` currently targets.
601+ // `set_proto_node_input_for_selected_layers` skips selected layers without that proto node, making it a no-op.
602+ let ( id, index) = match self . options . shape_type {
603+ ShapeType :: Polygon => ( regular_polygon:: IDENTIFIER , regular_polygon:: SidesInput :: < u32 > :: INDEX ) ,
604+ ShapeType :: Star => ( star:: IDENTIFIER , star:: SidesInput :: < u32 > :: INDEX ) ,
605+ _ => return ,
606+ } ;
607+ graph_modification_utils:: set_proto_node_input_for_selected_layers ( context. document , id, index, TaggedValue :: U32 ( vertices) , responses) ;
464608 }
465609 ShapeOptionsUpdate :: ArcType ( arc_type) => {
466610 self . options . arc_type = arc_type;
611+ graph_modification_utils:: set_proto_node_input_for_selected_layers ( context. document , arc:: IDENTIFIER , arc:: ArcTypeInput :: INDEX , TaggedValue :: ArcType ( arc_type) , responses) ;
467612 }
468613 ShapeOptionsUpdate :: SpiralType ( spiral_type) => {
469614 self . options . spiral_type = spiral_type;
615+ graph_modification_utils:: set_proto_node_input_for_selected_layers (
616+ context. document ,
617+ spiral:: IDENTIFIER ,
618+ spiral:: SpiralTypeInput :: INDEX ,
619+ TaggedValue :: SpiralType ( spiral_type) ,
620+ responses,
621+ ) ;
470622 }
471623 ShapeOptionsUpdate :: Turns ( turns) => {
472624 self . options . turns = turns;
625+ graph_modification_utils:: set_proto_node_input_for_selected_layers ( context. document , spiral:: IDENTIFIER , spiral:: TurnsInput :: INDEX , TaggedValue :: F64 ( turns) , responses) ;
473626 }
474627 ShapeOptionsUpdate :: GridType ( grid_type) => {
475628 self . options . grid_type = grid_type;
629+ graph_modification_utils:: set_proto_node_input_for_selected_layers ( context. document , grid:: IDENTIFIER , grid:: GridTypeInput :: INDEX , TaggedValue :: GridType ( grid_type) , responses) ;
476630 }
477631 ShapeOptionsUpdate :: ArrowShaftWidth ( shaft_width) => {
478632 self . options . arrow_shaft_width = shaft_width;
633+ graph_modification_utils:: set_proto_node_input_for_selected_layers ( context. document , arrow:: IDENTIFIER , arrow:: ShaftWidthInput :: INDEX , TaggedValue :: F64 ( shaft_width) , responses) ;
479634 }
480635 ShapeOptionsUpdate :: ArrowHeadWidth ( head_width) => {
481636 self . options . arrow_head_width = head_width;
637+ graph_modification_utils:: set_proto_node_input_for_selected_layers ( context. document , arrow:: IDENTIFIER , arrow:: HeadWidthInput :: INDEX , TaggedValue :: F64 ( head_width) , responses) ;
482638 }
483639 ShapeOptionsUpdate :: ArrowHeadLength ( head_length) => {
484640 self . options . arrow_head_length = head_length;
641+ graph_modification_utils:: set_proto_node_input_for_selected_layers ( context. document , arrow:: IDENTIFIER , arrow:: HeadLengthInput :: INDEX , TaggedValue :: F64 ( head_length) , responses) ;
485642 }
486643 }
487644
0 commit comments