makepad_render/
cx_macos.rs

1// MacOS specific loop
2use crate::cx_cocoa::*;
3use crate::cx::*;
4
5impl Cx {
6    
7    pub fn event_loop<F>(&mut self, mut event_handler: F)
8    where F: FnMut(&mut Cx, &mut Event),
9    {
10        self.platform_type = PlatformType::OSX;
11        
12        let mut cocoa_app = CocoaApp::new();
13        
14        cocoa_app.init();
15        
16        let mut metal_cx = MetalCx::new();
17        
18        let mut metal_windows: Vec<MetalWindow> = Vec::new();
19
20        self.mtl_compile_all_shaders(&metal_cx);
21
22        self.load_theme_fonts();
23        
24        self.call_event_handler(&mut event_handler, &mut Event::Construct);
25        
26        self.redraw_child_area(Area::All);
27        
28        let mut passes_todo = Vec::new();
29        
30        cocoa_app.event_loop( | cocoa_app, events | {
31            //let mut paint_dirty = false;
32            for mut event in events {
33                
34                self.process_desktop_pre_event(&mut event, &mut event_handler);
35                
36                match &event {
37                    Event::WindowGeomChange(re) => { // do this here because mac
38                        for metal_window in &mut metal_windows {
39                            if metal_window.window_id == re.window_id {
40                                metal_window.window_geom = re.new_geom.clone();
41                                self.windows[re.window_id].window_geom = re.new_geom.clone();
42                                // redraw just this windows root draw list
43                                if re.old_geom.inner_size != re.new_geom.inner_size{
44                                    if let Some(main_pass_id) = self.windows[re.window_id].main_pass_id {
45                                        self.redraw_pass_and_sub_passes(main_pass_id);
46                                    }
47                                }
48                                break;
49                            }
50                        }
51                        // ok lets not redraw all, just this window
52                        self.call_event_handler(&mut event_handler, &mut event);
53                    },
54                    Event::WindowClosed(wc) => { 
55                        // lets remove the window from the set
56                        self.windows[wc.window_id].window_state = CxWindowState::Closed;
57                        self.windows_free.push(wc.window_id);
58                        // remove the d3d11/win32 window
59                        
60                        for index in 0..metal_windows.len() {
61                            if metal_windows[index].window_id == wc.window_id {
62                                metal_windows.remove(index);
63                                if metal_windows.len() == 0 {
64                                    cocoa_app.terminate_event_loop();
65                                }
66                                for metal_window in &mut metal_windows {
67                                    metal_window.cocoa_window.update_ptrs();
68                                }
69                            }
70                        }
71                        self.call_event_handler(&mut event_handler, &mut event);
72                    },
73                    Event::Paint => {
74                        
75                        let vsync = self.process_desktop_paint_callbacks(cocoa_app.time_now(), &mut event_handler);
76                        
77                        // construct or destruct windows
78                        for (index, window) in self.windows.iter_mut().enumerate() {
79                            
80                            window.window_state = match &window.window_state {
81                                CxWindowState::Create {inner_size, position, title} => {
82                                    // lets create a platformwindow
83                                    let metal_window = MetalWindow::new(index, &metal_cx, cocoa_app, *inner_size, *position, &title);
84                                    window.window_geom = metal_window.window_geom.clone();
85                                    metal_windows.push(metal_window);
86                                    for metal_window in &mut metal_windows {
87                                        metal_window.cocoa_window.update_ptrs();
88                                    }
89                                    CxWindowState::Created
90                                },
91                                CxWindowState::Close => {
92                                    for metal_window in &mut metal_windows {if metal_window.window_id == index {
93                                        metal_window.cocoa_window.close_window();
94                                        break;
95                                    }}
96                                    CxWindowState::Closed
97                                },
98                                CxWindowState::Created => CxWindowState::Created,
99                                CxWindowState::Closed => CxWindowState::Closed
100                            };
101                            
102                            window.window_command = match &window.window_command {
103                                CxWindowCmd::Restore => {
104                                    for metal_window in &mut metal_windows {if metal_window.window_id == index {
105                                        metal_window.cocoa_window.restore();
106                                    }}
107                                    CxWindowCmd::None
108                                },
109                                CxWindowCmd::Maximize => {
110                                    for metal_window in &mut metal_windows {if metal_window.window_id == index {
111                                        metal_window.cocoa_window.maximize();
112                                    }}
113                                    CxWindowCmd::None
114                                },
115                                CxWindowCmd::Minimize => {
116                                    for metal_window in &mut metal_windows {if metal_window.window_id == index {
117                                        metal_window.cocoa_window.minimize();
118                                    }}
119                                    CxWindowCmd::None
120                                },
121                                _ => CxWindowCmd::None,
122                            };
123                            
124                            if let Some(topmost) = window.window_topmost {
125                                for metal_window in &mut metal_windows {if metal_window.window_id == index {
126                                    metal_window.cocoa_window.set_topmost(topmost);
127                                }}
128                            }
129                        }
130                        
131                        // set a cursor
132                        if !self.down_mouse_cursor.is_none() {
133                            cocoa_app.set_mouse_cursor(self.down_mouse_cursor.as_ref().unwrap().clone())
134                        }
135                        else if !self.hover_mouse_cursor.is_none() {
136                            cocoa_app.set_mouse_cursor(self.hover_mouse_cursor.as_ref().unwrap().clone())
137                        }
138                        else {
139                            cocoa_app.set_mouse_cursor(MouseCursor::Default)
140                        }
141                        
142                        if let Some(set_ime_position) = self.platform.set_ime_position {
143                            self.platform.set_ime_position = None;
144                            for metal_window in &mut metal_windows {
145                                metal_window.cocoa_window.set_ime_spot(set_ime_position);
146                            }
147                        }
148                        
149                        while self.platform.start_timer.len() > 0 {
150                            let (timer_id, interval, repeats) = self.platform.start_timer.pop().unwrap();
151                            cocoa_app.start_timer(timer_id, interval, repeats);
152                        }
153                        
154                        while self.platform.stop_timer.len() > 0 {
155                            let timer_id = self.platform.stop_timer.pop().unwrap();
156                            cocoa_app.stop_timer(timer_id);
157                        }
158                        
159                        if self.platform.set_menu{
160                            self.platform.set_menu = false;
161                            if let Some(menu) = &self.platform.last_menu{
162                                cocoa_app.update_app_menu(menu, &self.command_settings)
163                            }
164                        }
165                        
166                        // build a list of renderpasses to repaint
167                        let mut windows_need_repaint = 0;
168                        self.compute_passes_to_repaint(&mut passes_todo, &mut windows_need_repaint);
169                        
170                        if passes_todo.len() > 0 {
171                            for pass_id in &passes_todo {
172                                match self.passes[*pass_id].dep_of.clone() {
173                                    CxPassDepOf::Window(window_id) => {
174                                        // find the accompanying render window
175                                        // its a render window
176                                        windows_need_repaint -= 1;
177                                        for metal_window in &mut metal_windows {if metal_window.window_id == window_id {
178                                            metal_window.set_vsync_enable(windows_need_repaint == 0 && vsync);
179                                            metal_window.set_buffer_count(
180                                                if metal_window.window_geom.is_fullscreen {3}else {2}
181                                            );
182                                            
183                                            let dpi_factor = metal_window.window_geom.dpi_factor;
184                                            
185                                            metal_window.resize_core_animation_layer(&metal_cx);
186                                            
187                                            self.draw_pass_to_layer(
188                                                *pass_id,
189                                                dpi_factor,
190                                                metal_window.ca_layer,
191                                                &mut metal_cx,
192                                            );
193                                            // call redraw if we guessed the dpi wrong on startup
194                                            if metal_window.first_draw{
195                                                metal_window.first_draw = false;
196                                                if dpi_factor != self.default_dpi_factor{
197                                                    self.redraw_pass_and_sub_passes(*pass_id);
198                                                }
199
200                                            }
201                                        }}
202                                    }
203                                    CxPassDepOf::Pass(parent_pass_id) => {
204                                        let dpi_factor = self.get_delegated_dpi_factor(parent_pass_id);
205                                        self.draw_pass_to_texture(
206                                            *pass_id,
207                                            dpi_factor,
208                                            &mut metal_cx,
209                                        );
210                                    },
211                                    CxPassDepOf::None => {
212                                        self.draw_pass_to_texture(
213                                            *pass_id,
214                                            1.0,
215                                            &mut metal_cx,
216                                        );
217                                    }
218                                }
219                            }
220                        }
221                    },
222                    Event::None => {
223                    },
224                    Event::Signal{..}=>{
225                        self.call_event_handler(&mut event_handler, &mut event);
226                        self.call_signals(&mut event_handler);
227                    },
228                    _ => {
229                        self.call_event_handler(&mut event_handler, &mut event);
230                    }
231                }
232                if self.process_desktop_post_event(event) {
233                    cocoa_app.terminate_event_loop();
234                }
235            }
236            if self.playing_anim_areas.len() == 0 && self.redraw_parent_areas.len() == 0 && self.redraw_child_areas.len() == 0 && self.frame_callbacks.len() == 0 {
237                true
238            } else {
239                false
240            }
241        })
242    }
243    
244    pub fn show_text_ime(&mut self, x: f32, y: f32) {
245        self.platform.set_ime_position = Some(Vec2 {x: x, y: y});
246    }
247    
248    pub fn hide_text_ime(&mut self) {
249    }
250    
251    pub fn set_window_outer_size(&mut self, size: Vec2) {
252        self.platform.set_window_outer_size = Some(size);
253    }
254    
255    pub fn set_window_position(&mut self, pos: Vec2) {
256        self.platform.set_window_position = Some(pos);
257    }
258    
259    pub fn start_timer(&mut self, interval: f64, repeats: bool) -> Timer {
260        self.timer_id += 1;
261        self.platform.start_timer.push((self.timer_id, interval, repeats));
262        Timer {timer_id: self.timer_id}
263    }
264    
265    pub fn stop_timer(&mut self, timer: &mut Timer) {
266        if timer.timer_id != 0 {
267            self.platform.stop_timer.push(timer.timer_id);
268            timer.timer_id = 0;
269        }
270    }
271    
272    pub fn post_signal(signal: Signal, status: StatusId) {
273        if signal.signal_id != 0{
274            CocoaApp::post_signal(signal.signal_id, status);
275        }
276    }
277    
278    pub fn update_menu(&mut self, menu:&Menu){
279        // lets walk the menu and do the cocoa equivalents
280        let platform = &mut self.platform;
281        if platform.last_menu.is_none() || platform.last_menu.as_ref().unwrap() != menu{
282            platform.last_menu = Some(menu.clone());
283            platform.set_menu = true;
284        }
285    }
286}
287
288#[derive(Clone, Default)]
289pub struct CxPlatform {
290    pub bytes_written: usize,
291    pub draw_calls_done: usize,
292    pub last_menu: Option<Menu>,
293    pub set_menu: bool,
294    pub set_window_position: Option<Vec2>,
295    pub set_window_outer_size: Option<Vec2>,
296    pub set_ime_position: Option<Vec2>,
297    pub start_timer: Vec<(u64, f64, bool)>,
298    pub stop_timer: Vec<(u64)>,
299    pub text_clipboard_response: Option<String>,
300    pub desktop: CxDesktop,
301}