Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions winit-appkit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ pub trait WindowExtMacOS {

/// Getter for the [`WindowExtMacOS::set_unified_titlebar`].
fn unified_titlebar(&self) -> bool;

/// Returns the height of the titlebar in logical points.
///
/// This can be useful when drawing custom UI that needs to align with the
/// system titlebar.
fn titlebar_height(&self) -> f64;
}

impl WindowExtMacOS for dyn Window + '_ {
Expand Down Expand Up @@ -297,6 +303,12 @@ impl WindowExtMacOS for dyn Window + '_ {
let window = self.cast_ref::<AppKitWindow>().unwrap();
window.maybe_wait_on_main(|w| w.unified_titlebar())
}

#[inline]
fn titlebar_height(&self) -> f64 {
let window = self.cast_ref::<AppKitWindow>().unwrap();
window.maybe_wait_on_main(|w| w.titlebar_height())
}
}

/// Corresponds to `NSApplicationActivationPolicy`.
Expand Down
32 changes: 24 additions & 8 deletions winit-appkit/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,11 @@ define_class!(
fn draw_rect(&self, _rect: NSRect) {
trace_scope!("drawRect:");

self.ivars().app_state.handle_redraw(window_id(&self.window()));
let Some(window) = self.window() else {
return;
};

self.ivars().app_state.handle_redraw(window_id(&window));

// This is a direct subclass of NSView, no need to call superclass' drawRect:
}
Expand Down Expand Up @@ -420,7 +424,11 @@ define_class!(
}

// Send command action to user if they requested it.
let window_id = window_id(&self.window());
let Some(window) = self.window() else {
return;
};

let window_id = window_id(&window);
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
if let Some(handler) = app.macos_handler() {
handler.standard_key_binding(
Expand Down Expand Up @@ -525,7 +533,9 @@ define_class!(
#[unsafe(method(insertTab:))]
fn insert_tab(&self, _sender: Option<&AnyObject>) {
trace_scope!("insertTab:");
let window = self.window();
let Some(window) = self.window() else {
return;
};
if let Some(first_responder) = window.firstResponder() {
if *first_responder == ***self {
window.selectNextKeyView(Some(self))
Expand All @@ -536,7 +546,9 @@ define_class!(
#[unsafe(method(insertBackTab:))]
fn insert_back_tab(&self, _sender: Option<&AnyObject>) {
trace_scope!("insertBackTab:");
let window = self.window();
let Some(window) = self.window() else {
return;
};
if let Some(first_responder) = window.firstResponder() {
if *first_responder == ***self {
window.selectPreviousKeyView(Some(self))
Expand Down Expand Up @@ -818,19 +830,23 @@ impl WinitView {
this
}

fn window(&self) -> Retained<NSWindow> {
(**self).window().expect("view must be installed in a window")
fn window(&self) -> Option<Retained<NSWindow>> {
(**self).window()
}

fn queue_event(&self, event: WindowEvent) {
let window_id = window_id(&self.window());
let Some(window) = self.window() else {
return;
};

let window_id = window_id(&window);
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
app.window_event(event_loop, window_id, event);
});
}

fn scale_factor(&self) -> f64 {
self.window().backingScaleFactor() as f64
self.window().map(|window| window.backingScaleFactor() as f64).unwrap_or(1.0)
}

fn is_ime_enabled(&self) -> bool {
Expand Down
8 changes: 8 additions & 0 deletions winit-appkit/src/window_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2029,6 +2029,14 @@ impl WindowExtMacOS for WindowDelegate {

window.toolbar().is_some() && window.toolbarStyle() == NSWindowToolbarStyle::Unified
}

fn titlebar_height(&self) -> f64 {
let window = self.window();
let frame = window.frame();
let content = window.contentRectForFrameRect(frame);

(frame.size.height - content.size.height).max(0.0)
}
}

const DEFAULT_STANDARD_FRAME: NSRect =
Expand Down
5 changes: 4 additions & 1 deletion winit-core/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1388,7 +1388,10 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug {
/// the title bar. This is useful when implementing custom decorations.
///
/// ## Platform-specific
/// **Android / iOS / macOS / Orbital / Wayland / Web / X11:** Unsupported.
/// - **Windows:** Supported.
/// - **Wayland:** Supported.
/// - **X11:** Supported on some window managers (via `_GTK_SHOW_WINDOW_MENU`).
/// - **Android / iOS / macOS / Orbital / Web:** Unsupported.
///
/// [window menu]: https://en.wikipedia.org/wiki/Common_menus_in_Microsoft_Windows#System_menu
fn show_window_menu(&self, position: Position);
Expand Down
1 change: 1 addition & 0 deletions winit-x11/src/atoms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ atom_manager! {
None: b"None",

// Miscellaneous Atoms
_GTK_SHOW_WINDOW_MENU,
_GTK_THEME_VARIANT,
_MOTIF_WM_HINTS,
_NET_ACTIVE_WINDOW,
Expand Down
31 changes: 30 additions & 1 deletion winit-x11/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2007,7 +2007,36 @@ impl UnownedWindow {
}

#[inline]
pub fn show_window_menu(&self, _position: Position) {}
pub fn show_window_menu(&self, position: Position) {
let atoms = self.xconn.atoms();
let show_menu_message = atoms[_GTK_SHOW_WINDOW_MENU];

if !util::hint_is_supported(show_menu_message) {
return;
}

let (x, y): (i32, i32) = position.to_physical::<i32>(self.scale_factor()).into();
let (window_x, window_y) = self.inner_position_physical();
let x_root = (i64::from(window_x) + i64::from(x)).max(0) as u32;
let y_root = (i64::from(window_y) + i64::from(y)).max(0) as u32;

let result = self.xconn.send_client_msg(
self.xwindow,
self.root,
show_menu_message,
Some(xproto::EventMask::SUBSTRUCTURE_REDIRECT | xproto::EventMask::SUBSTRUCTURE_NOTIFY),
[util::VIRTUAL_CORE_POINTER as u32, x_root, y_root, 0, 0],
);

if let Err(err) = result {
tracing::error!("failed to request window menu: {err}");
return;
}

if let Err(err) = self.xconn.flush_requests() {
tracing::error!("failed to flush window menu request: {err}");
}
}

/// Resizes the window while it is being dragged.
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
Expand Down
Loading
Loading