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
85/// containers which can contain multiple widgets.
86pub trait InkChildren {
87    /// equivalent to `.children()`
88    /// but stripped of their [wrapper](InkWrapper), effectively making them orphans
89    /// (meaning their relative position in the graph cannot be determined anymore).
90    fn orphans(&self) -> Vec<Widget>;
91    /// children [wrappers](InkWrapper),
92    /// which conserve their relative index in the graph.
93    fn children(&self) -> Vec<InkWrapper<Widget>>;
94}
95
96pub trait InkLeafWidget: InkWidget + Debug {}
97
98pub trait InkCompoundWidget: InkWidget + InkChildren + Debug {}
99
100impl<T> InkCompoundWidget for T where T: InkWidget + InkChildren + Debug {}
101
102impl InkChildren for inkMultiChildren {
103    fn orphans(&self) -> Vec<Widget> {
104        self.children.iter().map(|x| x.data.clone()).collect()
105    }
106
107    fn children(&self) -> Vec<InkWrapper<Widget>> {
108        self.children.to_vec()
109    }
110}
111
112impl_ink_children!(inkCanvasWidget);
113impl_ink_children!(inkHorizontalPanelWidget);
114impl_ink_children!(inkVerticalPanelWidget);
115impl_ink_children!(inkScrollAreaWidget);
116impl_ink_children!(inkUniformGridWidget);
117impl_ink_children!(inkVirtualCompoundWidget);
118impl_ink_children!(inkFlexWidget);
119impl_ink_children!(inkCacheWidget);
120
121impl_ink_widget!(inkCanvasWidget);
122impl_ink_widget!(inkHorizontalPanelWidget);
123impl_ink_widget!(inkVerticalPanelWidget);
124impl_ink_widget!(inkScrollAreaWidget);
125impl_ink_widget!(inkUniformGridWidget);
126impl_ink_widget!(inkVirtualCompoundWidget);
127impl_ink_widget!(inkFlexWidget);
128impl_ink_widget!(inkCacheWidget);
129
130impl_ink_widget!(inkTextWidget);
131impl_ink_widget!(inkImageWidget);
132impl_ink_widget!(inkVideoWidget);
133impl_ink_widget!(inkMaskWidget);
134impl_ink_widget!(inkBorderWidget);
135impl_ink_widget!(inkShapeWidget);
136impl_ink_widget!(inkCircleWidget);
137impl_ink_widget!(inkRectangleWidget);
138impl_ink_widget!(inkVectorGraphicWidget);
139
140impl_leaf_widget!(inkTextWidget);
141impl_leaf_widget!(inkImageWidget);
142impl_leaf_widget!(inkVideoWidget);
143impl_leaf_widget!(inkMaskWidget);
144impl_leaf_widget!(inkBorderWidget);
145impl_leaf_widget!(inkShapeWidget);
146impl_leaf_widget!(inkCircleWidget);
147impl_leaf_widget!(inkRectangleWidget);
148impl_leaf_widget!(inkVectorGraphicWidget);
149
150impl_classname!(inkMultiChildren);
151
152impl_classname!(inkCanvasWidget);
153impl_classname!(inkHorizontalPanelWidget);
154impl_classname!(inkVerticalPanelWidget);
155impl_classname!(inkScrollAreaWidget);
156impl_classname!(inkUniformGridWidget);
157impl_classname!(inkVirtualCompoundWidget);
158impl_classname!(inkFlexWidget);
159impl_classname!(inkCacheWidget);
160
161impl_classname!(inkTextWidget);
162impl_classname!(inkImageWidget);
163impl_classname!(inkVideoWidget);
164impl_classname!(inkMaskWidget);
165impl_classname!(inkBorderWidget);
166impl_classname!(inkShapeWidget);
167impl_classname!(inkCircleWidget);
168impl_classname!(inkRectangleWidget);
169impl_classname!(inkVectorGraphicWidget);
170impl Widget {
171    pub fn name(&self) -> Option<&str> {
172        match self {
173            Self::inkMultiChildren(_) => None,
174            Self::inkCanvasWidget(node) => Some(node.name()),
175            Self::inkHorizontalPanelWidget(node) => Some(node.name()),
176            Self::inkVerticalPanelWidget(node) => Some(node.name()),
177            Self::inkScrollAreaWidget(node) => Some(node.name()),
178            Self::inkUniformGridWidget(node) => Some(node.name()),
179            Self::inkVirtualCompoundWidget(node) => Some(node.name()),
180            Self::inkFlexWidget(node) => Some(node.name()),
181            Self::inkCacheWidget(node) => Some(node.name()),
182            Self::inkTextWidget(node) => Some(node.name()),
183            Self::inkImageWidget(node) => Some(node.name()),
184            Self::inkVideoWidget(node) => Some(node.name()),
185            Self::inkMaskWidget(node) => Some(node.name()),
186            Self::inkBorderWidget(node) => Some(node.name()),
187            Self::inkShapeWidget(node) => Some(node.name()),
188            Self::inkCircleWidget(node) => Some(node.name()),
189            Self::inkRectangleWidget(node) => Some(node.name()),
190            Self::inkVectorGraphicWidget(node) => Some(node.name()),
191        }
192    }
193    pub fn as_compound(&self) -> Option<&dyn InkCompoundWidget> {
194        match self {
195            Self::inkCanvasWidget(node) => Some(node),
196            Self::inkHorizontalPanelWidget(node) => Some(node),
197            Self::inkVerticalPanelWidget(node) => Some(node),
198            Self::inkScrollAreaWidget(node) => Some(node),
199            Self::inkUniformGridWidget(node) => Some(node),
200            Self::inkVirtualCompoundWidget(node) => Some(node),
201            Self::inkFlexWidget(node) => Some(node),
202            Self::inkCacheWidget(node) => Some(node),
203            _ => None,
204        }
205    }
206    pub fn as_widget(&self) -> Option<&dyn InkWidget> {
207        match self {
208            Self::inkMultiChildren(_) => None,
209            Self::inkCanvasWidget(widget) => Some(widget),
210            Self::inkHorizontalPanelWidget(widget) => Some(widget),
211            Self::inkVerticalPanelWidget(widget) => Some(widget),
212            Self::inkScrollAreaWidget(widget) => Some(widget),
213            Self::inkUniformGridWidget(widget) => Some(widget),
214            Self::inkVirtualCompoundWidget(widget) => Some(widget),
215            Self::inkFlexWidget(widget) => Some(widget),
216            Self::inkCacheWidget(widget) => Some(widget),
217            Self::inkTextWidget(widget) => Some(widget),
218            Self::inkImageWidget(widget) => Some(widget),
219            Self::inkVideoWidget(widget) => Some(widget),
220            Self::inkMaskWidget(widget) => Some(widget),
221            Self::inkBorderWidget(widget) => Some(widget),
222            Self::inkShapeWidget(widget) => Some(widget),
223            Self::inkCircleWidget(widget) => Some(widget),
224            Self::inkRectangleWidget(widget) => Some(widget),
225            Self::inkVectorGraphicWidget(widget) => Some(widget),
226        }
227    }
228    pub fn as_leaf(&self) -> Option<&dyn InkLeafWidget> {
229        match self {
230            Self::inkTextWidget(widget) => Some(widget),
231            Self::inkImageWidget(widget) => Some(widget),
232            Self::inkVideoWidget(widget) => Some(widget),
233            Self::inkMaskWidget(widget) => Some(widget),
234            Self::inkBorderWidget(widget) => Some(widget),
235            Self::inkShapeWidget(widget) => Some(widget),
236            Self::inkCircleWidget(widget) => Some(widget),
237            Self::inkRectangleWidget(widget) => Some(widget),
238            Self::inkVectorGraphicWidget(widget) => Some(widget),
239            _ => None,
240        }
241    }
242    pub fn is_leaf(&self) -> bool {
243        self.as_leaf().is_some()
244    }
245    pub fn is_compound(&self) -> bool {
246        self.as_compound().is_some()
247    }
248}
249
250pub trait WidgetTree {
251    /// return the widget type
252    fn get_widget_classname(&self, path: &[usize]) -> Option<String>;
253    /// return the full path names to the widget
254    fn get_path_names(&self, path: &[usize]) -> Option<Vec<String>>;
255    /// return the full path indexes to the widget
256    fn get_path_indexes(&self, path: &[&str]) -> Option<Vec<usize>>;
257    /// return the full path indexes to the widget
258    /// (or the partial path indexes until where it failed)
259    /// alongside the last name searched.
260    fn get_partial_path_indexes(&self, path: &[&str]) -> (Vec<usize>, String);
261}
262
263pub trait ByIndex {
264    /// find a widget by index
265    fn by_index(&self, idx: usize) -> Option<Widget>;
266}
267
268pub trait ByName {
269    /// find a widget by name
270    fn by_name(&self, name: &str) -> Option<(usize, Widget)>;
271}
272
273pub trait Leaves {
274    /// get widget summary for elements
275    fn leaves(&self) -> Vec<WidgetSummary>;
276}
277
278impl<T> InkChildren for InkWrapper<T>
279where
280    T: InkChildren,
281{
282    fn orphans(&self) -> Vec<Widget> {
283        self.data.orphans()
284    }
285
286    fn children(&self) -> Vec<InkWrapper<Widget>> {
287        self.data.children()
288    }
289}
290
291impl InkChildren for inkWidgetLibraryItem {
292    fn orphans(&self) -> Vec<Widget> {
293        self.package.data.file.root_chunk.root_widget.orphans()
294    }
295
296    fn children(&self) -> Vec<InkWrapper<Widget>> {
297        self.package.data.file.root_chunk.root_widget.children()
298    }
299}
300
301impl<T> ByIndex for T
302where
303    T: InkChildren,
304{
305    fn by_index(&self, idx: usize) -> Option<Widget> {
306        self.orphans().get(idx).cloned()
307    }
308}
309
310impl<T> ByName for T
311where
312    T: InkChildren,
313{
314    fn by_name(&self, name: &str) -> Option<(usize, Widget)> {
315        for (idx, child) in self.orphans().iter().enumerate() {
316            if let Widget::inkMultiChildren(_) = &child {
317                panic!("unexpected inkMultiChildren with name {name}");
318            }
319            if let Some(compound) = child.as_compound()
320                && compound.name() == name
321            {
322                return Some((idx, child.clone()));
323            }
324            continue;
325        }
326        None
327    }
328}
329
330impl<T> Leaves for T
331where
332    T: InkCompoundWidget,
333{
334    fn leaves(&self) -> Vec<WidgetSummary> {
335        let mut out = vec![];
336        for child in self.children().iter() {
337            if let Some(name) = child.data.name() {
338                out.push(WidgetSummary {
339                    HandleId: child.handle_id,
340                    Name: Name {
341                        r#type: String::from("CName"),
342                        storage: String::from("string"),
343                        value: name.to_string(),
344                    },
345                });
346            }
347        }
348        out
349    }
350}
351
352impl ByName for Vec<InkWrapper<Widget>> {
353    fn by_name(&self, name: &str) -> Option<(usize, Widget)> {
354        for (idx, widget) in self.iter().enumerate() {
355            if let Some(compound) = widget.data.as_compound()
356                && compound.name() == name
357            {
358                return Some((idx, widget.data.clone()));
359            }
360            if let Some(leaf) = widget.data.as_leaf()
361                && leaf.name() == name
362            {
363                return Some((idx, widget.data.clone()));
364            }
365        }
366        None
367    }
368}
369
370impl ByIndex for Vec<InkWrapper<Widget>> {
371    fn by_index(&self, idx: usize) -> Option<Widget> {
372        self.get(idx).map(|x| x.data.clone())
373    }
374}
375
376impl ByName for &dyn InkCompoundWidget {
377    fn by_name(&self, name: &str) -> Option<(usize, Widget)> {
378        self.children().by_name(name)
379    }
380}
381
382impl ByIndex for &dyn InkCompoundWidget {
383    fn by_index(&self, idx: usize) -> Option<Widget> {
384        self.children().by_index(idx)
385    }
386}
387
388impl ByName for Widget {
389    fn by_name(&self, name: &str) -> Option<(usize, Widget)> {
390        self.as_compound().and_then(|x| x.by_name(name))
391    }
392}
393
394impl ByIndex for Widget {
395    fn by_index(&self, idx: usize) -> Option<Widget> {
396        if let Widget::inkMultiChildren(node) = self {
397            return node.by_index(idx);
398        }
399        if let Some(compound) = self.as_compound() {
400            return compound.children().by_index(idx);
401        }
402        Some(self.clone())
403    }
404}
405
406impl WidgetTree for inkWidgetLibraryItemInstance {
407    fn get_widget_classname(&self, path: &[usize]) -> Option<String> {
408        let mut parent: Option<Widget> = Some(Widget::inkMultiChildren(
409            self.root_widget.data.children.data.clone(),
410        ));
411        let last = path.len() - 1;
412        for (i, idx) in path.iter().enumerate() {
413            if parent.is_none() {
414                break;
415            }
416            if let Some(ref child) = parent.as_ref().unwrap().by_index(*idx) {
417                if let Widget::inkMultiChildren(_) = child {
418                    panic!("encountered unexpected inkMultiChildren at index {idx}");
419                }
420                if child.as_compound().is_some() {
421                    if i == last {
422                        return Some(child.classname());
423                    }
424                    parent = Some(child.clone());
425                    continue;
426                }
427                if child.as_leaf().is_some() {
428                    return Some(child.classname());
429                }
430            }
431        }
432        None
433    }
434
435    fn get_path_names(&self, path: &[usize]) -> Option<Vec<String>> {
436        let mut names: Vec<String> = vec![];
437        let mut parent: Option<Widget> = Some(Widget::inkMultiChildren(
438            self.root_widget.data.children.data.clone(),
439        ));
440
441        let depth = path.len() - 1;
442        for (i, idx) in path.iter().enumerate() {
443            if parent.is_none() {
444                break;
445            }
446            if let Some(ref child) = parent.unwrap().by_index(*idx) {
447                if let Some(name) = child.name() {
448                    if child.as_compound().is_some() {
449                        names.push(name.to_string());
450                        parent = Some(child.clone());
451                        continue;
452                    }
453                    if child.as_leaf().is_some() {
454                        names.push(name.to_string());
455                        if i < depth {
456                            return None;
457                        }
458                        break;
459                    }
460                } else {
461                    panic!("encountered unexpected inkMultiChildren at index {idx}");
462                }
463            }
464            return None;
465        }
466        Some(names)
467    }
468
469    fn get_path_indexes(&self, path: &[&str]) -> Option<Vec<usize>> {
470        let (found, _) = self.get_partial_path_indexes(path);
471        if path.len() != found.len() {
472            return None;
473        }
474        Some(found)
475    }
476
477    fn get_partial_path_indexes(&self, path: &[&str]) -> (Vec<usize>, String) {
478        if path.is_empty() {
479            return (Vec::with_capacity(0), "".to_string());
480        }
481        let mut indexes: Vec<usize> = vec![];
482        let depth = path.len() - 1;
483        let mut parent: Option<Widget> =
484            Some(Widget::inkCanvasWidget(self.root_widget.data.clone()));
485        for (i, name) in path.iter().enumerate() {
486            if parent.is_none() || (parent.as_ref().unwrap().is_leaf() && i < depth) {
487                return (indexes, name.to_string());
488            }
489
490            if let Some(compound) = parent.as_ref().unwrap().as_compound()
491                && let Some((idx, widget)) = compound.by_name(name)
492            {
493                indexes.push(idx);
494                parent = Some(widget);
495                continue;
496            }
497        }
498        (indexes, path.last().unwrap().to_string())
499    }
500}
501
502impl inkWidgetLibraryResource {
503    pub fn root(&self) -> &inkWidgetLibraryItem {
504        self.library_items.first().expect("Root")
505    }
506    pub fn root_chunk(&self) -> &inkWidgetLibraryItemInstance {
507        &self.root().package.data.file.root_chunk
508    }
509}
510
511impl WidgetTree for inkWidgetLibraryResource {
512    fn get_widget_classname(&self, indexes: &[usize]) -> Option<String> {
513        self.root_chunk().get_widget_classname(indexes)
514    }
515
516    fn get_path_names(&self, indexes: &[usize]) -> Option<Vec<String>> {
517        self.root_chunk().get_path_names(indexes)
518    }
519
520    fn get_path_indexes(&self, names: &[&str]) -> Option<Vec<usize>> {
521        self.root_chunk().get_path_indexes(names)
522    }
523
524    fn get_partial_path_indexes(&self, names: &[&str]) -> (Vec<usize>, String) {
525        self.root_chunk().get_partial_path_indexes(names)
526    }
527}