diff --git a/crates/snowbound/src/aqua.rs b/crates/snowbound/src/aqua.rs index 5f475e2829308318102b94322eff2981da57fb98..4ad03391dfcbd105b25bfda25d62ba74fd2ac6ab 100644 --- a/crates/snowbound/src/aqua.rs +++ b/crates/snowbound/src/aqua.rs @@ -12,7 +12,7 @@ use objc2::{ sel, }; use objc2_foundation::{MainThreadMarker, NSPoint, NSRect, NSSize}; -use std::{cell::Cell, collections::HashMap}; +use std::collections::HashMap; use ui::{Axis, PaintedScroller, Scroller, ScrollerPart}; use winit::{ raw_window_handle::{HasWindowHandle, RawWindowHandle}, @@ -57,19 +57,11 @@ pub fn textured(window: &Window, row: f32) -> bool { true } -thread_local! { - static DRAGS: Cell = const { Cell::new(false) }; -} - -/// Whether a press where the pointer is drags the window, which a textured window asks -/// its content of: presses on empty toolbar space do, and nowhere else. -pub fn set_window_drags(drags: bool) { - DRAGS.set(drags); -} - -/// `-mouseDownCanMoveWindow` for the content view of a textured window. -pub extern "C" fn window_drags(_: &AnyObject, _: Sel) -> Bool { - Bool::new(DRAGS.get()) +/// `-mouseDownCanMoveWindow` for the content view: a textured window drags from any press +/// on a view that allows it, asking once. The app drags from its toolbar's empty space +/// itself, through `-performWindowDragWithEvent:`. +pub extern "C" fn no_window_drags(_: &AnyObject, _: Sel) -> Bool { + Bool::NO } /// The cursor for moving something: winit loads its move cursor from a folder of cursors diff --git a/crates/snowbound/src/linux.rs b/crates/snowbound/src/linux.rs index 6c5861024be22516003aeeb68c6cab8890768291..8870b50becd4f298ea228c184b4a190a75231c27 100644 --- a/crates/snowbound/src/linux.rs +++ b/crates/snowbound/src/linux.rs @@ -579,9 +579,6 @@ pub fn move_cursor() -> winit::window::CursorIcon { winit::window::CursorIcon::Move } -/// Window drags are the app's own. -pub fn set_window_drags(_: bool) {} - /// Scrollbars overlay the content. pub fn scrollers() -> Option { None diff --git a/crates/snowbound/src/macos.rs b/crates/snowbound/src/macos.rs index 49256adcf8793f0adba8a36afa4120fb8fa1e819..1858d5e573b063d325b4219bfbb761225be71977 100644 --- a/crates/snowbound/src/macos.rs +++ b/crates/snowbound/src/macos.rs @@ -29,7 +29,7 @@ use winit::{ window::{Window, WindowAttributes}, }; -pub use crate::aqua::{move_cursor, resize_grip, scrollers, set_window_drags}; +pub use crate::aqua::{move_cursor, resize_grip, scrollers}; static QUIT: OnceLock> = OnceLock::new(); static INPUT_CLASS: OnceLock<&'static AnyClass> = OnceLock::new(); @@ -533,7 +533,7 @@ pub fn install_text_input(window: &Window) { if crate::aqua::before_lion() { class.add_method( sel!(mouseDownCanMoveWindow), - crate::aqua::window_drags as extern "C" fn(_, _) -> _, + crate::aqua::no_window_drags as extern "C" fn(_, _) -> _, ); } class.register() diff --git a/crates/snowbound/src/main.rs b/crates/snowbound/src/main.rs index 2e59deec87ebae9d394676ce4d13591df3f83f01..885971bbd4c69485bab4e505f88227c9e733611b 100644 --- a/crates/snowbound/src/main.rs +++ b/crates/snowbound/src/main.rs @@ -3072,7 +3072,6 @@ impl State { fn input(&mut self, event: ui::Event) { if let ui::Event::PointerMoved(point) = event { self.pointer = point; - platform::set_window_drags(self.ui.box_at(point) == Some(strip())); if std::mem::take(&mut self.strip_held) { let _ = self.window.drag_window(); } diff --git a/platform/snow-leopard/rt/appkit.c b/platform/snow-leopard/rt/appkit.c index b47e88a1c18c259d0442fbfa30dd3030097ca20b..4b7275f83d59006bf1be2c80210a03fa5b6a29f3 100644 --- a/platform/snow-leopard/rt/appkit.c +++ b/platform/snow-leopard/rt/appkit.c @@ -1,7 +1,8 @@ // AppKit that winit, AccessKit and the app call and 10.6 lacks, added at load time with // the neutral answer: scale 1, no precise scrolling, no appearances, no tabbing, no full -// screen. `remote.sh selectors` lists what a binary names that 10.6 lacks. Linked from -// the archive by the appearance name winit imports, so only AppKit binaries carry it. +// screen; window drags are done here. `remote.sh selectors` lists what a binary names +// that 10.6 lacks. Linked from the archive by the appearance name winit imports, so only +// AppKit binaries carry it. // class_addMethod adds nothing where the system already has the method. #include #include @@ -53,6 +54,37 @@ static CGPoint point_from_screen(id self, SEL _cmd, CGPoint point) { return CGPointMake(point.x - frame.origin.x, point.y - frame.origin.y); } +// 10.11: the window follows the pointer until the button comes up, as a press on its +// title does. +static void window_drag(id self, SEL _cmd, id event) { + typedef CGPoint (*SendPoint)(id, SEL); + typedef CGRect (*SendRect)(id, SEL); + id event_class = (id)objc_getClass("NSEvent"); + CGPoint start = ((SendPoint)objc_msgSend)(event_class, sel_registerName("mouseLocation")); + CGRect frame = ((SendRect)objc_msgSend_stret)(self, sel_registerName("frame")); + id app = SEND(objc_getClass("NSApplication"), "sharedApplication"); + id forever = SEND(objc_getClass("NSDate"), "distantFuture"); + for (;;) { + // NSLeftMouseDraggedMask | NSLeftMouseUpMask. + id next = ((id (*)(id, SEL, unsigned long long, id, CFStringRef, BOOL))objc_msgSend)( + app, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), + 1 << 6 | 1 << 2, forever, CFSTR("NSEventTrackingRunLoopMode"), YES); + // NSLeftMouseUp. + if (((unsigned long (*)(id, SEL))objc_msgSend)(next, sel_registerName("type")) == 2) { + // The button's release stays in the queue for whoever asked for the drag. + ((void (*)(id, SEL, id, BOOL))objc_msgSend)(app, sel_registerName("postEvent:atStart:"), next, YES); + return; + } + CGPoint now = ((SendPoint)objc_msgSend)(event_class, sel_registerName("mouseLocation")); + CGRect moved = frame; + moved.origin.x += now.x - start.x; + moved.origin.y += now.y - start.y; + moved = ((CGRect (*)(id, SEL, CGRect, id))objc_msgSend_stret)( + self, sel_registerName("constrainFrameRect:toScreen:"), moved, SEND(self, "screen")); + ((void (*)(id, SEL, CGPoint))objc_msgSend)(self, sel_registerName("setFrameOrigin:"), moved.origin); + } +} + static id srgb_color(id self, SEL _cmd, CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) { CGFloat components[] = {red, green, blue, alpha}; return ((id (*)(id, SEL, id, CGFloat *, long))objc_msgSend)( @@ -106,7 +138,7 @@ __attribute__((constructor)) static void polyfill(void) { add("NSWindow", 0, "backingScaleFactor", one, "d@:"); add("NSWindow", 0, "convertRectToScreen:", rect_to_screen, RECT "@:" RECT); add("NSWindow", 0, "convertPointFromScreen:", point_from_screen, "{CGPoint=dd}@:{CGPoint=dd}"); - add("NSWindow", 0, "performWindowDragWithEvent:", ignore, "v@:@"); + add("NSWindow", 0, "performWindowDragWithEvent:", window_drag, "v@:@"); add("NSWindow", 0, "toggleFullScreen:", ignore, "v@:@"); add("NSWindow", 0, "setTabbingMode:", ignore, "v@:q"); add("NSWindow", 0, "setTabbingIdentifier:", ignore, "v@:@");