Skip to content

Commit

Permalink
feat: add background_color APIs (#995)
Browse files Browse the repository at this point in the history
* feat: add background_color APIs

ref: tauri-apps/tauri#10519

* fix macOS

* fix macos

* unsafe

* Update .changes/background_color.md

Co-authored-by: Lucas Nogueira <[email protected]>

* actually use color

---------

Co-authored-by: Lucas Fernandes Nogueira <[email protected]>
Co-authored-by: Lucas Nogueira <[email protected]>
Co-authored-by: Lucas Nogueira <[email protected]>
  • Loading branch information
4 people authored Oct 28, 2024
1 parent c49b83a commit b404cde
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 10 deletions.
6 changes: 6 additions & 0 deletions .changes/background_color.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tao": "patch"
---

Add `WindowAttributes::background_color`, `WindowBuilder::with_background_color`, and `Window::set_background_color` APIs to set and change window background color.

2 changes: 2 additions & 0 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,8 @@ impl Window {
))
}

pub fn set_background_color(&self, _color: Option<crate::window::RGBA>) {}

pub fn set_ignore_cursor_events(&self, _ignore: bool) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
Expand Down
2 changes: 2 additions & 0 deletions src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ impl Inner {
warn!("`Window::request_user_attention` is ignored on iOS")
}

pub fn set_background_color(&self, _color: Option<crate::window::RGBA>) {}

