makepad_widgets/
window.rs

1use crate::{
2    makepad_derive_widget::*,
3    debug_view::DebugView,
4    performance_view::PerformanceView,
5    makepad_draw::*,
6    nav_control::NavControl,
7    desktop_button::*,
8    view::*,
9    widget::*,
10};
11
12live_design!{
13    link widgets;
14    use link::widgets::*;
15    use link::theme::*;
16    use makepad_draw::shader::std::*;
17    
18    pub WindowBase = {{Window}} {demo:false}
19    pub Window = <WindowBase> {
20        pass: { clear_color: (THEME_COLOR_BG_APP) }
21        flow: Down
22        nav_control: <NavControl> {}
23        caption_bar = <SolidView> {
24            visible: false,
25            
26            flow: Right
27            
28            draw_bg: {color: (THEME_COLOR_APP_CAPTION_BAR)}
29            height: 27,
30            caption_label = <View> {
31                width: Fill, height: Fill,
32                align: {x: 0.5, y: 0.5},
33                label = <Label> {text: "Makepad", margin: {left: 100}}
34            }
35            windows_buttons = <View> {
36                visible: false,
37                width: Fit, height: Fit,
38                min = <DesktopButton> {draw_bg: {button_type: WindowsMin}}
39                max = <DesktopButton> {draw_bg: {button_type: WindowsMax}}
40                close = <DesktopButton> {draw_bg: {button_type: WindowsClose}}
41            }
42            web_fullscreen = <View> {
43                visible: false,
44                width: Fit, height: Fit,
45                fullscreen = <DesktopButton> {draw_bg: {button_type: Fullscreen}}
46            }
47            web_xr = <View> {
48                visible: false,
49                width: Fit, height: Fit,
50                xr_on = <DesktopButton> {draw_bg: {button_type: XRMode}}
51            }
52        }
53        
54        window_menu = <WindowMenu> {
55            main = Main{items:[app]}
56            app = Sub { name:"Makepad", items:[quit] }
57            quit = Item {
58                name:"Quit",
59                shift: false,
60                key: KeyQ,
61                enabled: true
62            }
63        }
64        body = <KeyboardView> {
65            width: Fill, height: Fill,
66            keyboard_min_shift: 30,
67        }
68        
69        cursor: Default
70        mouse_cursor_size: vec2(20, 20),
71        draw_cursor: {
72            uniform border_size: 1.5
73            uniform color: (THEME_COLOR_CURSOR)
74            uniform border_color: (THEME_COLOR_CURSOR_BORDER)
75            
76            fn get_color(self) -> vec4 {
77                return self.color
78            }
79            
80            fn get_border_color(self) -> vec4 {
81                return self.border_color
82            }
83            
84            fn pixel(self) -> vec4 {
85                let sdf = Sdf2d::viewport(self.pos * self.rect_size)
86                sdf.move_to(1.0, 1.0);
87                sdf.line_to(self.rect_size.x - 1.0, self.rect_size.y * 0.5)
88                sdf.line_to(self.rect_size.x * 0.5, self.rect_size.y - 1.0)
89                sdf.close_path();
90                sdf.fill_keep(self.get_color())
91                if self.border_size > 0.0 {
92                    sdf.stroke(self.get_border_color(), self.border_size)
93                }
94                return sdf.result
95            }
96        }
97        window: {
98            inner_size: vec2(1024, 768)
99        }
100    }
101    
102}
103
104#[derive(Live, Widget)]
105pub struct Window {
106    //#[rust] caption_size: DVec2,
107    #[live] last_mouse_pos: DVec2,
108    #[live] mouse_cursor_size: DVec2,
109    #[live] demo: bool,
110    #[rust] demo_next_frame: NextFrame,
111    #[live] cursor_draw_list: DrawList2d,
112    #[live] draw_cursor: DrawQuad,
113    #[live] debug_view: DebugView,
114    #[live] performance_view: PerformanceView,
115    #[live] nav_control: NavControl,
116    #[live] window: WindowHandle,
117    #[live] stdin_size: DrawColor,
118    #[rust(Overlay::new(cx))] overlay: Overlay,
119    #[rust(DrawList2d::new(cx))] main_draw_list: DrawList2d,
120    #[live] pass: Pass,
121    #[rust(Texture::new(cx))] depth_texture: Texture,
122    #[live] hide_caption_on_fullscreen: bool, 
123    #[live] show_performance_view: bool,
124    #[rust(Mat4::nonuniform_scaled_translation(vec3(0.0004,-0.0004,-0.0004),vec3(-0.25,0.25,-0.5)))] xr_view_matrix: Mat4,
125    #[deref] view: View,
126    // #[rust(WindowMenu::new(cx))] _window_menu: WindowMenu,
127    /*#[rust(Menu::main(vec![
128        Menu::sub("App", vec![
129            //Menu::item("Quit App", Cx::command_quit()),
130        ]),
131    ]))]*/
132    
133    //#[live] _default_menu: Menu,
134    
135    //#[rust] last_menu: Option<Menu>,
136    
137    // testing
138    #[rust] draw_state: DrawStateWrap<DrawState>,
139    
140}
141
142#[derive(Clone)]
143enum DrawState {
144    Drawing,
145}
146
147impl LiveHook for Window {
148    fn after_new_from_doc(&mut self, cx: &mut Cx) {
149        self.window.set_pass(cx, &self.pass);
150        //self.pass.set_window_clear_color(cx, vec4(0.0,0.0,0.0,0.0));
151        self.depth_texture = Texture::new_with_format(cx, TextureFormat::DepthD32{
152            size:TextureSize::Auto,
153            initial: true,
154        });
155        self.pass.set_depth_texture(cx, &self.depth_texture, PassClearDepth::ClearWith(1.0));
156        // check if we are ar/vr capable
157        if cx.xr_capabilities().vr_supported {
158            // lets show a VR button
159            self.view(id!(web_xr)).set_visible(cx, true);
160            log!("VR IS SUPPORTED");
161        }
162       
163    }
164    
165    fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
166        if self.demo{
167            self.demo_next_frame = cx.new_next_frame();
168        }
169        match cx.os_type() {
170            OsType::Windows => {
171                if !cx.in_makepad_studio(){
172                    self.view(id!(caption_bar)).set_visible(cx, true);
173                    self.view(id!(windows_buttons)).set_visible(cx, true);
174                }
175            }
176            OsType::Macos => {
177                //self.view(id!(caption_bar)).set_visible(true);
178                //self.view(id!(windows_buttons)).set_visible(true);
179                //self.view(id!(caption_bar)).set_visible(true);
180                //self.view(id!(windows_buttons)).set_visible(true);
181                /*if std::env::args().find(|v| v == "--message-format=json").is_some(){
182                    self.apply_over(cx, live!{
183                        caption_bar={draw_bg:{color:(vec4(0.,0.2,0.2,1.0))}}
184                    }); 
185                }*/
186                                
187                //draw_bg: {color: (THEME_COLOR_BG_APP)}  
188                // self.frame.get_view(id!(caption_bar)).set_visible(false);
189            }
190            OsType::LinuxWindow(_) |
191            OsType::LinuxDirect |
192            OsType::Android(_) => {
193                //self.frame.get_view(id!(caption_bar)).set_visible(false);
194            }
195            OsType::Web(_) => {
196                // self.frame.get_view(id!(caption_bar)).set_visible(false);
197            }
198            _ => ()
199        }
200    }
201}
202
203#[derive(Clone, Debug, DefaultNone)]
204pub enum WindowAction {
205    EventForOtherWindow,
206    WindowClosed,
207    WindowGeomChange(WindowGeomChangeEvent),
208    None
209}
210
211impl Window {
212
213    pub fn begin(&mut self, cx: &mut Cx2d) -> Redrawing {
214
215        if !cx.will_redraw(&mut self.main_draw_list, Walk::default()) {
216            return Redrawing::no()
217        }
218        
219        cx.begin_pass(&self.pass, None);
220
221        self.main_draw_list.begin_always(cx);
222        
223        let size = cx.current_pass_size();
224        cx.begin_sized_turtle(size, Layout::flow_down());
225        
226        self.overlay.begin(cx);
227        
228        Redrawing::yes()
229    }
230    
231    pub fn end(&mut self, cx: &mut Cx2d) {
232        //while self.frame.draw_widget_continue(cx).is_not_done() {}
233        self.debug_view.draw(cx);
234        
235        // lets draw our cursor
236        if let OsType::LinuxDirect = cx.os_type() {
237            self.cursor_draw_list.begin_overlay_last(cx);
238            self.draw_cursor.draw_abs(cx, Rect {
239                pos: self.last_mouse_pos,
240                size: self.mouse_cursor_size
241            });
242            self.cursor_draw_list.end(cx);
243        }
244        
245        self.overlay.end(cx);
246        // lets get te pass size
247        fn encode_size(x: f64)->Vec4{
248            let x = x as usize;
249            let r = ((x >> 8)&0xff) as f32 / 255.0;
250            let b = ((x >> 0)&0xff) as f32 / 255.0;
251            vec4(r,0.0,b,1.0)
252        }
253        
254        // if we are running in stdin mode, write a tracking pixel with the pass size
255        if cx.in_makepad_studio(){
256            let df = cx.current_dpi_factor();
257            let size = self.pass.size(cx).unwrap() * df;
258            self.stdin_size.color = encode_size(size.x);
259            self.stdin_size.draw_abs(cx, Rect{pos:dvec2(0.0,0.0),size:dvec2(1.0/df,1.0/df)});
260            self.stdin_size.color = encode_size(size.y);
261            self.stdin_size.draw_abs(cx, Rect{pos:dvec2(1.0/df,0.0),size:dvec2(1.0/df,1.0/df)});
262        }
263
264        if self.show_performance_view {
265            self.performance_view.draw_all(cx, &mut Scope::empty());
266        }
267
268        cx.end_pass_sized_turtle();
269        
270        self.main_draw_list.end(cx);
271        cx.end_pass(&self.pass);
272    }
273    pub fn resize(&self, cx: &mut Cx, size: DVec2) {
274        self.window.resize(cx, size);
275    }
276    pub fn reposition(&self, cx: &mut Cx, size: DVec2) {
277        self.window.reposition(cx, size);
278    }
279    pub fn set_fullscreen(&mut self, cx: &mut Cx) {
280        self.window.fullscreen(cx);
281    }
282    
283}
284
285impl WindowRef{
286    pub fn get_inner_size(&self, cx:&Cx)->DVec2{
287        if let Some(inner) = self.borrow(){
288            inner.window.get_inner_size(cx)
289        }
290        else{
291            dvec2(0.0,0.0)
292        }
293    }
294
295    pub fn get_position(&self, cx:&Cx)->DVec2{
296        if let Some(inner) = self.borrow(){
297            inner.window.get_position(cx)
298        }
299        else{
300            dvec2(0.0,0.0)
301        }
302    }
303    pub fn is_fullscreen(&self, cx:&Cx)->bool{
304        if let Some(inner) = self.borrow(){
305            inner.window.is_fullscreen(cx)
306        }
307        else{
308            false
309        }
310    }
311    pub fn resize(&self, cx: &mut Cx, size: DVec2) {
312        if let Some(inner) = self.borrow() {
313            inner.resize(cx, size);
314        }
315    }
316
317    pub fn reposition(&self, cx: &mut Cx, size: DVec2) {
318        if let Some(inner) = self.borrow() {
319            inner.reposition(cx, size);
320        }
321    }
322
323}
324
325impl Widget for Window {
326    fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
327        if let Event::Draw(e) = event {
328            let mut cx_draw = CxDraw::new(cx, e);
329            let cx = &mut Cx2d::new(&mut cx_draw);
330            self.draw_all(cx, scope);
331            return
332        }
333        
334        let uid = self.widget_uid();
335        
336        self.debug_view.handle_event(cx, event);
337        if self.show_performance_view {
338            self.performance_view.handle_widget(cx, event);
339        }
340        
341        self.nav_control.handle_event(cx, event, self.main_draw_list.draw_list_id());
342        self.overlay.handle_event(cx, event);
343        if self.demo_next_frame.is_event(event).is_some(){
344            if self.demo{
345                self.demo_next_frame = cx.new_next_frame();
346            }
347            cx.repaint_pass_and_child_passes(self.pass.pass_id());
348        }
349        let is_for_other_window = match event {
350            Event::WindowCloseRequested(ev) => ev.window_id != self.window.window_id(),
351            Event::WindowClosed(ev) => {
352                if ev.window_id == self.window.window_id() {
353                    cx.widget_action(uid, &scope.path, WindowAction::WindowClosed)
354                }
355                true
356            }
357            Event::WindowGeomChange(ev) => {
358                if ev.window_id == self.window.window_id() {
359                    match cx.os_type() {
360                        OsType::Windows | OsType::Macos => {
361                            if self.hide_caption_on_fullscreen{
362                                if ev.new_geom.is_fullscreen && !ev.old_geom.is_fullscreen {
363                                    self.view(id!(caption_bar)).set_visible(cx, false);
364                                }
365                                else if !ev.new_geom.is_fullscreen && ev.old_geom.is_fullscreen {
366                                    self.view(id!(caption_bar)).set_visible(cx, true);
367                                };
368                            }
369                        }
370                        _ => ()
371                    }
372                    cx.widget_action(uid, &scope.path, WindowAction::WindowGeomChange(ev.clone()));
373                    return
374                }
375                true
376            },
377            Event::WindowDragQuery(dq) => {
378                if dq.window_id == self.window.window_id() {
379
380                    if self.view(id!(caption_bar)).visible() {
381                        let size = self.window.get_inner_size(cx);
382                    
383                        if dq.abs.y < 25. {
384                            if dq.abs.x < size.x - 135.0 {
385                                dq.response.set(WindowDragQueryResponse::Caption);
386                            }
387                            cx.set_cursor(MouseCursor::Default);
388
389                        }
390                        /*
391                        if dq.abs.x < self.caption_size.x && dq.abs.y < self.caption_size.y {
392                        }*/
393                    }
394                }
395                true
396            }
397            Event::TouchUpdate(ev) => ev.window_id != self.window.window_id(),
398            Event::MouseDown(ev) => ev.window_id != self.window.window_id(),
399            Event::MouseMove(ev) => ev.window_id != self.window.window_id(),
400            Event::MouseUp(ev) => ev.window_id != self.window.window_id(),
401            Event::Scroll(ev) => ev.window_id != self.window.window_id(),
402            _ => false
403        };
404        
405        if is_for_other_window {
406            cx.widget_action(uid, &scope.path, WindowAction::EventForOtherWindow);
407            return
408        }
409        else {
410            // lets store our inverse matrix
411            if cx.in_xr_mode(){
412                if let Event::XrUpdate(e) = &event{
413                    let event = Event::XrLocal(XrLocalEvent::from_update_event(e, &self.xr_view_matrix));
414                    self.view.handle_event(cx, &event, scope);
415                }
416                else{
417                    self.view.handle_event(cx, event, scope);
418                }
419            }
420            else{
421                self.view.handle_event(cx, event, scope);
422            }
423        }
424        
425        if let Event::Actions(actions) = event{
426            if self.desktop_button(id!(windows_buttons.min)).clicked(&actions) {
427                self.window.minimize(cx);
428            }
429            if self.desktop_button(id!(windows_buttons.max)).clicked(&actions) {
430                if self.window.is_fullscreen(cx) {
431                    self.window.restore(cx);
432                }
433                else {
434                    self.window.maximize(cx);
435                }
436            }
437            if self.desktop_button(id!(windows_buttons.close)).clicked(&actions) {
438                self.window.close(cx);
439            }
440            if self.desktop_button(id!(web_xr.xr_on)).clicked(&actions) {
441                cx.xr_start_presenting();
442            }
443        }
444                
445        if let Event::ClearAtlasses = event {
446            CxDraw::reset_icon_atlas(cx);
447        }
448        
449        if let Event::MouseMove(ev) = event {
450            if let OsType::LinuxDirect = cx.os_type() {
451                // ok move our mouse cursor
452                self.last_mouse_pos = ev.abs;
453                self.draw_cursor.update_abs(cx, Rect {
454                    pos: ev.abs,
455                    size: self.mouse_cursor_size
456                })
457            }
458        }
459    }
460    
461    fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, walk: Walk) -> DrawStep {
462        if self.draw_state.begin(cx, DrawState::Drawing) {
463            if self.begin(cx).is_not_redrawing() {
464                self.draw_state.end();
465                return DrawStep::done();
466            }
467        }
468        
469        if let Some(DrawState::Drawing) = self.draw_state.get() {
470            self.view.draw_walk(cx, scope, walk)?;
471            self.draw_state.end();
472            self.end(cx);
473        }
474        
475        DrawStep::done()
476    }
477    
478    fn draw_3d(&mut self, cx: &mut Cx3d, scope:&mut Scope)->DrawStep{
479        // lets create a Cx2d in which we can draw. we dont support stepping here
480        let cx = &mut Cx2d::new(cx.cx);
481        
482        self.main_draw_list.begin_always(cx);
483        
484        let size = dvec2(1500.0,1200.0);
485        cx.begin_sized_turtle(size, Layout::flow_down());
486                
487        self.overlay.begin(cx);
488        
489        self.view.draw_walk_all(cx, scope, Walk::default());
490        
491        self.debug_view.draw(cx);
492                        
493        self.main_draw_list.set_view_transform(cx, &self.xr_view_matrix);
494        
495        cx.end_pass_sized_turtle();
496                
497        self.main_draw_list.end(cx);
498        
499        DrawStep::done()
500    }
501}