@@ -25,10 +25,12 @@ use crate::text::{
2525use crate :: widget:: prelude:: * ;
2626use crate :: widget:: { Padding , Scroll , WidgetWrapper } ;
2727use crate :: {
28- theme, Color , Command , FontDescriptor , HotKey , KeyEvent , KeyOrValue , Point , Rect , SysMods ,
29- TextAlignment , TimerToken , Vec2 ,
28+ theme, ArcStr , Color , Command , FontDescriptor , HotKey , KeyEvent , KeyOrValue , Point , Rect ,
29+ SysMods , TextAlignment , TimerToken , Vec2 ,
3030} ;
3131
32+ use super :: LabelText ;
33+
3234const CURSOR_BLINK_DURATION : Duration = Duration :: from_millis ( 500 ) ;
3335const MAC_OR_LINUX : bool = cfg ! ( any( target_os = "macos" , target_os = "linux" ) ) ;
3436
@@ -47,7 +49,8 @@ const SCROLL_TO_INSETS: Insets = Insets::uniform_xy(40.0, 0.0);
4749/// [`Formatter`]: crate::text::format::Formatter
4850/// [`ValueTextBox`]: super::ValueTextBox
4951pub struct TextBox < T > {
50- placeholder : TextLayout < String > ,
52+ placeholder_text : LabelText < T > ,
53+ placeholder_layout : TextLayout < ArcStr > ,
5154 inner : Scroll < T , Padding < T , TextComponent < T > > > ,
5255 scroll_to_selection_after_layout : bool ,
5356 multiline : bool ,
@@ -70,8 +73,9 @@ pub struct TextBox<T> {
7073impl < T : EditableText + TextStorage > TextBox < T > {
7174 /// Create a new TextBox widget.
7275 pub fn new ( ) -> Self {
73- let mut placeholder = TextLayout :: from_text ( "" ) ;
74- placeholder. set_text_color ( theme:: PLACEHOLDER_COLOR ) ;
76+ let mut placeholder_layout = TextLayout :: new ( ) ;
77+ placeholder_layout. set_text_color ( theme:: PLACEHOLDER_COLOR ) ;
78+ let placeholder_text = "" . into ( ) ;
7579 let mut scroll = Scroll :: new ( Padding :: new (
7680 theme:: TEXTBOX_INSETS ,
7781 TextComponent :: default ( ) ,
@@ -81,7 +85,8 @@ impl<T: EditableText + TextStorage> TextBox<T> {
8185 Self {
8286 inner : scroll,
8387 scroll_to_selection_after_layout : false ,
84- placeholder,
88+ placeholder_text,
89+ placeholder_layout,
8590 multiline : false ,
8691 was_focused_from_click : false ,
8792 cursor_on : false ,
@@ -116,12 +121,6 @@ impl<T: EditableText + TextStorage> TextBox<T> {
116121}
117122
118123impl < T > TextBox < T > {
119- /// Builder-style method to set the `TextBox`'s placeholder text.
120- pub fn with_placeholder ( mut self , placeholder : impl Into < String > ) -> Self {
121- self . placeholder . set_text ( placeholder. into ( ) ) ;
122- self
123- }
124-
125124 /// Builder-style method for setting the text size.
126125 ///
127126 /// The argument can be either an `f64` or a [`Key<f64>`].
@@ -178,11 +177,6 @@ impl<T> TextBox<T> {
178177 self
179178 }
180179
181- /// Set the `TextBox`'s placeholder text.
182- pub fn set_placeholder ( & mut self , placeholder : impl Into < String > ) {
183- self . placeholder . set_text ( placeholder. into ( ) ) ;
184- }
185-
186180 /// Set the text size.
187181 ///
188182 /// The argument can be either an `f64` or a [`Key<f64>`].
@@ -199,7 +193,7 @@ impl<T> TextBox<T> {
199193 . borrow_mut ( )
200194 . layout
201195 . set_text_size ( size. clone ( ) ) ;
202- self . placeholder . set_text_size ( size) ;
196+ self . placeholder_layout . set_text_size ( size) ;
203197 }
204198
205199 /// Set the font.
@@ -217,7 +211,7 @@ impl<T> TextBox<T> {
217211 }
218212 let font = font. into ( ) ;
219213 self . text_mut ( ) . borrow_mut ( ) . layout . set_font ( font. clone ( ) ) ;
220- self . placeholder . set_font ( font) ;
214+ self . placeholder_layout . set_font ( font) ;
221215 }
222216
223217 /// Set the [`TextAlignment`] for this `TextBox``.
@@ -275,6 +269,21 @@ impl<T> TextBox<T> {
275269 }
276270}
277271
272+ impl < T : Data > TextBox < T > {
273+ /// Builder-style method to set the `TextBox`'s placeholder text.
274+ pub fn with_placeholder ( mut self , placeholder : impl Into < LabelText < T > > ) -> Self {
275+ self . set_placeholder ( placeholder) ;
276+ self
277+ }
278+
279+ /// Set the `TextBox`'s placeholder text.
280+ pub fn set_placeholder ( & mut self , placeholder : impl Into < LabelText < T > > ) {
281+ self . placeholder_text = placeholder. into ( ) ;
282+ self . placeholder_layout
283+ . set_text ( self . placeholder_text . display_text ( ) ) ;
284+ }
285+ }
286+
278287impl < T > TextBox < T > {
279288 /// An immutable reference to the inner [`TextComponent`].
280289 ///
@@ -466,6 +475,9 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
466475 fn lifecycle ( & mut self , ctx : & mut LifeCycleCtx , event : & LifeCycle , data : & T , env : & Env ) {
467476 match event {
468477 LifeCycle :: WidgetAdded => {
478+ if matches ! ( event, LifeCycle :: WidgetAdded ) {
479+ self . placeholder_text . resolve ( data, env) ;
480+ }
469481 ctx. register_text_input ( self . text ( ) . input_handler ( ) ) ;
470482 }
471483 LifeCycle :: BuildFocusChain => {
@@ -505,8 +517,16 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
505517
506518 #[ instrument( name = "TextBox" , level = "trace" , skip( self , ctx, old, data, env) ) ]
507519 fn update ( & mut self , ctx : & mut UpdateCtx , old : & T , data : & T , env : & Env ) {
520+ let placeholder_changed = self . placeholder_text . resolve ( data, env) ;
521+ if placeholder_changed {
522+ let new_text = self . placeholder_text . display_text ( ) ;
523+ self . placeholder_layout . set_text ( new_text) ;
524+ }
525+
508526 self . inner . update ( ctx, old, data, env) ;
509- if ctx. env_changed ( ) && self . placeholder . needs_rebuild_after_update ( ctx) {
527+ if placeholder_changed
528+ || ( ctx. env_changed ( ) && self . placeholder_layout . needs_rebuild_after_update ( ctx) )
529+ {
510530 ctx. request_layout ( ) ;
511531 }
512532 if self . text ( ) . can_write ( ) {
@@ -525,14 +545,14 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
525545 let min_width = env. get ( theme:: WIDE_WIDGET_WIDTH ) ;
526546 let textbox_insets = env. get ( theme:: TEXTBOX_INSETS ) ;
527547
528- self . placeholder . rebuild_if_needed ( ctx. text ( ) , env) ;
548+ self . placeholder_layout . rebuild_if_needed ( ctx. text ( ) , env) ;
529549 let min_size = bc. constrain ( ( min_width, 0.0 ) ) ;
530550 let child_bc = BoxConstraints :: new ( min_size, bc. max ( ) ) ;
531551
532552 let size = self . inner . layout ( ctx, & child_bc, data, env) ;
533553
534554 let text_metrics = if !self . text ( ) . can_read ( ) || data. is_empty ( ) {
535- self . placeholder . layout_metrics ( )
555+ self . placeholder_layout . layout_metrics ( )
536556 } else {
537557 self . text ( ) . borrow ( ) . layout . layout_metrics ( )
538558 } ;
@@ -586,7 +606,7 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
586606 if !data. is_empty ( ) {
587607 self . inner . paint ( ctx, data, env) ;
588608 } else {
589- let text_width = self . placeholder . layout_metrics ( ) . size . width ;
609+ let text_width = self . placeholder_layout . layout_metrics ( ) . size . width ;
590610 let extra_width = ( size. width - text_width - textbox_insets. x_value ( ) ) . max ( 0. ) ;
591611 let alignment = self . text ( ) . borrow ( ) . text_alignment ( ) ;
592612 // alignment is only used for single-line text boxes
@@ -599,7 +619,7 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
599619 // clip when we draw the placeholder, since it isn't in a clipbox
600620 ctx. with_save ( |ctx| {
601621 ctx. clip ( clip_rect) ;
602- self . placeholder
622+ self . placeholder_layout
603623 . draw ( ctx, ( textbox_insets. x0 + x_offset, textbox_insets. y0 ) ) ;
604624 } )
605625 }
0 commit comments