Skip to main content

vizia_winit/
window.rs

1use crate::window_modifiers::WindowModifiers;
2use glutin::context::GlProfile;
3use vizia_core::context::TreeProps;
4use vizia_window::{AnchorTarget, WindowDescription};
5#[cfg(target_os = "windows")]
6use winit::platform::windows::WindowExtWindows;
7#[cfg(target_os = "windows")]
8use winit::{platform::windows::WindowAttributesExtWindows, raw_window_handle::RawWindowHandle};
9
10use crate::convert::cursor_icon_to_cursor_icon;
11use hashbrown::HashMap;
12use std::error::Error;
13use std::num::NonZeroU32;
14use std::{ffi::CString, sync::Arc};
15use winit::raw_window_handle::HasWindowHandle;
16
17use gl_rs as gl;
18use glutin::config::Config;
19use glutin_winit::DisplayBuilder;
20
21use gl::types::*;
22
23use glutin::{
24    config::ConfigTemplateBuilder,
25    context::{ContextApi, ContextAttributesBuilder},
26    display::GetGlDisplay,
27    prelude::*,
28    surface::{SurfaceAttributesBuilder, WindowSurface},
29};
30
31use skia_safe::{
32    ColorSpace, ColorType, PixelGeometry, Surface, SurfaceProps, SurfacePropsFlags,
33    gpu::{
34        self, ContextOptions, SurfaceOrigin, backend_render_targets, ganesh::context_options,
35        gl::FramebufferInfo,
36    },
37};
38
39use vizia_core::prelude::*;
40use winit::event_loop::ActiveEventLoop;
41use winit::window::{CursorGrabMode, CursorIcon, CustomCursor, WindowAttributes, WindowLevel};
42use winit::{dpi::*, window::WindowId};
43
44pub struct WinState {
45    pub entity: Entity,
46    pub id: WindowId,
47    pub surface: skia_safe::Surface,
48    pub dirty_surface: skia_safe::Surface,
49    pub gr_context: skia_safe::gpu::DirectContext,
50    pub gl_surface: glutin::surface::Surface<glutin::surface::WindowSurface>,
51    gl_context: glutin::context::PossiblyCurrentContext,
52    gl_config: Config,
53    pub window: Arc<winit::window::Window>,
54    pub should_close: bool,
55    #[cfg(target_os = "windows")]
56    pub is_initially_cloaked: bool,
57}
58
59impl Drop for WinState {
60    fn drop(&mut self) {
61        let _ = self.gl_context.make_current(&self.gl_surface);
62    }
63}
64
65impl WinState {
66    pub fn new(
67        event_loop: &ActiveEventLoop,
68        entity: Entity,
69        #[allow(unused_mut)] mut window_attributes: WindowAttributes,
70        #[allow(unused_variables)] owner: Option<Arc<winit::window::Window>>,
71    ) -> Result<Self, Box<dyn Error>> {
72        #[cfg(target_os = "windows")]
73        let (window, gl_config) = {
74            if let Some(owner) = owner {
75                let RawWindowHandle::Win32(handle) = owner.window_handle().unwrap().as_raw() else {
76                    unreachable!();
77                };
78                window_attributes = window_attributes.with_owner_window(handle.hwnd.get());
79            }
80
81            // The current version of winit spawns new windows with unspecified position/size.
82            // As a workaround, we'll hide the window during creation and reveal it afterward.
83            let window_attributes = window_attributes.with_visible(false);
84
85            let (window, config) = build_window(event_loop, window_attributes);
86
87            let window = window.expect("Could not create window with OpenGL context");
88            // Another problem is the white background that briefly flashes on window creation.
89            // To avoid this one we must wait until the first draw is complete before revealing
90            // our window. The visible property won't work in this case as it prevents drawing.
91            // Instead we use the "cloak" attribute, which hides the window without that issue.
92            set_cloak(&window, true);
93
94            (window, config)
95        };
96
97        #[cfg(not(target_os = "windows"))]
98        let (window, gl_config) = {
99            let (window, config) = build_window(event_loop, window_attributes);
100            let window = window.expect("Could not create window with OpenGL context");
101            (window, config)
102        };
103
104        window.set_ime_allowed(true);
105
106        let raw_window_handle = window.window_handle().unwrap().as_raw();
107
108        let gl_display = gl_config.display();
109
110        let context_attributes = ContextAttributesBuilder::new()
111            .with_profile(GlProfile::Core)
112            .with_context_api(ContextApi::OpenGl(None))
113            .build(Some(raw_window_handle));
114
115        let fallback_context_attributes = ContextAttributesBuilder::new()
116            .with_profile(GlProfile::Core)
117            .with_context_api(ContextApi::Gles(None))
118            .build(Some(raw_window_handle));
119
120        let not_current_gl_context = unsafe {
121            gl_display.create_context(&gl_config, &context_attributes).unwrap_or_else(|_| {
122                gl_display
123                    .create_context(&gl_config, &fallback_context_attributes)
124                    .expect("failed to create context")
125            })
126        };
127
128        let (width, height): (u32, u32) = window.inner_size().into();
129
130        let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().with_srgb(Some(true)).build(
131            raw_window_handle,
132            NonZeroU32::new(width.max(1)).unwrap(),
133            NonZeroU32::new(height.max(1)).unwrap(),
134        );
135
136        let gl_surface =
137            unsafe { gl_config.display().create_window_surface(&gl_config, &attrs).unwrap() };
138
139        let gl_context = not_current_gl_context.make_current(&gl_surface).unwrap();
140
141        // if window_description.vsync {
142        //     gl_surface
143        //         .set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
144        //         .expect("Failed to set vsync");
145        // }
146
147        // Build skia renderer
148        gl::load_with(|s| {
149            gl_config.display().get_proc_address(CString::new(s).unwrap().as_c_str())
150        });
151
152        let interface = skia_safe::gpu::gl::Interface::new_load_with(|name| {
153            if name == "eglGetCurrentDisplay" {
154                return std::ptr::null();
155            }
156            gl_config.display().get_proc_address(CString::new(name).unwrap().as_c_str())
157        })
158        .expect("Could not create interface");
159
160        // https://github.com/rust-skia/rust-skia/issues/476
161        let mut context_options = ContextOptions::new();
162        context_options.skip_gl_error_checks = context_options::Enable::Yes;
163
164        let mut gr_context = skia_safe::gpu::direct_contexts::make_gl(interface, &context_options)
165            .expect("Could not create direct context");
166
167        let fb_info = {
168            let mut fboid: GLint = 0;
169            unsafe { gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid) };
170
171            FramebufferInfo {
172                fboid: fboid.try_into().unwrap(),
173                format: skia_safe::gpu::gl::Format::RGBA8.into(),
174                ..Default::default()
175            }
176        };
177
178        let num_samples = gl_config.num_samples() as usize;
179        let stencil_size = gl_config.stencil_size() as usize;
180
181        let mut surface =
182            create_surface(&window, fb_info, &mut gr_context, num_samples, stencil_size);
183
184        let inner_size = window.inner_size();
185
186        let dirty_surface = surface
187            .new_surface_with_dimensions((inner_size.width as i32, inner_size.height as i32))
188            .unwrap();
189
190        // Build our window
191        Ok(WinState {
192            entity,
193            gl_config,
194            gl_context,
195            id: window.id(),
196            gr_context,
197            gl_surface,
198            window: Arc::new(window),
199            surface,
200            dirty_surface,
201            should_close: false,
202            #[cfg(target_os = "windows")]
203            is_initially_cloaked: true,
204        })
205    }
206
207    // Returns a reference to the winit window
208    pub fn window(&self) -> &winit::window::Window {
209        &self.window
210    }
211
212    pub fn make_current(&mut self) {
213        self.gl_context.make_current(&self.gl_surface).unwrap();
214    }
215
216    pub fn resize(&mut self, size: PhysicalSize<u32>) {
217        self.gl_context.make_current(&self.gl_surface).unwrap();
218        let (width, height): (u32, u32) = size.into();
219
220        if width == 0 || height == 0 {
221            return;
222        }
223
224        let fb_info = {
225            let mut fboid: GLint = 0;
226            unsafe { gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid) };
227
228            FramebufferInfo {
229                fboid: fboid.try_into().unwrap(),
230                format: skia_safe::gpu::gl::Format::RGBA8.into(),
231                ..Default::default()
232            }
233        };
234
235        self.surface = create_surface(
236            &self.window,
237            fb_info,
238            &mut self.gr_context,
239            self.gl_config.num_samples() as usize,
240            self.gl_config.stencil_size() as usize,
241        );
242
243        self.dirty_surface = self
244            .surface
245            .new_surface_with_dimensions((width.max(1) as i32, height.max(1) as i32))
246            .unwrap();
247
248        self.gl_surface.resize(
249            &self.gl_context,
250            NonZeroU32::new(width.max(1)).unwrap(),
251            NonZeroU32::new(height.max(1)).unwrap(),
252        );
253    }
254
255    pub fn swap_buffers(&mut self) {
256        self.gr_context.flush_and_submit();
257        self.gl_surface.swap_buffers(&self.gl_context).expect("Failed to swap buffers");
258    }
259}
260
261fn build_window(
262    event_loop: &ActiveEventLoop,
263    window_attributes: WindowAttributes,
264) -> (Option<winit::window::Window>, Config) {
265    let template = ConfigTemplateBuilder::new().with_alpha_size(8).with_transparency(true);
266    let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_attributes));
267
268    display_builder
269        .build(event_loop, template, |configs| {
270            // Find the config with the maximum number of samples, so our triangle will
271            // be smooth.
272            configs
273                .reduce(|accum, config| {
274                    let transparency_check = config.supports_transparency().unwrap_or(false)
275                        & !accum.supports_transparency().unwrap_or(false);
276
277                    if transparency_check || config.num_samples() < accum.num_samples() {
278                        config
279                    } else {
280                        accum
281                    }
282                })
283                .unwrap()
284        })
285        .unwrap()
286}
287
288/// Cloaks the window such that it is not visible to the user, but will still be composited.
289///
290/// <https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute>
291///
292#[cfg(target_os = "windows")]
293pub fn set_cloak(window: &winit::window::Window, state: bool) -> bool {
294    use windows_sys::{
295        Win32::{
296            Foundation::{FALSE, HWND, TRUE},
297            Graphics::Dwm::{DWMWA_CLOAK, DwmSetWindowAttribute},
298        },
299        core::BOOL,
300    };
301
302    let RawWindowHandle::Win32(handle) = window.window_handle().unwrap().as_raw() else {
303        unreachable!();
304    };
305
306    let value = if state { TRUE } else { FALSE };
307
308    let result = unsafe {
309        DwmSetWindowAttribute(
310            handle.hwnd.get() as HWND,
311            DWMWA_CLOAK as u32,
312            std::ptr::from_ref(&value).cast(),
313            std::mem::size_of::<BOOL>() as u32,
314        )
315    };
316
317    result == 0 // success
318}
319
320pub fn create_surface(
321    window: &winit::window::Window,
322    fb_info: FramebufferInfo,
323    gr_context: &mut skia_safe::gpu::DirectContext,
324    num_samples: usize,
325    stencil_size: usize,
326) -> Surface {
327    let size = window.inner_size();
328    let size = (
329        size.width.try_into().expect("Could not convert width"),
330        size.height.try_into().expect("Could not convert height"),
331    );
332
333    let backend_render_target =
334        backend_render_targets::make_gl(size, num_samples, stencil_size, fb_info);
335
336    let surface_props = SurfaceProps::new_with_text_properties(
337        SurfacePropsFlags::default(),
338        PixelGeometry::default(),
339        0.5,
340        0.0,
341    );
342
343    gpu::surfaces::wrap_backend_render_target(
344        gr_context,
345        &backend_render_target,
346        SurfaceOrigin::BottomLeft,
347        ColorType::RGBA8888,
348        ColorSpace::new_srgb(),
349        Some(surface_props).as_ref(),
350        // None,
351    )
352    .expect("Could not create skia surface")
353}
354
355type WindowCallback = Option<Box<dyn Fn(&mut EventContext)>>;
356
357pub struct Window {
358    pub window: Option<Arc<winit::window::Window>>,
359    pub on_close: WindowCallback,
360    pub on_create: WindowCallback,
361    pub should_close: bool,
362    pub(crate) custom_cursors: Arc<HashMap<CursorIcon, CustomCursor>>,
363}
364
365impl Window {
366    fn window(&self) -> &winit::window::Window {
367        self.window.as_ref().unwrap()
368    }
369
370    pub fn new(cx: &mut Context, content: impl 'static + Fn(&mut Context)) -> Handle<Self> {
371        Self {
372            window: None,
373            on_close: None,
374            on_create: None,
375            should_close: false,
376            custom_cursors: Default::default(),
377        }
378        .build(cx, |cx| {
379            cx.windows.insert(
380                cx.current(),
381                WindowState {
382                    content: Some(Arc::new(content)),
383                    window_description: WindowDescription::new(),
384                    ..Default::default()
385                },
386            );
387            cx.tree.set_window(cx.current(), true);
388        })
389        .position_type(PositionType::Absolute)
390        .anchor_target(AnchorTarget::Window)
391    }
392
393    pub fn popup(
394        cx: &mut Context,
395        is_modal: bool,
396        content: impl 'static + Fn(&mut Context),
397    ) -> Handle<Self> {
398        Self {
399            window: None,
400            on_close: None,
401            on_create: None,
402            should_close: false,
403            custom_cursors: Default::default(),
404        }
405        .build(cx, |cx| {
406            let parent_window = cx.parent_window();
407            if is_modal {
408                cx.emit_to(parent_window, WindowEvent::SetEnabled(false));
409            }
410
411            cx.windows.insert(
412                cx.current(),
413                WindowState {
414                    owner: Some(parent_window),
415                    is_modal: true,
416                    content: Some(Arc::new(content)),
417                    window_description: WindowDescription::new(),
418                    ..Default::default()
419                },
420            );
421            cx.tree.set_window(cx.current(), true);
422        })
423        .position_type(PositionType::Absolute)
424        .anchor_target(AnchorTarget::Window)
425        .lock_focus_to_within()
426    }
427}
428
429impl View for Window {
430    fn element(&self) -> Option<&'static str> {
431        Some("window")
432    }
433
434    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
435        event.map(|window_event, meta| match window_event {
436            WindowEvent::Destroyed => {
437                let parent_window = cx.parent_window();
438                cx.emit_to(parent_window, WindowEvent::SetEnabled(true));
439            }
440
441            WindowEvent::GrabCursor(flag) => {
442                let grab_mode = if *flag { CursorGrabMode::Locked } else { CursorGrabMode::None };
443                self.window().set_cursor_grab(grab_mode).expect("Failed to set cursor grab");
444            }
445
446            WindowEvent::SetCursorPosition(x, y) => {
447                self.window()
448                    .set_cursor_position(winit::dpi::Position::Physical(PhysicalPosition::new(
449                        *x as i32, *y as i32,
450                    )))
451                    .expect("Failed to set cursor position");
452            }
453
454            WindowEvent::SetCursor(cursor) => {
455                let Some(icon) = cursor_icon_to_cursor_icon(*cursor) else {
456                    self.window().set_cursor_visible(false);
457                    return;
458                };
459
460                if let Some(custom_icon) = self.custom_cursors.get(&icon) {
461                    self.window().set_cursor(custom_icon.clone());
462                } else {
463                    self.window().set_cursor(icon);
464                }
465
466                self.window().set_cursor_visible(true);
467            }
468
469            WindowEvent::SetTitle(title) => {
470                if let Some(window_state) = cx.windows.get_mut(&cx.current()) {
471                    window_state.window_description.title = title.clone();
472                }
473
474                if let Some(window) = &self.window {
475                    window.set_title(title);
476                }
477            }
478
479            WindowEvent::SetSize(size) => {
480                let _ = self.window().request_inner_size(LogicalSize::new(size.width, size.height));
481            }
482
483            WindowEvent::SetMinSize(size) => {
484                self.window()
485                    .set_min_inner_size(size.map(|size| LogicalSize::new(size.width, size.height)));
486            }
487
488            WindowEvent::SetMaxSize(size) => {
489                self.window()
490                    .set_max_inner_size(size.map(|size| LogicalSize::new(size.width, size.height)));
491            }
492
493            WindowEvent::SetPosition(pos) => {
494                self.window().set_outer_position(LogicalPosition::new(pos.x, pos.y));
495                meta.consume();
496            }
497
498            WindowEvent::SetResizable(flag) => {
499                self.window().set_resizable(*flag);
500            }
501
502            WindowEvent::SetMinimized(flag) => {
503                self.window().set_minimized(*flag);
504            }
505
506            WindowEvent::SetMaximized(flag) => {
507                self.window().set_maximized(*flag);
508            }
509
510            WindowEvent::SetVisible(flag) => {
511                self.window().set_visible(*flag);
512
513                meta.consume();
514            }
515
516            WindowEvent::SetDecorations(flag) => {
517                self.window().set_decorations(*flag);
518            }
519
520            WindowEvent::ReloadStyles => {
521                cx.reload_styles().unwrap();
522            }
523
524            WindowEvent::WindowClose => {
525                self.should_close = true;
526
527                cx.close_window();
528
529                if let Some(callback) = &self.on_close {
530                    callback(cx);
531                }
532
533                meta.consume();
534            }
535
536            WindowEvent::FocusNext => {
537                cx.focus_next();
538            }
539
540            WindowEvent::FocusPrev => {
541                cx.focus_prev();
542            }
543
544            WindowEvent::Redraw => {
545                self.window().request_redraw();
546            }
547
548            #[allow(unused_variables)]
549            WindowEvent::SetEnabled(flag) => {
550                #[cfg(target_os = "windows")]
551                self.window().set_enable(*flag);
552
553                self.window().focus_window();
554            }
555
556            WindowEvent::DragWindow => {
557                self.window().drag_window().expect("Failed to init drag window");
558                meta.consume();
559            }
560
561            WindowEvent::SetAlwaysOnTop(flag) => {
562                self.window().set_window_level(if *flag {
563                    WindowLevel::AlwaysOnTop
564                } else {
565                    WindowLevel::Normal
566                });
567            }
568
569            WindowEvent::SetImeCursorArea(position, size) => {
570                let position = PhysicalPosition::new(position.0 as i32, position.1 as i32);
571                let size = PhysicalSize::new(size.0, size.1);
572                self.window().set_ime_cursor_area(position, size);
573            }
574
575            _ => {}
576        })
577    }
578}
579
580impl WindowModifiers for Handle<'_, Window> {
581    fn on_close(self, callback: impl Fn(&mut EventContext) + 'static) -> Self {
582        self.modify(|window| window.on_close = Some(Box::new(callback)))
583    }
584
585    fn on_create(self, callback: impl Fn(&mut EventContext) + 'static) -> Self {
586        self.modify(|window| window.on_create = Some(Box::new(callback)))
587    }
588
589    fn title<T: ToStringLocalized>(mut self, title: impl Res<T> + Clone + 'static) -> Self {
590        let entity = self.entity();
591        let initial_title = title.get_value(&self).to_string_local(&self);
592        if let Some(win_state) = self.context().windows.get_mut(&entity) {
593            win_state.window_description.title = initial_title;
594        }
595
596        let getter_for_locale = title.clone();
597
598        self.context().with_current(entity, |cx| {
599            title.set_or_bind(cx, move |cx, val| {
600                let title_str = val.get_value(cx).to_string_local(cx);
601                cx.emit(WindowEvent::SetTitle(title_str));
602            });
603
604            let locale = cx.environment().locale;
605            locale.set_or_bind(cx, move |cx, _| {
606                let title = getter_for_locale.get_value(cx).to_string_local(cx);
607                cx.emit(WindowEvent::SetTitle(title));
608            });
609        });
610
611        self
612    }
613
614    fn inner_size<S: Into<WindowSize>>(mut self, size: impl Res<S>) -> Self {
615        let entity = self.entity();
616        let size = size.get_value(&self).into();
617        if let Some(win_state) = self.context().windows.get_mut(&entity) {
618            win_state.window_description.inner_size = size;
619        }
620
621        self
622    }
623
624    fn min_inner_size<S: Into<WindowSize>>(mut self, size: impl Res<Option<S>>) -> Self {
625        let entity = self.entity();
626        let size = size.get_value(&self).map(|size| size.into());
627        if let Some(win_state) = self.context().windows.get_mut(&entity) {
628            win_state.window_description.min_inner_size = size;
629        }
630
631        self
632    }
633
634    fn max_inner_size<S: Into<WindowSize>>(mut self, size: impl Res<Option<S>>) -> Self {
635        let entity = self.entity();
636        let size = size.get_value(&self).map(|size| size.into());
637        if let Some(win_state) = self.context().windows.get_mut(&entity) {
638            win_state.window_description.max_inner_size = size;
639        }
640
641        self
642    }
643
644    fn position<P: Into<vizia_window::WindowPosition>>(mut self, position: impl Res<P>) -> Self {
645        let entity = self.entity();
646        let pos = Some(position.get_value(&self).into());
647        if let Some(win_state) = self.context().windows.get_mut(&entity) {
648            win_state.window_description.position = pos;
649        }
650
651        self
652    }
653
654    fn offset<P: Into<vizia_window::WindowPosition>>(mut self, offset: impl Res<P>) -> Self {
655        let entity = self.entity();
656        let offset = Some(offset.get_value(&self).into());
657        if let Some(win_state) = self.context().windows.get_mut(&entity) {
658            win_state.window_description.offset = offset;
659        }
660
661        self
662    }
663
664    fn anchor<P: Into<vizia_window::Anchor>>(mut self, anchor: impl Res<P>) -> Self {
665        let entity = self.entity();
666        let anchor = Some(anchor.get_value(&self).into());
667        if let Some(win_state) = self.context().windows.get_mut(&entity) {
668            win_state.window_description.anchor = anchor;
669        }
670
671        self
672    }
673
674    fn anchor_target<P: Into<vizia_window::AnchorTarget>>(
675        mut self,
676        anchor_target: impl Res<P>,
677    ) -> Self {
678        let entity = self.entity();
679        let anchor_target = Some(anchor_target.get_value(&self).into());
680        if let Some(win_state) = self.context().windows.get_mut(&entity) {
681            win_state.window_description.anchor_target = anchor_target;
682        }
683
684        self
685    }
686
687    fn parent_anchor<P: Into<Anchor>>(mut self, parent_anchor: impl Res<P>) -> Self {
688        let entity = self.entity();
689        let parent_anchor = Some(parent_anchor.get_value(&self).into());
690        if let Some(win_state) = self.context().windows.get_mut(&entity) {
691            win_state.window_description.parent_anchor = parent_anchor;
692        }
693
694        self
695    }
696
697    fn resizable(mut self, flag: impl Res<bool>) -> Self {
698        let entity = self.entity();
699        let flag = flag.get_value(&self);
700        if let Some(win_state) = self.context().windows.get_mut(&entity) {
701            win_state.window_description.resizable = flag;
702        }
703
704        self
705    }
706
707    fn minimized(mut self, flag: impl Res<bool>) -> Self {
708        let entity = self.entity();
709        let flag = flag.get_value(&self);
710        if let Some(win_state) = self.context().windows.get_mut(&entity) {
711            win_state.window_description.minimized = flag;
712        }
713
714        self
715    }
716
717    fn maximized(mut self, flag: impl Res<bool>) -> Self {
718        let entity = self.entity();
719        let flag = flag.get_value(&self);
720        if let Some(win_state) = self.context().windows.get_mut(&entity) {
721            win_state.window_description.maximized = flag;
722        }
723
724        self
725    }
726
727    fn visible(mut self, flag: impl Res<bool>) -> Self {
728        let entity = self.entity();
729        let flag = flag.get_value(&self);
730        if let Some(win_state) = self.context().windows.get_mut(&entity) {
731            win_state.window_description.visible = flag
732        }
733
734        self
735    }
736
737    fn transparent(mut self, flag: bool) -> Self {
738        let entity = self.entity();
739        if let Some(win_state) = self.context().windows.get_mut(&entity) {
740            win_state.window_description.transparent = flag
741        }
742
743        self
744    }
745
746    fn decorations(mut self, flag: bool) -> Self {
747        let entity = self.entity();
748        if let Some(win_state) = self.context().windows.get_mut(&entity) {
749            win_state.window_description.decorations = flag
750        }
751
752        self
753    }
754
755    fn always_on_top(mut self, flag: bool) -> Self {
756        let entity = self.entity();
757        if let Some(win_state) = self.context().windows.get_mut(&entity) {
758            win_state.window_description.always_on_top = flag
759        }
760
761        self
762    }
763
764    fn vsync(mut self, flag: bool) -> Self {
765        let entity = self.entity();
766        if let Some(win_state) = self.context().windows.get_mut(&entity) {
767            win_state.window_description.vsync = flag
768        }
769
770        self
771    }
772
773    fn icon(mut self, width: u32, height: u32, image: Vec<u8>) -> Self {
774        let entity = self.entity();
775        if let Some(win_state) = self.context().windows.get_mut(&entity) {
776            win_state.window_description.icon = Some(image);
777            win_state.window_description.icon_width = width;
778            win_state.window_description.icon_height = height;
779        }
780
781        self
782    }
783
784    fn enabled_window_buttons(mut self, window_buttons: WindowButtons) -> Self {
785        let entity = self.entity();
786        if let Some(win_state) = self.context().windows.get_mut(&entity) {
787            win_state.window_description.enabled_window_buttons = window_buttons;
788        }
789
790        self
791    }
792}