makepad_widgets/
designer.rs

1use crate::{
2    makepad_derive_widget::*,
3    makepad_draw::*,
4    multi_window::*,
5    widget_match_event::*,
6    designer_data::*,
7    designer_view::*,
8    designer_outline_tree::*,
9    widget::*,
10    makepad_platform::studio::*,
11    //makepad_platform::makepad_live_compiler::TextSpan,
12};
13
14live_design!{
15    link designer_real
16    use link::widgets::*;
17    use link::theme::*;
18    use crate::designer_outline::*;
19    use crate::designer_outline_tree::*;
20    use crate::designer_toolbox::*;
21    use crate::designer_view::*;
22    use crate::designer_theme::*;
23        
24    DesignerBase = {{Designer}} {
25    }
26    
27    pub Designer = <DesignerBase>{
28        <Window> {
29            window: { kind_id: 2 }
30            body = <View> {
31                designer_outline = <DesignerOutline> {
32                    flow: Down,
33                    <DockToolbar> {
34                        content = {
35                            margin: {left: (THEME_SPACE_1), right: (THEME_SPACE_1) },
36                            align: { x: 0., y: 0.0 }
37                            spacing: (THEME_SPACE_3)
38                            <Pbold> {
39                                width: Fit,
40                                text: "Filter",
41                                margin: 0.,
42                                padding: <THEME_MSPACE_V_1> {}
43                            }
44                            
45                            <View> {
46                                width: Fit
47                                flow: Right,
48                                spacing: (THEME_SPACE_1)
49                                <CheckBoxCustom> {
50                                    width: 25,
51                                    margin: {left: (THEME_SPACE_1)}
52                                    text: ""
53                                    draw_bg: { check_type: None }
54                                    icon_walk: {width: 13.5 }
55                                    draw_icon: {
56                                        color: (THEME_COLOR_D_3),
57                                        color_active: (STUDIO_PALETTE_2),
58                                        svg_file: dep("crate://self/resources/icons/icon_widget.svg"),
59                                    }
60                                }
61                                <CheckBoxCustom> {
62                                    width: 25,
63                                    text: ""
64                                    draw_bg: { check_type: None }
65                                    icon_walk: {width: 12.}
66                                    draw_icon: {
67                                        color: (THEME_COLOR_D_3),
68                                        color_active: (STUDIO_PALETTE_6),
69                                        svg_file: dep("crate://self/resources/icons/icon_layout.svg"),
70                                    }
71                                }
72                                <CheckBoxCustom> {
73                                    width: 25,
74                                    text: ""
75                                    draw_bg: { check_type: None }
76                                    icon_walk: {width: 10.5}
77                                    draw_icon: {
78                                        color: (THEME_COLOR_D_3),
79                                        color_active: (STUDIO_PALETTE_1),
80                                        svg_file: dep("crate://self/resources/icons/icon_text.svg"),
81                                    }
82                                }
83                                <CheckBoxCustom> {
84                                    width: 25,
85                                    text:""
86                                    draw_bg: { check_type: None }
87                                    icon_walk: {width: 13.}
88                                    draw_icon: {
89                                        color: (THEME_COLOR_D_3),
90                                        color_active: (STUDIO_PALETTE_5),
91                                        svg_file: dep("crate://self/resources/icons/icon_image.svg"),
92                                    }
93                                }
94                            }
95                            <TextInputFlat> {
96                                width: Fill,
97                                empty_text: "Filter",
98                            }
99                        }
100                    }
101                    outline_tree = <DesignerOutlineTree>{
102                        
103                    }
104                }
105            }
106        }
107        <Window>{
108            window:{ kind_id: 1 }
109            body = <View>{
110                flow: Overlay
111                designer_view = <DesignerView> {
112                    width: Fill, height: Fill
113                }
114                toolbox = <DesignerToolbox>{
115                }
116            }
117        }
118    }
119}
120
121#[derive(Live, Widget)]
122pub struct Designer {
123    #[deref] ui: MultiWindow,
124    #[rust] data: DesignerData,
125}
126
127impl LiveHook for Designer {
128    
129    fn before_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]){
130        self.data.update_from_live_registry(cx);
131    }
132    
133    fn after_update_from_doc(&mut self, cx:&mut Cx){
134        //let designer_view = self.ui.designer_view(id!(designer_view));
135        //designer_view.reload_view(cx);
136        let outline_tree = self.ui.designer_outline_tree(id!(outline_tree));
137        outline_tree.redraw(cx);
138        self.data.pending_revision = false;
139    }
140    
141    fn after_new_from_doc(&mut self, _cx:&mut Cx){
142        
143        Cx::send_studio_message(AppToStudio::DesignerStarted);
144    }
145}
146
147impl Designer{
148    fn studio_jump_to_component(&self, cx:&Cx, component:LiveId){
149        if let Some(OutlineNode::Component{ptr,..}) =  self.data.node_map.get(&component){
150            let (file_name,span) = cx.live_registry.borrow().ptr_to_file_name_and_object_span(*ptr);
151            Cx::send_studio_message(AppToStudio::JumpToFile(JumpToFile{
152                file_name,
153                line: span.start.line,
154                column: span.start.column
155            }));
156        }
157    }
158    
159    fn studio_select_component(&self, cx:&Cx, component:LiveId){
160        if let Some(OutlineNode::Component{ptr,..}) =  self.data.node_map.get(&component){
161            let (file_name,span) = cx.live_registry.borrow().ptr_to_file_name_and_object_span(*ptr);
162            println!("{:?}", span);
163            Cx::send_studio_message(AppToStudio::SelectInFile(SelectInFile{
164                file_name,
165                line_start: span.start.line,
166                column_start: span.start.column,
167                line_end: span.end.line,
168                column_end: span.end.column
169            }));
170        }
171    }
172    
173    fn studio_swap_components(&mut self, cx:&Cx, c1:LiveId, c2:LiveId){
174        if self.data.pending_revision{
175            return 
176        }
177        if let Some(OutlineNode::Component{ptr:ptr1,..}) =  self.data.node_map.get(&c1){
178            if let Some(OutlineNode::Component{ptr:ptr2,..}) =  self.data.node_map.get(&c2){
179                let (s1_file_name,s1_span) = cx.live_registry.borrow().ptr_to_file_name_and_object_span(*ptr1);
180                let (s2_file_name,s2_span) = cx.live_registry.borrow().ptr_to_file_name_and_object_span(*ptr2);
181                self.data.pending_revision = true;
182                Cx::send_studio_message(AppToStudio::SwapSelection(SwapSelection{
183                    s1_file_name,
184                    s1_line_start: s1_span.start.line,
185                    s1_column_start: s1_span.start.column,
186                    s1_line_end: s1_span.end.line,
187                    s1_column_end: s1_span.end.column,
188                    s2_file_name,
189                    s2_line_start: s2_span.start.line,
190                    s2_column_start: s2_span.start.column,
191                    s2_line_end: s2_span.end.line,
192                    s2_column_end: s2_span.end.column,
193                }));
194            }
195        }
196    }
197    
198    fn studio_jump_to_file(&self, cx:&Cx, file_id:LiveFileId){
199        let file_name = cx.live_registry.borrow().file_id_to_file(file_id).file_name.clone();
200        Cx::send_studio_message(AppToStudio::JumpToFile(JumpToFile{
201            file_name,
202            line: 0,
203            column: 0
204        }));
205    }
206}
207
208impl WidgetMatchEvent for Designer{
209    fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, _scope: &mut Scope){
210        let outline_tree = self.ui.designer_outline_tree(id!(outline_tree));
211        let designer_view = self.ui.designer_view(id!(designer_view));
212        if let Some((outline_id, km, tap_count)) = designer_view.selected(&actions){
213            // select the right node in the filetree
214            let path_ids = self.data.construct_path_ids(outline_id);
215            outline_tree.select_and_show_node(cx, &path_ids);
216            
217            // if we click with control
218            if km.control || tap_count > 1{
219                self.studio_select_component(cx, outline_id)
220            }
221        }
222        
223        if let Some((c1, c2)) = designer_view.swap_components(&actions){
224            self.studio_swap_components(cx, c1, c2);
225            outline_tree.redraw(cx);
226        }
227        // ok lets see if we have a designerselectfile action
228        for action in actions{
229            if let StudioToApp::DesignerSelectFile{file_name} = action.cast_ref(){
230                let path_ids = DesignerData::path_str_to_path_ids(&file_name);
231                if path_ids.len() > 0{
232                    outline_tree.select_and_show_node(cx, &path_ids);
233                    designer_view.select_component(cx, None);
234                    designer_view.view_file(cx, *path_ids.last().unwrap());
235                }
236            }
237             if let StudioToApp::DesignerLoadState{positions, zoom_pan} = action.cast_ref(){
238                 self.data.to_widget.positions = positions.clone();
239                 designer_view.set_zoom_pan(cx,zoom_pan);
240             }
241        }
242        if let Some((outline_id,km)) = outline_tree.selected(&actions) {
243            // alright we have a folder clicked
244            // lets get a file/line number out of it so we can open it in the code editor.
245            
246            if let Some(node) = self.data.node_map.get(&outline_id){
247                match node{
248                    OutlineNode::File{file_id,..}=>{
249                        let path_ids = self.data.construct_path_ids(outline_id);
250                        let file_name = self.data.path_ids_to_string(&path_ids);
251                        Cx::send_studio_message(AppToStudio::DesignerFileSelected{
252                            file_name
253                        });
254                        if km.control{
255                            self.studio_jump_to_file(cx, *file_id);
256                        }
257                        else if km.alt{
258                            Cx::send_studio_message(AppToStudio::FocusDesign);
259                        }
260                        else{
261                            designer_view.select_component(cx, None);
262                            designer_view.view_file(cx, outline_id);
263                        }        
264                    }
265                    OutlineNode::Component{..}=>{
266                        if km.control{
267                            self.studio_jump_to_component(cx, outline_id)
268                        }
269                        else if km.alt{
270                            Cx::send_studio_message(AppToStudio::FocusDesign);
271                        }
272                        else{
273                            // only select the file 
274                            if let Some(file_id) = self.data.find_file_parent(outline_id){
275                                designer_view.select_component(cx, Some(outline_id));
276                                designer_view.view_file(cx, file_id);
277                            }
278                        }
279                    }
280                    _=>()
281                }
282            }
283        }
284    }
285}
286
287impl Widget for Designer {
288    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope){
289        self.widget_match_event(cx, event, scope);
290        let mut scope = Scope::with_data(&mut self.data);
291        self.ui.handle_event(cx, event, &mut scope);
292    }
293    
294    fn draw_walk(&mut self, cx: &mut Cx2d, _scope:&mut Scope, _walk: Walk) -> DrawStep {
295        let mut scope = Scope::with_data(&mut self.data);
296        self.ui.draw(cx, &mut scope)
297    }
298}