1use crate::{
2 makepad_derive_widget::*,
3 makepad_draw::*,
4 makepad_platform::studio::*,
5 designer_data::*,
6 turtle_step::*,
7 view::*,
8 widget::*,
9};
10use std::collections::BTreeMap;
11
12live_design!{
13 use link::theme::*;
14 use makepad_draw::shader::std::*;
15 use link::widgets::*;
16
17 pub DesignerViewBase = {{DesignerView}}{
18 }
19
20 pub DesignerContainerBase = {{DesignerContainer}}{
21 }
22
23 pub DesignerContainer = <DesignerContainerBase>{
24 width: 1200,
25 height: 1200,
26 flow: Overlay,
27 clip_x: false,
28 clip_y: false,
29 align: {x:1.0},
30 animator: {
31 select = {
32 default: off
33 off = {
34 from: {all: Forward {duration: 0.1}}
35 apply: {
36 view = {draw_bg:{border_color:#5}}
37 }
38 }
39 on = {
40 from: {all: Snap}
41 apply: {
42 view = {draw_bg:{border_color:#c}}
43 }
44 }
45
46 }
47 }
48 view = <RoundedView>{
49 draw_bg:{
50 color:#4,
51 border_size:2
52 border_color:#5
53 }
54 padding: 10
55 inner = <BareStep>{}
56 }
57
58 widget_label = <RoundedShadowView>{
59 margin: { top: -35., right: 0. }
60 padding: 0.
61 width: Fit, height: Fit,
62 spacing: 0.,
63 align: { x: 1.0, y: 0.0 }
64 flow: Down,
65 clip_x: false, clip_y: false,
66
67 draw_bg: {
68 border_size: 1.0
69 border_color: (THEME_COLOR_BEVEL_OUTSET_1)
70 shadow_color: (THEME_COLOR_D_3)
71 shadow_radius: 5.0,
72 shadow_offset: vec2(0.0, 0.0)
73 border_radius: 2.5
74 color: (THEME_COLOR_FG_APP),
75 }
76
77 label = <Button> {
78 padding: <THEME_MSPACE_2> {}
79 text:"Hello world"
80
81 draw_bg: {
82 instance hover: 0.0
83 instance down: 0.0
84 uniform border_radius: (THEME_CORNER_RADIUS)
85 instance color: (THEME_COLOR_FG_APP)
86 instance color_down: #f00
87 fn pixel(self) -> vec4 {
88 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
89 let grad_top = 5.0;
90 let grad_bot = 2.0;
91 let body = mix(mix(self.color, self.color_down, self.hover), THEME_COLOR_OUTSET_DOWN, self.down);
92
93 let body_transp = vec4(body.xyz, 0.0);
94 let top_gradient = mix(
95 body_transp,
96 mix(THEME_COLOR_BEVEL_OUTSET_1, THEME_COLOR_BEVEL_OUTSET_1_DOWN, self.down),
97 max(0.0, grad_top - sdf.pos.y) / grad_top
98 );
99 let bot_gradient = mix(
100 mix(THEME_COLOR_BEVEL_OUTSET_2, THEME_COLOR_BEVEL_OUTSET_2_DOWN, self.down),
101 top_gradient,
102 clamp((self.rect_size.y - grad_bot - sdf.pos.y - 1.0) / grad_bot, 0.0, 1.0)
103 );
104
105 sdf.box(
106 1.,
107 1.,
108 self.rect_size.x - 2.0,
109 self.rect_size.y - 2.0,
110 self.border_radius
111 )
112 sdf.fill_keep(body)
113
114 sdf.stroke(
115 bot_gradient,
116 THEME_BEVELING
117 )
118
119 return sdf.result
120 }
121 }
122 }
123 }
124 }
125
126 pub DesignerView = <DesignerViewBase>{
127 clear_color: #3
128 draw_outline:{
129 fn pixel(self) -> vec4 {
130 let p = self.pos * self.rect_size;
131 let sdf = Sdf2d::viewport(p)
132 sdf.rect(0., 0., self.rect_size.x, self.rect_size.y);
133
134 let line_width = 0.58;
135 let dash_length = 10;
136 let pos = p.x + p.y;let dash_pattern = fract(pos / dash_length);
138 let alpha = step(dash_pattern, line_width);
139
140 let c = vec4(mix(#c, #555f, alpha))
141
142 sdf.stroke(c, 2.5);
143 return sdf.result;
144 }
146 }
147
148 draw_bg: {
149 texture image: texture2d
150 varying scale: vec2
151 varying shift: vec2
152 fn vertex(self) -> vec4 {
153
154 let dpi = self.dpi_factor;
155 let ceil_size = ceil(self.rect_size * dpi) / dpi
156 let floor_pos = floor(self.rect_pos * dpi) / dpi
157 self.scale = self.rect_size / ceil_size;
158 self.shift = (self.rect_pos - floor_pos) / ceil_size;
159 return self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
160 }
161 fn pixel(self) -> vec4 {
162 return sample2d(self.image, self.pos * self.scale + self.shift);
163 }
164 }
165 container: <DesignerContainer>{
166 }
167 }
168
169}
170
171#[derive(Clone, Debug, DefaultNone)]
172pub enum DesignerViewAction {
173 None,
174 SwapComponents{
175 comp: LiveId,
176 next_comp: LiveId,
177 },
178 Selected{
179 id:LiveId,
180 km:KeyModifiers,
181 tap_count: u32,
182 }
183}
184
185
186struct ContainerData{
187 ptr: LivePtr,
188 component: WidgetRef,
189 container: WidgetRef,
190 rect: Rect
191}
192
193enum Edge{
194 Left,
195 Right,
196 Bottom,
197 Top,
198 Body
199}
200
201impl ContainerData{
202 fn get_edge(&self, rel:DVec2, zoom:f64, pan: DVec2)->Option<Edge>{
203 let cp = rel * zoom + pan;
204 let edge_outer:f64 = 5.0 * zoom ;
205 let edge_inner:f64 = 5.0 * zoom ;
206
207 if cp.x >= self.rect.pos.x - edge_outer &&
208 cp.x <= self.rect.pos.x + edge_inner &&
209 cp.y >= self.rect.pos.y &&
210 cp.y <= self.rect.pos.y + self.rect.size.y{
211 return Some(Edge::Left);
213 }
214 if cp.x >= self.rect.pos.x + self.rect.size.x- edge_outer &&
215 cp.x <= self.rect.pos.x + self.rect.size.x+ edge_inner &&
216 cp.y >= self.rect.pos.y &&
217 cp.y <= self.rect.pos.y + self.rect.size.y{
218 return Some(Edge::Right);
219 }
220 else if cp.y >= self.rect.pos.y - edge_outer &&
221 cp.y <= self.rect.pos.y + edge_inner &&
222 cp.x >= self.rect.pos.x &&
223 cp.x <= self.rect.pos.x + self.rect.size.x{
224 return Some(Edge::Top);
226 }
227 else if cp.y >= self.rect.pos.y + self.rect.size.y- edge_outer &&
228 cp.y <= self.rect.pos.y + self.rect.size.y + edge_inner &&
229 cp.x >= self.rect.pos.x &&
230 cp.x <= self.rect.pos.x + self.rect.size.x{
231 return Some(Edge::Bottom);
233 }
234 else if self.rect.contains(cp){
235 return Some(Edge::Body);
237 }
238 None
239 }
240}
241
242#[derive(Live, Widget, LiveHook)]
243pub struct DesignerContainer {
244 #[deref] view: View
245}
246
247impl Widget for DesignerContainer {
248 fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope){
249 self.view.handle_event(cx, event, scope);
250 }
251
252 fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, _walk: Walk) -> DrawStep {
253 let data = scope.props.get::<ContainerData>().unwrap();
254 let _turtle_step = self.view.turtle_step(id!(inner));
256 self.walk = Walk{
257 abs_pos: Some(data.rect.pos),
258 width: Size::Fixed(data.rect.size.x),
259 height: Size::Fixed(data.rect.size.y),
260 margin: Default::default()
261 };
262 while let Some(_next) = self.view.draw(cx, &mut Scope::empty()).step() {
263 data.component.draw_all(cx, &mut Scope::empty());
264 }
265
266 DrawStep::done()
267 }
268}
269
270#[allow(unused)]
271enum FingerMove{
272 Pan{start_pan: DVec2},
273 DragBody{ptr: LivePtr},
274 DragEdge{edge: Edge, rect:Rect, id: LiveId},
275 DragAll{rects:BTreeMap<LiveId,Rect>},
276 DragSubComponentOrder(SelectedSubcomponent)
277}
278
279#[derive(Live, Widget)]
280pub struct DesignerView {
281 #[walk] walk:Walk,
282 #[area]
283 #[rust] area:Area,
284 #[rust] reapply: bool,
285 #[rust(1.5)] zoom: f64,
286 #[rust] undo_group: u64,
287 #[rust] pan: DVec2,
288 #[live] clear_color: Vec4,
289 #[rust] finger_move: Option<FingerMove>,
290 #[live] container: Option<LivePtr>,
291 #[live] draw_bg: DrawColor,
292 #[live] draw_outline: DrawQuad,
293 #[rust] view_file: Option<LiveId>,
294 #[rust] selected_component: Option<LiveId>,
295 #[rust] selected_subcomponent: Option<SelectedSubcomponent>,
296 #[rust] containers: ComponentMap<LiveId, ContainerData>,
297 #[redraw] #[rust(DrawList2d::new(cx))] draw_list: DrawList2d,
298 #[rust(Pass::new(cx))] pass: Pass,
299 #[rust] color_texture: Option<Texture>,
300}
301
302#[derive(Clone, Debug)]
303struct SelectedSubcomponent{
304 parent:Option<WidgetRef>,
305 component:WidgetRef
306}
307
308impl LiveHook for DesignerView {
309fn after_update_from_doc(&mut self, _cx: &mut Cx){
310 self.reapply = true;
312 }
313}
314
315impl DesignerView{
316 fn draw_container(&mut self, cx:&mut Cx2d, id:LiveId, ptr: LivePtr, name:&str, to_widget: &mut DesignerDataToWidget){
317
318 let rect = if let Some(v) = to_widget.positions.iter().find(|v| v.id == id){
319 rect(v.left, v.top, v.width, v.height)
320 }
321 else{
322 to_widget.positions.push(DesignerComponentPosition{
323 id,
324 left: 50.0,
325 top: 50.0,
326 width: 200.0,
327 height: 200.00
328 });
329 return self.draw_container(cx, id, ptr, name, to_widget);
330 };
331 let container_ptr = self.container.unwrap();
334 let mut is_new = false;
335
336 let mut scope = Scope::with_data(to_widget);
337 let cd = self.containers.get_or_insert(cx, id, | cx | {
338
339 is_new = true;
340 ContainerData{
341 ptr,
342 component :WidgetRef::new_from_ptr_with_scope(cx, Some(ptr), &mut scope),
343 container: WidgetRef::new_from_ptr_with_scope(cx, Some(container_ptr), &mut scope),
344 rect
345 }
346 });
347 cd.rect = rect;
348 cd.ptr = ptr;
349 if self.reapply{
351 cd.component.update_from_ptr_with_scope(cx, Some(ptr), &mut scope);
352 cd.container.update_from_ptr_with_scope(cx, Some(container_ptr), &mut scope);
353 }
355 if self.reapply || is_new{
356 cd.container.apply_over(cx, live!{
357 widget_label = { label = {text:(name)}}
358 });
359 }
361 cd.container.draw_all(cx, &mut Scope::with_props(cd))
363 }
364
365 fn select_component(&mut self, cx:&mut Cx, what_id:Option<LiveId>){
366 self.redraw(cx);
378 self.selected_component = what_id;
379 }
380
381 fn sync_zoom_pan(&self, _cx:&mut Cx){
382 Cx::send_studio_message(AppToStudio::DesignerZoomPan(
383 DesignerZoomPan{
384 zoom: self.zoom,
385 pan_x: self.pan.x,
386 pan_y: self.pan.y
387 }
388 ));
389 }
390
391 fn get_component_rect(&self, cx:&mut Cx, comp:&WidgetRef)->Rect{
392 let mut rect = self.area().rect(cx);
393 let cr = comp.area().rect(cx);
394 rect.pos += (cr.pos - self.pan)/self.zoom;
395 rect.size = cr.size;
396 rect.size /= self.zoom;
397 rect
398 }
399
400 fn update_rect(&mut self, cx:&mut Cx, id: LiveId, rect:Rect, pos:&mut Vec<DesignerComponentPosition>){
401 if let Some(container) = self.containers.get_mut(&id){
402 container.container.redraw(cx);
403 container.rect = rect;
404 if let Some(v) = pos.iter_mut().find(|v| v.id == id){
406 v.left = rect.pos.x;
407 v.top = rect.pos.y;
408 v.width = rect.size.x;
409 v.height = rect.size.y;
410 Cx::send_studio_message(AppToStudio::DesignerComponentMoved(v.clone()));
412 }
413 }
443 }
444}
445
446impl Widget for DesignerView {
447 fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope){
448 let uid = self.widget_uid();
449 let data = scope.data.get_mut::<DesignerData>().unwrap();
453 match event.hits(cx, self.area) {
454 Hit::FingerHoverOver(fe) =>{
455 self.selected_subcomponent = None;
461 for (_id, cd) in self.containers.iter(){
462 let abs = (fe.abs - fe.rect.pos) * self.zoom + self.pan;
465 let actions = cx.capture_actions(|cx|{
467 cd.component.handle_event(cx, &Event::DesignerPick(DesignerPickEvent{
468 abs: abs
469 }), &mut Scope::empty())
470 });
471 for action in actions{
472 if let Some(action) = action.as_widget_action(){
473 match action.cast(){
474 WidgetDesignAction::PickedBody=>{
475 let mut cs = action.widgets.iter();
478 let mut component = cs.next();
483 let mut parent = cs.next();
484 while parent.is_some(){
485 let p = parent.unwrap();
486 if let Some(p) = p.as_view().borrow(){
487 if p.child_count()>1{
488 break;
489 }
490 }
491 component = parent;
492 parent = cs.next();
493 }
494 if let Some(id) = data.find_component_by_widget_ref(component.as_ref().unwrap()){
496 cx.widget_action(uid, &scope.path, DesignerViewAction::Selected{
497 id,
498 tap_count: 1 ,
499 km:fe.modifiers
500 });
501 }
502
503 self.selected_subcomponent = Some(
504 SelectedSubcomponent{
505 component: component.unwrap().clone(),
506 parent: parent.cloned(),
507 }
508 );
509 break
513 }
514 _=>()
515 }
516 }
517 }
518 self.draw_list.redraw(cx);
519 }
520
521 let mut cursor = None;
522 for cd in self.containers.values(){
523 match cd.get_edge(fe.abs - fe.rect.pos, self.zoom, self.pan){
524 Some(edge)=> {
525 cursor = Some(match edge{
526 Edge::Left|Edge::Right=>MouseCursor::EwResize,
527 Edge::Top|Edge::Bottom=>MouseCursor::NsResize,
528 Edge::Body=>MouseCursor::Move
529 });
530 break;
531 }
532 None=>{}
533 }
534 }
535 if let Some(cursor) = cursor{
536 cx.set_cursor(cursor);
537 }
538 else{
539 cx.set_cursor(MouseCursor::Default);
540 }
541 }
542 Hit::FingerHoverOut(_fh)=>{
543 }
544 Hit::FingerDown(fe) => {
545 self.undo_group += 1;
546 if !fe.modifiers.shift{
547 if fe.modifiers.control && fe.modifiers.alt{
548 let mut rects = BTreeMap::new();
549 for (id, cd) in self.containers.iter(){
550 rects.insert(*id, cd.rect);
551 }
552 self.finger_move = Some(FingerMove::DragAll{rects})
553 }
554 else{
555 if fe.modifiers.logo || self.selected_subcomponent.is_none(){
556 for (id, cd) in self.containers.iter(){
557 match cd.get_edge(fe.abs -fe.rect.pos, self.zoom, self.pan){
558 Some(edge)=>{
559 self.finger_move = Some(FingerMove::DragEdge{
560 rect: cd.rect,
561 id: *id,
562 edge
563 });
564 cx.widget_action(uid, &scope.path, DesignerViewAction::Selected{
566 id:*id,
567 tap_count: fe.tap_count ,
568 km:fe.modifiers
569 });
570 self.select_component(cx, Some(*id));
573 break;
574 }
575 None=>()
576 }
577 }
578 }
579 else{
580 if let Some(sc) = &self.selected_subcomponent{
581 self.finger_move = Some(FingerMove::DragSubComponentOrder(sc.clone()));
582 }
583 }
584 }
585 }
586
587 if self.finger_move.is_none(){
588 self.finger_move = Some(FingerMove::Pan{
589 start_pan: self.pan
590 });
591 }
592 },
593 Hit::KeyDown(_k)=>{
594 }
595 Hit::FingerScroll(fs)=>{
596 let last_zoom = self.zoom;
597
598 if fs.scroll.y < 0.0{
599 let step = (-fs.scroll.y).min(200.0) / 500.0;
600 self.zoom *= 1.0 - step;
601 }
602 else{
603 let step = (fs.scroll.y).min(200.0) / 500.0;
604 self.zoom *= 1.0 + step;
605 }
606 self.zoom = self.zoom.max(0.01).min(10.0);
607
608 let pan1 = (fs.abs - fs.rect.pos) * last_zoom;
610 let pan2 = (fs.abs - fs.rect.pos) * self.zoom;
611 self.pan += pan1 - pan2;
614 self.sync_zoom_pan(cx);
615 self.redraw(cx);
616 }
617 Hit::FingerMove(fe) => {
618 match self.finger_move.as_ref().unwrap(){
619 FingerMove::DragSubComponentOrder(cs)=>{
620 if let Some(parent) = &cs.parent{
624 let vw = parent.as_view();
625
626 if let Some(mut vw) = vw.borrow_mut(){
627
628 let index = vw.child_index(&cs.component).unwrap();
630 let mut reorder:Option<isize> = None;
631 if let Some(next_child) = vw.child_at_index(index+1){
632 let rect = self.get_component_rect(cx, &next_child);
633 if let Flow::Down = vw.layout.flow{
634 if fe.abs.y > rect.pos.y + 0.6* rect.size.y{
635 reorder = Some(1);
636 }
637 }
638 else{
639 if fe.abs.x > rect.pos.x + 0.6* rect.size.x{
640 reorder = Some(1);
641 }
642 }
643 }
644 if index > 0{
645 if let Some(prev_child) = vw.child_at_index(index-1){
646 let rect = self.get_component_rect(cx, &prev_child);
647 if let Flow::Down = vw.layout.flow{
648 if fe.abs.y < rect.pos.y + 0.6 *rect.size.y{
649 reorder = Some(-1);
650 }
651 }else{
652 if fe.abs.x < rect.pos.x + 0.6 *rect.size.x{
653 reorder = Some(-1);
654 }
655 }
656 }
657 }
658 if let Some(dir) = reorder{
659 let next_index = (index as isize + dir) as usize;
660 if !data.pending_revision{
661 let c1 = vw.child_at_index(index).unwrap();
662 if let Some(comp) = data.find_component_by_widget_ref(c1){
665 let c2 = vw.child_at_index(next_index).unwrap();
666 let next_comp = data.find_component_by_widget_ref(c2).unwrap();
667 cx.widget_action(uid, &scope.path, DesignerViewAction::SwapComponents{
668 comp,
669 next_comp
670 });
671 }
672 else{
673 }
675 }
676 else{
677 }
679 }
680 }
681 vw.redraw(cx);
682 }
683 }
684 FingerMove::Pan{start_pan} =>{
685 self.pan= *start_pan - (fe.abs - fe.abs_start) * self.zoom;
686 self.sync_zoom_pan(cx);
687 self.redraw(cx);
688 }
689 FingerMove::DragAll{rects}=>{
690 let delta = (fe.abs - fe.abs_start)* self.zoom;
691 let rects = rects.clone();
692 for (id, rect) in rects{
693 let r = Rect{
694 pos: rect.pos + delta,
695 size: rect.size
696 };
697 self.update_rect(cx, id, r, &mut data.to_widget.positions);
698 }
699 }
700 FingerMove::DragEdge{edge, rect, id}=>{
701 let delta = (fe.abs - fe.abs_start)* self.zoom;
702 let rect = match edge{
703 Edge::Left=>Rect{
704 pos:dvec2(rect.pos.x + delta.x, rect.pos.y),
705 size:dvec2(rect.size.x - delta.x, rect.size.y)
706 },
707 Edge::Right=>Rect{
708 pos: rect.pos,
709 size: dvec2(rect.size.x + delta.x, rect.size.y)
710 },
711 Edge::Top=>Rect{
712 pos:dvec2(rect.pos.x, rect.pos.y + delta.y),
713 size:dvec2(rect.size.x, rect.size.y - delta.y)
714 },
715 Edge::Bottom=>Rect{
716 pos: rect.pos,
717 size: dvec2(rect.size.x, rect.size.y + delta.y)
718 },
719 Edge::Body=>Rect{
720 pos: rect.pos + delta,
721 size: rect.size
722 }
723 };
724 self.update_rect(cx, *id, rect, &mut data.to_widget.positions);
725 }
726 FingerMove::DragBody{ptr:_}=>{
727
728 }
729 }
730 }
731 Hit::FingerUp(_) => {
732 self.finger_move = None;
733 }
734 _ => ()
735 }
736 }
737
738 fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, walk: Walk) -> DrawStep {
739
740 if self.color_texture.is_none(){
741 self.color_texture = Some(Texture::new_with_format(
742 cx,
743 TextureFormat::RenderBGRAu8 {
744 size: TextureSize::Auto,
745 initial: true,
746 },
747 ));
748 }
749
750 if cx.will_redraw(&mut self.draw_list, walk) {
751
752 cx.make_child_pass(&self.pass);
753 cx.begin_pass(&self.pass, None);
754 self.pass.clear_color_textures(cx);
755 self.pass.add_color_texture(
756 cx,
757 self.color_texture.as_ref().unwrap(),
758 PassClearColor::ClearWith(self.clear_color),
759 );
760
761 self.draw_list.begin_always(cx);
762 let size = cx.current_pass_size();
763 cx.begin_sized_turtle_no_clip(size, Layout::flow_down());
764
765 let data = scope.data.get_mut::<DesignerData>().unwrap();
766
767 if let Some(view_file) = &self.view_file{
770 match data.node_map.get(view_file){
772 Some(OutlineNode::File{children,..})=>{
773 for child in children{
774 if let Some(OutlineNode::Component{ptr,name,..}) = data.node_map.get(child){
777 if name == "App=<App>"{ if let Some(child) = data.get_node_by_path(*child, "ui:/main_window=/body="){
779 if let Some(OutlineNode::Component{ptr,name,..}) = data.node_map.get(&child){
780 self.draw_container(cx, child, *ptr, name, &mut data.to_widget);
783 }
784 }
785 }
786 else{
787 self.draw_container(cx, *child, *ptr, name, &mut data.to_widget);
789 }
790 }
791 }
792 }
793 _=>()
794 }
795 }
796 self.reapply = false;
797
798 cx.end_pass_sized_turtle_no_clip();
799 self.draw_list.end(cx);
800 cx.end_pass(&self.pass);
801 }
803
804 self.draw_bg.draw_vars.set_texture(0, self.color_texture.as_ref().unwrap());
805 let rect = cx.walk_turtle_with_area(&mut self.area, walk);
806 self.draw_bg.draw_abs(cx, rect);
807 if let Some(cs) = &self.selected_subcomponent{
811 let rect = self.get_component_rect(cx, &cs.component);
812 self.draw_outline.draw_abs(cx, rect);
813 }
814 else if let Some(component) = self.selected_component{
815 if let Some(container) = self.containers.get(&component){
816 let mut rect = rect;
817 rect.pos += (container.rect.pos - self.pan)/self.zoom;
818 rect.size = container.rect.size;
819 rect.size /= self.zoom;
820 self.draw_outline.draw_abs(cx, rect);
821 }
822 }
823 cx.set_pass_area_with_origin(
824 &self.pass,
825 self.area,
826 dvec2(0.0,0.0)
827 );
828 cx.set_pass_shift_scale(&self.pass, self.pan, dvec2(self.zoom,self.zoom));
829
830 DrawStep::done()
831 }
832}
833
834impl DesignerViewRef{
835 pub fn select_component(&self, cx:&mut Cx, comp:Option<LiveId>) {
836 if let Some(mut inner) = self.borrow_mut(){
837 inner.select_component(cx, comp);
838 inner.redraw(cx);
839 }
840 }
841
842 pub fn view_file(&self, cx:&mut Cx, file_id:LiveId){
843 if let Some(mut inner) = self.borrow_mut(){
844 if inner.view_file != Some(file_id){
845 inner.containers.clear();
846 inner.view_file = Some(file_id);
847 inner.redraw(cx);
848 }
849 }
850 }
851
852 pub fn set_zoom_pan (&self, cx:&mut Cx, zp:&DesignerZoomPan){
853 if let Some(mut inner) = self.borrow_mut(){
854 inner.zoom = zp.zoom;
855 inner.pan.x = zp.pan_x;
856 inner.pan.y = zp.pan_y;
857 inner.redraw(cx);
858 }
859 }
860 pub fn selected(&self, actions: &Actions) -> Option<(LiveId,KeyModifiers,u32)> {
869 if let Some(item) = actions.find_widget_action(self.widget_uid()) {
870 if let DesignerViewAction::Selected{id, km, tap_count} = item.cast() {
871 return Some((id, km, tap_count))
872 }
873 }
874 None
875 }
876
877 pub fn swap_components(&self, actions: &Actions) -> Option<(LiveId,LiveId)> {
878 if let Some(item) = actions.find_widget_action(self.widget_uid()) {
879 if let DesignerViewAction::SwapComponents{comp,next_comp} = item.cast() {
880 return Some((comp, next_comp))
881 }
882 }
883 None
884 }
885}