pub struct LensWrap<T, U, L, W> { /* private fields */ }
Expand description
A wrapper for its widget subtree to have access to a part of its parent’s data.
Every widget in Druid is instantiated with access to data of some
type; the root widget has access to the entire application data.
Often, a part of the widget hierarchy is only concerned with a part
of that data. The LensWrap
widget is a way to “focus” the data
reference down, for the subtree. One advantage is performance;
data changes that don’t intersect the scope of the lens aren’t
propagated.
Another advantage is generality and reuse. If a widget (or tree of widgets) is designed to work with some chunk of data, then with a lens that same code can easily be reused across all occurrences of that chunk within the application state.
This wrapper takes a Lens
as an argument, which is a specification
of a struct field, or some other way of narrowing the scope.
Implementations§
source§impl<T, U, L, W> LensWrap<T, U, L, W>
impl<T, U, L, W> LensWrap<T, U, L, W>
sourcepub fn new(child: W, lens: L) -> LensWrap<T, U, L, W>
pub fn new(child: W, lens: L) -> LensWrap<T, U, L, W>
Wrap a widget with a lens.
When the lens has type Lens<T, U>
, the child widget has data
of type U
, and the wrapped widget has data of type T
.
Examples found in repository?
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
fn build_widget() -> impl Widget<DemoState> {
let mut col = Flex::column();
let mut row = Flex::row();
let switch = LensWrap::new(Switch::new(), DemoState::value);
let check_box = LensWrap::new(Checkbox::new(""), DemoState::value);
let switch_label = Label::new("Setting label");
row.add_child(Padding::new(5.0, switch_label));
row.add_child(Padding::new(5.0, switch));
row.add_child(Padding::new(5.0, check_box));
let stepper = LensWrap::new(
Stepper::new()
.with_range(0.0, 10.0)
.with_step(0.5)
.with_wraparound(false),
DemoState::stepper_value,
);
let mut textbox_row = Flex::row();
// TODO: Replace Parse usage with TextBox::with_formatter
#[allow(deprecated)]
let textbox = LensWrap::new(
Parse::new(TextBox::new()),
DemoState::stepper_value.map(|x| Some(*x), |x, y| *x = y.unwrap_or(0.0)),
);
textbox_row.add_child(Padding::new(5.0, textbox));
textbox_row.add_child(Padding::new(5.0, stepper.center()));
let mut label_row = Flex::row();
let label = Label::new(|data: &DemoState, _env: &_| {
format!("Stepper value: {0:.2}", data.stepper_value)
});
label_row.add_child(Padding::new(5.0, label));
col.set_main_axis_alignment(MainAxisAlignment::Center);
col.add_child(Padding::new(5.0, row));
col.add_child(Padding::new(5.0, textbox_row));
col.add_child(Padding::new(5.0, label_row));
col.center()
}
More examples
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
fn ui_builder() -> impl Widget<AppData> {
let my_painter = Painter::new(|ctx, _, _| {
let bounds = ctx.size().to_rect();
if ctx.is_hot() {
ctx.fill(bounds, &Color::rgba8(0, 0, 0, 128));
}
if ctx.is_active() {
ctx.stroke(bounds, &Color::WHITE, 2.0);
}
});
// This is Druid's default text style.
// It's set by theme::LABEL_COLOR and theme::UI_FONT
let label =
Label::new(|data: &String, _env: &_| format!("Default: {data}")).lens(AppData::text);
// The text_color, text_size, and font builder methods can override the
// defaults provided by the theme by passing in a Key or a concrete value.
//
// In this example, text_color receives a Key from the theme, text_size
// gets a custom key which we set with the env_scope wrapper, and the
// default font key (theme::FONT_NAME) is overridden in the env_scope
// wrapper. (Like text_color and text_size, the font can be set using the
// with_font builder method, but overriding here makes it easy to fall back
// to the default font)
let styled_label = Label::new(|data: &AppData, _env: &_| format!("{data}"))
.with_text_color(theme::PRIMARY_LIGHT)
.with_font(MY_CUSTOM_FONT)
.background(my_painter)
.on_click(|_, data, _| {
data.size *= 1.1;
})
.env_scope(|env: &mut druid::Env, data: &AppData| {
let new_font = if data.mono {
FontDescriptor::new(FontFamily::MONOSPACE)
} else {
FontDescriptor::new(FontFamily::SYSTEM_UI)
}
.with_size(data.size);
env.set(MY_CUSTOM_FONT, new_font);
});
let labels = Scroll::new(
Flex::column()
.cross_axis_alignment(CrossAxisAlignment::Start)
.with_child(label)
.with_default_spacer()
.with_child(styled_label),
)
.expand_height()
.fix_width(COLUMN_WIDTH);
let stepper = Stepper::new()
.with_range(0.0, 100.0)
.with_step(1.0)
.with_wraparound(false)
.lens(AppData::size);
// TODO: Replace Parse usage with TextBox::with_formatter
#[allow(deprecated)]
let stepper_textbox = LensWrap::new(
Parse::new(TextBox::new()),
AppData::size.map(|x| Some(*x), |x, y| *x = y.unwrap_or(24.0)),
);
let mono_checkbox = Checkbox::new("Monospace").lens(AppData::mono);
let stepper_row = Flex::row()
.with_child(stepper_textbox)
.with_child(stepper)
.with_default_spacer()
.with_child(mono_checkbox);
let input = TextBox::multiline()
.with_placeholder("Your sample text here :)")
.fix_width(COLUMN_WIDTH)
.fix_height(140.0)
.lens(AppData::text);
Flex::column()
.main_axis_alignment(MainAxisAlignment::Center)
.with_default_spacer()
.with_flex_child(labels, 1.0)
.with_default_spacer()
.with_child(input)
.with_default_spacer()
.with_child(stepper_row)
.with_default_spacer()
}
Trait Implementations§
source§impl<T, U, L, W> Widget<T> for LensWrap<T, U, L, W>where
T: Data,
U: Data,
L: Lens<T, U>,
W: Widget<U>,
impl<T, U, L, W> Widget<T> for LensWrap<T, U, L, W>where T: Data, U: Data, L: Lens<T, U>, W: Widget<U>,
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
source§impl<T, U, L, W> WidgetWrapper for LensWrap<T, U, L, W>
impl<T, U, L, W> WidgetWrapper for LensWrap<T, U, L, W>
§type Wrapped = W
type Wrapped = W
Widget<impl Data>
(if existential bounds were supported).
Any other scheme leads to T
being unconstrained in unification at some point