winemac: Restore a maximized window if a user tries to move it by dragging its title bar.

OS X doesn't have the same concept of maximized windows as Windows does.
There's no mode that prevents a normally-movable window from being moved.  If
a window is "zoomed", it mostly fills the screen but the user can still move
or resize it, at which point it ceases to be in the zoomed state.  So, users
are confused and frustrated when they can't move a window that's maximized.

To get similar behavior while still respecting Win32 semantics, we detect when
the user tries to move a maximized window.  When they start, a request is
submitted to the app to restore the window.  Unless and until the window is
restored, we don't actually allow the window to move.

The user expects to move the window from its current (maximized) position.  It
should not jump to its normal position upon being restored.  So, we set the
window's normal position to its current position before restoring it.
This commit is contained in:
Ken Thomases 2015-03-23 18:58:11 -05:00 committed by Alexandre Julliard
parent 8d581d0e48
commit 792b47ad3b
4 changed files with 135 additions and 9 deletions

View File

@ -119,6 +119,8 @@ - (BOOL) waitUntilQueryDone:(int*)done timeout:(NSDate*)timeout processEvents:(B
- (void) keyboardSelectionDidChange;
- (void) noteKey:(uint16_t)keyCode pressed:(BOOL)pressed;
- (void) window:(WineWindow*)window isBeingDragged:(BOOL)dragged;
- (void) flipRect:(NSRect*)rect;
- (WineWindow*) frontWineWindow;

View File

@ -1436,6 +1436,15 @@ - (void) noteKey:(uint16_t)keyCode pressed:(BOOL)pressed
pressedKeyCodes[index] &= ~mask;
}
- (void) window:(WineWindow*)window isBeingDragged:(BOOL)dragged
{
if (dragged)
[windowsBeingDragged addObject:window];
else
[windowsBeingDragged removeObject:window];
[self updateCursorClippingState];
}
- (void) handleMouseMove:(NSEvent*)anEvent
{
WineWindow* targetWindow;

View File

@ -69,6 +69,10 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
NSRect nonFullscreenFrame;
NSTimeInterval enteredFullScreenTime;
int draggingPhase;
NSPoint dragStartPosition;
NSPoint dragWindowStartPosition;
BOOL ignore_windowDeminiaturize;
BOOL ignore_windowResize;
BOOL fakingClose;

View File

@ -653,6 +653,16 @@ - (BOOL) preventResizing
return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
}
- (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
{
if (allow_immovable_windows && (disabled || inMaximized))
return NO;
else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
return NO;
else
return YES;
}
- (void) adjustFeaturesForState
{
NSUInteger style = [self styleMask];
@ -682,14 +692,7 @@ - (void) adjustFeaturesForState
}
if (allow_immovable_windows || cursor_clipping_locks_windows)
{
if (allow_immovable_windows && (disabled || maximized))
[self setMovable:NO];
else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
[self setMovable:NO];
else
[self setMovable:YES];
}
[self setMovable:[self allowsMovingWithMaximized:maximized]];
}
- (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
@ -1543,6 +1546,22 @@ - (void) updateForCursorClipping
[self adjustFeaturesForState];
}
- (void) endWindowDragging
{
if (draggingPhase)
{
if (draggingPhase == 3)
{
macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
[queue postEvent:event];
macdrv_release_event(event);
}
draggingPhase = 0;
[[WineApplicationController sharedController] window:self isBeingDragged:NO];
}
}
/*
* ---------- NSWindow method overrides ----------
@ -1603,14 +1622,104 @@ - (void) makeKeyAndOrderFront:(id)sender
- (void) sendEvent:(NSEvent*)event
{
NSEventType type = event.type;
/* NSWindow consumes certain key-down events as part of Cocoa's keyboard
interface control. For example, Control-Tab switches focus among
views. We want to bypass that feature, so directly route key-down
events to -keyDown:. */
if ([event type] == NSKeyDown)
if (type == NSKeyDown)
[[self firstResponder] keyDown:event];
else
{
if (!draggingPhase && maximized && ![self isMovable] &&
![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
{
NSRect titleBar = self.frame;
NSRect contentRect = [self contentRectForFrameRect:titleBar];
titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
titleBar.origin.y = NSMaxY(contentRect);
dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
if (NSMouseInRect(dragStartPosition, titleBar, NO))
{
static const NSWindowButton buttons[] = {
NSWindowCloseButton,
NSWindowMiniaturizeButton,
NSWindowZoomButton,
NSWindowFullScreenButton,
};
BOOL hitButton = NO;
int i;
for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
{
NSButton* button;
if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
continue;
button = [self standardWindowButton:buttons[i]];
if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
{
hitButton = YES;
break;
}
}
if (!hitButton)
{
draggingPhase = 1;
dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
[[WineApplicationController sharedController] window:self isBeingDragged:YES];
}
}
}
else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
{
if ([self isMovable])
{
NSPoint point = [self convertBaseToScreen:event.locationInWindow];
NSPoint newTopLeft = dragWindowStartPosition;
newTopLeft.x += point.x - dragStartPosition.x;
newTopLeft.y += point.y - dragStartPosition.y;
if (draggingPhase == 2)
{
macdrv_event* event = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
[queue postEvent:event];
macdrv_release_event(event);
draggingPhase = 3;
}
[self setFrameTopLeftPoint:newTopLeft];
}
else if (draggingPhase == 1 && type == NSLeftMouseDragged)
{
macdrv_event* event;
NSRect frame = [self contentRectForFrameRect:self.frame];
[[WineApplicationController sharedController] flipRect:&frame];
event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
event->window_restore_requested.keep_frame = TRUE;
event->window_restore_requested.frame = NSRectToCGRect(frame);
[queue postEvent:event];
macdrv_release_event(event);
draggingPhase = 2;
}
if (type == NSLeftMouseUp)
[self endWindowDragging];
}
[super sendEvent:event];
}
}
- (void) miniaturize:(id)sender
@ -2011,6 +2120,8 @@ - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
- (void) windowWillStartLiveResize:(NSNotification *)notification
{
[self endWindowDragging];
if (maximized)
{
macdrv_event* event;