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 };
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 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 let path_ids = self.data.construct_path_ids(outline_id);
215 outline_tree.select_and_show_node(cx, &path_ids);
216
217 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 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 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 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}