Re: Snapping windows while moving
Re: Snapping windows while moving
- Subject: Re: Snapping windows while moving
- From: Ken Thomases <email@hidden>
- Date: Sat, 23 Mar 2013 02:41:21 -0500
On Mar 23, 2013, at 12:18 AM, Steve Mills wrote:
> On Mar 13, 2013, at 10:34:35, Ken Thomases <email@hidden> wrote:
>
>> The Window Server moves most windows entirely without involving the app (until the move is completed). If you want to change how windows get moved, I think you have to take over the whole process. You do [window setMovable:NO] to make the Window Server not move your windows itself. Instead, you get the relevant mouse events and you write typical mouse drag code and change the window frame manually. See the docs for -[NSWindow isMovable].
>
> I'm getting back to this now. The window being dragged is an NSPanel. I've subclassed it so I could catch mouseDown, mouseDragged, and mouseUp. I also return NO from isMovable.
Meaning you've overridden -isMovable? Or did you call -setMovable: with NO? I think you have to do the latter because that configures the Window Server's metadata about the window. It might also configure the theme frame views (the framework-private views that make up the title bar, etc.) to behave differently. So, basically, I think "movability" is a value that you have to set rather than something the frameworks and Window Server get by querying -isMovable.
> The problem is, when I click in the titlebar, neither mouseDown nor mouseUp get called, only mouseDragged. This makes it impossible to know the starting position so I can move the window. Seems like a bug. Is there any way to get mouseDown and mouseUp in the titlebar?
I just did a test and confirm what you've described. Then I rechecked the documentation of -isMovable and see that it says you have to handle the mouse down, drag, and up events inside of -sendEvent:. Of course, it confuses matters by referring to those events by their responder method names, when it should have said "Applications may choose to enable application-controlled window dragging after disabling user-initiating dragging by handling the NSLeftMouseDown/NSLeftMouseDragged/NSLeftMouseUp sequence in sendEvent: in an NSWindow subclass."
I tested that approach and it works, of course. -sendEvent: is what would dispatch those events to the theme frame views, but if you handle them directly there, instead, they won't get dispatched that way. You have to be careful to still dispatch clicks inside the title bar's various widgets, or the user won't be able to, for example, close the window by clicking on its close button.
Here's an example which works. It allows moving the window by its background (simply because it doesn't bother checking if the click is in the content view) and I haven't tested if it interferes with clicks in the actual title area (like pulling down the menu for a document), but it probably does.
// instance variables
BOOL draggingWindow;
NSPoint dragStart;
NSPoint originAtDragStart;
- (BOOL) eventHitsWindowButton:(NSEvent*)event
{
NSWindowButton buttons[] = {
NSWindowCloseButton,
NSWindowMiniaturizeButton,
NSWindowZoomButton,
NSWindowToolbarButton,
NSWindowDocumentIconButton,
#if defined(MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
NSWindowDocumentVersionsButton,
NSWindowFullScreenButton,
#endif
};
for (int i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
{
NSButton* button = [self standardWindowButton:buttons[i]];
if ([button hitTest:[[button superview] convertPoint:[event locationInWindow] fromView:nil]])
return TRUE;
}
return FALSE;
}
-(void) sendEvent:(NSEvent *)theEvent
{
NSEventType type = [theEvent type];
if (draggingWindow)
{
switch (type)
{
case NSLeftMouseDragged:
{
NSPoint dragLoc = [self convertBaseToScreen:[theEvent locationInWindow]];
NSPoint newOrigin = originAtDragStart;
newOrigin.x += dragLoc.x - dragStart.x;
newOrigin.y += dragLoc.y - dragStart.y;
[self setFrameOrigin:newOrigin];
return;
}
case NSLeftMouseUp:
draggingWindow = FALSE;
return;
}
}
else if (type == NSLeftMouseDown && ![self eventHitsWindowButton:theEvent])
{
draggingWindow = TRUE;
dragStart = [self convertBaseToScreen:[theEvent locationInWindow]];
originAtDragStart = [self frame].origin;
return;
}
[super sendEvent:theEvent];
}
Cheers,
Ken
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden