mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-05 02:13:40 +00:00
Fixed: CPPopover doesn't handle semi-transient behavior
This commit is contained in:
+38
-7
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user