diff --git a/AppKit/CPPopover.j b/AppKit/CPPopover.j index 7c0faa7ca..3f5ee8b20 100644 --- a/AppKit/CPPopover.j +++ b/AppKit/CPPopover.j @@ -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) diff --git a/AppKit/_CPPopoverWindow.j b/AppKit/_CPPopoverWindow.j index bc8794ac0..945f3b960 100644 --- a/AppKit/_CPPopoverWindow.j +++ b/AppKit/_CPPopoverWindow.j @@ -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; + } } }