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

feat: add a function to set theme dynamically #937

Merged
merged 52 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
89de496
Add a function to set theme dynamically
Legend-Master Jun 9, 2024
3a107a2
Always refresh title bar and clippy
Legend-Master Jun 9, 2024
f0b15fb
Don't PostMessageW
Legend-Master Jun 9, 2024
fbacc5f
Add change file
Legend-Master Jun 9, 2024
1691178
windows only
Legend-Master Jun 9, 2024
5d612d8
Update example
Legend-Master Jun 9, 2024
c19e00c
Doc comment
Legend-Master Jun 9, 2024
7ba5d70
Refactor theme example
Legend-Master Jun 10, 2024
e66ba7c
Send ThemeChanged event
Legend-Master Jun 10, 2024
f0f7e40
Move SET_THEME_MSG_ID down
Legend-Master Jun 10, 2024
30b37b2
Fix indent
Legend-Master Jun 10, 2024
29e6376
Clippy
Legend-Master Jun 10, 2024
6bab6a3
Only redraw title bar on set_theme
Legend-Master Jun 10, 2024
400117d
Implement for mac
Legend-Master Jun 14, 2024
e6626d8
Change file
Legend-Master Jun 14, 2024
81cc889
Merge branch 'dev' into set-theme-windows
Legend-Master Jun 14, 2024
80a743a
Fix mac compile
Legend-Master Jun 14, 2024
d42d1e6
#[allow(unused)] for linux
Legend-Master Jun 14, 2024
92a243b
Merge branch 'dev' into set-theme-windows
Legend-Master Jun 27, 2024
d2c359e
Merge branch 'dev' into set-theme-windows
Legend-Master Jun 28, 2024
efa545b
Merge branch 'dev' into set-theme-windows
Legend-Master Jun 30, 2024
96acfc3
Merge branch 'dev' into set-theme-windows
Legend-Master Jul 25, 2024
061547c
Try implement for linux
Legend-Master Jul 25, 2024
9fa9c41
Set theme state on mac
Legend-Master Jul 25, 2024
154b3f2
Fix linux import
Legend-Master Jul 25, 2024
2fe54be
Fix set and get preferred_theme on linux
Legend-Master Jul 25, 2024
11c244d
missing deref
Legend-Master Jul 25, 2024
595e4f5
Merge branch 'dev' into set-theme-windows
Legend-Master Aug 17, 2024
6ebb6a2
Fix for windows 0.58
Legend-Master Aug 17, 2024
7e044ea
Merge branch 'dev' into set-theme-windows
Legend-Master Aug 28, 2024
2c6de75
Merge branch 'dev' into set-theme-windows
Legend-Master Sep 4, 2024
c18000f
Update src/platform_impl/windows/event_loop.rs
Legend-Master Sep 18, 2024
740ba92
Rename to EMIT_THEME_MSG_ID
Legend-Master Sep 18, 2024
9372ae6
Add platform specific note on set_theme
Legend-Master Sep 18, 2024
6e9252f
Add set_theme to event loot target
Legend-Master Sep 18, 2024
1e33dee
Missing import
Legend-Master Sep 18, 2024
70f40ab
Wrong import
Legend-Master Sep 18, 2024
9c399bd
Wrong import
Legend-Master Sep 18, 2024
c626141
Use 19 on older versions
Legend-Master Sep 18, 2024
b44c378
Apply suggestions from code review
Legend-Master Sep 18, 2024
5f090ef
Support set theme on event loop for windows
Legend-Master Sep 18, 2024
ce0a204
Forgot to exclude mobile
Legend-Master Sep 18, 2024
bceaae9
Ignore post message error instead of panic
Legend-Master Sep 18, 2024
1925ecb
Use parking_lot::Mutex on linux preferred_theme
Legend-Master Sep 18, 2024
a908a34
Fix compile
Legend-Master Sep 18, 2024
40ada0e
Only send change theme event to owned windows
Legend-Master Sep 19, 2024
affd0b5
change file
amrbashir Sep 19, 2024
1fee12a
use `SendMessageW` with `WM_SETTINGCHANGE` instead
amrbashir Sep 19, 2024
715f37a
Cleanup manual theme events and duplicated code
amrbashir Sep 19, 2024
1b4b5b4
cleanup example
amrbashir Sep 19, 2024
e595125
use RefCell on Linux
amrbashir Sep 19, 2024
0b1daad
always redraw titlebar
amrbashir Sep 19, 2024
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
5 changes: 5 additions & 0 deletions .changes/set-theme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": "patch"
---

