makepad_platform/
cx_api.rs

1use crate::file_dialogs::FileDialog;
2
3use {
4    crate::{
5        area::Area,
6        cursor::MouseCursor,
7        cx::{Cx, CxRef, OsType, XrCapabilities},
8        draw_list::DrawListId,
9        event::{DragItem, HttpRequest, NextFrame, Timer, Trigger, VideoSource},
10        gpu_info::GpuInfo,
11        macos_menu::MacosMenu,
12        makepad_futures::executor::Spawner,
13        makepad_live_id::*,
14        event::xr::XrAnchor,
15        makepad_math::{DVec2, Rect},
16        pass::{CxPassParent, CxPassRect, PassId},
17        texture::Texture,
18        window::WindowId,
19        dvec2,
20    },
21    std::{
22        any::{Any, TypeId},
23        rc::Rc,
24    },
25};
26pub enum OpenUrlInPlace{
27    Yes,
28    No
29}
30pub trait CxOsApi {
31    fn init_cx_os(&mut self);
32
33    fn spawn_thread<F>(&mut self, f: F)
34    where
35        F: FnOnce() + Send + 'static;
36        
37    fn start_stdin_service(&mut self) {}
38    fn pre_start() -> bool {
39        false
40    }
41    
42    fn open_url(&mut self, url:&str, in_place:OpenUrlInPlace);
43    
44    fn seconds_since_app_start(&self)->f64;
45    
46    fn default_window_size(&self)->DVec2{dvec2(800.,600.)}
47    
48    fn max_texture_width()->usize{4096}
49    
50    fn in_xr_mode(&self)->bool{false}
51    
52    fn micro_zbias_step(&self)->f32{0.00001}
53    
54    /*
55    fn web_socket_open(&mut self, url: String, rec: WebSocketAutoReconnect) -> WebSocket;
56    fn web_socket_send(&mut self, socket: WebSocket, data: Vec<u8>);*/
57}
58
59#[derive(PartialEq)]
60pub enum CxOsOp {
61    CreateWindow(WindowId),
62    ResizeWindow(WindowId, DVec2),
63    RepositionWindow(WindowId, DVec2),
64    CloseWindow(WindowId),
65    MinimizeWindow(WindowId),
66    Deminiaturize(WindowId),
67    MaximizeWindow(WindowId),
68    FullscreenWindow(WindowId),
69    NormalizeWindow(WindowId),
70    RestoreWindow(WindowId),
71    HideWindow(WindowId),
72    SetTopmost(WindowId, bool),
73    ShowInDock(bool),
74
75    ShowTextIME(Area, DVec2),
76    HideTextIME,
77    SetCursor(MouseCursor),
78    StartTimer {
79        timer_id: u64,
80        interval: f64,
81        repeats: bool,
82    },
83    StopTimer(u64),
84    Quit,
85
86    StartDragging(Vec<DragItem>),
87    UpdateMacosMenu(MacosMenu),
88    ShowClipboardActions(String),
89    CopyToClipboard(String),
90
91    HttpRequest {
92        request_id: LiveId,
93        request: HttpRequest,
94    },
95    CancelHttpRequest{
96        request_id: LiveId,
97    },
98
99    PrepareVideoPlayback(LiveId, VideoSource, u32, bool, bool),
100    BeginVideoPlayback(LiveId),
101    PauseVideoPlayback(LiveId),
102    ResumeVideoPlayback(LiveId),
103    MuteVideoPlayback(LiveId),
104    UnmuteVideoPlayback(LiveId),
105    CleanupVideoPlaybackResources(LiveId),
106    UpdateVideoSurfaceTexture(LiveId),
107    
108    CreateWebView{
109        id: LiveId,
110        area: Area,
111        texture: Texture,
112        url: String
113    },
114    UpdateWebView{
115        id: LiveId,
116        area: Area
117    },
118    CloseWebView{
119        id:LiveId
120    },
121    SaveFileDialog(FileDialog),
122    SelectFileDialog(FileDialog),
123    SaveFolderDialog(FileDialog),
124    SelectFolderDialog(FileDialog),    
125    
126    XrStartPresenting,
127    XrSetLocalAnchor(XrAnchor),
128    XrAdvertiseAnchor(XrAnchor),
129    XrDiscoverAnchor(u8),
130    XrStopPresenting,
131    
132}
133
134impl std::fmt::Debug for CxOsOp {
135    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        match self{
137            Self::CreateWindow(..)=>write!(f, "CreateWindow"),
138            Self::CloseWindow(..)=>write!(f, "CloseWindow"),
139            Self::MinimizeWindow(..)=>write!(f, "MinimizeWindow"),
140            Self::Deminiaturize(..)=>write!(f, "Deminiaturize"),
141            Self::MaximizeWindow(..)=>write!(f, "MaximizeWindow"),
142            Self::FullscreenWindow(..)=>write!(f, "FullscreenWindow"),
143            Self::NormalizeWindow(..)=>write!(f, "NormalizeWindow"),
144            Self::RestoreWindow(..)=>write!(f, "RestoreWindow"),
145            Self::HideWindow(..)=>write!(f, "HideWindow"),
146            Self::SetTopmost(..)=>write!(f, "SetTopmost"),
147            Self::ShowInDock(..)=>write!(f, "ShowInDock"),
148            
149            Self::ShowTextIME(..)=>write!(f, "ShowTextIME"),
150            Self::HideTextIME=>write!(f, "HideTextIME"),
151            Self::SetCursor(..)=>write!(f, "SetCursor"),
152            Self::StartTimer{..}=>write!(f, "StartTimer"),
153            Self::StopTimer(..)=>write!(f, "StopTimer"),
154            Self::Quit=>write!(f, "Quit"),
155            
156            Self::StartDragging(..)=>write!(f, "StartDragging"),
157            Self::UpdateMacosMenu(..)=>write!(f, "UpdateMacosMenu"),
158            Self::ShowClipboardActions(..)=>write!(f, "ShowClipboardActions"),
159            Self::CopyToClipboard(..)=>write!(f, "CopyToClipboard"),
160            
161            Self::HttpRequest{..}=>write!(f, "HttpRequest"),
162            Self::CancelHttpRequest{..}=>write!(f, "CancelHttpRequest"),
163            
164            Self::PrepareVideoPlayback(..)=>write!(f, "PrepareVideoPlayback"),
165            Self::BeginVideoPlayback(..)=>write!(f, "BeginVideoPlayback"),
166            Self::PauseVideoPlayback(..)=>write!(f, "PauseVideoPlayback"),
167            Self::ResumeVideoPlayback(..)=>write!(f, "ResumeVideoPlayback"),
168            Self::MuteVideoPlayback(..)=>write!(f, "MuteVideoPlayback"),
169            Self::UnmuteVideoPlayback(..)=>write!(f, "UnmuteVideoPlayback"),
170            Self::CleanupVideoPlaybackResources(..)=>write!(f, "CleanupVideoPlaybackResources"),
171            Self::UpdateVideoSurfaceTexture(..)=>write!(f, "UpdateVideoSurfaceTexture"),
172            Self::CreateWebView{..}=>write!(f, "CreateWebView"),
173            Self::UpdateWebView{..}=>write!(f, "UpdateWebView"),
174            Self::CloseWebView{..}=>write!(f, "CloseWebView"),
175            Self::SaveFileDialog(..)=>write!(f, "SaveFileDialog"),
176            Self::SelectFileDialog(..)=>write!(f, "SelectFileDialog"),
177            Self::SaveFolderDialog(..)=>write!(f, "SaveFolderDialog"),
178            Self::SelectFolderDialog(..)=>write!(f, "SelectFolderDialog"),
179            Self::ResizeWindow(..)=>write!(f, "ResizeWindow"),
180            Self::RepositionWindow(..)=>write!(f, "RepositionWindow"),
181            
182            Self::XrStartPresenting=>write!(f, "XrStartPresenting"),
183            Self::XrStopPresenting=>write!(f, "XrStopPresenting"),
184            Self::XrAdvertiseAnchor(_)=>write!(f, "XrAdvertiseAnchor"),
185            Self::XrSetLocalAnchor(_)=>write!(f, "XrSetLocalAnchor"),
186            Self::XrDiscoverAnchor(_)=>write!(f, "XrDiscoverAnchor"),
187        }
188    }
189}
190impl Cx {
191    pub fn in_draw_event(&self)->bool{
192        self.in_draw_event
193    }
194
195    pub fn xr_capabilities(&self) -> &XrCapabilities {
196        &self.xr_capabilities
197    }
198
199    pub fn get_ref(&self) -> CxRef {
200        CxRef(self.self_ref.clone().unwrap())
201    }
202    
203    pub fn take_dependency(&mut self, path: &str) -> Result<Rc<Vec<u8>>, String> {
204        if let Some(data) = self.dependencies.get_mut(path) {
205            if let Some(data) = data.data.take() {
206                return match data {
207                    Ok(data) => Ok(data),
208                    Err(s) => Err(s.clone()),
209                };
210            }
211        }
212        Err(format!("Dependency not loaded {}", path))
213    }
214    
215    pub fn get_dependency(&self, path: &str) -> Result<Rc<Vec<u8>>, String> {
216        if let Some(data) = self.dependencies.get(path) {
217            if let Some(data) = &data.data {
218                return match data {
219                    Ok(data) => Ok(data.clone()),
220                    Err(s) => Err(s.clone()),
221                };
222            }
223        }
224        Err(format!("Dependency not loaded {}", path))
225    }
226    pub fn null_texture(&self) -> Texture {
227        self.null_texture.clone()
228    }
229    pub fn redraw_id(&self) -> u64 {
230        self.redraw_id
231    }
232
233    pub fn os_type(&self) -> &OsType {
234        &self.os_type
235    }
236    pub fn in_makepad_studio(&self) -> bool {
237        self.in_makepad_studio
238    }
239
240    pub fn cpu_cores(&self) -> usize {
241        self.cpu_cores
242    }
243    pub fn gpu_info(&self) -> &GpuInfo {
244        &self.gpu_info
245    }
246
247    pub fn update_macos_menu(&mut self, menu: MacosMenu) {
248        self.platform_ops.push(CxOsOp::UpdateMacosMenu(menu));
249    }
250    
251    pub fn xr_start_presenting(&mut self) {
252        self.platform_ops.push(CxOsOp::XrStartPresenting);
253    }
254    
255    pub fn xr_advertise_anchor(&mut self, anchor:XrAnchor) {
256        self.platform_ops.push(CxOsOp::XrAdvertiseAnchor(anchor));
257    }
258    
259    pub fn xr_set_local_anchor(&mut self,  anchor:XrAnchor) {
260        self.platform_ops.push(CxOsOp::XrSetLocalAnchor(anchor));
261    }
262            
263    pub fn xr_discover_anchor(&mut self, id: u8) {
264        self.platform_ops.push(CxOsOp::XrDiscoverAnchor(id));
265    }
266        
267        
268    pub fn quit(&mut self) {
269        self.platform_ops.push(CxOsOp::Quit);
270    }
271    // Determines whether to show your application in the dock when it runs. The default value is true. 
272    // You can remove the dock icon by setting this value to false. 
273    pub fn show_in_dock(&mut self, show: bool) {
274        self.platform_ops.push(CxOsOp::ShowInDock(show));
275    }
276    pub fn push_unique_platform_op(&mut self, op: CxOsOp) {
277        if self.platform_ops.iter().find(|o| **o == op).is_none() {
278            self.platform_ops.push(op);
279        }
280    }
281
282    pub fn show_text_ime(&mut self, area: Area, pos: DVec2) {
283        if !self.keyboard.text_ime_dismissed {
284            self.ime_area = area;
285            self.platform_ops.push(CxOsOp::ShowTextIME(area, pos));
286        }
287    }
288
289    pub fn hide_text_ime(&mut self) {
290        self.keyboard.reset_text_ime_dismissed();
291        self.platform_ops.push(CxOsOp::HideTextIME);
292    }
293
294    pub fn text_ime_was_dismissed(&mut self) {
295        self.keyboard.set_text_ime_dismissed();
296        self.platform_ops.push(CxOsOp::HideTextIME);
297    }
298
299    pub fn show_clipboard_actions(&mut self, selected: String) {
300        self.platform_ops
301            .push(CxOsOp::ShowClipboardActions(selected));
302    }
303
304    /// Copies the given string to the clipboard.
305    /// 
306    /// Due to lack of platform clipboard support, it does not work on Web or tvOS.
307    pub fn copy_to_clipboard(&mut self, content: &str) {
308        self.platform_ops.push(CxOsOp::CopyToClipboard(content.to_owned()));
309    }
310
311    pub fn start_dragging(&mut self, items: Vec<DragItem>) {
312        self.platform_ops.iter().for_each(|p| {
313            if let CxOsOp::StartDragging { .. } = p {
314                panic!("start drag twice");
315            }
316        });
317        self.platform_ops.push(CxOsOp::StartDragging(items));
318    }
319
320    pub fn set_cursor(&mut self, cursor: MouseCursor) {
321        // down cursor overrides the hover cursor
322        if let Some(p) = self.platform_ops.iter_mut().find(|p| match p {
323            CxOsOp::SetCursor(_) => true,
324            _ => false,
325        }) {
326            *p = CxOsOp::SetCursor(cursor)
327        } else {
328            self.platform_ops.push(CxOsOp::SetCursor(cursor))
329        }
330    }
331
332    pub fn sweep_lock(&mut self, value: Area) {
333        self.fingers.sweep_lock(value);
334    }
335
336    pub fn sweep_unlock(&mut self, value: Area) {
337        self.fingers.sweep_unlock(value);
338    }
339
340    pub fn start_timeout(&mut self, interval: f64) -> Timer {
341        self.timer_id += 1;
342        self.platform_ops.push(CxOsOp::StartTimer {
343            timer_id: self.timer_id,
344            interval,
345            repeats: false,
346        });
347        Timer(self.timer_id)
348    }
349
350    pub fn start_interval(&mut self, interval: f64) -> Timer {
351        self.timer_id += 1;
352        self.platform_ops.push(CxOsOp::StartTimer {
353            timer_id: self.timer_id,
354            interval,
355            repeats: true,
356        });
357        Timer(self.timer_id)
358    }
359
360    pub fn stop_timer(&mut self, timer: Timer) {
361        if timer.0 != 0 {
362            self.platform_ops.push(CxOsOp::StopTimer(timer.0));
363        }
364    }
365
366
367    pub fn get_dpi_factor_of(&mut self, area: &Area) -> f64 {
368        if let Some(draw_list_id) = area.draw_list_id() {
369            let pass_id = self.draw_lists[draw_list_id].pass_id.unwrap();
370            return self.get_delegated_dpi_factor(pass_id);
371        }
372        return 1.0;
373    }
374    
375    pub fn get_pass_window_id(&self, pass_id: PassId) -> Option<WindowId> {
376         let mut pass_id_walk = pass_id;
377         for _ in 0..25 {
378             match self.passes[pass_id_walk].parent {
379                 CxPassParent::Window(window_id) => {
380                     return Some(window_id)
381                 }
382                 CxPassParent::Pass(next_pass_id) => {
383                     pass_id_walk = next_pass_id;
384                 }
385                 _ => {
386                     break;
387                 }
388             }
389         }
390         None
391     }
392    
393    pub fn get_delegated_dpi_factor(&mut self, pass_id: PassId) -> f64 {
394        let mut pass_id_walk = pass_id;
395        for _ in 0..25 {
396            match self.passes[pass_id_walk].parent {
397                CxPassParent::Window(window_id) => {
398                    if !self.windows[window_id].is_created {
399                        return 1.0;
400                    }
401                    return self.windows[window_id].window_geom.dpi_factor;
402                }
403                CxPassParent::Pass(next_pass_id) => {
404                    pass_id_walk = next_pass_id;
405                }
406                _ => {
407                    break;
408                }
409            }
410        }
411        1.0
412    }
413
414    pub fn redraw_pass_and_parent_passes(&mut self, pass_id: PassId) {
415        let mut walk_pass_id = pass_id;
416        loop {
417            if let Some(main_list_id) = self.passes[walk_pass_id].main_draw_list_id {
418                self.redraw_list_and_children(main_list_id);
419            }
420            match self.passes[walk_pass_id].parent.clone() {
421                CxPassParent::Pass(next_pass_id) => {
422                    walk_pass_id = next_pass_id;
423                }
424                _ => {
425                    break;
426                }
427            }
428        }
429    }
430
431    pub fn get_pass_rect(&self, pass_id: PassId, dpi: f64) -> Option<Rect> {
432        match self.passes[pass_id].pass_rect {
433            Some(CxPassRect::Area(area)) => {
434                let rect = area.rect(self);
435                Some(Rect {
436                    pos: (rect.pos * dpi).floor() / dpi,
437                    size: (rect.size * dpi).ceil() / dpi,
438                })
439            }
440            Some(CxPassRect::AreaOrigin(area, origin)) => {
441                let rect = area.rect(self);
442                Some(Rect {
443                    pos: origin,
444                    size: (rect.size * dpi).ceil() / dpi,
445                })
446            }
447            /*Some(CxPassRect::ScaledArea(area, scale)) => {
448                let rect = area.rect(self);
449                Some(Rect {
450                    pos: (rect.pos * dpi).floor() / dpi,
451                    size: scale * (rect.size * dpi).ceil() / dpi,
452                })
453            }*/
454            Some(CxPassRect::Size(size)) => Some(Rect {
455                pos: DVec2::default(),
456                size: (size * dpi).ceil() / dpi,
457            }),
458            None => None,
459        }
460    }
461
462    pub fn get_pass_name(&self, pass_id: PassId) -> &str {
463        &self.passes[pass_id].debug_name
464    }
465
466    pub fn repaint_pass(&mut self, pass_id: PassId) {
467        let cxpass = &mut self.passes[pass_id];
468        cxpass.paint_dirty = true;
469    }
470
471    pub fn repaint_pass_and_child_passes(&mut self, pass_id: PassId) {
472        let cxpass = &mut self.passes[pass_id];
473        cxpass.paint_dirty = true;
474        for sub_pass_id in self.passes.id_iter() {
475            if let CxPassParent::Pass(dep_pass_id) = self.passes[sub_pass_id].parent.clone() {
476                if dep_pass_id == pass_id {
477                    self.repaint_pass_and_child_passes(sub_pass_id);
478                }
479            }
480        }
481    }
482
483    pub fn redraw_pass_and_child_passes(&mut self, pass_id: PassId) {
484        let cxpass = &self.passes[pass_id];
485        if let Some(main_list_id) = cxpass.main_draw_list_id {
486            self.redraw_list_and_children(main_list_id);
487        }
488        // lets redraw all subpasses as well
489        for sub_pass_id in self.passes.id_iter() {
490            if let CxPassParent::Pass(dep_pass_id) = self.passes[sub_pass_id].parent.clone() {
491                if dep_pass_id == pass_id {
492                    self.redraw_pass_and_child_passes(sub_pass_id);
493                }
494            }
495        }
496    }
497
498    pub fn redraw_all(&mut self) {
499        self.new_draw_event.redraw_all = true;
500    }
501
502    pub fn redraw_area(&mut self, area: Area) {
503        if let Some(draw_list_id) = area.draw_list_id() {
504            self.redraw_list(draw_list_id);
505        }
506    }
507    
508    pub fn redraw_area_in_draw(&mut self, area: Area) {
509        if let Some(draw_list_id) = area.draw_list_id() {
510            self.redraw_list_in_draw(draw_list_id);
511        }
512    }
513    
514    pub fn redraw_area_and_children(&mut self, area: Area) {
515        if let Some(draw_list_id) = area.draw_list_id() {
516            self.redraw_list_and_children(draw_list_id);
517        }
518    }
519
520    pub fn redraw_list(&mut self, draw_list_id: DrawListId) {
521        if self.in_draw_event{
522            return
523        }
524        self.redraw_list_in_draw(draw_list_id);
525    }
526    
527    pub fn redraw_list_in_draw(&mut self, draw_list_id: DrawListId) {
528        if self
529        .new_draw_event
530        .draw_lists
531        .iter()
532        .position(|v| *v == draw_list_id)
533        .is_some()
534        {
535            return;
536        }
537        self.new_draw_event.draw_lists.push(draw_list_id);
538    }
539
540    pub fn redraw_list_and_children(&mut self, draw_list_id: DrawListId) {
541        if self.in_draw_event{
542            return
543        }
544        if self
545            .new_draw_event
546            .draw_lists_and_children
547            .iter()
548            .position(|v| *v == draw_list_id)
549            .is_some()
550        {
551            return;
552        }
553        self.new_draw_event
554            .draw_lists_and_children
555            .push(draw_list_id);
556    }
557
558    pub fn get_ime_area_rect(&self) -> Rect {
559        self.ime_area.rect(self)
560    }
561
562    pub fn update_area_refs(&mut self, old_area: Area, new_area: Area) -> Area {
563        if old_area == Area::Empty {
564            return new_area;
565        }
566        if self.ime_area == old_area {
567            self.ime_area = new_area;
568        }
569        self.fingers.update_area(old_area, new_area);
570        self.drag_drop.update_area(old_area, new_area);
571        self.keyboard.update_area(old_area, new_area);
572
573        new_area
574    }
575
576    pub fn set_key_focus(&mut self, focus_area: Area) {
577        self.keyboard.set_key_focus(focus_area);
578    }
579
580    pub fn revert_key_focus(&mut self) {
581        self.keyboard.revert_key_focus();
582    }
583
584    pub fn has_key_focus(&self, focus_area: Area) -> bool {
585        self.keyboard.has_key_focus(focus_area)
586    }
587
588    pub fn new_next_frame(&mut self) -> NextFrame {
589        let res = NextFrame(self.next_frame_id);
590        self.next_frame_id += 1;
591        self.new_next_frames.insert(res);
592        res
593    }
594
595    pub fn send_trigger(&mut self, area: Area, trigger: Trigger) {
596        if let Some(triggers) = self.triggers.get_mut(&area) {
597            triggers.push(trigger);
598        } else {
599            let mut new_set = Vec::new();
600            new_set.push(trigger);
601            self.triggers.insert(area, new_set);
602        }
603    }
604
605    pub fn set_global<T: 'static + Any + Sized>(&mut self, value: T) {
606        if !self.globals.iter().any(|v| v.0 == TypeId::of::<T>()) {
607            self.globals.push((TypeId::of::<T>(), Box::new(value)));
608        }
609    }
610
611    pub fn get_global<T: 'static + Any>(&mut self) -> &mut T {
612        let item = self
613            .globals
614            .iter_mut()
615            .find(|v| v.0 == TypeId::of::<T>())
616            .unwrap();
617        item.1.downcast_mut().unwrap()
618    }
619
620    pub fn has_global<T: 'static + Any>(&mut self) -> bool {
621        self.globals
622            .iter_mut()
623            .find(|v| v.0 == TypeId::of::<T>())
624            .is_some()
625    }
626
627    pub fn global<T: 'static + Any + Default>(&mut self) -> &mut T {
628        if !self.has_global::<T>() {
629            self.set_global(T::default());
630        }
631        self.get_global::<T>()
632    }
633
634    pub fn spawner(&self) -> &Spawner {
635        &self.spawner
636    }
637
638    pub fn http_request(&mut self, request_id: LiveId, request: HttpRequest) {
639        self.platform_ops.push(CxOsOp::HttpRequest {
640            request_id,
641            request,
642        });
643    }
644    
645    pub fn cancel_http_request(&mut self, request_id: LiveId) {
646        self.platform_ops.push(CxOsOp::CancelHttpRequest {
647            request_id,
648        });
649    }
650    /*
651        pub fn web_socket_open(&mut self, request_id: LiveId, request: HttpRequest) {
652            self.platform_ops.push(CxOsOp::WebSocketOpen{
653                request,
654                request_id,
655            });
656        }
657
658        pub fn web_socket_send_binary(&mut self, request_id: LiveId, data: Vec<u8>) {
659            self.platform_ops.push(CxOsOp::WebSocketSendBinary{
660                request_id,
661                data,
662            });
663        }
664    */
665    pub fn prepare_video_playback(
666        &mut self,
667        video_id: LiveId,
668        source: VideoSource,
669        external_texture_id: u32,
670        autoplay: bool,
671        should_loop: bool,
672    ) {
673        self.platform_ops.push(CxOsOp::PrepareVideoPlayback(
674            video_id,
675            source,
676            external_texture_id,
677            autoplay,
678            should_loop,
679        ));
680    }
681
682    pub fn begin_video_playback(&mut self, video_id: LiveId) {
683        self.platform_ops.push(CxOsOp::BeginVideoPlayback(video_id));
684    }
685
686    pub fn pause_video_playback(&mut self, video_id: LiveId) {
687        self.platform_ops.push(CxOsOp::PauseVideoPlayback(video_id));
688    }
689
690    pub fn resume_video_playback(&mut self, video_id: LiveId) {
691        self.platform_ops
692            .push(CxOsOp::ResumeVideoPlayback(video_id));
693    }
694
695    pub fn mute_video_playback(&mut self, video_id: LiveId) {
696        self.platform_ops.push(CxOsOp::MuteVideoPlayback(video_id));
697    }
698
699    pub fn unmute_video_playback(&mut self, video_id: LiveId) {
700        self.platform_ops
701            .push(CxOsOp::UnmuteVideoPlayback(video_id));
702    }
703
704    pub fn cleanup_video_playback_resources(&mut self, video_id: LiveId) {
705        self.platform_ops
706            .push(CxOsOp::CleanupVideoPlaybackResources(video_id));
707    }
708
709    pub fn println_resources(&self) {
710        println!("Num textures: {}", self.textures.0.pool.len());
711    }
712
713    pub fn open_system_savefile_dialog(&mut self) {
714        self.platform_ops.push(CxOsOp::SaveFileDialog(FileDialog::new()));
715    }
716
717    pub fn open_system_openfile_dialog(&mut self) {
718        self.platform_ops.push(CxOsOp::SelectFileDialog(FileDialog::new()));
719    }
720
721    pub fn open_system_savefolder_dialog(&mut self) {
722        self.platform_ops.push(CxOsOp::SaveFolderDialog(FileDialog::new()));
723
724    }
725
726    pub fn open_system_openfolder_dialog(&mut self) {
727        self.platform_ops.push(CxOsOp::SelectFolderDialog(FileDialog::new()));
728
729    }
730
731    pub fn event_id(&self) -> u64 {
732        self.event_id
733    }
734}
735
736#[macro_export]
737macro_rules! register_component_factory {
738    ( $ cx: ident, $ registry: ident, $ ty: ty, $ factory: ident) => {
739        let module_id = LiveModuleId::from_str(&module_path!()).unwrap();
740        if let Some((reg, _)) = $cx
741            .live_registry
742            .borrow()
743            .components
744            .get_or_create::<$registry>()
745            .map
746            .get(&LiveType::of::<$ty>())
747        {
748            if reg.module_id != module_id {
749                panic!(
750                    "Component already registered {} {}",
751                    stringify!($ty),
752                    reg.module_id
753                );
754            }
755        }
756        $cx.live_registry
757            .borrow()
758            .components
759            .get_or_create::<$registry>()
760            .map
761            .insert(
762                LiveType::of::<$ty>(),
763                (
764                    LiveComponentInfo {
765                        name: LiveId::from_str_with_lut(stringify!($ty)).unwrap(),
766                        module_id,
767                    },
768                    Box::new($factory()),
769                ),
770            );
771    };
772}