1use std::hash::{Hash, Hasher};
14
15use crate::widget::Widget;
16
17pub struct Keyed<Msg = crate::callback::Callback> {
19 key: u64,
20 inner: Box<dyn Widget<Msg>>,
21}
22
23impl<Msg> Keyed<Msg> {
24 pub fn new(key: impl Hash, inner: impl Widget<Msg> + 'static) -> Self {
26 let mut hasher = std::collections::hash_map::DefaultHasher::new();
27 key.hash(&mut hasher);
28 Self {
29 key: hasher.finish(),
30 inner: Box::new(inner),
31 }
32 }
33
34 pub fn wrap(index: usize, inner: impl Widget<Msg> + 'static) -> Self {
42 match inner.key() {
43 Some(key) => Self {
44 key,
45 inner: Box::new(inner),
46 },
47 None => Self::new(index, inner),
48 }
49 }
50
51 fn restyle(&self, base: frus_layout::Style) -> frus_layout::Style {
53 base
54 }
55}
56
57crate::transparent::forward_transparent!(Keyed {
58 fn key(&self) -> Option<u64> {
60 Some(self.key)
61 }
62
63 fn positioned(&self) -> Option<crate::positioned::Positioning> {
65 self.inner.positioned()
66 }
67
68 fn theme_override(
70 &self,
71 inherited: &crate::theme::Theme,
72 ) -> Option<Box<crate::theme::Theme>> {
73 self.inner.theme_override(inherited)
74 }
75
76 fn media_override(&self, inherited: crate::MediaQuery) -> Option<crate::MediaQuery> {
78 self.inner.media_override(inherited)
79 }
80 fn scaffold_override(&self) -> Option<crate::ScaffoldInfo> {
81 self.inner.scaffold_override()
82 }
83 fn scroll_behavior_override(&self) -> Option<crate::ScrollBehavior> {
84 self.inner.scroll_behavior_override()
85 }
86 fn autofill_group(&self) -> bool {
88 self.inner.autofill_group()
89 }
90 fn area_toolbar(
92 &self,
93 context: crate::ToolbarContext,
94 ) -> Option<Option<&dyn crate::widget::Widget<Msg>>> {
95 self.inner.area_toolbar(context)
96 }
97});
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use crate::widget::ReorderAxis;
103 use crate::{Container, Text};
104
105 struct Card;
108
109 impl Widget<()> for Card {
110 fn style(&self) -> frus_layout::Style {
111 frus_layout::Style::default()
112 }
113 fn children(&self) -> &[Box<dyn Widget<()>>] {
114 &[]
115 }
116 fn paint(
117 &self,
118 _bounds: frus_core::Rect,
119 _status: crate::interaction::Status,
120 _theme: &crate::theme::Theme,
121 _scene: &mut frus_core::Scene,
122 ) {
123 }
124 fn on_click(&self) -> Option<()> {
125 None
126 }
127 fn reorder_axis(&self) -> ReorderAxis {
128 ReorderAxis::Vertical
129 }
130 fn reorder_draggable(&self) -> bool {
131 false
132 }
133 }
134
135 #[test]
140 fn a_keyed_reorderable_keeps_its_axis_and_its_refusal_to_be_lifted() {
141 let keyed = Keyed::new(1u64, Card);
142 assert_eq!(Widget::<()>::reorder_axis(&keyed), ReorderAxis::Vertical);
143 assert!(!Widget::<()>::reorder_draggable(&keyed));
144 }
145
146 #[test]
151 fn structural_questions_pass_through() {
152 let stack = crate::Stack::<()>::new()
153 .width(100.0)
154 .height(50.0)
155 .layer(Container::new())
156 .layer(Container::new());
157 assert!(Widget::<()>::stack(&stack));
158 assert!(
159 Widget::<()>::stack(&Keyed::new(1u64, stack)),
160 "a keyed stack is still a stack"
161 );
162
163 let spinner = crate::CircularProgressIndicator::new();
164 assert!(Widget::<()>::continuous(&spinner));
165 assert!(Widget::<()>::continuous(&Keyed::new(2u64, spinner)));
166 }
167
168 #[test]
169 fn reports_key_and_delegates() {
170 let inner = Container::<()>::new().width(40.0).child(Text::new("x"));
171 let keyed = Keyed::new(99u64, inner);
172 assert!(Widget::<()>::key(&keyed).is_some());
174 assert_eq!(Widget::<()>::children(&keyed).len(), 1);
176 }
177
178 #[test]
179 fn same_key_same_hash() {
180 let a: Keyed<()> = Keyed::new("todo-3", Container::new());
181 let b: Keyed<()> = Keyed::new("todo-3", Container::new());
182 assert_eq!(Widget::<()>::key(&a), Widget::<()>::key(&b));
183 }
184
185 #[test]
188 fn wrapping_keeps_a_key_the_child_already_had() {
189 let named: Keyed<()> = Keyed::new("row-7", Text::new("seven"));
190 let key = Widget::<()>::key(&named).expect("a key of its own");
191 let wrapped = Keyed::wrap(3, named);
192 assert_eq!(
193 Widget::<()>::key(&wrapped),
194 Some(key),
195 "the position did not overwrite the name"
196 );
197
198 let plain: Keyed<()> = Keyed::wrap(3, Text::new("nobody named me"));
199 let other: Keyed<()> = Keyed::wrap(4, Text::new("nobody named me"));
200 assert_ne!(
201 Widget::<()>::key(&plain),
202 Widget::<()>::key(&other),
203 "and an unnamed child is at least distinguished by where it is"
204 );
205 }
206}