/AppKit/CPClipView.j

http://github.com/cacaodev/cappuccino · Unknown · 227 lines · 184 code · 43 blank · 0 comment · 0 complexity · 97b47db1861c930bcff79edfc83515e6 MD5 · raw file

  1. /*
  2. * CPClipView.j
  3. * AppKit
  4. *
  5. * Created by Francisco Tolmasky.
  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. @import "CPView.j"
  23. @class CPScrollView
  24. /*!
  25. @ingroup appkit
  26. @class CPClipView
  27. CPClipView allows you to define a clip rect and display only that portion of its containing view.
  28. It is used to hold the document view in a CPScrollView.
  29. */
  30. @implementation CPClipView : CPView
  31. {
  32. CPView _documentView;
  33. }
  34. /*!
  35. Sets the document view to be \c aView.
  36. @param aView the new document view. It's frame origin will be changed to \c (0,0) after calling this method.
  37. */
  38. - (void)setDocumentView:(CPView)aView
  39. {
  40. if (_documentView == aView)
  41. return;
  42. if (_documentView)
  43. [_documentView removeFromSuperview];
  44. _documentView = aView;
  45. if (_documentView)
  46. [self addSubview:_documentView];
  47. }
  48. /*!
  49. Returns the document view.
  50. */
  51. - (id)documentView
  52. {
  53. return _documentView;
  54. }
  55. /*!
  56. Returns a new point that may be adjusted from \c aPoint
  57. to make sure it lies within the document view.
  58. @param aPoint
  59. @return the adjusted point
  60. */
  61. - (CGPoint)constrainScrollPoint:(CGPoint)aPoint
  62. {
  63. if (!_documentView)
  64. return CGPointMakeZero();
  65. var documentFrame = [_documentView frame];
  66. aPoint.x = MAX(0.0, MIN(aPoint.x, MAX(CGRectGetWidth(documentFrame) - CGRectGetWidth(_bounds), 0.0)));
  67. aPoint.y = MAX(0.0, MIN(aPoint.y, MAX(CGRectGetHeight(documentFrame) - CGRectGetHeight(_bounds), 0.0)));
  68. return aPoint;
  69. }
  70. - (void)setBoundsOrigin:(CGPoint)aPoint
  71. {
  72. if (CGPointEqualToPoint(_bounds.origin, aPoint))
  73. return;
  74. [super setBoundsOrigin:aPoint];
  75. var superview = [self superview],
  76. // This is hack to avoid having to import CPScrollView.
  77. // FIXME: Should CPScrollView be finding out about this on its own somehow?
  78. scrollViewClass = objj_getClass("CPScrollView");
  79. if ([superview isKindOfClass:scrollViewClass])
  80. [superview reflectScrolledClipView:self];
  81. }
  82. /*!
  83. Scrolls the clip view to the specified point. The method
  84. sets its bounds origin to \c aPoint.
  85. */
  86. - (void)scrollToPoint:(CGPoint)aPoint
  87. {
  88. [self setBoundsOrigin:[self constrainScrollPoint:aPoint]];
  89. }
  90. /*!
  91. Handles a CPViewBoundsDidChangeNotification.
  92. @param aNotification the notification event
  93. */
  94. - (void)viewBoundsChanged:(CPNotification)aNotification
  95. {
  96. [self _constrainScrollPoint];
  97. }
  98. /*!
  99. Handles a CPViewFrameDidChangeNotification.
  100. @param aNotification the notification event
  101. */
  102. - (void)viewFrameChanged:(CPNotification)aNotification
  103. {
  104. [self _constrainScrollPoint];
  105. }
  106. - (void)resizeSubviewsWithOldSize:(CGSize)aSize
  107. {
  108. [super resizeSubviewsWithOldSize:aSize];
  109. [self _constrainScrollPoint];
  110. }
  111. - (void)_constrainScrollPoint
  112. {
  113. var oldScrollPoint = [self bounds].origin;
  114. // Call scrollToPoint: because the current scroll point may no longer make
  115. // sense given the new frame of the document view.
  116. [self scrollToPoint:oldScrollPoint];
  117. // scrollToPoint: takes care of reflectScrollClipView: for us, so bail if
  118. // the scroll points are not equal (meaning scrollToPoint: didn't early bail).
  119. if (!CGPointEqualToPoint(oldScrollPoint, [self bounds].origin))
  120. return;
  121. // ... and we're in a scroll view of course.
  122. var superview = [self superview],
  123. // This is hack to avoid having to import CPScrollView.
  124. // FIXME: Should CPScrollView be finding out about this on its own somehow?
  125. scrollViewClass = objj_getClass("CPScrollView");
  126. if ([superview isKindOfClass:scrollViewClass])
  127. [superview reflectScrolledClipView:self];
  128. }
  129. - (BOOL)autoscroll:(CPEvent)anEvent
  130. {
  131. var bounds = [self bounds],
  132. eventLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
  133. superview = [self superview],
  134. deltaX = 0,
  135. deltaY = 0;
  136. if (CGRectContainsPoint(bounds, eventLocation))
  137. return NO;
  138. if (![superview isKindOfClass:[CPScrollView class]] || [superview hasVerticalScroller])
  139. {
  140. if (eventLocation.y < CGRectGetMinY(bounds))
  141. deltaY = CGRectGetMinY(bounds) - eventLocation.y;
  142. else if (eventLocation.y > CGRectGetMaxY(bounds))
  143. deltaY = CGRectGetMaxY(bounds) - eventLocation.y;
  144. if (deltaY < -bounds.size.height)
  145. deltaY = -bounds.size.height;
  146. if (deltaY > bounds.size.height)
  147. deltaY = bounds.size.height;
  148. }
  149. if (![superview isKindOfClass:[CPScrollView class]] || [superview hasHorizontalScroller])
  150. {
  151. if (eventLocation.x < CGRectGetMinX(bounds))
  152. deltaX = CGRectGetMinX(bounds) - eventLocation.x;
  153. else if (eventLocation.x > CGRectGetMaxX(bounds))
  154. deltaX = CGRectGetMaxX(bounds) - eventLocation.x;
  155. if (deltaX < -bounds.size.width)
  156. deltaX = -bounds.size.width;
  157. if (deltaX > bounds.size.width)
  158. deltaX = bounds.size.width;
  159. }
  160. return [self scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
  161. }
  162. - (CGRect)documentVisibleRect
  163. {
  164. return [self convertRect:[self bounds] fromView:_documentView];
  165. }
  166. @end
  167. var CPClipViewDocumentViewKey = @"CPScrollViewDocumentView";
  168. @implementation CPClipView (CPCoding)
  169. - (id)initWithCoder:(CPCoder)aCoder
  170. {
  171. if (self = [super initWithCoder:aCoder])
  172. {
  173. // Don't call setDocumentView: here. It calls addSubview:, but it's A) not necessary since the
  174. // view hierarchy is fully encoded and B) dangerous if the subview is not fully decoded.
  175. _documentView = [aCoder decodeObjectForKey:CPClipViewDocumentViewKey];
  176. }
  177. return self;
  178. }
  179. - (void)encodeWithCoder:(CPCoder)aCoder
  180. {
  181. [super encodeWithCoder:aCoder];
  182. [aCoder encodeObject:_documentView forKey:CPClipViewDocumentViewKey];
  183. }
  184. @end