inkanim_types/ink/widget/
implementation.rs

1use std::fmt::Debug;
2
3use enum_dispatch::enum_dispatch;
4
5use crate::{Name, ink::InkWrapper};
6
7use super::{
8    SiblingOrNested, Widget, WidgetSummary, inkBorderWidget, inkCacheWidget, inkCanvasWidget,
9    inkCircleWidget, inkFlexWidget, inkHorizontalPanelWidget, inkImageWidget, inkMaskWidget,
10    inkMultiChildren, inkRectangleWidget, inkScrollAreaWidget, inkShapeWidget, inkTextWidget,
11    inkUniformGridWidget, inkVectorGraphicWidget, inkVerticalPanelWidget, inkVideoWidget,
12    inkVirtualCompoundWidget, inkWidgetLibraryItem, inkWidgetLibraryItemInstance,
13    inkWidgetLibraryResource,
14};
15
16impl SiblingOrNested for Vec<usize> {
17    fn sibling_or_nested(&self, searched: &[usize]) -> bool {
18        let count_own = self.len();
19        let count_searched = searched.len();
20        if count_searched == 0 {
21            return true;
22        }
23        let last_searched = count_searched - 1;
24        for (i, path_index) in self.iter().enumerate() {
25            if *path_index != searched[i] {
26                return false;
27            }
28            if i == last_searched && count_own >= count_searched {
29                return true;
30            }
31        }
32        false
33    }
34}
35
36macro_rules! impl_ink_children {
37    ($ty:ident) => {
38        impl InkChildren for $ty {
39            fn orphans(&self) -> Vec<Widget> {
40                self.children.data.orphans()
41            }
42
43            fn children(&self) -> Vec<InkWrapper<Widget>> {
44                self.children.data.children()
45            }
46        }
47    };
48}
49
50macro_rules! impl_ink_widget {
51    ($ty:ident) => {
52        impl InkWidget for $ty {
53            fn name(&self) -> &str {
54                self.name.as_str()
55            }
56        }
57    };
58}
59
60macro_rules! impl_leaf_widget {
61    ($ty:ident) => {
62        impl InkLeafWidget for $ty {}
63    };
64}
65
66macro_rules! impl_classname {
67    ($ty:ident) => {
68        impl Classname for $ty {
69            fn classname(&self) -> String {
70                stringify!($ty).to_string()
71            }
72        }
73    };
74}
75
76#[enum_dispatch]
77pub trait Classname {
78    fn classname(&self) -> String;
79}
80
81pub trait InkWidget: Debug {
82    fn name(&self) -> &str;
83}
84
85pub trait InkChildren {
86    fn orphans(&self) -> Vec<Widget>;
87    fn children(&self) -> Vec<InkWrapper<Widget>>;
88}
89
90pub trait InkLeafWidget: InkWidget + Debug {}
91
92pub trait InkCompoundWidget: InkWidget + InkChildren + Debug {}
93
94impl<T> InkCompoundWidget for T where T: InkWidget + InkChildren + Debug {}
95
96impl InkChildren for inkMultiChildren {
97    fn orphans(&self) -> Vec<Widget> {
98        self.children.iter().map(|x| x.data.clone()).collect()
99    }
100
101    fn children(&self) -> Vec<InkWrapper<Widget>> {
102        self.children.to_vec()
103    }
104}
105
106impl_ink_children!(inkCanvasWidget);
107impl_ink_children!(inkHorizontalPanelWidget);
108impl_ink_children!(inkVerticalPanelWidget);
109impl_ink_children!(inkScrollAreaWidget);
110impl_ink_children!(inkUniformGridWidget);
111impl_ink_children!(inkVirtualCompoundWidget);
112impl_ink_children!(inkFlexWidget);
113impl_ink_children!(inkCacheWidget);
114
115impl_ink_widget!(inkCanvasWidget);
116impl_ink_widget!(inkHorizontalPanelWidget);
117impl_ink_widget!(inkVerticalPanelWidget);
118impl_ink_widget!(inkScrollAreaWidget);
119impl_ink_widget!(inkUniformGridWidget);
120impl_ink_widget!(inkVirtualCompoundWidget);
121impl_ink_widget!(inkFlexWidget);
122impl_ink_widget!(inkCacheWidget);
123
124impl_ink_widget!(inkTextWidget);
125impl_ink_widget!(inkImageWidget);
126impl_ink_widget!(inkVideoWidget);
127impl_ink_widget!(inkMaskWidget);
128impl_ink_widget!(inkBorderWidget);
129impl_ink_widget!(inkShapeWidget);
130impl_ink_widget!(inkCircleWidget);
131impl_ink_widget!(inkRectangleWidget);
132impl_ink_widget!(inkVectorGraphicWidget);
133
134impl_leaf_widget!(inkTextWidget);
135impl_leaf_widget!(inkImageWidget);
136impl_leaf_widget!(inkVideoWidget);
137impl_leaf_widget!(inkMaskWidget);
138impl_leaf_widget!(inkBorderWidget);
139impl_leaf_widget!(inkShapeWidget);
140impl_leaf_widget!(inkCircleWidget);
141impl_leaf_widget!(inkRectangleWidget);
142impl_leaf_widget!(inkVectorGraphicWidget);
143
144impl_classname!(inkMultiChildren);
145
146impl_classname!(inkCanvasWidget);
147impl_classname!(inkHorizontalPanelWidget);
148impl_classname!(inkVerticalPanelWidget);
149impl_classname!(inkScrollAreaWidget);
150impl_classname!(inkUniformGridWidget);
151impl_classname!(inkVirtualCompoundWidget);
152impl_classname!(inkFlexWidget);
153impl_classname!(inkCacheWidget);
154
155impl_classname!(inkTextWidget);
156impl_classname!(inkImageWidget);
157impl_classname!(inkVideoWidget);
158impl_classname!(inkMaskWidget);
159impl_classname!(inkBorderWidget);
160impl_classname!(inkShapeWidget);
161impl_classname!(inkCircleWidget);
162impl_classname!(inkRectangleWidget);
163impl_classname!(inkVectorGraphicWidget);
164impl Widget {
165    pub fn name(&self) -> Option<&str> {
166        match self {
167            Self::inkMultiChildren(_) => None,
168            Self::inkCanvasWidget(node) => Some(node.name()),
169            Self::inkHorizontalPanelWidget(node) => Some(node.name()),
170            Self::inkVerticalPanelWidget(node) => Some(node.name()),
171            Self::inkScrollAreaWidget(node) => Some(node.name()),
172            Self::inkUniformGridWidget(node) => Some(node.name()),
173            Self::inkVirtualCompoundWidget(node) => Some(node.name()),
174            Self::inkFlexWidget(node) => Some(node.name()),
175            Self::inkCacheWidget(node) => Some(node.name()),
176            Self::inkTextWidget(node) => Some(node.name()),
177            Self::inkImageWidget(node) => Some(node.name()),
178            Self::inkVideoWidget(node) => Some(node.name()),
179            Self::inkMaskWidget(node) => Some(node.name()),
180            Self::inkBorderWidget(node) => Some(node.name()),
181            Self::inkShapeWidget(node) => Some(node.name()),
182            Self::inkCircleWidget(node) => Some(node.name()),
183            Self::inkRectangleWidget(node) => Some(node.name()),
184            Self::inkVectorGraphicWidget(node) => Some(node.name()),
185        }
186    }
187    pub fn as_compound(&self) -> Option<&dyn InkCompoundWidget> {
188        match self {
189            Self::inkCanvasWidget(node) => Some(node),
190            Self::inkHorizontalPanelWidget(node) => Some(node),
191            Self::inkVerticalPanelWidget(node) => Some(node),
192            Self::inkScrollAreaWidget(node) => Some(node),
193            Self::inkUniformGridWidget(node) => Some(node),
194            Self::inkVirtualCompoundWidget(node) => Some(node),
195            Self::inkFlexWidget(node) => Some(node),
196            Self::inkCacheWidget(node) => Some(node),
197            _ => None,
198        }
199    }
200    pub fn as_widget(&self) -> Option<&dyn InkWidget> {
201        match self {
202            Self::inkMultiChildren(_) => None,
203            Self::inkCanvasWidget(widget) => Some(widget),
204            Self::inkHorizontalPanelWidget(widget) => Some(widget),
205            Self::inkVerticalPanelWidget(widget) => Some(widget),
206            Self::inkScrollAreaWidget(widget) => Some(widget),
207            Self::inkUniformGridWidget(widget) => Some(widget),
208            Self::inkVirtualCompoundWidget(widget) => Some(widget),
209            Self::inkFlexWidget(widget) => Some(widget),
210            Self::inkCacheWidget(widget) => Some(widget),
211            Self::inkTextWidget(widget) => Some(widget),
212            Self::inkImageWidget(widget) => Some(widget),
213            Self::inkVideoWidget(widget) => Some(widget),
214            Self::inkMaskWidget(widget) => Some(widget),
215            Self::inkBorderWidget(widget) => Some(widget),
216            Self::inkShapeWidget(widget) => Some(widget),
217            Self::inkCircleWidget(widget) => Some(widget),
218            Self::inkRectangleWidget(widget) => Some(widget),
219            Self::inkVectorGraphicWidget(widget) => Some(widget),
220        }
221    }
222    pub fn as_leaf(&self) -> Option<&dyn InkLeafWidget> {
223        match self {
224            Self::inkTextWidget(widget) => Some(widget),
225            Self::inkImageWidget(widget) => Some(widget),
226            Self::inkVideoWidget(widget) => Some(widget),
227            Self::inkMaskWidget(widget) => Some(widget),
228            Self::inkBorderWidget(widget) => Some(widget),
229            Self::inkShapeWidget(widget) => Some(widget),
230            Self::inkCircleWidget(widget) => Some(widget),
231            Self::inkRectangleWidget(widget) => Some(widget),
232            Self::inkVectorGraphicWidget(widget) => Some(widget),
233            _ => None,
234        }
235    }
236    pub fn is_leaf(&self) -> bool {
237        self.as_leaf().is_some()
238    }
239    pub fn is_compound(&self) -> bool {
240        self.as_compound().is_some()
241    }
242}
243
244pub trait WidgetTree {
245    /// return the widget type
246    fn get_widget_classname(&self, path: &[usize]) -> Option<String>;
247    /// return the full path names to the widget
248    fn get_path_names(&self, path: &[usize]) -> Option<Vec<String>>;
249    /// return the full path indexes to the widget
250    fn get_path_indexes(&self, path: &[&str]) -> Option<Vec<usize>>;
251}
252
253pub trait ByIndex {
254    /// find a widget by index
255    fn by_index(&self, idx: usize) -> Option<Widget>;
256}
257
258pub trait ByName {
259    /// find a widget by name
260    fn by_name(&self, name: &str) -> Option<(usize, Widget)>;
261}
262
263pub trait Leaves {
264    /// get widget summary for elements
265    fn leaves(&self) -> Vec<WidgetSummary>;
266}
267
268impl<T> InkChildren for InkWrapper<T>
269where
270    T: InkChildren,
271{
272    fn orphans(&self) -> Vec<Widget> {
273        self.data.orphans()
274    }
275
276    fn children(&self) -> Vec<InkWrapper<Widget>> {
277        self.data.children()
278    }
279}
280
281impl<T> ByIndex for T
282where
283    T: InkChildren,
284{
285    fn by_index(&self, idx: usize) -> Option<Widget> {
286        self.orphans().get(idx).cloned()
287    }
288}
289
290impl<T> ByName for T
291where
292    T: InkChildren,
293{
294    fn by_name(&self, name: &str) -> Option<(usize, Widget)> {
295        for (idx, child) in self.orphans().iter().enumerate() {
296            if let Widget::inkMultiChildren(_) = &child {
297                panic!("unexpected inkMultiChildren with name {name}");
298            }
299            if let Some(compound) = child.as_compound() {
300                if compound.name() == name {
301                    return Some((idx, child.clone()));
302                }
303            }
304            continue;
305        }
306        None
307    }
308}
309
310impl<T> Leaves for T
311where
312    T: InkCompoundWidget,
313{
314    fn leaves(&self) -> Vec<WidgetSummary> {
315        let mut out = vec![];
316        for child in self.children().iter() {
317            if let Some(name) = child.data.name() {
318                out.push(WidgetSummary {
319                    HandleId: child.handle_id,
320                    Name: Name {
321                        r#type: String::from("CName"),
322                        storage: String::from("string"),
323                        value: name.to_string(),
324                    },
325                });
326            }
327        }
328        out
329    }
330}
331
332impl ByName for Vec<InkWrapper<Widget>> {
333    fn by_name(&self, name: &str) -> Option<(usize, Widget)> {
334        for (idx, widget) in self.iter().enumerate() {
335            if let Some(compound) = widget.data.as_compound() {
336                if compound.name() == name {
337                    return Some((idx, widget.data.clone()));
338                }
339            }
340            if let Some(leaf) = widget.data.as_leaf() {
341                if leaf.name() == name {
342                    return Some((idx, widget.data.clone()));
343                }
344            }
345        }
346        None
347    }
348}
349
350impl ByIndex for Vec<InkWrapper<Widget>> {
351    fn by_index(&self, idx: usize) -> Option<Widget> {
352        self.get(idx).map(|x| x.data.clone())
353    }
354}
355
356impl ByName for &dyn InkCompoundWidget {
357    fn by_name(&self, name: &str) -> Option<(usize, Widget)> {
358        self.children().by_name(name)
359    }
360}
361
362impl ByIndex for &dyn InkCompoundWidget {
363    fn by_index(&self, idx: usize) -> Option<Widget> {
364        self.children().by_index(idx)
365    }
366}
367
368impl ByIndex for Widget {
369    fn by_index(&self, idx: usize) -> Option<Widget> {
370        if let Widget::inkMultiChildren(node) = self {
371            return node.by_index(idx);
372        }
373        if let Some(compound) = self.as_compound() {
374            return compound.children().by_index(idx);
375        }
376        Some(self.clone())
377    }
378}
379
380impl WidgetTree for inkWidgetLibraryItemInstance {
381    fn get_widget_classname(&self, path: &[usize]) -> Option<String> {
382        let mut parent: Option<Widget> = Some(Widget::inkMultiChildren(
383            self.root_widget.data.children.data.clone(),
384        ));
385        let last = path.len() - 1;
386        for (i, idx) in path.iter().enumerate() {
387            if parent.is_none() {
388                break;
389            }
390            if let Some(ref child) = parent.as_ref().unwrap().by_index(*idx) {
391                if let Widget::inkMultiChildren(_) = child {
392                    panic!("encountered unexpected inkMultiChildren at index {idx}");
393                }
394                if child.as_compound().is_some() {
395                    if i == last {
396                        return Some(child.classname());
397                    }
398                    parent = Some(child.clone());
399                    continue;
400                }
401                if child.as_leaf().is_some() {
402                    return Some(child.classname());
403                }
404            }
405        }
406        None
407    }
408
409    fn get_path_names(&self, path: &[usize]) -> Option<Vec<String>> {
410        let mut names: Vec<String> = vec![];
411        let mut parent: Option<Widget> = Some(Widget::inkMultiChildren(
412            self.root_widget.data.children.data.clone(),
413        ));
414
415        let depth = path.len() - 1;
416        for (i, idx) in path.iter().enumerate() {
417            if parent.is_none() {
418                break;
419            }
420            if let Some(ref child) = parent.unwrap().by_index(*idx) {
421                if let Some(name) = child.name() {
422                    if child.as_compound().is_some() {
423                        names.push(name.to_string());
424                        parent = Some(child.clone());
425                        continue;
426                    }
427                    if child.as_leaf().is_some() {
428                        names.push(name.to_string());
429                        if i < depth {
430                            return None;
431                        }
432                        break;
433                    }
434                } else {
435                    panic!("encountered unexpected inkMultiChildren at index {idx}");
436                }
437            }
438            return None;
439        }
440        Some(names)
441    }
442
443    fn get_path_indexes(&self, path: &[&str]) -> Option<Vec<usize>> {
444        let mut indexes: Vec<usize> = vec![];
445        let depth = path.len() - 1;
446        let mut parent: Option<Widget> =
447            Some(Widget::inkCanvasWidget(self.root_widget.data.clone()));
448        for (i, name) in path.iter().enumerate() {
449            if parent.is_none() {
450                break;
451            }
452
453            if parent.as_ref().unwrap().is_leaf() {
454                if i < depth {
455                    return None;
456                }
457                break;
458            }
459
460            if let Some(compound) = parent.as_ref().unwrap().as_compound() {
461                if let Some((idx, widget)) = compound.by_name(name) {
462                    indexes.push(idx);
463                    parent = Some(widget);
464                    continue;
465                }
466            }
467            return None;
468        }
469        Some(indexes)
470    }
471}
472
473impl inkWidgetLibraryResource {
474    pub fn root(&self) -> &inkWidgetLibraryItem {
475        self.library_items.first().expect("Root")
476    }
477    pub fn root_chunk(&self) -> &inkWidgetLibraryItemInstance {
478        &self.root().package.data.file.root_chunk
479    }
480}
481
482impl WidgetTree for inkWidgetLibraryResource {
483    fn get_widget_classname(&self, indexes: &[usize]) -> Option<String> {
484        self.root_chunk().get_widget_classname(indexes)
485    }
486
487    fn get_path_names(&self, indexes: &[usize]) -> Option<Vec<String>> {
488        self.root_chunk().get_path_names(indexes)
489    }
490
491    fn get_path_indexes(&self, names: &[&str]) -> Option<Vec<usize>> {
492        self.root_chunk().get_path_indexes(names)
493    }
494}