// Allow directly accessing the current monitor internally without unwrapping.
fn current_monitor_inner(&self) -> RootMonitorHandle {
unsafe {
Expand Down
49 changes: 46 additions & 3 deletions src/platform_impl/linux/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,28 @@ impl<T: 'static> EventLoop<T> {
window.set_skip_taskbar_hint(skip);
window.set_skip_pager_hint(skip)
}
WindowRequest::BackgroundColor(css_provider, color) => {
unsafe { window.set_data("background_color", color) };

let style_context = window.style_context();
style_context.remove_provider(&css_provider);

if let Some(color) = color {
let theme = format!(
r#"
window {{
background-color: rgba({},{},{},{});
}}
"#,
color.0,
color.1,
color.2,
color.3 as f64 / 255.0
);
let _ = css_provider.load_from_data(theme.as_bytes());
style_context.add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
};
}
WindowRequest::SetVisibleOnAllWorkspaces(visible) => {
if visible {
window.stick();
Expand Down Expand Up @@ -854,15 +876,36 @@ impl<T: 'static> EventLoop<T> {

// Receive draw events of the window.
let draw_clone = draw_tx.clone();
window.connect_draw(move |_, cr| {
window.connect_draw(move |window, cr| {
if let Err(e) = draw_clone.send(id) {
log::warn!("Failed to send redraw event to event channel: {}", e);
}

if transparent {
cr.set_source_rgba(0., 0., 0., 0.);
let background_color = unsafe {
window
.data::<Option<crate::window::RGBA>>("background_color")
.and_then(|c| c.as_ref().clone())
};

let rgba = background_color
.map(|(r, g, b, a)| (r as f64, g as f64, b as f64, a as f64 / 255.0))
.unwrap_or((0., 0., 0., 0.));

let rect = window
.child()
.map(|c| c.allocation())
.unwrap_or_else(|| window.allocation());

cr.rectangle(
rect.x() as _,
rect.y() as _,
rect.width() as _,
rect.height() as _,
);
cr.set_source_rgba(rgba.0, rgba.1, rgba.2, rgba.3);
cr.set_operator(cairo::Operator::Source);
let _ = cr.paint();
let _ = cr.fill();
cr.set_operator(cairo::Operator::Over);
}

Expand Down
20 changes: 17 additions & 3 deletions src/platform_impl/linux/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use gtk::{
gdk::WindowState,
glib::{self, translate::ToGlibPtr},
prelude::*,
Settings,
CssProvider, Settings,
};

use crate::{
Expand All @@ -27,7 +27,7 @@ use crate::{
platform_impl::wayland::header::WlHeader,
window::{
CursorIcon, Fullscreen, ProgressBarState, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowSizeConstraints,
WindowAttributes, WindowSizeConstraints, RGBA,
},
};

Expand Down Expand Up @@ -69,6 +69,7 @@ pub struct Window {
/// Draw event Sender
draw_tx: crossbeam_channel::Sender<WindowId>,
preferred_theme: RefCell<Option<Theme>>,
css_provider: CssProvider,
}

impl Window {
Expand Down Expand Up @@ -324,9 +325,11 @@ impl Window {
fullscreen: RefCell::new(attributes.fullscreen),
inner_size_constraints: RefCell::new(attributes.inner_size_constraints),
preferred_theme: RefCell::new(preferred_theme),
css_provider: CssProvider::new(),
};

win.set_skip_taskbar(pl_attribs.skip_taskbar);
let _ = win.set_skip_taskbar(pl_attribs.skip_taskbar);
win.set_background_color(attributes.background_color);

Ok(win)
}
Expand Down Expand Up @@ -407,6 +410,7 @@ impl Window {
fullscreen: RefCell::new(None),
inner_size_constraints: RefCell::new(WindowSizeConstraints::default()),
preferred_theme: RefCell::new(None),
css_provider: CssProvider::new(),
};

Ok(win)
Expand Down Expand Up @@ -456,6 +460,15 @@ impl Window {
}
}

pub fn set_background_color(&self, color: Option<RGBA>) {
if let Err(e) = self.window_requests_tx.send((
self.window_id,
WindowRequest::BackgroundColor(self.css_provider.clone(), color),
)) {
log::warn!("Fail to send size request: {}", e);
}
}

pub fn inner_size(&self) -> PhysicalSize<u32> {
let (width, height) = &*self.size;

Expand Down Expand Up @@ -1029,6 +1042,7 @@ pub enum WindowRequest {
SetVisibleOnAllWorkspaces(bool),
ProgressBarState(ProgressBarState),
SetTheme(Option<Theme>),
BackgroundColor(CssProvider, Option<RGBA>),
}

impl Drop for Window {
Expand Down
44 changes: 43 additions & 1 deletion src/platform_impl/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ pub struct UnownedWindow {
pub shared_state: Arc<Mutex<SharedState>>,
decorations: AtomicBool,
cursor_state: Weak<Mutex<CursorState>>,
transparent: bool,
pub inner_rect: Option<PhysicalSize<u32>>,
}

Expand Down Expand Up @@ -500,7 +501,22 @@ impl UnownedWindow {
unsafe {
if win_attribs.transparent {
ns_window.setOpaque_(NO);
ns_window.setBackgroundColor_(NSColor::clearColor(nil));
}

if win_attribs.transparent || win_attribs.background_color.is_some() {
let color = win_attribs
.background_color
.map(|(r, g, b, a)| {
NSColor::colorWithRed_green_blue_alpha_(
nil,
r as f64,
g as f64,
b as f64,
a as f64 / 255.0,
)
})
.unwrap_or_else(|| NSColor::clearColor(nil));
ns_window.setBackgroundColor_(color);
}

if win_attribs.inner_size_constraints.has_min() {
Expand Down Expand Up @@ -530,6 +546,7 @@ impl UnownedWindow {
// `WindowDelegate` to update the state.
let fullscreen = win_attribs.fullscreen.take();
let maximized = win_attribs.maximized;
let transparent = win_attribs.transparent;
let visible = win_attribs.visible;
let focused = win_attribs.focused;
let decorations = win_attribs.decorations;
Expand All @@ -548,6 +565,7 @@ impl UnownedWindow {
decorations: AtomicBool::new(decorations),
cursor_state,
inner_rect,
transparent,
});

match cloned_preferred_theme {
Expand Down Expand Up @@ -848,6 +866,30 @@ impl UnownedWindow {
Ok(())
}

#[inline]
pub fn set_background_color(&self, color: Option<crate::window::RGBA>) {
unsafe {
let color = color
.map(|(r, g, b, a)| {
NSColor::colorWithRed_green_blue_alpha_(
nil,
r as f64,
g as f64,
b as f64,
a as f64 / 255.0,
)
})
.unwrap_or_else(|| {
if self.transparent {
NSColor::clearColor(nil)
} else {
nil
}
});
self.ns_window.setBackgroundColor_(color);
}
}

#[inline]
pub fn drag_window(&self) -> Result<(), ExternalError> {
unsafe {
Expand Down
19 changes: 19 additions & 0 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,25 @@ unsafe fn public_window_callback_inner<T: 'static>(
}
}

win32wm::WM_ERASEBKGND => {
let w = subclass_input.window_state.lock();
if let Some(color) = w.background_color {
let hdc = HDC(wparam.0 as *mut _);
let mut rc = RECT::default();
if GetClientRect(window, &mut rc).is_ok() {
let brush = CreateSolidBrush(util::RGB(color.0, color.1, color.2));
FillRect(hdc, &rc, brush);
let _ = DeleteObject(brush);

result = ProcResult::Value(LRESULT(1));
} else {
result = ProcResult::DefSubclassProc;
}
} else {
result = ProcResult::DefSubclassProc;
}
}

win32wm::WM_WINDOWPOSCHANGING => {
let mut window_state = subclass_input.window_state.lock();

Expand Down
9 changes: 8 additions & 1 deletion src/platform_impl/windows/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
use windows::{
core::{HRESULT, PCSTR, PCWSTR},
Win32::{
Foundation::{BOOL, FARPROC, HWND, LPARAM, LRESULT, POINT, RECT, WPARAM},
Foundation::{BOOL, COLORREF, FARPROC, HWND, LPARAM, LRESULT, POINT, RECT, WPARAM},
Globalization::lstrlenW,
Graphics::Gdi::{ClientToScreen, InvalidateRgn, HMONITOR, HRGN},
System::LibraryLoader::*,
Expand Down Expand Up @@ -393,6 +393,13 @@ pub fn PRIMARYLANGID(hkl: HKL) -> u32 {
((hkl.0 as usize) & 0x3FF) as u32
}

/// Implementation of the `RGB` macro.
#[allow(non_snake_case)]
#[inline]
pub fn RGB<T: Into<u32>>(r: T, g: T, b: T) -> COLORREF {
COLORREF(r.into() | g.into() << 8 | b.into() << 16)
}

pub unsafe extern "system" fn call_default_window_proc(
hwnd: HWND,
msg: u32,
Expand Down
13 changes: 12 additions & 1 deletion src/platform_impl/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ use crate::{
},
window::{
CursorIcon, Fullscreen, ProgressBarState, ProgressState, ResizeDirection, Theme,
UserAttentionType, WindowAttributes, WindowSizeConstraints,
UserAttentionType, WindowAttributes, WindowSizeConstraints, RGBA,
},
};

Expand Down Expand Up @@ -982,6 +982,16 @@ impl Window {
unsafe { set_skip_taskbar(self.hwnd(), skip) }
}

#[inline]
pub fn set_background_color(&self, color: Option<RGBA>) {
self.window_state.lock().background_color = color;

unsafe {
let _ = InvalidateRect(self.hwnd(), None, true);
let _ = UpdateWindow(self.hwnd());
}
}

#[inline]
pub fn set_progress_bar(&self, progress: ProgressBarState) {
unsafe {
Expand Down Expand Up @@ -1176,6 +1186,7 @@ unsafe fn init<T: 'static>(
scale_factor,
current_theme,
attributes.preferred_theme,
attributes.background_color,
);
let window_state = Arc::new(Mutex::new(window_state));
WindowState::set_window_flags(window_state.lock(), real_window.0, |f| *f = window_flags);
Expand Down
7 changes: 6 additions & 1 deletion src/platform_impl/windows/window_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
icon::Icon,
keyboard::ModifiersState,
platform_impl::platform::{event_loop, minimal_ime::MinimalIme, util},
window::{CursorIcon, Fullscreen, Theme, WindowAttributes, WindowSizeConstraints},
window::{CursorIcon, Fullscreen, Theme, WindowAttributes, WindowSizeConstraints, RGBA},
};
use parking_lot::MutexGuard;
use std::io;
Expand Down Expand Up @@ -46,6 +46,8 @@ pub struct WindowState {
// Used by WM_NCACTIVATE, WM_SETFOCUS and WM_KILLFOCUS
pub is_active: bool,
pub is_focused: bool,

pub background_color: Option<RGBA>,
}

unsafe impl Send for WindowState {}
Expand Down Expand Up @@ -126,6 +128,7 @@ impl WindowState {
scale_factor: f64,
current_theme: Theme,
preferred_theme: Option<Theme>,
background_color: Option<RGBA>,
) -> WindowState {
WindowState {
mouse: MouseProperties {
Expand Down Expand Up @@ -155,6 +158,8 @@ impl WindowState {
window_flags: WindowFlags::empty(),
is_active: false,
is_focused: false,

background_color,
}
}

Expand Down
Loading

0 comments on commit b404cde

Please sign in to comment.