Skip to content

Commit

Permalink
feat: support light/dark theme and adherence to system theme (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
sigoden authored Oct 21, 2024
1 parent 47543c4 commit 6d6bcff
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 8 deletions.
46 changes: 39 additions & 7 deletions src/painter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::app::SwitchAppsState;
use crate::utils::RegKey;
use anyhow::Result;
use windows::core::w;
use windows::Win32::Foundation::{COLORREF, RECT};
use windows::Win32::Graphics::Gdi::{
BeginPaint, BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, CreateSolidBrush, DeleteDC,
Expand All @@ -8,10 +11,14 @@ use windows::Win32::Graphics::Gdi::{
use windows::Win32::UI::WindowsAndMessaging::{DrawIconEx, DI_NORMAL};
use windows::Win32::{Foundation::HWND, Graphics::Gdi::GetDC};

// window background color
pub const BG_COLOR: COLORREF = COLORREF(0x3b3b3b);
// selected icon box color
pub const FG_COLOR: COLORREF = COLORREF(0x4c4c4c);
// window background color in dark theme
pub const BG_DARK_COLOR: COLORREF = COLORREF(0x3b3b3b);
// selected icon box color in dark theme
pub const FG_DARK_COLOR: COLORREF = COLORREF(0x4c4c4c);
// window background color in light theme
pub const BG_LIGHT_COLOR: COLORREF = COLORREF(0xf2f2f2);
// selected icon box color in light theme
pub const FG_LIGHT_COLOR: COLORREF = COLORREF(0xe0e0e0);
// minimum icon size
pub const ICON_SIZE: i32 = 64;
// window padding
Expand All @@ -35,13 +42,27 @@ pub struct GdiAAPainter {
size: i32,
// scale
scale: i32,
// color
fg_color: COLORREF,
bg_color: COLORREF,
}

impl GdiAAPainter {
/// Creates a new [GdiAAPainter] instance.
///
/// The `scale` must be a multiple of 2, for example 2, 4, 6, 8, 12 ...
pub fn new(hwnd: HWND, scale: i32) -> Self {
let light_theme = match is_light_theme() {
Ok(v) => v,
Err(_) => {
warn!("Fail to get system theme");
false
}
};
let (fg_color, bg_color) = match light_theme {
true => (FG_LIGHT_COLOR, BG_LIGHT_COLOR),
false => (FG_DARK_COLOR, BG_DARK_COLOR),
};
GdiAAPainter {
mem_hdc: Default::default(),
mem_map: Default::default(),
Expand All @@ -52,6 +73,8 @@ impl GdiAAPainter {
height: 0,
size: 0,
scale,
fg_color,
bg_color,
}
}

Expand Down Expand Up @@ -86,7 +109,7 @@ impl GdiAAPainter {
let mem_map = CreateCompatibleBitmap(hdc, width, height);
SelectObject(mem_dc, mem_map);

let brush = CreateSolidBrush(BG_COLOR);
let brush = CreateSolidBrush(self.fg_color);
let rect = RECT {
left: 0,
top: 0,
Expand Down Expand Up @@ -164,7 +187,7 @@ impl GdiAAPainter {
right: self.width * self.scale,
bottom: self.width * self.scale,
};
FillRect(self.scaled_hdc, &rect as _, CreateSolidBrush(FG_COLOR));
FillRect(self.scaled_hdc, &rect as _, CreateSolidBrush(self.fg_color));

let cy = (WINDOW_BORDER_SIZE + ICON_BORDER_SIZE) * self.scale;
let brush_icon = HBRUSH::default();
Expand All @@ -183,7 +206,7 @@ impl GdiAAPainter {
right,
bottom,
};
FillRect(self.scaled_hdc, &rect as _, CreateSolidBrush(BG_COLOR));
FillRect(self.scaled_hdc, &rect as _, CreateSolidBrush(self.bg_color));
}

let cx = cy + item_size * (i as i32);
Expand All @@ -202,3 +225,12 @@ impl GdiAAPainter {
}
}
}

fn is_light_theme() -> Result<bool> {
let reg_key = RegKey::new_hkcu(
w!("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"),
w!("SystemUsesLightTheme"),
)?;
let value = reg_key.get_int()?;
Ok(value == 1)
}
32 changes: 31 additions & 1 deletion src/utils/regedit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use windows::core::PCWSTR;
use windows::Win32::Foundation::ERROR_FILE_NOT_FOUND;
use windows::Win32::System::Registry::{
RegCloseKey, RegDeleteValueW, RegGetValueW, RegOpenKeyExW, RegSetValueExW, HKEY,
HKEY_CURRENT_USER, KEY_ALL_ACCESS, REG_SZ, REG_VALUE_TYPE, RRF_RT_REG_SZ,
HKEY_CURRENT_USER, KEY_ALL_ACCESS, REG_DWORD_BIG_ENDIAN, REG_SZ, REG_VALUE_TYPE,
RRF_RT_REG_DWORD, RRF_RT_REG_SZ,
};

#[derive(Debug)]
Expand Down Expand Up @@ -57,6 +58,35 @@ impl RegKey {
Ok(Some(buffer[..len].to_vec()))
}

pub fn get_int(&self) -> Result<u32> {
let mut value: [u8; 4] = Default::default();
let mut size: u32 = std::mem::size_of_val(&value) as u32;
let mut kind: REG_VALUE_TYPE = Default::default();
let ret = unsafe {
RegGetValueW(
self.hkey,
None,
self.name,
RRF_RT_REG_DWORD,
Some(&mut kind),
Some(value.as_mut_ptr() as *mut _),
Some(&mut size),
)
};
if ret.is_err() {
bail!(
"Fail to get reg value, {:?}",
windows::core::Error::from(ret)
);
}
let value = if kind == REG_DWORD_BIG_ENDIAN {
u32::from_be_bytes(value)
} else {
u32::from_le_bytes(value)
};
Ok(value)
}

pub fn set_value(&self, value: &[u8]) -> Result<()> {
unsafe { RegSetValueExW(self.hkey, self.name, 0, REG_SZ, Some(value)) }
.ok()
Expand Down

0 comments on commit 6d6bcff

Please sign in to comment.