/* 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 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 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::(); 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 { unsafe { let mut font = std::mem::zeroed::(); 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 _) }; } }