@@ -8,6 +8,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
88use crate :: messages:: tool:: common_functionality:: auto_panning:: AutoPanning ;
99use crate :: messages:: tool:: common_functionality:: graph_modification_utils:: { NodeGraphLayer , get_gradient} ;
1010use crate :: messages:: tool:: common_functionality:: snapping:: { SnapCandidatePoint , SnapConstraint , SnapData , SnapManager , SnapTypeConfiguration } ;
11+ use graphene_std:: raster:: color:: Color ;
1112use graphene_std:: vector:: style:: { Fill , Gradient , GradientStops , GradientType } ;
1213
1314#[ derive( Default , ExtractField ) ]
@@ -38,6 +39,10 @@ pub enum GradientToolMessage {
3839 PointerMove { constrain_axis : Key , lock_angle : Key } ,
3940 PointerOutsideViewport { constrain_axis : Key , lock_angle : Key } ,
4041 PointerUp ,
42+ StartTransactionForColorStop ,
43+ CommitTransactionForColorStop ,
44+ CloseStopColorPicker ,
45+ UpdateStopColor { color : Color } ,
4146 UpdateOptions { options : GradientOptionsUpdate } ,
4247}
4348
@@ -63,29 +68,59 @@ impl ToolMetadata for GradientTool {
6368#[ message_handler_data]
6469impl < ' a > MessageHandler < ToolMessage , & mut ToolActionMessageContext < ' a > > for GradientTool {
6570 fn process_message ( & mut self , message : ToolMessage , responses : & mut VecDeque < Message > , context : & mut ToolActionMessageContext < ' a > ) {
66- let ToolMessage :: Gradient ( GradientToolMessage :: UpdateOptions { options } ) = message else {
67- self . fsm_state . process_event ( message, & mut self . data , context, & self . options , responses, false ) ;
68-
69- let has_gradient = has_gradient_on_selected_layers ( context. document ) ;
70- if has_gradient != self . data . has_selected_gradient {
71- self . data . has_selected_gradient = has_gradient;
72- responses. add ( ToolMessage :: RefreshToolOptions ) ;
71+ match message {
72+ ToolMessage :: Gradient ( GradientToolMessage :: UpdateOptions { options } ) => match options {
73+ GradientOptionsUpdate :: Type ( gradient_type) => {
74+ self . options . gradient_type = gradient_type;
75+ apply_gradient_update ( & mut self . data , context, responses, |g| g. gradient_type != gradient_type, |g| g. gradient_type = gradient_type) ;
76+ responses. add ( ToolMessage :: UpdateHints ) ;
77+ responses. add ( ToolMessage :: UpdateCursor ) ;
78+ }
79+ GradientOptionsUpdate :: ReverseStops => {
80+ apply_gradient_update ( & mut self . data , context, responses, |_| true , |g| g. stops = g. stops . reversed ( ) ) ;
81+ }
82+ GradientOptionsUpdate :: ReverseDirection => {
83+ apply_gradient_update ( & mut self . data , context, responses, |_| true , |g| std:: mem:: swap ( & mut g. start , & mut g. end ) ) ;
84+ }
85+ } ,
86+ ToolMessage :: Gradient ( GradientToolMessage :: StartTransactionForColorStop ) => {
87+ if self . data . color_picker_transaction_open {
88+ responses. add ( DocumentMessage :: EndTransaction ) ;
89+ }
90+ responses. add ( DocumentMessage :: StartTransaction ) ;
91+ self . data . color_picker_transaction_open = true ;
7392 }
74-
75- return ;
76- } ;
77- match options {
78- GradientOptionsUpdate :: Type ( gradient_type) => {
79- self . options . gradient_type = gradient_type;
80- apply_gradient_update ( & mut self . data , context, responses, |g| g. gradient_type != gradient_type, |g| g. gradient_type = gradient_type) ;
81- responses. add ( ToolMessage :: UpdateHints ) ;
82- responses. add ( ToolMessage :: UpdateCursor ) ;
93+ ToolMessage :: Gradient ( GradientToolMessage :: CommitTransactionForColorStop ) => {
94+ if self . data . color_picker_transaction_open {
95+ responses. add ( DocumentMessage :: EndTransaction ) ;
96+ self . data . color_picker_transaction_open = false ;
97+ }
98+ }
99+ ToolMessage :: Gradient ( GradientToolMessage :: UpdateStopColor { color } ) => {
100+ if let Some ( stop_index) = self . data . color_picker_editing_color_stop
101+ && let Some ( selected_gradient) = & mut self . data . selected_gradient
102+ && stop_index < selected_gradient. gradient . stops . color . len ( )
103+ {
104+ selected_gradient. gradient . stops . color [ stop_index] = color;
105+ selected_gradient. render_gradient ( responses) ;
106+ responses. add ( PropertiesPanelMessage :: Refresh ) ;
107+ }
83108 }
84- GradientOptionsUpdate :: ReverseStops => {
85- apply_gradient_update ( & mut self . data , context, responses, |_| true , |g| g. stops = g. stops . reversed ( ) ) ;
109+ ToolMessage :: Gradient ( GradientToolMessage :: CloseStopColorPicker ) => {
110+ if self . data . color_picker_transaction_open {
111+ responses. add ( DocumentMessage :: EndTransaction ) ;
112+ self . data . color_picker_transaction_open = false ;
113+ }
114+ self . data . color_picker_editing_color_stop = None ;
86115 }
87- GradientOptionsUpdate :: ReverseDirection => {
88- apply_gradient_update ( & mut self . data , context, responses, |_| true , |g| std:: mem:: swap ( & mut g. start , & mut g. end ) ) ;
116+ _ => {
117+ self . fsm_state . process_event ( message, & mut self . data , context, & self . options , responses, false ) ;
118+
119+ let has_gradient = has_gradient_on_selected_layers ( context. document ) ;
120+ if has_gradient != self . data . has_selected_gradient {
121+ self . data . has_selected_gradient = has_gradient;
122+ responses. add ( ToolMessage :: RefreshToolOptions ) ;
123+ }
89124 }
90125 }
91126 }
@@ -515,6 +550,8 @@ struct GradientToolData {
515550 auto_pan_shift : DVec2 ,
516551 gradient_angle : f64 ,
517552 has_selected_gradient : bool ,
553+ color_picker_editing_color_stop : Option < usize > ,
554+ color_picker_transaction_open : bool ,
518555}
519556
520557impl Fsm for GradientToolFsmState {
@@ -723,9 +760,31 @@ impl Fsm for GradientToolFsmState {
723760 let snap_data = SnapData :: new ( document, input, viewport) ;
724761 tool_data. snap_manager . draw_overlays ( snap_data, & mut overlay_context) ;
725762
763+ // Update color picker position if active (keeps it anchored to the stop during pan/zoom)
764+ if let Some ( stop_index) = tool_data. color_picker_editing_color_stop
765+ && let Some ( selected_gradient) = tool_data. selected_gradient . as_ref ( )
766+ && let Some ( layer) = selected_gradient. layer
767+ {
768+ let transform = gradient_space_transform ( layer, document) ;
769+ let gradient = & selected_gradient. gradient ;
770+ if stop_index < gradient. stops . position . len ( ) {
771+ let color = gradient. stops . color [ stop_index] . to_gamma_srgb ( ) ;
772+ let position = gradient. stops . position [ stop_index] ;
773+ let DVec2 { x, y } = transform. transform_point2 ( gradient. start . lerp ( gradient. end , position) ) ;
774+ responses. add ( FrontendMessage :: UpdateGradientStopColorPickerPosition { color, x, y } ) ;
775+ }
776+ }
777+
726778 self
727779 }
728780 ( GradientToolFsmState :: Ready { .. } , GradientToolMessage :: SelectionChanged ) => {
781+ if tool_data. color_picker_editing_color_stop . is_some ( ) {
782+ if tool_data. color_picker_transaction_open {
783+ responses. add ( DocumentMessage :: EndTransaction ) ;
784+ tool_data. color_picker_transaction_open = false ;
785+ }
786+ tool_data. color_picker_editing_color_stop = None ;
787+ }
729788 tool_data. selected_gradient = None ;
730789 GradientToolFsmState :: Ready {
731790 hovering : GradientHoverTarget :: None ,
@@ -737,11 +796,45 @@ impl Fsm for GradientToolFsmState {
737796 let drag_start_viewport = document. metadata ( ) . document_to_viewport . transform_point2 ( tool_data. drag_start ) ;
738797 if input. mouse . position . distance ( drag_start_viewport) <= DRAG_THRESHOLD
739798 && let Some ( selected_gradient) = & mut tool_data. selected_gradient
740- && let GradientDragTarget :: Midpoint ( index) = selected_gradient. dragging
741799 {
742- selected_gradient. gradient . stops . midpoint [ index] = 0.5 ;
743- selected_gradient. render_gradient ( responses) ;
744- responses. add ( PropertiesPanelMessage :: Refresh ) ;
800+ match selected_gradient. dragging {
801+ GradientDragTarget :: Midpoint ( index) => {
802+ selected_gradient. gradient . stops . midpoint [ index] = 0.5 ;
803+ selected_gradient. render_gradient ( responses) ;
804+ responses. add ( PropertiesPanelMessage :: Refresh ) ;
805+ }
806+ GradientDragTarget :: Start | GradientDragTarget :: End | GradientDragTarget :: Stop ( _) => {
807+ // Find the stop index from the drag target
808+ let stop_index = match selected_gradient. dragging {
809+ GradientDragTarget :: Stop ( i) => Some ( i) ,
810+ GradientDragTarget :: Start => selected_gradient. gradient . stops . position . iter ( ) . position ( |& p| p. abs ( ) < f64:: EPSILON * 1000. ) ,
811+ GradientDragTarget :: End => selected_gradient. gradient . stops . position . iter ( ) . position ( |& p| ( 1. - p) . abs ( ) < f64:: EPSILON * 1000. ) ,
812+ _ => None ,
813+ } ;
814+ if let Some ( stop_index) = stop_index
815+ && stop_index < selected_gradient. gradient . stops . color . len ( )
816+ {
817+ // Dismiss any existing color picker first
818+ if tool_data. color_picker_editing_color_stop . is_some ( ) && tool_data. color_picker_transaction_open {
819+ responses. add ( DocumentMessage :: EndTransaction ) ;
820+ tool_data. color_picker_transaction_open = false ;
821+ }
822+
823+ let stop_pos = selected_gradient. gradient . stops . position [ stop_index] ;
824+ let viewport_pos = selected_gradient
825+ . transform
826+ . transform_point2 ( selected_gradient. gradient . start . lerp ( selected_gradient. gradient . end , stop_pos) ) ;
827+ let color = selected_gradient. gradient . stops . color [ stop_index] . to_gamma_srgb ( ) ;
828+ tool_data. color_picker_editing_color_stop = Some ( stop_index) ;
829+ responses. add ( FrontendMessage :: UpdateGradientStopColorPickerPosition {
830+ color,
831+ x : viewport_pos. x ,
832+ y : viewport_pos. y ,
833+ } ) ;
834+ }
835+ }
836+ _ => { }
837+ }
745838 }
746839 self
747840 }
@@ -1178,15 +1271,21 @@ impl Fsm for GradientToolFsmState {
11781271 tool_data. selected_gradient = None ;
11791272 responses. add ( OverlaysMessage :: Draw ) ;
11801273
1274+ dismiss_color_stop_color_picker ( tool_data, responses) ;
1275+
1276+ GradientToolFsmState :: Ready {
1277+ hovering : GradientHoverTarget :: None ,
1278+ selected : GradientSelectedTarget :: None ,
1279+ }
1280+ }
1281+ ( _, GradientToolMessage :: Abort ) => {
1282+ dismiss_color_stop_color_picker ( tool_data, responses) ;
1283+
11811284 GradientToolFsmState :: Ready {
11821285 hovering : GradientHoverTarget :: None ,
11831286 selected : GradientSelectedTarget :: None ,
11841287 }
11851288 }
1186- ( _, GradientToolMessage :: Abort ) => GradientToolFsmState :: Ready {
1187- hovering : GradientHoverTarget :: None ,
1188- selected : GradientSelectedTarget :: None ,
1189- } ,
11901289 _ => self ,
11911290 }
11921291 }
@@ -1273,6 +1372,16 @@ impl Fsm for GradientToolFsmState {
12731372 }
12741373}
12751374
1375+ fn dismiss_color_stop_color_picker ( tool_data : & mut GradientToolData , responses : & mut VecDeque < Message > ) {
1376+ if tool_data. color_picker_editing_color_stop . is_some ( ) {
1377+ if tool_data. color_picker_transaction_open {
1378+ responses. add ( DocumentMessage :: EndTransaction ) ;
1379+ tool_data. color_picker_transaction_open = false ;
1380+ }
1381+ tool_data. color_picker_editing_color_stop = None ;
1382+ }
1383+ }
1384+
12761385fn detect_hover_target ( mouse : DVec2 , document : & DocumentMessageHandler ) -> GradientHoverTarget {
12771386 let stop_tolerance = ( MANIPULATOR_GROUP_MARKER_SIZE * 2. ) . powi ( 2 ) ;
12781387 let midpoint_tolerance = GRADIENT_MIDPOINT_DIAMOND_RADIUS . powi ( 2 ) ;
@@ -1380,7 +1489,7 @@ fn apply_gradient_update(
13801489 }
13811490
13821491 if transaction_started {
1383- responses. add ( DocumentMessage :: AddTransaction ) ;
1492+ responses. add ( DocumentMessage :: EndTransaction ) ;
13841493 }
13851494 if let Some ( selected_gradient) = & mut data. selected_gradient
13861495 && let Some ( layer) = selected_gradient. layer
0 commit comments