CTTabStripView.m 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. // Copyright (c) 2010 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE-chromium file.
  4. #import "CTTabStripView.h"
  5. #import "CTTabStripController.h"
  6. #import "URLDropTarget.h"
  7. #import <PDF_Reader_Pro-Swift.h>
  8. // ripped out from libbase mac_util.mm:
  9. static BOOL ShouldWindowsMiniaturizeOnDoubleClick() {
  10. // We use an undocumented method in Cocoa; if it doesn't exist, default to
  11. // |true|. If it ever goes away, we can do (using an undocumented pref key):
  12. // NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
  13. // return ![defaults objectForKey:@"AppleMiniaturizeOnDoubleClick"] ||
  14. // [defaults boolForKey:@"AppleMiniaturizeOnDoubleClick"];
  15. BOOL methodImplemented =
  16. [NSWindow respondsToSelector:@selector(_shouldMiniaturizeOnDoubleClick)];
  17. assert(methodImplemented); // if this happens, se discussion above
  18. return !methodImplemented ||
  19. [NSWindow performSelector:@selector(_shouldMiniaturizeOnDoubleClick)];
  20. }
  21. @implementation CTTabStripView {
  22. NSTimeInterval lastMouseUp_;
  23. // Handles being a drag-and-drop target.
  24. URLDropTargetHandler *dropHandler_;
  25. // Weak; the following come from the nib.
  26. __weak NewTabButton* addTabButton_;
  27. // Whether the drop-indicator arrow is shown, and if it is, the coordinate of
  28. // its tip.
  29. BOOL dropArrowShown_;
  30. NSPoint dropArrowPosition_;
  31. BOOL allowGradient_;
  32. }
  33. @synthesize addTabButton = addTabButton_;
  34. @synthesize dropArrowShown = dropArrowShown_;
  35. @synthesize dropArrowPosition = dropArrowPosition_;
  36. @synthesize allowGradient = allowGradient_;
  37. -(void)dealloc
  38. {
  39. [NSObject cancelPreviousPerformRequestsWithTarget:self];
  40. [self removeObserverForAppearanceChange];
  41. }
  42. - (void)mouseDown:(NSEvent *)event {
  43. if (event.clickCount == 2) {
  44. BOOL isMaximized = self.window.zoomed;
  45. if (isMaximized) {
  46. [self.window zoom:nil];
  47. } else {
  48. [self.window zoom:nil];
  49. }
  50. } else {
  51. [super mouseDown:event];
  52. }
  53. }
  54. - (id)initWithFrame:(NSRect)frame {
  55. self = [super initWithFrame:frame];
  56. if (self) {
  57. // Set lastMouseUp_ = -1000.0 so that timestamp-lastMouseUp_ is big unless
  58. // lastMouseUp_ has been reset.
  59. lastMouseUp_ = -1000.0;
  60. // Register to be an URL drop target.
  61. dropHandler_ = [[URLDropTargetHandler alloc] initWithView:self];
  62. self.wantsLayer = YES;
  63. [self addObserverForAppearanceChange];
  64. [self updateViewColor];
  65. }
  66. return self;
  67. }
  68. // Draw bottom border (a dark border and light highlight). Each tab is
  69. // responsible for mimicking this bottom border, unless it's the active
  70. // tab.
  71. - (void)drawBorder:(NSRect)bounds {
  72. NSRect borderRect, contentRect;
  73. borderRect = bounds;
  74. borderRect.origin.y = 1;
  75. borderRect.size.height = 1;
  76. [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set];
  77. NSRectFillUsingOperation(borderRect, NSCompositeSourceOver);
  78. NSDivideRect(bounds, &borderRect, &contentRect, 1, NSMinYEdge);
  79. // TODO: cache this color
  80. NSColor* bezelColor = [NSColor colorWithCalibratedWhite:0xf7/255.0
  81. alpha:1.0];
  82. [bezelColor set];
  83. NSRectFill(borderRect);
  84. NSRectFillUsingOperation(borderRect, NSCompositeSourceOver);
  85. }
  86. - (void)drawRect:(NSRect)rect {
  87. NSRect boundsRect = [self bounds];
  88. if (allowGradient_)
  89. {
  90. NSGraphicsContext* context = [NSGraphicsContext currentContext];
  91. [context saveGraphicsState];
  92. // Set up our clip.
  93. float cornerRadius = 4.0;
  94. [[NSBezierPath bezierPathWithRoundedRect:boundsRect
  95. xRadius:cornerRadius
  96. yRadius:cornerRadius] addClip];
  97. [[NSBezierPath bezierPathWithRect:rect] addClip];
  98. [super drawBackground];
  99. [context restoreGraphicsState];
  100. }
  101. // [self drawBorder:boundsRect];
  102. // Draw drop-indicator arrow (if appropriate).
  103. // TODO(viettrungluu): this is all a stop-gap measure.
  104. if ([self dropArrowShown]) {
  105. // Programmer art: an arrow parametrized by many knobs. Note that the arrow
  106. // points downwards (so understand "width" and "height" accordingly).
  107. // How many (pixels) to inset on the top/bottom.
  108. const CGFloat kArrowTopInset = 1.5;
  109. const CGFloat kArrowBottomInset = 1;
  110. // What proportion of the vertical space is dedicated to the arrow tip,
  111. // i.e., (arrow tip height)/(amount of vertical space).
  112. const CGFloat kArrowTipProportion = 0.5;
  113. // This is a slope, i.e., (arrow tip height)/(0.5 * arrow tip width).
  114. const CGFloat kArrowTipSlope = 1.2;
  115. // What proportion of the arrow tip width is the stem, i.e., (stem
  116. // width)/(arrow tip width).
  117. const CGFloat kArrowStemProportion = 0.33;
  118. NSPoint arrowTipPos = [self dropArrowPosition];
  119. arrowTipPos.y += kArrowBottomInset; // Inset on the bottom.
  120. // Height we have to work with (insetting on the top).
  121. CGFloat availableHeight =
  122. NSMaxY(boundsRect) - arrowTipPos.y - kArrowTopInset;
  123. assert(availableHeight >= 5);
  124. // Based on the knobs above, calculate actual dimensions which we'll need
  125. // for drawing.
  126. CGFloat arrowTipHeight = kArrowTipProportion * availableHeight;
  127. CGFloat arrowTipWidth = 2 * arrowTipHeight / kArrowTipSlope;
  128. CGFloat arrowStemHeight = availableHeight - arrowTipHeight;
  129. CGFloat arrowStemWidth = kArrowStemProportion * arrowTipWidth;
  130. CGFloat arrowStemInset = (arrowTipWidth - arrowStemWidth) / 2;
  131. // The line width is arbitrary, but our path really should be mitered.
  132. NSBezierPath* arrow = [NSBezierPath bezierPath];
  133. [arrow setLineJoinStyle:NSMiterLineJoinStyle];
  134. [arrow setLineWidth:1];
  135. // Define the arrow's shape! We start from the tip and go clockwise.
  136. [arrow moveToPoint:arrowTipPos];
  137. [arrow relativeLineToPoint:NSMakePoint(-arrowTipWidth / 2, arrowTipHeight)];
  138. [arrow relativeLineToPoint:NSMakePoint(arrowStemInset, 0)];
  139. [arrow relativeLineToPoint:NSMakePoint(0, arrowStemHeight)];
  140. [arrow relativeLineToPoint:NSMakePoint(arrowStemWidth, 0)];
  141. [arrow relativeLineToPoint:NSMakePoint(0, -arrowStemHeight)];
  142. [arrow relativeLineToPoint:NSMakePoint(arrowStemInset, 0)];
  143. [arrow closePath];
  144. // Draw and fill the arrow.
  145. // TODO: cache colors
  146. [[NSColor colorWithCalibratedWhite:0 alpha:0.67] set];
  147. [arrow stroke];
  148. [[NSColor colorWithCalibratedWhite:1 alpha:0.67] setFill];
  149. [arrow fill];
  150. }
  151. }
  152. // YES if a double-click in the background of the tab strip minimizes the
  153. // window.
  154. - (BOOL)doubleClickMinimizesWindow {
  155. return YES;
  156. }
  157. // We accept first mouse so clicks onto close/zoom/miniaturize buttons and
  158. // title bar double-clicks are properly detected even when the window is in the
  159. // background.
  160. - (BOOL)acceptsFirstMouse:(NSEvent*)event {
  161. return YES;
  162. }
  163. // Trap double-clicks and make them miniaturize the browser window.
  164. - (void)mouseUp:(NSEvent*)event {
  165. // Bail early if double-clicks are disabled.
  166. if (![self doubleClickMinimizesWindow]) {
  167. [super mouseUp:event];
  168. return;
  169. }
  170. NSInteger clickCount = [event clickCount];
  171. NSTimeInterval timestamp = [event timestamp];
  172. // Double-clicks on Zoom/Close/Mininiaturize buttons shouldn't cause
  173. // miniaturization. For those, we miss the first click but get the second
  174. // (with clickCount == 2!). We thus check that we got a first click shortly
  175. // before (measured up-to-up) a double-click. Cocoa doesn't have a documented
  176. // way of getting the proper interval (= (double-click-threshold) +
  177. // (drag-threshold); the former is Carbon GetDblTime()/60.0 or
  178. // com.apple.mouse.doubleClickThreshold [undocumented]). So we hard-code
  179. // "short" as 0.8 seconds. (Measuring up-to-up isn't enough to properly
  180. // detect double-clicks, but we're actually using Cocoa for that.)
  181. if (clickCount == 2 && (timestamp - lastMouseUp_) < 0.8) {
  182. if (ShouldWindowsMiniaturizeOnDoubleClick())
  183. [[self window] performMiniaturize:self];
  184. } else {
  185. [super mouseUp:event];
  186. }
  187. // If clickCount is 0, the drag threshold was passed.
  188. lastMouseUp_ = (clickCount == 1) ? timestamp : -1000.0;
  189. }
  190. // (URLDropTarget protocol)
  191. - (id<URLDropTargetController>)urlDropController {
  192. //CTBrowserWindowController* windowController = [[self window] windowController];
  193. //assert([windowController isKindOfClass:[CTBrowserWindowController class]]);
  194. //return [windowController tabStripController];
  195. return nil;
  196. }
  197. // (URLDropTarget protocol)
  198. - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
  199. return [dropHandler_ draggingEntered:sender];
  200. }
  201. // (URLDropTarget protocol)
  202. - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
  203. return [dropHandler_ draggingUpdated:sender];
  204. }
  205. // (URLDropTarget protocol)
  206. - (void)draggingExited:(id<NSDraggingInfo>)sender {
  207. return [dropHandler_ draggingExited:sender];
  208. }
  209. // (URLDropTarget protocol)
  210. - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
  211. return [dropHandler_ performDragOperation:sender];
  212. }
  213. - (BOOL)accessibilityIsIgnored {
  214. return NO;
  215. }
  216. - (id)accessibilityAttributeValue:(NSString*)attribute {
  217. if ([attribute isEqual:NSAccessibilityRoleAttribute])
  218. return NSAccessibilityGroupRole;
  219. return [super accessibilityAttributeValue:attribute];
  220. }
  221. #pragma mark - Dark&Light
  222. - (void)addObserverForAppearanceChange
  223. {
  224. [self addObserver:self forKeyPath:@"effectiveAppearance" options:NSKeyValueObservingOptionNew context:nil];
  225. }
  226. - (void)removeObserverForAppearanceChange
  227. {
  228. [self removeObserver:self forKeyPath:@"effectiveAppearance"];
  229. }
  230. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
  231. if ([keyPath isEqualToString:@"effectiveAppearance"]) {
  232. [self updateViewColor];
  233. }
  234. }
  235. -(void)updateViewColor
  236. {
  237. self.layer.backgroundColor = [KMTabAppearance tabsViewBackgroundColor].CGColor;
  238. }
  239. @end