Fixed: CPPopover doesn't handle semi-transient behavior

This commit is contained in:
Alexandre Wilhelm
2014-06-29 19:17:56 -07:00
parent 6d78ed3462
commit c642d76e91
2 changed files with 94 additions and 16 deletions
+38 -7
View File
@@ -78,6 +78,7 @@ var CPPopoverDelegate_popover_willShow_ = 1 << 0,
int _behavior @accessors(getter=behavior);
_CPPopoverWindow _popoverWindow;
CPView _positioningView;
int _implementedDelegateMethods;
}
@@ -237,13 +238,17 @@ Set the behavior of the CPPopover. It can be:
[CPException raise:CPInternalInconsistencyException reason:@"contentViewController must not be nil"];
// If the popover is currently closing or opening, do nothing. That is what Cocoa does.
if ([_popoverWindow isClosing] || [self isShown])
if ([_popoverWindow isClosing] || [_popoverWindow isOpening])
return;
_positioningView = positioningView;
if (!_popoverWindow)
_popoverWindow = [[_CPPopoverWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:[self styleMaskForBehavior]];
[_popoverWindow setPlatformWindow:[[positioningView window] platformWindow]];
if (_positioningView != positioningView)
[_popoverWindow setPlatformWindow:[[positioningView window] platformWindow]];
[_popoverWindow setAppearance:_appearance];
[_popoverWindow setAnimates:_animates];
[_popoverWindow setDelegate:self];
@@ -251,18 +256,29 @@ Set the behavior of the CPPopover. It can be:
[_popoverWindow setFrame:[_popoverWindow frameRectForContentRect:[[_contentViewController view] frame]]];
[_popoverWindow setContentView:[_contentViewController view]];
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willShow_)
[_delegate popoverWillShow:self];
if (![self isShown])
[self popoverWillShow:self];
[_popoverWindow positionRelativeToRect:positioningRect ofView:positioningView preferredEdge:preferredEdge];
if (!_animates && _implementedDelegateMethods & CPPopoverDelegate_popover_didShow_)
[_delegate popoverDidShow:self];
if (![self isShown])
[self popoverDidShow:self];
}
- (unsigned)styleMaskForBehavior
{
return (_behavior == CPPopoverBehaviorTransient) ? CPClosableOnBlurWindowMask : 0;
switch (_behavior)
{
case CPPopoverBehaviorSemitransient:
case CPPopoverBehaviorTransient:
return CPClosableOnBlurWindowMask;
case CPPopoverBehaviorApplicationDefined:
return 0;
default:
return 0;
}
}
/*!
@@ -284,6 +300,7 @@ Set the behavior of the CPPopover. It can be:
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willClose_)
[_delegate popoverWillClose:self];
_positioningView = nil;
[_popoverWindow close];
// popoverDidClose will be sent from popoverWindowDidClose, since
@@ -340,6 +357,20 @@ Set the behavior of the CPPopover. It can be:
[_delegate popoverDidShow:self];
}
/*! @ignore */
- (void)popoverWillClose:(_CPPopoverWindow)aPopoverWindow
{
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willClose_)
[_delegate popoverWillClose:self];
}
/*! @ignore */
- (void)popoverWillShow:(_CPPopoverWindow)aPopoverWindow
{
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willShow_)
[_delegate popoverWillShow:self];
}
@end
@implementation CPPopover (Deprecated)
+56 -9
View File
@@ -30,6 +30,9 @@
@class CPPopover
@global CPApp
@global CPPopoverBehaviorSemitransient
@global CPPopoverBehaviorTransient
@global CPPopoverBehaviorApplicationDefined
CPClosableOnBlurWindowMask = 1 << 4;
CPPopoverAppearanceMinimal = 0;
@@ -61,6 +64,7 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
BOOL _isObservingFrame;
BOOL _shouldPerformAnimation;
CPInteger _implementedDelegateMethods;
CPWindow _targetWindow;
JSObject _orderOutTransitionFunction;
JSObject _transitionCompleteFunction;
JSObject _orderFrontTransitionFunction;
@@ -377,6 +381,8 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
if (![targetWindow isFullPlatformWindow])
[[_targetView window] addChildWindow:self ordered:CPWindowAbove];
_targetWindow = targetWindow;
if (!wasVisible)
[self _trapNextMouseDown];
}
@@ -620,6 +626,8 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
_shouldPerformAnimation = YES;
_isClosing = NO;
_isOpening = NO;
_targetWindow = nil;
if (_implementedDelegateMethods & _CPPopoverWindow_didClose_)
[_delegate popoverWindowDidClose:self];
@@ -629,6 +637,27 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
#pragma mark -
#pragma mark Private
- (BOOL)_hasOnlyTransientChild:(_CPPopoverWindow)aWindow
{
var childWindows = [aWindow childWindows];
for (var i = [childWindows count] - 1; i >= 0; i--)
{
var childWindow = childWindows[i];
if (![childWindow isKindOfClass:[self class]])
continue;
if ([[childWindow delegate] behavior] != CPPopoverBehaviorTransient)
return NO;
if (![self _hasOnlyTransientChild:childWindow])
return NO;
}
return YES;
}
- (void)_mouseWasClicked:(CPEvent)anEvent
{
/*
@@ -637,26 +666,44 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
message to any parent popovers so they have a chance to close
if necessary.
*/
if (![self isVisible])
if (![self isVisible] || !_targetWindow)
return;
var mouseWindow = [anEvent window];
// Consider clicks in child windows to be "inside". This keeps a transient popover from
// closing if e.g. the window containing the menu of a token field is clicked.
if (mouseWindow === self || [mouseWindow _hasAncestorWindow:self])
if (mouseWindow === self || [mouseWindow _hasAncestorWindow:self] || ![self _hasOnlyTransientChild:self])
{
[self _trapNextMouseDown];
}
else
{
// Send _close to the delegate so popoverWillClose is sent to the popover's delegate
if (_closeOnBlur)
[_delegate _close];
switch ([_delegate behavior])
{
case CPPopoverBehaviorSemitransient:
// Give a transient parent popover a chance to close
var parent = [self parentWindow];
// Click on the same button
// Or click on a different window (we just care about the parentWindow)
// We use targetWindow bacause parentWindow is set to nil when opening a semi-transient window in a bridgeless window
if (CGRectContainsPoint([_delegate._positioningView frame], [[_delegate._positioningView superview] convertPointFromBase:[anEvent locationInWindow]])
|| mouseWindow != _targetWindow)
{
[self _trapNextMouseDown];
return;
}
if ([parent isKindOfClass:[self class]])
[parent _mouseWasClicked:anEvent];
[_delegate close];
break;
case CPPopoverBehaviorTransient:
[_delegate close];
break;
case CPPopoverBehaviorApplicationDefined:
[self _trapNextMouseDown];
return;
}
}
}