native_browser_view_mac.mm 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // Copyright (c) 2017 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "atom/browser/native_browser_view_mac.h"
  5. #include "atom/browser/ui/inspectable_web_contents.h"
  6. #include "atom/browser/ui/inspectable_web_contents_view.h"
  7. #include "skia/ext/skia_utils_mac.h"
  8. #include "ui/gfx/geometry/rect.h"
  9. // Match view::Views behavior where the view sticks to the top-left origin.
  10. const NSAutoresizingMaskOptions kDefaultAutoResizingMask =
  11. NSViewMaxXMargin | NSViewMinYMargin;
  12. @interface DragRegionView : NSView
  13. @property(assign) NSPoint initialLocation;
  14. @end
  15. @interface NSWindow ()
  16. - (void)performWindowDragWithEvent:(NSEvent*)event;
  17. @end
  18. @implementation DragRegionView
  19. @synthesize initialLocation;
  20. - (BOOL)mouseDownCanMoveWindow {
  21. return NO;
  22. }
  23. - (NSView*)hitTest:(NSPoint)aPoint {
  24. // Pass-through events that don't hit one of the exclusion zones
  25. for (NSView* exlusion_zones in [self subviews]) {
  26. if ([exlusion_zones hitTest:aPoint])
  27. return nil;
  28. }
  29. return self;
  30. }
  31. - (void)mouseDown:(NSEvent*)event {
  32. if ([self.window respondsToSelector:@selector(performWindowDragWithEvent)]) {
  33. // According to Google, using performWindowDragWithEvent:
  34. // does not generate a NSWindowWillMoveNotification. Hence post one.
  35. [[NSNotificationCenter defaultCenter]
  36. postNotificationName:NSWindowWillMoveNotification
  37. object:self];
  38. if (@available(macOS 10.11, *)) {
  39. [self.window performWindowDragWithEvent:event];
  40. }
  41. return;
  42. }
  43. if (self.window.styleMask & NSWindowStyleMaskFullScreen) {
  44. return;
  45. }
  46. self.initialLocation = [event locationInWindow];
  47. }
  48. - (void)mouseDragged:(NSEvent*)theEvent {
  49. if ([self.window respondsToSelector:@selector(performWindowDragWithEvent)]) {
  50. return;
  51. }
  52. if (self.window.styleMask & NSWindowStyleMaskFullScreen) {
  53. return;
  54. }
  55. NSPoint currentLocation = [NSEvent mouseLocation];
  56. NSPoint newOrigin;
  57. NSRect screenFrame = [[NSScreen mainScreen] frame];
  58. NSSize screenSize = screenFrame.size;
  59. NSRect windowFrame = [self.window frame];
  60. NSSize windowSize = windowFrame.size;
  61. newOrigin.x = currentLocation.x - self.initialLocation.x;
  62. newOrigin.y = currentLocation.y - self.initialLocation.y;
  63. BOOL inMenuBar = (newOrigin.y + windowSize.height) >
  64. (screenFrame.origin.y + screenSize.height);
  65. BOOL screenAboveMainScreen = false;
  66. if (inMenuBar) {
  67. for (NSScreen* screen in [NSScreen screens]) {
  68. NSRect currentScreenFrame = [screen frame];
  69. BOOL isHigher = currentScreenFrame.origin.y > screenFrame.origin.y;
  70. // If there's another screen that is generally above the current screen,
  71. // we'll draw a new rectangle that is just above the current screen. If
  72. // the "higher" screen intersects with this rectangle, we'll allow drawing
  73. // above the menubar.
  74. if (isHigher) {
  75. NSRect aboveScreenRect =
  76. NSMakeRect(screenFrame.origin.x,
  77. screenFrame.origin.y + screenFrame.size.height - 10,
  78. screenFrame.size.width, 200);
  79. BOOL screenAboveIntersects =
  80. NSIntersectsRect(currentScreenFrame, aboveScreenRect);
  81. if (screenAboveIntersects) {
  82. screenAboveMainScreen = true;
  83. break;
  84. }
  85. }
  86. }
  87. }
  88. // Don't let window get dragged up under the menu bar
  89. if (inMenuBar && !screenAboveMainScreen) {
  90. newOrigin.y = screenFrame.origin.y +
  91. (screenFrame.size.height - windowFrame.size.height);
  92. }
  93. // Move the window to the new location
  94. [self.window setFrameOrigin:newOrigin];
  95. }
  96. // Debugging tips:
  97. // Uncomment the following four lines to color DragRegionView bright red
  98. // #ifdef DEBUG_DRAG_REGIONS
  99. // - (void)drawRect:(NSRect)aRect
  100. // {
  101. // [[NSColor redColor] set];
  102. // NSRectFill([self bounds]);
  103. // }
  104. // #endif
  105. @end
  106. @interface ExcludeDragRegionView : NSView
  107. @end
  108. @implementation ExcludeDragRegionView
  109. - (BOOL)mouseDownCanMoveWindow {
  110. return NO;
  111. }
  112. // Debugging tips:
  113. // Uncomment the following four lines to color ExcludeDragRegionView bright red
  114. // #ifdef DEBUG_DRAG_REGIONS
  115. // - (void)drawRect:(NSRect)aRect
  116. // {
  117. // [[NSColor greenColor] set];
  118. // NSRectFill([self bounds]);
  119. // }
  120. // #endif
  121. @end
  122. namespace atom {
  123. NativeBrowserViewMac::NativeBrowserViewMac(
  124. InspectableWebContents* inspectable_web_contents)
  125. : NativeBrowserView(inspectable_web_contents) {
  126. auto* view =
  127. GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
  128. view.autoresizingMask = kDefaultAutoResizingMask;
  129. }
  130. NativeBrowserViewMac::~NativeBrowserViewMac() {}
  131. void NativeBrowserViewMac::SetAutoResizeFlags(uint8_t flags) {
  132. NSAutoresizingMaskOptions autoresizing_mask = kDefaultAutoResizingMask;
  133. if (flags & kAutoResizeWidth) {
  134. autoresizing_mask |= NSViewWidthSizable;
  135. }
  136. if (flags & kAutoResizeHeight) {
  137. autoresizing_mask |= NSViewHeightSizable;
  138. }
  139. if (flags & kAutoResizeHorizontal) {
  140. autoresizing_mask |=
  141. NSViewMaxXMargin | NSViewMinXMargin | NSViewWidthSizable;
  142. }
  143. if (flags & kAutoResizeVertical) {
  144. autoresizing_mask |=
  145. NSViewMaxYMargin | NSViewMinYMargin | NSViewHeightSizable;
  146. }
  147. auto* view =
  148. GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
  149. view.autoresizingMask = autoresizing_mask;
  150. }
  151. void NativeBrowserViewMac::SetBounds(const gfx::Rect& bounds) {
  152. auto* view =
  153. GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
  154. auto* superview = view.superview;
  155. const auto superview_height = superview ? superview.frame.size.height : 0;
  156. view.frame =
  157. NSMakeRect(bounds.x(), superview_height - bounds.y() - bounds.height(),
  158. bounds.width(), bounds.height());
  159. }
  160. void NativeBrowserViewMac::SetBackgroundColor(SkColor color) {
  161. auto* view =
  162. GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
  163. view.wantsLayer = YES;
  164. view.layer.backgroundColor = skia::CGColorCreateFromSkColor(color);
  165. }
  166. void NativeBrowserViewMac::UpdateDraggableRegions(
  167. const std::vector<gfx::Rect>& drag_exclude_rects) {
  168. NSView* web_view = GetWebContents()->GetNativeView().GetNativeNSView();
  169. NSView* inspectable_view =
  170. GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
  171. NSView* window_content_view = inspectable_view.superview;
  172. const auto window_content_view_height = NSHeight(window_content_view.bounds);
  173. // Remove all DragRegionViews that were added last time. Note that we need
  174. // to copy the `subviews` array to avoid mutation during iteration.
  175. base::scoped_nsobject<NSArray> subviews([[web_view subviews] copy]);
  176. for (NSView* subview in subviews.get()) {
  177. if ([subview isKindOfClass:[DragRegionView class]]) {
  178. [subview removeFromSuperview];
  179. }
  180. }
  181. // Create one giant NSView that is draggable.
  182. base::scoped_nsobject<NSView> drag_region_view(
  183. [[DragRegionView alloc] initWithFrame:web_view.bounds]);
  184. [web_view addSubview:drag_region_view];
  185. // Then, on top of that, add "exclusion zones"
  186. for (const auto& rect : drag_exclude_rects) {
  187. const auto window_content_view_exclude_rect =
  188. NSMakeRect(rect.x(), window_content_view_height - rect.bottom(),
  189. rect.width(), rect.height());
  190. const auto drag_region_view_exclude_rect =
  191. [window_content_view convertRect:window_content_view_exclude_rect
  192. toView:drag_region_view];
  193. base::scoped_nsobject<NSView> exclude_drag_region_view(
  194. [[ExcludeDragRegionView alloc]
  195. initWithFrame:drag_region_view_exclude_rect]);
  196. [drag_region_view addSubview:exclude_drag_region_view];
  197. }
  198. }
  199. // static
  200. NativeBrowserView* NativeBrowserView::Create(
  201. InspectableWebContents* inspectable_web_contents) {
  202. return new NativeBrowserViewMac(inspectable_web_contents);
  203. }
  204. } // namespace atom