Skip to content

Commit

Permalink
feat: expose theme APIs, closes #3903 (#3937)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Apr 21, 2022
1 parent 0299e50 commit 4cebcf6
Show file tree
Hide file tree
Showing 22 changed files with 258 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .changes/api-theme-getter.md
@@ -0,0 +1,5 @@
---
"api": patch
---

Added `theme` getter to `WebviewWindow`.
5 changes: 5 additions & 0 deletions .changes/api-theme-window-option.md
@@ -0,0 +1,5 @@
---
"api": patch
---

Added `theme` field to `WindowOptions`.
5 changes: 5 additions & 0 deletions .changes/tauri-event-api.md
@@ -0,0 +1,5 @@
---
"api": patch
---

Added the `tauri://theme-changed` event.
7 changes: 7 additions & 0 deletions .changes/theme-event.md
@@ -0,0 +1,7 @@
---
"tauri": patch
"tauri-runtime": patch
"tauri-runtime-wry": patch
---

Added `WindowEvent::ThemeChanged(theme)`.
7 changes: 7 additions & 0 deletions .changes/theme-getter.md
@@ -0,0 +1,7 @@
---
"tauri": patch
"tauri-runtime": patch
"tauri-runtime-wry": patch
---

Added `theme` getter on `Window`.
7 changes: 7 additions & 0 deletions .changes/window-builder-theme.md
@@ -0,0 +1,7 @@
---
"tauri": patch
"tauri-runtime": patch
"tauri-runtime-wry": patch
---

