summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/client/app/src/ui/windows/window.rs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/client/app/src/ui/windows/window.rs')
-rw-r--r--toolkit/crashreporter/client/app/src/ui/windows/window.rs302
1 files changed, 302 insertions, 0 deletions
diff --git a/toolkit/crashreporter/client/app/src/ui/windows/window.rs b/toolkit/crashreporter/client/app/src/ui/windows/window.rs
new file mode 100644
index 0000000000..7e5e8f3f2a
--- /dev/null
+++ b/toolkit/crashreporter/client/app/src/ui/windows/window.rs
@@ -0,0 +1,302 @@
+/* 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/. */
+
+//! Types and helpers relating to windows and window classes.
+
+use super::Font;
+use super::WideString;
+use std::cell::RefCell;
+use windows_sys::Win32::{
+ Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, WPARAM},
+ Graphics::Gdi::{self, HBRUSH},
+ UI::WindowsAndMessaging::{self as win, HCURSOR, HICON},
+};
+
+/// Types representing a window class.
+pub trait WindowClass: Sized + 'static {
+ fn class_name() -> WideString;
+
+ fn builder(self, module: HINSTANCE) -> WindowBuilder<'static, Self> {
+ WindowBuilder {
+ name: None,
+ style: None,
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0,
+ parent: None,
+ child_id: 0,
+ module,
+ data: self,
+ }
+ }
+}
+
+/// Window classes which have their own message handler.
+///
+/// A type implementing this trait provides its data to a single window.
+///
+/// `register` must be called before use.
+pub trait CustomWindowClass: WindowClass {
+ /// Handle a message.
+ fn message(
+ data: &RefCell<Self>,
+ hwnd: HWND,
+ umsg: u32,
+ wparam: WPARAM,
+ lparam: LPARAM,
+ ) -> Option<LRESULT>;
+
+ /// The class's default background brush.
+ fn background() -> HBRUSH {
+ (Gdi::COLOR_3DFACE + 1) as HBRUSH
+ }
+
+ /// The class's default cursor.
+ fn cursor() -> HCURSOR {
+ unsafe { win::LoadCursorW(0, win::IDC_ARROW) }
+ }
+
+ /// The class's default icon.
+ fn icon() -> HICON {
+ 0
+ }
+
+ /// Register the class.
+ fn register(module: HINSTANCE) -> anyhow::Result<()> {
+ unsafe extern "system" fn wnd_proc<W: CustomWindowClass>(
+ hwnd: HWND,
+ umsg: u32,
+ wparam: WPARAM,
+ lparam: LPARAM,
+ ) -> LRESULT {
+ if umsg == win::WM_CREATE {
+ let create_struct = &*(lparam as *const win::CREATESTRUCTW);
+ success!(lasterror win::SetWindowLongPtrW(hwnd, 0, create_struct.lpCreateParams as _));
+ // Changes made with SetWindowLongPtr don't take effect until SetWindowPos is called...
+ success!(nonzero win::SetWindowPos(
+ hwnd,
+ win::HWND_TOP,
+ 0,
+ 0,
+ 0,
+ 0,
+ win::SWP_NOMOVE | win::SWP_NOSIZE | win::SWP_NOZORDER | win::SWP_FRAMECHANGED,
+ ));
+ }
+
+ let result = unsafe { W::get(hwnd).as_ref() }
+ .and_then(|data| W::message(data, hwnd, umsg, wparam, lparam));
+ if umsg == win::WM_DESTROY {
+ drop(Box::from_raw(
+ win::GetWindowLongPtrW(hwnd, 0) as *mut RefCell<W>
+ ));
+ }
+ result.unwrap_or_else(|| win::DefWindowProcW(hwnd, umsg, wparam, lparam))
+ }
+
+ let class_name = Self::class_name();
+ let window_class = win::WNDCLASSW {
+ lpfnWndProc: Some(wnd_proc::<Self>),
+ hInstance: module,
+ lpszClassName: class_name.pcwstr(),
+ hbrBackground: Self::background(),
+ hIcon: Self::icon(),
+ hCursor: Self::cursor(),
+ cbWndExtra: std::mem::size_of::<isize>() as i32,
+ ..unsafe { std::mem::zeroed() }
+ };
+
+ if unsafe { win::RegisterClassW(&window_class) } == 0 {
+ anyhow::bail!("RegisterClassW failed")
+ }
+ Ok(())
+ }
+
+ /// Get the window data from a window created with this class.
+ ///
+ /// # Safety
+ /// This must only be called on window handles which were created with this class.
+ unsafe fn get(hwnd: HWND) -> *const RefCell<Self> {
+ win::GetWindowLongPtrW(hwnd, 0) as *const RefCell<Self>
+ }
+}
+
+/// Types that can be stored as associated window data.
+pub trait WindowData: Sized {
+ fn to_ptr(self) -> *mut RefCell<Self> {
+ std::ptr::null_mut()
+ }
+}
+
+impl<T: CustomWindowClass> WindowData for T {
+ fn to_ptr(self) -> *mut RefCell<Self> {
+ Box::into_raw(Box::new(RefCell::new(self)))
+ }
+}
+
+macro_rules! basic_window_classes {
+ () => {};
+ ( $(#[$attr:meta])* struct $name:ident => $class:expr; $($rest:tt)* ) => {
+ #[derive(Default)]
+ $(#[$attr])*
+ struct $name;
+
+ impl $crate::ui::ui_impl::window::WindowClass for $name {
+ fn class_name() -> $crate::ui::ui_impl::WideString {
+ $crate::ui::ui_impl::WideString::new($class)
+ }
+ }
+
+ impl $crate::ui::ui_impl::window::WindowData for $name {}
+
+ $crate::ui::ui_impl::window::basic_window_classes!($($rest)*);
+ }
+}
+
+pub(crate) use basic_window_classes;
+
+pub struct WindowBuilder<'a, W> {
+ name: Option<&'a WideString>,
+ style: Option<u32>,
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+ parent: Option<HWND>,
+ child_id: i32,
+ module: HINSTANCE,
+ data: W,
+}
+
+impl<'a, W> WindowBuilder<'a, W> {
+ #[must_use]
+ pub fn name<'b>(self, s: &'b WideString) -> WindowBuilder<'b, W> {
+ WindowBuilder {
+ name: Some(s),
+ style: self.style,
+ x: self.x,
+ y: self.y,
+ width: self.width,
+ height: self.height,
+ parent: self.parent,
+ child_id: self.child_id,
+ module: self.module,
+ data: self.data,
+ }
+ }
+
+ #[must_use]
+ pub fn style(mut self, d: u32) -> Self {
+ self.style = Some(d);
+ self
+ }
+
+ #[must_use]
+ pub fn add_style(mut self, d: u32) -> Self {
+ *self.style.get_or_insert(0) |= d;
+ self
+ }
+
+ #[must_use]
+ pub fn pos(mut self, x: i32, y: i32) -> Self {
+ self.x = x;
+ self.y = y;
+ self
+ }
+
+ #[must_use]
+ pub fn size(mut self, width: i32, height: i32) -> Self {
+ self.width = width;
+ self.height = height;
+ self
+ }
+
+ #[must_use]
+ pub fn parent(mut self, parent: HWND) -> Self {
+ self.parent = Some(parent);
+ self
+ }
+
+ #[must_use]
+ pub fn child_id(mut self, id: i32) -> Self {
+ self.child_id = id;
+ self
+ }
+
+ pub fn create(self) -> Window<W>
+ where
+ W: WindowClass + WindowData,
+ {
+ let class_name = W::class_name();
+ let handle = unsafe {
+ win::CreateWindowExW(
+ 0,
+ class_name.pcwstr(),
+ self.name.map(|n| n.pcwstr()).unwrap_or(std::ptr::null()),
+ self.style.unwrap_or_default(),
+ self.x,
+ self.y,
+ self.width,
+ self.height,
+ self.parent.unwrap_or_default(),
+ self.child_id as _,
+ self.module,
+ self.data.to_ptr() as _,
+ )
+ };
+ assert!(handle != 0);
+
+ Window {
+ handle,
+ child_id: self.child_id,
+ font_set: false,
+ _class: std::marker::PhantomData,
+ }
+ }
+}
+
+/// A window handle with a known class type.
+///
+/// Without a type parameter (defaulting to `()`), the window handle is generic (class type
+/// unknown).
+pub struct Window<W: 'static = ()> {
+ pub handle: HWND,
+ pub child_id: i32,
+ font_set: bool,
+ _class: std::marker::PhantomData<&'static RefCell<W>>,
+}
+
+impl<W: CustomWindowClass> Window<W> {
+ /// Get the window data of the window.
+ #[allow(dead_code)]
+ pub fn data(&self) -> &RefCell<W> {
+ unsafe { W::get(self.handle).as_ref().unwrap() }
+ }
+}
+
+impl<W> Window<W> {
+ /// Get a generic window handle.
+ pub fn generic(self) -> Window {
+ Window {
+ handle: self.handle,
+ child_id: self.child_id,
+ font_set: self.font_set,
+ _class: std::marker::PhantomData,
+ }
+ }
+
+ /// Set a window's font.
+ pub fn set_font(&mut self, font: &Font) {
+ unsafe { win::SendMessageW(self.handle, win::WM_SETFONT, **font as _, 1 as _) };
+ self.font_set = true;
+ }
+
+ /// Set a window's font if not already set.
+ pub fn set_default_font(&mut self, font: &Font) {
+ if !self.font_set {
+ self.set_font(font);
+ }
+ }
+}