Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
use super::Dpi;
use super::WideString;
use windows_sys::Win32::{
Foundation::{ERROR_SUCCESS, S_OK},
Graphics::Gdi,
System::Registry,
UI::Controls,
};
/// The default font size to use.
///
/// `GetThemeSysFont` scales the font based on DPI and accessibility settings, however it only takes
/// those active at application startup into account (it won't scale correctly across monitors, nor
/// if the DPI of the current monitor changes). So we use a fixed size instead and scale that.
const DEFAULT_FONT_SIZE: i32 = -12;
/// The set of fonts to use.
pub struct Fonts {
pub normal: Font,
pub bold: Font,
}
/// The scale factor set by the windows 10 "make text bigger" accessibility setting.
#[derive(Clone, Copy, Debug)]
pub struct ScaleFactor(f32);
impl ScaleFactor {
/// Create a new scale factor.
///
/// The factor will be clamped to [1,2.25], to match the
/// [documentation](https://learn.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.uisettings.textscalefactor)
/// and avoid any surprises.
pub fn new(factor: f32) -> Self {
ScaleFactor(factor.clamp(1., 2.25))
}
/// Get the current scale factor setting from the registry.
pub fn from_registry() -> Self {
let key = WideString::new("SOFTWARE\\Microsoft\\Accessibility");
let value = WideString::new("TextScaleFactor");
let mut scale_factor: [u8; 4] = Default::default();
let mut size: u32 = std::mem::size_of_val(&scale_factor) as u32;
let mut reg_type: u32 = 0;
let result = unsafe {
Registry::RegGetValueW(
Registry::HKEY_CURRENT_USER,
key.pcwstr(),
value.pcwstr(),
Registry::RRF_RT_REG_DWORD,
&mut reg_type,
&mut scale_factor as *mut u8 as _,
&mut size,
)
};
let percent = if result == ERROR_SUCCESS {
if reg_type == Registry::REG_DWORD_BIG_ENDIAN {
u32::from_be_bytes(scale_factor)
} else {
u32::from_le_bytes(scale_factor)
}
} else {
100
};
Self::new(percent as f32 / 100.)
}
}
impl Fonts {
pub fn new(dpi: Dpi, scale_factor: ScaleFactor) -> Self {
let builder = FontBuilder { dpi, scale_factor };
Fonts {
normal: builder.caption(),
bold: builder.caption_bold().unwrap_or_else(|| builder.caption()),
}
}
}
pub struct FontBuilder {
dpi: Dpi,
scale_factor: ScaleFactor,
}
impl FontBuilder {
/// Get the system theme caption font.
///
/// Panics if the font cannot be retrieved.
pub fn caption(&self) -> Font {
unsafe {
let mut font = std::mem::zeroed::<Gdi::LOGFONTW>();
success!(hresult
Controls::GetThemeSysFont(0, Controls::TMT_CAPTIONFONT as i32, &mut font)
);
font.lfHeight = DEFAULT_FONT_SIZE;
self.scale_font_height(&mut font.lfHeight);
font.lfWidth = 0;
Font(success!(pointer Gdi::CreateFontIndirectW(&font)))
}
}
/// Get the system theme bold caption font.
///
/// Returns `None` if the font cannot be retrieved.
pub fn caption_bold(&self) -> Option<Font> {
unsafe {
let mut font = std::mem::zeroed::<Gdi::LOGFONTW>();
if Controls::GetThemeSysFont(0, Controls::TMT_CAPTIONFONT as i32, &mut font) != S_OK {
return None;
}
font.lfHeight = DEFAULT_FONT_SIZE;
self.scale_font_height(&mut font.lfHeight);
font.lfWidth = 0;
font.lfWeight = Gdi::FW_BOLD as i32;
let ptr = Gdi::CreateFontIndirectW(&font);
if ptr == 0 {
return None;
}
Some(Font(ptr))
}
}
fn scale_font_height(&self, height: &mut i32) {
*height = (self.dpi.scale(height.abs() as u32) as f32 * self.scale_factor.0) as i32
* if height.is_negative() { -1 } else { 1 };
}
}
/// Windows font handle (`HFONT`).
pub struct Font(Gdi::HFONT);
impl std::ops::Deref for Font {
type Target = Gdi::HFONT;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Drop for Font {
fn drop(&mut self) {
unsafe { Gdi::DeleteObject(self.0 as _) };
}
}