Skip to main content

crabshell_ui/
prop.rs

1//! Widget properties that take either a plain value or a closure.
2
3use std::cell::RefCell;
4use std::path::{Path, PathBuf};
5use std::rc::Rc;
6use std::time::Duration;
7
8use crate::menu::MenuEntry;
9use crate::popup::Adjust;
10use crate::region::Region;
11use crate::screencopy::CaptureSource;
12use crate::style::Style;
13use crate::widgets::{Align, Fit, IconSource, Justify};
14use crate::window::{Anchors, Edge, KeyboardFocus, Layer, Margins};
15
16/// A widget property: either a value set once, or a binding that reruns
17/// whenever a signal it read changes.
18///
19/// You never build one yourself. Widget methods take `impl IntoProp<T>`, so
20/// `.height(30)` sets a value and `.height(move || ...)` makes a binding.
21pub struct Prop<T>(pub(crate) PropKind<T>);
22
23impl<T: Clone + 'static> Prop<T> {
24  /// The property as a closure that can be called again and shared, as a
25  /// widget built from other widgets hands one property to many of them.
26  /// A binding stays one: whatever calls it subscribes to the signals it
27  /// reads.
28  pub fn shared(self) -> Rc<dyn Fn() -> T> {
29    match self.0 {
30      PropKind::Value(value) => Rc::new(move || value.clone()),
31      PropKind::Binding(compute) => {
32        let compute = RefCell::new(compute);
33        Rc::new(move || (compute.borrow_mut())())
34      }
35    }
36  }
37}
38
39pub(crate) enum PropKind<T> {
40  Value(T),
41  Binding(Box<dyn FnMut() -> T>),
42}
43
44/// Anything a widget property accepts: a value of type `T`, or a closure
45/// that returns one.
46///
47/// Plain values work for the property types this crate uses, and a string
48/// slice works where a `String` is expected. Any `FnMut() -> T` closure
49/// works too: it runs in an effect, so it reruns when a signal it read
50/// changes.
51pub trait IntoProp<T> {
52  /// Wraps the value or closure.
53  fn into_prop(self) -> Prop<T>;
54}
55
56// A closure is a binding. This impl can sit next to the value impls below
57// because Rust knows that no plain type like `u32` implements `FnMut`.
58impl<T, F> IntoProp<T> for F
59where
60  F: FnMut() -> T + 'static,
61{
62  fn into_prop(self) -> Prop<T> {
63    Prop(PropKind::Binding(Box::new(self)))
64  }
65}
66
67/// Lets each listed type be passed as a plain value.
68macro_rules! value_props {
69    ($($ty:ty),* $(,)?) => {
70        $(
71            impl IntoProp<$ty> for $ty {
72                fn into_prop(self) -> Prop<$ty> {
73                    Prop(PropKind::Value(self))
74                }
75            }
76        )*
77    };
78}
79
80value_props!(
81  bool,
82  u32,
83  i32,
84  f32,
85  String,
86  Style,
87  Align,
88  Justify,
89  Anchors,
90  Layer,
91  Margins,
92  KeyboardFocus,
93  Option<Region>,
94  PathBuf,
95  Fit,
96  Option<usize>,
97  (u32, u32),
98  Adjust,
99  IconSource,
100  CaptureSource,
101  Vec<MenuEntry>,
102  Duration,
103);
104
105impl IntoProp<String> for &str {
106  fn into_prop(self) -> Prop<String> {
107    Prop(PropKind::Value(self.to_owned()))
108  }
109}
110
111impl IntoProp<Option<Region>> for Region {
112  fn into_prop(self) -> Prop<Option<Region>> {
113    Prop(PropKind::Value(Some(self)))
114  }
115}
116
117impl IntoProp<Anchors> for Edge {
118  fn into_prop(self) -> Prop<Anchors> {
119    Prop(PropKind::Value(Anchors::from(self)))
120  }
121}
122
123/// An icon name, such as `"audio-volume-high"`.
124impl IntoProp<IconSource> for &str {
125  fn into_prop(self) -> Prop<IconSource> {
126    Prop(PropKind::Value(IconSource::Name(self.to_owned())))
127  }
128}
129
130/// An icon name, as tray items send them.
131impl IntoProp<IconSource> for String {
132  fn into_prop(self) -> Prop<IconSource> {
133    Prop(PropKind::Value(IconSource::Name(self)))
134  }
135}
136
137impl IntoProp<PathBuf> for &Path {
138  fn into_prop(self) -> Prop<PathBuf> {
139    Prop(PropKind::Value(self.to_owned()))
140  }
141}
142
143impl IntoProp<PathBuf> for &str {
144  fn into_prop(self) -> Prop<PathBuf> {
145    Prop(PropKind::Value(PathBuf::from(self)))
146  }
147}
148
149impl IntoProp<PathBuf> for String {
150  fn into_prop(self) -> Prop<PathBuf> {
151    Prop(PropKind::Value(PathBuf::from(self)))
152  }
153}
154
155/// An SVG or PNG file.
156impl IntoProp<IconSource> for &Path {
157  fn into_prop(self) -> Prop<IconSource> {
158    Prop(PropKind::Value(IconSource::File(self.to_owned())))
159  }
160}
161
162/// An SVG or PNG file.
163impl IntoProp<IconSource> for PathBuf {
164  fn into_prop(self) -> Prop<IconSource> {
165    Prop(PropKind::Value(IconSource::File(self)))
166  }
167}