ribir_core/builtin_widgets/
key.rs

1use std::{
2  cmp::{Eq, Ord, PartialOrd},
3  fmt::Debug,
4};
5
6use crate::prelude::*;
7
8/// `Key` help `Ribir` to track if two widget is a same widget in two frames.
9/// Abstract all builtin key into a same type.
10#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
11pub enum Key {
12  Kusize(usize),
13  Ku1(u8),
14  Ku2(u16),
15  Ku4(u32),
16  Ku8(u64),
17  Ku16(u128),
18
19  Kisize(isize),
20  Ki1(i8),
21  Ki2(i16),
22  Ki4(i32),
23  Ki8(i64),
24  Ki16(i128),
25
26  Kbool(bool),
27  Kchar(char),
28
29  Kstring(String),
30  K32([u8; 32]),
31}
32
33#[derive(Clone, Debug, PartialEq, Copy)]
34pub struct KeyChange<V>(pub Option<V>, pub V);
35
36impl<V: Default> Default for KeyChange<V> {
37  fn default() -> Self { KeyChange(None, V::default()) }
38}
39
40/// A widget that can be used to track if the widget is the same widget in two
41/// frames by its key. If two widget has same parent and key in two frames, the
42/// new widget in the next frame will be treated as the same widget in the last
43/// frame.
44///
45/// ## Notice
46///
47/// You should always keep the `KeyWidget` is the root of a `Pipe` widget. And
48/// not use builtin fields for `KeyWidget`. For example:
49///
50/// ```rust
51/// use ribir_core::prelude::*;
52///  
53/// let trigger = State::value(0);
54///
55/// // This widget will be tracked by `Key`, `Pipe` know itself generate `KeyWidget`
56/// fn_widget!{
57///   @ {
58///     pipe!($trigger;).map(move |_| @KeyWidget {
59///       key: "key",
60///       value: (),
61///       @Void {}
62///     })
63///   }
64/// };
65///
66/// let trigger = State::value(0);
67/// // This widget will not be tracked by `Key`, `Pipe` don't know itself
68/// // generate `KeyWidget`, because the root of generated widget is `Margin`.
69/// fn_widget!{
70///   @ {
71///     pipe!($trigger;).map(move |_| @KeyWidget {
72///       margin: EdgeInsets::all(10.),
73///       key: "key",
74///       value: (),
75///       @Void {}
76///     })
77///   }
78/// };
79#[derive(Declare)]
80pub struct KeyWidget<V: 'static> {
81  pub key: Key,
82  #[declare(strict)]
83  pub value: V,
84  #[declare(skip)]
85  before_value: Option<V>,
86  #[declare(skip)]
87  has_successor: bool,
88}
89
90/// A trait for `keyWidget` that use to record information of the previous and
91/// next key widget.
92pub(crate) trait AnyKey: Any {
93  fn key(&self) -> Key;
94  /// Record the previous KeyWidget associated with the same key.
95  fn record_prev_key_widget(&self, key: &dyn AnyKey);
96  /// Record the next KeyWidget associated with the same key.
97  fn record_next_key_widget(&self, key: &dyn AnyKey);
98  fn as_any(&self) -> &dyn Any;
99}
100
101impl<T, V> AnyKey for T
102where
103  T: StateWriter<Value = KeyWidget<V>>,
104  V: Clone + PartialEq + 'static,
105{
106  fn key(&self) -> Key { self.read().key.clone() }
107
108  fn record_prev_key_widget(&self, key: &dyn AnyKey) {
109    assert_eq!(self.key(), key.key());
110    let Some(key) = key.as_any().downcast_ref::<Self>() else {
111      log::warn!("Different value type for same key.");
112      return;
113    };
114    self
115      .write()
116      .record_before_value(key.read().value.clone());
117  }
118
119  fn record_next_key_widget(&self, _: &dyn AnyKey) { self.silent().has_successor = true; }
120
121  fn as_any(&self) -> &dyn Any { self }
122}
123
124impl<V: 'static + Default + Clone + PartialEq> ComposeChild for KeyWidget<V> {
125  type Child = Widget;
126  #[inline]
127  fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> impl WidgetBuilder {
128    fn_widget! {
129      let data: Box<dyn AnyKey> = Box::new(this);
130      child.attach_data(data, ctx!()).build(ctx!())
131    }
132  }
133}
134
135impl Query for Box<dyn AnyKey> {
136  crate::widget::impl_query_self_only!();
137}
138
139impl<V> KeyWidget<V>
140where
141  V: Clone + PartialEq,
142{
143  /// Detect if the key widget is a new widget, there is not predecessor widget
144  /// that has same key. Usually used in `on_mounted` callback.
145  pub fn is_enter(&self) -> bool { self.before_value.is_none() }
146  /// Detect if the key widget is really be disposed, there is not successor
147  /// widget has same key. Usually used in `on_disposed` callback.
148  pub fn is_leave(&self) -> bool { !self.has_successor }
149
150  /// Detect if the value of the key widget is changed
151  pub fn is_changed(&self) -> bool {
152    self.before_value.is_some() && self.before_value.as_ref() != Some(&self.value)
153  }
154
155  pub fn get_change(&self) -> KeyChange<V> {
156    KeyChange(self.before_value.clone(), self.value.clone())
157  }
158
159  pub fn before_value(&self) -> Option<&V> { self.before_value.as_ref() }
160
161  fn record_before_value(&mut self, value: V) { self.before_value = Some(value); }
162}
163
164macro_rules! from_key_impl {
165  ($($ty: ty : $name: ident)*) => {
166    $(
167      impl From<$ty> for Key {
168        fn from(s: $ty) -> Self {
169          Key::$name(s)
170        }
171      }
172    )*
173  };
174}
175
176from_key_impl!(
177  usize:Kusize u8:Ku1 u16:Ku2 u32:Ku4 u64:Ku8 u128:Ku16
178  isize:Kisize i8:Ki1 i16:Ki2 i32:Ki4 i64:Ki8 i128:Ki16
179  bool:Kbool char:Kchar
180  [u8;32]:K32
181);
182
183const MAX_KEY_STR: usize = 16;
184
185impl From<String> for Key {
186  fn from(s: String) -> Self {
187    if s.len() < MAX_KEY_STR {
188      Key::Kstring(s)
189    } else {
190      Key::K32(blake3::hash(s.as_bytes()).into())
191    }
192  }
193}
194
195impl From<&str> for Key {
196  fn from(s: &str) -> Self {
197    if s.len() < MAX_KEY_STR {
198      Key::Kstring(s.to_owned())
199    } else {
200      Key::K32(blake3::hash(s.as_bytes()).into())
201    }
202  }
203}
204
205#[macro_export]
206macro_rules! complex_key {
207  ($($k: expr),*) => {{
208    let mut hasher = blake3::Hasher::new();
209    $(
210      $k.consume(&mut hasher);
211    )*
212    let bytes: [u8;32] = hasher.finalize().into();
213    bytes
214  }};
215}
216pub trait ConsumeByHasher {
217  fn consume(self, hasher: &mut blake3::Hasher);
218}
219
220impl ConsumeByHasher for String {
221  #[inline]
222  fn consume(self, hasher: &mut blake3::Hasher) { hasher.update(self.as_bytes()); }
223}
224
225impl<'a> ConsumeByHasher for &'a str {
226  #[inline]
227  fn consume(self, hasher: &mut blake3::Hasher) { hasher.update(self.as_bytes()); }
228}
229
230macro_rules! impl_as_u8_consume_by_hasher {
231  ($($t: ty)*) => {
232    $(
233      impl ConsumeByHasher for $t {
234        #[inline]
235        fn consume(self, hasher: &mut blake3::Hasher) {
236          hasher.update(&[self as u8]);
237        }
238      }
239    )*
240  };
241}
242impl_as_u8_consume_by_hasher!(bool char);
243
244macro_rules! impl_bytes_consume_by_hasher {
245  ($($ty: ty)*) => {
246    $(
247      impl ConsumeByHasher for $ty {
248        #[inline]
249        fn consume(self, hasher: &mut blake3::Hasher) {
250          hasher.update(&self.to_ne_bytes());
251        }
252      }
253    )*
254  };
255}
256
257impl_bytes_consume_by_hasher!(
258  usize u8 u16 u32 u64 u128
259  isize i8 i16 i32 i64 i128
260  f32 f64
261);
262
263#[test]
264fn key_detect() {
265  let k1: Key = 0i32.into();
266  let k2: Key = String::new().into();
267  let k3: Key = "".into();
268  let ck1 = complex_key!("asd", true, 1);
269  let ck2 = complex_key!("asd", true, 1);
270  assert!(k1 != k2);
271  assert!(k2 == k3);
272  assert!(k3 != k1);
273  assert!(ck1 == ck2);
274}