Skip to main content

compose_rust/ui/
mod.rs

1//! The widget vocabulary as composables: the same widgets, the same properties and the
2//! same Modifier slots the `rsx!` layer writes, with Compose's names and Compose's shape.
3//!
4//! ```ignore
5//! use compose_rust::runtime::*;
6//! use compose_rust::ui::*;
7//!
8//! #[composable]
9//! fn Greeting(name: String) {
10//!     Column().modifier(Modifier.padding(16.0).fill_max_width()).content(|| {
11//!         Text(format!("Hello, {name}")).type_role(TypeRole::Title);
12//!         Button("Wave").on_click(|| show_message("Hello back"));
13//!     });
14//! }
15//! ```
16//!
17//! **A widget is written as a statement.** `Text("Hello")` creates a description and the
18//! statement's end hands it to the composition; the methods in between set what Compose
19//! would take as named arguments. A container's children are the closure given to
20//! `content`, which runs at once. Binding a widget to a variable delays it to the end of
21//! the variable's scope, so a widget is not bound.
22//!
23//! **Nothing here defines a widget.** Each composable writes exactly the attributes its
24//! `rsx!` counterpart writes, through the same translation into wire records, so a screen
25//! written either way is the same tree of the same nodes for the Renderer. A Modifier the
26//! `rsx!` counterpart does not accept is still written when it is set, because the wire
27//! carries every Modifier slot on every node.
28
29mod lazy;
30mod widgets;
31
32pub use crate::boundary::LaunchBuilder;
33pub use crate::brush::{Brush, Stop, brush};
34pub use crate::drawing::{DrawCommand, DrawList, DrawListBuilder};
35pub use crate::message::{Message, show_message};
36pub use crate::runtime::{application, launch};
37pub use crate::schema::{
38    Alignment, Arrangement, ButtonKind, ButtonVariant, Color, ColorRole, ColorScheme, DesignSystem,
39    IconRole, Key, MaterialRole, MessageDuration, MotionRole, Paint, ShapeRole, SpaceRole,
40    TextAlign, TextOverflow, Theme, TypeRole, WindowHeightClass, WindowSizeClass,
41};
42pub use crate::spans::TextSpans;
43pub use crate::theme::{ThemeHandle, current_theme};
44
45/// The application's theme, to read and to change while it runs. A change is one
46/// `SetTheme` record in the batch the call that made it produces.
47pub fn theme() -> ThemeHandle {
48    ThemeHandle::default()
49}
50pub use crate::window::{NodeSize, WindowSafeArea, WindowSize};
51pub use crate::{
52    Cursor, FileDrop, KeyEvent, PointerEvent, PointerKind, PointerKinds, RangeRequest, UserSelect,
53    asset, pointer_button,
54};
55pub use lazy::{LazyColumn, LazyGrid, LazyList, LazyListScope, LazyRow};
56pub use widgets::*;
57
58use crate::runtime::composer::{EventCallback, OwnedAttr};
59use crate::runtime::{group_guard, with_composer, with_composer_if_idle};
60use crate::schema::WidgetKind;
61
62/// The most shadow layers one node can carry through this API.
63pub const MAX_SHADOWS: usize = 4;
64
65/// One layer of a drop shadow, as CSS `box-shadow` writes it: an offset, a blur, a spread
66/// and a colour with its alpha. It follows the corners of the node that casts it.
67#[derive(Clone, Copy, Debug, PartialEq)]
68pub struct Shadow {
69    pub x: f32,
70    pub y: f32,
71    pub blur: f32,
72    pub spread: f32,
73    pub paint: Paint,
74}
75
76impl Shadow {
77    pub const fn new(x: f32, y: f32, blur: f32, spread: f32, paint: Paint) -> Self {
78        Self {
79            x,
80            y,
81            blur,
82            spread,
83            paint,
84        }
85    }
86}
87
88/// What sits around a widget: its size, its surface, its outline, how far its content
89/// is inset, and what the Renderer reports about it. Compose's `Modifier`.
90///
91/// Written the way Compose writes it, from the empty value `Modifier`:
92///
93/// ```ignore
94/// Card().modifier(Modifier.fill_max_width().padding(16.0).background(Paint::Role(ColorRole::Surface)))
95/// ```
96///
97/// The order of the calls does not matter. Each kind of Modifier has a fixed slot in the
98/// node's chain, outside in: size, shape, fill, outline, lift, press, inset. That is what
99/// the `rsx!` attributes do too, so the same Modifier draws the same either way.
100#[derive(Clone, Copy, Debug, Default, PartialEq)]
101pub struct Modifier {
102    observe_size: Option<i64>,
103    motion: Option<MotionRole>,
104    material: Option<MaterialRole>,
105    weight: Option<f32>,
106    width: Option<f32>,
107    height: Option<f32>,
108    padding: Option<f32>,
109    padding_role: Option<SpaceRole>,
110    background: Option<Paint>,
111    shape_role: Option<ShapeRole>,
112    corner_radius: Option<f32>,
113    corner_radii: Option<[f32; 4]>,
114    border_width: Option<f32>,
115    border_color: Option<Paint>,
116    elevation: Option<f32>,
117    alpha: Option<f32>,
118    press_feedback: Option<(f32, f32)>,
119    transform: Option<[f32; 8]>,
120    animated_alpha: Option<crate::Animated<f32>>,
121    animated_background: Option<crate::Animated<Paint>>,
122    animated_offset: Option<crate::Animated<(f32, f32)>>,
123    cursor: Option<crate::Cursor>,
124    user_select: Option<crate::UserSelect>,
125    clip: bool,
126    window_drag: Option<bool>,
127    offset: Option<(f32, f32)>,
128    shadows: [Option<Shadow>; MAX_SHADOWS],
129    fill_max_width: bool,
130    match_parent_size: bool,
131    fill_max_height: bool,
132}
133
134/// The empty Modifier, to start a chain from: `Modifier.padding(8.0)`.
135#[allow(non_upper_case_globals)]
136pub const Modifier: Modifier = Modifier::new();
137
138impl Modifier {
139    /// The empty Modifier. The same as the value `Modifier`.
140    pub const fn new() -> Self {
141        Self {
142            observe_size: None,
143            motion: None,
144            material: None,
145            weight: None,
146            width: None,
147            height: None,
148            padding: None,
149            padding_role: None,
150            background: None,
151            shape_role: None,
152            corner_radius: None,
153            corner_radii: None,
154            border_width: None,
155            border_color: None,
156            elevation: None,
157            alpha: None,
158            press_feedback: None,
159            transform: None,
160            animated_alpha: None,
161            animated_background: None,
162            animated_offset: None,
163            cursor: None,
164            user_select: None,
165            clip: false,
166            window_drag: None,
167            offset: None,
168            shadows: [None; MAX_SHADOWS],
169            fill_max_width: false,
170            match_parent_size: false,
171            fill_max_height: false,
172        }
173    }
174
175    /// A share of the space left in a `Row` or `Column`.
176    pub const fn weight(mut self, weight: f32) -> Self {
177        self.weight = Some(weight);
178        self
179    }
180
181    pub const fn width(mut self, dp: f32) -> Self {
182        self.width = Some(dp);
183        self
184    }
185
186    pub const fn height(mut self, dp: f32) -> Self {
187        self.height = Some(dp);
188        self
189    }
190
191    /// Both at once.
192    pub const fn size(self, width: f32, height: f32) -> Self {
193        self.width(width).height(height)
194    }
195
196    /// A width that may be unset, for a screen whose measure depends on the window.
197    pub const fn width_if(mut self, dp: Option<f32>) -> Self {
198        self.width = dp;
199        self
200    }
201
202    /// Matches the bounds of a Box parent without affecting its size.
203    pub const fn match_parent_size(mut self) -> Self {
204        self.match_parent_size = true;
205        self
206    }
207
208    pub const fn fill_max_width(mut self) -> Self {
209        self.fill_max_width = true;
210        self
211    }
212
213    pub const fn fill_max_height(mut self) -> Self {
214        self.fill_max_height = true;
215        self
216    }
217
218    pub const fn fill_max_size(self) -> Self {
219        self.fill_max_width().fill_max_height()
220    }
221
222    /// Fills the width only when `fill` is true.
223    pub const fn fill_max_width_if(mut self, fill: bool) -> Self {
224        self.fill_max_width = fill;
225        self
226    }
227
228    /// The same inset on every side, in dp.
229    pub const fn padding(mut self, dp: f32) -> Self {
230        self.padding = Some(dp);
231        self
232    }
233
234    /// An inset from the design system's spacing ladder. Wins over `padding`.
235    pub const fn padding_role(mut self, role: SpaceRole) -> Self {
236        self.padding_role = Some(role);
237        self
238    }
239
240    /// The same, when the inset may be unset.
241    pub const fn padding_role_if(mut self, role: Option<SpaceRole>) -> Self {
242        self.padding_role = role;
243        self
244    }
245
246    pub const fn background(mut self, paint: Paint) -> Self {
247        self.background = Some(paint);
248        self
249    }
250
251    /// The same, when there may be no background.
252    pub const fn background_if(mut self, paint: Option<Paint>) -> Self {
253        self.background = paint;
254        self
255    }
256
257    /// A shape from the design system's ladder.
258    pub const fn shape_role(mut self, role: ShapeRole) -> Self {
259        self.shape_role = Some(role);
260        self
261    }
262
263    /// A literal corner, in dp. Wins over `shape_role`.
264    pub const fn corner_radius(mut self, dp: f32) -> Self {
265        self.corner_radius = Some(dp);
266        self.corner_radii = None;
267        self
268    }
269
270    /// Individual corner radii `[top_start, top_end, bottom_end, bottom_start]`, in dp.
271    /// Wins over `shape_role` and `corner_radius`.
272    pub const fn corner_radii(mut self, radii: [f32; 4]) -> Self {
273        self.corner_radii = Some(radii);
274        self.corner_radius = None;
275        self
276    }
277
278    /// Clips the node and its children to its shape, or to its rectangle when it has none.
279    pub const fn clip(mut self, clip: bool) -> Self {
280        self.clip = clip;
281        self
282    }
283
284    /// Includes or excludes this node from window dragging.
285    pub const fn window_drag(mut self, enabled: bool) -> Self {
286        self.window_drag = Some(enabled);
287        self
288    }
289
290    pub const fn border(mut self, width: f32, paint: Paint) -> Self {
291        self.border_width = Some(width);
292        self.border_color = Some(paint);
293        self
294    }
295
296    /// A resting height above the default, in dp. How it is drawn is the design system's.
297    pub const fn elevation(mut self, dp: f32) -> Self {
298        self.elevation = Some(dp);
299        self
300    }
301
302    /// Asks the Renderer to report this node's size under the token from
303    /// [`crate::runtime::remember_node_size`].
304    pub const fn observe_size(mut self, token: i64) -> Self {
305        self.observe_size = Some(token);
306        self
307    }
308
309    /// How important this node's changes are. The curve is the design system's.
310    pub const fn motion(mut self, role: MotionRole) -> Self {
311        self.motion = Some(role);
312        self
313    }
314
315    /// What this node's surface is made of. The effect is the design system's.
316    pub const fn material(mut self, role: MaterialRole) -> Self {
317        self.material = Some(role);
318        self
319    }
320
321    /// Moves the node from where it was laid out, in dp, without moving anything else.
322    pub const fn offset(mut self, x: f32, y: f32) -> Self {
323        self.offset = Some((x, y));
324        self
325    }
326
327    /// Adds a shadow layer. Layers are drawn in the order they are added, the first at
328    /// the bottom. A node takes at most [`MAX_SHADOWS`]; a layer past that is ignored.
329    pub const fn shadow(mut self, shadow: Shadow) -> Self {
330        let mut index = 0;
331        while index < MAX_SHADOWS {
332            if self.shadows[index].is_none() {
333                self.shadows[index] = Some(shadow);
334                break;
335            }
336            index += 1;
337        }
338        self
339    }
340
341    /// Adds several shadow layers, in order.
342    pub fn shadows(mut self, shadows: impl IntoIterator<Item = Shadow>) -> Self {
343        for shadow in shadows {
344            self = self.shadow(shadow);
345        }
346        self
347    }
348
349    /// An opacity for the node and everything in it, faded as one group.
350    pub const fn alpha(mut self, alpha: f32) -> Self {
351        self.alpha = Some(alpha);
352        self
353    }
354
355    /// Sets the pressed scale and tint opacity.
356    pub const fn press_feedback(mut self, scale: f32, tint_alpha: f32) -> Self {
357        self.press_feedback = Some((scale, tint_alpha));
358        self
359    }
360
361    /// Compose's `graphicsLayer`: a rotation, a scale and a translation about an origin,
362    /// drawn without moving the layout, and an opacity for the whole group. A pointer is
363    /// tested against the shape that is drawn.
364    pub fn graphics_layer(mut self, layer: crate::GraphicsLayer) -> Self {
365        if let crate::schema::Modifier::Transform {
366            a,
367            b,
368            c,
369            d,
370            e,
371            f,
372            origin_x,
373            origin_y,
374        } = layer.transform()
375        {
376            self.transform = Some([a, b, c, d, e, f, origin_x, origin_y]);
377        }
378        self.alpha = Some(layer.alpha);
379        self
380    }
381
382    /// An opacity the Renderer animates to, from [`crate::animate_float_as_state`]. It
383    /// takes the place of `alpha`.
384    pub const fn animated_alpha(mut self, value: crate::Animated<f32>) -> Self {
385        self.animated_alpha = Some(value);
386        self
387    }
388
389    /// A background the Renderer animates to, from [`crate::animate_color_as_state`]. It
390    /// takes the place of `background`.
391    pub const fn animated_background(mut self, value: crate::Animated<Paint>) -> Self {
392        self.animated_background = Some(value);
393        self
394    }
395
396    /// A translation the Renderer animates to, from [`crate::animate_offset_as_state`].
397    /// It takes the place of the transform a `graphics_layer` would draw.
398    pub const fn animated_offset(mut self, value: crate::Animated<(f32, f32)>) -> Self {
399        self.animated_offset = Some(value);
400        self
401    }
402
403    /// The cursor the platform shows over the node. CSS `cursor`: the deepest node under
404    /// the pointer that names one other than `Cursor::Auto` wins, and while a node holds a
405    /// pointer capture its cursor stays.
406    pub const fn cursor(mut self, cursor: crate::Cursor) -> Self {
407        self.cursor = Some(cursor);
408        self
409    }
410
411    /// Whether the text under the node can be selected. CSS `user-select`; `Auto` follows
412    /// the parent.
413    pub const fn user_select(mut self, user_select: crate::UserSelect) -> Self {
414        self.user_select = Some(user_select);
415        self
416    }
417}
418
419/// Which Modifier attributes a widget's `rsx!` counterpart always writes. The others are
420/// written only when they are set.
421#[derive(Clone, Copy, PartialEq, Eq)]
422pub(crate) enum Mods {
423    /// The containers that take every Modifier: Column, Row, Box, Card, Surface.
424    Container,
425    /// The thirteen every other widget takes.
426    Standard,
427    /// A chip's six.
428    Chip,
429    /// A Scaffold's background and material.
430    Scaffold,
431    /// None.
432    None,
433}
434
435pub(crate) type Attrs = Vec<(&'static str, OwnedAttr)>;
436
437fn float(value: Option<f32>) -> OwnedAttr {
438    value.map_or(OwnedAttr::None, |value| OwnedAttr::Float(f64::from(value)))
439}
440
441fn role(value: Option<impl Into<u16>>) -> OwnedAttr {
442    value.map_or(OwnedAttr::None, |value| {
443        OwnedAttr::Int(i64::from(value.into()))
444    })
445}
446
447fn paint(value: Option<Paint>) -> OwnedAttr {
448    value.map_or(OwnedAttr::None, |paint| {
449        OwnedAttr::Int(paint.to_bits() as i64)
450    })
451}
452
453/// A role that was not set is tag 0, which means "not sent".
454pub(crate) fn role_or_zero(value: Option<impl Into<u16>>) -> OwnedAttr {
455    OwnedAttr::Int(value.map_or(0, |value| i64::from(value.into())))
456}
457
458/// A dp value that was not set is 0.
459pub(crate) fn dp_or_zero(value: Option<f32>) -> OwnedAttr {
460    OwnedAttr::Float(f64::from(value.unwrap_or(0.0)))
461}
462
463/// A paint that was not set is 0.
464pub(crate) fn paint_or_zero(value: Option<Paint>) -> OwnedAttr {
465    OwnedAttr::Int(value.map_or(0, |paint| paint.to_bits() as i64))
466}
467
468pub(crate) fn optional_text(value: Option<String>) -> OwnedAttr {
469    value.map_or(OwnedAttr::None, OwnedAttr::Text)
470}
471
472/// Writes the Modifier attributes, in the order the `rsx!` widgets write them.
473pub(crate) fn push_modifiers(modifier: &Modifier, mods: Mods, out: &mut Attrs) {
474    let container = mods == Mods::Container;
475    let standard = container || mods == Mods::Standard;
476    let chip = standard || mods == Mods::Chip;
477    let scaffold = mods == Mods::Scaffold;
478    let mut push = |always: bool, name: &'static str, value: OwnedAttr| {
479        let set = !matches!(value, OwnedAttr::None | OwnedAttr::Bool(false));
480        if always || set {
481            out.push((name, value));
482        }
483    };
484    push(
485        container,
486        "observe_size",
487        modifier
488            .observe_size
489            .map_or(OwnedAttr::None, OwnedAttr::Int),
490    );
491    push(container, "motion", role(modifier.motion));
492    push(container || scaffold, "material", role(modifier.material));
493    push(chip, "weight", float(modifier.weight));
494    push(chip, "width", float(modifier.width));
495    push(chip, "height", float(modifier.height));
496    push(chip, "padding", float(modifier.padding));
497    push(chip, "padding_role", role(modifier.padding_role));
498    // An animated background owns the background's slot while it is set.
499    push(
500        standard || scaffold,
501        "background",
502        paint(if modifier.animated_background.is_some() {
503            None
504        } else {
505            modifier.background
506        }),
507    );
508    push(standard, "shape_role", role(modifier.shape_role));
509    push(standard, "corner_radius", float(modifier.corner_radius));
510    push(
511        false,
512        "corner_radii",
513        modifier.corner_radii.map_or(OwnedAttr::None, |values| {
514            OwnedAttr::Bytes(
515                values
516                    .iter()
517                    .flat_map(|value| value.to_le_bytes())
518                    .collect(),
519            )
520        }),
521    );
522    push(false, "clip", OwnedAttr::Bool(modifier.clip));
523    push(
524        true,
525        "window_drag",
526        modifier
527            .window_drag
528            .map_or(OwnedAttr::None, OwnedAttr::Bool),
529    );
530    push(standard, "border_width", float(modifier.border_width));
531    push(standard, "border_color", paint(modifier.border_color));
532    push(standard, "elevation", float(modifier.elevation));
533    push(
534        false,
535        "match_parent_size",
536        OwnedAttr::Bool(modifier.match_parent_size),
537    );
538    push(
539        chip,
540        "fill_max_width",
541        OwnedAttr::Bool(modifier.fill_max_width),
542    );
543    push(
544        standard,
545        "fill_max_height",
546        OwnedAttr::Bool(modifier.fill_max_height),
547    );
548    // The layer and the values the Renderer animates, written only when they are set. An
549    // animated value owns its slot while it is set, as an animated background does above.
550    push(
551        false,
552        "press_feedback",
553        modifier
554            .press_feedback
555            .map_or(OwnedAttr::None, |(scale, alpha)| {
556                OwnedAttr::Bytes([scale.to_le_bytes(), alpha.to_le_bytes()].concat())
557            }),
558    );
559    push(
560        false,
561        "alpha",
562        float(if modifier.animated_alpha.is_some() {
563            None
564        } else {
565            modifier.alpha
566        }),
567    );
568    push(
569        false,
570        "transform",
571        match (modifier.animated_offset, modifier.transform) {
572            (None, Some(values)) => OwnedAttr::Bytes(
573                values
574                    .iter()
575                    .flat_map(|value| value.to_le_bytes())
576                    .collect(),
577            ),
578            _ => OwnedAttr::None,
579        },
580    );
581    // Written only when set, and `Auto` is the same as not set.
582    push(
583        false,
584        "cursor",
585        match modifier.cursor {
586            Some(crate::Cursor::Auto) | None => OwnedAttr::None,
587            Some(cursor) => OwnedAttr::Int(i64::from(u16::from(cursor))),
588        },
589    );
590    push(
591        false,
592        "user_select",
593        match modifier.user_select {
594            Some(crate::UserSelect::Auto) | None => OwnedAttr::None,
595            Some(select) => OwnedAttr::Int(i64::from(u16::from(select))),
596        },
597    );
598    // The layers and the displacement, written only when they are set.
599    push(
600        false,
601        "offset",
602        modifier.offset.map_or(OwnedAttr::None, |(x, y)| {
603            let mut bytes = Vec::with_capacity(8);
604            bytes.extend_from_slice(&x.to_le_bytes());
605            bytes.extend_from_slice(&y.to_le_bytes());
606            OwnedAttr::Bytes(bytes)
607        }),
608    );
609    push(
610        false,
611        "shadows",
612        if modifier.shadows[0].is_none() {
613            OwnedAttr::None
614        } else {
615            let mut bytes = Vec::new();
616            for shadow in modifier.shadows.iter().flatten() {
617                for value in [shadow.x, shadow.y, shadow.blur, shadow.spread] {
618                    bytes.extend_from_slice(&value.to_le_bytes());
619                }
620                bytes.extend_from_slice(&shadow.paint.to_bits().to_le_bytes());
621            }
622            OwnedAttr::Bytes(bytes)
623        },
624    );
625    push(
626        false,
627        "animated_alpha",
628        modifier.animated_alpha.map_or(OwnedAttr::None, |value| {
629            OwnedAttr::Bytes(crate::animation::animated_bytes(
630                &[value.target.to_bits()],
631                &value.spec,
632            ))
633        }),
634    );
635    push(
636        false,
637        "animated_background",
638        modifier
639            .animated_background
640            .map_or(OwnedAttr::None, |value| {
641                let bits = value.target.to_bits();
642                OwnedAttr::Bytes(crate::animation::animated_bytes(
643                    &[bits as u32, (bits >> 32) as u32],
644                    &value.spec,
645                ))
646            }),
647    );
648    push(
649        false,
650        "animated_offset",
651        modifier.animated_offset.map_or(OwnedAttr::None, |value| {
652            OwnedAttr::Bytes(crate::animation::animated_bytes(
653                &[value.target.0.to_bits(), value.target.1.to_bits()],
654                &value.spec,
655            ))
656        }),
657    );
658}
659
660/// Composes one widget node: its group, its attributes and listeners, and its children.
661///
662/// `fill` writes the attributes and runs while the composer is held, so it only moves
663/// values out of the builder. `content` runs afterwards, with the node as the parent of
664/// whatever it composes.
665pub(crate) fn emit(
666    key: u64,
667    widget: WidgetKind,
668    fill: impl FnOnce(&mut Attrs),
669    listeners: Vec<(&'static str, EventCallback)>,
670    content: impl FnOnce(),
671) {
672    let _group = group_guard(key);
673    emit_node(widget, fill, listeners, content);
674}
675
676/// The same, inside a group the caller has already opened, for a widget that remembers
677/// something of its own before its node.
678pub(crate) fn emit_node(
679    widget: WidgetKind,
680    fill: impl FnOnce(&mut Attrs),
681    listeners: Vec<(&'static str, EventCallback)>,
682    content: impl FnOnce(),
683) {
684    let node = with_composer_if_idle(|composer| {
685        if !composer.composing() {
686            return None;
687        }
688        let mut attrs = composer.take_attrs();
689        fill(&mut attrs);
690        Some(composer.update_node(widget, attrs, listeners))
691    })
692    .flatten();
693    let Some(node) = node else {
694        return;
695    };
696    with_composer(|composer| composer.push_node_context(node));
697    let _context = NodeContextGuard;
698    content();
699}
700
701/// Closes a widget's node context on every way out of its content, an unwind included.
702struct NodeContextGuard;
703
704impl Drop for NodeContextGuard {
705    fn drop(&mut self) {
706        with_composer_if_idle(|composer| composer.pop_node_context());
707    }
708}
709
710/// A call site key for a widget, from its name.
711pub(crate) const fn widget_key(name: &str) -> u64 {
712    crate::runtime::composer::call_site(name, 0)
713}
714
715#[cfg(test)]
716mod fr19_9_tests {
717    use super::*;
718
719    #[test]
720    fn fr19_9_window_drag_builder_preserves_false() {
721        for enabled in [false, true] {
722            let mut attrs = Vec::new();
723            push_modifiers(
724                &Modifier::new().window_drag(enabled),
725                Mods::Standard,
726                &mut attrs,
727            );
728            assert!(attrs.iter().any(|(name, value)| *name == "window_drag"
729                && matches!(value, OwnedAttr::Bool(value) if *value == enabled)));
730        }
731    }
732}