Skip to content

Commit d927640

Browse files
committed
Widget adaptor for string parsing
1 parent be2315f commit d927640

File tree

4 files changed

+114
-1
lines changed

4 files changed

+114
-1
lines changed

druid/examples/parse.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2019 The xi-editor Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use druid::widget::{Align, Column, DynLabel, Padding, Parse, TextBox};
16+
use druid::{AppLauncher, Widget, WindowDesc};
17+
18+
fn main() {
19+
let main_window = WindowDesc::new(ui_builder);
20+
let data = Some(0);
21+
AppLauncher::with_window(main_window)
22+
.use_simple_logger()
23+
.launch(data)
24+
.expect("launch failed");
25+
}
26+
27+
fn ui_builder() -> impl Widget<Option<u32>> {
28+
let label = DynLabel::new(|data: &Option<u32>, _env| {
29+
data.map_or_else(|| "Invalid input".into(), |x| x.to_string())
30+
});
31+
let input = Parse::new(TextBox::new());
32+
33+
let mut col = Column::new();
34+
col.add_child(Align::centered(Padding::new(5.0, label)), 1.0);
35+
col.add_child(Padding::new(5.0, input), 1.0);
36+
col
37+
}

druid/src/widget/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,6 @@ pub use widget_ext::WidgetExt;
7070

7171
mod list;
7272
pub use crate::widget::list::{List, ListIter};
73+
74+
mod parse;
75+
pub use crate::widget::parse::Parse;

druid/src/widget/parse.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use std::fmt::Display;
2+
use std::mem;
3+
use std::str::FromStr;
4+
5+
use crate::kurbo::Size;
6+
use crate::{
7+
BaseState, BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, PaintCtx, UpdateCtx, Widget,
8+
};
9+
10+
/// Converts a `Widget<String>` to a `Widget<Option<T>>`, mapping parse errors to None
11+
pub struct Parse<T> {
12+
widget: T,
13+
state: String,
14+
}
15+
16+
impl<T> Parse<T> {
17+
pub fn new(widget: T) -> Self {
18+
Self {
19+
widget,
20+
state: String::new(),
21+
}
22+
}
23+
}
24+
25+
impl<T: FromStr + Display + Data, W: Widget<String>> Widget<Option<T>> for Parse<W> {
26+
fn update(
27+
&mut self,
28+
ctx: &mut UpdateCtx,
29+
old_data: Option<&Option<T>>,
30+
data: &Option<T>,
31+
env: &Env,
32+
) {
33+
let old = match *data {
34+
None => return, // Don't clobber the input
35+
Some(ref x) => mem::replace(&mut self.state, x.to_string()),
36+
};
37+
let old = old_data.map(|_| old);
38+
self.widget.update(ctx, old.as_ref(), &self.state, env)
39+
}
40+
41+
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut Option<T>, env: &Env) {
42+
self.widget.event(ctx, event, &mut self.state, env);
43+
*data = self.state.parse().ok();
44+
}
45+
46+
fn layout(
47+
&mut self,
48+
ctx: &mut LayoutCtx,
49+
bc: &BoxConstraints,
50+
_data: &Option<T>,
51+
env: &Env,
52+
) -> Size {
53+
self.widget.layout(ctx, bc, &self.state, env)
54+
}
55+
56+
fn paint(
57+
&mut self,
58+
paint: &mut PaintCtx,
59+
base_state: &BaseState,
60+
_data: &Option<T>,
61+
env: &Env,
62+
) {
63+
self.widget.paint(paint, base_state, &self.state, env)
64+
}
65+
}

druid/src/widget/widget_ext.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use crate::kurbo::Insets;
1818
use crate::piet::{PaintBrush, UnitPoint};
1919

20-
use super::{Align, Container, EnvScope, Padding, SizedBox};
20+
use super::{Align, Container, EnvScope, Padding, Parse, SizedBox};
2121
use crate::{Data, Env, Lens, LensWrap, Widget};
2222

2323
/// A trait that provides extra methods for combining `Widget`s.
@@ -116,6 +116,14 @@ pub trait WidgetExt<T: Data>: Widget<T> + Sized + 'static {
116116
fn lens<U: Data, L: Lens<T, U>>(self, lens: L) -> LensWrap<U, L, Self> {
117117
LensWrap::new(self, lens)
118118
}
119+
120+
/// Parse a `Widget<String>`'s contents
121+
fn parse(self) -> Parse<Self>
122+
where
123+
Self: Widget<String>,
124+
{
125+
Parse::new(self)
126+
}
119127
}
120128

121129
impl<T: Data + 'static, W: Widget<T> + 'static> WidgetExt<T> for W {}

0 commit comments

Comments
 (0)