Skip to content

Commit f7e4991

Browse files
haasalamrbashirJonasKruckenberg
authored
Inset Mac OS traffic lights (#513)
* new example * feat(macos): Fixed the traffic light example Traffic lights get refreshed. Still artifacts visible * feat(macos): Implemented and exposed a traffic light reposition method for Builder and Window * fix(macos): fixed the issue with the artifact on resize * Update src/platform_impl/macos/window.rs Co-authored-by: Amr Bashir <[email protected]> Signed-off-by: amrbashir <[email protected]> * Update src/platform_impl/macos/window.rs Co-authored-by: Amr Bashir <[email protected]> Signed-off-by: amrbashir <[email protected]> * Update src/platform_impl/macos/window.rs Co-authored-by: Amr Bashir <[email protected]> Signed-off-by: amrbashir <[email protected]> * Update src/platform_impl/macos/window.rs Co-authored-by: Amr Bashir <[email protected]> Signed-off-by: amrbashir <[email protected]> * Update src/platform_impl/macos/window.rs Co-authored-by: Amr Bashir <[email protected]> Signed-off-by: amrbashir <[email protected]> * Update src/platform_impl/macos/window.rs Co-authored-by: Amr Bashir <[email protected]> Signed-off-by: amrbashir <[email protected]> * Update src/platform_impl/macos/window.rs Co-authored-by: Amr Bashir <[email protected]> Signed-off-by: amrbashir <[email protected]> * Update src/platform_impl/macos/window.rs Co-authored-by: Amr Bashir <[email protected]> Signed-off-by: amrbashir <[email protected]> * Delete macos_traffic_lights.rs Example not needed Signed-off-by: amrbashir <[email protected]> * Update window.rs Deleted outdated TODO Signed-off-by: amrbashir <[email protected]> * refactor: Replaced type of inset with LogicalSize + docs Signed-off-by: amrbashir <[email protected]> * fixes Signed-off-by: amrbashir <[email protected]> * revert accidental commit Signed-off-by: amrbashir <[email protected]> * add back example Signed-off-by: amrbashir <[email protected]> * fix(macos): traffic light offset now works as expected The set_traffic_light_offset() method on window was removed and only the builder function now has to be called to set the offset. No more manual event handling is needed. Signed-off-by: amrbashir <[email protected]> * refactor(macos): removed the traffic light example the example is no longer needed as the api design is now clear and only one method call is needed to set the inset Signed-off-by: amrbashir <[email protected]> * feat(macos): added set_traffic_light_position back in the set_traffic_light_position on window will now work as expected. I.e. only one call is necessary Signed-off-by: amrbashir <[email protected]> * delete comment Co-authored-by: Amr Bashir <[email protected]> Signed-off-by: amrbashir <[email protected]> * revert(macos): reverted accidental deletion of null check Signed-off-by: amrbashir <[email protected]> * refactor(macos): changed traffic light inset to LogicalPosition<f64> Signed-off-by: amrbashir <[email protected]> * refactor(macos): changed pulic api back to Position Also unified the api to always use inset instead of position Signed-off-by: amrbashir <[email protected]> * cleanup after rebase Signed-off-by: amrbashir <[email protected]> * more cleanup * fix macos --------- Signed-off-by: amrbashir <[email protected]> Co-authored-by: Amr Bashir <[email protected]> Co-authored-by: Jonas Kruckenberg <[email protected]>
1 parent bd8bafe commit f7e4991

File tree

3 files changed

+71
-3
lines changed

3 files changed

+71
-3
lines changed

src/platform/macos.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use std::os::raw::c_void;
88

99
use crate::{
10-
dpi::LogicalSize,
10+
dpi::{LogicalSize, Position},
1111
event_loop::{EventLoop, EventLoopWindowTarget},
1212
monitor::MonitorHandle,
1313
platform_impl::{get_aux_state_mut, Parent},
@@ -49,6 +49,8 @@ pub trait WindowExtMacOS {
4949
/// Sets whether or not the window has shadow.
5050
fn set_has_shadow(&self, has_shadow: bool);
5151

52+
/// Set the window traffic light position relative to the upper left corner
53+
fn set_traffic_light_inset<P: Into<Position>>(&self, position: P);
5254
/// Put the window in a state which indicates a file save is required.
5355
///
5456
/// <https://developer.apple.com/documentation/appkit/nswindow/1419311-isdocumentedited>
@@ -105,6 +107,11 @@ impl WindowExtMacOS for Window {
105107
self.window.set_has_shadow(has_shadow)
106108
}
107109

110+
#[inline]
111+
fn set_traffic_light_inset<P: Into<Position>>(&self, position: P) {
112+
self.window.set_traffic_light_inset(position)
113+
}
114+
108115
#[inline]
109116
fn set_is_document_edited(&self, edited: bool) {
110117
self.window.set_is_document_edited(edited)
@@ -194,6 +201,8 @@ pub trait WindowBuilderExtMacOS {
194201
fn with_disallow_hidpi(self, disallow_hidpi: bool) -> WindowBuilder;
195202
/// Sets whether or not the window has shadow.
196203
fn with_has_shadow(self, has_shadow: bool) -> WindowBuilder;
204+
/// Sets the traffic light position to (x, y) relative to the upper left corner
205+
fn with_traffic_light_inset<P: Into<Position>>(self, inset: P) -> WindowBuilder;
197206
/// Sets whether the system can automatically organize windows into tabs.
198207
fn with_automatic_window_tabbing(self, automatic_tabbing: bool) -> WindowBuilder;
199208
/// Defines the window [tabbing identifier].
@@ -266,6 +275,12 @@ impl WindowBuilderExtMacOS for WindowBuilder {
266275
self
267276
}
268277

278+
#[inline]
279+
fn with_traffic_light_inset<P: Into<Position>>(mut self, inset: P) -> WindowBuilder {
280+
self.platform_specific.traffic_light_inset = Some(inset.into());
281+
self
282+
}
283+
269284
#[inline]
270285
fn with_automatic_window_tabbing(mut self, automatic_tabbing: bool) -> WindowBuilder {
271286
self.platform_specific.automatic_tabbing = automatic_tabbing;

src/platform_impl/macos/view.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::{
1111
};
1212

1313
use cocoa::{
14-
appkit::{NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow},
14+
appkit::{NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow, NSWindowButton},
1515
base::{id, nil},
1616
foundation::{NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger},
1717
};
@@ -70,6 +70,7 @@ pub(super) struct ViewState {
7070
pub(super) modifiers: ModifiersState,
7171
phys_modifiers: HashSet<KeyCode>,
7272
tracking_rect: Option<NSInteger>,
73+
pub(super) traffic_light_inset: Option<LogicalPosition<f64>>,
7374
}
7475

7576
impl ViewState {
@@ -91,6 +92,7 @@ pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
9192
modifiers: Default::default(),
9293
phys_modifiers: Default::default(),
9394
tracking_rect: None,
95+
traffic_light_inset: None,
9496
};
9597
unsafe {
9698
// This is free'd in `dealloc`
@@ -384,6 +386,11 @@ extern "C" fn draw_rect(this: &Object, _sel: Sel, rect: NSRect) {
384386
let state_ptr: *mut c_void = *this.get_ivar("taoState");
385387
let state = &mut *(state_ptr as *mut ViewState);
386388

389+
if let Some(position) = state.traffic_light_inset {
390+
let window = state.ns_window;
391+
inset_traffic_lights(window, position);
392+
}
393+
387394
AppState::handle_redraw(WindowId(get_window_id(state.ns_window)));
388395

389396
let superclass = util::superclass(this);
@@ -1176,3 +1183,29 @@ extern "C" fn wants_key_down_for_event(_this: &Object, _sel: Sel, _event: id) ->
11761183
extern "C" fn accepts_first_mouse(_this: &Object, _sel: Sel, _event: id) -> BOOL {
11771184
YES
11781185
}
1186+
1187+
pub unsafe fn inset_traffic_lights<W: NSWindow + Copy>(window: W, position: LogicalPosition<f64>) {
1188+
let (x, y) = (position.x, position.y);
1189+
1190+
let close = window.standardWindowButton_(NSWindowButton::NSWindowCloseButton);
1191+
let miniaturize = window.standardWindowButton_(NSWindowButton::NSWindowMiniaturizeButton);
1192+
let zoom = window.standardWindowButton_(NSWindowButton::NSWindowZoomButton);
1193+
1194+
let title_bar_container_view = close.superview().superview();
1195+
1196+
let close_rect = NSView::frame(close);
1197+
let title_bar_frame_height = close_rect.size.height + y;
1198+
let mut title_bar_rect = NSView::frame(title_bar_container_view);
1199+
title_bar_rect.size.height = title_bar_frame_height;
1200+
title_bar_rect.origin.y = window.frame().size.height - title_bar_frame_height;
1201+
let _: () = msg_send![title_bar_container_view, setFrame: title_bar_rect];
1202+
1203+
let window_buttons = vec![close, miniaturize, zoom];
1204+
let space_between = NSView::frame(miniaturize).origin.x - close_rect.origin.x;
1205+
1206+
for (i, button) in window_buttons.into_iter().enumerate() {
1207+
let mut rect = NSView::frame(button);
1208+
rect.origin.x = x + (i as f64 * space_between);
1209+
button.setFrameOrigin(rect.origin);
1210+
}
1211+
}

src/platform_impl/macos/window.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use objc::{
5555
runtime::{Class, Object, Sel, BOOL, NO, YES},
5656
};
5757

58-
use super::util::ns_string_to_rust;
58+
use super::{util::ns_string_to_rust, view::ViewState};
5959

6060
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6161
pub struct Id(pub usize);
@@ -91,6 +91,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
9191
pub resize_increments: Option<LogicalSize<f64>>,
9292
pub disallow_hidpi: bool,
9393
pub has_shadow: bool,
94+
pub traffic_light_inset: Option<Position>,
9495
pub automatic_tabbing: bool,
9596
pub tabbing_identifier: Option<String>,
9697
}
@@ -109,6 +110,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
109110
resize_increments: None,
110111
disallow_hidpi: false,
111112
has_shadow: true,
113+
traffic_light_inset: None,
112114
automatic_tabbing: true,
113115
tabbing_identifier: None,
114116
}
@@ -125,6 +127,14 @@ unsafe fn create_view(
125127
ns_view.setWantsBestResolutionOpenGLSurface_(YES);
126128
}
127129

130+
if let Some(position) = pl_attribs.traffic_light_inset {
131+
let state_ptr: *mut c_void = *(**ns_view).get_ivar("taoState");
132+
let state = &mut *(state_ptr as *mut ViewState);
133+
let scale_factor = NSWindow::backingScaleFactor(ns_window);
134+
let position = position.to_logical(scale_factor);
135+
state.traffic_light_inset = Some(position);
136+
}
137+
128138
// On Mojave, views automatically become layer-backed shortly after being added to
129139
// a window. Changing the layer-backedness of a view breaks the association between
130140
// the view and its associated OpenGL context. To work around this, on Mojave we
@@ -1547,6 +1557,16 @@ impl WindowExtMacOS for UnownedWindow {
15471557
}
15481558
}
15491559

1560+
#[inline]
1561+
fn set_traffic_light_inset<P: Into<Position>>(&self, position: P) {
1562+
let position: Position = position.into();
1563+
unsafe {
1564+
let state_ptr: *mut c_void = *(**self.ns_view).get_ivar("taoState");
1565+
let state = &mut *(state_ptr as *mut ViewState);
1566+
state.traffic_light_inset = Some(position.to_logical(self.scale_factor()));
1567+
}
1568+
}
1569+
15501570
#[inline]
15511571
fn set_is_document_edited(&self, edited: bool) {
15521572
unsafe {

0 commit comments

Comments
 (0)