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(core): add a new function to set theme dynamically #10210

Open
wants to merge 23 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/tauri-runtime-wry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ wry = { version = "0.44.0", default-features = false, features = [
"os-webview",
"linux-body",
] }
tao = { version = "0.30", default-features = false, features = ["rwh_06"] }
tao = { version = "0.30.1", default-features = false, features = ["rwh_06"] }
tauri-runtime = { version = "2.0.0-rc.12", path = "../tauri-runtime" }
tauri-utils = { version = "2.0.0-rc.12", path = "../tauri-utils" }
raw-window-handle = "0.6"
Expand Down
35 changes: 35 additions & 0 deletions crates/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,7 @@ pub enum WindowMessage {
SetIgnoreCursorEvents(bool),
SetProgressBar(ProgressBarState),
SetTitleBarStyle(tauri_utils::TitleBarStyle),
SetTheme(Option<Theme>),
DragWindow,
ResizeDragWindow(tauri_runtime::ResizeDirection),
RequestRedraw,
Expand Down Expand Up @@ -2010,6 +2011,13 @@ impl<T: UserEvent> WindowDispatch<T> for WryWindowDispatcher<T> {
Message::Window(self.window_id, WindowMessage::SetTitleBarStyle(style)),
)
}

fn set_theme(&self, theme: Option<Theme>) -> Result<()> {
send_user_message(
&self.context,
Message::Window(self.window_id, WindowMessage::SetTheme(theme)),
)
}
}

#[derive(Clone)]
Expand Down Expand Up @@ -2262,6 +2270,18 @@ impl<T: UserEvent> RuntimeHandle<T> for WryHandle<T> {
.map_err(|_| Error::FailedToGetCursorPosition)
}

fn set_theme(&self, theme: Option<Theme>) {
self
.context
.main_thread
.window_target
.set_theme(match theme {
Some(Theme::Light) => Some(TaoTheme::Light),
Some(Theme::Dark) => Some(TaoTheme::Dark),
_ => None,
});
}

#[cfg(target_os = "macos")]
fn show(&self) -> tauri_runtime::Result<()> {
send_user_message(
Expand Down Expand Up @@ -2541,6 +2561,14 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
.map_err(|_| Error::FailedToGetCursorPosition)
}

fn set_theme(&self, theme: Option<Theme>) {
self.event_loop.set_theme(match theme {
Some(Theme::Light) => Some(TaoTheme::Light),
Some(Theme::Dark) => Some(TaoTheme::Dark),
_ => None,
});
}

#[cfg(target_os = "macos")]
fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
self
Expand Down Expand Up @@ -2973,6 +3001,13 @@ fn handle_user_message<T: UserEvent>(
}
};
}
WindowMessage::SetTheme(theme) => {
window.set_theme(match theme {
amrbashir marked this conversation as resolved.
Show resolved Hide resolved
Some(Theme::Light) => Some(TaoTheme::Light),
Some(Theme::Dark) => Some(TaoTheme::Dark),
_ => None,
});
}
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions crates/tauri-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ pub trait RuntimeHandle<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 'st

fn cursor_position(&self) -> Result<PhysicalPosition<f64>>;

fn set_theme(&self, theme: Option<Theme>);

/// Shows the application, but does not automatically focus it.
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
Expand Down Expand Up @@ -402,6 +404,8 @@ pub trait Runtime<T: UserEvent>: Debug + Sized + 'static {

fn cursor_position(&self) -> Result<PhysicalPosition<f64>>;

fn set_theme(&self, theme: Option<Theme>);

/// Sets the activation policy for the application.
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
Expand Down Expand Up @@ -799,4 +803,12 @@ pub trait WindowDispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 's
///
/// - **Linux / Windows / iOS / Android:** Unsupported.
fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()>;

