Revert "windows: better looking titlebar" and follow-up (#9392)

This reverts #9053 and #9375 because they introduced a regression on
`main` that broke the titlebars on macOS:


![image](https://github.com/zed-industries/zed/assets/1185253/d046003b-5c66-4a42-9385-623f5d58c9a4)

Two things are off:

- Left padding is missing
- Titlebar height is less than it was before, which means the
traffic-light buttons are not centered vertically

What @as-cii and I noticed while looking into this: the `cfg!(macos)`
macros that were used don't work like that. You need to check for
`cfg!(target = "macos")` etc. Means that on macOS we never used the
macOS-specific code because the condition was always false.

Overall height, we're not sure about.

Release Notes:

- N/A
This commit is contained in:
Thorsten Ball 2024-03-15 12:25:51 +01:00 committed by GitHub
parent 5ae145145e
commit 5bf0c8ed2d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 121 additions and 687 deletions

View file

@ -1,4 +1,6 @@
#![deny(unsafe_op_in_unsafe_fn)]
// todo(windows): remove
#![allow(unused_variables)]
use std::{
any::Any,
@ -12,7 +14,6 @@ use std::{
sync::{Arc, Once},
};
use ::util::ResultExt;
use blade_graphics as gpu;
use futures::channel::oneshot::{self, Receiver};
use itertools::Itertools;
@ -27,7 +28,6 @@ use windows::{
System::{Com::*, Ole::*, SystemServices::*},
UI::{
Controls::*,
HiDpi::*,
Input::{Ime::*, KeyboardAndMouse::*},
Shell::*,
WindowsAndMessaging::*,
@ -48,7 +48,6 @@ pub(crate) struct WindowsWindowInner {
callbacks: RefCell<Callbacks>,
platform_inner: Rc<WindowsPlatformInner>,
handle: AnyWindowHandle,
scale_factor: f32,
}
impl WindowsWindowInner {
@ -111,48 +110,9 @@ impl WindowsWindowInner {
callbacks,
platform_inner,
handle,
scale_factor: 1.0,
}
}
fn is_maximized(&self) -> bool {
let mut placement = WINDOWPLACEMENT::default();
placement.length = std::mem::size_of::<WINDOWPLACEMENT>() as u32;
if unsafe { GetWindowPlacement(self.hwnd, &mut placement) }.is_ok() {
return placement.showCmd == SW_SHOWMAXIMIZED.0 as u32;
}
return false;
}
fn get_titlebar_rect(&self) -> anyhow::Result<RECT> {
let top_and_bottom_borders = 2;
let theme = unsafe { OpenThemeData(self.hwnd, w!("WINDOW")) };
let title_bar_size = unsafe {
GetThemePartSize(
theme,
HDC::default(),
WP_CAPTION.0,
CS_ACTIVE.0,
None,
TS_TRUE,
)
}?;
unsafe { CloseThemeData(theme) }?;
let mut height =
(title_bar_size.cy as f32 * self.scale_factor).round() as i32 + top_and_bottom_borders;
if self.is_maximized() {
let dpi = unsafe { GetDpiForWindow(self.hwnd) };
height += unsafe { (GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) * 2) as i32 };
}
let mut rect = RECT::default();
unsafe { GetClientRect(self.hwnd, &mut rect) }?;
rect.bottom = rect.top + height;
Ok(rect)
}
fn is_virtual_key_pressed(&self, vkey: VIRTUAL_KEY) -> bool {
unsafe { GetKeyState(vkey.0 as i32) < 0 }
}
@ -176,30 +136,12 @@ impl WindowsWindowInner {
fn handle_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
log::debug!("msg: {msg}, wparam: {}, lparam: {}", wparam.0, lparam.0);
match msg {
WM_ACTIVATE => self.handle_activate_msg(msg, wparam, lparam),
WM_CREATE => self.handle_create_msg(lparam),
WM_MOVE => self.handle_move_msg(lparam),
WM_SIZE => self.handle_size_msg(lparam),
WM_NCCALCSIZE => self.handle_calc_client_size(msg, wparam, lparam),
WM_DPICHANGED => self.handle_dpi_changed_msg(msg, wparam, lparam),
WM_NCHITTEST => self.handle_hit_test_msg(msg, wparam, lparam),
WM_PAINT => self.handle_paint_msg(),
WM_CLOSE => self.handle_close_msg(msg, wparam, lparam),
WM_DESTROY => self.handle_destroy_msg(),
WM_MOUSEMOVE => self.handle_mouse_move_msg(lparam, wparam),
WM_NCMOUSEMOVE => self.handle_nc_mouse_move_msg(msg, wparam, lparam),
WM_NCLBUTTONDOWN => {
self.handle_nc_mouse_down_msg(MouseButton::Left, msg, wparam, lparam)
}
WM_NCRBUTTONDOWN => {
self.handle_nc_mouse_down_msg(MouseButton::Right, msg, wparam, lparam)
}
WM_NCMBUTTONDOWN => {
self.handle_nc_mouse_down_msg(MouseButton::Middle, msg, wparam, lparam)
}
WM_NCLBUTTONUP => self.handle_nc_mouse_up_msg(MouseButton::Left, msg, wparam, lparam),
WM_NCRBUTTONUP => self.handle_nc_mouse_up_msg(MouseButton::Right, msg, wparam, lparam),
WM_NCMBUTTONUP => self.handle_nc_mouse_up_msg(MouseButton::Middle, msg, wparam, lparam),
WM_LBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Left, lparam),
WM_RBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Right, lparam),
WM_MBUTTONDOWN => self.handle_mouse_down_msg(MouseButton::Middle, lparam),
@ -282,7 +224,7 @@ impl WindowsWindowInner {
fn handle_paint_msg(&self) -> LRESULT {
let mut paint_struct = PAINTSTRUCT::default();
let _hdc = unsafe { BeginPaint(self.hwnd, &mut paint_struct) };
let hdc = unsafe { BeginPaint(self.hwnd, &mut paint_struct) };
let mut callbacks = self.callbacks.borrow_mut();
if let Some(request_frame) = callbacks.request_frame.as_mut() {
request_frame();
@ -349,7 +291,7 @@ impl WindowsWindowInner {
pressed_button,
modifiers: self.current_modifiers(),
};
if callback(PlatformInput::MouseMove(event)).default_prevented {
if callback(PlatformInput::MouseMove(event)) {
return LRESULT(0);
}
}
@ -475,7 +417,7 @@ impl WindowsWindowInner {
keystroke,
is_held: lparam.0 & (0x1 << 30) > 0,
};
if func(PlatformInput::KeyDown(event)).default_prevented {
if func(PlatformInput::KeyDown(event)) {
self.invalidate_client_area();
return LRESULT(0);
}
@ -492,14 +434,14 @@ impl WindowsWindowInner {
return unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) };
};
let event = KeyUpEvent { keystroke };
if func(PlatformInput::KeyUp(event)).default_prevented {
if func(PlatformInput::KeyUp(event)) {
self.invalidate_client_area();
return LRESULT(0);
}
unsafe { DefWindowProcW(self.hwnd, message, wparam, lparam) }
}
fn handle_keydown_msg(&self, _msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
fn handle_keydown_msg(&self, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
let Some(keystroke) = self.parse_keydown_msg_keystroke(wparam) else {
return LRESULT(1);
};
@ -510,14 +452,14 @@ impl WindowsWindowInner {
keystroke,
is_held: lparam.0 & (0x1 << 30) > 0,
};
if func(PlatformInput::KeyDown(event)).default_prevented {
if func(PlatformInput::KeyDown(event)) {
self.invalidate_client_area();
return LRESULT(0);
}
LRESULT(1)
}
fn handle_keyup_msg(&self, _msg: u32, wparam: WPARAM) -> LRESULT {
fn handle_keyup_msg(&self, message: u32, wparam: WPARAM) -> LRESULT {
let Some(keystroke) = self.parse_keydown_msg_keystroke(wparam) else {
return LRESULT(1);
};
@ -525,14 +467,14 @@ impl WindowsWindowInner {
return LRESULT(1);
};
let event = KeyUpEvent { keystroke };
if func(PlatformInput::KeyUp(event)).default_prevented {
if func(PlatformInput::KeyUp(event)) {
self.invalidate_client_area();
return LRESULT(0);
}
LRESULT(1)
}
fn handle_char_msg(&self, _msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
fn handle_char_msg(&self, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
let Some(keystroke) = self.parse_char_msg_keystroke(wparam) else {
return LRESULT(1);
};
@ -545,7 +487,7 @@ impl WindowsWindowInner {
keystroke,
is_held: lparam.0 & (0x1 << 30) > 0,
};
if func(PlatformInput::KeyDown(event)).default_prevented {
if func(PlatformInput::KeyDown(event)) {
self.invalidate_client_area();
return LRESULT(0);
}
@ -573,7 +515,7 @@ impl WindowsWindowInner {
modifiers: self.current_modifiers(),
click_count: 1,
};
if callback(PlatformInput::MouseDown(event)).default_prevented {
if callback(PlatformInput::MouseDown(event)) {
return LRESULT(0);
}
}
@ -591,7 +533,7 @@ impl WindowsWindowInner {
modifiers: self.current_modifiers(),
click_count: 1,
};
if callback(PlatformInput::MouseUp(event)).default_prevented {
if callback(PlatformInput::MouseUp(event)) {
return LRESULT(0);
}
}
@ -636,7 +578,7 @@ impl WindowsWindowInner {
modifiers: self.current_modifiers(),
touch_phase: TouchPhase::Moved,
};
if callback(PlatformInput::ScrollWheel(event)).default_prevented {
if callback(PlatformInput::ScrollWheel(event)) {
return LRESULT(0);
}
}
@ -743,237 +685,12 @@ impl WindowsWindowInner {
};
func(input);
}
/// SEE: https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize
fn handle_calc_client_size(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
if wparam.0 == 0 {
return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) };
}
let dpi = unsafe { GetDpiForWindow(self.hwnd) };
let frame_x = unsafe { GetSystemMetricsForDpi(SM_CXFRAME, dpi) };
let frame_y = unsafe { GetSystemMetricsForDpi(SM_CYFRAME, dpi) };
let padding = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) };
// wparam is TRUE so lparam points to an NCCALCSIZE_PARAMS structure
let mut params = lparam.0 as *mut NCCALCSIZE_PARAMS;
let mut requested_client_rect = unsafe { &mut ((*params).rgrc) };
requested_client_rect[0].right -= frame_x + padding;
requested_client_rect[0].left += frame_x + padding;
requested_client_rect[0].bottom -= frame_y + padding;
LRESULT(0)
}
fn handle_activate_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
if let Some(titlebar_rect) = self.get_titlebar_rect().log_err() {
unsafe { InvalidateRect(self.hwnd, Some(&titlebar_rect), FALSE) };
}
return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) };
}
fn handle_create_msg(&self, _lparam: LPARAM) -> LRESULT {
let mut size_rect = RECT::default();
unsafe { GetWindowRect(self.hwnd, &mut size_rect).log_err() };
let width = size_rect.right - size_rect.left;
let height = size_rect.bottom - size_rect.top;
self.size.set(Size {
width: GlobalPixels::from(width as f64),
height: GlobalPixels::from(height as f64),
});
// Inform the application of the frame change to force redrawing with the new
// client area that is extended into the title bar
unsafe {
SetWindowPos(
self.hwnd,
HWND::default(),
size_rect.left,
size_rect.top,
width,
height,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE,
)
.log_err()
};
LRESULT(0)
}
fn handle_dpi_changed_msg(&self, _msg: u32, _wparam: WPARAM, _lparam: LPARAM) -> LRESULT {
LRESULT(1)
}
fn handle_hit_test_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
// default handler for resize areas
let hit = unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) };
if matches!(
hit.0 as u32,
HTNOWHERE
| HTRIGHT
| HTLEFT
| HTTOPLEFT
| HTTOP
| HTTOPRIGHT
| HTBOTTOMRIGHT
| HTBOTTOM
| HTBOTTOMLEFT
) {
return hit;
}
let dpi = unsafe { GetDpiForWindow(self.hwnd) };
let frame_y = unsafe { GetSystemMetricsForDpi(SM_CYFRAME, dpi) };
let padding = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) };
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
y: lparam.signed_hiword().into(),
};
unsafe { ScreenToClient(self.hwnd, &mut cursor_point) };
if cursor_point.y > 0 && cursor_point.y < frame_y + padding {
return LRESULT(HTTOP as _);
}
let titlebar_rect = self.get_titlebar_rect();
if let Ok(titlebar_rect) = titlebar_rect {
if cursor_point.y < titlebar_rect.bottom {
let caption_btn_width = unsafe { GetSystemMetricsForDpi(SM_CXSIZE, dpi) };
if cursor_point.x >= titlebar_rect.right - caption_btn_width {
return LRESULT(HTCLOSE as _);
} else if cursor_point.x >= titlebar_rect.right - caption_btn_width * 2 {
return LRESULT(HTMAXBUTTON as _);
} else if cursor_point.x >= titlebar_rect.right - caption_btn_width * 3 {
return LRESULT(HTMINBUTTON as _);
}
return LRESULT(HTCAPTION as _);
}
}
LRESULT(HTCLIENT as _)
}
fn handle_nc_mouse_move_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
y: lparam.signed_hiword().into(),
};
unsafe { ScreenToClient(self.hwnd, &mut cursor_point) };
let x = Pixels::from(cursor_point.x as f32);
let y = Pixels::from(cursor_point.y as f32);
self.mouse_position.set(Point { x, y });
let mut callbacks = self.callbacks.borrow_mut();
if let Some(callback) = callbacks.input.as_mut() {
let event = MouseMoveEvent {
position: Point { x, y },
pressed_button: None,
modifiers: self.current_modifiers(),
};
if callback(PlatformInput::MouseMove(event)).default_prevented {
return LRESULT(0);
}
}
drop(callbacks);
unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }
}
fn handle_nc_mouse_down_msg(
&self,
button: MouseButton,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
let mut callbacks = self.callbacks.borrow_mut();
if let Some(callback) = callbacks.input.as_mut() {
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
y: lparam.signed_hiword().into(),
};
unsafe { ScreenToClient(self.hwnd, &mut cursor_point) };
let x = Pixels::from(cursor_point.x as f32);
let y = Pixels::from(cursor_point.y as f32);
let event = MouseDownEvent {
button: button.clone(),
position: Point { x, y },
modifiers: self.current_modifiers(),
click_count: 1,
};
if callback(PlatformInput::MouseDown(event)).default_prevented {
return LRESULT(0);
}
}
drop(callbacks);
match wparam.0 as u32 {
// Since these are handled in handle_nc_mouse_up_msg we must prevent the default window proc
HTMINBUTTON | HTMAXBUTTON | HTCLOSE => LRESULT(0),
_ => unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) },
}
}
fn handle_nc_mouse_up_msg(
&self,
button: MouseButton,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
let mut callbacks = self.callbacks.borrow_mut();
if let Some(callback) = callbacks.input.as_mut() {
let mut cursor_point = POINT {
x: lparam.signed_loword().into(),
y: lparam.signed_hiword().into(),
};
unsafe { ScreenToClient(self.hwnd, &mut cursor_point) };
let x = Pixels::from(cursor_point.x as f32);
let y = Pixels::from(cursor_point.y as f32);
let event = MouseUpEvent {
button,
position: Point { x, y },
modifiers: self.current_modifiers(),
click_count: 1,
};
if callback(PlatformInput::MouseUp(event)).default_prevented {
return LRESULT(0);
}
}
drop(callbacks);
if button == MouseButton::Left {
match wparam.0 as u32 {
HTMINBUTTON => unsafe {
ShowWindowAsync(self.hwnd, SW_MINIMIZE);
return LRESULT(0);
},
HTMAXBUTTON => unsafe {
if self.is_maximized() {
ShowWindowAsync(self.hwnd, SW_NORMAL);
} else {
ShowWindowAsync(self.hwnd, SW_MAXIMIZE);
}
return LRESULT(0);
},
HTCLOSE => unsafe {
PostMessageW(self.hwnd, WM_CLOSE, WPARAM::default(), LPARAM::default())
.log_err();
return LRESULT(0);
},
_ => {}
};
}
unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }
}
}
#[derive(Default)]
struct Callbacks {
request_frame: Option<Box<dyn FnMut()>>,
input: Option<Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>>,
input: Option<Box<dyn FnMut(crate::PlatformInput) -> bool>>,
active_status_change: Option<Box<dyn FnMut(bool)>>,
resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
fullscreen: Option<Box<dyn FnMut(bool)>>,
@ -1001,6 +718,7 @@ impl WindowsWindow {
handle: AnyWindowHandle,
options: WindowParams,
) -> Self {
let dwexstyle = WINDOW_EX_STYLE::default();
let classname = register_wnd_class();
let windowname = HSTRING::from(
options
@ -1010,7 +728,7 @@ impl WindowsWindow {
.map(|title| title.as_ref())
.unwrap_or(""),
);
let dwstyle = WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX;
let dwstyle = WS_OVERLAPPEDWINDOW & !WS_VISIBLE;
let x = options.bounds.origin.x.0 as i32;
let y = options.bounds.origin.y.0 as i32;
let nwidth = options.bounds.size.width.0 as i32;
@ -1026,7 +744,7 @@ impl WindowsWindow {
let lpparam = Some(&context as *const _ as *const _);
unsafe {
CreateWindowExW(
WS_EX_APPWINDOW,
dwexstyle,
classname,
&windowname,
dwstyle,
@ -1067,7 +785,7 @@ impl WindowsWindow {
}
fn maximize(&self) {
unsafe { ShowWindowAsync(self.inner.hwnd, SW_MAXIMIZE) };
unsafe { ShowWindow(self.inner.hwnd, SW_MAXIMIZE) };
}
}
@ -1108,10 +826,6 @@ impl PlatformWindow for WindowsWindow {
}
}
fn is_maximized(&self) -> bool {
self.inner.is_maximized()
}
// todo(windows)
fn content_size(&self) -> Size<Pixels> {
let size = self.inner.size.get();
@ -1123,12 +837,12 @@ impl PlatformWindow for WindowsWindow {
// todo(windows)
fn scale_factor(&self) -> f32 {
self.inner.scale_factor
1.0
}
// todo(windows)
fn titlebar_height(&self) -> Pixels {
let titlebar_rect = self.inner.get_titlebar_rect().unwrap();
((titlebar_rect.bottom - titlebar_rect.top) as f64).into()
20.0.into()
}
// todo(windows)
@ -1238,9 +952,8 @@ impl PlatformWindow for WindowsWindow {
Some(done_rx)
}
fn activate(&self) {
unsafe { ShowWindowAsync(self.inner.hwnd, SW_NORMAL) };
}
// todo(windows)
fn activate(&self) {}
// todo(windows)
fn set_title(&mut self, title: &str) {
@ -1250,18 +963,16 @@ impl PlatformWindow for WindowsWindow {
}
// todo(windows)
fn set_edited(&mut self, _edited: bool) {}
fn set_edited(&mut self, edited: bool) {}
// todo(windows)
fn show_character_palette(&self) {}
fn minimize(&self) {
unsafe { ShowWindowAsync(self.inner.hwnd, SW_MINIMIZE) };
}
// todo(windows)
fn minimize(&self) {}
fn zoom(&self) {
unsafe { ShowWindowAsync(self.inner.hwnd, SW_MAXIMIZE) };
}
// todo(windows)
fn zoom(&self) {}
// todo(windows)
fn toggle_fullscreen(&self) {}
@ -1277,7 +988,7 @@ impl PlatformWindow for WindowsWindow {
}
// todo(windows)
fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> DispatchEventResult>) {
fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
self.inner.callbacks.borrow_mut().input = Some(callback);
}
@ -1317,7 +1028,7 @@ impl PlatformWindow for WindowsWindow {
}
// todo(windows)
fn is_topmost_for_position(&self, _position: Point<Pixels>) -> bool {
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
true
}
@ -1335,7 +1046,6 @@ impl PlatformWindow for WindowsWindow {
#[implement(IDropTarget)]
struct WindowsDragDropHandler(pub Rc<WindowsWindowInner>);
#[allow(non_snake_case)]
impl IDropTarget_Impl for WindowsDragDropHandler {
fn DragEnter(
&self,
@ -1449,7 +1159,6 @@ fn register_wnd_class() -> PCWSTR {
lpfnWndProc: Some(wnd_proc),
hCursor: unsafe { LoadCursorW(None, IDC_ARROW).ok().unwrap() },
lpszClassName: PCWSTR(CLASS_NAME.as_ptr()),
style: CS_HREDRAW | CS_VREDRAW,
..Default::default()
};
unsafe { RegisterClassW(&wc) };
@ -1507,6 +1216,28 @@ pub(crate) fn try_get_window_inner(hwnd: HWND) -> Option<Rc<WindowsWindowInner>>
}
}
unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize {
#[cfg(target_pointer_width = "64")]
unsafe {
GetWindowLongPtrW(hwnd, nindex)
}
#[cfg(target_pointer_width = "32")]
unsafe {
GetWindowLongW(hwnd, nindex) as isize
}
}
unsafe fn set_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX, dwnewlong: isize) -> isize {
#[cfg(target_pointer_width = "64")]
unsafe {
SetWindowLongPtrW(hwnd, nindex, dwnewlong)
}
#[cfg(target_pointer_width = "32")]
unsafe {
SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize
}
}
fn basic_vkcode_to_string(code: u16, modifiers: Modifiers) -> Option<Keystroke> {
match code {
// VK_0 - VK_9