makepad_widgets/
designer_data.rs

1use crate::{
2    makepad_draw::*,
3    widget::*,
4    makepad_platform::studio::DesignerComponentPosition,
5    //makepad_live_compiler::LiveTokenId,
6};
7use std::collections::HashMap;
8use std::fmt::Write;
9
10pub enum OutlineNode{
11    Virtual{
12        name: String,
13        children: SmallVec<[LiveId;4]>
14    },
15    File{
16        file_id: LiveFileId,
17        name: String,
18        children: SmallVec<[LiveId;4]>
19    },
20    Folder{
21        name: String,        
22        children: SmallVec<[LiveId;4]>
23    },
24    Component{
25        name: String,        
26        id: LiveId,
27        class: LiveId,
28        prop_type: LivePropType,
29        //token_id: LiveTokenId,
30        ptr: LivePtr,
31        children: SmallVec<[LiveId;4]>
32    }
33}
34
35impl OutlineNode{
36    fn children(&self)->&[LiveId]{
37        match self{
38            Self::Virtual{children,..}=>children,
39            Self::File{children,..}=>children,
40            Self::Folder{children,..}=>children,
41            Self::Component{children,..}=>children,
42        }
43    }
44    
45    fn name(&self)->&str{
46        match self{
47            Self::Virtual{name,..}=>name,
48            Self::File{name,..}=>name,
49            Self::Folder{name,..}=>name,
50            Self::Component{name,..}=>name,
51        }
52    }
53}
54
55
56#[derive(Default)]
57pub struct DesignerDataToWidget{
58    pub positions: Vec<DesignerComponentPosition>,
59    pub live_ptr_to_widget: HashMap<LivePtr, WidgetRef>, 
60}
61
62impl DesignerDataToWidget{
63    pub fn find_ptr_by_widget_ref(&self, widget:&WidgetRef)->Option<LivePtr>{
64        for (ptr, wref) in &self.live_ptr_to_widget{
65            if wref == widget{
66                return Some(*ptr)
67            }
68        }
69        None
70    }
71}
72
73#[derive(Default)]
74pub struct DesignerData{
75    pub pending_revision: bool,
76    pub root: LiveId,
77    pub node_map: HashMap<LiveId, OutlineNode>,
78    pub selected: Option<LiveId>,
79    pub to_widget: DesignerDataToWidget,
80}
81
82impl DesignerData{
83    pub fn get_node_by_path(&self, root:LiveId, path:&str)->Option<LiveId>{
84        let mut current = root;
85        let mut split = path.split("/");
86        'outer: while let Some(node) = self.node_map.get(&current){
87            if let Some(next_name) = split.next(){
88                for child in node.children(){
89                    let node = self.node_map.get(&child).unwrap();
90                    if node.name().starts_with(next_name){
91                        current = *child;
92                        continue 'outer;
93                    }
94                }
95                return None
96            }
97            else{
98                return Some(current)
99            }
100        }
101        None
102    }
103    
104    pub fn update_from_live_registry(&mut self, cx:&mut Cx){
105        self.node_map.clear();
106        self.to_widget.live_ptr_to_widget.clear();
107        let root_uid = live_id!(designer_root).into();
108        self.root = root_uid;
109        self.node_map.insert(root_uid, OutlineNode::Virtual{
110            name: "root".into(),
111            children: SmallVec::new()
112        });
113                
114        // lets take the doc we need (app_mobile for instance)
115        let live_registry_rc = cx.live_registry.clone();
116        let live_registry = &*live_registry_rc.borrow();
117        let main_module_lti = live_registry.main_module.as_ref().unwrap().clone();
118        let mut path_hash = String::new();
119        for (file_path, file_id) in live_registry.file_ids(){
120            // now we fetch the unexpanded nodes
121            // and build a list
122            let file = live_registry.file_id_to_file(*file_id);
123            let nodes = &file.expanded.nodes;
124            // lets run over the file
125                        
126            fn recur_walk_components(
127                main_module_lti:&LiveTypeInfo, 
128                live_registry: &LiveRegistry, 
129                hash_id:LiveId, 
130                base_ptr: LivePtr, 
131                mut index: usize, 
132                nodes: &[LiveNode],
133                map: &mut HashMap<LiveId, OutlineNode>,
134                parent_children: &mut SmallVec<[LiveId;4]>) -> usize {
135                                        
136                while index < nodes.len().saturating_sub(1) { 
137                    if let LiveValue::Class {live_type, class_parent, ..} = &nodes[index].value {
138                        // lets check if its a widget
139                        let wr = live_registry.components.get::<WidgetRegistry>();
140                        if main_module_lti.live_type == *live_type || wr.map.get(live_type).is_some(){
141                                                            
142                            // lets emit a class at our level
143                            let id = nodes[index].id;
144                            let class = live_registry.ptr_to_node(*class_parent).id;
145                            
146                            // skip the fluff
147                            if class == live_id!(Designer) || class == live_id!(PerformanceView) || 
148                            id == live_id!(caption_bar) && class == live_id!(SolidView) ||
149                            id == live_id!(window_menu) && class == live_id!(WindowMenu){
150                                index = nodes.skip_node(index);
151                                continue;
152                            }
153                            
154                            let ptr = base_ptr.with_index(index);
155                            let prop_type =  nodes[index].origin.prop_type();
156                            //let token_id = nodes[index].origin.token_id();
157                            let uid = hash_id.bytes_append(&id.0.to_be_bytes());
158                            let mut children = SmallVec::new();
159                                                            
160                            index = recur_walk_components(
161                                main_module_lti, 
162                                live_registry, 
163                                uid, 
164                                base_ptr, 
165                                index + 1, 
166                                nodes, 
167                                map, 
168                                &mut children
169                            );
170                                                            
171                            let uid = uid.into();
172                            parent_children.push(uid);
173                            
174                            let mut name = String::new();
175                            
176                            if !id.is_unique(){
177                                if let LivePropType::Field = prop_type {
178                                    write!(name, "{}: <{}>", id, class).unwrap();
179                                }
180                                else {
181                                    write!(name, "{}=<{}>", id, class).unwrap();
182                                }
183                            }
184                            else {
185                                write!(name, "<{}>", class).unwrap();
186                            }
187                                
188                            map.insert(uid, OutlineNode::Component {
189                                id,
190                                name,
191                                prop_type,
192                                class,
193                                ptr,
194                                children
195                            });
196                        
197                            // find all the components that start with app_ and make sure the folder is visible
198                            
199                        }
200                        else{
201                            //   log!("NOT A WIDGET {}", nodes[index].id);
202                            index = nodes.skip_node(index);
203                        }
204                    }
205                    else if nodes[index].value.is_close() {
206                        return index + 1;
207                    }
208                    else {
209                        index = nodes.skip_node(index);
210                    }
211                }
212                index
213            }
214            // alright lets iterate over the files
215                            
216            let base_ptr = live_registry.file_id_index_to_live_ptr(*file_id, 0);
217                            
218            path_hash.clear();
219                            
220
221            let base_id = LiveId(0).bytes_append(&file_id.0.to_be_bytes());
222                            
223            let mut children_out = SmallVec::new();
224            recur_walk_components(
225                &main_module_lti, 
226                live_registry, 
227                base_id, 
228                base_ptr, 
229                1, 
230                nodes,
231                &mut self.node_map, 
232                &mut children_out
233            );
234            if children_out.len() == 0{
235                continue
236            }
237            let parent_id = path_split(self.root, &mut path_hash, file_path, &mut self.node_map, *file_id);
238            if let Some(OutlineNode::File{children,..}) = self.node_map.get_mut(&parent_id){
239                *children = children_out;
240            }
241                            
242            fn path_split<'a>(parent_id: LiveId, path_hash:&mut String, name:&str, map:&mut HashMap<LiveId, OutlineNode>, file_id:LiveFileId)->LiveId{
243                if let Some((folder,rest)) = name.split_once("/"){
244                                            
245                    if folder == "src"{ // flatten this
246                        return path_split(parent_id, path_hash, rest, map, file_id)
247                    }
248                    
249                    path_hash.push_str(folder);
250                    path_hash.push_str("/");
251                                                                                    
252                    let what_uid =  LiveId::from_str(&path_hash).into();
253                                            
254                    // add node to pareht
255                    if let Some(OutlineNode::Folder{children, ..}) | Some(OutlineNode::Virtual{children, ..})= map.get_mut(&parent_id){
256                        if !children.contains(&what_uid){
257                            children.push(what_uid);
258                        }
259                    }
260                                            
261                    if map.get_mut(&what_uid).is_some(){
262                        return path_split(what_uid, path_hash, rest, map, file_id)
263                    }
264                    
265                    map.insert(what_uid, OutlineNode::Folder{
266                        name: folder.to_string(),
267                        children: SmallVec::new()
268                    });
269                                            
270                    return path_split(what_uid, path_hash, rest, map, file_id)
271                }
272                else{ // we're a file
273                    path_hash.push_str(name);
274                    path_hash.push_str("/");
275                    let what_uid =  LiveId::from_str(&path_hash).into();
276                    if let Some(OutlineNode::Folder{children, ..}) | Some(OutlineNode::Virtual{children, ..})= map.get_mut(&parent_id){
277                        if !children.contains(&what_uid){
278                            children.push(what_uid);
279                        }
280                    }
281                    map.insert(what_uid, OutlineNode::File{
282                        file_id,
283                        name: name.to_string(),
284                        children: SmallVec::new()
285                    });
286                                            
287                    return what_uid
288                }
289            }
290        }
291    }
292    
293    pub fn swap_child_refs(&mut self,parent:&WidgetRef, index:usize, index2:usize){
294        if let Some(id) = self.find_component_by_widget_ref(parent){
295            if let Some(OutlineNode::Component{children,..}) = self.node_map.get_mut(&id){
296                children.swap(index, index2);
297            }
298        }
299    }
300        
301    pub fn find_component_by_ptr(&mut self, find_ptr:LivePtr)->Option<LiveId>{
302        for (node_id, node) in &self.node_map{
303            if let OutlineNode::Component{ptr,..} = node{
304                if *ptr == find_ptr{
305                    return Some(*node_id)
306                }
307            }
308        }
309        None
310    }
311    
312        
313    pub fn find_component_by_widget_ref(&mut self, wref:&WidgetRef)->Option<LiveId>{
314        if let Some(ptr) = self.to_widget.find_ptr_by_widget_ref(wref){
315            return self.find_component_by_ptr(ptr);
316        }
317        None
318    }
319    
320    pub fn construct_path_ids(&self, find_node:LiveId)->Vec<LiveId>{
321        let mut result = Vec::new();
322        result.push(find_node);
323        let mut iter = find_node;
324        while let Some(parent) = self.find_parent(iter){
325            result.insert(0, parent);
326            iter = parent;
327        }
328        result
329    }
330    
331    pub fn path_ids_to_string(&self, path:&[LiveId])->String{
332        let mut path_str= String::new();
333        for node_id in path{
334            if let Some(node) = self.node_map.get(&node_id){
335                match node{
336                    OutlineNode::Folder{name,..} | OutlineNode::File{name,..}=>{
337                        path_str.push_str(name);
338                        path_str.push_str("/");
339                    }
340                    _=>()
341                }
342            }
343        }
344        path_str
345    }
346    
347    pub fn path_str_to_path_ids(path:&str)->Vec<LiveId>{
348        let mut path_id = Vec::new();
349        for (idx,_) in path.match_indices('/'){
350            let slice = &path[0..idx+1];
351            let hash =  LiveId::from_str(slice).into();
352            path_id.push(hash);
353        }
354        path_id
355    }
356
357    pub fn find_parent(&self, find_node:LiveId)->Option<LiveId>{
358        for (node_id, node) in &self.node_map{
359            match node{
360                OutlineNode::Component{children,..} | OutlineNode::Virtual{children,..} | OutlineNode::File{children,..} | OutlineNode::Folder{children, ..} =>{
361                    if children.iter().position(|v| *v == find_node).is_some(){
362                        return Some(*node_id)
363                    }
364                }
365            }
366        }
367        None
368    }
369    
370    pub fn find_file_parent(&mut self, find_node:LiveId)->Option<LiveId>{
371        let mut iter = find_node;
372        while let Some(parent) = self.find_parent(iter){
373            if let Some(OutlineNode::File{..}) = self.node_map.get(&parent){
374                return Some(parent);
375            }
376            iter = parent;
377        }
378        None
379    }
380    
381    pub fn _remove_child(&mut self, find_node:LiveId){
382        for node in &mut self.node_map.values_mut(){
383            match node{
384                OutlineNode::Component{children,..} | OutlineNode::Virtual{children,..} | OutlineNode::File{children,..} | OutlineNode::Folder{children, ..} =>{
385                    if let Some(i) = children.iter().position(|v| *v == find_node){
386                        children.remove(i);
387                        break;
388                    }
389                }
390            }
391        }
392    }
393    
394    pub fn _find_component_by_path(&self, path:&[LiveId])->Option<LiveId>{
395        fn get_node(node:LiveId, path:&[LiveId],  map:&HashMap<LiveId, OutlineNode>)->Option<LiveId>{
396            match map.get(&node).as_ref(){
397                Some(OutlineNode::Virtual{children,..}) |
398                Some(OutlineNode::Folder{children,..}) |
399                Some(OutlineNode::File{children,..}) =>{
400                    for child in children{
401                        if let Some(v) = get_node(*child, path, map){
402                            return Some(v)
403                        }
404                    }
405                }
406                Some(OutlineNode::Component{children, id, ..}) => {
407                    if *id == path[0]{
408                        if path.len()>1{
409                            for child in children{
410                                 if let Some(v) = get_node(*child, &path[1..], map){
411                                     return Some(v)
412                                 }
413                            }
414                        }
415                        else{
416                            return Some(node)
417                        }
418                    }
419                    for child in children{
420                        if let Some(v) = get_node(*child, path, map){
421                            return Some(v)
422                        }
423                    }
424                }
425                _=>()
426            }
427            None
428        }
429        get_node(self.root, path, &self.node_map)
430    }
431}