makepad_widgets/
designer.rs

1use crate::{
2    makepad_draw::*,
3    file_tree::*,
4    view::View,
5    widget::*,
6};
7
8live_design!{
9    import makepad_widgets::base::*
10    import makepad_widgets::theme_desktop_dark::*
11    import makepad_draw::shader::std::*
12    
13    Designer = {{Designer}} {
14        has_view: true,
15        flow: Right
16        container: <RoundedView> {
17            draw_bg: {color: #3}
18            width: Fill, height: 400
19            flow: Down, spacing: 10, padding:10
20            <RoundedView>{
21                width: Fill, height: Fit
22                padding:5
23                draw_bg:{color:#5}
24                label = <Label> {text: "HI", draw_text:{color:#f}}
25            }
26            inner = <HookWidget> {}
27        }
28        <Splitter> {
29            align: FromStart(300),
30            a: <View> {
31                outline = <FileTree> {
32                }
33            },
34            b: <CachedScrollXY> {
35                dpi_factor: 1.5
36                draw_bg: {color: #4}
37                width: Fill, height: Fill
38                flow: Down
39                design = <HookWidget> {}
40            },
41        }
42    }
43}
44
45#[allow(dead_code)]
46enum OutlineNode {
47    Global {
48        uid: FileNodeId,
49        name: LiveId,
50        ptr: LivePtr
51    },
52    Component {
53        uid: FileNodeId,
54        name: LiveId,
55        class: LiveId,
56        prop_type: LivePropType,
57        ptr: LivePtr,
58        children: Vec<OutlineNode>
59    }
60}
61
62#[derive(Live)]
63pub struct Designer {
64    #[live] container: Option<LivePtr>,
65    #[rust] outline_nodes: Vec<OutlineNode>,
66    #[rust] components: ComponentMap<LivePtr, (WidgetRef, WidgetRef)>,
67    #[deref] ui: View,
68}
69
70impl LiveHook for Designer {
71    fn before_live_design(cx: &mut Cx) {
72        register_widget!(cx, Designer)
73    }
74    
75    fn after_new_from_doc(&mut self, cx: &mut Cx) {
76        // lets take the doc we need (app_mobile for instance)
77        let live_registry_rc = cx.live_registry.clone();
78        let live_registry = &*live_registry_rc.borrow();
79        let file_id = live_registry.file_name_to_file_id("examples/ironfish/src/app_desktop.rs").unwrap();
80        // now we fetch the unexpanded nodes
81        // and build a list
82        let file = live_registry.file_id_to_file(file_id);
83        let nodes = &file.expanded.nodes;
84        // lets run over the file
85        fn recur_walk(live_registry: &LiveRegistry, base_ptr: LivePtr, mut index: usize, nodes: &[LiveNode], out: &mut Vec<OutlineNode>) -> usize {
86            while index < nodes.len() - 1 {
87                if let LiveValue::Class {class_parent, ..} = &nodes[index].value {
88                    // lets emit a class at our level
89                    let mut children = Vec::new();
90                    let name = nodes[index].id;
91                    let class = live_registry.ptr_to_node(class_parent.unwrap()).id;
92                    let ptr = base_ptr.with_index(index);
93                    index = recur_walk(live_registry, base_ptr, index + 1, nodes, &mut children);
94                    out.insert(0, OutlineNode::Component {
95                        uid: LiveId::unique().into(),
96                        name,
97                        prop_type: nodes[index].origin.prop_type(),
98                        class,
99                        ptr,
100                        children
101                    });
102                }
103                else if nodes[index].value.is_close() {
104                    return index + 1;
105                }
106                else {
107                    index = nodes.skip_node(index);
108                }
109            }
110            index
111        }
112        let base_ptr = live_registry.file_id_index_to_live_ptr(file_id, 0);
113        recur_walk(live_registry, base_ptr, 1, nodes, &mut self.outline_nodes);
114    }
115    // ok now we can iterate our top level components
116    // and instance them
117}
118
119impl Designer {
120    
121    fn draw_design(&mut self, cx: &mut Cx2d) {
122        // alrigh so. lets draw the designs
123        let mut count = 0;
124        for node in &self.outline_nodes {
125            if let OutlineNode::Component {ptr, name, class, ..} = node {
126                count += 1;
127                if count > 5{
128                    break;
129                }
130                let container_ptr = self.container;
131                let (widget, container) = self.components.get_or_insert(cx, *ptr, | cx | {
132                    (
133                        WidgetRef::new_from_ptr(cx, Some(*ptr)),
134                        WidgetRef::new_from_ptr(cx, container_ptr),
135                    )
136                });
137                container.widget(id!(label)).set_text(&format!("{}=<{}>", name, class));
138                // lets draw this thing in a neat little container box with a title bar
139                while let Some(_) = container.draw_widget(cx).hook_widget() {
140                    widget.draw_widget_all(cx);
141                }
142            }
143        }
144        
145    }
146    
147    fn draw_outline(&mut self, cx: &mut Cx2d, outline: &mut FileTree) {
148        fn recur_walk(cx: &mut Cx2d, outline: &mut FileTree, children: &[OutlineNode]) {
149            for child in children {
150                match child {
151                    OutlineNode::Global {..} => {}
152                    OutlineNode::Component {name, children, uid, class, prop_type, ..} => {
153                        if outline.begin_folder(cx, *uid, &if !name.is_unique(){
154                            if let LivePropType::Field = prop_type {
155                                format!("{}: <{}>", name, class)
156                            }
157                            else {
158                                format!("{}=<{}>", name, class)
159                            }
160                        }else {
161                            format!("<{}>", class)
162                        }).is_ok() {
163                            recur_walk(cx, outline, children);
164                            outline.end_folder();
165                        }
166                    }
167                }
168            }
169        }
170        recur_walk(cx, outline, &self.outline_nodes);
171    }
172    
173}
174
175impl Widget for Designer {
176    fn handle_widget_event_with(&mut self, cx: &mut Cx, event: &Event, _dispatch_action: &mut dyn FnMut(&mut Cx, WidgetActionItem)) {
177        let _actions = self.ui.handle_widget_event(cx, event);
178        for (component, container) in self.components.values_mut() {
179            component.handle_widget_event(cx, event);
180            container.handle_widget_event(cx, event);
181        }
182    }
183    
184    fn redraw(&mut self, cx: &mut Cx) {
185        self.ui.redraw(cx)
186    }
187    
188    fn draw_walk_widget(&mut self, cx: &mut Cx2d, _walk: Walk) -> WidgetDraw {
189        let outline = self.ui.file_tree(id!(outline));
190        while let Some(next) = self.ui.draw_widget(cx).hook_widget() {
191            if let Some(mut outline) = outline.has_widget(&next).borrow_mut() {
192                self.draw_outline(cx, &mut *outline);
193            }
194            else if next == self.ui.widget(id!(design)) {
195                self.draw_design(cx);
196            }
197        }
198        WidgetDraw::done()
199    }
200}