@@ -32,7 +32,6 @@ pub struct TextTool {
3232
3333pub struct TextOptions {
3434 font_size : f64 ,
35- line_height_ratio : f64 ,
3635 character_spacing : f64 ,
3736 font : Font ,
3837 fill : ToolColorOptions ,
@@ -44,7 +43,6 @@ impl Default for TextOptions {
4443 fn default ( ) -> Self {
4544 Self {
4645 font_size : 24. ,
47- line_height_ratio : 1.2 ,
4846 character_spacing : 0. ,
4947 font : Font :: new ( graphene_std:: consts:: DEFAULT_FONT_FAMILY . into ( ) , graphene_std:: consts:: DEFAULT_FONT_STYLE . into ( ) ) ,
5048 fill : ToolColorOptions :: new_primary ( ) ,
@@ -84,7 +82,6 @@ pub enum TextOptionsUpdate {
8482 FillColorType ( ToolColorType ) ,
8583 Font { font : Font } ,
8684 FontSize ( f64 ) ,
87- LineHeightRatio ( f64 ) ,
8885 Align ( TextAlign ) ,
8986 WorkingColors ( Option < Color > , Option < Color > ) ,
9087}
@@ -101,36 +98,63 @@ impl ToolMetadata for TextTool {
10198 }
10299}
103100
104- fn create_text_widgets ( tool : & TextTool , font_catalog : & FontCatalog ) -> Vec < WidgetInstance > {
105- fn update_options ( font : Font , commit_style : Option < String > ) -> impl Fn ( & ( ) ) -> Message + Clone {
106- let mut font = font;
107- if let Some ( style) = commit_style {
108- font. font_style = style;
101+ fn create_text_widgets ( tool : & TextTool , font_catalog : & FontCatalog , document : & DocumentMessageHandler ) -> Vec < WidgetInstance > {
102+ // If a single text layer is selected, the toolbar's font/style menus drive that layer's text node directly, going through the
103+ // same code path as the Properties panel (LoadFontData + SetInputValue, with closest_style and font_style_to_restore bookkeeping).
104+ // Otherwise the menus only update the toolbar option for the next created text.
105+ let text_node_id = can_edit_selected ( document) . and_then ( |layer| graph_modification_utils:: get_text_id ( layer, & document. network_interface ) ) ;
106+
107+ let font_input_index = graphene_std:: text:: text:: FontInput :: INDEX ;
108+ let apply_font = move |new_font : Font | -> Message {
109+ match text_node_id {
110+ Some ( node_id) => NodeGraphMessage :: SetInputValue {
111+ node_id,
112+ input_index : font_input_index,
113+ value : TaggedValue :: Font ( new_font) ,
114+ }
115+ . into ( ) ,
116+ None => TextToolMessage :: UpdateOptions {
117+ options : TextOptionsUpdate :: Font { font : new_font } ,
118+ }
119+ . into ( ) ,
109120 }
110-
111- move |_| {
112- TextToolMessage :: UpdateOptions {
113- options : TextOptionsUpdate :: Font { font : font. clone ( ) } ,
121+ } ;
122+ let preview_font = move |new_font : Font | -> Message {
123+ Message :: Batched {
124+ messages : Box :: new ( [ PortfolioMessage :: LoadFontData { font : new_font. clone ( ) } . into ( ) , apply_font ( new_font) ] ) ,
125+ }
126+ } ;
127+ let commit_font = move |new_font : Font | -> Message {
128+ match text_node_id {
129+ Some ( _) => DeferMessage :: AfterGraphRun {
130+ messages : vec ! [ apply_font( new_font) , DocumentMessage :: AddTransaction . into( ) ] ,
114131 }
115- . into ( )
132+ . into ( ) ,
133+ None => apply_font ( new_font) ,
116134 }
117- }
135+ } ;
118136
119137 let font = DropdownInput :: new ( vec ! [
120138 font_catalog
121139 . 0
122140 . iter( )
123141 . map( |family| {
124- let font = Font :: new( family. name. clone( ) , tool. options. font. font_style. clone( ) ) ;
125- let commit_style = font_catalog. find_font_style_in_catalog( & tool. options. font) . map( |style| style. to_named_style( ) ) ;
126- let update = update_options( font. clone( ) , None ) ;
127- let commit = update_options( font, commit_style) ;
142+ let current_font = & tool. options. font;
143+ let mut new_font = Font :: new( family. name. clone( ) , current_font. font_style_to_restore. clone( ) . unwrap_or_else( || current_font. font_style. clone( ) ) ) ;
144+ new_font. font_style_to_restore = current_font. font_style_to_restore. clone( ) . or_else( || Some ( new_font. font_style. clone( ) ) ) ;
145+ let FontCatalogStyle { weight, italic, .. } = FontCatalogStyle :: from_named_style( & new_font. font_style, "" ) ;
146+ new_font. font_style = family. closest_style( weight, italic) . to_named_style( ) ;
147+
148+ // Intentionally drop `font_style_to_restore` on commit so the committed style becomes the new basis for
149+ // subsequent family switches. Preserving the original style intent is hover-only behavior (handled by `new_font`).
150+ let FontCatalogStyle { weight, italic, .. } = FontCatalogStyle :: from_named_style( & current_font. font_style, "" ) ;
151+ let commit_only_font = Font :: new( family. name. clone( ) , family. closest_style( weight, italic) . to_named_style( ) ) ;
128152
129153 MenuListEntry :: new( family. name. clone( ) )
130154 . label( family. name. clone( ) )
131155 . font( family. closest_style( 400 , false ) . preview_url( & family. name) )
132- . on_update( update )
133- . on_commit( commit )
156+ . on_update( move |_| preview_font ( new_font . clone ( ) ) )
157+ . on_commit( move |_| commit_font ( commit_only_font . clone ( ) ) )
134158 } )
135159 . collect:: <Vec <_>>( ) ,
136160 ] )
@@ -146,13 +170,14 @@ fn create_text_widgets(tool: &TextTool, font_catalog: &FontCatalog) -> Vec<Widge
146170 . map ( |family| {
147171 let build_entry = |style : & FontCatalogStyle | {
148172 let font_style = style. to_named_style ( ) ;
173+ let new_font = Font :: new ( tool. options . font . font_family . clone ( ) , font_style. clone ( ) ) ;
149174
150- let font = Font :: new ( tool. options . font . font_family . clone ( ) , font_style. clone ( ) ) ;
151- let commit_style = font_catalog. find_font_style_in_catalog ( & tool. options . font ) . map ( |style| style. to_named_style ( ) ) ;
152- let update = update_options ( font. clone ( ) , None ) ;
153- let commit = update_options ( font, commit_style) ;
175+ let new_font_for_commit = new_font. clone ( ) ;
154176
155- MenuListEntry :: new ( font_style. clone ( ) ) . on_update ( update) . on_commit ( commit) . label ( font_style)
177+ MenuListEntry :: new ( font_style. clone ( ) )
178+ . label ( font_style)
179+ . on_update ( move |_| preview_font ( new_font. clone ( ) ) )
180+ . on_commit ( move |_| commit_font ( new_font_for_commit. clone ( ) ) )
156181 } ;
157182
158183 vec ! [
@@ -192,19 +217,6 @@ fn create_text_widgets(tool: &TextTool, font_catalog: &FontCatalog) -> Vec<Widge
192217 . into ( )
193218 } )
194219 . widget_instance ( ) ;
195- let line_height_ratio = NumberInput :: new ( Some ( tool. options . line_height_ratio ) )
196- . label ( "Line Height" )
197- . int ( )
198- . min ( 0. )
199- . max ( ( 1_u64 << f64:: MANTISSA_DIGITS ) as f64 )
200- . step ( 0.1 )
201- . on_update ( |number_input : & NumberInput | {
202- TextToolMessage :: UpdateOptions {
203- options : TextOptionsUpdate :: LineHeightRatio ( number_input. value . unwrap ( ) ) ,
204- }
205- . into ( )
206- } )
207- . widget_instance ( ) ;
208220 let align_entries: Vec < _ > = TextAlign :: list ( )
209221 . iter ( )
210222 . flat_map ( |section| section. iter ( ) )
@@ -229,29 +241,29 @@ fn create_text_widgets(tool: &TextTool, font_catalog: &FontCatalog) -> Vec<Widge
229241 style,
230242 Separator :: new( SeparatorStyle :: Related ) . widget_instance( ) ,
231243 size,
232- Separator :: new( SeparatorStyle :: Related ) . widget_instance( ) ,
233- line_height_ratio,
234244 Separator :: new( SeparatorStyle :: Unrelated ) . widget_instance( ) ,
235245 align,
236246 ]
237247}
238248
239249impl ToolRefreshOptions for TextTool {
240- fn refresh_options ( & self , responses : & mut VecDeque < Message > , cached_data : & CachedData ) {
241- self . send_layout ( responses, LayoutTarget :: ToolOptions , & cached_data. font_catalog ) ;
250+ fn refresh_options ( & self , responses : & mut VecDeque < Message > , _cached_data : & CachedData ) {
251+ // Defer to the SelectionChanged handler which has document context, required for the font/style
252+ // dropdowns to bind to the selected text layer's node graph inputs
253+ responses. add ( TextToolMessage :: SelectionChanged ) ;
242254 }
243255}
244256
245257impl TextTool {
246- fn send_layout ( & self , responses : & mut VecDeque < Message > , layout_target : LayoutTarget , font_catalog : & FontCatalog ) {
258+ fn send_layout ( & self , responses : & mut VecDeque < Message > , layout_target : LayoutTarget , font_catalog : & FontCatalog , document : & DocumentMessageHandler ) {
247259 responses. add ( LayoutMessage :: SendLayout {
248- layout : self . layout ( font_catalog) ,
260+ layout : self . layout ( font_catalog, document ) ,
249261 layout_target,
250262 } ) ;
251263 }
252264
253- fn layout ( & self , font_catalog : & FontCatalog ) -> Layout {
254- let mut widgets = create_text_widgets ( self , font_catalog) ;
265+ fn layout ( & self , font_catalog : & FontCatalog , document : & DocumentMessageHandler ) -> Layout {
266+ let mut widgets = create_text_widgets ( self , font_catalog, document ) ;
255267
256268 widgets. push ( Separator :: new ( SeparatorStyle :: Unrelated ) . widget_instance ( ) ) ;
257269
@@ -291,14 +303,18 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Text
291303 ToolMessage :: Text ( TextToolMessage :: UpdateOptions { options } ) => options,
292304 ToolMessage :: Text ( TextToolMessage :: SelectionChanged ) => {
293305 if let Some ( layer) = can_edit_selected ( context. document )
294- && let Some ( ( _, _ , typesetting, _) ) = graph_modification_utils:: get_text ( layer, & context. document . network_interface )
306+ && let Some ( ( _, font , typesetting, _) ) = graph_modification_utils:: get_text ( layer, & context. document . network_interface )
295307 {
296308 self . options . align = typesetting. align ;
309+ self . options . font_size = typesetting. font_size ;
310+ self . options . font = font. clone ( ) ;
297311 if let Some ( editing_text) = self . tool_data . editing_text . as_mut ( ) {
298312 editing_text. typesetting . align = typesetting. align ;
313+ editing_text. typesetting . font_size = typesetting. font_size ;
314+ editing_text. font = font. clone ( ) ;
299315 }
300316 }
301- self . send_layout ( responses, LayoutTarget :: ToolOptions , & context. cached_data . font_catalog ) ;
317+ self . send_layout ( responses, LayoutTarget :: ToolOptions , & context. cached_data . font_catalog , context . document ) ;
302318 return ;
303319 }
304320 _ => {
@@ -308,10 +324,28 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Text
308324 } ;
309325 match options {
310326 TextOptionsUpdate :: Font { font } => {
311- self . options . font = font;
327+ // The toolbar font/style menus go through `SetInputValue` directly when a text layer is selected, so this
328+ // arm only fires when no layer is selected (toolbar font is just the default for the next-created text).
329+ self . options . font = font. clone ( ) ;
330+ if let Some ( editing_text) = self . tool_data . editing_text . as_mut ( ) {
331+ editing_text. font = font;
332+ }
333+ }
334+ TextOptionsUpdate :: FontSize ( font_size) => {
335+ self . options . font_size = font_size;
336+ if let Some ( editing_text) = self . tool_data . editing_text . as_mut ( ) {
337+ editing_text. typesetting . font_size = font_size;
338+ }
339+ if let Some ( layer) = can_edit_selected ( context. document )
340+ && let Some ( node_id) = graph_modification_utils:: get_text_id ( layer, & context. document . network_interface )
341+ {
342+ responses. add ( NodeGraphMessage :: SetInputValue {
343+ node_id,
344+ input_index : graphene_std:: text:: text:: SizeInput :: INDEX ,
345+ value : TaggedValue :: F64 ( font_size) ,
346+ } ) ;
347+ }
312348 }
313- TextOptionsUpdate :: FontSize ( font_size) => self . options . font_size = font_size,
314- TextOptionsUpdate :: LineHeightRatio ( line_height_ratio) => self . options . line_height_ratio = line_height_ratio,
315349 TextOptionsUpdate :: Align ( align) => {
316350 self . options . align = align;
317351 if let Some ( editing_text) = self . tool_data . editing_text . as_mut ( ) {
@@ -320,11 +354,11 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Text
320354 if let Some ( layer) = can_edit_selected ( context. document )
321355 && let Some ( node_id) = graph_modification_utils:: get_text_id ( layer, & context. document . network_interface )
322356 {
323- responses. add ( NodeGraphMessage :: SetInput {
324- input_connector : InputConnector :: node ( node_id, graphene_std:: text:: text:: AlignInput :: INDEX ) ,
325- input : NodeInput :: value ( TaggedValue :: TextAlign ( align) , false ) ,
357+ responses. add ( NodeGraphMessage :: SetInputValue {
358+ node_id,
359+ input_index : graphene_std:: text:: text:: AlignInput :: INDEX ,
360+ value : TaggedValue :: TextAlign ( align) ,
326361 } ) ;
327- responses. add ( NodeGraphMessage :: RunDocumentGraph ) ;
328362 }
329363 }
330364 TextOptionsUpdate :: FillColor ( color) => {
@@ -338,7 +372,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Text
338372 }
339373 }
340374
341- self . send_layout ( responses, LayoutTarget :: ToolOptions , & context. cached_data . font_catalog ) ;
375+ self . send_layout ( responses, LayoutTarget :: ToolOptions , & context. cached_data . font_catalog , context . document ) ;
342376 }
343377
344378 fn actions ( & self ) -> ActionList {
@@ -446,6 +480,7 @@ impl TextToolData {
446480 /// Set the editing state of the currently modifying layer
447481 fn set_editing ( & self , editable : bool , font_cache : & FontCache , responses : & mut VecDeque < Message > ) {
448482 if let Some ( editing_text) = self . editing_text . as_ref ( ) . filter ( |_| editable) {
483+ let ( align, align_last) = editing_text. typesetting . align . css ( ) ;
449484 responses. add ( FrontendMessage :: DisplayEditableTextbox {
450485 text : editing_text. text . clone ( ) ,
451486 line_height_ratio : editing_text. typesetting . line_height_ratio ,
@@ -455,7 +490,8 @@ impl TextToolData {
455490 transform : editing_text. transform . to_cols_array ( ) ,
456491 max_width : editing_text. typesetting . max_width ,
457492 max_height : editing_text. typesetting . max_height ,
458- align : editing_text. typesetting . align ,
493+ align : align. to_string ( ) ,
494+ align_last : align_last. to_string ( ) ,
459495 } ) ;
460496 } else {
461497 // Check if DisplayRemoveEditableTextbox is already in the responses queue
@@ -930,12 +966,12 @@ impl Fsm for TextToolFsmState {
930966 transform : DAffine2 :: from_translation ( start) ,
931967 typesetting : TypesettingConfig {
932968 font_size : tool_options. font_size ,
933- line_height_ratio : tool_options. line_height_ratio ,
934969 max_width : constraint_size. map ( |size| size. x ) ,
935970 character_spacing : tool_options. character_spacing ,
936971 max_height : constraint_size. map ( |size| size. y ) ,
937972 tilt : tool_options. tilt ,
938973 align : tool_options. align ,
974+ ..TypesettingConfig :: default ( )
939975 } ,
940976 font : Font :: new ( tool_options. font . font_family . clone ( ) , tool_options. font . font_style . clone ( ) ) ,
941977 color : tool_options. fill . active_color ( ) ,
0 commit comments