API  0.9.10
CPSplitView.j
Go to the documentation of this file.
1 /*
2  * CPSplitView.j
3  * AppKit
4  *
5  * Created by Thomas Robinson.
6  * Copyright 2008, 280 North, Inc.
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 #include "../Foundation/Foundation.h"
24 
25 
26 @global CPApp
27 
28 @protocol CPSplitViewDelegate <CPObject>
29 
30 @optional
31 - (BOOL)splitView:(CPSplitView)splitView canCollapseSubview:(CPView)subview;
32 - (BOOL)splitView:(CPSplitView)splitView shouldAdjustSizeOfSubview:(CPView)subview;
33 - (BOOL)splitView:(CPSplitView)splitView shouldCollapseSubview:(CPView)subview forDoubleClickOnDividerAtIndex:(CPInteger)dividerIndex;
34 - (CGRect)splitView:(CPSplitView)splitView additionalEffectiveRectOfDividerAtIndex:(CPInteger)dividerIndex;
35 - (CGRect)splitView:(CPSplitView)splitView effectiveRect:(CGRect)proposedEffectiveRect forDrawnRect:(CGRect)drawnRect ofDividerAtIndex:(CPInteger)dividerIndex;
36 - (float)splitView:(CPSplitView)splitView constrainMaxCoordinate:(float)proposedMax ofSubviewAt:(CPInteger)dividerIndex;
37 - (float)splitView:(CPSplitView)splitView constrainMinCoordinate:(float)proposedMin ofSubviewAt:(CPInteger)dividerIndex;
38 - (float)splitView:(CPSplitView)splitView constrainSplitPosition:(float)proposedPosition ofSubviewAt:(CPInteger)dividerIndex;
39 - (void)splitView:(CPSplitView)splitView resizeSubviewsWithOldSize:(CGSize)oldSize;
40 - (void)splitViewDidResizeSubviews:(CPNotification)aNotification;
41 - (void)splitViewWillResizeSubviews:(CPNotification)aNotification;
42 
43 @end
44 
56 
57 #define SPLIT_VIEW_MAYBE_POST_WILL_RESIZE() \
58  if ((_suppressResizeNotificationsMask & DidPostWillResizeNotification) === 0) \
59  { \
60  [self _postNotificationWillResize]; \
61  _suppressResizeNotificationsMask |= DidPostWillResizeNotification; \
62  }
63 
64 #define SPLIT_VIEW_MAYBE_POST_DID_RESIZE() \
65  if ((_suppressResizeNotificationsMask & ShouldSuppressResizeNotifications) !== 0) \
66  _suppressResizeNotificationsMask |= DidSuppressResizeNotification; \
67  else \
68  [self _postNotificationDidResize];
69 
70 #define SPLIT_VIEW_DID_SUPPRESS_RESIZE_NOTIFICATION() \
71  ((_suppressResizeNotificationsMask & DidSuppressResizeNotification) !== 0)
72 
73 #define SPLIT_VIEW_SUPPRESS_RESIZE_NOTIFICATIONS(shouldSuppress) \
74  if (shouldSuppress) \
75  _suppressResizeNotificationsMask |= ShouldSuppressResizeNotifications; \
76  else \
77  _suppressResizeNotificationsMask = 0;
78 
79 CPSplitViewDidResizeSubviewsNotification = @"CPSplitViewDidResizeSubviewsNotification";
80 CPSplitViewWillResizeSubviewsNotification = @"CPSplitViewWillResizeSubviewsNotification";
81 
85 
97 @implementation CPSplitView : CPView
98 {
99  id <CPSplitViewDelegate> _delegate;
100  BOOL _isVertical;
101  BOOL _isPaneSplitter;
102 
103  int _currentDivider;
104  float _initialOffset;
105  CPDictionary _preCollapsePositions;
106 
107  CPString _originComponent;
108  CPString _sizeComponent;
109 
110  CPArray _DOMDividerElements;
111  CPString _dividerImagePath;
112  int _drawingDivider;
113 
114  CPString _autosaveName;
115  BOOL _shouldAutosave;
116  CGSize _shouldRestoreFromAutosaveUnlessFrameSize;
117 
118  BOOL _needsResizeSubviews;
119  int _suppressResizeNotificationsMask;
120 
121  CPArray _buttonBars;
122 
123  unsigned _implementedDelegateMethods;
124 }
125 
127 {
128  return @"splitview";
129 }
130 
132 {
133  return @{
134  @"divider-thickness": 1.0,
135  @"pane-divider-thickness": 10.0,
136  @"pane-divider-color": [CPColor grayColor],
137  @"horizontal-divider-color": [CPNull null],
138  @"vertical-divider-color": [CPNull null],
139  };
140 }
141 
142 - (id)initWithFrame:(CGRect)aFrame
143 {
144  if (self = [super initWithFrame:aFrame])
145  {
146  _suppressResizeNotificationsMask = 0;
147  _preCollapsePositions = [CPMutableDictionary new];
148  _currentDivider = CPNotFound;
149 
150  _DOMDividerElements = [];
151  _buttonBars = [];
152 
153  _shouldAutosave = YES;
154 
155  [self _setVertical:YES];
156  }
157 
158  return self;
159 }
160 
166 {
167  return [self currentValueForThemeAttribute:[self isPaneSplitter] ? @"pane-divider-thickness" : @"divider-thickness"];
168 }
169 
174 - (BOOL)isVertical
175 {
176  return _isVertical;
177 }
178 
183 - (void)setVertical:(BOOL)shouldBeVertical
184 {
185  if (![self _setVertical:shouldBeVertical])
186  return;
187 
188  // Just re-adjust evenly.
189  var frame = [self frame],
191 
192  [self _postNotificationWillResize];
193 
194  var eachSize = ROUND((frame.size[_sizeComponent] - dividerThickness * (_subviews.length - 1)) / _subviews.length),
195  index = 0,
196  count = _subviews.length;
197 
198  if ([self isVertical])
199  {
200  for (; index < count; ++index)
201  [_subviews[index] setFrame:CGRectMake(ROUND((eachSize + dividerThickness) * index), 0, eachSize, frame.size.height)];
202  }
203  else
204  {
205  for (; index < count; ++index)
206  [_subviews[index] setFrame:CGRectMake(0, ROUND((eachSize + dividerThickness) * index), frame.size.width, eachSize)];
207  }
208 
209  [self setNeedsDisplay:YES];
210  [self _postNotificationDidResize];
211 
212 }
213 
214 - (BOOL)_setVertical:(BOOL)shouldBeVertical
215 {
216  var changed = (_isVertical != shouldBeVertical);
217 
218  _isVertical = shouldBeVertical;
219 
220  _originComponent = [self isVertical] ? "x" : "y";
221  _sizeComponent = [self isVertical] ? "width" : "height";
222  _dividerImagePath = [self isVertical] ? [[self valueForThemeAttribute:@"vertical-divider-color"] filename] : [[self valueForThemeAttribute:@"horizontal-divider-color"] filename];
223 
224  return changed;
225 }
226 
233 {
234  return _isPaneSplitter;
235 }
236 
242 - (void)setIsPaneSplitter:(BOOL)shouldBePaneSplitter
243 {
244  if (_isPaneSplitter == shouldBePaneSplitter)
245  return;
246 
247  _isPaneSplitter = shouldBePaneSplitter;
248 
249  if (_DOMDividerElements[_drawingDivider])
250  [self _setupDOMDivider];
251 
252  // The divider changes size when pane splitter mode is toggled, so the
253  // subviews need to change size too.
254  _needsResizeSubviews = YES;
255  [self setNeedsDisplay:YES];
256 }
257 
258 - (void)didAddSubview:(CPView)aSubview
259 {
260  _needsResizeSubviews = YES;
261 }
262 
268 - (BOOL)isSubviewCollapsed:(CPView)subview
269 {
270  return [subview frame].size[_sizeComponent] < 1 ? YES : NO;
271 }
272 
279 - (CGRect)rectOfDividerAtIndex:(int)aDivider
280 {
281  var frame = [_subviews[aDivider] frame],
282  rect = CGRectMakeZero();
283 
284  rect.size = [self frame].size;
285  rect.size[_sizeComponent] = [self dividerThickness];
286  rect.origin[_originComponent] = frame.origin[_originComponent] + frame.size[_sizeComponent];
287 
288  return rect;
289 }
290 
297 - (CGRect)effectiveRectOfDividerAtIndex:(int)aDivider
298 {
299  var realRect = [self rectOfDividerAtIndex:aDivider],
300  padding = 2;
301 
302  realRect.size[_sizeComponent] += padding * 2;
303  realRect.origin[_originComponent] -= padding;
304 
305  return realRect;
306 }
307 
308 - (void)drawRect:(CGRect)rect
309 {
310  var count = [_subviews count] - 1;
311 
312  while ((count--) > 0)
313  {
314  _drawingDivider = count;
315  [self drawDividerInRect:[self rectOfDividerAtIndex:count]];
316  }
317 }
318 
324 - (void)willRemoveSubview:(CPView)aView
325 {
326 #if PLATFORM(DOM)
327  var dividerToRemove = _DOMDividerElements.pop();
328 
329  // The divider may not exist if we never rendered out the DOM.
330  if (dividerToRemove)
331  CPDOMDisplayServerRemoveChild(_DOMElement, dividerToRemove);
332 #endif
333 
334  _needsResizeSubviews = YES;
335  [self setNeedsLayout];
336  [self setNeedsDisplay:YES];
337 }
338 
340 {
341  [self _adjustSubviewsWithCalculatedSize]
342 }
343 
348 - (void)drawDividerInRect:(CGRect)aRect
349 {
350 #if PLATFORM(DOM)
351  if (!_DOMDividerElements[_drawingDivider])
352  {
353  _DOMDividerElements[_drawingDivider] = document.createElement("div");
354 
355  _DOMDividerElements[_drawingDivider].style.position = "absolute";
356  _DOMDividerElements[_drawingDivider].style.backgroundRepeat = "repeat";
357 
358  CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]);
359  }
360 
361  [self _setupDOMDivider];
362  CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, CGRectGetMinX(aRect), CGRectGetMinY(aRect));
363  CPDOMDisplayServerSetStyleSize(_DOMDividerElements[_drawingDivider], CGRectGetWidth(aRect), CGRectGetHeight(aRect));
364 #endif
365 }
366 
367 - (void)_setupDOMDivider
368 {
369  if (_isPaneSplitter)
370  {
371  _DOMDividerElements[_drawingDivider].style.backgroundColor = "";
372  _DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')";
373  }
374  else
375  {
376  _DOMDividerElements[_drawingDivider].style.backgroundColor = [[self currentValueForThemeAttribute:@"pane-divider-color"] cssString];
377  _DOMDividerElements[_drawingDivider].style.backgroundImage = "";
378  }
379 }
380 
382 {
383  [self _adjustSubviewsWithCalculatedSize];
384 }
385 
386 - (void)_adjustSubviewsWithCalculatedSize
387 {
388  if (!_needsResizeSubviews)
389  return;
390 
391  _needsResizeSubviews = NO;
392 
393  [self resizeSubviewsWithOldSize:[self _calculateSize]];
394 }
395 
396 - (CGSize)_calculateSize
397 {
398  var subviews = [self subviews],
399  count = subviews.length,
400  size = CGSizeMakeZero();
401 
402  if ([self isVertical])
403  {
404  size.width += [self dividerThickness] * (count - 1);
405  size.height = CGRectGetHeight([self frame]);
406  }
407  else
408  {
409  size.width = CGRectGetWidth([self frame]);
410  size.height += [self dividerThickness] * (count - 1);
411  }
412 
413  while (count--)
414  size[_sizeComponent] += [subviews[count] frame].size[_sizeComponent];
415 
416  return size;
417 }
418 
419 - (BOOL)cursorAtPoint:(CGPoint)aPoint hitDividerAtIndex:(int)anIndex
420 {
421  var frame = [_subviews[anIndex] frame],
422  startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent],
423  effectiveRect = [self effectiveRectOfDividerAtIndex:anIndex],
424  buttonBar = _buttonBars[anIndex],
425  buttonBarRect = null,
426  additionalRect = null;
427 
428  if (buttonBar != null)
429  {
430  buttonBarRect = [buttonBar resizeControlFrame];
431  buttonBarRect.origin = [self convertPoint:buttonBarRect.origin fromView:buttonBar];
432  }
433 
434  effectiveRect = [self _sendDelegateSplitViewEffectiveRect:effectiveRect forDrawnRect:effectiveRect ofDividerAtIndex:anIndex];
435  additionalRect = [self _sendDelegateSplitViewAdditionalEffectiveRectOfDividerAtIndex:anIndex];
436 
437  return CGRectContainsPoint(effectiveRect, aPoint) ||
438  (additionalRect && CGRectContainsPoint(additionalRect, aPoint)) ||
439  (buttonBarRect && CGRectContainsPoint(buttonBarRect, aPoint));
440 }
441 
442 - (CPView)hitTest:(CGPoint)aPoint
443 {
444  if ([self isHidden] || ![self hitTests] || !CGRectContainsPoint([self frame], aPoint))
445  return nil;
446 
447  var point = [self convertPoint:aPoint fromView:[self superview]],
448  count = [_subviews count] - 1;
449 
450  for (var i = 0; i < count; i++)
451  {
452  if ([self cursorAtPoint:point hitDividerAtIndex:i])
453  return self;
454  }
455 
456  return [super hitTest:aPoint];
457 }
458 
459 /*
460  Tracks the divider.
461  @param anEvent the input event
462 */
463 - (void)trackDivider:(CPEvent)anEvent
464 {
465  var type = [anEvent type];
466 
467  if (type == CPLeftMouseUp)
468  {
469  // We disabled autosaving during tracking.
470  _shouldAutosave = YES;
471 
472  if (_currentDivider != CPNotFound)
473  {
474  _currentDivider = CPNotFound;
475  [self _autosave];
476  [self _updateResizeCursor:anEvent];
477  }
478 
479  return;
480  }
481 
482  if (type == CPLeftMouseDown)
483  {
484  var point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
485  count = [_subviews count] - 1;
486 
487  _currentDivider = CPNotFound;
488 
489  for (var i = 0; i < count; i++)
490  {
491  var frame = [_subviews[i] frame],
492  startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent];
493 
494  if ([self cursorAtPoint:point hitDividerAtIndex:i])
495  {
496  if ([anEvent clickCount] == 2 &&
497  [self _delegateRespondsToSplitViewCanCollapseSubview] &&
498  [self _delegateRespondsToSplitViewshouldCollapseSubviewForDoubleClickOnDividerAtIndex])
499  {
500  var minPosition = [self minPossiblePositionOfDividerAtIndex:i],
501  maxPosition = [self maxPossiblePositionOfDividerAtIndex:i],
502  preCollapsePosition = [_preCollapsePositions objectForKey:"" + i] || 0;
503 
504  if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[i]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
505  {
506  if ([self isSubviewCollapsed:_subviews[i]])
507  [self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
508  else
509  [self setPosition:minPosition ofDividerAtIndex:i];
510  }
511  else if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[i + 1]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[i + 1] forDoubleClickOnDividerAtIndex:i])
512  {
513  if ([self isSubviewCollapsed:_subviews[i + 1]])
514  [self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
515  else
516  [self setPosition:maxPosition ofDividerAtIndex:i];
517  }
518  }
519  else
520  {
521  _currentDivider = i;
522  _initialOffset = startPosition - point[_originComponent];
523 
524  // Don't autosave during a resize. We'll wait until it's done.
525  _shouldAutosave = NO;
526  [self _postNotificationWillResize];
527  }
528  }
529  }
530 
531  if (_currentDivider === CPNotFound)
532  return;
533  }
534 
535  else if (type == CPLeftMouseDragged && _currentDivider != CPNotFound)
536  {
537  var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
538 
539  [self setPosition:(point[_originComponent] + _initialOffset) ofDividerAtIndex:_currentDivider];
540  // Cursor might change if we reach a resize limit.
541  [self _updateResizeCursor:anEvent];
542  }
543 
544  [CPApp setTarget:self selector:@selector(trackDivider:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
545 }
546 
547 - (void)mouseDown:(CPEvent)anEvent
548 {
549  // FIXME: This should not trap events if not on a divider!
550  [self trackDivider:anEvent];
551 }
552 
554 {
555  // Enable split view resize cursors. Commented out pending CPTrackingArea implementation.
556  //[[self window] setAcceptsMouseMovedEvents:YES];
557 }
558 
559 - (void)_updateResizeCursor:(CPEvent)anEvent
560 {
561  var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
562 
563  if ([anEvent type] === CPLeftMouseUp && ![[self window] acceptsMouseMovedEvents])
564  {
566  return;
567  }
568 
569  for (var i = 0, count = [_subviews count] - 1; i < count; i++)
570  {
571  // If we are currently tracking, keep the resize cursor active even outside of hit areas.
572  if (_currentDivider === i || (_currentDivider == CPNotFound && [self cursorAtPoint:point hitDividerAtIndex:i]))
573  {
574  var frameA = [_subviews[i] frame],
575  sizeA = frameA.size[_sizeComponent],
576  startPosition = frameA.origin[_originComponent] + sizeA,
577  frameB = [_subviews[i + 1] frame],
578  sizeB = frameB.size[_sizeComponent],
579  canShrink = [self _realPositionForPosition:startPosition - 1 ofDividerAtIndex:i] < startPosition,
580  canGrow = [self _realPositionForPosition:startPosition + 1 ofDividerAtIndex:i] > startPosition,
581  cursor = [CPCursor arrowCursor];
582 
583  if (sizeA === 0)
584  canGrow = YES; // Subview is collapsed.
585  else if (!canShrink && [self _sendDelegateSplitViewCanCollapseSubview:_subviews[i]])
586  canShrink = YES; // Subview is collapsible.
587 
588  if (sizeB === 0)
589  {
590  // Right/lower subview is collapsed.
591  canGrow = NO;
592  // It's safe to assume it can always be uncollapsed.
593  canShrink = YES;
594  }
595  else if (!canGrow && [self _sendDelegateSplitViewCanCollapseSubview:_subviews[i + 1]])
596  {
597  canGrow = YES; // Right/lower subview is collapsible.
598  }
599 
600  if (_isVertical && canShrink && canGrow)
601  cursor = [CPCursor resizeLeftRightCursor];
602  else if (_isVertical && canShrink)
603  cursor = [CPCursor resizeLeftCursor];
604  else if (_isVertical && canGrow)
605  cursor = [CPCursor resizeRightCursor];
606  else if (canShrink && canGrow)
607  cursor = [CPCursor resizeUpDownCursor];
608  else if (canShrink)
609  cursor = [CPCursor resizeUpCursor];
610  else if (canGrow)
611  cursor = [CPCursor resizeDownCursor];
612 
613  [cursor set];
614  return;
615  }
616  }
617 
619 }
620 
626 - (float)maxPossiblePositionOfDividerAtIndex:(int)dividerIndex
627 {
628  var frame = [_subviews[dividerIndex + 1] frame];
629 
630  if (dividerIndex + 1 < [_subviews count] - 1)
631  return frame.origin[_originComponent] + frame.size[_sizeComponent] - [self dividerThickness];
632  else
633  return [self frame].size[_sizeComponent] - [self dividerThickness];
634 }
635 
641 - (float)minPossiblePositionOfDividerAtIndex:(int)dividerIndex
642 {
643  if (dividerIndex > 0)
644  {
645  var frame = [_subviews[dividerIndex - 1] frame];
646 
647  return frame.origin[_originComponent] + frame.size[_sizeComponent] + [self dividerThickness];
648  }
649  else
650  return 0;
651 }
652 
653 - (int)_realPositionForPosition:(float)position ofDividerAtIndex:(int)dividerIndex
654 {
655  // not sure where this should override other positions?
656  var proposedPosition = [self _sendDelegateSplitViewConstrainSplitPosition:position ofSubviewAt:dividerIndex];
657 
658  // Silently ignore bad positions which could result from odd delegate responses. We don't want these
659  // bad results to go into the system and cause havoc with frame sizes as the split view tries to resize
660  // its subviews.
661  if (_IS_NUMERIC(proposedPosition))
662  position = proposedPosition;
663 
664  var proposedMax = [self maxPossiblePositionOfDividerAtIndex:dividerIndex],
665  proposedMin = [self minPossiblePositionOfDividerAtIndex:dividerIndex],
666  actualMax = proposedMax,
667  actualMin = proposedMin,
668  proposedActualMin = [self _sendDelegateSplitViewConstrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex],
669  proposedActualMax = [self _sendDelegateSplitViewConstrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
670 
671  if (_IS_NUMERIC(proposedActualMin))
672  actualMin = proposedActualMin;
673 
674  if (_IS_NUMERIC(proposedActualMax))
675  actualMax = proposedActualMax;
676 
677  var viewA = _subviews[dividerIndex],
678  viewB = _subviews[dividerIndex + 1],
679  realPosition = MAX(MIN(position, actualMax), actualMin);
680 
681  // Is this position past the halfway point to collapse?
682  if ((position < proposedMin + (actualMin - proposedMin) / 2) && [self _sendDelegateSplitViewCanCollapseSubview:viewA])
683  realPosition = proposedMin;
684 
685  // We can also collapse to the right.
686  if ((position > proposedMax - (proposedMax - actualMax) / 2) && [self _sendDelegateSplitViewCanCollapseSubview:viewB])
687  realPosition = proposedMax;
688 
689  return realPosition;
690 }
691 
697 - (void)setPosition:(float)position ofDividerAtIndex:(int)dividerIndex
698 {
699  // Any manual changes to the divider position should override anything we are restoring from
700  // autosave.
701  _shouldRestoreFromAutosaveUnlessFrameSize = nil;
702 
704  [self _adjustSubviewsWithCalculatedSize];
705 
706  var realPosition = [self _realPositionForPosition:position ofDividerAtIndex:dividerIndex],
707  viewA = _subviews[dividerIndex],
708  frameA = [viewA frame],
709  viewB = _subviews[dividerIndex + 1],
710  frameB = [viewB frame],
711  preCollapsePosition = 0,
712  preSize = frameA.size[_sizeComponent];
713 
714  frameA.size[_sizeComponent] = realPosition - frameA.origin[_originComponent];
715 
716  if (preSize !== 0 && frameA.size[_sizeComponent] === 0)
717  preCollapsePosition = preSize;
718 
719  if (preSize !== frameA.size[_sizeComponent])
720  {
722  [_subviews[dividerIndex] setFrame:frameA];
724  }
725 
726  preSize = frameB.size[_sizeComponent];
727 
728  var preOrigin = frameB.origin[_originComponent];
729  frameB.size[_sizeComponent] = frameB.origin[_originComponent] + frameB.size[_sizeComponent] - realPosition - [self dividerThickness];
730 
731  if (preSize !== 0 && frameB.size[_sizeComponent] === 0)
732  preCollapsePosition = frameB.origin[_originComponent];
733 
734  frameB.origin[_originComponent] = realPosition + [self dividerThickness];
735 
736  if (preSize !== frameB.size[_sizeComponent] || preOrigin !== frameB.origin[_originComponent])
737  {
739  [_subviews[dividerIndex + 1] setFrame:frameB];
741  }
742 
743  if (preCollapsePosition)
744  [_preCollapsePositions setObject:preCollapsePosition forKey:"" + dividerIndex];
745 
746  [self setNeedsDisplay:YES];
747 
749  [self _postNotificationDidResize];
750 
752 }
753 
754 - (void)setFrameSize:(CGSize)aSize
755 {
756  if (_shouldRestoreFromAutosaveUnlessFrameSize)
757  _shouldAutosave = NO;
758  else
759  [self _adjustSubviewsWithCalculatedSize];
760 
761  [super setFrameSize:aSize];
762 
763  if (_shouldRestoreFromAutosaveUnlessFrameSize)
764  _shouldAutosave = YES;
765 
766  [self setNeedsDisplay:YES];
767 }
768 
769 - (void)resizeSubviewsWithOldSize:(CGSize)oldSize
770 {
771  if ([self _delegateRespondsToSplitViewResizeSubviewsWithOldSize])
772  {
773  [self _sendDelegateSplitViewResizeSubviewsWithOldSize:oldSize];
774  return;
775  }
776 
777  [self adjustSubviews];
778 }
779 
781 {
782  var count = [_subviews count];
783 
784  if (!count)
785  return;
786 
788  [self _postNotificationWillResize];
789 
790  var index = 0,
791  bounds = [self bounds],
792  boundsSize = bounds.size[_sizeComponent],
793  oldSize = [self _calculateSize],
795  totalDividers = count - 1,
796  oldFlexibleSpace = 0,
797  totalSizablePanes = 0,
798  isSizableMap = {},
799  viewSizes = [];
800 
801  // What we want to do is to preserve non resizable sizes first, and then to preserve the ratio of size to available
802  // non fixed space for every other subview. E.g. assume fixed space was 20 pixels initially, view 1 was 20 and
803  // view 2 was 30 pixels, for a total of 70 pixels. Then the new total size becomes 140 pixels. Now we want the fixed
804  // space to still be 20 pixels, view 1 to be 48 pixels and view 2 to be 72 pixels. This way the relative size of
805  // view 1 to view 2 remains the same - view 1 was 66% of view 2 initially and after the resize view 1 is still
806  // 66% of view 2's size.
807  //
808  // For this calculation, we can consider the dividers themselves to also be fixed size areas - they should remain
809  // the same size before and after.
810 
811  // How much flexible size do we have in pre-resize pixels?
812  for (index = 0; index < count; ++index)
813  {
814  var view = _subviews[index],
815  isSizable = [self _sendDelegateSplitViewShouldAdjustSizeOfSubview:view],
816  size = [view frame].size[_sizeComponent];
817 
818  isSizableMap[index] = isSizable;
819  viewSizes.push(size);
820 
821  if (isSizable)
822  {
823  oldFlexibleSpace += size;
824  totalSizablePanes++;
825  }
826  }
827 
828  // nonSizableSpace is the number of fixed pixels in pre-resize terms and the desired number post-resize.
829  var nonSizableSpace = oldSize[_sizeComponent] - oldFlexibleSpace,
830  newFlexibleSpace = boundsSize - nonSizableSpace,
831  remainingFixedPixelsToRemove = 0;
832 
833  if (newFlexibleSpace < 0)
834  {
835  remainingFixedPixelsToRemove = -newFlexibleSpace;
836  newFlexibleSpace = 0;
837  }
838 
839  var remainingFixedPanes = count - totalSizablePanes;
840 
841  for (index = 0; index < count; ++index)
842  {
843  var view = _subviews[index],
844  viewFrame = CGRectMakeCopy(bounds),
845  isSizable = isSizableMap[index],
846  targetSize = 0;
847 
848  // The last area must take up exactly the remaining space, fixed or not.
849  if (index + 1 === count)
850  targetSize = boundsSize - viewFrame.origin[_originComponent];
851  // Try to keep fixed size areas the same size.
852  else if (!isSizable)
853  {
854  var removedFixedPixels = MIN(remainingFixedPixelsToRemove / remainingFixedPanes, viewSizes[index]);
855  targetSize = viewSizes[index] - removedFixedPixels;
856  remainingFixedPixelsToRemove -= removedFixedPixels;
857  remainingFixedPanes--;
858  }
859  // (new size / flexible size available) == (old size / old flexible size available)
860  else if (oldFlexibleSpace > 0)
861  targetSize = newFlexibleSpace * viewSizes[index] / oldFlexibleSpace;
862  // oldFlexibleSpace <= 0 so all flexible areas were crushed. When we get space, allocate it evenly.
863  // totalSizablePanes cannot be 0 since isSizable.
864  else
865  targetSize = newFlexibleSpace / totalSizablePanes;
866 
867  targetSize = MAX(0, ROUND(targetSize));
868  viewFrame.size[_sizeComponent] = targetSize;
869  [view setFrame:viewFrame];
870  bounds.origin[_originComponent] += targetSize + dividerThickness;
871  }
872 
874 }
875 
941 - (void)setDelegate:(id <CPSplitViewDelegate>)aDelegate
942 {
943  if (_delegate === aDelegate)
944  return;
945 
946  _delegate = aDelegate;
947  _implementedDelegateMethods = 0;
948 
949  if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
950  _implementedDelegateMethods |= CPSplitViewDelegate_splitViewWillResizeSubviews_;
951 
952  if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
953  _implementedDelegateMethods |= CPSplitViewDelegate_splitViewDidResizeSubviews_;
954 
955  if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
956  _implementedDelegateMethods |= CPSplitViewDelegate_splitView_canCollapseSubview_;
957 
958  if ([_delegate respondsToSelector:@selector(splitView:shouldAdjustSizeOfSubview:)])
959  _implementedDelegateMethods |= CPSplitViewDelegate_splitView_shouldAdjustSizeOfSubview_;
960 
961  if ([_delegate respondsToSelector:@selector(splitView:shouldCollapseSubview:forDoubleClickOnDividerAtIndex:)])
963 
964  if ([_delegate respondsToSelector:@selector(splitView:additionalEffectiveRectOfDividerAtIndex:)])
966 
967  if ([_delegate respondsToSelector:@selector(splitView:effectiveRect:forDrawnRect:ofDividerAtIndex:)])
969 
970  if ([_delegate respondsToSelector:@selector(splitView:constrainMaxCoordinate:ofSubviewAt:)])
972 
973  if ([_delegate respondsToSelector:@selector(splitView:constrainMinCoordinate:ofSubviewAt:)])
975 
976  if ([_delegate respondsToSelector:@selector(splitView:constrainSplitPosition:ofSubviewAt:)])
978 
979  if ([_delegate respondsToSelector:@selector(splitView:resizeSubviewsWithOldSize:)])
980  _implementedDelegateMethods |= CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_;
981 
982 }
983 
999 // FIXME Should be renamed to setButtonBar:ofDividerAtIndex:.
1000 - (void)setButtonBar:(CPButtonBar)aButtonBar forDividerAtIndex:(CPUInteger)dividerIndex
1001 {
1002  if (!aButtonBar)
1003  {
1004  _buttonBars[dividerIndex] = nil;
1005  return;
1006  }
1007 
1008  var view = [aButtonBar superview],
1009  subview = aButtonBar;
1010 
1011  while (view && view !== self)
1012  {
1013  subview = view;
1014  view = [view superview];
1015  }
1016 
1017  if (view !== self)
1018  [CPException raise:CPInvalidArgumentException
1019  reason:@"CPSplitView button bar must be a subview of the split view."];
1020 
1021  var viewIndex = [[self subviews] indexOfObject:subview];
1022 
1023  [aButtonBar setHasResizeControl:YES];
1024  [aButtonBar setResizeControlIsLeftAligned:dividerIndex < viewIndex];
1025 
1026  _buttonBars[dividerIndex] = aButtonBar;
1027 }
1028 
1029 - (void)_postNotificationWillResize
1030 {
1031  [self _sendDelegateSplitViewWillResizeSubviews];
1032 }
1033 
1034 - (void)_postNotificationDidResize
1035 {
1036  [self _sendDelegateSplitViewDidResizeSubviews];
1037 
1038  // TODO Cocoa always autosaves on "viewDidEndLiveResize". If Cappuccino adds support for this we
1039  // should do the same.
1040  [self _autosave];
1041 }
1042 
1048 - (void)setAutosaveName:(CPString)autosaveName
1049 {
1050  if (_autosaveName == autosaveName)
1051  return;
1052 
1053  _autosaveName = autosaveName;
1054 }
1055 
1061 - (CPString)autosaveName
1062 {
1063  return _autosaveName;
1064 }
1065 
1069 - (void)_autosave
1070 {
1071  if (_shouldRestoreFromAutosaveUnlessFrameSize || !_shouldAutosave || !_autosaveName)
1072  return;
1073 
1074  var userDefaults = [CPUserDefaults standardUserDefaults],
1075  autosaveName = [self _framesKeyForAutosaveName:[self autosaveName]],
1076  autosavePrecollapseName = [self _precollapseKeyForAutosaveName:[self autosaveName]],
1077  count = [_subviews count],
1078  positions = [CPMutableArray new],
1079  preCollapseArray = [CPMutableArray new];
1080 
1081  for (var i = 0; i < count; i++)
1082  {
1083  var frame = [_subviews[i] frame];
1084  [positions addObject:CGStringFromRect(frame)];
1085  [preCollapseArray addObject:[_preCollapsePositions objectForKey:"" + i]];
1086  }
1087 
1088  [userDefaults setObject:positions forKey:autosaveName];
1089  [userDefaults setObject:preCollapseArray forKey:autosavePrecollapseName];
1090 }
1091 
1098 - (void)_restoreFromAutosaveIfNeeded
1099 {
1100  if (_shouldRestoreFromAutosaveUnlessFrameSize && !CGSizeEqualToSize([self frameSize], _shouldRestoreFromAutosaveUnlessFrameSize))
1101  {
1102  [self _restoreFromAutosave];
1103  }
1104 
1105  _shouldRestoreFromAutosaveUnlessFrameSize = nil;
1106 }
1107 
1111 - (void)_restoreFromAutosave
1112 {
1113  if (!_autosaveName)
1114  return;
1115 
1116  var autosaveName = [self _framesKeyForAutosaveName:[self autosaveName]],
1117  autosavePrecollapseName = [self _precollapseKeyForAutosaveName:[self autosaveName]],
1118  userDefaults = [CPUserDefaults standardUserDefaults],
1119  frames = [userDefaults objectForKey:autosaveName],
1120  preCollapseArray = [userDefaults objectForKey:autosavePrecollapseName];
1121 
1122  if (frames)
1123  {
1124  var dividerThickness = [self dividerThickness],
1125  position = 0;
1126 
1127  _shouldAutosave = NO;
1128 
1129  for (var i = 0, count = [frames count] - 1; i < count; i++)
1130  {
1131  var frame = CGRectFromString(frames[i]);
1132  position += frame.size[_sizeComponent];
1133 
1134  [self setPosition:position ofDividerAtIndex:i];
1135 
1137  }
1138 
1139  _shouldAutosave = YES;
1140  }
1141 
1142  if (preCollapseArray)
1143  {
1144  _preCollapsePositions = [CPMutableDictionary new];
1145 
1146  for (var i = 0, count = [preCollapseArray count]; i < count; i++)
1147  {
1148  var item = preCollapseArray[i];
1149 
1150  if (item === nil)
1151  [_preCollapsePositions removeObjectForKey:String(i)];
1152  else
1153  [_preCollapsePositions setObject:item forKey:String(i)];
1154  }
1155  }
1156 }
1157 
1161 - (CPString)_framesKeyForAutosaveName:(CPString)theAutosaveName
1162 {
1163  if (!theAutosaveName)
1164  return nil;
1165 
1166  return @"CPSplitView Subview Frames " + theAutosaveName;
1167 }
1168 
1172 - (CPString)_precollapseKeyForAutosaveName:(CPString)theAutosaveName
1173 {
1174  if (!theAutosaveName)
1175  return nil;
1176 
1177  return @"CPSplitView Subview Precollapse Positions " + theAutosaveName;
1178 }
1179 
1180 @end
1181 
1183 
1185 {
1186  [self removeAllTrackingAreas];
1187 
1189 
1190  for (var i = 0; i < _subviews.length - 1; i++)
1191  [self addTrackingArea:[[CPTrackingArea alloc] initWithRect:[self effectiveRectOfDividerAtIndex:i]
1192  options:options
1193  owner:self
1194  userInfo:nil]];
1195 }
1196 
1197 - (void)cursorUpdate:(CPEvent)anEvent
1198 {
1199  if (_currentDivider === CPNotFound)
1200  [self _updateResizeCursor:anEvent];
1201 }
1202 
1203 @end
1204 
1205 
1207 
1212 - (BOOL)_delegateRespondsToSplitViewResizeSubviewsWithOldSize
1213 {
1214  return _implementedDelegateMethods & CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_;
1215 }
1216 
1221 - (BOOL)_delegateRespondsToSplitViewCanCollapseSubview
1222 {
1223  return _implementedDelegateMethods & CPSplitViewDelegate_splitView_canCollapseSubview_;
1224 }
1225 
1230 - (BOOL)_delegateRespondsToSplitViewshouldCollapseSubviewForDoubleClickOnDividerAtIndex
1231 {
1233 }
1234 
1235 
1240 - (BOOL)_sendDelegateSplitViewCanCollapseSubview:(CPView)aView
1241 {
1242  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_canCollapseSubview_))
1243  return NO;
1244 
1245  return [_delegate splitView:self canCollapseSubview:aView];
1246 }
1247 
1252 - (BOOL)_sendDelegateSplitViewShouldAdjustSizeOfSubview:(CPView)aView
1253 {
1254  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_shouldAdjustSizeOfSubview_))
1255  return YES;
1256 
1257  return [_delegate splitView:self shouldAdjustSizeOfSubview:aView];
1258 }
1259 
1264 - (BOOL)_sendDelegateSplitViewShouldCollapseSubview:(CPView)aView forDoubleClickOnDividerAtIndex:(int)anIndex
1265 {
1267  return NO;
1268 
1269  return [_delegate splitView:self shouldCollapseSubview:aView forDoubleClickOnDividerAtIndex:anIndex];
1270 }
1271 
1276 - (CGRect)_sendDelegateSplitViewAdditionalEffectiveRectOfDividerAtIndex:(int)anIndex
1277 {
1279  return nil;
1280 
1281  return [_delegate splitView:self additionalEffectiveRectOfDividerAtIndex:anIndex];
1282 }
1283 
1288 - (CGRect)_sendDelegateSplitViewEffectiveRect:(CGRect)proposedEffectiveRect forDrawnRect:(CGRect)drawnRect ofDividerAtIndex:(CPInteger)dividerIndex
1289 {
1291  return proposedEffectiveRect;
1292 
1293  return [_delegate splitView:self effectiveRect:proposedEffectiveRect forDrawnRect:drawnRect ofDividerAtIndex:dividerIndex];
1294 }
1295 
1300 - (float)_sendDelegateSplitViewConstrainMaxCoordinate:(float)proposedMax ofSubviewAt:(CPInteger)dividerIndex
1301 {
1302  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_constrainMaxCoordinate_ofSubviewAt_))
1303  return nil;
1304 
1305  return [_delegate splitView:self constrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
1306 }
1307 
1312 - (float)_sendDelegateSplitViewConstrainMinCoordinate:(float)proposedMin ofSubviewAt:(CPInteger)dividerIndex
1313 {
1314  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_constrainMinCoordinate_ofSubviewAt_))
1315  return nil;
1316 
1317  return [_delegate splitView:self constrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex];
1318 }
1319 
1324 - (float)_sendDelegateSplitViewConstrainSplitPosition:(float)proposedMax ofSubviewAt:(CPInteger)dividerIndex
1325 {
1326  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_constrainSplitPosition_ofSubviewAt_))
1327  return nil;
1328 
1329  return [_delegate splitView:self constrainSplitPosition:proposedMax ofSubviewAt:dividerIndex];
1330 }
1331 
1336 - (void)_sendDelegateSplitViewResizeSubviewsWithOldSize:(CGSize)oldSize
1337 {
1338  if (!(_implementedDelegateMethods & CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_))
1339  return;
1340 
1341  [_delegate splitView:self resizeSubviewsWithOldSize:oldSize];
1342 }
1343 
1348 - (void)_sendDelegateSplitViewWillResizeSubviews
1349 {
1350  var userInfo = nil;
1351 
1352  if (_currentDivider !== CPNotFound)
1353  userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
1354 
1355  if (_implementedDelegateMethods & CPSplitViewDelegate_splitViewWillResizeSubviews_)
1356  [_delegate splitViewWillResizeSubviews:[[CPNotification alloc] initWithName:CPSplitViewWillResizeSubviewsNotification object:self userInfo:userInfo]];
1357 
1358  [[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewWillResizeSubviewsNotification object:self userInfo:userInfo];
1359 }
1360 
1365 - (void)_sendDelegateSplitViewDidResizeSubviews
1366 {
1367  var userInfo = nil;
1368 
1369  if (_currentDivider !== CPNotFound)
1370  userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
1371 
1372  if (_implementedDelegateMethods & CPSplitViewDelegate_splitViewDidResizeSubviews_)
1373  [_delegate splitViewDidResizeSubviews:[[CPNotification alloc] initWithName:CPSplitViewDidResizeSubviewsNotification object:self userInfo:userInfo]];
1374 
1375  [[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewDidResizeSubviewsNotification object:self userInfo:userInfo];
1376 
1377  [self updateTrackingAreas];
1378 }
1379 
1380 @end
1381 
1382 
1383 var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
1384  CPSplitViewIsVerticalKey = "CPSplitViewIsVerticalKey",
1385  CPSplitViewIsPaneSplitterKey = "CPSplitViewIsPaneSplitterKey",
1386  CPSplitViewButtonBarsKey = "CPSplitViewButtonBarsKey",
1387  CPSplitViewAutosaveNameKey = "CPSplitViewAutosaveNameKey";
1388 
1389 @implementation CPSplitView (CPCoding)
1390 
1391 /*
1392  Initializes the split view by unarchiving data from \c aCoder.
1393  @param aCoder the coder containing the archived CPSplitView.
1394 */
1395 - (id)initWithCoder:(CPCoder)aCoder
1396 {
1397  // We need to restore this property before calling super's initWithCoder:.
1398  _autosaveName = [aCoder decodeObjectForKey:CPSplitViewAutosaveNameKey];
1399 
1400  /*
1401 
1402  It is common for the main window of a Cappuccino app window to be resized to match the browser
1403  window size at the end of the UI being loaded from a cib. But at decoding time (now) whatever
1404  window size was originally saved will be in place, so if we try to restore the autosaved divider
1405  positions now they might be constrained to the wrong positions due to the difference in frame size,
1406  and in addition they might move later when the window is resized.
1407 
1408  The workaround is to restore the position once now (so it's approximately correct during loading),
1409  and then once more in the next runloop cycle when any `setFullPlatformWindow` calls are done.
1410 
1411  (However if the frame size doesn't change before the next cycle, we should not restore the position
1412  again because that would overwrite any changes the app developer might have made in user code.)
1413 
1414  The other consideration is that any parent split views need to be restored before any child
1415  subviews, otherwise the parent restore will also change the positioning of the child.
1416 
1417  */
1418  if (_autosaveName)
1419  {
1420  // Schedule /before/ [super initWithCoder:]. This way this instance's _restoreFromAutosaveIfNeeded
1421  // will happen before that of any subviews loaded by [super initWithCoder:].
1422  [[CPRunLoop currentRunLoop] performSelector:@selector(_restoreFromAutosaveIfNeeded) target:self argument:nil order:0 modes:[CPDefaultRunLoopMode]];
1423  }
1424 
1425  self = [super initWithCoder:aCoder];
1426 
1427  if (self)
1428  {
1429  _suppressResizeNotificationsMask = 0;
1430  _preCollapsePositions = [CPMutableDictionary new];
1431 
1432  _currentDivider = CPNotFound;
1433  _shouldAutosave = YES;
1434 
1435  _DOMDividerElements = [];
1436 
1437  _buttonBars = [aCoder decodeObjectForKey:CPSplitViewButtonBarsKey] || [];
1438 
1439  [self setDelegate:[aCoder decodeObjectForKey:CPSplitViewDelegateKey]];
1440 
1441  _isPaneSplitter = [aCoder decodeBoolForKey:CPSplitViewIsPaneSplitterKey];
1442  [self _setVertical:[aCoder decodeBoolForKey:CPSplitViewIsVerticalKey]];
1443 
1444  if (_autosaveName)
1445  {
1446  [self _restoreFromAutosave];
1447  // Remember the frame size we had at this point so that we can restore again if it changes
1448  // before the next runloop cycle. See above notes.
1449  _shouldRestoreFromAutosaveUnlessFrameSize = [self frameSize];
1450  }
1451  }
1452 
1453  return self;
1454 }
1455 
1456 /*
1457  Archives this split view into the provided coder.
1458  @param aCoder the coder to which the button's instance data will be written.
1459 */
1460 - (void)encodeWithCoder:(CPCoder)aCoder
1461 {
1462  [super encodeWithCoder:aCoder];
1463 
1464  //FIXME how should we handle this?
1465  //[aCoder encodeObject:_buttonBars forKey:CPSplitViewButtonBarsKey];
1466 
1467  [aCoder encodeConditionalObject:_delegate forKey:CPSplitViewDelegateKey];
1468 
1469  [aCoder encodeBool:_isVertical forKey:CPSplitViewIsVerticalKey];
1470  [aCoder encodeBool:_isPaneSplitter forKey:CPSplitViewIsPaneSplitterKey];
1471 
1472  [aCoder encodeObject:_autosaveName forKey:CPSplitViewAutosaveNameKey];
1473 }
1474 
1475 @end
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
#define SPLIT_VIEW_MAYBE_POST_DID_RESIZE()
Definition: CPSplitView.j:64
CGPoint position()
Definition: CALayer.j:225
void postNotificationName:object:userInfo:(CPString aNotificationName, [object] id anObject, [userInfo] CPDictionary aUserInfo)
An object representation of nil.
Definition: CPNull.h:2
CPCursor resizeRightCursor()
Definition: CPCursor.j:257
id initWithCoder:(CPCoder aCoder)
Definition: CPView.j:3662
CPCursor arrowCursor()
Definition: CPCursor.j:192
BOOL hitTests()
Definition: CPView.j:1814
var CPSplitViewDelegate_splitViewWillResizeSubviews_
Definition: CPSplitView.j:55
The main run loop for the application.
Definition: CPRunLoop.h:2
var DidSuppressResizeNotification
Definition: CPSplitView.j:84
void layoutSubviews()
Definition: CPSplitView.j:339
void performSelector:target:argument:order:modes:(SEL aSelector, [target] id aTarget, [argument] id anArgument, [order] int anOrder, [modes] CPArray modes)
Definition: CPRunLoop.j:253
CGSize boundsSize()
Definition: CPView.j:1312
CGPoint locationInWindow()
Definition: CPEvent.j:290
CGRect bounds()
Definition: CPView.j:1302
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
CPEventType type()
Definition: CPEvent.j:325
CPTrackingActiveInKeyWindow
float dividerThickness()
Definition: CPSplitView.j:165
CPView hitTest:(CGPoint aPoint)
Definition: CPView.j:1833
CGRect effectiveRectOfDividerAtIndex:(int aDivider)
Definition: CPSplitView.j:297
CPColor grayColor()
Definition: CPColor.j:316
CPNotificationCenter defaultCenter()
var CPSplitViewDelegate_splitViewDidResizeSubviews_
Definition: CPSplitView.j:54
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPRunLoop currentRunLoop()
Definition: CPRunLoop.j:232
var DidPostWillResizeNotification
Definition: CPSplitView.j:83
var CPSplitViewDelegate_splitView_constrainMinCoordinate_ofSubviewAt_
Definition: CPSplitView.j:51
CPWindow window()
Definition: CPView.j:503
CGSize frameSize()
Definition: CPView.j:1032
CPDictionary themeAttributes()
Definition: CPSplitView.j:131
id initWithName:object:userInfo:(CPString aNotificationName, [object] id anObject, [userInfo] CPDictionary aUserInfo)
void setResizeControlIsLeftAligned:(BOOL shouldBeLeftAligned)
Definition: CPButtonBar.j:171
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
var CPSplitViewDelegate_splitView_additionalEffectiveRectOfDividerAtIndex_
Definition: CPSplitView.j:48
CGPoint convertPoint:fromView:(CGPoint aPoint, [fromView] CPView aView)
Definition: CPView.j:2208
var CPSplitViewIsVerticalKey
Definition: CPSplitView.j:1384
var CPSplitViewDelegate_splitView_effectiveRect_forDrawnRect_ofDividerAtIndex_
Definition: CPSplitView.j:49
var CPSplitViewDelegate_splitView_shouldAdjustSizeOfSubview_
Definition: CPSplitView.j:46
var CPSplitViewAutosaveNameKey
Definition: CPSplitView.j:1387
var CPSplitViewDelegateKey
Definition: CPSplitView.j:1383
var CPSplitViewIsPaneSplitterKey
Definition: CPSplitView.j:1385
CPString autosaveName()
Definition: CPSplitView.j:1061
CPCursor resizeLeftRightCursor()
Definition: CPCursor.j:262
void viewDidMoveToWindow()
Definition: CPSplitView.j:553
void setNeedsDisplay:(BOOL aFlag)
Definition: CPView.j:2556
BOOL isHidden()
Definition: CPView.j:1697
void encodeWithCoder:(CPCoder aCoder)
Definition: CPView.j:3770
float maxPossiblePositionOfDividerAtIndex:(int dividerIndex)
Definition: CPSplitView.j:626
#define SPLIT_VIEW_SUPPRESS_RESIZE_NOTIFICATIONS(shouldSuppress)
Definition: CPSplitView.j:73
CPCursor resizeUpCursor()
Definition: CPCursor.j:247
CPLeftMouseUp
CGRect rectOfDividerAtIndex:(int aDivider)
Definition: CPSplitView.j:279
A notification that can be posted to a CPNotificationCenter.
Definition: CPNotification.h:2
CPArray subviews()
Definition: CPView.j:495
CPSplitViewDidResizeSubviewsNotification
Definition: CPSplitView.j:79
void setNeedsLayout()
Definition: CPView.j:2707
CPSplitViewWillResizeSubviewsNotification
Definition: CPSplitView.j:80
var CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_
Definition: CPSplitView.j:53
void setDelegate:(id< CPSplitViewDelegate > aDelegate)
Definition: CPSplitView.j:941
void setHasResizeControl:(BOOL shouldHaveResizeControl)
Definition: CPButtonBar.j:157
void setPosition:ofDividerAtIndex:(float position, [ofDividerAtIndex] int dividerIndex)
Definition: CPSplitView.j:697
var ShouldSuppressResizeNotifications
Definition: CPSplitView.j:82
CPLeftMouseDragged
var CPSplitViewButtonBarsKey
Definition: CPSplitView.j:1386
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
CPCursor resizeDownCursor()
Definition: CPCursor.j:242
CPNotFound
Definition: CPObjJRuntime.j:62
Sends messages (CPNotification) between objects.
void drawDividerInRect:(CGRect aRect)
Definition: CPSplitView.j:348
id new()
Definition: CPObject.j:122
BOOL isPaneSplitter()
Definition: CPSplitView.j:232
var CPSplitViewDelegate_splitView_constrainSplitPosition_ofSubviewAt_
Definition: CPSplitView.j:52
void adjustSubviews()
Definition: CPSplitView.j:780
BOOL isVertical()
Definition: CPSplitView.j:174
CPLeftMouseDown
void setFrameSize:(CGSize aSize)
Definition: CPView.j:1100
float minPossiblePositionOfDividerAtIndex:(int dividerIndex)
Definition: CPSplitView.j:641
void set()
Definition: CPCursor.j:122
Definition: CPEvent.h:2
CPString defaultThemeClass()
Definition: CPSplitView.j:126
CPView superview()
Definition: CPView.j:486
CGRect frame()
Definition: CPView.j:1022
#define SPLIT_VIEW_DID_SUPPRESS_RESIZE_NOTIFICATION()
Definition: CPSplitView.j:70
void removeAllTrackingAreas()
Definition: CPView.j:3564
void updateTrackingAreas()
Definition: CPSplitView.j:1184
CPCursor resizeLeftCursor()
Definition: CPCursor.j:252
var CPSplitViewDelegate_splitView_shouldCollapseSubview_forDoubleClickOnDividerAtIndex_
Definition: CPSplitView.j:47
void viewWillDraw()
Definition: CPSplitView.j:381
CPCursor resizeUpDownCursor()
Definition: CPCursor.j:272
#define SPLIT_VIEW_MAYBE_POST_WILL_RESIZE()
Definition: CPSplitView.j:57
id alloc()
Definition: CPObject.j:130
Definition: CPView.j:136
CPTrackingCursorUpdate
void resizeSubviewsWithOldSize:(CGSize oldSize)
Definition: CPSplitView.j:769
var CPSplitViewDelegate_splitView_canCollapseSubview_
Definition: CPSplitView.j:45
var CPSplitViewDelegate_splitView_constrainMaxCoordinate_ofSubviewAt_
Definition: CPSplitView.j:50
void trackDivider:(CPEvent anEvent)
Definition: CPSplitView.j:463