/// Sets the theme for this window.
///
/// ## Platform-specific
///
/// - **Linux / macOS**: Theme is app-wide and not specific to this window.
/// - **iOS / Android:** Unsupported.
fn set_theme(&self, theme: Option<Theme>) -> Result<()>;
}
2 changes: 2 additions & 0 deletions crates/tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
("set_progress_bar", false),
("set_icon", false),
("set_title_bar_style", false),
("set_theme", false),
("toggle_maximize", false),
// internal
("internal_toggle_maximize", true),
Expand Down Expand Up @@ -140,6 +141,7 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
("app_show", false),
("app_hide", false),
("default_window_icon", false),
("set_app_theme", false),
],
),
(
Expand Down
26 changes: 26 additions & 0 deletions crates/tauri/permissions/app/autogenerated/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,32 @@ Denies the name command without any pre-configured scope.
<tr>
<td>

`core:app:allow-set-app-theme`

</td>
<td>

Enables the set_app_theme command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`core:app:deny-set-app-theme`

</td>
<td>

Denies the set_app_theme command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`core:app:allow-tauri-version`

</td>
Expand Down
26 changes: 26 additions & 0 deletions crates/tauri/permissions/window/autogenerated/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,32 @@ Denies the set_skip_taskbar command without any pre-configured scope.
<tr>
<td>

`core:window:allow-set-theme`

</td>
<td>

Enables the set_theme command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`core:window:deny-set-theme`

</td>
<td>

Denies the set_theme command without any pre-configured scope.

</td>
</tr>

<tr>
<td>

`core:window:allow-set-title`

</td>
Expand Down
2 changes: 1 addition & 1 deletion crates/tauri/scripts/bundle.global.js

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions crates/tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,30 @@ macro_rules! shared_app_impl {
})
}

/// Set the app theme.
pub fn set_theme(&self, theme: Option<Theme>) {
#[cfg(windows)]
for window in self.manager.windows().values() {
if let (Some(window_menu), Ok(hwnd)) = (&*window.menu_lock(), window.hwnd()) {
let raw_hwnd = hwnd.0 as isize;
let menu = window_menu.menu.clone();
let _ = self.run_on_main_thread(move || {
let _ = menu.inner().set_theme_for_hwnd(
raw_hwnd,
theme
.map(crate::menu::map_to_menu_theme)
.unwrap_or(muda::MenuTheme::Auto),
);
});
};
}
match self.runtime() {
RuntimeOrDispatch::Runtime(h) => h.set_theme(theme),
RuntimeOrDispatch::RuntimeHandle(h) => h.set_theme(theme),
_ => unreachable!(),
}
}

/// Returns the default window icon.
pub fn default_window_icon(&self) -> Option<&Image<'_>> {
self.manager.window.default_icon.as_ref()
Expand Down
8 changes: 8 additions & 0 deletions crates/tauri/src/app/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use tauri_utils::Theme;

use crate::{
command,
plugin::{Builder, TauriPlugin},
Expand Down Expand Up @@ -50,6 +52,11 @@ pub fn default_window_icon<R: Runtime>(
})
}

#[command(root = "crate")]
pub async fn set_app_theme<R: Runtime>(app: AppHandle<R>, theme: Option<Theme>) {
Copy link
Contributor Author

@Legend-Master Legend-Master Sep 20, 2024

Choose a reason for hiding this comment

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

This will cause a deadlock on plugins without async, lock happened on these two places, I'm not sure how should we handle this, seems like it's because this call triggers a ThemeChanged event while having a lock on the plugins when invoked

pub fn extend_api(&self, plugin: &str, invoke: Invoke<R>) -> bool {

manager

app.set_theme(theme);
}

pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("app")
.invoke_handler(crate::generate_handler![
Expand All @@ -59,6 +66,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
app_show,
app_hide,
default_window_icon,
set_app_theme,
])
.build()
}
12 changes: 12 additions & 0 deletions crates/tauri/src/test/mock_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ impl<T: UserEvent> RuntimeHandle<T> for MockRuntimeHandle {
unimplemented!()
}

fn set_theme(&self, theme: Option<Theme>) {
unimplemented!()
}

