wde_renderer/core/
window.rs1use bevy::{
6 a11y::AccessibilityPlugin,
7 app::{PluginGroup, PluginGroupBuilder},
8 ecs::{message::Message, system::NonSendMarker},
9 prelude::{Entity, Event, MessageReader, MessageWriter, Query, Res, ResMut, Resource, With},
10 utils::default,
11 window::{PresentMode, PrimaryWindow, Window, WindowPlugin, WindowResized, WindowTheme},
12 winit::{WINIT_WINDOWS, WinitPlugin}
13};
14use wde_wgpu::instance;
15
16use crate::core::RenderInstance;
17
18use super::extract_macros::ExtractWorld;
19
20#[derive(Clone)]
23pub struct WindowIcon {
24 pub rgba: Vec<u8>,
25 pub width: u32,
26 pub height: u32
27}
28impl WindowIcon {
29 pub fn from_bytes(bytes: &[u8]) -> Result<Self, image::ImageError> {
31 let image = image::load_from_memory(bytes)?.into_rgba8();
32 let (width, height) = image.dimensions();
33 Ok(Self {
34 rgba: image.into_raw(),
35 width,
36 height
37 })
38 }
39}
40
41#[derive(Resource, Default)]
43pub(crate) struct PrimaryWindowIcon(pub Option<WindowIcon>);
44
45pub(crate) fn apply_window_icon(
52 icon: Res<PrimaryWindowIcon>,
53 _main_thread: NonSendMarker,
54 primary_window: Query<Entity, With<PrimaryWindow>>
55) {
56 let Some(icon) = &icon.0 else { return };
57 let Ok(entity) = primary_window.single() else {
58 return;
59 };
60
61 WINIT_WINDOWS.with_borrow(|winit_windows| {
62 let Some(winit_window) = winit_windows.get_window(entity) else {
63 return;
64 };
65
66 match winit::window::Icon::from_rgba(icon.rgba.clone(), icon.width, icon.height) {
67 Ok(winit_icon) => winit_window.set_window_icon(Some(winit_icon)),
68 Err(err) => wde_logger::warn!("Failed to set window icon: {err}")
69 }
70 });
71}
72
73#[derive(Debug, Event, Message)]
76pub struct SurfaceResized {
77 pub width: u32,
78 pub height: u32
79}
80
81pub(crate) struct WindowPlugins {
82 pub title: String,
83 pub resolution: (u32, u32)
84}
85impl Default for WindowPlugins {
86 fn default() -> Self {
87 Self {
88 title: "WaterDropEngine".into(),
89 resolution: (600, 500)
90 }
91 }
92}
93impl PluginGroup for WindowPlugins {
94 fn build(self) -> PluginGroupBuilder {
95 let mut group = PluginGroupBuilder::start::<Self>();
96
97 group = group
99 .add(WindowPlugin {
100 primary_window: Some(Window {
101 title: self.title,
102 name: Some("waterdropengine".into()),
103 resolution: self.resolution.into(),
104 present_mode: PresentMode::AutoVsync,
105 fit_canvas_to_parent: true,
106 prevent_default_event_handling: false,
107 window_theme: Some(WindowTheme::Dark),
108 enabled_buttons: bevy::window::EnabledButtons {
109 maximize: true,
110 ..Default::default()
111 },
112 visible: true,
113 ..default()
114 }),
115 ..default()
116 })
117 .add::<WinitPlugin>(WinitPlugin::default())
118 .add(AccessibilityPlugin);
119
120 group
121 }
122}
123
124pub(crate) fn send_surface_resized(
126 mut events_writer: MessageWriter<SurfaceResized>,
127 mut events_reader: MessageReader<WindowResized>,
128 window: Query<&Window>
129) {
130 for _ in events_reader.read() {
131 if let Ok(window) = window.single() {
132 let (width, height) = (
133 window.resolution.physical_width().max(1),
134 window.resolution.physical_height().max(1)
135 );
136
137 if width == 0 && height == 0 {
139 continue;
140 }
141
142 events_writer.write(SurfaceResized { width, height });
144 }
145 }
146}
147
148pub(crate) fn extract_surface_size(
150 render_instance: ResMut<RenderInstance>,
151 windows: ExtractWorld<Query<&Window>>
152) {
153 if windows.iter().count() == 0 {
155 return;
156 }
157
158 let window = windows.single().unwrap();
160 let (width, height) = (
161 window.resolution.physical_width().max(1),
162 window.resolution.physical_height().max(1)
163 );
164
165 let mut render_instance = render_instance.0.write().unwrap();
167 let old_size = render_instance.surface_config.as_ref().unwrap();
168 if width == old_size.width && height == old_size.height {
169 return;
170 }
171
172 let surface_config = render_instance.surface_config.as_mut().unwrap();
174 surface_config.width = width;
175 surface_config.height = height;
176
177 instance::resize(
178 &render_instance.device,
179 render_instance.surface.as_ref().unwrap(),
180 render_instance.surface_config.as_ref().unwrap()
181 );
182}