Add a function `Window::set_theme` and `EventLoopWindowTarget::set_them`to set theme after window or event loop creation.
35 changes: 22 additions & 13 deletions examples/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0

#[allow(clippy::single_match)]
use tao::{event::KeyEvent, keyboard::KeyCode};

fn main() {
use tao::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
window::{Theme, WindowBuilder},
};

env_logger::init();
Expand All @@ -20,22 +21,30 @@ fn main() {
.unwrap();

println!("Initial theme: {:?}", window.theme());
println!("Press D for Dark Mode");
println!("Press L for Light Mode");
println!("Press A for Auto Mode");

event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;

match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
Event::WindowEvent {
event: WindowEvent::ThemeChanged(theme),
window_id,
..
} if window_id == window.id() => {
println!("Theme is changed: {:?}", theme)
}
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput {
event: KeyEvent { physical_key, .. },
..
} => match physical_key {
KeyCode::KeyD => window.set_theme(Some(Theme::Dark)),
KeyCode::KeyL => window.set_theme(Some(Theme::Light)),
KeyCode::KeyA => window.set_theme(None),
_ => {}
},
WindowEvent::ThemeChanged(theme) => {
println!("Theme is changed: {theme:?}")
}
_ => (),
},
_ => (),
}
});
Expand Down
27 changes: 25 additions & 2 deletions src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ use instant::Instant;
use std::{error, fmt, marker::PhantomData, ops::Deref};

use crate::{
dpi::PhysicalPosition, error::ExternalError, event::Event, monitor::MonitorHandle, platform_impl,
window::ProgressBarState,
dpi::PhysicalPosition,
error::ExternalError,
event::Event,
monitor::MonitorHandle,
platform_impl,
window::{ProgressBarState, Theme},
};

/// Provides a way to retrieve events from the system and from the windows that were registered to
Expand Down Expand Up @@ -296,6 +300,25 @@ impl<T> EventLoopWindowTarget<T> {
#[cfg(any(target_os = "linux", target_os = "macos"))]
self.p.set_progress_bar(_progress)
}

/// Sets the theme for the application.
///
/// ## Platform-specific
///
/// - **iOS / Android:** Unsupported.
#[inline]
pub fn set_theme(&self, theme: Option<Theme>) {
#[cfg(any(
windows,
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "macos",
))]
self.p.set_theme(theme)
}
}

#[cfg(feature = "rwh_05")]
Expand Down
26 changes: 24 additions & 2 deletions src/platform_impl/linux/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use gtk::{
cairo, gdk, gio,
glib::{self},
prelude::*,
Settings,
};

use crate::{
Expand All @@ -33,7 +34,9 @@ use crate::{
keyboard::ModifiersState,
monitor::MonitorHandle as RootMonitorHandle,
platform_impl::platform::{device, DEVICE_ID},
window::{CursorIcon, Fullscreen, ProgressBarState, ResizeDirection, WindowId as RootWindowId},
window::{
CursorIcon, Fullscreen, ProgressBarState, ResizeDirection, Theme, WindowId as RootWindowId,
},
};

use super::{
Expand Down Expand Up @@ -153,7 +156,17 @@ impl<T> EventLoopWindowTarget<T> {
.window_requests_tx
.send((WindowId::dummy(), WindowRequest::ProgressBarState(progress)))
{
log::warn!("Fail to send update progress bar request: {}", e);
log::warn!("Fail to send update progress bar request: {e}");
}
}

#[inline]
pub fn set_theme(&self, theme: Option<Theme>) {
if let Err(e) = self
.window_requests_tx
.send((WindowId::dummy(), WindowRequest::SetTheme(theme)))
{
log::warn!("Fail to send update theme request: {e}");
}
}
}
Expand Down Expand Up @@ -392,6 +405,7 @@ impl<T: 'static> EventLoop<T> {
};
}
WindowRequest::ProgressBarState(_) => unreachable!(),
WindowRequest::SetTheme(_) => unreachable!(),
WindowRequest::WireUpEvents {
transparent,
fullscreen,
Expand Down Expand Up @@ -857,6 +871,14 @@ impl<T: 'static> EventLoop<T> {
WindowRequest::ProgressBarState(state) => {
taskbar.update(state);
}
WindowRequest::SetTheme(theme) => {
if let Some(settings) = Settings::default() {
match theme {
Some(Theme::Dark) => settings.set_gtk_application_prefer_dark_theme(true),
Some(Theme::Light) | None => settings.set_gtk_application_prefer_dark_theme(false),
}
}
}
_ => unreachable!(),
}
}
Expand Down
19 changes: 15 additions & 4 deletions src/platform_impl/linux/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub struct Window {
inner_size_constraints: RefCell<WindowSizeConstraints>,
/// Draw event Sender
draw_tx: crossbeam_channel::Sender<WindowId>,
preferred_theme: Option<Theme>,
preferred_theme: RefCell<Option<Theme>>,
}

