Skip to content

Commit feb602c

Browse files
committed
Cleanup formatter work
This resolves a bunch of fixmes and loose ends
1 parent c33f74f commit feb602c

File tree

4 files changed

+62
-59
lines changed

4 files changed

+62
-59
lines changed

druid/examples/flex.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@
1414

1515
//! Demonstrates alignment of children in the flex container.
1616
17+
use druid::text::format::ParseFormatter;
1718
use druid::widget::prelude::*;
1819
use druid::widget::{
1920
Button, Checkbox, CrossAxisAlignment, Flex, Label, MainAxisAlignment, ProgressBar, RadioGroup,
2021
SizedBox, Slider, Stepper, Switch, TextBox, WidgetExt,
2122
};
22-
use druid::{
23-
AppLauncher, Color, Data, Lens, LensExt, LocalizedString, PlatformError, WidgetId, WindowDesc,
24-
};
23+
use druid::{AppLauncher, Color, Data, Lens, LocalizedString, PlatformError, WidgetId, WindowDesc};
2524

2625
const DEFAULT_SPACER_SIZE: f64 = 8.;
2726

@@ -210,11 +209,8 @@ fn make_spacer_select() -> impl Widget<Params> {
210209
Flex::row()
211210
.with_child(
212211
TextBox::new()
213-
.parse()
214-
.lens(
215-
Params::spacer_size
216-
.map(|x| Some(*x), |x, y| *x = y.unwrap_or(DEFAULT_SPACER_SIZE)),
217-
)
212+
.with_formatter(ParseFormatter)
213+
.lens(Params::spacer_size)
218214
.fix_width(60.0),
219215
)
220216
.with_spacer(druid::theme::WIDGET_CONTROL_COMPONENT_PADDING)

druid/src/core.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,7 @@ impl WidgetState {
10941094
mod tests {
10951095
use super::*;
10961096
use crate::ext_event::ExtEventHost;
1097+
use crate::text::format::ParseFormatter;
10971098
use crate::widget::{Flex, Scroll, Split, TextBox};
10981099
use crate::{WidgetExt, WindowHandle, WindowId};
10991100

@@ -1103,13 +1104,13 @@ mod tests {
11031104

11041105
#[test]
11051106
fn register_children() {
1106-
fn make_widgets() -> impl Widget<Option<u32>> {
1107+
fn make_widgets() -> impl Widget<u32> {
11071108
Split::columns(
1108-
Flex::<Option<u32>>::row()
1109-
.with_child(TextBox::new().with_id(ID_1).parse())
1110-
.with_child(TextBox::new().with_id(ID_2).parse())
1111-
.with_child(TextBox::new().with_id(ID_3).parse()),
1112-
Scroll::new(TextBox::new().parse()),
1109+
Flex::<u32>::row()
1110+
.with_child(TextBox::new().with_formatter(ParseFormatter).with_id(ID_1))
1111+
.with_child(TextBox::new().with_formatter(ParseFormatter).with_id(ID_2))
1112+
.with_child(TextBox::new().with_formatter(ParseFormatter).with_id(ID_3)),
1113+
Scroll::new(TextBox::new().with_formatter(ParseFormatter)),
11131114
)
11141115
}
11151116

@@ -1136,7 +1137,7 @@ mod tests {
11361137

11371138
let env = Env::default();
11381139

1139-
widget.lifecycle(&mut ctx, &LifeCycle::WidgetAdded, &None, &env);
1140+
widget.lifecycle(&mut ctx, &LifeCycle::WidgetAdded, &1, &env);
11401141
assert!(ctx.widget_state.children.may_contain(&ID_1));
11411142
assert!(ctx.widget_state.children.may_contain(&ID_2));
11421143
assert!(ctx.widget_state.children.may_contain(&ID_3));

druid/src/widget/textbox.rs

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ const COMPLETE_EDITING: Selector = Selector::new("druid.builtin.textbox-complete
3636
const CANCEL_EDITING: Selector = Selector::new("druid.builtin.textbox-cancel-editing");
3737

3838
/// A widget that allows user text input.
39+
///
40+
/// # Editing values
41+
///
42+
/// If the text you are editing represents a value of some other type, such
43+
/// as a number, you should use a [`ValueTextBox`] and an appropriate
44+
/// [`Formatter`]. You can create a [`ValueTextBox`] by passing the appropriate
45+
/// [`Formatter`] to [`TextBox::with_formatter`].
3946
#[derive(Debug, Clone)]
4047
pub struct TextBox<T> {
4148
placeholder: TextLayout<String>,
@@ -275,14 +282,6 @@ impl<T> TextBox<T> {
275282
pub fn editor(&self) -> &Editor<T> {
276283
&self.editor
277284
}
278-
279-
/// Return a mutable reference to the [`Editor`] used by this `TextBox`.
280-
///
281-
/// [`Editor`]: crate::text::Editor
282-
//TODO: document the ways you should and shouldn't use this
283-
pub fn editor_mut(&mut self) -> &mut Editor<T> {
284-
&mut self.editor
285-
}
286285
}
287286

288287
impl TextBox<String> {
@@ -300,8 +299,16 @@ impl TextBox<String> {
300299
}
301300

302301
impl<T: TextStorage + EditableText> TextBox<T> {
302+
/// Set the textbox's selection.
303+
pub fn set_selection(&mut self, selection: Selection) {
304+
self.editor.set_selection(selection);
305+
}
306+
303307
/// Set the text and force the editor to update.
304-
//FIXME: do we need this? can we not just rely on `update`?
308+
///
309+
/// This should be rarely needed; the main use-case would be if you need
310+
/// to manually set the text and then immediately do hit-testing or other
311+
/// tasks that rely on having an up-to-date text layout.
305312
pub fn force_rebuild(&mut self, text: T, factory: &mut PietText, env: &Env) {
306313
self.editor.set_text(text);
307314
self.editor.rebuild_if_needed(factory, env);
@@ -634,14 +641,13 @@ impl<T: Data> ValueTextBox<T> {
634641
self
635642
}
636643

637-
fn complete(&mut self, ctx: &mut EventCtx, data: &mut T, env: &Env) {
644+
fn complete(&mut self, ctx: &mut EventCtx, data: &mut T) {
638645
match self.formatter.value(&self.buffer) {
639646
Ok(new_data) => {
640647
*data = new_data;
641-
self.inner
642-
.force_rebuild(self.formatter.format(data), ctx.text(), env);
648+
self.buffer = self.formatter.format(data);
643649
self.is_editing = false;
644-
ctx.request_layout();
650+
ctx.request_update();
645651
if ctx.has_focus() {
646652
ctx.resign_focus();
647653
}
@@ -665,24 +671,19 @@ impl<T: Data> ValueTextBox<T> {
665671
}
666672
}
667673

668-
fn cancel(&mut self, ctx: &mut EventCtx, data: &T, env: &Env) {
674+
fn cancel(&mut self, ctx: &mut EventCtx, data: &T) {
669675
self.is_editing = false;
670676
self.buffer = self.formatter.format(data);
671-
ctx.request_layout();
677+
ctx.request_update();
672678
ctx.resign_focus();
673-
self.inner
674-
.force_rebuild(self.buffer.clone(), ctx.text(), env);
675679
self.send_event(ctx, TextBoxEvent::Cancel);
676680
}
677681

678-
fn begin(&mut self, ctx: &mut EventCtx, data: &T, env: &Env) {
682+
fn begin(&mut self, ctx: &mut EventCtx, data: &T) {
679683
self.is_editing = true;
680684
self.buffer = self.formatter.format_for_editing(data);
681685
self.last_known_data = Some(data.clone());
682-
self.inner
683-
.force_rebuild(self.buffer.clone(), ctx.text(), env);
684-
self.old_buffer = self.buffer.clone();
685-
ctx.request_layout();
686+
ctx.request_update();
686687
self.send_event(ctx, TextBoxEvent::Began);
687688
}
688689

@@ -696,27 +697,23 @@ impl<T: Data> ValueTextBox<T> {
696697
impl<T: Data> Widget<T> for ValueTextBox<T> {
697698
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
698699
if matches!(event, Event::Command(cmd) if cmd.is(BEGIN_EDITING)) {
699-
return self.begin(ctx, data, env);
700+
return self.begin(ctx, data);
700701
}
701702

702703
if self.is_editing {
703704
// if we reject an edit we want to reset the selection
704705
let pre_sel = *self.inner.editor().selection();
705706
match event {
706-
Event::Command(cmd) if cmd.is(COMPLETE_EDITING) => {
707-
return self.complete(ctx, data, env)
708-
}
709-
Event::Command(cmd) if cmd.is(CANCEL_EDITING) => {
710-
return self.cancel(ctx, data, env)
711-
}
707+
Event::Command(cmd) if cmd.is(COMPLETE_EDITING) => return self.complete(ctx, data),
708+
Event::Command(cmd) if cmd.is(CANCEL_EDITING) => return self.cancel(ctx, data),
712709
Event::KeyDown(k_e) if HotKey::new(None, KbKey::Enter).matches(k_e) => {
713710
ctx.set_handled();
714-
self.complete(ctx, data, env);
711+
self.complete(ctx, data);
715712
return;
716713
}
717714
Event::KeyDown(k_e) if HotKey::new(None, KbKey::Escape).matches(k_e) => {
718715
ctx.set_handled();
719-
self.cancel(ctx, data, env);
716+
self.cancel(ctx, data);
720717
return;
721718
}
722719
event => {
@@ -759,10 +756,8 @@ impl<T: Data> Widget<T> for ValueTextBox<T> {
759756
};
760757

761758
if let Some(new_buf) = new_buf {
762-
self.buffer = new_buf.clone();
763-
self.inner.editor_mut().set_text(new_buf);
759+
self.buffer = new_buf;
764760
}
765-
//FIXME we stash this and set it in update; can we do the same with `new_buf`?
766761
self.force_selection = new_sel;
767762

768763
if self.update_data_while_editing && !validation.is_err() {
@@ -782,7 +777,11 @@ impl<T: Data> Widget<T> for ValueTextBox<T> {
782777
}
783778
ctx.request_update();
784779
} else if let Event::MouseDown(_) = event {
785-
self.begin(ctx, data, env);
780+
self.begin(ctx, data);
781+
// we need to rebuild immediately here in order for the click
782+
// to be handled with the most recent text.
783+
self.inner
784+
.force_rebuild(self.buffer.clone(), ctx.text(), env);
786785
self.inner.event(ctx, event, &mut self.buffer, env);
787786
}
788787
}
@@ -820,17 +819,23 @@ impl<T: Data> Widget<T> for ValueTextBox<T> {
820819
self.formatter.format(data)
821820
);
822821
}
823-
} else if !old_data.same(data) {
824-
// we aren't editing and data changed
825-
let new_text = self.formatter.format(data);
826-
self.old_buffer = std::mem::replace(&mut self.buffer, new_text);
827-
self.inner.update(ctx, &self.old_buffer, &self.buffer, env);
828-
} else if ctx.env_changed() {
829-
self.inner.update(ctx, &self.buffer, &self.buffer, env);
830-
ctx.request_layout();
822+
} else {
823+
if !old_data.same(data) {
824+
// we aren't editing and data changed
825+
let new_text = self.formatter.format(data);
826+
self.old_buffer = std::mem::replace(&mut self.buffer, new_text);
827+
}
828+
829+
if !self.old_buffer.same(&self.buffer) {
830+
// inner widget handles calling request_layout, as needed
831+
self.inner.update(ctx, &self.old_buffer, &self.buffer, env);
832+
self.old_buffer = self.buffer.clone();
833+
} else if ctx.env_changed() {
834+
self.inner.update(ctx, &self.buffer, &self.buffer, env);
835+
}
831836
}
832837
if let Some(sel) = self.force_selection.take() {
833-
self.inner.editor_mut().set_selection(sel);
838+
self.inner.set_selection(sel);
834839
}
835840
}
836841

druid/src/widget/widget_ext.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ pub trait WidgetExt<T: Data>: Widget<T> + Sized + 'static {
220220
}
221221

222222
/// Parse a `Widget<String>`'s contents
223+
#[deprecated(since = "0.7.0", note = "Use TextBox::with_formatter instead")]
223224
fn parse(self) -> Parse<Self>
224225
where
225226
Self: Widget<String>,

0 commit comments

Comments
 (0)