Added `theme` setter to the WindowBuilder.
46 changes: 43 additions & 3 deletions core/tauri-runtime-wry/src/lib.rs
Expand Up @@ -40,7 +40,7 @@ use wry::application::platform::windows::{WindowBuilderExtWindows, WindowExtWind
#[cfg(feature = "system-tray")]
use wry::application::system_tray::{SystemTray as WrySystemTray, SystemTrayBuilder};

use tauri_utils::config::WindowConfig;
use tauri_utils::{config::WindowConfig, Theme};
use uuid::Uuid;
use wry::{
application::{
Expand All @@ -62,7 +62,10 @@ use wry::{
MenuType,
},
monitor::MonitorHandle,
window::{Fullscreen, Icon as WryWindowIcon, UserAttentionType as WryUserAttentionType},
window::{
Fullscreen, Icon as WryWindowIcon, Theme as WryTheme,
UserAttentionType as WryUserAttentionType,
},
},
http::{
Request as WryHttpRequest, RequestParts as WryRequestParts, Response as WryHttpResponse,
Expand Down Expand Up @@ -607,6 +610,14 @@ impl WindowEventWrapper {
}
}

fn map_theme(theme: &WryTheme) -> Theme {
match theme {
WryTheme::Light => Theme::Light,
WryTheme::Dark => Theme::Dark,
_ => Theme::Light,
}
}

impl<'a> From<&WryWindowEvent<'a>> for WindowEventWrapper {
fn from(event: &WryWindowEvent<'a>) -> Self {
let event = match event {
Expand All @@ -624,6 +635,7 @@ impl<'a> From<&WryWindowEvent<'a>> for WindowEventWrapper {
},
#[cfg(any(target_os = "linux", target_os = "macos"))]
WryWindowEvent::Focused(focused) => WindowEvent::Focused(*focused),
WryWindowEvent::ThemeChanged(theme) => WindowEvent::ThemeChanged(map_theme(theme)),
_ => return Self(None),
};
Self(Some(event))
Expand Down Expand Up @@ -776,7 +788,8 @@ impl WindowBuilder for WindowBuilderWrapper {
.decorations(config.decorations)
.maximized(config.maximized)
.always_on_top(config.always_on_top)
.skip_taskbar(config.skip_taskbar);
.skip_taskbar(config.skip_taskbar)
.theme(config.theme);

#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
{
Expand Down Expand Up @@ -936,6 +949,22 @@ impl WindowBuilder for WindowBuilderWrapper {
self
}

#[allow(unused_variables, unused_mut)]
fn theme(mut self, theme: Option<Theme>) -> Self {
#[cfg(windows)]
{
self.inner = self.inner.with_theme(if let Some(t) = theme {
match t {
Theme::Dark => Some(WryTheme::Dark),
_ => Some(WryTheme::Light),
}
} else {
None
});
}
self
}

fn has_icon(&self) -> bool {
self.inner.window.window_icon.is_some()
}
Expand Down Expand Up @@ -1025,6 +1054,7 @@ pub enum WindowMessage {
target_os = "openbsd"
))]
GtkWindow(Sender<GtkWindow>),
Theme(Sender<Theme>),
// Setters
Center(Sender<Result<()>>),
RequestUserAttention(Option<UserAttentionTypeWrapper>),
Expand Down Expand Up @@ -1275,6 +1305,10 @@ impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {
window_getter!(self, WindowMessage::Hwnd).map(|w| w.0)
}

fn theme(&self) -> Result<Theme> {
window_getter!(self, WindowMessage::Theme)
}

/// Returns the `ApplicatonWindow` from gtk crate that is used by this window.
#[cfg(any(
target_os = "linux",
Expand Down Expand Up @@ -2090,6 +2124,12 @@ fn handle_user_message<T: UserEvent>(
target_os = "openbsd"
))]
WindowMessage::GtkWindow(tx) => tx.send(GtkWindow(window.gtk_window().clone())).unwrap(),
WindowMessage::Theme(tx) => {
#[cfg(windows)]
tx.send(map_theme(&window.theme())).unwrap();
#[cfg(not(windows))]
tx.send(Theme::Light).unwrap();
}
// Setters
WindowMessage::Center(tx) => {
tx.send(center_window(window, window_handle.inner_size()))
Expand Down
4 changes: 4 additions & 0 deletions core/tauri-runtime/src/lib.rs
Expand Up @@ -8,6 +8,7 @@

use serde::Deserialize;
use std::{fmt::Debug, path::PathBuf, sync::mpsc::Sender};
use tauri_utils::Theme;
use uuid::Uuid;

#[cfg(windows)]
Expand Down Expand Up @@ -465,6 +466,9 @@ pub trait Dispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 'static
#[cfg(windows)]
fn hwnd(&self) -> Result<HWND>;

/// Returns the current window theme.
fn theme(&self) -> Result<Theme>;

/// Returns the native handle that is used by this window.
#[cfg(target_os = "macos")]
fn ns_window(&self) -> Result<*mut std::ffi::c_void>;
Expand Down
8 changes: 7 additions & 1 deletion core/tauri-runtime/src/webview.rs
Expand Up @@ -6,7 +6,10 @@

use crate::{menu::Menu, window::DetachedWindow, WindowIcon};

use tauri_utils::config::{WindowConfig, WindowUrl};
use tauri_utils::{
config::{WindowConfig, WindowUrl},
Theme,
};

#[cfg(windows)]
use windows::Win32::Foundation::HWND;
Expand Down Expand Up @@ -186,6 +189,9 @@ pub trait WindowBuilder: WindowBuilderBase {
#[must_use]
fn owner_window(self, owner: HWND) -> Self;

/// Forces a theme or uses the system settings if None was provided.
fn theme(self, theme: Option<Theme>) -> Self;

/// Whether the icon was set or not.
fn has_icon(&self) -> bool;

Expand Down
8 changes: 7 additions & 1 deletion core/tauri-runtime/src/window.rs
Expand Up @@ -11,7 +11,7 @@ use crate::{
Dispatch, Runtime, UserEvent, WindowBuilder,
};
use serde::Serialize;
use tauri_utils::config::WindowConfig;
use tauri_utils::{config::WindowConfig, Theme};

use std::{
collections::{HashMap, HashSet},
Expand Down Expand Up @@ -59,6 +59,12 @@ pub enum WindowEvent {
},
/// An event associated with the file drop action.
FileDrop(FileDropEvent),
/// The system window theme has changed.
///
/// Applications might wish to react to this to change the theme of the content of the window when the system changes the window theme.
///
/// At the moment this is only supported on Windows.
ThemeChanged(Theme),
}

/// The file drop event payload.
Expand Down
18 changes: 17 additions & 1 deletion core/tauri-utils/src/config.rs
Expand Up @@ -572,6 +572,8 @@ pub struct WindowConfig {
/// Whether or not the window icon should be added to the taskbar.
#[serde(default)]
pub skip_taskbar: bool,
/// The initial window theme. Defaults to the system theme. Only implemented on Windows.
pub theme: Option<crate::Theme>,
}

impl Default for WindowConfig {
Expand Down Expand Up @@ -599,6 +601,7 @@ impl Default for WindowConfig {
decorations: default_decorations(),
always_on_top: false,
skip_taskbar: false,
theme: None,
}
}
}
Expand Down Expand Up @@ -2302,6 +2305,17 @@ mod build {
}
}

impl ToTokens for crate::Theme {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::Theme };

tokens.append_all(match self {
Self::Light => quote! { #prefix::Light },
Self::Dark => quote! { #prefix::Dark },
})
}
}

impl ToTokens for WindowConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let label = str_lit(&self.label);
Expand All @@ -2326,6 +2340,7 @@ mod build {
let decorations = self.decorations;
let always_on_top = self.always_on_top;
let skip_taskbar = self.skip_taskbar;
let theme = opt_lit(self.theme.as_ref());

literal_struct!(
tokens,
Expand All @@ -2351,7 +2366,8 @@ mod build {
visible,
decorations,
always_on_top,
skip_taskbar
skip_taskbar,
theme
);
}
}
Expand Down
50 changes: 50 additions & 0 deletions core/tauri-utils/src/lib.rs
Expand Up @@ -5,6 +5,10 @@
//! Tauri utility helpers
#![warn(missing_docs, rust_2018_idioms)]

use std::fmt::Display;

use serde::{Deserialize, Deserializer, Serialize, Serializer};

pub mod assets;
pub mod config;
pub mod html;
Expand Down Expand Up @@ -43,6 +47,52 @@ impl PackageInfo {
}
}

/// System theme.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[non_exhaustive]
pub enum Theme {
/// Light theme.
Light,
/// Dark theme.
Dark,
}

impl Serialize for Theme {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}

impl<'de> Deserialize<'de> for Theme {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(match s.to_lowercase().as_str() {
"dark" => Self::Dark,
_ => Self::Light,
})
}
}

