From 17dc5f407e8a080ea45b678a1a997f4b37893cc2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 Nov 2016 09:29:44 -0400 Subject: [PATCH] And implemented moves on OS X. --- README.md | 3 ++ darwin/uipriv_darwin.h | 1 + darwin/window.m | 2 +- darwin/winmoveresize.m | 82 +++++++++++++++++++++++++++++++++++------- 4 files changed, 75 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e9d6beac..322a0ece 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,9 @@ This README is being written.
## Announcements +* **2 November 2016** + * Added two new functions to replace the deleted `uiWindowPosition()` and friends: `uiAreaBeginUserWindowMove()` and `uiAreaBeginUserWindowResize()`. When used in a `uiAreaHandler.Mouse()` event handler, these let you initiate a user-driven mouse move or mouse resize of the window at any point in a uiArea. + * **31 October 2016** * @krakjoe noticed that I accidentally used thread-unsafe code in uiQueueMain() on Unix. Fixed. diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 7332b050..78f31af5 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -140,4 +140,5 @@ extern NSTextField *newLabel(NSString *str); extern NSImage *imageImage(uiImage *); // winmoveresize.m +extern void doManualMove(NSWindow *w, NSEvent *initialEvent); extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); diff --git a/darwin/window.m b/darwin/window.m index 8456c767..97c22e62 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -22,7 +22,7 @@ struct uiWindow { - (void)libui_doMove:(NSEvent *)initialEvent { - // TODO + doManualMove(self, initialEvent); } - (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 1a1cfe0d..4b307016 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -1,6 +1,76 @@ // 1 november 2016 #import "uipriv_darwin.h" +// because we are changing the window frame each time the mouse moves, the successive -[NSEvent locationInWindow]s cannot be meaningfully used together +// make sure they are all following some sort of standard to avoid this problem; the screen is the most obvious possibility since it requires only one conversion (the only one that a NSWindow provides) +static NSPoint makeIndependent(NSPoint p, NSWindow *w) +{ + NSRect r; + + r.origin = p; + // mikeash in irc.freenode.net/#macdev confirms both that any size will do and that we can safely ignore the resultant size + r.size = NSZeroSize; + return [w convertRectToScreen:r].origin; +} + +struct onMoveDragParams { + NSWindow *w; + // using the previous point causes weird issues like the mouse seeming to fall behind the window edge... so do this instead + // TODO will this make things like the menubar and dock easier too? + NSRect initialFrame; + NSPoint initialPoint; +}; + +void onMoveDrag(struct onMoveDragParams *p, NSEvent *e) +{ + NSPoint new; + NSRect frame; + CGFloat offx, offy; + + new = makeIndependent([e locationInWindow], p->w); + frame = p->initialFrame; + + offx = new.x - p->initialPoint.x; + offy = new.y - p->initialPoint.y; + frame.origin.x += offx; + frame.origin.y += offy; + + // TODO handle the menubar + // TODO wait the system does this for us already?! + + [p->w setFrameOrigin:frame.origin]; +} + +// LONGTERM FUTURE -[NSWindow performWindowDragWithEvent:] would be better but that's 10.11-only +void doManualMove(NSWindow *w, NSEvent *initialEvent) +{ + __block struct onMoveDragParams mdp; + struct nextEventArgs nea; + BOOL (^handleEvent)(NSEvent *e); + __block BOOL done; + + mdp.w = w; + mdp.initialFrame = [mdp.w frame]; + mdp.initialPoint = makeIndependent([initialEvent locationInWindow], mdp.w); + + nea.mask = NSLeftMouseDraggedMask | NSLeftMouseUpMask; + nea.duration = [NSDate distantFuture]; + nea.mode = NSEventTrackingRunLoopMode; // nextEventMatchingMask: docs suggest using this for manual mouse tracking + nea.dequeue = YES; + handleEvent = ^(NSEvent *e) { + if ([e type] == NSLeftMouseUp) { + done = YES; + return YES; // do not send + } + onMoveDrag(&mdp, e); + return YES; // do not send + }; + done = NO; + while (mainStep(&nea, handleEvent)) + if (done) + break; +} + // see http://stackoverflow.com/a/40352996/3408572 static void minMaxAutoLayoutSizes(NSWindow *w, NSSize *min, NSSize *max) { @@ -93,18 +163,6 @@ struct onResizeDragParams { NSSize max; }; -// because we are changing the window frame each time the mouse moves, the successive -[NSEvent locationInWindow]s cannot be meaningfully used together -// make sure they are all following some sort of standard to avoid this problem; the screen is the most obvious possibility since it requires only one conversion (the only one that a NSWindow provides) -static NSPoint makeIndependent(NSPoint p, NSWindow *w) -{ - NSRect r; - - r.origin = p; - // mikeash in irc.freenode.net/#macdev confirms both that any size will do and that we can safely ignore the resultant size - r.size = NSZeroSize; - return [w convertRectToScreen:r].origin; -} - static void onResizeDrag(struct onResizeDragParams *p, NSEvent *e) { NSPoint new;