pub struct TextBox<T> {
pub handles_tab_notifications: bool,
/* private fields */
}Expand description
A widget that allows user text input.
§Editing values
If the text you are editing represents a value of some other type, such
as a number, you should use a ValueTextBox and an appropriate
Formatter. You can create a ValueTextBox by passing the appropriate
Formatter to TextBox::with_formatter.
Fields§
§handles_tab_notifications: boolif true (the default), this textbox will attempt to change focus on tab.
You can override this in a controller if you want to customize tab behaviour.
Implementations§
Source§impl<T: EditableText + TextStorage> TextBox<T>
impl<T: EditableText + TextStorage> TextBox<T>
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new TextBox widget.
§Examples
use druid::widget::TextBox;
use druid::{ WidgetExt, Data, Lens };
#[derive(Clone, Data, Lens)]
struct AppState {
name: String,
}
let _ = TextBox::new()
.with_placeholder("placeholder text")
.lens(AppState::name);Examples found in repository?
More examples
67fn build_root_widget() -> impl Widget<AppState> {
68 let blurb = Label::new(EXPLAINER)
69 .with_line_break_mode(druid::widget::LineBreaking::WordWrap)
70 .padding(8.0)
71 .border(Color::grey(0.6), 2.0)
72 .rounded(5.0);
73
74 Flex::column()
75 .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
76 .with_child(blurb)
77 .with_spacer(24.0)
78 .with_child(
79 TextBox::new()
80 .with_placeholder("Single")
81 .lens(AppState::single),
82 )
83 .with_default_spacer()
84 .with_flex_child(
85 TextBox::multiline()
86 .with_placeholder("Multi")
87 .lens(AppState::multi)
88 .expand_width(),
89 1.0,
90 )
91 .padding(8.0)
92}51fn build_root_widget() -> impl Widget<HelloState> {
52 // a label that will determine its text based on the current app data.
53 let label = Label::new(|data: &HelloState, _env: &Env| {
54 if data.name.is_empty() {
55 "Hello anybody!?".to_string()
56 } else {
57 format!("Hello {}!", data.name)
58 }
59 })
60 .with_text_size(32.0);
61
62 // a textbox that modifies `name`.
63 let textbox = TextBox::new()
64 .with_placeholder("Who are we greeting?")
65 .with_text_size(18.0)
66 .fix_width(TEXT_BOX_WIDTH)
67 .lens(HelloState::name);
68
69 // arrange the two widgets vertically, with some padding
70 Flex::column()
71 .with_child(label)
72 .with_spacer(VERTICAL_WIDGET_SPACING)
73 .with_child(textbox)
74 .align_vertical(UnitPoint::CENTER)
75}44fn ui_builder() -> impl Widget<MyComplexState> {
45 // `TextBox` is of type `Widget<String>`
46 // via `.lens` we get it to be of type `Widget<MyComplexState>`
47 let searchbar = TextBox::new().lens(MyComplexState::term_lens);
48
49 // `Slider` is of type `Widget<f64>`
50 // via `.lens` we get it to be of type `Widget<MyComplexState>`
51 let slider = Slider::new().lens(MyComplexState::scale);
52
53 let label = Label::new(|d: &MyComplexState, _: &Env| format!("{}: {:.2}", d.term, d.scale));
54
55 Flex::column()
56 .cross_axis_alignment(CrossAxisAlignment::Center)
57 .with_child(label)
58 .with_default_spacer()
59 .with_child(
60 Flex::row()
61 .cross_axis_alignment(CrossAxisAlignment::Center)
62 .with_child(searchbar)
63 .with_default_spacer()
64 .with_child(slider),
65 )
66 .center()
67}207fn make_spacer_select() -> impl Widget<Params> {
208 Flex::column()
209 .cross_axis_alignment(CrossAxisAlignment::Start)
210 .with_child(Label::new("Insert Spacers:"))
211 .with_default_spacer()
212 .with_child(RadioGroup::column(SPACER_OPTIONS.to_vec()).lens(Params::spacers))
213 .with_default_spacer()
214 .with_child(
215 Flex::row()
216 .with_child(
217 TextBox::new()
218 .with_formatter(ParseFormatter::new())
219 .lens(Params::spacer_size)
220 .fix_width(60.0),
221 )
222 .with_spacer(druid::theme::WIDGET_CONTROL_COMPONENT_PADDING)
223 .with_child(
224 Stepper::new()
225 .with_range(2.0, 50.0)
226 .with_step(2.0)
227 .lens(Params::spacer_size),
228 ),
229 )
230}
231
232fn space_if_needed<T: Data>(flex: &mut Flex<T>, params: &Params) {
233 match params.spacers {
234 Spacers::None => (),
235 Spacers::Default => flex.add_default_spacer(),
236 Spacers::Fixed => flex.add_spacer(params.spacer_size),
237 Spacers::Flex => flex.add_flex_spacer(1.0),
238 }
239}
240
241fn build_widget(state: &Params) -> Box<dyn Widget<AppState>> {
242 let mut flex = match state.axis {
243 FlexType::Column => Flex::column(),
244 FlexType::Row => Flex::row(),
245 }
246 .cross_axis_alignment(state.cross_alignment)
247 .main_axis_alignment(state.main_alignment)
248 .must_fill_main_axis(state.fill_major_axis);
249
250 flex.add_child(
251 TextBox::new()
252 .with_placeholder("Sample text")
253 .lens(DemoState::input_text),
254 );
255 space_if_needed(&mut flex, state);
256
257 flex.add_child(
258 Button::new("Clear").on_click(|_ctx, data: &mut DemoState, _env| {
259 data.input_text.clear();
260 data.enabled = false;
261 data.volume = 0.0;
262 }),
263 );
264
265 space_if_needed(&mut flex, state);
266
267 flex.add_child(
268 Label::new(|data: &DemoState, _: &Env| data.input_text.clone()).with_text_size(32.0),
269 );
270 space_if_needed(&mut flex, state);
271 flex.add_child(Checkbox::new("Demo").lens(DemoState::enabled));
272 space_if_needed(&mut flex, state);
273 flex.add_child(Switch::new().lens(DemoState::enabled));
274 space_if_needed(&mut flex, state);
275 flex.add_child(Slider::new().lens(DemoState::volume));
276 space_if_needed(&mut flex, state);
277 flex.add_child(ProgressBar::new().lens(DemoState::volume));
278 space_if_needed(&mut flex, state);
279 flex.add_child(
280 Stepper::new()
281 .with_range(0.0, 1.0)
282 .with_step(0.1)
283 .with_wraparound(true)
284 .lens(DemoState::volume),
285 );
286
287 let mut flex = SizedBox::new(flex);
288 if state.fix_minor_axis {
289 match state.axis {
290 FlexType::Row => flex = flex.height(200.),
291 FlexType::Column => flex = flex.width(200.),
292 }
293 }
294 if state.fix_major_axis {
295 match state.axis {
296 FlexType::Row => flex = flex.width(600.),
297 FlexType::Column => flex = flex.height(300.),
298 }
299 }
300
301 let flex = flex
302 .padding(8.0)
303 .border(Color::grey(0.6), 2.0)
304 .rounded(5.0)
305 .lens(AppState::demo_state);
306
307 if state.debug_layout {
308 flex.debug_paint_layout().boxed()
309 } else {
310 flex.boxed()
311 }
312}39fn ui_builder() -> impl Widget<String> {
40 let rs = FileSpec::new("Rust source", &["rs"]);
41 let txt = FileSpec::new("Text file", &["txt"]);
42 let other = FileSpec::new("Bogus file", &["foo", "bar", "baz"]);
43 // The options can also be generated at runtime,
44 // so to show that off we create a String for the default save name.
45 let default_save_name = String::from("MyFile.txt");
46 let save_dialog_options = FileDialogOptions::new()
47 .allowed_types(vec![rs, txt, other])
48 .default_type(txt)
49 .default_name(default_save_name)
50 .name_label("Target")
51 .title("Choose a target for this lovely file")
52 .button_text("Export");
53 let open_dialog_options = save_dialog_options
54 .clone()
55 .default_name("MySavedFile.txt")
56 .name_label("Source")
57 .title("Where did you put that file?")
58 .button_text("Import");
59
60 let input = TextBox::new();
61 let save = Button::new("Save").on_click(move |ctx, _, _| {
62 ctx.submit_command(druid::commands::SHOW_SAVE_PANEL.with(save_dialog_options.clone()))
63 });
64 let open = Button::new("Open").on_click(move |ctx, _, _| {
65 ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(open_dialog_options.clone()))
66 });
67
68 let mut col = Flex::column();
69 col.add_child(input);
70 col.add_spacer(8.0);
71 col.add_child(save);
72 col.add_child(open);
73 Align::centered(col)
74}Sourcepub fn multiline() -> Self
pub fn multiline() -> Self
Create a new multi-line TextBox.
§Examples
let multiline = TextBox::multiline()
.lens(AppState::name);Examples found in repository?
115fn build_root_widget() -> impl Widget<AppState> {
116 let label = Scroll::new(
117 RawLabel::new()
118 .with_text_color(Color::BLACK)
119 .with_line_break_mode(LineBreaking::WordWrap)
120 .lens(AppState::rendered)
121 .expand_width()
122 .padding((SPACER_SIZE * 4.0, SPACER_SIZE)),
123 )
124 .vertical()
125 .background(Color::grey8(222))
126 .expand();
127
128 let textbox = TextBox::multiline()
129 .lens(AppState::raw)
130 .controller(RichTextRebuilder)
131 .expand()
132 .padding(5.0);
133
134 Split::columns(label, textbox)
135}More examples
67fn build_root_widget() -> impl Widget<AppState> {
68 let blurb = Label::new(EXPLAINER)
69 .with_line_break_mode(druid::widget::LineBreaking::WordWrap)
70 .padding(8.0)
71 .border(Color::grey(0.6), 2.0)
72 .rounded(5.0);
73
74 Flex::column()
75 .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
76 .with_child(blurb)
77 .with_spacer(24.0)
78 .with_child(
79 TextBox::new()
80 .with_placeholder("Single")
81 .lens(AppState::single),
82 )
83 .with_default_spacer()
84 .with_flex_child(
85 TextBox::multiline()
86 .with_placeholder("Multi")
87 .lens(AppState::multi)
88 .expand_width(),
89 1.0,
90 )
91 .padding(8.0)
92}231fn interactive_area() -> impl Widget<AppState> {
232 let text_box = TextBox::multiline()
233 .with_text_color(Color::rgb8(0xf0, 0xf0, 0xea))
234 .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
235 .lens(AppState::text_input)
236 .controller(EventLogger {
237 filter: |event| matches!(event, Event::KeyDown(_) | Event::KeyUp(_)),
238 });
239
240 let mouse_box = SizedBox::empty()
241 .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
242 .background(CURSOR_BACKGROUND_COLOR)
243 .rounded(5.0)
244 .border(INTERACTIVE_AREA_BORDER, 1.0)
245 .controller(EventLogger {
246 filter: |event| {
247 matches!(
248 event,
249 Event::MouseDown(_) | Event::MouseUp(_) | Event::Wheel(_)
250 )
251 },
252 });
253
254 Flex::row()
255 .with_flex_spacer(1.0)
256 .with_child(text_box)
257 .with_flex_spacer(1.0)
258 .with_child(mouse_box)
259 .with_flex_spacer(1.0)
260 .padding(10.0)
261}72fn ui_builder() -> impl Widget<AppData> {
73 let my_painter = Painter::new(|ctx, _, _| {
74 let bounds = ctx.size().to_rect();
75 if ctx.is_hot() {
76 ctx.fill(bounds, &Color::rgba8(0, 0, 0, 128));
77 }
78
79 if ctx.is_active() {
80 ctx.stroke(bounds, &Color::WHITE, 2.0);
81 }
82 });
83
84 // This is Druid's default text style.
85 // It's set by theme::LABEL_COLOR and theme::UI_FONT
86 let label =
87 Label::new(|data: &String, _env: &_| format!("Default: {data}")).lens(AppData::text);
88
89 // The text_color, text_size, and font builder methods can override the
90 // defaults provided by the theme by passing in a Key or a concrete value.
91 //
92 // In this example, text_color receives a Key from the theme, text_size
93 // gets a custom key which we set with the env_scope wrapper, and the
94 // default font key (theme::FONT_NAME) is overridden in the env_scope
95 // wrapper. (Like text_color and text_size, the font can be set using the
96 // with_font builder method, but overriding here makes it easy to fall back
97 // to the default font)
98 let styled_label = Label::new(|data: &AppData, _env: &_| format!("{data}"))
99 .with_text_color(theme::PRIMARY_LIGHT)
100 .with_font(MY_CUSTOM_FONT)
101 .background(my_painter)
102 .on_click(|_, data, _| {
103 data.size *= 1.1;
104 })
105 .env_scope(|env: &mut druid::Env, data: &AppData| {
106 let new_font = if data.mono {
107 FontDescriptor::new(FontFamily::MONOSPACE)
108 } else {
109 FontDescriptor::new(FontFamily::SYSTEM_UI)
110 }
111 .with_size(data.size);
112 env.set(MY_CUSTOM_FONT, new_font);
113 });
114
115 let labels = Scroll::new(
116 Flex::column()
117 .cross_axis_alignment(CrossAxisAlignment::Start)
118 .with_child(label)
119 .with_default_spacer()
120 .with_child(styled_label),
121 )
122 .expand_height()
123 .fix_width(COLUMN_WIDTH);
124
125 let stepper = Stepper::new()
126 .with_range(0.0, 100.0)
127 .with_step(1.0)
128 .with_wraparound(false)
129 .lens(AppData::size);
130
131 // TODO: Replace Parse usage with TextBox::with_formatter
132 #[allow(deprecated)]
133 let stepper_textbox = LensWrap::new(
134 Parse::new(TextBox::new()),
135 AppData::size.map(|x| Some(*x), |x, y| *x = y.unwrap_or(24.0)),
136 );
137
138 let mono_checkbox = Checkbox::new("Monospace").lens(AppData::mono);
139 let stepper_row = Flex::row()
140 .with_child(stepper_textbox)
141 .with_child(stepper)
142 .with_default_spacer()
143 .with_child(mono_checkbox);
144
145 let input = TextBox::multiline()
146 .with_placeholder("Your sample text here :)")
147 .fix_width(COLUMN_WIDTH)
148 .fix_height(140.0)
149 .lens(AppData::text);
150
151 Flex::column()
152 .main_axis_alignment(MainAxisAlignment::Center)
153 .with_default_spacer()
154 .with_flex_child(labels, 1.0)
155 .with_default_spacer()
156 .with_child(input)
157 .with_default_spacer()
158 .with_child(stepper_row)
159 .with_default_spacer()
160}Sourcepub fn with_line_wrapping(self, wrap_lines: bool) -> Self
pub fn with_line_wrapping(self, wrap_lines: bool) -> Self
If true (and this is a multiline text box) lines will be wrapped
at the maximum layout width.
If false, lines will not be wrapped, and horizontal scrolling will
be enabled.
§Examples
//will scroll horizontally
let scroll_text_box = TextBox::new()
.with_line_wrapping(false)
.lens(AppState::name);
//will wrap only for a single line
let wrap_text_box = TextBox::new()
.with_line_wrapping(true)
.lens(AppState::name);
//will scroll as well as having multiple lines
let scroll_multi_line_text_box = TextBox::multiline()
.with_line_wrapping(false)
.lens(AppState::name);
//will wrap for each line
let wrap_multi_line_text_box = TextBox::multiline()
.with_line_wrapping(true) // this is default and can be removed for the same result
.lens(AppState::name);
Source§impl<T> TextBox<T>
impl<T> TextBox<T>
Sourcepub fn with_text_size(self, size: impl Into<KeyOrValue<f64>>) -> Self
pub fn with_text_size(self, size: impl Into<KeyOrValue<f64>>) -> Self
Builder-style method for setting the text size.
The argument can be either an f64 or a Key<f64>.
§Examples
let text_box = TextBox::new()
.with_text_size(14.)
.lens(AppState::name);use druid::Key;
const FONT_SIZE : Key<f64> = Key::new("font-size");
let text_box = TextBox::new()
.with_text_size(FONT_SIZE)
.lens(AppState::name);Examples found in repository?
51fn build_root_widget() -> impl Widget<HelloState> {
52 // a label that will determine its text based on the current app data.
53 let label = Label::new(|data: &HelloState, _env: &Env| {
54 if data.name.is_empty() {
55 "Hello anybody!?".to_string()
56 } else {
57 format!("Hello {}!", data.name)
58 }
59 })
60 .with_text_size(32.0);
61
62 // a textbox that modifies `name`.
63 let textbox = TextBox::new()
64 .with_placeholder("Who are we greeting?")
65 .with_text_size(18.0)
66 .fix_width(TEXT_BOX_WIDTH)
67 .lens(HelloState::name);
68
69 // arrange the two widgets vertically, with some padding
70 Flex::column()
71 .with_child(label)
72 .with_spacer(VERTICAL_WIDGET_SPACING)
73 .with_child(textbox)
74 .align_vertical(UnitPoint::CENTER)
75}More examples
62fn build_root_widget() -> impl Widget<HelloState> {
63 // Draw red circle, and two semi-transparent rectangles
64 let circle_and_rects = Painter::new(|ctx, _data, _env| {
65 let boundaries = ctx.size().to_rect();
66 let center = (boundaries.width() / 2., boundaries.height() / 2.);
67 let circle = Circle::new(center, center.0.min(center.1));
68 ctx.fill(circle, &Color::RED);
69
70 let rect1 = Rect::new(0., 0., boundaries.width() / 2., boundaries.height() / 2.);
71 ctx.fill(rect1, &Color::rgba8(0x0, 0xff, 0, 125));
72
73 let rect2 = Rect::new(
74 boundaries.width() / 2.,
75 boundaries.height() / 2.,
76 boundaries.width(),
77 boundaries.height(),
78 );
79 ctx.fill(rect2, &Color::rgba8(0x0, 0x0, 0xff, 125));
80 });
81
82 // This textbox modifies the label, idea here is to test that the background
83 // invalidation works when you type to the textbox
84 let textbox = TextBox::new()
85 .with_placeholder("Type to test clearing")
86 .with_text_size(18.0)
87 .lens(HelloState::name)
88 .fix_width(250.);
89
90 let label = Label::new(|data: &HelloState, _env: &Env| {
91 if data.name.is_empty() {
92 "Text: ".to_string()
93 } else {
94 format!("Text: {}!", data.name)
95 }
96 })
97 .with_text_color(Color::RED)
98 .with_text_size(32.0);
99
100 Flex::column()
101 .with_flex_child(circle_and_rects.expand().controller(DragController), 10.0)
102 .with_spacer(4.0)
103 .with_child(textbox)
104 .with_spacer(4.0)
105 .with_child(label)
106}Sourcepub fn with_text_alignment(self, alignment: TextAlignment) -> Self
pub fn with_text_alignment(self, alignment: TextAlignment) -> Self
Builder-style method to set the TextAlignment.
This is only relevant when the TextBox is not multiline,
in which case it determines how the text is positioned inside the
TextBox when it does not fill the available space.
§Note:
This does not behave exactly like TextAlignment does when used
with label; in particular this does not account for reading direction.
This means that TextAlignment::Start (the default) always means
left aligned, and TextAlignment::End always means right aligned.
This should be considered a bug, but it will not be fixed until proper BiDi support is implemented.
§Examples
use druid::TextAlignment;
let text_box = TextBox::new()
.with_text_alignment(TextAlignment::Center)
.lens(AppState::name);Sourcepub fn with_font(self, font: impl Into<KeyOrValue<FontDescriptor>>) -> Self
pub fn with_font(self, font: impl Into<KeyOrValue<FontDescriptor>>) -> Self
Builder-style method for setting the font.
The argument can be a FontDescriptor or a Key<FontDescriptor>
that refers to a font defined in the Env.
§Examples
use druid::{ FontDescriptor, FontFamily, Key };
const FONT : Key<FontDescriptor> = Key::new("font");
let text_box = TextBox::new()
.with_font(FontDescriptor::new(FontFamily::MONOSPACE))
.lens(AppState::name);
let text_box = TextBox::new()
.with_font(FONT)
.lens(AppState::name);Sourcepub fn with_text_color(self, color: impl Into<KeyOrValue<Color>>) -> Self
pub fn with_text_color(self, color: impl Into<KeyOrValue<Color>>) -> Self
Builder-style method for setting the text color.
The argument can be either a Color or a Key<Color>.
§Examples
use druid::{ Color, Key };
const COLOR : Key<Color> = Key::new("color");
let text_box = TextBox::new()
.with_text_color(Color::RED)
.lens(AppState::name);
let text_box = TextBox::new()
.with_text_color(COLOR)
.lens(AppState::name);Examples found in repository?
231fn interactive_area() -> impl Widget<AppState> {
232 let text_box = TextBox::multiline()
233 .with_text_color(Color::rgb8(0xf0, 0xf0, 0xea))
234 .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
235 .lens(AppState::text_input)
236 .controller(EventLogger {
237 filter: |event| matches!(event, Event::KeyDown(_) | Event::KeyUp(_)),
238 });
239
240 let mouse_box = SizedBox::empty()
241 .fix_size(INTERACTIVE_AREA_DIM, INTERACTIVE_AREA_DIM)
242 .background(CURSOR_BACKGROUND_COLOR)
243 .rounded(5.0)
244 .border(INTERACTIVE_AREA_BORDER, 1.0)
245 .controller(EventLogger {
246 filter: |event| {
247 matches!(
248 event,
249 Event::MouseDown(_) | Event::MouseUp(_) | Event::Wheel(_)
250 )
251 },
252 });
253
254 Flex::row()
255 .with_flex_spacer(1.0)
256 .with_child(text_box)
257 .with_flex_spacer(1.0)
258 .with_child(mouse_box)
259 .with_flex_spacer(1.0)
260 .padding(10.0)
261}Sourcepub fn set_text_size(&mut self, size: impl Into<KeyOrValue<f64>>)
pub fn set_text_size(&mut self, size: impl Into<KeyOrValue<f64>>)
Set the text size.
The argument can be either an f64 or a Key<f64>.
Sourcepub fn set_font(&mut self, font: impl Into<KeyOrValue<FontDescriptor>>)
pub fn set_font(&mut self, font: impl Into<KeyOrValue<FontDescriptor>>)
Set the font.
The argument can be a FontDescriptor or a Key<FontDescriptor>
that refers to a font defined in the Env.
Sourcepub fn set_text_alignment(&mut self, alignment: TextAlignment)
pub fn set_text_alignment(&mut self, alignment: TextAlignment)
Set the TextAlignment for this `TextBox``.
This is only relevant when the TextBox is not multiline,
in which case it determines how the text is positioned inside the
TextBox when it does not fill the available space.
§Note:
This does not behave exactly like TextAlignment does when used
with label; in particular this does not account for reading direction.
This means that TextAlignment::Start (the default) always means
left aligned, and TextAlignment::End always means right aligned.
This should be considered a bug, but it will not be fixed until proper BiDi support is implemented.
Sourcepub fn set_text_color(&mut self, color: impl Into<KeyOrValue<Color>>)
pub fn set_text_color(&mut self, color: impl Into<KeyOrValue<Color>>)
Set the text color.
The argument can be either a Color or a Key<Color>.
If you change this property, you are responsible for calling
request_layout to ensure the label is updated.
Sourcepub fn text_position(&self) -> Point
pub fn text_position(&self) -> Point
The point, relative to the origin, where this text box draws its
TextLayout.
This is exposed in case the user wants to do additional drawing based on properties of the text.
This is not valid until layout has been called.
Source§impl<T: Data> TextBox<T>
impl<T: Data> TextBox<T>
Sourcepub fn with_placeholder(self, placeholder: impl Into<LabelText<T>>) -> Self
pub fn with_placeholder(self, placeholder: impl Into<LabelText<T>>) -> Self
Builder-style method to set the TextBox’s placeholder text.
Examples found in repository?
67fn build_root_widget() -> impl Widget<AppState> {
68 let blurb = Label::new(EXPLAINER)
69 .with_line_break_mode(druid::widget::LineBreaking::WordWrap)
70 .padding(8.0)
71 .border(Color::grey(0.6), 2.0)
72 .rounded(5.0);
73
74 Flex::column()
75 .cross_axis_alignment(druid::widget::CrossAxisAlignment::Start)
76 .with_child(blurb)
77 .with_spacer(24.0)
78 .with_child(
79 TextBox::new()
80 .with_placeholder("Single")
81 .lens(AppState::single),
82 )
83 .with_default_spacer()
84 .with_flex_child(
85 TextBox::multiline()
86 .with_placeholder("Multi")
87 .lens(AppState::multi)
88 .expand_width(),
89 1.0,
90 )
91 .padding(8.0)
92}More examples
51fn build_root_widget() -> impl Widget<HelloState> {
52 // a label that will determine its text based on the current app data.
53 let label = Label::new(|data: &HelloState, _env: &Env| {
54 if data.name.is_empty() {
55 "Hello anybody!?".to_string()
56 } else {
57 format!("Hello {}!", data.name)
58 }
59 })
60 .with_text_size(32.0);
61
62 // a textbox that modifies `name`.
63 let textbox = TextBox::new()
64 .with_placeholder("Who are we greeting?")
65 .with_text_size(18.0)
66 .fix_width(TEXT_BOX_WIDTH)
67 .lens(HelloState::name);
68
69 // arrange the two widgets vertically, with some padding
70 Flex::column()
71 .with_child(label)
72 .with_spacer(VERTICAL_WIDGET_SPACING)
73 .with_child(textbox)
74 .align_vertical(UnitPoint::CENTER)
75}62fn build_root_widget() -> impl Widget<HelloState> {
63 // Draw red circle, and two semi-transparent rectangles
64 let circle_and_rects = Painter::new(|ctx, _data, _env| {
65 let boundaries = ctx.size().to_rect();
66 let center = (boundaries.width() / 2., boundaries.height() / 2.);
67 let circle = Circle::new(center, center.0.min(center.1));
68 ctx.fill(circle, &Color::RED);
69
70 let rect1 = Rect::new(0., 0., boundaries.width() / 2., boundaries.height() / 2.);
71 ctx.fill(rect1, &Color::rgba8(0x0, 0xff, 0, 125));
72
73 let rect2 = Rect::new(
74 boundaries.width() / 2.,
75 boundaries.height() / 2.,
76 boundaries.width(),
77 boundaries.height(),
78 );
79 ctx.fill(rect2, &Color::rgba8(0x0, 0x0, 0xff, 125));
80 });
81
82 // This textbox modifies the label, idea here is to test that the background
83 // invalidation works when you type to the textbox
84 let textbox = TextBox::new()
85 .with_placeholder("Type to test clearing")
86 .with_text_size(18.0)
87 .lens(HelloState::name)
88 .fix_width(250.);
89
90 let label = Label::new(|data: &HelloState, _env: &Env| {
91 if data.name.is_empty() {
92 "Text: ".to_string()
93 } else {
94 format!("Text: {}!", data.name)
95 }
96 })
97 .with_text_color(Color::RED)
98 .with_text_size(32.0);
99
100 Flex::column()
101 .with_flex_child(circle_and_rects.expand().controller(DragController), 10.0)
102 .with_spacer(4.0)
103 .with_child(textbox)
104 .with_spacer(4.0)
105 .with_child(label)
106}332fn build_root_widget() -> impl Widget<HelloState> {
333 let label = EnvScope::new(
334 |env, _t| env.set(theme::TEXT_COLOR, env.get(theme::PRIMARY_LIGHT)),
335 ControllerHost::new(
336 Label::new(|data: &HelloState, _env: &Env| {
337 format!("Hello {}! {} ", data.name, data.sub.my_stuff)
338 }),
339 TooltipController::new("Tips! Are good"),
340 ),
341 );
342 // a textbox that modifies `name`.
343 let textbox = TextBox::new()
344 .with_placeholder("Who are we greeting?")
345 .fix_width(TEXT_BOX_WIDTH)
346 .lens(HelloState::sub.then(SubState::my_stuff));
347
348 let button = Button::new("Make sub window")
349 .on_click(|ctx, data: &mut SubState, env| {
350 let tb = TextBox::new().lens(SubState::my_stuff);
351 let drag_thing = Label::new("Drag me").controller(DragWindowController::new());
352 let col = Flex::column().with_child(drag_thing).with_child(tb);
353
354 ctx.new_sub_window(
355 WindowConfig::default()
356 .show_titlebar(false)
357 .window_size(Size::new(100., 100.))
358 .set_level(WindowLevel::AppWindow),
359 col,
360 data.clone(),
361 env.clone(),
362 );
363 })
364 .center()
365 .lens(HelloState::sub);
366
367 let check_box =
368 ControllerHost::new(Checkbox::new("Closeable?"), CancelClose).lens(HelloState::closeable);
369 // arrange the two widgets vertically, with some padding
370 let layout = Flex::column()
371 .with_child(label)
372 .with_flex_child(ScreenThing.lens(Unit::default()).padding(5.), 1.)
373 .with_spacer(VERTICAL_WIDGET_SPACING)
374 .with_child(textbox)
375 .with_child(button)
376 .with_child(check_box);
377
378 // center the two widgets in the available space
379 Align::centered(layout)
380}241fn build_widget(state: &Params) -> Box<dyn Widget<AppState>> {
242 let mut flex = match state.axis {
243 FlexType::Column => Flex::column(),
244 FlexType::Row => Flex::row(),
245 }
246 .cross_axis_alignment(state.cross_alignment)
247 .main_axis_alignment(state.main_alignment)
248 .must_fill_main_axis(state.fill_major_axis);
249
250 flex.add_child(
251 TextBox::new()
252 .with_placeholder("Sample text")
253 .lens(DemoState::input_text),
254 );
255 space_if_needed(&mut flex, state);
256
257 flex.add_child(
258 Button::new("Clear").on_click(|_ctx, data: &mut DemoState, _env| {
259 data.input_text.clear();
260 data.enabled = false;
261 data.volume = 0.0;
262 }),
263 );
264
265 space_if_needed(&mut flex, state);
266
267 flex.add_child(
268 Label::new(|data: &DemoState, _: &Env| data.input_text.clone()).with_text_size(32.0),
269 );
270 space_if_needed(&mut flex, state);
271 flex.add_child(Checkbox::new("Demo").lens(DemoState::enabled));
272 space_if_needed(&mut flex, state);
273 flex.add_child(Switch::new().lens(DemoState::enabled));
274 space_if_needed(&mut flex, state);
275 flex.add_child(Slider::new().lens(DemoState::volume));
276 space_if_needed(&mut flex, state);
277 flex.add_child(ProgressBar::new().lens(DemoState::volume));
278 space_if_needed(&mut flex, state);
279 flex.add_child(
280 Stepper::new()
281 .with_range(0.0, 1.0)
282 .with_step(0.1)
283 .with_wraparound(true)
284 .lens(DemoState::volume),
285 );
286
287 let mut flex = SizedBox::new(flex);
288 if state.fix_minor_axis {
289 match state.axis {
290 FlexType::Row => flex = flex.height(200.),
291 FlexType::Column => flex = flex.width(200.),
292 }
293 }
294 if state.fix_major_axis {
295 match state.axis {
296 FlexType::Row => flex = flex.width(600.),
297 FlexType::Column => flex = flex.height(300.),
298 }
299 }
300
301 let flex = flex
302 .padding(8.0)
303 .border(Color::grey(0.6), 2.0)
304 .rounded(5.0)
305 .lens(AppState::demo_state);
306
307 if state.debug_layout {
308 flex.debug_paint_layout().boxed()
309 } else {
310 flex.boxed()
311 }
312}72fn ui_builder() -> impl Widget<AppData> {
73 let my_painter = Painter::new(|ctx, _, _| {
74 let bounds = ctx.size().to_rect();
75 if ctx.is_hot() {
76 ctx.fill(bounds, &Color::rgba8(0, 0, 0, 128));
77 }
78
79 if ctx.is_active() {
80 ctx.stroke(bounds, &Color::WHITE, 2.0);
81 }
82 });
83
84 // This is Druid's default text style.
85 // It's set by theme::LABEL_COLOR and theme::UI_FONT
86 let label =
87 Label::new(|data: &String, _env: &_| format!("Default: {data}")).lens(AppData::text);
88
89 // The text_color, text_size, and font builder methods can override the
90 // defaults provided by the theme by passing in a Key or a concrete value.
91 //
92 // In this example, text_color receives a Key from the theme, text_size
93 // gets a custom key which we set with the env_scope wrapper, and the
94 // default font key (theme::FONT_NAME) is overridden in the env_scope
95 // wrapper. (Like text_color and text_size, the font can be set using the
96 // with_font builder method, but overriding here makes it easy to fall back
97 // to the default font)
98 let styled_label = Label::new(|data: &AppData, _env: &_| format!("{data}"))
99 .with_text_color(theme::PRIMARY_LIGHT)
100 .with_font(MY_CUSTOM_FONT)
101 .background(my_painter)
102 .on_click(|_, data, _| {
103 data.size *= 1.1;
104 })
105 .env_scope(|env: &mut druid::Env, data: &AppData| {
106 let new_font = if data.mono {
107 FontDescriptor::new(FontFamily::MONOSPACE)
108 } else {
109 FontDescriptor::new(FontFamily::SYSTEM_UI)
110 }
111 .with_size(data.size);
112 env.set(MY_CUSTOM_FONT, new_font);
113 });
114
115 let labels = Scroll::new(
116 Flex::column()
117 .cross_axis_alignment(CrossAxisAlignment::Start)
118 .with_child(label)
119 .with_default_spacer()
120 .with_child(styled_label),
121 )
122 .expand_height()
123 .fix_width(COLUMN_WIDTH);
124
125 let stepper = Stepper::new()
126 .with_range(0.0, 100.0)
127 .with_step(1.0)
128 .with_wraparound(false)
129 .lens(AppData::size);
130
131 // TODO: Replace Parse usage with TextBox::with_formatter
132 #[allow(deprecated)]
133 let stepper_textbox = LensWrap::new(
134 Parse::new(TextBox::new()),
135 AppData::size.map(|x| Some(*x), |x, y| *x = y.unwrap_or(24.0)),
136 );
137
138 let mono_checkbox = Checkbox::new("Monospace").lens(AppData::mono);
139 let stepper_row = Flex::row()
140 .with_child(stepper_textbox)
141 .with_child(stepper)
142 .with_default_spacer()
143 .with_child(mono_checkbox);
144
145 let input = TextBox::multiline()
146 .with_placeholder("Your sample text here :)")
147 .fix_width(COLUMN_WIDTH)
148 .fix_height(140.0)
149 .lens(AppData::text);
150
151 Flex::column()
152 .main_axis_alignment(MainAxisAlignment::Center)
153 .with_default_spacer()
154 .with_flex_child(labels, 1.0)
155 .with_default_spacer()
156 .with_child(input)
157 .with_default_spacer()
158 .with_child(stepper_row)
159 .with_default_spacer()
160}Sourcepub fn set_placeholder(&mut self, placeholder: impl Into<LabelText<T>>)
pub fn set_placeholder(&mut self, placeholder: impl Into<LabelText<T>>)
Set the TextBox’s placeholder text.
Source§impl<T> TextBox<T>
impl<T> TextBox<T>
Sourcepub fn text(&self) -> &TextComponent<T>
pub fn text(&self) -> &TextComponent<T>
An immutable reference to the inner TextComponent.
Using this correctly is difficult; please see the TextComponent
docs for more information.
Sourcepub fn text_mut(&mut self) -> &mut TextComponent<T>
pub fn text_mut(&mut self) -> &mut TextComponent<T>
A mutable reference to the inner TextComponent.
Using this correctly is difficult; please see the TextComponent
docs for more information.
Source§impl TextBox<String>
impl TextBox<String>
Sourcepub fn with_formatter<T: Data>(
self,
formatter: impl Formatter<T> + 'static,
) -> ValueTextBox<T>
pub fn with_formatter<T: Data>( self, formatter: impl Formatter<T> + 'static, ) -> ValueTextBox<T>
Turn this TextBox into a ValueTextBox, using the Formatter to
manage the value.
For simple value formatting, you can use the ParseFormatter.
Examples found in repository?
207fn make_spacer_select() -> impl Widget<Params> {
208 Flex::column()
209 .cross_axis_alignment(CrossAxisAlignment::Start)
210 .with_child(Label::new("Insert Spacers:"))
211 .with_default_spacer()
212 .with_child(RadioGroup::column(SPACER_OPTIONS.to_vec()).lens(Params::spacers))
213 .with_default_spacer()
214 .with_child(
215 Flex::row()
216 .with_child(
217 TextBox::new()
218 .with_formatter(ParseFormatter::new())
219 .lens(Params::spacer_size)
220 .fix_width(60.0),
221 )
222 .with_spacer(druid::theme::WIDGET_CONTROL_COMPONENT_PADDING)
223 .with_child(
224 Stepper::new()
225 .with_range(2.0, 50.0)
226 .with_step(2.0)
227 .lens(Params::spacer_size),
228 ),
229 )
230}Trait Implementations§
Source§impl<T: TextStorage + EditableText> Default for TextBox<T>
impl<T: TextStorage + EditableText> Default for TextBox<T>
Source§impl<T: TextStorage + EditableText> Widget<T> for TextBox<T>
impl<T: TextStorage + EditableText> Widget<T> for TextBox<T>
Source§fn event(
&mut self,
ctx: &mut EventCtx<'_, '_>,
event: &Event,
data: &mut T,
env: &Env,
)
fn event( &mut self, ctx: &mut EventCtx<'_, '_>, event: &Event, data: &mut T, env: &Env, )
Source§fn lifecycle(
&mut self,
ctx: &mut LifeCycleCtx<'_, '_>,
event: &LifeCycle,
data: &T,
env: &Env,
)
fn lifecycle( &mut self, ctx: &mut LifeCycleCtx<'_, '_>, event: &LifeCycle, data: &T, env: &Env, )
Source§fn layout(
&mut self,
ctx: &mut LayoutCtx<'_, '_>,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> Size
fn layout( &mut self, ctx: &mut LayoutCtx<'_, '_>, bc: &BoxConstraints, data: &T, env: &Env, ) -> Size
Source§fn paint(&mut self, ctx: &mut PaintCtx<'_, '_, '_>, data: &T, env: &Env)
fn paint(&mut self, ctx: &mut PaintCtx<'_, '_, '_>, data: &T, env: &Env)
Source§fn compute_max_intrinsic(
&mut self,
axis: Axis,
ctx: &mut LayoutCtx<'_, '_>,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> f64
fn compute_max_intrinsic( &mut self, axis: Axis, ctx: &mut LayoutCtx<'_, '_>, bc: &BoxConstraints, data: &T, env: &Env, ) -> f64
Auto Trait Implementations§
impl<T> !Freeze for TextBox<T>
impl<T> !RefUnwindSafe for TextBox<T>
impl<T> !Send for TextBox<T>
impl<T> !Sync for TextBox<T>
impl<T> Unpin for TextBox<T>where
T: Unpin,
impl<T> !UnwindSafe for TextBox<T>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T, U> RoundInto<U> for Twhere
U: RoundFrom<T>,
impl<T, U> RoundInto<U> for Twhere
U: RoundFrom<T>,
Source§fn round_into(self) -> U
fn round_into(self) -> U
Source§impl<T, W> TestWidgetExt<T> for W
impl<T, W> TestWidgetExt<T> for W
Source§impl<T, W> WidgetExt<T> for W
impl<T, W> WidgetExt<T> for W
Source§fn align_left(self) -> Align<T>
fn align_left(self) -> Align<T>
Align widget, configured to align left.Source§fn align_right(self) -> Align<T>
fn align_right(self) -> Align<T>
Align widget, configured to align right.Source§fn align_vertical(self, align: UnitPoint) -> Align<T>
fn align_vertical(self, align: UnitPoint) -> Align<T>
Align widget, configured to align vertically.Source§fn align_horizontal(self, align: UnitPoint) -> Align<T>
fn align_horizontal(self, align: UnitPoint) -> Align<T>
Align widget, configured to align horizontally.Source§fn fix_width(self, width: impl Into<KeyOrValue<f64>>) -> SizedBox<T>
fn fix_width(self, width: impl Into<KeyOrValue<f64>>) -> SizedBox<T>
SizedBox with an explicit width.Source§fn fix_height(self, height: impl Into<KeyOrValue<f64>>) -> SizedBox<T>
fn fix_height(self, height: impl Into<KeyOrValue<f64>>) -> SizedBox<T>
SizedBox with an explicit height.Source§fn fix_size(
self,
width: impl Into<KeyOrValue<f64>>,
height: impl Into<KeyOrValue<f64>>,
) -> SizedBox<T>
fn fix_size( self, width: impl Into<KeyOrValue<f64>>, height: impl Into<KeyOrValue<f64>>, ) -> SizedBox<T>
SizedBox with an explicit width and heightSource§fn expand_width(self) -> SizedBox<T>
fn expand_width(self) -> SizedBox<T>
Source§fn expand_height(self) -> SizedBox<T>
fn expand_height(self) -> SizedBox<T>
Source§fn background(self, brush: impl Into<BackgroundBrush<T>>) -> Container<T>
fn background(self, brush: impl Into<BackgroundBrush<T>>) -> Container<T>
Source§fn foreground(self, brush: impl Into<BackgroundBrush<T>>) -> Container<T>
fn foreground(self, brush: impl Into<BackgroundBrush<T>>) -> Container<T>
Source§fn border(
self,
color: impl Into<KeyOrValue<Color>>,
width: impl Into<KeyOrValue<f64>>,
) -> Container<T>
fn border( self, color: impl Into<KeyOrValue<Color>>, width: impl Into<KeyOrValue<f64>>, ) -> Container<T>
Source§fn controller<C: Controller<T, Self>>(
self,
controller: C,
) -> ControllerHost<Self, C>
fn controller<C: Controller<T, Self>>( self, controller: C, ) -> ControllerHost<Self, C>
Controller.Source§fn on_added(
self,
f: impl Fn(&mut Self, &mut LifeCycleCtx<'_, '_>, &T, &Env) + 'static,
) -> ControllerHost<Self, Added<T, Self>>
fn on_added( self, f: impl Fn(&mut Self, &mut LifeCycleCtx<'_, '_>, &T, &Env) + 'static, ) -> ControllerHost<Self, Added<T, Self>>
Source§fn on_click(
self,
f: impl Fn(&mut EventCtx<'_, '_>, &mut T, &Env) + 'static,
) -> ControllerHost<Self, Click<T>>
fn on_click( self, f: impl Fn(&mut EventCtx<'_, '_>, &mut T, &Env) + 'static, ) -> ControllerHost<Self, Click<T>>
Source§fn debug_paint_layout(self) -> EnvScope<T, Self>
fn debug_paint_layout(self) -> EnvScope<T, Self>
layout Rects of this widget and its children.Source§fn debug_widget_id(self) -> EnvScope<T, Self>
fn debug_widget_id(self) -> EnvScope<T, Self>
WidgetIds for this widget and its children, when hot. Read moreSource§fn debug_invalidation(self) -> DebugInvalidation<T, Self>
fn debug_invalidation(self) -> DebugInvalidation<T, Self>
Source§fn debug_widget(self) -> EnvScope<T, Self>
fn debug_widget(self) -> EnvScope<T, Self>
DEBUG_WIDGET env variable for this widget (and its descendants). Read moreSource§fn with_id(self, id: WidgetId) -> IdentityWrapper<Self>
fn with_id(self, id: WidgetId) -> IdentityWrapper<Self>
Source§fn disabled_if(
self,
disabled_if: impl Fn(&T, &Env) -> bool + 'static,
) -> DisabledIf<T, Self>
fn disabled_if( self, disabled_if: impl Fn(&T, &Env) -> bool + 'static, ) -> DisabledIf<T, Self>
DisabledIf widget. Read more