mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Support to set window theme and expose system window theme changed event (#8593)
# Objective - Fixes https://github.com/bevyengine/bevy/issues/8586. ## Solution - Add `preferred_theme` field to `Window` and set it when window creation - Add `window_theme` field to `InternalWindowState` to store current window theme - Expose winit `WindowThemeChanged` event --------- Co-authored-by: hate <15314665+hate@users.noreply.github.com> Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François <mockersf@gmail.com>
This commit is contained in:
parent
25add57614
commit
b72b15465d
8 changed files with 102 additions and 8 deletions
|
@ -7,6 +7,8 @@ use bevy_reflect::{FromReflect, Reflect};
|
|||
#[cfg(feature = "serialize")]
|
||||
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
|
||||
|
||||
use crate::WindowTheme;
|
||||
|
||||
/// A window event that is sent whenever a window's logical size has changed.
|
||||
#[derive(Debug, Clone, PartialEq, Reflect, FromReflect)]
|
||||
#[reflect(Debug, PartialEq)]
|
||||
|
@ -289,3 +291,19 @@ pub struct WindowMoved {
|
|||
/// Where the window moved to in physical pixels.
|
||||
pub position: IVec2,
|
||||
}
|
||||
|
||||
/// An event sent when system changed window theme.
|
||||
///
|
||||
/// This event is only sent when the window is relying on the system theme to control its appearance.
|
||||
/// i.e. It is only sent when [`Window::window_theme`](crate::window::Window::window_theme) is `None` and the system theme changes.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Reflect, FromReflect)]
|
||||
#[reflect(Debug, PartialEq)]
|
||||
#[cfg_attr(
|
||||
feature = "serialize",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
reflect(Serialize, Deserialize)
|
||||
)]
|
||||
pub struct WindowThemeChanged {
|
||||
pub window: Entity,
|
||||
pub theme: WindowTheme,
|
||||
}
|
||||
|
|
|
@ -84,7 +84,8 @@ impl Plugin for WindowPlugin {
|
|||
.add_event::<WindowScaleFactorChanged>()
|
||||
.add_event::<WindowBackendScaleFactorChanged>()
|
||||
.add_event::<FileDragAndDrop>()
|
||||
.add_event::<WindowMoved>();
|
||||
.add_event::<WindowMoved>()
|
||||
.add_event::<WindowThemeChanged>();
|
||||
|
||||
if let Some(primary_window) = &self.primary_window {
|
||||
app.world
|
||||
|
@ -121,7 +122,8 @@ impl Plugin for WindowPlugin {
|
|||
.register_type::<WindowScaleFactorChanged>()
|
||||
.register_type::<WindowBackendScaleFactorChanged>()
|
||||
.register_type::<FileDragAndDrop>()
|
||||
.register_type::<WindowMoved>();
|
||||
.register_type::<WindowMoved>()
|
||||
.register_type::<WindowThemeChanged>();
|
||||
|
||||
// Register window descriptor and related types
|
||||
app.register_type::<Window>()
|
||||
|
@ -136,7 +138,8 @@ impl Plugin for WindowPlugin {
|
|||
.register_type::<PresentMode>()
|
||||
.register_type::<InternalWindowState>()
|
||||
.register_type::<MonitorSelection>()
|
||||
.register_type::<WindowResizeConstraints>();
|
||||
.register_type::<WindowResizeConstraints>()
|
||||
.register_type::<WindowTheme>();
|
||||
|
||||
// Register `PathBuf` as it's used by `FileDragAndDrop`
|
||||
app.register_type::<PathBuf>();
|
||||
|
|
|
@ -184,6 +184,14 @@ pub struct Window {
|
|||
///
|
||||
/// - iOS / Android / Web: Unsupported.
|
||||
pub ime_position: Vec2,
|
||||
/// Sets a specific theme for the window.
|
||||
///
|
||||
/// If `None` is provided, the window will use the system theme.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - iOS / Android / Web: Unsupported.
|
||||
pub window_theme: Option<WindowTheme>,
|
||||
}
|
||||
|
||||
impl Default for Window {
|
||||
|
@ -208,6 +216,7 @@ impl Default for Window {
|
|||
fit_canvas_to_parent: false,
|
||||
prevent_default_event_handling: true,
|
||||
canvas: None,
|
||||
window_theme: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -832,3 +841,19 @@ pub enum WindowLevel {
|
|||
/// The window will always be on top of normal windows.
|
||||
AlwaysOnTop,
|
||||
}
|
||||
|
||||
/// The window theme variant to use.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect, FromReflect)]
|
||||
#[cfg_attr(
|
||||
feature = "serialize",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
reflect(Serialize, Deserialize)
|
||||
)]
|
||||
#[reflect(Debug, PartialEq)]
|
||||
pub enum WindowTheme {
|
||||
/// Use the light variant.
|
||||
Light,
|
||||
|
||||
/// Use the dark variant.
|
||||
Dark,
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use bevy_input::{
|
|||
ButtonState,
|
||||
};
|
||||
use bevy_math::Vec2;
|
||||
use bevy_window::{CursorIcon, WindowLevel};
|
||||
use bevy_window::{CursorIcon, WindowLevel, WindowTheme};
|
||||
|
||||
pub fn convert_keyboard_input(keyboard_input: &winit::event::KeyboardInput) -> KeyboardInput {
|
||||
KeyboardInput {
|
||||
|
@ -274,3 +274,17 @@ pub fn convert_window_level(window_level: WindowLevel) -> winit::window::WindowL
|
|||
WindowLevel::AlwaysOnTop => winit::window::WindowLevel::AlwaysOnTop,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_winit_theme(theme: winit::window::Theme) -> WindowTheme {
|
||||
match theme {
|
||||
winit::window::Theme::Light => WindowTheme::Light,
|
||||
winit::window::Theme::Dark => WindowTheme::Dark,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_window_theme(theme: WindowTheme) -> winit::window::Theme {
|
||||
match theme {
|
||||
WindowTheme::Light => winit::window::Theme::Light,
|
||||
WindowTheme::Dark => winit::window::Theme::Dark,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ use bevy_window::{
|
|||
exit_on_all_closed, CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, Ime,
|
||||
ReceivedCharacter, RequestRedraw, Window, WindowBackendScaleFactorChanged,
|
||||
WindowCloseRequested, WindowCreated, WindowFocused, WindowMoved, WindowResized,
|
||||
WindowScaleFactorChanged,
|
||||
WindowScaleFactorChanged, WindowThemeChanged,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
|
@ -54,6 +54,7 @@ use winit::{
|
|||
|
||||
use crate::accessibility::{AccessKitAdapters, AccessibilityPlugin, WinitActionHandlers};
|
||||
|
||||
use crate::converters::convert_winit_theme;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::web_resize::{CanvasParentResizeEventChannel, CanvasParentResizePlugin};
|
||||
|
||||
|
@ -227,6 +228,7 @@ struct WindowEvents<'w> {
|
|||
window_backend_scale_factor_changed: EventWriter<'w, WindowBackendScaleFactorChanged>,
|
||||
window_focused: EventWriter<'w, WindowFocused>,
|
||||
window_moved: EventWriter<'w, WindowMoved>,
|
||||
window_theme_changed: EventWriter<'w, WindowThemeChanged>,
|
||||
}
|
||||
|
||||
#[derive(SystemParam)]
|
||||
|
@ -613,6 +615,12 @@ pub fn winit_runner(mut app: App) {
|
|||
window: window_entity,
|
||||
}),
|
||||
},
|
||||
WindowEvent::ThemeChanged(theme) => {
|
||||
window_events.window_theme_changed.send(WindowThemeChanged {
|
||||
window: window_entity,
|
||||
theme: convert_winit_theme(theme),
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ use winit::{
|
|||
use crate::web_resize::{CanvasParentResizeEventChannel, WINIT_CANVAS_SELECTOR};
|
||||
use crate::{
|
||||
accessibility::{AccessKitAdapters, WinitActionHandlers},
|
||||
converters::{self, convert_window_level},
|
||||
converters::{self, convert_window_level, convert_window_theme, convert_winit_theme},
|
||||
get_best_videomode, get_fitting_videomode, WinitWindows,
|
||||
};
|
||||
|
||||
|
@ -62,6 +62,11 @@ pub(crate) fn create_window<'a>(
|
|||
&mut handlers,
|
||||
&mut accessibility_requested,
|
||||
);
|
||||
|
||||
if let Some(theme) = winit_window.theme() {
|
||||
window.window_theme = Some(convert_winit_theme(theme));
|
||||
}
|
||||
|
||||
window
|
||||
.resolution
|
||||
.set_scale_factor(winit_window.scale_factor());
|
||||
|
@ -296,6 +301,10 @@ pub(crate) fn changed_window(
|
|||
));
|
||||
}
|
||||
|
||||
if window.window_theme != cache.window.window_theme {
|
||||
winit_window.set_theme(window.window_theme.map(convert_window_theme));
|
||||
}
|
||||
|
||||
cache.window = window.clone();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use winit::{
|
|||
|
||||
use crate::{
|
||||
accessibility::{AccessKitAdapters, WinitActionHandler, WinitActionHandlers},
|
||||
converters::convert_window_level,
|
||||
converters::{convert_window_level, convert_window_theme},
|
||||
};
|
||||
|
||||
/// A resource which maps window entities to [`winit`] library windows.
|
||||
|
@ -92,6 +92,7 @@ impl WinitWindows {
|
|||
|
||||
winit_window_builder = winit_window_builder
|
||||
.with_window_level(convert_window_level(window.window_level))
|
||||
.with_theme(window.window_theme.map(convert_window_theme))
|
||||
.with_resizable(window.resizable)
|
||||
.with_decorations(window.decorations)
|
||||
.with_transparent(window.transparent);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
use bevy::{
|
||||
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||
prelude::*,
|
||||
window::{CursorGrabMode, PresentMode, WindowLevel},
|
||||
window::{CursorGrabMode, PresentMode, WindowLevel, WindowTheme},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
|
@ -18,6 +18,7 @@ fn main() {
|
|||
fit_canvas_to_parent: true,
|
||||
// Tells wasm not to override default event handling, like F5, Ctrl+R etc.
|
||||
prevent_default_event_handling: false,
|
||||
window_theme: Some(WindowTheme::Dark),
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
|
@ -28,6 +29,7 @@ fn main() {
|
|||
Update,
|
||||
(
|
||||
change_title,
|
||||
toggle_theme,
|
||||
toggle_cursor,
|
||||
toggle_vsync,
|
||||
cycle_cursor_icon,
|
||||
|
@ -93,6 +95,20 @@ fn toggle_cursor(mut windows: Query<&mut Window>, input: Res<Input<KeyCode>>) {
|
|||
}
|
||||
}
|
||||
|
||||
// This system will toggle the color theme used by the window
|
||||
fn toggle_theme(mut windows: Query<&mut Window>, input: Res<Input<KeyCode>>) {
|
||||
if input.just_pressed(KeyCode::F) {
|
||||
let mut window = windows.single_mut();
|
||||
|
||||
if let Some(current_theme) = window.window_theme {
|
||||
window.window_theme = match current_theme {
|
||||
WindowTheme::Light => Some(WindowTheme::Dark),
|
||||
WindowTheme::Dark => Some(WindowTheme::Light),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This system cycles the cursor's icon through a small set of icons when clicking
|
||||
fn cycle_cursor_icon(
|
||||
mut windows: Query<&mut Window>,
|
||||
|
|
Loading…
Reference in a new issue