Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support to set window theme and expose system window theme changed event #8593

Merged
merged 19 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions crates/bevy_window/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -290,3 +292,18 @@ pub struct WindowMoved {
/// Where the window moved to in physical pixels.
pub position: IVec2,
}

/// An event that is sent when system changed window theme.
lewiszlw marked this conversation as resolved.
Show resolved Hide resolved
///
/// It is only sent when [`Window::preferred_theme`](crate::window::Window::preferred_theme) is `None`.
lewiszlw marked this conversation as resolved.
Show resolved Hide resolved
#[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,
}
9 changes: 6 additions & 3 deletions crates/bevy_window/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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>()
Expand All @@ -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>();
Expand Down
49 changes: 49 additions & 0 deletions crates/bevy_window/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 / x11: Unsupported.
lewiszlw marked this conversation as resolved.
Show resolved Hide resolved
pub preferred_theme: Option<WindowTheme>,
lewiszlw marked this conversation as resolved.
Show resolved Hide resolved
}

impl Default for Window {
Expand All @@ -208,6 +216,7 @@ impl Default for Window {
fit_canvas_to_parent: false,
prevent_default_event_handling: true,
canvas: None,
preferred_theme: None,
}
}
}
Expand Down Expand Up @@ -283,6 +292,24 @@ impl Window {
pub fn set_physical_cursor_position(&mut self, position: Option<DVec2>) {
self.internal.physical_cursor_position = position;
}

/// The window's current theme
///
/// ## Platform-specific
///
/// - iOS / Android / Web / x11: Unsupported.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// - iOS / Android / Web / x11: Unsupported.
/// Unsupported on iOS, Android, Web and X11. The return value is always the last set value or `None` on those platforms.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a bit of an issue here.

On unsupported platforms, winit always returns None when using window.theme(). However, in this bevy implementation, it's possible to set self.internal.window_theme to Some(Theme) in set_window_theme. Now this will return a misleading value.

I suggest conditionally returning None here, something like

if cfg!(any(target_os = "ios", target_os = "android", target_family = "wasm", feature = "x11")) {
  None
} else {
  self.internal.window_theme
}

pub fn window_theme(&self) -> Option<WindowTheme> {
self.internal.window_theme
}

/// Set the window's theme
///
/// ## Platform-specific
///
/// - iOS / Android / Web / x11: Unsupported.
lewiszlw marked this conversation as resolved.
Show resolved Hide resolved
pub fn set_window_theme(&mut self, theme: Option<WindowTheme>) {
self.internal.window_theme = theme;
}
}

/// The size limits on a window.
Expand Down Expand Up @@ -658,6 +685,12 @@ pub struct InternalWindowState {
maximize_request: Option<bool>,
/// Unscaled cursor position.
physical_cursor_position: Option<DVec2>,
/// The window's current theme
///
/// ## Platform-specific
///
/// - iOS / Android / Web / x11: Unsupported.
lewiszlw marked this conversation as resolved.
Show resolved Hide resolved
window_theme: Option<WindowTheme>,
}

impl InternalWindowState {
Expand Down Expand Up @@ -832,3 +865,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,
}
16 changes: 15 additions & 1 deletion crates/bevy_winit/src/converters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
}
}
lewiszlw marked this conversation as resolved.
Show resolved Hide resolved
lewiszlw marked this conversation as resolved.
Show resolved Hide resolved
10 changes: 9 additions & 1 deletion crates/bevy_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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};

Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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),
});
}
_ => {}
}

Expand Down
13 changes: 12 additions & 1 deletion crates/bevy_winit/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -62,6 +62,11 @@ pub(crate) fn create_window<'a>(
&mut handlers,
&mut accessibility_requested,
);

if let Some(theme) = winit_window.theme() {
window.set_window_theme(Some(convert_winit_theme(theme)));
}

window
.resolution
.set_scale_factor(winit_window.scale_factor());
Expand Down Expand Up @@ -296,6 +301,12 @@ pub(crate) fn changed_window(
));
}

if window.window_theme() != cache.window.window_theme() {
if let Some(theme) = window.window_theme() {
winit_window.set_theme(Some(convert_window_theme(theme)));
}
lewiszlw marked this conversation as resolved.
Show resolved Hide resolved
}

cache.window = window.clone();
}
}
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_winit/src/winit_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -92,6 +92,7 @@ impl WinitWindows {

winit_window_builder = winit_window_builder
.with_window_level(convert_window_level(window.window_level))
.with_theme(window.preferred_theme.map(convert_window_theme))
.with_resizable(window.resizable)
.with_decorations(window.decorations)
.with_transparent(window.transparent);
Expand Down
18 changes: 17 additions & 1 deletion examples/window/window_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*,
window::{CursorGrabMode, PresentMode, WindowLevel},
window::{CursorGrabMode, PresentMode, WindowLevel, WindowTheme},
};

fn main() {
Expand All @@ -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,
preferred_theme: Some(WindowTheme::Dark),
..default()
}),
..default()
Expand All @@ -28,6 +29,7 @@ fn main() {
Update,
(
change_title,
toggle_theme,
toggle_cursor,
toggle_vsync,
cycle_cursor_icon,
Expand Down Expand Up @@ -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() {
lewiszlw marked this conversation as resolved.
Show resolved Hide resolved
window.set_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>,
Expand Down