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