/// Shows the application, but does not automatically focus it.
#[cfg(target_os = "macos")]
fn show(&self) -> Result<()> {
Expand Down Expand Up @@ -951,6 +955,10 @@ impl<T: UserEvent> WindowDispatch<T> for MockWindowDispatcher {
) -> Result<()> {
Ok(())
}

fn set_theme(&self, theme: Option<Theme>) -> Result<()> {
Ok(())
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -1092,6 +1100,10 @@ impl<T: UserEvent> Runtime<T> for MockRuntime {
unimplemented!()
}

fn set_theme(&self, theme: Option<Theme>) {
unimplemented!()
}

#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
fn set_activation_policy(&mut self, activation_policy: tauri_runtime::ActivationPolicy) {}
Expand Down
10 changes: 9 additions & 1 deletion crates/tauri/src/webview/webview_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ use crate::{
},
};
use serde::Serialize;
use tauri_utils::config::{WebviewUrl, WindowConfig};
use tauri_utils::{
config::{WebviewUrl, WindowConfig},
Theme,
};
use url::Url;

use crate::{
Expand Down Expand Up @@ -1582,6 +1585,11 @@ impl<R: Runtime> WebviewWindow<R> {
pub fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> crate::Result<()> {
self.webview.window().set_title_bar_style(style)
}

/// Set the window theme.
pub fn set_theme(&self, theme: Option<Theme>) -> crate::Result<()> {
self.webview.window().set_theme(theme)
}
}

/// Desktop webview setters and actions.
Expand Down
29 changes: 29 additions & 0 deletions crates/tauri/src/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1981,6 +1981,7 @@ tauri::Builder::default()
})
.map_err(Into::into)
}

/// Sets the title bar style. **macOS only**.
pub fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> crate::Result<()> {
self
Expand All @@ -1989,6 +1990,34 @@ tauri::Builder::default()
.set_title_bar_style(style)
.map_err(Into::into)
}

/// Sets the theme for this window.
///
/// ## Platform-specific
///
/// - **Linux / macOS**: Theme is app-wide and not specific to this window.
/// - **iOS / Android:** Unsupported.
pub fn set_theme(&self, theme: Option<Theme>) -> crate::Result<()> {
self
.window
.dispatcher
.set_theme(theme)
.map_err(Into::<crate::Error>::into)?;
#[cfg(windows)]
if let (Some(window_menu), Ok(hwnd)) = (&*self.menu_lock(), self.hwnd()) {
let raw_hwnd = hwnd.0 as isize;
let menu = window_menu.menu.clone();
self.run_on_main_thread(move || {
let _ = menu.inner().set_theme_for_hwnd(
raw_hwnd,
theme
.map(crate::menu::map_to_menu_theme)
.unwrap_or(muda::MenuTheme::Auto),
);
})?;
};
Ok(())
}
}

/// Progress bar state.
Expand Down
2 changes: 2 additions & 0 deletions crates/tauri/src/window/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ mod desktop_commands {
setter!(set_visible_on_all_workspaces, bool);
setter!(set_title_bar_style, TitleBarStyle);
setter!(set_size_constraints, WindowSizeConstraints);
setter!(set_theme, Option<Theme>);

#[command(root = "crate")]
pub async fn set_icon<R: Runtime>(
Expand Down Expand Up @@ -287,6 +288,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
desktop_commands::set_icon,
desktop_commands::set_visible_on_all_workspaces,
desktop_commands::set_title_bar_style,
desktop_commands::set_theme,
desktop_commands::toggle_maximize,
desktop_commands::internal_toggle_maximize,
]);
Expand Down
2 changes: 2 additions & 0 deletions examples/api/src-tauri/capabilities/run-app.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"core:default",
"core:app:allow-app-hide",
"core:app:allow-app-show",
"core:app:allow-set-app-theme",
"core:window:allow-set-theme",
"core:window:allow-center",
"core:window:allow-request-user-attention",
"core:window:allow-set-resizable",
Expand Down
Loading
Loading