Skip to content

Commit

Permalink
On Windows, expose DWM attributes (#3409)
Browse files Browse the repository at this point in the history
  • Loading branch information
sidit77 authored Jan 19, 2024
1 parent d7c7ba1 commit b0c59c8
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Unreleased` header.
- Add the `OwnedDisplayHandle` type for allowing safe display handle usage outside of trivial cases.
- **Breaking:** Rename `TouchpadMagnify` to `PinchGesture`, `SmartMagnify` to `DoubleTapGesture` and `TouchpadRotate` to `RotationGesture` to represent the action rather than the intent.
- on iOS, add detection support for `PinchGesture`, `DoubleTapGesture` and `RotationGesture`.
- on Windows: add `with_border_color`, `with_title_background_color`, `with_title_text_color` and `with_corner_preference`

# 0.29.10

Expand Down
4 changes: 4 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ If your PR makes notable changes to Winit's features, please update this section
* Setting a menu bar
* `WS_EX_NOREDIRECTIONBITMAP` support
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme
* Setting the window border color
* Setting the title bar background color
* Setting the title color
* Setting the corner rounding preference

### macOS
* Window activation policy
Expand Down
141 changes: 141 additions & 0 deletions src/platform/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,60 @@ pub type HMENU = isize;
/// Monitor Handle type used by Win32 API
pub type HMONITOR = isize;

/// Describes a color used by Windows
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Color(u32);

impl Color {
/// Use the system's default color
pub const SYSTEM_DEFAULT: Color = Color(0xFFFFFFFF);

//Special constant only valid for the window border and therefore modeled using Option<Color> for user facing code
const NONE: Color = Color(0xFFFFFFFE);

/// Create a new color from the given RGB values
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
Self((r as u32) | ((g as u32) << 8) | ((b as u32) << 16))
}
}

impl Default for Color {
fn default() -> Self {
Self::SYSTEM_DEFAULT
}
}

/// Describes how the corners of a window should look like.
///
/// For a detailed explanation, see [`DWM_WINDOW_CORNER_PREFERENCE docs`].
///
/// [`DWM_WINDOW_CORNER_PREFERENCE docs`]: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference
#[repr(i32)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CornerPreference {
/// Corresponds to `DWMWCP_DEFAULT`.
///
/// Let the system decide when to round window corners.
#[default]
Default = 0,

/// Corresponds to `DWMWCP_DONOTROUND`.
///
/// Never round window corners.
DoNotRound = 1,

/// Corresponds to `DWMWCP_ROUND`.
///
/// Round the corners, if appropriate.
Round = 2,

/// Corresponds to `DWMWCP_ROUNDSMALL`.
///
/// Round the corners if appropriate, with a small radius.
RoundSmall = 3,
}

/// Additional methods on `EventLoop` that are specific to Windows.
pub trait EventLoopBuilderExtWindows {
/// Whether to allow the event loop to be created off of the main thread.
Expand Down Expand Up @@ -133,6 +187,26 @@ pub trait WindowExtWindows {
///
/// Enabling the shadow causes a thin 1px line to appear on the top of the window.
fn set_undecorated_shadow(&self, shadow: bool);

/// Sets the color of the window border.
///
/// Supported starting with Windows 11 Build 22000.
fn set_border_color(&self, color: Option<Color>);

/// Sets the background color of the title bar.
///
/// Supported starting with Windows 11 Build 22000.
fn set_title_background_color(&self, color: Option<Color>);

/// Sets the color of the window title.
///
/// Supported starting with Windows 11 Build 22000.
fn set_title_text_color(&self, color: Color);

/// Sets the preferred style of the window corners.
///
/// Supported starting with Windows 11 Build 22000.
fn set_corner_preference(&self, preference: CornerPreference);
}

impl WindowExtWindows for Window {
Expand All @@ -155,6 +229,29 @@ impl WindowExtWindows for Window {
fn set_undecorated_shadow(&self, shadow: bool) {
self.window.set_undecorated_shadow(shadow)
}

#[inline]
fn set_border_color(&self, color: Option<Color>) {
self.window.set_border_color(color.unwrap_or(Color::NONE))
}

#[inline]
fn set_title_background_color(&self, color: Option<Color>) {
// The windows docs don't mention NONE as a valid options but it works in practice and is useful
// to circumvent the Windows option "Show accent color on title bars and window borders"
self.window
.set_title_background_color(color.unwrap_or(Color::NONE))
}

#[inline]
fn set_title_text_color(&self, color: Color) {
self.window.set_title_text_color(color)
}

#[inline]
fn set_corner_preference(&self, preference: CornerPreference) {
self.window.set_corner_preference(preference)
}
}

/// Additional methods on `WindowBuilder` that are specific to Windows.
Expand Down Expand Up @@ -217,6 +314,26 @@ pub trait WindowBuilderExtWindows {
/// The shadow is hidden by default.
/// Enabling the shadow causes a thin 1px line to appear on the top of the window.
fn with_undecorated_shadow(self, shadow: bool) -> Self;

/// Sets the color of the window border.
///
/// Supported starting with Windows 11 Build 22000.
fn with_border_color(self, color: Option<Color>) -> Self;

/// Sets the background color of the title bar.
///
/// Supported starting with Windows 11 Build 22000.
fn with_title_background_color(self, color: Option<Color>) -> Self;

/// Sets the color of the window title.
///
/// Supported starting with Windows 11 Build 22000.
fn with_title_text_color(self, color: Color) -> Self;

/// Sets the preferred style of the window corners.
///
/// Supported starting with Windows 11 Build 22000.
fn with_corner_preference(self, corners: CornerPreference) -> Self;
}

impl WindowBuilderExtWindows for WindowBuilder {
Expand Down Expand Up @@ -267,6 +384,30 @@ impl WindowBuilderExtWindows for WindowBuilder {
self.window.platform_specific.decoration_shadow = shadow;
self
}

#[inline]
fn with_border_color(mut self, color: Option<Color>) -> Self {
self.window.platform_specific.border_color = Some(color.unwrap_or(Color::NONE));
self
}

#[inline]
fn with_title_background_color(mut self, color: Option<Color>) -> Self {
self.window.platform_specific.title_background_color = Some(color.unwrap_or(Color::NONE));
self
}

#[inline]
fn with_title_text_color(mut self, color: Color) -> Self {
self.window.platform_specific.title_text_color = Some(color);
self
}

#[inline]
fn with_corner_preference(mut self, corners: CornerPreference) -> Self {
self.window.platform_specific.corner_preference = Some(corners);
self
}
}

/// Additional methods on `MonitorHandle` that are specific to Windows.
Expand Down
9 changes: 9 additions & 0 deletions src/platform_impl/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use crate::platform_impl::Fullscreen;
use crate::event::DeviceId as RootDeviceId;
use crate::icon::Icon;
use crate::keyboard::Key;
use crate::platform::windows::{Color, CornerPreference};

#[derive(Clone, Debug)]
pub struct PlatformSpecificWindowBuilderAttributes {
Expand All @@ -36,6 +37,10 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub skip_taskbar: bool,
pub class_name: String,
pub decoration_shadow: bool,
pub border_color: Option<Color>,
pub title_background_color: Option<Color>,
pub title_text_color: Option<Color>,
pub corner_preference: Option<CornerPreference>,
}

impl Default for PlatformSpecificWindowBuilderAttributes {
Expand All @@ -49,6 +54,10 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
skip_taskbar: false,
class_name: "Window Class".to_string(),
decoration_shadow: false,
border_color: None,
title_background_color: None,
title_text_color: None,
corner_preference: None,
}
}
}
Expand Down
68 changes: 67 additions & 1 deletion src/platform_impl/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ use windows_sys::Win32::{
HWND, LPARAM, OLE_E_WRONGCOMPOBJ, POINT, POINTS, RECT, RPC_E_CHANGED_MODE, S_OK, WPARAM,
},
Graphics::{
Dwm::{DwmEnableBlurBehindWindow, DWM_BB_BLURREGION, DWM_BB_ENABLE, DWM_BLURBEHIND},
Dwm::{
DwmEnableBlurBehindWindow, DwmSetWindowAttribute, DWMWA_BORDER_COLOR,
DWMWA_CAPTION_COLOR, DWMWA_TEXT_COLOR, DWMWA_WINDOW_CORNER_PREFERENCE,
DWM_BB_BLURREGION, DWM_BB_ENABLE, DWM_BLURBEHIND, DWM_WINDOW_CORNER_PREFERENCE,
},
Gdi::{
ChangeDisplaySettingsExW, ClientToScreen, CreateRectRgn, DeleteObject, InvalidateRgn,
RedrawWindow, CDS_FULLSCREEN, DISP_CHANGE_BADFLAGS, DISP_CHANGE_BADMODE,
Expand Down Expand Up @@ -56,6 +60,7 @@ use windows_sys::Win32::{

use log::warn;

use crate::platform::windows::{Color, CornerPreference};
use crate::{
cursor::Cursor,
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
Expand Down Expand Up @@ -1060,6 +1065,54 @@ impl Window {
);
}
}

#[inline]
pub fn set_border_color(&self, color: Color) {
unsafe {
DwmSetWindowAttribute(
self.hwnd(),
DWMWA_BORDER_COLOR,
&color as *const _ as _,
mem::size_of::<Color>() as _,
);
}
}

#[inline]
pub fn set_title_background_color(&self, color: Color) {
unsafe {
DwmSetWindowAttribute(
self.hwnd(),
DWMWA_CAPTION_COLOR,
&color as *const _ as _,
mem::size_of::<Color>() as _,
);
}
}

#[inline]
pub fn set_title_text_color(&self, color: Color) {
unsafe {
DwmSetWindowAttribute(
self.hwnd(),
DWMWA_TEXT_COLOR,
&color as *const _ as _,
mem::size_of::<Color>() as _,
);
}
}

#[inline]
pub fn set_corner_preference(&self, preference: CornerPreference) {
unsafe {
DwmSetWindowAttribute(
self.hwnd(),
DWMWA_WINDOW_CORNER_PREFERENCE,
&(preference as DWM_WINDOW_CORNER_PREFERENCE) as *const _ as _,
mem::size_of::<DWM_WINDOW_CORNER_PREFERENCE>() as _,
);
}
}
}

impl Drop for Window {
Expand Down Expand Up @@ -1265,6 +1318,19 @@ impl<'a> InitData<'a> {
if let Some(position) = attributes.position {
win.set_outer_position(position);
}

if let Some(color) = self.attributes.platform_specific.border_color {
win.set_border_color(color);
}
if let Some(color) = self.attributes.platform_specific.title_background_color {
win.set_title_background_color(color);
}
if let Some(color) = self.attributes.platform_specific.title_text_color {
win.set_title_text_color(color);
}
if let Some(corner) = self.attributes.platform_specific.corner_preference {
win.set_corner_preference(corner);
}
}
}
unsafe fn init(
Expand Down

0 comments on commit b0c59c8

Please sign in to comment.