smudge_core/element.rs
1//! Elements: the builder that describes one node, and the bindings that keep
2//! its properties current.
3//!
4//! An element holds a widget, a style, the bindings that update parts of that
5//! style, and its children. Building one touches no tree: mounting creates the
6//! nodes and installs one render effect per binding inside the node's owner.
7//!
8//! A binding never writes to the tree itself. Its effect reads the value and
9//! pushes an update onto the shared update queue, and the frame applies the
10//! queued updates before it lays anything out. That is what keeps every write
11//! to the tree in one place and in one order.
12//!
13//! Every builder takes either a plain value or a closure. A plain value is
14//! written when the element is built and costs nothing afterwards. A closure
15//! becomes a binding: it is read when the node mounts and again whenever a
16//! signal it reads changes, and it updates that one property without rebuilding
17//! the element or its children.
18//!
19//! ```
20//! use smudge_core::geometry::Color;
21//! use smudge_core::reactive::{self, prelude::*};
22//! use smudge_core::style::px;
23//! use smudge_core::widget::panel;
24//!
25//! let (on, _set_on) = reactive::signal_local(false);
26//! let card = panel()
27//! .width(px(120.0))
28//! .height(px(40.0))
29//! .corner_radius(6.0)
30//! .background(move || {
31//! if on.get() {
32//! Color::rgb(220, 60, 60).into()
33//! } else {
34//! Color::rgb(60, 60, 220).into()
35//! }
36//! })
37//! .child(panel().width(px(20.0)).height(px(20.0)).background(Color::WHITE));
38//! ```
39
40use std::any::TypeId;
41use std::cell::RefCell;
42use std::collections::{HashMap, VecDeque};
43use std::hash::Hash;
44use std::rc::Rc;
45
46use crate::geometry::{Color, Point, Size};
47use crate::input::{Handled, KeyEvent};
48use crate::paint::PaintOp;
49use crate::reactive::{Owner, RenderEffect, RwSignal};
50use crate::semantics::Role;
51use crate::style::{
52 Align, Direction, Edges, Justify, LayoutProps, Length, Overflow, PaintProps, Position, Style,
53};
54use crate::theme::{ColorToken, ColorValue, RadiusToken, RadiusValue};
55use crate::tree::interaction::Handlers;
56use crate::tree::{Dirty, WidgetId, WidgetTree};
57use crate::variants::{PatchBuilder, StylePatch, Variants};
58use crate::widget::{PaintCx, Widget};
59
60/// One change to the widget tree, applied by the frame's apply step.
61pub(crate) type Update = Box<dyn FnOnce(&mut WidgetTree)>;
62
63/// The style that an element applies to every child as it is added.
64type ChildStyling = Rc<dyn Fn(&mut Style)>;
65
66/// The updates that are waiting to be applied.
67///
68/// Every binding shares one queue, so a frame applies the effects of all of
69/// them in the order the effects were queued.
70#[derive(Clone, Default)]
71pub(crate) struct UpdateQueue(Rc<RefCell<VecDeque<Update>>>);
72
73impl UpdateQueue {
74 /// Queues `update` to run in the next apply step.
75 pub(crate) fn push(&self, update: Update) {
76 self.0.borrow_mut().push_back(update);
77 }
78
79 /// Applies every queued update to `tree`, in the order they were queued,
80 /// and returns whether any ran.
81 ///
82 /// An update may queue another update, as mounting a child does when its
83 /// bindings run. Those run after the updates that were already waiting, in
84 /// the same call, so one call settles everything the queue holds.
85 pub(crate) fn apply(&self, tree: &mut WidgetTree) -> bool {
86 let mut applied = false;
87 loop {
88 let next = self.0.borrow_mut().pop_front();
89 let Some(update) = next else {
90 return applied;
91 };
92 applied = true;
93 update(tree);
94 }
95 }
96}
97
98/// A property that is either fixed or computed from signals.
99///
100/// [`Value::Static`] is written once, when the element is built. [`Value::Dynamic`]
101/// becomes a binding: the closure is read when the node mounts and again
102/// whenever a signal it reads changes.
103///
104/// A value converts into this type, so a builder method can take either one.
105#[derive(Clone)]
106pub enum Value<T> {
107 /// A value that never changes.
108 Static(T),
109 /// A closure that reads the value from signals.
110 Dynamic(Rc<dyn Fn() -> T>),
111}
112
113/// Implements the conversion from a plain value for every property type.
114macro_rules! static_values {
115 ($($property:ty),* $(,)?) => {
116 $(
117 impl From<$property> for Value<$property> {
118 fn from(value: $property) -> Value<$property> {
119 Value::Static(value)
120 }
121 }
122 )*
123 };
124}
125
126static_values!(
127 ColorValue,
128 RadiusValue,
129 Length,
130 Size,
131 Edges<Length>,
132 f32,
133 bool,
134 Direction,
135 Align,
136 Justify,
137 Overflow,
138 Point,
139);
140
141impl From<crate::window::Cursor> for Value<Option<crate::window::Cursor>> {
142 fn from(cursor: crate::window::Cursor) -> Value<Option<crate::window::Cursor>> {
143 Value::Static(Some(cursor))
144 }
145}
146
147impl From<Option<crate::window::Cursor>> for Value<Option<crate::window::Cursor>> {
148 fn from(cursor: Option<crate::window::Cursor>) -> Value<Option<crate::window::Cursor>> {
149 Value::Static(cursor)
150 }
151}
152
153impl From<Option<u32>> for Value<Option<u32>> {
154 fn from(value: Option<u32>) -> Value<Option<u32>> {
155 Value::Static(value)
156 }
157}
158
159impl From<u32> for Value<u32> {
160 fn from(value: u32) -> Value<u32> {
161 Value::Static(value)
162 }
163}
164
165impl From<String> for Value<String> {
166 fn from(text: String) -> Value<String> {
167 Value::Static(text)
168 }
169}
170
171impl From<&str> for Value<String> {
172 fn from(text: &str) -> Value<String> {
173 Value::Static(text.to_owned())
174 }
175}
176
177impl From<Color> for Value<ColorValue> {
178 fn from(color: Color) -> Value<ColorValue> {
179 Value::Static(color.into())
180 }
181}
182
183impl From<ColorToken> for Value<ColorValue> {
184 fn from(token: ColorToken) -> Value<ColorValue> {
185 Value::Static(token.into())
186 }
187}
188
189impl From<f32> for Value<RadiusValue> {
190 fn from(radius: f32) -> Value<RadiusValue> {
191 Value::Static(radius.into())
192 }
193}
194
195impl From<RadiusToken> for Value<RadiusValue> {
196 fn from(token: RadiusToken) -> Value<RadiusValue> {
197 Value::Static(token.into())
198 }
199}
200
201impl<F: Fn() -> T + 'static, T: 'static> From<F> for Value<T> {
202 fn from(get: F) -> Value<T> {
203 Value::Dynamic(Rc::new(get))
204 }
205}
206
207/// Installs a binding's effect inside a node's owner and returns its handle.
208type Install = Box<dyn FnOnce(WidgetId, &UpdateQueue) -> RenderEffect>;
209
210/// One property of one node that a render effect keeps up to date.
211struct Binding {
212 /// Creates the effect inside the node's owner and returns its handle.
213 ///
214 /// The effect runs once immediately and queues an update every time it
215 /// runs.
216 install: Install,
217 /// The property the binding writes, when it writes one the element's
218 /// builders can set again.
219 key: Option<Key>,
220}
221
222/// What a binding writes, so a later write of the same thing can replace an
223/// earlier binding of it.
224#[derive(Copy, Clone, Debug, PartialEq, Eq)]
225enum Key {
226 /// A property of the node's style or state.
227 Style(Prop),
228 /// A property of the node's widget, named by the marker type given to
229 /// [`Element::bind`].
230 Widget(TypeId),
231}
232
233/// A property an element's builders write, so a later write of the same
234/// property can replace an earlier binding of it.
235#[derive(Copy, Clone, Debug, PartialEq, Eq)]
236enum Prop {
237 Width,
238 Height,
239 MinWidth,
240 MinHeight,
241 MaxWidth,
242 MaxHeight,
243 Padding,
244 Margin,
245 Gap,
246 Direction,
247 Wrap,
248 Grow,
249 Shrink,
250 Basis,
251 AlignItems,
252 JustifyContent,
253 Inset,
254 BorderWidth,
255 Overflow,
256 Hidden,
257 Background,
258 BorderColor,
259 TextColor,
260 Radius,
261 ScrollOffset,
262 Disabled,
263 Cursor,
264 Pin,
265}
266
267impl Binding {
268 /// A binding that writes one layout property.
269 fn layout<T: 'static>(
270 prop: Prop,
271 get: Rc<dyn Fn() -> T>,
272 write: fn(&mut LayoutProps, T),
273 ) -> Binding {
274 Binding {
275 key: Some(Key::Style(prop)),
276 install: Box::new(move |id, queue| {
277 let queue = queue.clone();
278 RenderEffect::new(move || {
279 let value = get();
280 let queue = queue.clone();
281 queue.push(Box::new(move |tree| {
282 tree.apply_layout(id, move |layout| write(layout, value));
283 }));
284 })
285 }),
286 }
287 }
288
289 /// A binding that writes one paint property.
290 fn paint<T: 'static>(
291 prop: Prop,
292 get: Rc<dyn Fn() -> T>,
293 write: fn(&mut PaintProps, T),
294 ) -> Binding {
295 Binding {
296 key: Some(Key::Style(prop)),
297 install: Box::new(move |id, queue| {
298 let queue = queue.clone();
299 RenderEffect::new(move || {
300 let value = get();
301 let queue = queue.clone();
302 queue.push(Box::new(move |tree| {
303 tree.apply_paint(id, move |paint| write(paint, value));
304 }));
305 })
306 }),
307 }
308 }
309
310 /// A binding that writes the node's disabled bit.
311 fn disabled(get: Rc<dyn Fn() -> bool>) -> Binding {
312 Binding {
313 key: Some(Key::Style(Prop::Disabled)),
314 install: Box::new(move |id, queue| {
315 let queue = queue.clone();
316 RenderEffect::new(move || {
317 let value = get();
318 let queue = queue.clone();
319 queue.push(Box::new(move |tree| {
320 tree.set_disabled(id, value);
321 }));
322 })
323 }),
324 }
325 }
326
327 /// A binding that writes the node's accessible label.
328 fn a11y_label(get: Rc<dyn Fn() -> String>) -> Binding {
329 Binding {
330 key: None,
331 install: Box::new(move |id, queue| {
332 let queue = queue.clone();
333 RenderEffect::new(move || {
334 let value = get();
335 let queue = queue.clone();
336 queue.push(Box::new(move |tree| {
337 tree.set_a11y_label(id, Some(value));
338 }));
339 })
340 }),
341 }
342 }
343
344 /// A binding that writes the node's scroll offset.
345 ///
346 /// The write moves the node's flowing descendants without running the
347 /// layout engine, which is what a scroll view does on every wheel tick.
348 fn scroll_offset(get: Rc<dyn Fn() -> Point>) -> Binding {
349 Binding {
350 key: Some(Key::Style(Prop::ScrollOffset)),
351 install: Box::new(move |id, queue| {
352 let queue = queue.clone();
353 RenderEffect::new(move || {
354 let value = get();
355 let queue = queue.clone();
356 queue.push(Box::new(move |tree| {
357 tree.set_scroll_offset(id, value);
358 }));
359 })
360 }),
361 }
362 }
363
364 /// A binding that writes which flowing child the node pins, and where.
365 fn pin_child(get: Rc<dyn Fn() -> Option<(usize, f32)>>) -> Binding {
366 Binding {
367 key: Some(Key::Style(Prop::Pin)),
368 install: Box::new(move |id, queue| {
369 let queue = queue.clone();
370 RenderEffect::new(move || {
371 let value = get();
372 let queue = queue.clone();
373 queue.push(Box::new(move |tree| {
374 tree.set_pin(id, value);
375 }));
376 })
377 }),
378 }
379 }
380
381 /// A binding that writes the node's cursor.
382 fn cursor(get: Rc<dyn Fn() -> Option<crate::window::Cursor>>) -> Binding {
383 Binding {
384 key: Some(Key::Style(Prop::Cursor)),
385 install: Box::new(move |id, queue| {
386 let queue = queue.clone();
387 RenderEffect::new(move || {
388 let value = get();
389 let queue = queue.clone();
390 queue.push(Box::new(move |tree| {
391 tree.set_cursor(id, value);
392 }));
393 })
394 }),
395 }
396 }
397
398 /// A binding that writes one property of the node's widget.
399 ///
400 /// The apply function reports how much of the node its write invalidated,
401 /// and the node is marked with that. A write that changes what the widget
402 /// measures returns [`Dirty::Layout`], which also invalidates the size the
403 /// layout engine cached for the node.
404 fn widget<W: Widget, T: 'static>(
405 key: Key,
406 get: Rc<dyn Fn() -> T>,
407 apply: fn(&mut W, T) -> Dirty,
408 ) -> Binding {
409 Binding {
410 key: Some(key),
411 install: Box::new(move |id, queue| {
412 let queue = queue.clone();
413 RenderEffect::new(move || {
414 let value = get();
415 let queue = queue.clone();
416 queue.push(Box::new(move |tree| {
417 let dirty = match tree.widget_mut::<W>(id) {
418 Some(widget) => apply(widget, value),
419 None => return,
420 };
421 tree.mark(id, dirty);
422 }));
423 })
424 }),
425 }
426 }
427}
428
429/// A node under construction: its widget, its style, its pending bindings and
430/// its children.
431///
432/// Build one with a widget constructor such as [`panel`](crate::widget::panel),
433/// chain the builder methods, and hand it to a parent with [`Element::child`]
434/// or to [`show`].
435///
436/// A builder takes either a plain value, which is written into the style
437/// immediately, or a closure, which becomes a binding that is read at mount and
438/// again whenever a signal it reads changes.
439pub struct Element<W: Widget> {
440 /// The widget the node will own.
441 widget: W,
442 /// The style the node starts with.
443 style: Style,
444 /// Bindings installed when the node mounts.
445 bindings: Vec<Binding>,
446 /// The style patches per interaction state, when a variant builder ran.
447 variants: Option<Variants>,
448 /// The input handlers the node will own.
449 handlers: Handlers,
450 /// Whether the node starts disabled.
451 disabled: bool,
452 /// The cursor the node sets for the pointer over it, when its element
453 /// set a static one.
454 cursor: Option<crate::window::Cursor>,
455 /// Whether the node can take the keyboard focus.
456 focusable: bool,
457 /// Whether the node requests the focus on the first frame after it is
458 /// laid out.
459 autofocus: bool,
460 /// Whether the node keeps Tab movement inside its subtree while it is
461 /// mounted.
462 trap_focus: bool,
463 /// The signal that pins the node's scroll offset to the end of its
464 /// content while it reads `true`.
465 follow_end: Option<RwSignal<bool>>,
466 /// The role the accessibility projection reports, when an element set
467 /// one.
468 a11y_role: Option<Role>,
469 /// The accessible label an element set, when it set one.
470 a11y_label: Option<Value<String>>,
471 /// The scroll offset written to the node when it mounts.
472 scroll_offset: Point,
473 /// Applied to the style of every child as it is added.
474 child_styling: Option<ChildStyling>,
475 /// The children, in paint order.
476 children: Vec<AnyElement>,
477}
478
479impl<W: Widget> Element<W> {
480 /// Creates an element for `widget` with the default style.
481 ///
482 /// ```
483 /// use smudge_core::element::Element;
484 /// use smudge_core::widget::Panel;
485 ///
486 /// let element = Element::new(Panel);
487 /// ```
488 pub fn new(widget: W) -> Element<W> {
489 Element {
490 widget,
491 style: Style::default(),
492 bindings: Vec::new(),
493 variants: None,
494 handlers: Handlers::default(),
495 disabled: false,
496 cursor: None,
497 focusable: false,
498 autofocus: false,
499 trap_focus: false,
500 follow_end: None,
501 a11y_role: None,
502 a11y_label: None,
503 scroll_offset: Point::default(),
504 child_styling: None,
505 children: Vec::new(),
506 }
507 }
508
509 /// Appends `child` after the children the element already has.
510 ///
511 /// When [`Element::style_children`] set a style for the children, it is
512 /// applied to this one.
513 pub fn child(mut self, child: impl IntoElement) -> Self {
514 let mut child = child.into_any();
515 if let Some(style) = &self.child_styling {
516 style(&mut child.style);
517 }
518 self.children.push(child);
519 self
520 }
521
522 /// Appends every child of `children`, in order.
523 ///
524 /// When [`Element::style_children`] set a style for the children, it is
525 /// applied to each one.
526 pub fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self {
527 self.children.extend(children.into_iter().map(|child| {
528 let mut child = child.into_any();
529 if let Some(style) = &self.child_styling {
530 style(&mut child.style);
531 }
532 child
533 }));
534 self
535 }
536
537 /// Applies `style` to every direct child of this element: to the children
538 /// added so far, at once, and to every child added afterwards as it is
539 /// added.
540 ///
541 /// The children of a child keep their own style, and a child that mounts
542 /// later through [`show`] mounts under a slot, so it is styled only
543 /// through that slot. A binding on the same field of a child applies when
544 /// the child mounts, after this style, so the binding wins.
545 ///
546 /// This is how a container decides how its children sit: a stack applies
547 /// the absolute position that makes children overlap, for example.
548 ///
549 /// ```
550 /// use smudge_core::element::Element;
551 /// use smudge_core::widget::Panel;
552 ///
553 /// let layers = Element::new(Panel).style_children(|style| {
554 /// style.paint.corner_radius = 4.0.into();
555 /// });
556 /// ```
557 pub fn style_children(mut self, style: impl Fn(&mut Style) + 'static) -> Self {
558 let style = Rc::new(style);
559 for child in &mut self.children {
560 style(&mut child.style);
561 }
562 self.child_styling = Some(style);
563 self
564 }
565
566 /// Writes one layout property, or records a binding for it.
567 fn layout_value<T: 'static>(
568 mut self,
569 prop: Prop,
570 value: impl Into<Value<T>>,
571 write: fn(&mut LayoutProps, T),
572 ) -> Self {
573 self.replace_binding(prop);
574 match value.into() {
575 Value::Static(value) => write(&mut self.style.layout, value),
576 Value::Dynamic(get) => self.bindings.push(Binding::layout(prop, get, write)),
577 }
578 self
579 }
580
581 /// Drops the bindings recorded for `prop` so far, because a later write
582 /// of the same property, fixed or bound, replaces them: the last builder
583 /// call for a property wins, as it does between two fixed values.
584 fn replace_binding(&mut self, prop: Prop) {
585 self.drop_bindings(Key::Style(prop));
586 }
587
588 /// Drops the bindings recorded for `key` so far.
589 fn drop_bindings(&mut self, key: Key) {
590 self.bindings.retain(|binding| binding.key != Some(key));
591 }
592
593 /// Writes one paint property, or records a binding for it.
594 fn paint_value<T: 'static>(
595 mut self,
596 prop: Prop,
597 value: impl Into<Value<T>>,
598 write: fn(&mut PaintProps, T),
599 ) -> Self {
600 self.replace_binding(prop);
601 match value.into() {
602 Value::Static(value) => write(&mut self.style.paint, value),
603 Value::Dynamic(get) => self.bindings.push(Binding::paint(prop, get, write)),
604 }
605 self
606 }
607
608 /// Sets the width. This takes part in layout.
609 pub fn width(self, width: impl Into<Value<Length>>) -> Self {
610 self.layout_value(Prop::Width, width, |layout, width| layout.width = width)
611 }
612
613 /// Sets the height. This takes part in layout.
614 pub fn height(self, height: impl Into<Value<Length>>) -> Self {
615 self.layout_value(Prop::Height, height, |layout, height| {
616 layout.height = height
617 })
618 }
619
620 /// Sets the width and the height, in logical pixels. This takes part in
621 /// layout.
622 pub fn size(self, size: impl Into<Value<Size>>) -> Self {
623 let (width, height): (Value<Length>, Value<Length>) = match size.into() {
624 Value::Static(size) => (
625 Value::Static(Length::px(size.width)),
626 Value::Static(Length::px(size.height)),
627 ),
628 Value::Dynamic(get) => {
629 let get_height = Rc::clone(&get);
630 (
631 Value::Dynamic(Rc::new(move || Length::px(get().width))),
632 Value::Dynamic(Rc::new(move || Length::px(get_height().height))),
633 )
634 }
635 };
636 self.width(width).height(height)
637 }
638
639 /// Sets the lower bound of the width. This takes part in layout.
640 pub fn min_width(self, width: impl Into<Value<Length>>) -> Self {
641 self.layout_value(Prop::MinWidth, width, |layout, width| {
642 layout.min_width = width
643 })
644 }
645
646 /// Sets the lower bound of the height. This takes part in layout.
647 pub fn min_height(self, height: impl Into<Value<Length>>) -> Self {
648 self.layout_value(Prop::MinHeight, height, |layout, height| {
649 layout.min_height = height
650 })
651 }
652
653 /// Sets the upper bound of the width. This takes part in layout.
654 pub fn max_width(self, width: impl Into<Value<Length>>) -> Self {
655 self.layout_value(Prop::MaxWidth, width, |layout, width| {
656 layout.max_width = width
657 })
658 }
659
660 /// Sets the upper bound of the height. This takes part in layout.
661 pub fn max_height(self, height: impl Into<Value<Length>>) -> Self {
662 self.layout_value(Prop::MaxHeight, height, |layout, height| {
663 layout.max_height = height
664 })
665 }
666
667 /// Sets the same padding on all four sides. This takes part in layout.
668 pub fn padding(self, padding: impl Into<Value<Length>>) -> Self {
669 self.layout_value(Prop::Padding, padding, |layout, padding| {
670 layout.padding = Edges::all(padding);
671 })
672 }
673
674 /// Sets the horizontal padding from `width` and the vertical padding from
675 /// `height`. This takes part in layout.
676 pub fn padding_xy(self, padding: impl Into<Value<Size>>) -> Self {
677 self.layout_value(Prop::Padding, padding, |layout, padding| {
678 layout.padding = Edges::xy(Length::px(padding.width), Length::px(padding.height));
679 })
680 }
681
682 /// Sets the same margin on all four sides. This takes part in layout.
683 pub fn margin(self, margin: impl Into<Value<Length>>) -> Self {
684 self.layout_value(Prop::Margin, margin, |layout, margin| {
685 layout.margin = Edges::all(margin);
686 })
687 }
688
689 /// Sets the space between adjacent children. This takes part in layout.
690 pub fn gap(self, gap: impl Into<Value<Size>>) -> Self {
691 self.layout_value(Prop::Gap, gap, |layout, gap| layout.gap = gap)
692 }
693
694 /// Sets the main axis of the children's flex layout. This takes part in
695 /// layout.
696 pub fn direction(self, direction: impl Into<Value<Direction>>) -> Self {
697 self.layout_value(Prop::Direction, direction, |layout, direction| {
698 layout.direction = direction;
699 })
700 }
701
702 /// Lays the children out left to right. This takes part in layout.
703 pub fn row(mut self) -> Self {
704 self.replace_binding(Prop::Direction);
705 self.style.layout.direction = Direction::Row;
706 self
707 }
708
709 /// Lays the children out top to bottom. This takes part in layout.
710 pub fn column(mut self) -> Self {
711 self.replace_binding(Prop::Direction);
712 self.style.layout.direction = Direction::Column;
713 self
714 }
715
716 /// Lets children that do not fit move to a new line instead of shrinking.
717 /// This takes part in layout.
718 pub fn wrap(self, wrap: impl Into<Value<bool>>) -> Self {
719 self.layout_value(Prop::Wrap, wrap, |layout, wrap| layout.wrap = wrap)
720 }
721
722 /// Sets the share of the leftover space on the main axis this node takes.
723 /// This takes part in layout.
724 pub fn grow(self, grow: impl Into<Value<f32>>) -> Self {
725 self.layout_value(Prop::Grow, grow, |layout, grow| layout.grow = grow)
726 }
727
728 /// Sets how readily this node gives up space when its siblings overflow.
729 /// This takes part in layout.
730 pub fn shrink(self, shrink: impl Into<Value<f32>>) -> Self {
731 self.layout_value(Prop::Shrink, shrink, |layout, shrink| {
732 layout.shrink = shrink
733 })
734 }
735
736 /// Sets the main axis size the node starts from before growing or
737 /// shrinking. This takes part in layout.
738 pub fn basis(self, basis: impl Into<Value<Length>>) -> Self {
739 self.layout_value(Prop::Basis, basis, |layout, basis| layout.basis = basis)
740 }
741
742 /// Sets how the children line up across the main axis. This takes part in
743 /// layout.
744 pub fn align_items(self, align: impl Into<Value<Align>>) -> Self {
745 self.layout_value(Prop::AlignItems, align, |layout, align| {
746 layout.align_items = Some(align)
747 })
748 }
749
750 /// Sets how the children are spread along the main axis. This takes part in
751 /// layout.
752 pub fn justify_content(self, justify: impl Into<Value<Justify>>) -> Self {
753 self.layout_value(Prop::JustifyContent, justify, |layout, justify| {
754 layout.justify_content = Some(justify);
755 })
756 }
757
758 /// Takes the node out of normal flow, so its insets place it against its
759 /// containing block and its siblings are laid out as if it were not there.
760 /// This takes part in layout.
761 pub fn absolute(mut self) -> Self {
762 self.style.layout.position = Position::Absolute;
763 self
764 }
765
766 /// Sets the offsets from the containing block's edges. This takes part in
767 /// layout.
768 pub fn inset(self, inset: impl Into<Value<Edges<Length>>>) -> Self {
769 self.layout_value(Prop::Inset, inset, |layout, inset| layout.inset = inset)
770 }
771
772 /// Sets the border width, the same on all four sides. This takes part in
773 /// layout, because the border takes space away from the content. The border
774 /// is painted in the color set by [`Element::border_color`].
775 pub fn border(self, width: impl Into<Value<f32>>) -> Self {
776 self.layout_value(Prop::BorderWidth, width, |layout, width| {
777 layout.border_width = Edges::all(width);
778 })
779 }
780
781 /// Clips the children to the node's bounds. This takes part in layout,
782 /// because clipping also changes the automatic minimum size of a flex item.
783 pub fn clip(self, clip: impl Into<Value<bool>>) -> Self {
784 self.layout_value(Prop::Overflow, clip, |layout, clip| {
785 layout.overflow = if clip {
786 Overflow::Clip
787 } else {
788 Overflow::Visible
789 };
790 })
791 }
792
793 /// Sets what happens to children that do not fit inside the node. This
794 /// takes part in layout: [`Overflow::Scroll`] makes the node a scroll
795 /// container, whose automatic minimum size as a flex item is zero, and
796 /// [`Element::clip`] is the shorthand for [`Overflow::Clip`].
797 pub fn overflow(self, overflow: impl Into<Value<Overflow>>) -> Self {
798 self.layout_value(Prop::Overflow, overflow, |layout, overflow| {
799 layout.overflow = overflow;
800 })
801 }
802
803 /// Hides the node and its subtree, or shows them again. This takes part in
804 /// layout, because a hidden node is not laid out and paints nothing.
805 pub fn hidden(self, hidden: impl Into<Value<bool>>) -> Self {
806 self.layout_value(Prop::Hidden, hidden, |layout, hidden| {
807 layout.hidden = hidden
808 })
809 }
810
811 /// Translates the node's flowing children by this offset after layout,
812 /// outside the layout engine.
813 ///
814 /// This is what a scroll view changes when it scrolls: every child that
815 /// stays in the flow moves by minus the offset in the window, without
816 /// running the layout engine again. The frame damages the areas the
817 /// children moved out of and into and wraps them in an offset display
818 /// item, so a child that only moved does not paint again. An absolutely
819 /// positioned child is left where its insets place it, so chrome drawn
820 /// over the content does not scroll away with it.
821 ///
822 /// The offset can be bound to a signal, like the style builders. It is a
823 /// position, not a style: it takes part in no layout comparison, and an
824 /// equal offset changes nothing.
825 pub fn scroll_offset(mut self, offset: impl Into<Value<Point>>) -> Self {
826 self.replace_binding(Prop::ScrollOffset);
827 match offset.into() {
828 Value::Static(offset) => self.scroll_offset = offset,
829 Value::Dynamic(get) => self.bindings.push(Binding::scroll_offset(get)),
830 }
831 self
832 }
833
834 /// Keeps one flowing child at a fixed place inside the node, whatever
835 /// the heights of the children before it.
836 ///
837 /// While `pin` returns `Some((index, y))`, every layout pass ends with
838 /// the top of the flowing child at `index` sitting `y` logical pixels
839 /// below the top of the node's content box. The children keep their
840 /// order and the sizes the pass gave them, so the ones before the pinned
841 /// child stack upward from it and the ones after it stack downward,
842 /// edge to edge. A child that grows therefore pushes its neighbours away
843 /// from the pinned child in the frame it grows, and never overlaps them.
844 ///
845 /// The pin moves the children the way [`Element::scroll_offset`] does,
846 /// outside the layout engine, so do not give the node a scroll offset
847 /// too. `None`, an index past the last child, or a pinned child that is
848 /// hidden or absolutely positioned leaves the children where they are. A change of the pin alone moves them without a
849 /// layout pass.
850 ///
851 /// ```
852 /// use smudge_core::element::Element;
853 /// use smudge_core::style::px;
854 /// use smudge_core::widget::{Panel, panel};
855 ///
856 /// // The second row stays 100 pixels down, however tall the first is.
857 /// let rows = Element::new(Panel)
858 /// .column()
859 /// .pin_child(|| Some((1, 100.0)))
860 /// .child(panel().height(px(30.0)))
861 /// .child(panel().height(px(30.0)));
862 /// ```
863 pub fn pin_child(mut self, pin: impl Fn() -> Option<(usize, f32)> + 'static) -> Self {
864 self.replace_binding(Prop::Pin);
865 self.bindings.push(Binding::pin_child(Rc::new(pin)));
866 self
867 }
868
869 /// Sets the fill painted behind the node. This repaints the node without
870 /// laying it out again.
871 ///
872 /// The color can be a [`Color`] or a
873 /// [`ColorToken`]. A token resolves against the
874 /// theme that is current when the node paints.
875 pub fn background(self, color: impl Into<Value<ColorValue>>) -> Self {
876 self.paint_value(Prop::Background, color, |paint, color| {
877 paint.background = Some(color)
878 })
879 }
880
881 /// Sets the color of the border whose width [`Element::border`] sets. This
882 /// repaints the node without laying it out again.
883 ///
884 /// The color can be a [`Color`] or a
885 /// [`ColorToken`]. A token resolves against the
886 /// theme that is current when the node paints.
887 pub fn border_color(self, color: impl Into<Value<ColorValue>>) -> Self {
888 self.paint_value(Prop::BorderColor, color, |paint, color| {
889 paint.border_color = Some(color)
890 })
891 }
892
893 /// Sets the color a widget's text paints with. This repaints the node
894 /// without laying it out again.
895 ///
896 /// Widgets that show text read it; widgets without text ignore it. The
897 /// color can be a [`Color`] or a
898 /// [`ColorToken`]. A token resolves against the
899 /// theme that is current when the node paints.
900 pub fn text_color(self, color: impl Into<Value<ColorValue>>) -> Self {
901 self.paint_value(Prop::TextColor, color, |paint, color| {
902 paint.text_color = Some(color)
903 })
904 }
905
906 /// Sets the corner radius of the background and the border, the same on all
907 /// four corners. An image widget rounds the pixels it paints by the same
908 /// radius. This repaints the node without laying it out again.
909 ///
910 /// The radius can be an [`f32`] or a
911 /// [`RadiusToken`]. A token resolves against
912 /// the theme that is current when the node paints.
913 pub fn corner_radius(self, radius: impl Into<Value<RadiusValue>>) -> Self {
914 self.paint_value(Prop::Radius, radius, |paint, radius| {
915 paint.corner_radius = radius;
916 })
917 }
918
919 /// Records a patch built by `build` into one slot of the element's
920 /// variants.
921 fn variant(
922 mut self,
923 select: impl FnOnce(&mut Variants) -> &mut Option<StylePatch>,
924 build: impl FnOnce(PatchBuilder) -> PatchBuilder,
925 ) -> Self {
926 let builder = build(PatchBuilder::default());
927 let variants = self.variants.get_or_insert_with(Variants::default);
928 *select(variants) = Some(builder.patch());
929 self
930 }
931
932 /// Applies a patch while the node is in the hovered chain, which is the
933 /// node under the pointer that can respond and its ancestors.
934 ///
935 /// The closure records static values into a [`PatchBuilder`]; a reactive
936 /// value has no place in a patch, so the patch methods take plain values.
937 /// While the pointer is over the node the patch overrides the fields it
938 /// sets, and when the pointer leaves, the base style stands again. A node
939 /// with a variant is a target for hover and presses even without
940 /// handlers, so a styled card reacts to the pointer on its own.
941 ///
942 /// A change of hover re-resolves the style: a patch that touches only
943 /// paint properties repaints the node, one that touches layout
944 /// properties lays it out again. Hover also follows a layout that moves
945 /// nodes under a resting pointer, except that a layout never takes the
946 /// hover away from a node whose hover patch changes its layout: such a
947 /// patch can move the node off the pointer, and the node then stays
948 /// hovered until the pointer moves, instead of flickering.
949 ///
950 /// ```
951 /// use smudge_core::geometry::Color;
952 /// use smudge_core::widget::panel;
953 ///
954 /// let card = panel()
955 /// .background(Color::rgb(50, 50, 50))
956 /// .hover(|p| p.background(Color::rgb(70, 70, 70)));
957 /// ```
958 pub fn hover(self, patch: impl FnOnce(PatchBuilder) -> PatchBuilder) -> Self {
959 self.variant(|variants| &mut variants.hovered, patch)
960 }
961
962 /// Applies a patch while the node holds the keyboard focus and the focus
963 /// last moved by keyboard navigation.
964 ///
965 /// The focus a press moved draws no focus style, so a ring follows Tab and
966 /// not a mouse click. Everything else works as
967 /// [`hover`](Element::hover) does.
968 ///
969 /// ```
970 /// use smudge_core::geometry::Color;
971 /// use smudge_core::style::px;
972 /// use smudge_core::widget::panel;
973 ///
974 /// let field = panel()
975 /// .width(px(120.0))
976 /// .height(px(24.0))
977 /// .focusable()
978 /// .focused(|p| p.border(1.0).border_color(Color::rgb(0, 120, 215)));
979 /// ```
980 pub fn focused(self, patch: impl FnOnce(PatchBuilder) -> PatchBuilder) -> Self {
981 self.variant(|variants| &mut variants.focused, patch)
982 }
983
984 /// Applies a patch while a primary press is held on the node.
985 ///
986 /// Everything works as [`hover`](Element::hover) does; the pressed state
987 /// starts when the button goes down on the node and ends when it is
988 /// released.
989 ///
990 /// ```
991 /// use smudge_core::geometry::Color;
992 /// use smudge_core::widget::panel;
993 ///
994 /// let button = panel()
995 /// .background(Color::rgb(50, 50, 50))
996 /// .pressed(|p| p.background(Color::rgb(30, 30, 30)));
997 /// ```
998 pub fn pressed(self, patch: impl FnOnce(PatchBuilder) -> PatchBuilder) -> Self {
999 self.variant(|variants| &mut variants.pressed, patch)
1000 }
1001
1002 /// Applies a patch while the node is disabled.
1003 ///
1004 /// The patch wins over every other state, so a disabled control keeps its
1005 /// switched-off look while it is hovered or pressed. Everything else
1006 /// works as [`hover`](Element::hover) does.
1007 ///
1008 /// ```
1009 /// use smudge_core::geometry::Color;
1010 /// use smudge_core::widget::panel;
1011 ///
1012 /// let button = panel()
1013 /// .background(Color::rgb(50, 50, 50))
1014 /// .disabled_style(|p| p.background(Color::rgb(35, 35, 35)));
1015 /// ```
1016 pub fn disabled_style(self, patch: impl FnOnce(PatchBuilder) -> PatchBuilder) -> Self {
1017 self.variant(|variants| &mut variants.disabled, patch)
1018 }
1019
1020 /// Runs `handler` when the primary button is released over the node after
1021 /// a press that started on it.
1022 ///
1023 /// The press is captured: between the press and the release the node keeps
1024 /// the pointer even when it moves away, and the handler runs only when the
1025 /// release happens over the node again. A press released elsewhere runs
1026 /// nothing.
1027 ///
1028 /// ```
1029 /// use std::cell::Cell;
1030 /// use std::rc::Rc;
1031 ///
1032 /// use smudge_core::app::{App, AppConfig, RepaintMode};
1033 /// use smudge_core::geometry::{Color, Point, Size};
1034 /// use smudge_core::input::{InputEvent, PointerButton};
1035 /// use smudge_core::style::px;
1036 /// use smudge_core::widget::panel;
1037 ///
1038 /// let presses = Rc::new(Cell::new(0u32));
1039 /// let counted = Rc::clone(&presses);
1040 /// let mut app = App::new(
1041 /// AppConfig {
1042 /// viewport: Size {
1043 /// width: 100.0,
1044 /// height: 60.0,
1045 /// },
1046 /// scale: 1.0,
1047 /// clear_color: Color::WHITE,
1048 /// repaint: RepaintMode::Damage,
1049 /// },
1050 /// move || {
1051 /// panel().width(px(40.0)).height(px(20.0)).on_press(move || {
1052 /// counted.set(counted.get() + 1);
1053 /// })
1054 /// },
1055 /// );
1056 /// app.frame();
1057 ///
1058 /// let corner = Point { x: 10.0, y: 5.0 };
1059 /// app.handle_event(InputEvent::PointerMoved { position: corner });
1060 /// app.handle_event(InputEvent::PointerDown {
1061 /// position: corner,
1062 /// button: PointerButton::Primary,
1063 /// });
1064 /// app.handle_event(InputEvent::PointerUp {
1065 /// position: corner,
1066 /// button: PointerButton::Primary,
1067 /// });
1068 /// assert_eq!(presses.get(), 1);
1069 /// ```
1070 pub fn on_press(mut self, handler: impl FnMut() + 'static) -> Self {
1071 self.handlers.on_press = Some(Box::new(handler));
1072 self
1073 }
1074
1075 /// Runs `handler` when the node enters (`true`) or leaves (`false`) the
1076 /// hovered chain.
1077 ///
1078 /// The hovered chain is the nearest enabled node under the pointer that can
1079 /// respond and its enabled ancestors, so a container stays hovered while
1080 /// the pointer is over one of its children, and a disabled node never
1081 /// hears it. While a press is held, hover does not change until the
1082 /// button is released. The handler runs inside the node's owner, so it can
1083 /// write the signals the element's bindings read.
1084 pub fn on_hover(mut self, handler: impl FnMut(bool) + 'static) -> Self {
1085 self.handlers.on_hover = Some(Box::new(handler));
1086 self
1087 }
1088
1089 /// Runs `handler` when the primary pointer button is pressed on the node.
1090 ///
1091 /// The position is in logical pixels, relative to the top left of the
1092 /// node's rectangle, so it does not depend on where layout or a scroll
1093 /// view put the node. Other buttons do not run it.
1094 pub fn on_pointer_down(mut self, handler: impl FnMut(Point) + 'static) -> Self {
1095 self.handlers.on_pointer_down = Some(Box::new(handler));
1096 self
1097 }
1098
1099 /// Runs `handler` when a primary press targets a node that is neither this
1100 /// node nor one of its descendants.
1101 ///
1102 /// A press on the node itself, on a child, or on a deeper descendant does
1103 /// not run it, so a popup asks for presses outside itself and closes on
1104 /// the ones it gets. A press that reaches no node at all is outside every
1105 /// node.
1106 pub fn on_press_outside(mut self, handler: impl FnMut() + 'static) -> Self {
1107 self.handlers.on_press_outside = Some(Box::new(handler));
1108 self
1109 }
1110
1111 /// Runs `handler` when the wheel scrolls while the node is in the hovered
1112 /// chain, which is the case when the pointer is over the node or over one
1113 /// of its descendants.
1114 ///
1115 /// The delta is in logical pixels, positive for scrolling right and down.
1116 /// The handlers run from the deepest node in the hovered chain outward,
1117 /// and the first that returns [`Handled::Yes`] takes the whole event, so
1118 /// of two nested scroll views the one under the pointer scrolls and the
1119 /// one around it does not. A handler that cannot use the event, such as
1120 /// a view already scrolled to its end in the wheel's direction, returns
1121 /// [`Handled::No`] and the event bubbles to the next one. The handler
1122 /// runs inside the node's owner, so it can write the signals the
1123 /// element's bindings read.
1124 pub fn on_wheel(mut self, handler: impl FnMut(Point) -> Handled + 'static) -> Self {
1125 self.handlers.on_wheel = Some(Box::new(handler));
1126 self
1127 }
1128
1129 /// Runs `handler` when the pointer moves while a press that started on
1130 /// the node is held, which is how a drag reaches the node it started on.
1131 ///
1132 /// With no press held, the handler runs when the node is the hover target
1133 /// and the pointer moves, so a node can follow the pointer without being
1134 /// pressed. While a press is held, hover does not change, and the moves
1135 /// go to the captured node alone. The position is in logical pixels,
1136 /// relative to the top left of the node's rectangle. The handler runs
1137 /// inside the node's owner, so it can write the signals the element's
1138 /// bindings read.
1139 pub fn on_pointer_move(mut self, handler: impl FnMut(Point) + 'static) -> Self {
1140 self.handlers.on_pointer_move = Some(Box::new(handler));
1141 self
1142 }
1143
1144 /// Runs `handler` when the primary button is released.
1145 ///
1146 /// After a press on the node, the handler runs when the button comes up
1147 /// wherever that happens, which is how a drag ends even when the pointer
1148 /// is no longer over the node. With no press held, the handler runs when
1149 /// the release happens over the node as the hover target. The position is
1150 /// in logical pixels, relative to the top left of the node's rectangle.
1151 /// The handler runs inside the node's owner, so it can write the signals the
1152 /// element's bindings read.
1153 pub fn on_pointer_up(mut self, handler: impl FnMut(Point) + 'static) -> Self {
1154 self.handlers.on_pointer_up = Some(Box::new(handler));
1155 self
1156 }
1157
1158 /// Runs `handler` when the input method commits text while the node or
1159 /// one of its descendants holds the keyboard focus.
1160 ///
1161 /// A commit can arrive with no key event at all, because composing text
1162 /// with an input method ends in a commit rather than a key press, so an
1163 /// editor that takes text needs this handler. The commit goes to the
1164 /// deepest handler in the focused chain and does not bubble past it.
1165 /// While a focus trap is active and the focus is outside it, the commit
1166 /// starts at the trap instead, as a key does. The handler runs inside
1167 /// the node's owner, so it can write the signals the element's bindings
1168 /// read.
1169 pub fn on_ime_commit(mut self, handler: impl FnMut(&str) + 'static) -> Self {
1170 self.handlers.on_ime_commit = Some(Box::new(handler));
1171 self
1172 }
1173
1174 /// Runs `handler` when the input method changes its composing text while
1175 /// the node or one of its descendants holds the keyboard focus.
1176 ///
1177 /// The handler receives the composing text and the byte range of the
1178 /// cursor inside it, or `None` when the input method shows no cursor. An
1179 /// empty text means the composition was canceled or committed, and the
1180 /// preedit must go. An editor draws the composing text itself, usually
1181 /// underlined, and does not write it into its value until the commit
1182 /// arrives. Like [`Element::on_ime_commit`], the event goes to the
1183 /// deepest handler in the focused chain and does not bubble past it, and
1184 /// the handler runs inside the node's owner.
1185 pub fn on_ime_preedit(
1186 mut self,
1187 handler: impl FnMut(&str, Option<(usize, usize)>) + 'static,
1188 ) -> Self {
1189 self.handlers.on_ime_preedit = Some(Box::new(handler));
1190 self
1191 }
1192
1193 /// Disables the node, which takes it out of hit testing, or enables it
1194 /// again.
1195 ///
1196 /// A disabled node receives no pointer events of its own: its handlers
1197 /// never run, and a press over it passes to whatever enabled node is hit
1198 /// instead. Its children are still hit. This can be bound to a signal,
1199 /// like the style builders.
1200 pub fn disabled(mut self, disabled: impl Into<Value<bool>>) -> Self {
1201 self.replace_binding(Prop::Disabled);
1202 match disabled.into() {
1203 Value::Static(disabled) => self.disabled = disabled,
1204 Value::Dynamic(get) => self.bindings.push(Binding::disabled(get)),
1205 }
1206 self
1207 }
1208
1209 /// Sets the cursor the pointer shows while it is over the node, or over
1210 /// a descendant that sets none of its own.
1211 ///
1212 /// The window shows the cursor of the deepest enabled node under the
1213 /// pointer that sets one, so a nested setter wins over an ancestor's.
1214 /// Setting a cursor does not make the node a target: a press over a node
1215 /// that sets only a cursor still lands on the nearest ancestor that
1216 /// responds, and so does hover. This takes a [`Cursor`], or an
1217 /// `Option<Cursor>` where `None` leaves the choice to the ancestors, and
1218 /// it can be bound to a signal, like the style builders: a binding that
1219 /// returns `None` hands the choice back to the ancestors.
1220 ///
1221 /// [`Cursor`]: crate::window::Cursor
1222 ///
1223 /// ```
1224 /// use smudge_core::geometry::Color;
1225 /// use smudge_core::widget::panel;
1226 /// use smudge_core::window::Cursor;
1227 ///
1228 /// let link = panel()
1229 /// .width(smudge_core::style::px(80.0))
1230 /// .height(smudge_core::style::px(24.0))
1231 /// .background(Color::rgb(40, 40, 48))
1232 /// .cursor(Cursor::Pointer);
1233 /// ```
1234 pub fn cursor(mut self, cursor: impl Into<Value<Option<crate::window::Cursor>>>) -> Self {
1235 self.replace_binding(Prop::Cursor);
1236 match cursor.into() {
1237 Value::Static(cursor) => self.cursor = cursor,
1238 Value::Dynamic(get) => self.bindings.push(Binding::cursor(get)),
1239 }
1240 self
1241 }
1242
1243 /// Sets the role the accessibility projection reports for the node.
1244 ///
1245 /// A widget that reports no role of its own stays out of the projection
1246 /// and its children attach to the nearest projected ancestor, so this
1247 /// builder places a plain container on the accessibility tree. The role
1248 /// is fixed for the node's lifetime, and it overrides the role the
1249 /// widget itself reports.
1250 pub fn role(mut self, role: Role) -> Self {
1251 self.a11y_role = Some(role);
1252 self
1253 }
1254
1255 /// Sets the accessible name a screen reader announces for the node.
1256 ///
1257 /// This can be bound to a signal, like the style builders. It overrides
1258 /// the name the widget itself reports.
1259 pub fn accessible_label(mut self, label: impl Into<Value<String>>) -> Self {
1260 self.a11y_label = Some(label.into());
1261 self
1262 }
1263
1264 /// Makes the node able to take the keyboard focus, by a press on it or by
1265 /// Tab navigation.
1266 ///
1267 /// While a node holds the focus, key events reach it before they bubble to
1268 /// its ancestors, which is what [`Element::on_key`] hears.
1269 /// [`Element::on_key`] implies this, so a key handler never needs a
1270 /// separate `focusable` call.
1271 pub fn focusable(mut self) -> Self {
1272 self.focusable = true;
1273 self
1274 }
1275
1276 /// Takes the keyboard focus on the first frame the node is laid out
1277 /// after it mounts.
1278 ///
1279 /// The request resolves after the layout pass that lays the node out, so
1280 /// the focus is set before the frame paints and a node a [`show`] mounts
1281 /// takes the focus in the frame it appears in. The request runs once per
1282 /// mount: when the focus moves away later, the node does not ask again,
1283 /// and a node unmounted and mounted again by a fresh [`show`] run does.
1284 /// A node that is hidden, or sits under a hidden ancestor, keeps its
1285 /// request until it is first laid out visible, so a dialog that mounts
1286 /// hidden and shows later takes the focus in the frame it shows in. A
1287 /// request whose node is disabled when it resolves is dropped and never
1288 /// asked again. When several nodes show in one frame, the node painted
1289 /// last wins.
1290 ///
1291 /// While a focus trap is mounted, only a node inside the trap can take
1292 /// the focus this way; a request for a node outside it is dropped, like
1293 /// any other request that cannot take the focus.
1294 ///
1295 /// This makes the node focusable, like [`Element::on_key`] does.
1296 ///
1297 /// ```
1298 /// use std::cell::Cell;
1299 /// use std::rc::Rc;
1300 ///
1301 /// use smudge_core::app::{App, AppConfig, RepaintMode};
1302 /// use smudge_core::geometry::{Color, Size};
1303 /// use smudge_core::widget::panel;
1304 ///
1305 /// let focused = Rc::new(Cell::new(false));
1306 /// let count = Rc::clone(&focused);
1307 /// let mut app = App::new(
1308 /// AppConfig {
1309 /// viewport: Size {
1310 /// width: 100.0,
1311 /// height: 60.0,
1312 /// },
1313 /// scale: 1.0,
1314 /// clear_color: Color::WHITE,
1315 /// repaint: RepaintMode::Damage,
1316 /// },
1317 /// move || panel().autofocus().on_focus(move |on| count.set(on)),
1318 /// );
1319 /// app.frame();
1320 ///
1321 /// assert!(focused.get(), "the node took the focus on its first frame");
1322 /// ```
1323 pub fn autofocus(mut self) -> Self {
1324 self.focusable = true;
1325 self.autofocus = true;
1326 self
1327 }
1328
1329 /// Keeps Tab and Shift+Tab cycling among the focusable nodes inside the
1330 /// node's subtree, for as long as the node is mounted and not hidden.
1331 ///
1332 /// A node the user Tabs to inside the trap never lands outside it: the
1333 /// cycle wraps within the subtree, and a Tab pressed with nothing
1334 /// focused enters the trap at its first focusable node. While the trap
1335 /// holds no focusable node, a Tab press leaves the focus where it is. A
1336 /// pointer press that lands on no node that can respond, or on a node
1337 /// outside the trap, leaves the focus where it is, and a press on a node
1338 /// inside the trap moves the focus as usual: to that node when it can
1339 /// take the focus, and away when it cannot. This is how a modal dialog
1340 /// keeps the keyboard inside itself instead of wandering into the view
1341 /// behind it.
1342 ///
1343 /// While the trap is active, a key that would start outside it, because
1344 /// the focus is outside the trap or nothing holds the focus, starts at
1345 /// the trap node and bubbles from there to the root, so the trap's own
1346 /// key handlers hear it and a control behind it does not.
1347 ///
1348 /// When several traps are mounted, the last one wins, and a nested trap
1349 /// therefore takes over from the one around it. Removing a trap, which
1350 /// is what unmounting a [`show`] child does, restores the trap mounted
1351 /// before it, and returns the focus to the node that held it when the
1352 /// trap became active, when that node is still mounted and can take it.
1353 /// A trap becomes active when it mounts visible or shows again. A trap
1354 /// that cannot hold the focus, because it became disabled or hidden or
1355 /// sits under a hidden ancestor, yields to an earlier one while it stays
1356 /// mounted, and a trap that hides gives the focus back the way removing
1357 /// it does, when the focus was inside it or nowhere. A hidden or
1358 /// disabled state counts once a layout pass has run, which every frame
1359 /// that changes the hidden state does.
1360 pub fn trap_focus(mut self) -> Self {
1361 self.trap_focus = true;
1362 self
1363 }
1364
1365 /// Pins the node's scroll offset to the end of its flowing content while
1366 /// `signal` reads `true`.
1367 ///
1368 /// Every layout pass that runs while the signal reads `true` ends with
1369 /// the node's offset at the furthest its flowing content reaches, minus
1370 /// the height of its content box, floored at zero. The pin uses the
1371 /// rectangles that pass just computed, so a frame that grows the content
1372 /// shows the end in view in the same frame. A signal that reads `false`
1373 /// leaves the offset where it is, and a write of `true` from anywhere
1374 /// pins the node in the next frame.
1375 ///
1376 /// What turns the signal is the caller's decision, not the core's: the
1377 /// scroll view widget pairs this builder with its wheel handling, so a
1378 /// step that leaves the end releases the pin and a step back to it
1379 /// engages it again.
1380 ///
1381 /// ```
1382 /// use smudge_core::element::Element;
1383 /// use smudge_core::reactive::RwSignal;
1384 /// use smudge_core::widget::Panel;
1385 ///
1386 /// let following = RwSignal::new(true);
1387 /// let view = Element::new(Panel).follow(following);
1388 /// ```
1389 pub fn follow(mut self, signal: RwSignal<bool>) -> Self {
1390 self.follow_end = Some(signal);
1391 self
1392 }
1393
1394 /// Runs `handler` when a key event reaches the node while the node or one
1395 /// of its descendants holds the keyboard focus.
1396 ///
1397 /// The event walks up from the focused node, and the first handler that
1398 /// returns [`Handled::Yes`] stops it, so a handler that takes a key hides
1399 /// it from its ancestors. Tab moves the focus away only when no handler
1400 /// took the Tab key. This makes the node focusable, so a press on it or a
1401 /// Tab puts the focus on it.
1402 ///
1403 /// The handler runs inside the node's owner, so it can write the signals
1404 /// the element's bindings read.
1405 ///
1406 /// ```
1407 /// use std::cell::Cell;
1408 /// use std::rc::Rc;
1409 ///
1410 /// use smudge_core::app::{App, AppConfig, RepaintMode};
1411 /// use smudge_core::geometry::{Color, Size};
1412 /// use smudge_core::input::{Handled, InputEvent, Key, KeyEvent};
1413 /// use smudge_core::style::px;
1414 /// use smudge_core::widget::panel;
1415 ///
1416 /// let enters = Rc::new(Cell::new(0u32));
1417 /// let counted = Rc::clone(&enters);
1418 /// let mut app = App::new(
1419 /// AppConfig {
1420 /// viewport: Size {
1421 /// width: 100.0,
1422 /// height: 60.0,
1423 /// },
1424 /// scale: 1.0,
1425 /// clear_color: Color::WHITE,
1426 /// repaint: RepaintMode::Damage,
1427 /// },
1428 /// move || {
1429 /// panel().child(
1430 /// panel()
1431 /// .width(px(40.0))
1432 /// .height(px(20.0))
1433 /// .on_key(move |event: &KeyEvent| {
1434 /// if event.pressed && event.key == Key::Enter {
1435 /// counted.set(counted.get() + 1);
1436 /// Handled::Yes
1437 /// } else {
1438 /// Handled::No
1439 /// }
1440 /// }),
1441 /// )
1442 /// },
1443 /// );
1444 /// app.frame();
1445 ///
1446 /// // Tab moves the focus to the panel, which then hears the Enter key.
1447 /// app.handle_event(InputEvent::Key(KeyEvent {
1448 /// key: Key::Tab,
1449 /// pressed: true,
1450 /// repeat: false,
1451 /// modifiers: Default::default(),
1452 /// text: None,
1453 /// }));
1454 /// app.handle_event(InputEvent::Key(KeyEvent {
1455 /// key: Key::Enter,
1456 /// pressed: true,
1457 /// repeat: false,
1458 /// modifiers: Default::default(),
1459 /// text: None,
1460 /// }));
1461 /// assert_eq!(enters.get(), 1);
1462 /// ```
1463 pub fn on_key(mut self, handler: impl FnMut(&KeyEvent) -> Handled + 'static) -> Self {
1464 self.handlers.on_key = Some(Box::new(handler));
1465 self.focusable = true;
1466 self
1467 }
1468
1469 /// Runs the handler set with [`Element::on_press`] when the node holds
1470 /// the keyboard focus and a fresh press of Enter or Space arrives.
1471 ///
1472 /// This is how a button fires from the keyboard with the same handler its
1473 /// pointer press uses. The activation runs before any
1474 /// [`Element::on_key`] handler on the same node, and a key repeat fires
1475 /// nothing. A press handler that ran takes the key, so it does not reach
1476 /// an ancestor; without a press handler the key bubbles as usual.
1477 ///
1478 /// This makes the node focusable, like [`Element::on_key`] does.
1479 pub fn activate_on_keys(mut self) -> Self {
1480 self.handlers.activate_on_keys = true;
1481 self.focusable = true;
1482 self
1483 }
1484
1485 /// Runs `handler` when the node gains (`true`) or loses (`false`) the
1486 /// keyboard focus.
1487 ///
1488 /// The handler does not make the node focusable; pair it with
1489 /// [`Element::focusable`] for that. It runs inside the node's owner, so it
1490 /// can write the signals the element's bindings read. A node that is
1491 /// removed while focused clears the focus without hearing `on_focus` again,
1492 /// because its handlers are being torn down with it.
1493 pub fn on_focus(mut self, handler: impl FnMut(bool) + 'static) -> Self {
1494 self.handlers.on_focus = Some(Box::new(handler));
1495 self
1496 }
1497
1498 /// Binds one property of the element's own widget.
1499 ///
1500 /// The first argument names the property: pass a value of a marker type, usually a
1501 /// unit struct, that stands for it and for nothing else. The last call
1502 /// for a key wins, fixed or bound, the way it does for the style
1503 /// builders: a later call drops the binding an earlier call with a key
1504 /// of the same type recorded. Bindings with different key types all
1505 /// stay, and they apply in the order they were made.
1506 ///
1507 /// `apply` writes the value into the widget and returns how much of the
1508 /// node the write invalidated. A write that changes what the widget
1509 /// measures returns [`Dirty::Layout`], and the node is laid out and painted
1510 /// again. Any other write returns [`Dirty::Paint`] and only repaints the
1511 /// node.
1512 ///
1513 /// ```
1514 /// use smudge_core::element::Element;
1515 /// use smudge_core::paint::PaintOp;
1516 /// use smudge_core::tree::Dirty;
1517 /// use smudge_core::widget::{PaintCx, Widget};
1518 ///
1519 /// struct Dot(f32);
1520 ///
1521 /// impl Widget for Dot {
1522 /// fn paint(&self, _cx: &PaintCx<'_>, _out: &mut Vec<PaintOp>) {}
1523 /// }
1524 ///
1525 /// /// The key of the dot's radius.
1526 /// struct Radius;
1527 ///
1528 /// let element = Element::new(Dot(0.0)).bind(Radius, 3.0, |dot, value| {
1529 /// dot.0 = value;
1530 /// Dirty::Paint
1531 /// });
1532 /// ```
1533 pub fn bind<K: 'static, T: 'static>(
1534 mut self,
1535 _key: K,
1536 value: impl Into<Value<T>>,
1537 apply: fn(&mut W, T) -> Dirty,
1538 ) -> Self {
1539 let key = Key::Widget(TypeId::of::<K>());
1540 self.drop_bindings(key);
1541 match value.into() {
1542 Value::Static(value) => {
1543 apply(&mut self.widget, value);
1544 }
1545 Value::Dynamic(get) => self.bindings.push(Binding::widget(key, get, apply)),
1546 }
1547 self
1548 }
1549}
1550
1551/// An element with its widget type erased, so that a parent can hold children
1552/// of different widget types.
1553///
1554/// Build one with [`IntoElement::into_any`]. Its contents are private: only
1555/// mounting reads them.
1556pub struct AnyElement {
1557 /// The node's widget.
1558 widget: Box<dyn Widget>,
1559 /// The style the node starts with.
1560 style: Style,
1561 /// Bindings installed when the node mounts.
1562 bindings: Vec<Binding>,
1563 /// The style patches per interaction state, when a variant builder ran.
1564 variants: Option<Variants>,
1565 /// The input handlers the node will own.
1566 handlers: Handlers,
1567 /// Whether the node starts disabled.
1568 disabled: bool,
1569 /// The cursor the node sets for the pointer over it, when its element
1570 /// set a static one.
1571 cursor: Option<crate::window::Cursor>,
1572 /// Whether the node can take the keyboard focus.
1573 focusable: bool,
1574 /// Whether the node requests the focus on the first frame after it is
1575 /// laid out.
1576 autofocus: bool,
1577 /// Whether the node keeps Tab movement inside its subtree while it is
1578 /// mounted.
1579 trap_focus: bool,
1580 /// The signal that pins the node's scroll offset to the end of its
1581 /// content while it reads `true`.
1582 follow_end: Option<RwSignal<bool>>,
1583 /// The role the accessibility projection reports, when an element set
1584 /// one.
1585 a11y_role: Option<Role>,
1586 /// The accessible label an element set, when it set one.
1587 a11y_label: Option<Value<String>>,
1588 /// The scroll offset written to the node when it mounts.
1589 scroll_offset: Point,
1590 /// The children, in paint order.
1591 children: Vec<AnyElement>,
1592}
1593
1594impl AnyElement {
1595 /// Erases the widget type of `element`.
1596 fn new<W: Widget>(element: Element<W>) -> AnyElement {
1597 AnyElement {
1598 widget: Box::new(element.widget),
1599 style: element.style,
1600 bindings: element.bindings,
1601 variants: element.variants,
1602 handlers: element.handlers,
1603 disabled: element.disabled,
1604 cursor: element.cursor,
1605 focusable: element.focusable,
1606 autofocus: element.autofocus,
1607 trap_focus: element.trap_focus,
1608 follow_end: element.follow_end,
1609 a11y_role: element.a11y_role,
1610 a11y_label: element.a11y_label,
1611 scroll_offset: element.scroll_offset,
1612 children: element.children,
1613 }
1614 }
1615}
1616
1617/// Converts an element into the erased form a parent stores.
1618pub trait IntoElement {
1619 /// Erases the widget type of this element.
1620 fn into_any(self) -> AnyElement;
1621}
1622
1623impl<W: Widget> IntoElement for Element<W> {
1624 fn into_any(self) -> AnyElement {
1625 AnyElement::new(self)
1626 }
1627}
1628
1629impl IntoElement for AnyElement {
1630 fn into_any(self) -> AnyElement {
1631 self
1632 }
1633}
1634
1635/// Mounts `element` as a child of `parent`, or as the root when `parent` is
1636/// `None`, and returns the id of the node it created.
1637///
1638/// The node starts at [`Dirty::Layout`]. Bindings are installed inside the
1639/// node's owner and run once immediately, so the updates they queue are applied
1640/// before the first layout.
1641pub(crate) fn mount(
1642 element: AnyElement,
1643 tree: &mut WidgetTree,
1644 queue: &UpdateQueue,
1645 parent: Option<WidgetId>,
1646) -> WidgetId {
1647 let AnyElement {
1648 widget,
1649 style,
1650 mut bindings,
1651 variants,
1652 handlers,
1653 disabled,
1654 cursor,
1655 focusable,
1656 autofocus,
1657 trap_focus,
1658 follow_end,
1659 a11y_role,
1660 a11y_label,
1661 scroll_offset,
1662 children,
1663 } = element;
1664 let id = tree.insert(parent, widget, style);
1665 tree.set_variants(id, variants.map(Rc::new));
1666 tree.set_disabled(id, disabled);
1667 tree.set_cursor(id, cursor);
1668 tree.set_focusable(id, focusable);
1669 if autofocus {
1670 tree.queue_autofocus(id);
1671 }
1672 if trap_focus {
1673 tree.push_focus_trap(id);
1674 }
1675 if let Some(signal) = follow_end {
1676 tree.set_follow(id, signal);
1677 }
1678 tree.set_a11y_role(id, a11y_role);
1679 tree.set_handlers(id, handlers);
1680 let bindings = match a11y_label {
1681 Some(Value::Static(text)) => {
1682 tree.set_a11y_label(id, Some(text));
1683 bindings
1684 }
1685 Some(Value::Dynamic(get)) => {
1686 bindings.push(Binding::a11y_label(get));
1687 bindings
1688 }
1689 None => bindings,
1690 };
1691 if scroll_offset != Point::default() {
1692 tree.set_scroll_offset(id, scroll_offset);
1693 }
1694 let owner = tree.owner(id).expect("the node was just inserted").clone();
1695 for binding in bindings {
1696 owner.with(|| (binding.install)(id, queue));
1697 }
1698 for child in children {
1699 mount(child, tree, queue, Some(id));
1700 }
1701 id
1702}
1703
1704/// A node that paints nothing and leaves its children to a binding.
1705///
1706/// [`show`] mounts its child under a slot, and [`for_each`] keeps one child
1707/// per key of a list under one.
1708pub struct Slot;
1709
1710impl Widget for Slot {
1711 fn paint(&self, _cx: &PaintCx<'_>, _out: &mut Vec<PaintOp>) {}
1712}
1713
1714/// Mounts the element that `build` returns as a child of `slot`, inside an
1715/// owner of its own.
1716///
1717/// The owner keeps the values the build creates alive for as long as the
1718/// child stays mounted, and [`unmount_child`] disposes them. Returns `None`
1719/// when `slot` is a stale id, which happens when the slot was removed while
1720/// an update that mounts into it was still queued.
1721fn mount_under_slot(
1722 slot: WidgetId,
1723 tree: &mut WidgetTree,
1724 queue: &UpdateQueue,
1725 build: impl FnOnce() -> AnyElement,
1726) -> Option<(WidgetId, Owner)> {
1727 let owner = tree.owner(slot)?.child();
1728 let element = owner.with(build);
1729 let child = mount(element, tree, queue, Some(slot));
1730 Some((child, owner))
1731}
1732
1733/// Removes a child that [`mount_under_slot`] mounted and disposes the owner
1734/// its build ran in.
1735///
1736/// The tree removal runs first, so the child's bindings stop before the
1737/// values the build created are disposed.
1738fn unmount_child(child: WidgetId, owner: &Owner, tree: &mut WidgetTree) {
1739 tree.remove(child);
1740 owner.cleanup();
1741}
1742
1743/// Mounts the element `build` returns while `when` is true, and removes it
1744/// while `when` is false.
1745///
1746/// Only `when` runs inside a binding, so only the signals it reads make the
1747/// slot check again. `build` runs untracked, once each time the child mounts,
1748/// under an owner of its own. Signals that `build` creates therefore live
1749/// exactly as long as the child: they survive every check that leaves the
1750/// child mounted, and they are disposed when the child is removed, after the
1751/// child's bindings have stopped.
1752///
1753/// ```
1754/// use smudge_core::element::show;
1755/// use smudge_core::reactive::{self, prelude::*};
1756/// use smudge_core::widget::panel;
1757///
1758/// let (visible, _set_visible) = reactive::signal_local(true);
1759/// let root = show(move || visible.get(), panel);
1760/// ```
1761pub fn show<E: IntoElement>(
1762 when: impl Fn() -> bool + 'static,
1763 build: impl Fn() -> E + 'static,
1764) -> Element<Slot> {
1765 let build: Rc<dyn Fn() -> AnyElement> = Rc::new(move || build().into_any());
1766 let mut element = Element::new(Slot);
1767 element.bindings.push(Binding {
1768 key: None,
1769 install: Box::new(move |id, queue| {
1770 let queue = queue.clone();
1771 let mounted: Rc<RefCell<Option<(WidgetId, Owner)>>> = Rc::new(RefCell::new(None));
1772 RenderEffect::new(move || {
1773 let visible = when();
1774 let child_queue = queue.clone();
1775 let mounted = Rc::clone(&mounted);
1776 let build = Rc::clone(&build);
1777 queue.push(Box::new(move |tree| {
1778 let mut mounted = mounted.borrow_mut();
1779 match (visible, mounted.take()) {
1780 (true, None) => {
1781 if let Some(child) =
1782 mount_under_slot(id, tree, &child_queue, || build())
1783 {
1784 *mounted = Some(child);
1785 }
1786 }
1787 (false, Some((child, owner))) => unmount_child(child, &owner, tree),
1788 (_, unchanged) => *mounted = unchanged,
1789 }
1790 }));
1791 })
1792 }),
1793 });
1794 element
1795}
1796
1797/// Mounts one child per key of the list that `items` returns, keeping the
1798/// children that survive a change mounted and in place.
1799///
1800/// The returned element is a slot whose children follow the list. A key the
1801/// list gains mounts a new child, a key it loses removes that child's whole
1802/// tree, and a key that moves keeps its child, which is reordered rather than
1803/// rebuilt. A child keeps its reactive state across a reorder, and the frame
1804/// repaints only what changed position or stacking.
1805///
1806/// A row is built once per key, by `row`, the first time the key appears in
1807/// the list. Content that changes while a row stays mounted must come from
1808/// the signals that row reads: a new value for a key the list already had
1809/// reaches no mounted child.
1810///
1811/// The keys of a list must be unique. In a debug build a duplicate key trips
1812/// a debug assertion. In a release build the last duplicate wins: the row
1813/// mounted for that key takes the position of its last occurrence, and the
1814/// earlier occurrences contribute nothing.
1815///
1816/// `items` runs inside a binding, so only the signals it reads make the slot
1817/// diff the list again. `key` should depend on its item alone, because a key
1818/// that changes makes the slot remove one child and mount another. `row` runs
1819/// once per new key inside an owner of its own, so the signals a row creates
1820/// live exactly as long as the row.
1821///
1822/// The slot manages its own children; do not add children to the returned
1823/// element beside it.
1824///
1825/// ```
1826/// use smudge_core::element::for_each;
1827/// use smudge_core::reactive::{self, prelude::*};
1828/// use smudge_core::style::px;
1829/// use smudge_core::widget::panel;
1830///
1831/// let (items, _set_items) = reactive::signal_local(vec![1u32, 2, 3]);
1832/// let list = for_each(
1833/// move || items.get(),
1834/// |item| *item,
1835/// |item| panel().width(px(40.0)).height(px(10.0)),
1836/// );
1837/// ```
1838pub fn for_each<T, K, E>(
1839 items: impl Fn() -> Vec<T> + 'static,
1840 key: impl Fn(&T) -> K + 'static,
1841 row: impl Fn(T) -> E + 'static,
1842) -> Element<Slot>
1843where
1844 T: 'static,
1845 K: Eq + Hash + Clone + 'static,
1846 E: IntoElement,
1847{
1848 let items = Rc::new(items);
1849 let key = Rc::new(key);
1850 let row: Rc<dyn Fn(T) -> AnyElement> = Rc::new(move |item| row(item).into_any());
1851 let mut element = Element::new(Slot);
1852 element.bindings.push(Binding {
1853 key: None,
1854 install: Box::new(move |slot, queue| {
1855 let queue = queue.clone();
1856 let mounted: Rc<RefCell<HashMap<K, (WidgetId, Owner)>>> =
1857 Rc::new(RefCell::new(HashMap::new()));
1858 let order: Rc<RefCell<Vec<K>>> = Rc::new(RefCell::new(Vec::new()));
1859 RenderEffect::new(move || {
1860 let items = items();
1861 let keys: Vec<K> = items.iter().map(|item| key(item)).collect();
1862 // The last occurrence of a key decides where its row goes, so
1863 // a duplicate keeps one row and the earlier ones are skipped.
1864 let mut last: HashMap<K, usize> = HashMap::with_capacity(keys.len());
1865 for (at, item_key) in keys.iter().enumerate() {
1866 last.insert(item_key.clone(), at);
1867 }
1868 debug_assert!(
1869 last.len() == keys.len(),
1870 "for_each found a duplicate key, but the keys of a list must be unique"
1871 );
1872 let child_queue = queue.clone();
1873 let mounted = Rc::clone(&mounted);
1874 let order = Rc::clone(&order);
1875 let row = Rc::clone(&row);
1876 queue.push(Box::new(move |tree| {
1877 let mut mounted = mounted.borrow_mut();
1878 let mut order = order.borrow_mut();
1879
1880 mounted.retain(|key, (child, owner)| {
1881 let kept = last.contains_key(key);
1882 if !kept {
1883 unmount_child(*child, owner, tree);
1884 }
1885 kept
1886 });
1887
1888 let mut new_order = Vec::with_capacity(last.len());
1889 let mut children = Vec::with_capacity(last.len());
1890 for (at, (item, item_key)) in items.into_iter().zip(keys).enumerate() {
1891 if last[&item_key] != at {
1892 continue;
1893 }
1894 let child = match mounted.get(&item_key) {
1895 Some((child, _)) => *child,
1896 None => {
1897 let row = Rc::clone(&row);
1898 let Some((child, owner)) =
1899 mount_under_slot(slot, tree, &child_queue, move || row(item))
1900 else {
1901 continue;
1902 };
1903 mounted.insert(item_key.clone(), (child, owner));
1904 child
1905 }
1906 };
1907 children.push(child);
1908 new_order.push(item_key);
1909 }
1910
1911 if *order != new_order {
1912 tree.set_children(slot, children);
1913 *order = new_order;
1914 }
1915 }));
1916 })
1917 }),
1918 });
1919 element
1920}
1921
1922#[cfg(test)]
1923#[path = "element_tests.rs"]
1924mod tests;