impl Window {
Expand Down Expand Up @@ -306,7 +306,7 @@ impl Window {
minimized,
fullscreen: RefCell::new(attributes.fullscreen),
inner_size_constraints: RefCell::new(attributes.inner_size_constraints),
preferred_theme,
preferred_theme: RefCell::new(preferred_theme),
};

win.set_skip_taskbar(pl_attribs.skip_taskbar);
Expand Down Expand Up @@ -385,7 +385,7 @@ impl Window {
minimized,
fullscreen: RefCell::new(None),
inner_size_constraints: RefCell::new(WindowSizeConstraints::default()),
preferred_theme: None,
preferred_theme: RefCell::new(None),
};

Ok(win)
Expand Down Expand Up @@ -941,7 +941,7 @@ impl Window {
}

pub fn theme(&self) -> Theme {
if let Some(theme) = self.preferred_theme {
if let Some(theme) = *self.preferred_theme.borrow() {
return theme;
}

Expand All @@ -954,6 +954,16 @@ impl Window {

Theme::Light
}

pub fn set_theme(&self, theme: Option<Theme>) {
*self.preferred_theme.borrow_mut() = theme;
if let Err(e) = self
.window_requests_tx
.send((WindowId::dummy(), WindowRequest::SetTheme(theme)))
{
log::warn!("Fail to send set theme request: {e}");
}
}
}

// We need GtkWindow to initialize WebView, so we have to keep it in the field.
Expand Down Expand Up @@ -992,6 +1002,7 @@ pub enum WindowRequest {
},
SetVisibleOnAllWorkspaces(bool),
ProgressBarState(ProgressBarState),
SetTheme(Option<Theme>),
}

impl Drop for Window {
Expand Down
9 changes: 8 additions & 1 deletion src/platform_impl/macos/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ use crate::{
util::{self, IdRef},
},
platform_impl::set_progress_indicator,
window::ProgressBarState,
window::{ProgressBarState, Theme},
};

use super::window::set_ns_theme;

#[derive(Default)]
pub struct PanicInfo {
inner: Cell<Option<Box<dyn Any + Send + 'static>>>,
Expand Down Expand Up @@ -120,6 +122,11 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub fn set_progress_bar(&self, progress: ProgressBarState) {
set_progress_indicator(progress);
}

#[inline]
pub fn set_theme(&self, theme: Option<Theme>) {
set_ns_theme(theme)
}
}

pub struct EventLoop<T: 'static> {
Expand Down
23 changes: 16 additions & 7 deletions src/platform_impl/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,17 +337,20 @@ pub(super) fn get_ns_theme() -> Theme {
}
}

