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, 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 mut icon: ResMut<PrimaryWindowIcon>,
53 _main_thread: NonSendMarker,
54 primary_window: Query<Entity, With<PrimaryWindow>>
55) {
56 let Some(window_icon) = &icon.0 else { return };
57 let Ok(entity) = primary_window.single() else {
58 return;
59 };
60
61 let applied = WINIT_WINDOWS.with_borrow(|winit_windows| {
62 let Some(winit_window) = winit_windows.get_window(entity) else {
63 return false;
64 };
65 match winit::window::Icon::from_rgba(
66 window_icon.rgba.clone(),
67 window_icon.width,
68 window_icon.height
69 ) {
70 Ok(winit_icon) => winit_window.set_window_icon(Some(winit_icon)),
71 Err(err) => wde_logger::warn!("Failed to set window icon: {err}")
72 }
73 true
74 });
75 if applied {
76 icon.0 = None;
77 }
78}
79
80#[derive(Debug, Event, Message)]
83pub struct SurfaceResized {
84 pub width: u32,
85 pub height: u32
86}
87
88pub(crate) struct WindowPlugins {
89 pub title: String,
90 pub resolution: (u32, u32)
91}
92impl Default for WindowPlugins {
93 fn default() -> Self {
94 Self {
95 title: "WaterDropEngine".into(),
96 resolution: (600, 500)
97 }
98 }
99}
100impl PluginGroup for WindowPlugins {
101 fn build(self) -> PluginGroupBuilder {
102 let mut group = PluginGroupBuilder::start::<Self>();
103
104 group = group
106 .add(WindowPlugin {
107 primary_window: Some(Window {
108 title: self.title,
109 name: Some("waterdropengine".into()),
110 resolution: self.resolution.into(),
111 present_mode: PresentMode::AutoVsync,
112 fit_canvas_to_parent: true,
113 prevent_default_event_handling: false,
114 window_theme: Some(WindowTheme::Dark),
115 enabled_buttons: bevy::window::EnabledButtons {
116 maximize: true,
117 ..Default::default()
118 },
119 visible: true,
120 ..default()
121 }),
122 ..default()
123 })
124 .add::<WinitPlugin>(WinitPlugin::default())
125 .add(AccessibilityPlugin);
126
127 group
128 }
129}
130
131pub(crate) fn send_surface_resized(
133 mut events_writer: MessageWriter<SurfaceResized>,
134 mut events_reader: MessageReader<WindowResized>,
135 window: Query<&Window>
136) {
137 for _ in events_reader.read() {
138 if let Ok(window) = window.single() {
139 let (width, height) = (
140 window.resolution.physical_width().max(1),
141 window.resolution.physical_height().max(1)
142 );
143
144 if width == 0 && height == 0 {
146 continue;
147 }
148
149 events_writer.write(SurfaceResized { width, height });
151 }
152 }
153}
154
155pub(crate) fn extract_surface_size(
157 render_instance: ResMut<RenderInstance>,
158 windows: ExtractWorld<Query<&Window>>
159) {
160 if windows.iter().count() == 0 {
162 return;
163 }
164
165 let window = windows.single().unwrap();
167 let (width, height) = (
168 window.resolution.physical_width().max(1),
169 window.resolution.physical_height().max(1)
170 );
171
172 let mut render_instance = render_instance.0.write().unwrap();
174 let old_size = render_instance.surface_config.as_ref().unwrap();
175 if width == old_size.width && height == old_size.height {
176 return;
177 }
178
179 let surface_config = render_instance.surface_config.as_mut().unwrap();
181 surface_config.width = width;
182 surface_config.height = height;
183
184 instance::resize(
185 &render_instance.device,
186 render_instance.surface.as_ref().unwrap(),
187 render_instance.surface_config.as_ref().unwrap()
188 );
189}