impl Display for Theme {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Light => "light",
Self::Dark => "dark",
}
)
}
}

/// Information about environment variables.
#[derive(Debug, Clone)]
#[non_exhaustive]
Expand Down
2 changes: 1 addition & 1 deletion core/tauri/scripts/bundle.js

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion core/tauri/src/app.rs
Expand Up @@ -24,7 +24,7 @@ use crate::{
utils::config::Config,
utils::{assets::Assets, Env},
Context, EventLoopMessage, Invoke, InvokeError, InvokeResponse, Manager, Runtime, Scopes,
StateManager, Window,
StateManager, Theme, Window,
};

#[cfg(shell_scope)]
Expand Down Expand Up @@ -118,6 +118,15 @@ pub enum WindowEvent {
},
/// An event associated with the file drop action.
FileDrop(FileDropEvent),
/// The system window theme has changed.
///
/// Applications might wish to react to this to change the theme of the content of the window when the system changes the window theme.
///
/// ## Platform-specific
///
/// - **macOS / Linux**: Not supported.
/// - **Windows**: Only delivered if the window [`theme`](`crate::window::WindowBuilder#method.theme`) is `None`.
ThemeChanged(Theme),
}

impl From<RuntimeWindowEvent> for WindowEvent {
Expand All @@ -138,6 +147,7 @@ impl From<RuntimeWindowEvent> for WindowEvent {
new_inner_size,
},
RuntimeWindowEvent::FileDrop(event) => Self::FileDrop(event),
RuntimeWindowEvent::ThemeChanged(theme) => Self::ThemeChanged(theme),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions core/tauri/src/endpoints/window.rs
Expand Up @@ -68,6 +68,7 @@ pub enum WindowManagerCmd {
CurrentMonitor,
PrimaryMonitor,
AvailableMonitors,
Theme,
// Setters
Center,
RequestUserAttention(Option<UserAttentionType>),
Expand Down Expand Up @@ -218,6 +219,7 @@ impl Cmd {
WindowManagerCmd::CurrentMonitor => return Ok(window.current_monitor()?.into()),
WindowManagerCmd::PrimaryMonitor => return Ok(window.primary_monitor()?.into()),
WindowManagerCmd::AvailableMonitors => return Ok(window.available_monitors()?.into()),
WindowManagerCmd::Theme => return Ok(window.theme()?.into()),
// Setters
#[cfg(window_center)]
WindowManagerCmd::Center => window.center()?,
Expand Down
2 changes: 1 addition & 1 deletion core/tauri/src/lib.rs
Expand Up @@ -222,7 +222,7 @@ pub use {
self::utils::{
assets::Assets,
config::{Config, WindowUrl},
Env, PackageInfo,
Env, PackageInfo, Theme,
},
self::window::{Monitor, Window, WindowBuilder},
scope::*,
Expand Down

0 comments on commit 4cebcf6

Please sign in to comment.