pub(super) fn set_ns_theme(theme: Theme) {
let name = match theme {
Theme::Dark => "NSAppearanceNameDarkAqua",
Theme::Light => "NSAppearanceNameAqua",
};
pub(super) fn set_ns_theme(theme: Option<Theme>) {
unsafe {
let app_class = class!(NSApplication);
let app: id = msg_send![app_class, sharedApplication];
let has_theme: BOOL = msg_send![app, respondsToSelector: sel!(effectiveAppearance)];
if has_theme == YES {
let name = NSString::alloc(nil).init_str(name);
let name = if let Some(theme) = theme {
NSString::alloc(nil).init_str(match theme {
Theme::Dark => "NSAppearanceNameDarkAqua",
Theme::Light => "NSAppearanceNameAqua",
})
} else {
nil
};
let appearance: id = msg_send![class!(NSAppearance), appearanceNamed: name];
let _: () = msg_send![app, setAppearance: appearance];
}
Expand Down Expand Up @@ -547,7 +550,7 @@ impl UnownedWindow {

match cloned_preferred_theme {
Some(theme) => {
set_ns_theme(theme);
set_ns_theme(Some(theme));
let mut state = window.shared_state.lock().unwrap();
state.current_theme = theme.clone();
}
Expand Down Expand Up @@ -1417,6 +1420,12 @@ impl UnownedWindow {
state.current_theme
}

pub fn set_theme(&self, theme: Option<Theme>) {
set_ns_theme(theme);
let mut state = self.shared_state.lock().unwrap();
state.current_theme = theme.unwrap_or_else(get_ns_theme);
}

pub fn set_content_protection(&self, enabled: bool) {
unsafe {
let _: () = msg_send![*self.ns_window, setSharingType: !enabled as i32];
Expand Down
46 changes: 21 additions & 25 deletions src/platform_impl/windows/dark_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use once_cell::sync::Lazy;
use windows::{
core::{s, w, PCSTR, PSTR},
Win32::{
Foundation::{BOOL, HANDLE, HMODULE, HWND},
Foundation::{BOOL, HANDLE, HMODULE, HWND, WPARAM},
Graphics::Dwm::{DwmSetWindowAttribute, DWMWINDOWATTRIBUTE},
System::LibraryLoader::*,
UI::{Accessibility::*, WindowsAndMessaging::*},
},
Expand Down Expand Up @@ -160,40 +161,35 @@ pub fn allow_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) {
}
}

type SetWindowCompositionAttribute =
unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL;
static SET_WINDOW_COMPOSITION_ATTRIBUTE: Lazy<Option<SetWindowCompositionAttribute>> =
Lazy::new(|| get_function!("user32.dll", SetWindowCompositionAttribute));

type WINDOWCOMPOSITIONATTRIB = u32;
const WCA_USEDARKMODECOLORS: WINDOWCOMPOSITIONATTRIB = 26;
#[repr(C)]
struct WINDOWCOMPOSITIONATTRIBDATA {
Attrib: WINDOWCOMPOSITIONATTRIB,
pvData: *mut c_void,
cbData: usize,
}

fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool) {
// SetWindowCompositionAttribute needs a bigbool (i32), not bool.
let mut is_dark_mode_bigbool: i32 = is_dark_mode.into();

if let Some(ver) = *WIN10_BUILD_VERSION {
if ver < 18362 {
if ver < 17763 {
let mut is_dark_mode_bigbool: i32 = is_dark_mode.into();
unsafe {
let _ = SetPropW(
hwnd,
w!("UseImmersiveDarkModeColors"),
HANDLE(&mut is_dark_mode_bigbool as *mut _ as _),
);
}
} else if let Some(set_window_composition_attribute) = *SET_WINDOW_COMPOSITION_ATTRIBUTE {
let mut data = WINDOWCOMPOSITIONATTRIBDATA {
Attrib: WCA_USEDARKMODECOLORS,
pvData: &mut is_dark_mode_bigbool as *mut _ as _,
cbData: std::mem::size_of_val(&is_dark_mode_bigbool) as _,
} else {
// https://github.com/MicrosoftDocs/sdk-api/pull/966/files
let dwmwa_use_immersive_dark_mode = if ver > 18985 {
DWMWINDOWATTRIBUTE(20)
} else {
DWMWINDOWATTRIBUTE(19)
};
let _ = unsafe { set_window_composition_attribute(hwnd, &mut data as *mut _) };
let dark_mode = BOOL::from(is_dark_mode);
unsafe {
let _ = DwmSetWindowAttribute(
hwnd,
dwmwa_use_immersive_dark_mode,
&dark_mode as *const BOOL as *const c_void,
std::mem::size_of::<BOOL>() as u32,
);
}
unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, None, None) };
unsafe { DefWindowProcW(hwnd, WM_NCACTIVATE, WPARAM(true.into()), None) };
}
}
}
Expand Down
Loading
Loading