mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-19 16:16:45 +00:00
fix(desktop): keep macOS traffic lights aligned while resizing
This commit is contained in:
@@ -55,6 +55,14 @@ async fn set_macos_traffic_light_position_inner(
|
||||
button.setFrameOrigin(rect.origin);
|
||||
}
|
||||
|
||||
// AppKit re-runs the title bar layout during a live window resize and resets
|
||||
// the standard buttons to their default frames. The vendored wry re-applies
|
||||
// the traffic light inset from `drawRect:` (after that layout pass), which is
|
||||
// what the user actually sees. Hand it the position computed above so both
|
||||
// writers agree; otherwise the buttons flicker between the toolbar alignment
|
||||
// and wry's window creation inset while the window is being resized.
|
||||
apply_traffic_light_inset_to_wry(ns_window, target_x, target_y);
|
||||
|
||||
let Some(close) = ns_window.standardWindowButton(NSWindowButton::CloseButton) else {
|
||||
let _ = tx.send(None);
|
||||
return;
|
||||
@@ -98,6 +106,32 @@ fn validate_macos_traffic_light_position(x: f64, y: f64, scale: f64) -> Result<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hands the traffic light inset DBX computed to the vendored wry parent view.
|
||||
///
|
||||
/// wry re-applies the stored inset from `drawRect:` and that write is the one that
|
||||
/// survives AppKit's title bar layout, so keeping it in sync with DBX's toolbar
|
||||
/// alignment removes the flicker between the two during a window resize.
|
||||
#[cfg(target_os = "macos")]
|
||||
fn apply_traffic_light_inset_to_wry(ns_window: &objc2_app_kit::NSWindow, x: f64, y: f64) {
|
||||
use objc2::runtime::Sel;
|
||||
use objc2_app_kit::NSView;
|
||||
|
||||
let selector: Sel = objc2::sel!(dbxUpdateTrafficLightInsetX:y:);
|
||||
|
||||
fn visit(view: &NSView, selector: Sel, x: f64, y: f64) -> bool {
|
||||
let responds: bool = unsafe { objc2::msg_send![view, respondsToSelector: selector] };
|
||||
if responds {
|
||||
let _: () = unsafe { objc2::msg_send![view, dbxUpdateTrafficLightInsetX: x, y: y] };
|
||||
return true;
|
||||
}
|
||||
view.subviews().iter().any(|subview| visit(&subview, selector, x, y))
|
||||
}
|
||||
|
||||
if let Some(content_view) = ns_window.contentView() {
|
||||
let _ = visit(&content_view, selector, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub struct MacosTrafficLightLayout {
|
||||
x: f64,
|
||||
|
||||
Vendored
+13
@@ -24,3 +24,16 @@ dead-key/compose input. This matches the proposal in upstream
|
||||
tauri-apps/wry#1711 (see also tauri-apps/wry#1175, #1177) and unblocks
|
||||
in-app shortcuts such as the table structure editor (t8y2/dbx#7245,
|
||||
landed via PR #8650).
|
||||
|
||||
On macOS, DBX also adds `WryWebViewParent.dbxUpdateTrafficLightInsetX:y:` and
|
||||
makes `inset_traffic_lights` place the buttons' vertical position explicitly
|
||||
(top-down center-delta against the window frame) instead of relying on AppKit's
|
||||
title bar layout. Three writers fought over the button frames during a live
|
||||
window resize — AppKit's layout pass, the one-shot `set_macos_traffic_light_
|
||||
position` placement, and wry's `drawRect:` re-application of the creation-time
|
||||
inset — which made the traffic lights jump while dragging the window edge.
|
||||
DBX pushes its dynamic toolbar-aligned position through the new method so the
|
||||
`drawRect:` re-application replays the latest inset, and both writers converge
|
||||
on the same explicit placement. Without it, the stored static inset and AppKit
|
||||
layout disagree on every resize pass (see upstream tauri-apps/wry#1747 and
|
||||
tauri-apps/tauri#15451).
|
||||
|
||||
@@ -70,6 +70,22 @@ define_class!(
|
||||
unsafe { inset_traffic_lights(&self.window().unwrap(), x, y) };
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the traffic light inset stored at window creation.
|
||||
///
|
||||
/// `drawRect:` re-applies the stored inset after AppKit's title bar layout
|
||||
/// pass, which is the only write that survives a live window resize. DBX
|
||||
/// keeps the buttons aligned with its toolbar, so it pushes the dynamic
|
||||
/// position here to keep that re-application in sync with the one-shot
|
||||
/// placement instead of fighting it while the window is resized.
|
||||
#[cfg(target_os = "macos")]
|
||||
#[unsafe(method(dbxUpdateTrafficLightInsetX:y:))]
|
||||
fn dbx_update_traffic_light_inset(&self, x: f64, y: f64) {
|
||||
self.ivars().traffic_light_inset.set(Some((x, y)));
|
||||
if let Some(window) = self.window() {
|
||||
unsafe { inset_traffic_lights(&window, x, y) };
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -123,6 +139,17 @@ pub unsafe fn inset_traffic_lights(window: &NSWindow, x: f64, y: f64) {
|
||||
|
||||
let space_between = NSView::frame(&miniaturize).origin.x - close_rect.origin.x;
|
||||
|
||||
// AppKit re-runs its title bar layout during a live window resize and can move
|
||||
// the standard buttons back to their default frames, which is why this is also
|
||||
// called from `drawRect:`. Place the buttons explicitly instead of relying on
|
||||
// that layout pass so the requested inset is what ends up on screen.
|
||||
let window_height = window.frame().size.height;
|
||||
let current_window_rect =
|
||||
close.superview().map(|view| view.convertRect_toView(close_rect, None)).unwrap_or(close_rect);
|
||||
let current_center_y = window_height - (current_window_rect.origin.y + current_window_rect.size.height / 2.0);
|
||||
let target_center_y = y + close_rect.size.height / 2.0;
|
||||
let center_delta_y = target_center_y - current_center_y;
|
||||
|
||||
let mut window_buttons = vec![close, miniaturize];
|
||||
if let Some(zoom) = zoom {
|
||||
window_buttons.push(zoom);
|
||||
@@ -131,6 +158,7 @@ pub unsafe fn inset_traffic_lights(window: &NSWindow, x: f64, y: f64) {
|
||||
for (i, button) in window_buttons.into_iter().enumerate() {
|
||||
let mut rect = NSView::frame(&button);
|
||||
rect.origin.x = x + (i as f64 * space_between);
|
||||
rect.origin.y -= center_delta_y;
|
||||
button.setFrameOrigin(rect.origin);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user