API  1.0.0
CPPopover.j
Go to the documentation of this file.
1 /*
2  * CPPopover.j
3  * AppKit
4  *
5  * Created by Antoine Mercadal.
6  * Copyright 2011 Antoine Mercadal.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 @protocol CPPopoverDelegate <CPObject>
26 
27 @optional
28 - (BOOL)popoverShouldClose:(CPPopover)aPopover;
29 - (void)popoverWillClose:(CPPopover)aPopover;
30 - (void)popoverDidClose:(CPPopover)aPopover;
31 - (void)popoverWillShow:(CPPopover)aPopover;
32 - (void)popoverDidShow:(CPPopover)aPopover;
33 
34 @end
35 
39 
45 
46 
62 @implementation CPPopover : CPResponder
63 {
64  @outlet CPViewController _contentViewController;
65  @outlet id <CPPopoverDelegate> _delegate @accessors(getter=delegate);
66 
67  BOOL _animates;
68  int _appearance;
69  int _behavior;
70 
71  _CPPopoverWindow _popoverWindow;
72  CPView _positioningView;
73  int _implementedDelegateMethods;
74 }
75 
76 
77 #pragma mark -
78 #pragma mark Initialization
79 
86 {
87  if (self = [super init])
88  {
89  _animates = YES;
90  _appearance = CPPopoverAppearanceMinimal;
92  }
93 
94  return self;
95 }
96 
97 
98 #pragma mark -
99 #pragma mark Getters / Setters
100 
106 - (CGRect)positioningRect
107 {
108  if (![_popoverWindow isVisible])
109  return CGRectMakeZero();
110 
111  return [_popoverWindow frame];
112 }
113 
117 - (void)setPositioningRect:(CGRect)aRect
118 {
119  if (![_popoverWindow isVisible])
120  return;
121 
122  [_popoverWindow setFrame:aRect];
123 }
124 
130 - (CGSize)contentSize
131 {
132  return [[_contentViewController view] frameSize];
133 }
134 
140 - (void)setContentSize:(CGSize)aSize
141 {
142  if (!_popoverWindow)
143  [[_contentViewController view] setFrameSize:aSize];
144  else
145  [_popoverWindow updateFrameWithSize:aSize];
146 }
147 
153 - (BOOL)isShown
154 {
155  return [_popoverWindow isVisible];
156 }
157 
163 - (void)setAnimates:(BOOL)shouldAnimate
164 {
165  if (_animates == shouldAnimate)
166  return;
167 
168  _animates = shouldAnimate;
169  [_popoverWindow setAnimates:_animates];
170 }
171 
180 - (void)setBehavior:(int)aBehavior
181 {
182  if (_behavior == aBehavior)
183  return;
184 
185  _behavior = aBehavior;
186  [_popoverWindow setStyleMask:[self _styleMaskForBehavior]];
187 }
188 
189 - (void)setDelegate:(id <CPPopoverDelegate>)aDelegate
190 {
191  if (_delegate === aDelegate)
192  return;
193 
194  _delegate = aDelegate;
195  _implementedDelegateMethods = 0;
196 
197  if ([_delegate respondsToSelector:@selector(popoverWillShow:)])
198  _implementedDelegateMethods |= CPPopoverDelegate_popover_willShow_;
199 
200  if ([_delegate respondsToSelector:@selector(popoverDidShow:)])
201  _implementedDelegateMethods |= CPPopoverDelegate_popover_didShow_;
202 
203  if ([_delegate respondsToSelector:@selector(popoverShouldClose:)])
204  _implementedDelegateMethods |= CPPopoverDelegate_popover_shouldClose_;
205 
206  if ([_delegate respondsToSelector:@selector(popoverWillClose:)])
207  _implementedDelegateMethods |= CPPopoverDelegate_popover_willClose_;
208 
209  if ([_delegate respondsToSelector:@selector(popoverDidClose:)])
210  _implementedDelegateMethods |= CPPopoverDelegate_popover_didClose_;
211 }
212 
213 #pragma mark -
214 #pragma mark Positioning
215 
223 - (void)showRelativeToRect:(CGRect)positioningRect ofView:(CPView)positioningView preferredEdge:(CPRectEdge)preferredEdge
224 {
225  if (!positioningView)
226  [CPException raise:CPInvalidArgumentException reason:"positionView must not be nil"];
227 
228  if (!_contentViewController)
229  [CPException raise:CPInternalInconsistencyException reason:@"contentViewController must not be nil"];
230 
231  // If the popover is currently closing or opening, do nothing. That is what Cocoa does.
232  if ([_popoverWindow isClosing] || [_popoverWindow isOpening])
233  return;
234 
235  _positioningView = positioningView;
236 
237  if (!_popoverWindow)
238  _popoverWindow = [[_CPPopoverWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:[self _styleMaskForBehavior]];
239 
240  [_popoverWindow setPlatformWindow:[[positioningView window] platformWindow]];
241  [_popoverWindow setAppearance:_appearance];
242  [_popoverWindow setAnimates:_animates];
243  [_popoverWindow setDelegate:self];
244  [_popoverWindow setMovableByWindowBackground:NO];
245  [_popoverWindow setFrame:[_popoverWindow frameRectForContentRect:[[_contentViewController view] frame]]];
246  [_popoverWindow setContentView:[_contentViewController view]];
247 
248  if (![self isShown])
249  [self _popoverWillShow];
250 
251  [_popoverWindow positionRelativeToRect:positioningRect ofView:positioningView preferredEdge:preferredEdge];
252 
253  if (![self isShown])
254  [self _popoverWindowDidShow];
255 }
256 
257 - (unsigned)_styleMaskForBehavior
258 {
259  if (_behavior == CPPopoverBehaviorApplicationDefined)
260  return 0;
261 
262  return CPClosableOnBlurWindowMask
263 }
264 
268 - (void)close
269 {
270  [self _close];
271 }
272 
276 - (void)_close
277 {
278  if ([_popoverWindow isClosing] || ![self isShown])
279  return;
280 
281  [self _popoverWillClose];
282 
283  _positioningView = nil;
284  [_popoverWindow close];
285 
286  // popoverDidClose will be sent from popoverWindowDidClose, since
287  // the popover window will close asynchronously when animating.
288 }
289 
290 
291 #pragma mark -
292 #pragma mark Action
293 
299 - (IBAction)performClose:(id)sender
300 {
301  if ([_popoverWindow isClosing])
302  return;
303 
304  if (![self _popoverShouldClose])
305  return;
306 
307  [self _close];
308 }
309 
310 
311 #pragma mark -
312 #pragma mark Delegates
313 
315 - (BOOL)_popoverWindowShouldClose
316 {
317  [self performClose:self];
318 
319  // We return NO, because we want the CPPopover to determine
320  // if the popover window can be closed and to give us a chance
321  // to send delegate messages.
322  return NO;
323 }
324 
326 - (void)_popoverWindowDidClose
327 {
328  if (_implementedDelegateMethods & CPPopoverDelegate_popover_didClose_)
329  [_delegate popoverDidClose:self];
330 }
331 
333 - (void)_popoverWindowDidShow
334 {
335  if (_implementedDelegateMethods & CPPopoverDelegate_popover_didShow_)
336  [_delegate popoverDidShow:self];
337 }
338 
340 - (BOOL)_popoverShouldClose
341 {
342  if (_implementedDelegateMethods & CPPopoverDelegate_popover_shouldClose_)
343  return [_delegate popoverShouldClose:self];
344 
345  return YES;
346 }
347 
349 - (void)_popoverWillClose
350 {
351  if (_implementedDelegateMethods & CPPopoverDelegate_popover_willClose_)
352  [_delegate popoverWillClose:self];
353 }
354 
356 - (void)_popoverWillShow
357 {
358  if (_implementedDelegateMethods & CPPopoverDelegate_popover_willShow_)
359  [_delegate popoverWillShow:self];
360 }
361 
362 @end
363 
364 @implementation CPPopover (Deprecated)
365 
366 - (void)setBehaviour:(int)aBehavior
367 {
368  _CPReportLenientDeprecation(self, _cmd, @selector(setBehavior:));
369 
370  [self setBehavior:aBehavior];
371 }
372 
373 @end
374 
375 var CPPopoverNeedsNewPopoverWindowKey = @"CPPopoverNeedsNewPopoverWindowKey",
376  CPPopoverAppearanceKey = @"CPPopoverAppearanceKey",
377  CPPopoverAnimatesKey = @"CPPopoverAnimatesKey",
378  CPPopoverContentViewControllerKey = @"CPPopoverContentViewControllerKey",
379  CPPopoverDelegateKey = @"CPPopoverDelegateKey",
380  CPPopoverBehaviorKey = @"CPPopoverBehaviorKey";
381 
382 @implementation CPPopover (CPCoding)
383 
384 - (id)initWithCoder:(CPCoder)aCoder
385 {
386  self = [super initWithCoder:aCoder];
387 
388  if (self)
389  {
390  _appearance = [aCoder decodeIntForKey:CPPopoverAppearanceKey];
391  _animates = [aCoder decodeBoolForKey:CPPopoverAnimatesKey];
392  _contentViewController = [aCoder decodeObjectForKey:CPPopoverContentViewControllerKey];
393  [self setDelegate:[aCoder decodeObjectForKey:CPPopoverDelegateKey]];
394  [self setBehavior:[aCoder decodeIntForKey:CPPopoverBehaviorKey]];
395  }
396  return self;
397 }
398 
399 - (void)encodeWithCoder:(CPCoder)aCoder
400 {
401  [super encodeWithCoder:aCoder];
402 
403  [aCoder encodeInt:_appearance forKey:CPPopoverAppearanceKey];
404  [aCoder encodeBool:_animates forKey:CPPopoverAnimatesKey];
405  [aCoder encodeObject:_contentViewController forKey:CPPopoverContentViewControllerKey];
406  [aCoder encodeObject:_delegate forKey:CPPopoverDelegateKey];
407  [aCoder encodeInt:_behavior forKey:CPPopoverBehaviorKey];
408 }
409 
410 @end
411 
413 
417 - (CPViewController)contentViewController
418 {
419  return _contentViewController;
420 }
421 
425 - (void)setContentViewController:(CPViewController)aValue
426 {
427  _contentViewController = aValue;
428 }
429 
433 - (BOOL)animates
434 {
435  return _animates;
436 }
437 
441 - (int)appearance
442 {
443  return _appearance;
444 }
445 
449 - (void)setAppearance:(int)aValue
450 {
451  _appearance = aValue;
452 }
453 
457 - (int)behavior
458 {
459  return _behavior;
460 }
461 
462 @end
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
id initWithCoder:(CPCoder aCoder)
Definition: CPResponder.j:373
var CPPopoverBehaviorKey
Definition: CPPopover.j:380
id init()
Definition: CALayer.j:126
var CPPopoverContentViewControllerKey
Definition: CPPopover.j:378
var CPPopoverAppearanceKey
Definition: CPPopover.j:376
id delegate()
Definition: CALayer.j:965
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
var CPPopoverDelegate_popover_didShow_
Definition: CPPopover.j:41
CPPopoverBehaviorTransient
Definition: CPPopover.j:37
var CPPopoverAnimatesKey
Definition: CPPopover.j:377
void encodeWithCoder:(CPCoder aCoder)
Definition: CPResponder.j:390
CPWindow window()
Definition: CPView.j:527
var CPPopoverDelegateKey
Definition: CPPopover.j:379
void setDelegate:(id< CPPopoverDelegate > aDelegate)
Definition: CPPopover.j:189
outlet id< CPPopoverDelegate > _delegate accessors(getter=delegate)
var CPPopoverDelegate_popover_willClose_
Definition: CPPopover.j:43
var CPPopoverDelegate_popover_shouldClose_
Definition: CPPopover.j:42
var CPPopoverNeedsNewPopoverWindowKey
Definition: CPPopover.j:375
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
void setBehavior:(int aBehavior)
Definition: CPPopover.j:180
CPPopoverBehaviorApplicationDefined
Definition: CPPopover.j:36
var CPPopoverDelegate_popover_willShow_
Definition: CPPopover.j:40
var CPPopoverDelegate_popover_didClose_
Definition: CPPopover.j:44
CPPlatformWindow platformWindow()
Definition: CPWindow.j:389
CPPopoverBehaviorSemitransient
Definition: CPPopover.j:38
Definition: CPView.j:137