mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-11-10 00:48:45 +00:00
AppKit: Implement window minimize with restore from Windows menu
Order minimized window off screen and keep it in Windows menu for restore. Enable minimize button for miniaturizable windows. * CPWindow.j: - Order window out with attached sheet and visible child windows on miniaturize:; keep first responder and child windows for restore; ignore miniaturize: on minimized window. - Restore window, first responder and child windows when window orders front; post CPWindowDidDeminiaturizeNotification after restore. - Restore minimized window through makeKeyAndOrderFront: in deminiaturize:. - Remove calls to platform window's miniaturize: and deminiaturize:. - Minimize through performMiniaturize: only when window can minimize; enable performMiniaturize: and miniaturize: menu items under same condition. * _CPStandardWindowView.j: Enable minimize button for miniaturizable windows and target window's miniaturize:. * CPWindowTest.j: Add tests for restore, notification order, first responder restore, performMiniaturize: on non-miniaturizable window, minimize button action and menu validation. * CPApplicationWindowsMenuTest.j: Add tests for minimized window listed in mixed state and restored from Windows menu. * DropInCompatibilityTest.j: Replace test for minimize button never enabled with test for minimize button enabled only for miniaturizable windows.
This commit is contained in:
1 parent
af4d9dd9b9
commit
af2edf6c97
5 files changed
+237
-33
No files matched your search
+90
-26
@@ -187,6 +187,8 @@ var CPWindowActionMessageKeys = [
|
||||
BOOL _isVisible;
|
||||
BOOL _hasBeenOrderedIn @accessors;
|
||||
BOOL _isMiniaturized;
|
||||
CPResponder _miniaturizedFirstResponder;
|
||||
CPArray _miniaturizedChildWindows;
|
||||
CGRect _userStateFrame;
|
||||
BOOL _isExcludedFromWindowsMenu;
|
||||
BOOL _isAnimating @accessors(setter=_setAnimating:);
|
||||
@@ -997,6 +999,14 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
- (void)_orderFront
|
||||
{
|
||||
/*
|
||||
Ordering a minimized window front restores it, so choosing it in the Windows menu,
|
||||
which sends makeKeyAndOrderFront:, brings it back. The minimized state stayed set, so
|
||||
a window ordered front still counted as minimized.
|
||||
*/
|
||||
var wasMiniaturized = _isMiniaturized;
|
||||
|
||||
_isMiniaturized = NO;
|
||||
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
@@ -1018,6 +1028,9 @@ CPTexturedBackgroundWindowMask
|
||||
[self _setFrame:_frame display:YES animate:NO constrainWidth:YES constrainHeight:YES];
|
||||
}
|
||||
|
||||
if (wasMiniaturized)
|
||||
[self _restoreMiniaturizedState];
|
||||
|
||||
if (!CPApp._keyWindow)
|
||||
[self makeKeyWindow];
|
||||
|
||||
@@ -1031,6 +1044,9 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
if ([self attachedSheet])
|
||||
[_platformWindow order:CPWindowAbove window:[self attachedSheet] relativeTo:nil];
|
||||
|
||||
if (wasMiniaturized)
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidDeminiaturizeNotification object:self];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2495,49 +2511,97 @@ CPTexturedBackgroundWindowMask
|
||||
// Minimizing Windows
|
||||
|
||||
/*!
|
||||
Simulates the user clicking the minimize button. A page cannot minimize its browser
|
||||
window, so the minimize button is always disabled and this method does nothing.
|
||||
Simulates the user clicking the minimize button. Minimizes the window when its minimize
|
||||
button is enabled, and does nothing otherwise. The minimize button is enabled when the
|
||||
window is miniaturizable and is neither a sheet nor a full platform window.
|
||||
@param aSender the object making this request
|
||||
*/
|
||||
- (void)performMiniaturize:(id)aSender
|
||||
{
|
||||
/*
|
||||
It called -miniaturize:, whose platform window does nothing in the browser, so the
|
||||
window stayed on screen but counted as minimized.
|
||||
*/
|
||||
if ([self _canMiniaturize])
|
||||
[self miniaturize:aSender];
|
||||
}
|
||||
|
||||
/*!
|
||||
Minimizes the window. Posts a \c CPWindowWillMiniaturizeNotification to the
|
||||
notification center before minimizing the window.
|
||||
Minimizes the window. The window leaves the screen with its attached sheet and its
|
||||
visible child windows, and stays in the Windows menu, from which the user restores it.
|
||||
Posts \c CPWindowWillMiniaturizeNotification before the window leaves the screen and
|
||||
\c CPWindowDidMiniaturizeNotification after.
|
||||
@param sender the object making this request
|
||||
*/
|
||||
- (void)miniaturize:(id)sender
|
||||
{
|
||||
if (_isMiniaturized)
|
||||
return;
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillMiniaturizeNotification object:self];
|
||||
|
||||
[[self platformWindow] miniaturize:sender];
|
||||
/*
|
||||
The window leaves the screen and keeps its first responder and visible child windows
|
||||
for the restore. It called the platform window's miniaturize:, which does nothing in
|
||||
the browser, so the window stayed on screen but counted as minimized.
|
||||
*/
|
||||
_isMiniaturized = YES;
|
||||
_miniaturizedFirstResponder = _firstResponder;
|
||||
_miniaturizedChildWindows = _childWindows.filter(function(aChildWindow)
|
||||
{
|
||||
return [aChildWindow isVisible];
|
||||
});
|
||||
|
||||
[self _updateMainAndKeyWindows];
|
||||
var sheet = [self attachedSheet];
|
||||
|
||||
if (sheet)
|
||||
{
|
||||
[_platformWindow order:CPWindowOut window:sheet relativeTo:nil];
|
||||
[sheet _updateMainAndKeyWindows];
|
||||
}
|
||||
|
||||
[self _orderOutRecursively:YES];
|
||||
[self _updateWindowsMenuItem];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidMiniaturizeNotification object:self];
|
||||
|
||||
_isMiniaturized = YES;
|
||||
|
||||
[self _updateWindowsMenuItem];
|
||||
}
|
||||
|
||||
/*!
|
||||
Restores a minimized window to it's original size.
|
||||
Restores a minimized window: orders it front with its attached sheet and the child
|
||||
windows it took off the screen, and makes it the key and main window. Ordering a
|
||||
minimized window front restores it in the same way, which is how the Windows menu
|
||||
restores it.
|
||||
@param sender the object making this request
|
||||
*/
|
||||
- (void)deminiaturize:(id)sender
|
||||
{
|
||||
[[self platformWindow] deminiaturize:sender];
|
||||
if (_isMiniaturized)
|
||||
[self makeKeyAndOrderFront:sender];
|
||||
}
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPWindowDidDeminiaturizeNotification object:self];
|
||||
/*
|
||||
The window can minimize when it shows an enabled minimize button.
|
||||
*/
|
||||
- (BOOL)_canMiniaturize
|
||||
{
|
||||
return !!(_styleMask & CPMiniaturizableWindowMask) && !_isSheet && !_isFullPlatformWindow;
|
||||
}
|
||||
|
||||
_isMiniaturized = NO;
|
||||
/*
|
||||
Restores the first responder and the child windows that the window had when it was
|
||||
minimized.
|
||||
*/
|
||||
- (void)_restoreMiniaturizedState
|
||||
{
|
||||
if (_miniaturizedFirstResponder && _miniaturizedFirstResponder !== self)
|
||||
[self makeFirstResponder:_miniaturizedFirstResponder];
|
||||
|
||||
[self _updateWindowsMenuItem];
|
||||
for (var i = 0, count = [_miniaturizedChildWindows count]; i < count; i++)
|
||||
{
|
||||
var childWindow = _miniaturizedChildWindows[i];
|
||||
|
||||
if ([childWindow parentWindow] === self)
|
||||
[childWindow orderWindow:[childWindow childOrdering] relativeTo:_windowNumber];
|
||||
}
|
||||
|
||||
_miniaturizedFirstResponder = nil;
|
||||
_miniaturizedChildWindows = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -2951,9 +3015,9 @@ CPTexturedBackgroundWindowMask
|
||||
For \c -toggleToolbarShown:, the item's title becomes "Hide Toolbar" or "Show Toolbar"
|
||||
to reflect the toolbar's visibility, and the item is enabled only when the window has
|
||||
a toolbar. \c -undo: and \c -redo: are enabled only when the window's undo manager has a
|
||||
change to undo or redo. \c -performMiniaturize: is disabled, because a page cannot
|
||||
minimize its browser window. \c -performZoom: and \c -zoom: are enabled only when the
|
||||
window's zoom button is enabled. Every other item is enabled.
|
||||
change to undo or redo. \c -performMiniaturize: and \c -miniaturize: are enabled only
|
||||
when the window's minimize button is enabled, and \c -performZoom: and \c -zoom: only
|
||||
when its zoom button is enabled. Every other item is enabled.
|
||||
@param anItem the item to validate
|
||||
@return \c YES if the item should be enabled
|
||||
*/
|
||||
@@ -2968,11 +3032,11 @@ CPTexturedBackgroundWindowMask
|
||||
return [[self undoManager] canRedo];
|
||||
|
||||
/*
|
||||
The Minimize and Zoom items agree with the window's buttons. Minimize was enabled,
|
||||
and left the window on screen but counted as minimized.
|
||||
The Minimize and Zoom items agree with the window's buttons. Minimize was enabled
|
||||
for every window, including a window that cannot minimize.
|
||||
*/
|
||||
if (action === @selector(performMiniaturize:))
|
||||
return NO;
|
||||
if (action === @selector(performMiniaturize:) || action === @selector(miniaturize:))
|
||||
return [self _canMiniaturize];
|
||||
|
||||
if (action === @selector(performZoom:) || action === @selector(zoom:))
|
||||
return [self _canZoom];
|
||||
|
||||
@@ -209,16 +209,16 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
/*
|
||||
A standard titled window shows close, minimize and zoom, and enables each only
|
||||
when it is available, as Apple's guidelines ask. Only the available buttons
|
||||
were shown, so an unavailable control was hidden instead of disabled. Minimize
|
||||
is never available, because a page cannot minimize its browser window. Zoom
|
||||
is available when the window is resizable, as in macOS.
|
||||
were shown, so an unavailable control was hidden instead of disabled. As in
|
||||
macOS, minimize is available when the window is miniaturizable, and zoom when
|
||||
it is resizable.
|
||||
*/
|
||||
_closeButton = [self _windowButton];
|
||||
_minimizeButton = [self _windowButton];
|
||||
_zoomButton = [self _windowButton];
|
||||
|
||||
[_closeButton setEnabled:!!(_styleMask & CPClosableWindowMask)];
|
||||
[_minimizeButton setEnabled:NO];
|
||||
[_minimizeButton setEnabled:!!(_styleMask & CPMiniaturizableWindowMask)];
|
||||
[_zoomButton setEnabled:!!(_styleMask & CPResizableWindowMask)];
|
||||
|
||||
[self _updateWindowButtons];
|
||||
@@ -250,7 +250,7 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
[_closeButton setAction:@selector(performClose:)];
|
||||
|
||||
[_minimizeButton setTarget:[self window]];
|
||||
[_minimizeButton setAction:@selector(performMiniaturize:)];
|
||||
[_minimizeButton setAction:@selector(miniaturize:)];
|
||||
|
||||
[_zoomButton setTarget:[self window]];
|
||||
[_zoomButton setAction:@selector(zoom:)];
|
||||
|
||||
@@ -157,6 +157,34 @@
|
||||
[self assert:@"Report" equals:[[windowsMenu itemAtIndex:2] title]];
|
||||
}
|
||||
|
||||
- (void)testMinimizedWindowStaysListedInTheMixedState
|
||||
{
|
||||
var aWindow = [self visibleWindowWithTitle:@"Report"];
|
||||
|
||||
[aWindow makeMainWindow];
|
||||
[aWindow miniaturize:self];
|
||||
|
||||
var item = [windowsMenu itemAtIndex:2];
|
||||
|
||||
[self assert:aWindow equals:[item target]];
|
||||
[self assert:CPMixedState equals:[item state]];
|
||||
}
|
||||
|
||||
- (void)testChoosingAMinimizedWindowRestoresIt
|
||||
{
|
||||
var aWindow = [self visibleWindowWithTitle:@"Report"];
|
||||
|
||||
[aWindow makeMainWindow];
|
||||
[aWindow miniaturize:self];
|
||||
|
||||
var item = [windowsMenu itemAtIndex:2];
|
||||
|
||||
[[item target] performSelector:[item action] withObject:item];
|
||||
|
||||
[self assertFalse:[aWindow isMiniaturized]];
|
||||
[self assert:CPOnState equals:[item state]];
|
||||
}
|
||||
|
||||
- (void)testSetWindowsMenuMovesWindowItems
|
||||
{
|
||||
var aWindow = [self visibleWindowWithTitle:@"Report"],
|
||||
|
||||
@@ -309,6 +309,117 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
There is no DOM in the test runner, so ordering a window in or out does not change its
|
||||
visibility. The minimize tests start from a window marked visible.
|
||||
*/
|
||||
- (CPWindow)visibleWindowWithStyleMask:(unsigned)aStyleMask
|
||||
{
|
||||
var window = [[CPWindow alloc] initWithContentRect:CGRectMake(100.0, 100.0, 400.0, 300.0) styleMask:aStyleMask];
|
||||
|
||||
window._isVisible = YES;
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
- (void)testDeminiaturizeRestoresAMinimizedWindow
|
||||
{
|
||||
var window = [self visibleWindowWithStyleMask:CPTitledWindowMask | CPMiniaturizableWindowMask];
|
||||
|
||||
[window miniaturize:self];
|
||||
[self assertTrue:[window isMiniaturized]];
|
||||
|
||||
[window deminiaturize:self];
|
||||
[self assertFalse:[window isMiniaturized]];
|
||||
}
|
||||
|
||||
- (void)testMinimizingAndRestoringPostNotificationsInOrder
|
||||
{
|
||||
var window = [self visibleWindowWithStyleMask:CPTitledWindowMask | CPMiniaturizableWindowMask],
|
||||
center = [CPNotificationCenter defaultCenter],
|
||||
names = [],
|
||||
record = function(aNotification) { names.push([aNotification name]); },
|
||||
observers = [CPWindowWillMiniaturizeNotification, CPWindowDidMiniaturizeNotification, CPWindowDidDeminiaturizeNotification].map(function(aName)
|
||||
{
|
||||
return [center addObserverForName:aName object:window queue:nil usingBlock:record];
|
||||
});
|
||||
|
||||
[window miniaturize:self];
|
||||
[window miniaturize:self];
|
||||
[window deminiaturize:self];
|
||||
|
||||
observers.forEach(function(anObserver) { [center removeObserver:anObserver]; });
|
||||
|
||||
[self assert:[CPWindowWillMiniaturizeNotification, CPWindowDidMiniaturizeNotification, CPWindowDidDeminiaturizeNotification] equals:names];
|
||||
}
|
||||
|
||||
- (void)testRestoringAMinimizedWindowRestoresItsFirstResponder
|
||||
{
|
||||
var window = [self visibleWindowWithStyleMask:CPTitledWindowMask | CPMiniaturizableWindowMask],
|
||||
responder = [[CPWindowTestResponderView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 100.0)];
|
||||
|
||||
/*
|
||||
The window is key before it is minimized, so that restoring it does not make it key
|
||||
for the first time, which would choose a first responder of its own.
|
||||
*/
|
||||
[[window contentView] addSubview:responder];
|
||||
[window makeFirstResponder:responder];
|
||||
[window makeKeyWindow];
|
||||
|
||||
[window miniaturize:self];
|
||||
[window deminiaturize:self];
|
||||
|
||||
[self assert:responder equals:[window firstResponder]];
|
||||
}
|
||||
|
||||
- (void)testPerformMiniaturizeMinimizesOnlyAMiniaturizableWindow
|
||||
{
|
||||
var miniaturizableWindow = [self visibleWindowWithStyleMask:CPTitledWindowMask | CPMiniaturizableWindowMask],
|
||||
fixedWindow = [self visibleWindowWithStyleMask:CPTitledWindowMask];
|
||||
|
||||
[miniaturizableWindow performMiniaturize:self];
|
||||
[fixedWindow performMiniaturize:self];
|
||||
|
||||
[self assertTrue:[miniaturizableWindow isMiniaturized]];
|
||||
[self assertFalse:[fixedWindow isMiniaturized]];
|
||||
}
|
||||
|
||||
- (void)testMinimizeButtonMinimizesTheWindow
|
||||
{
|
||||
var window = [self visibleWindowWithStyleMask:CPTitledWindowMask | CPMiniaturizableWindowMask],
|
||||
button = [window _windowView]._minimizeButton;
|
||||
|
||||
[button sendAction:[button action] to:[button target]];
|
||||
|
||||
[self assertTrue:[window isMiniaturized]];
|
||||
}
|
||||
|
||||
- (void)testMinimizeItemsAreEnabledOnlyForAMiniaturizableWindow
|
||||
{
|
||||
var miniaturizableWindow = [self visibleWindowWithStyleMask:CPTitledWindowMask | CPMiniaturizableWindowMask],
|
||||
fixedWindow = [self visibleWindowWithStyleMask:CPTitledWindowMask];
|
||||
|
||||
[@selector(performMiniaturize:), @selector(miniaturize:)].forEach(function(anAction)
|
||||
{
|
||||
var item = [[CPMenuItem alloc] initWithTitle:@"Minimize" action:anAction keyEquivalent:@""];
|
||||
|
||||
[self assertTrue:[miniaturizableWindow validateUserInterfaceItem:item] message:CPStringFromSelector(anAction) + " in a miniaturizable window"];
|
||||
[self assertFalse:[fixedWindow validateUserInterfaceItem:item] message:CPStringFromSelector(anAction) + " in a window that cannot minimize"];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
A view that accepts first responder status.
|
||||
*/
|
||||
@implementation CPWindowTestResponderView : CPView
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
|
||||
@@ -74,9 +74,10 @@ function DropInWindowButton(aStyleMask, aName)
|
||||
[self assertFalse:[DropInWindowButton(CPTitledWindowMask, @"_closeButton") isEnabled]];
|
||||
}
|
||||
|
||||
- (void)testMinimizeButtonIsNeverEnabled
|
||||
- (void)testMinimizeButtonIsEnabledOnlyForAMiniaturizableWindow
|
||||
{
|
||||
[self assertFalse:[DropInWindowButton(CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask, @"_minimizeButton") isEnabled]];
|
||||
[self assertTrue:[DropInWindowButton(CPTitledWindowMask | CPMiniaturizableWindowMask, @"_minimizeButton") isEnabled]];
|
||||
[self assertFalse:[DropInWindowButton(CPTitledWindowMask | CPClosableWindowMask, @"_minimizeButton") isEnabled]];
|
||||
}
|
||||
|
||||
- (void)testZoomButtonIsEnabledOnlyForAResizableWindow
|
||||
|
||||
Reference in new issue
Block a user