240 lines
		
	
	
	
		
			7.4 KiB
			
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
	
		
			7.4 KiB
			
		
	
	
	
		
			Text
		
	
	
	
	
	
// Copyright (c) 2017 GitHub, Inc.
 | 
						|
// Use of this source code is governed by the MIT license that can be
 | 
						|
// found in the LICENSE file.
 | 
						|
 | 
						|
#include "atom/browser/native_browser_view_mac.h"
 | 
						|
 | 
						|
#include "brightray/browser/inspectable_web_contents.h"
 | 
						|
#include "brightray/browser/inspectable_web_contents_view.h"
 | 
						|
#include "skia/ext/skia_utils_mac.h"
 | 
						|
#include "ui/gfx/geometry/rect.h"
 | 
						|
 | 
						|
// Match view::Views behavior where the view sticks to the top-left origin.
 | 
						|
const NSAutoresizingMaskOptions kDefaultAutoResizingMask =
 | 
						|
    NSViewMaxXMargin | NSViewMinYMargin;
 | 
						|
 | 
						|
@interface DragRegionView : NSView
 | 
						|
 | 
						|
@property(assign) NSPoint initialLocation;
 | 
						|
 | 
						|
@end
 | 
						|
 | 
						|
@interface NSWindow ()
 | 
						|
- (void)performWindowDragWithEvent:(NSEvent*)event;
 | 
						|
@end
 | 
						|
 | 
						|
@implementation DragRegionView
 | 
						|
 | 
						|
@synthesize initialLocation;
 | 
						|
 | 
						|
- (BOOL)mouseDownCanMoveWindow {
 | 
						|
  return NO;
 | 
						|
}
 | 
						|
 | 
						|
- (NSView*)hitTest:(NSPoint)aPoint {
 | 
						|
  // Pass-through events that don't hit one of the exclusion zones
 | 
						|
  for (NSView* exlusion_zones in [self subviews]) {
 | 
						|
    if ([exlusion_zones hitTest:aPoint])
 | 
						|
      return nil;
 | 
						|
  }
 | 
						|
 | 
						|
  return self;
 | 
						|
}
 | 
						|
 | 
						|
- (void)mouseDown:(NSEvent*)event {
 | 
						|
  if ([self.window respondsToSelector:@selector(performWindowDragWithEvent)]) {
 | 
						|
    // According to Google, using performWindowDragWithEvent:
 | 
						|
    // does not generate a NSWindowWillMoveNotification. Hence post one.
 | 
						|
    [[NSNotificationCenter defaultCenter]
 | 
						|
        postNotificationName:NSWindowWillMoveNotification
 | 
						|
                      object:self];
 | 
						|
 | 
						|
    if (@available(macOS 10.11, *)) {
 | 
						|
      [self.window performWindowDragWithEvent:event];
 | 
						|
    }
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (self.window.styleMask & NSFullScreenWindowMask) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  self.initialLocation = [event locationInWindow];
 | 
						|
}
 | 
						|
 | 
						|
- (void)mouseDragged:(NSEvent*)theEvent {
 | 
						|
  if ([self.window respondsToSelector:@selector(performWindowDragWithEvent)]) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (self.window.styleMask & NSFullScreenWindowMask) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  NSPoint currentLocation = [NSEvent mouseLocation];
 | 
						|
  NSPoint newOrigin;
 | 
						|
 | 
						|
  NSRect screenFrame = [[NSScreen mainScreen] frame];
 | 
						|
  NSSize screenSize = screenFrame.size;
 | 
						|
  NSRect windowFrame = [self.window frame];
 | 
						|
  NSSize windowSize = windowFrame.size;
 | 
						|
 | 
						|
  newOrigin.x = currentLocation.x - self.initialLocation.x;
 | 
						|
  newOrigin.y = currentLocation.y - self.initialLocation.y;
 | 
						|
 | 
						|
  BOOL inMenuBar = (newOrigin.y + windowSize.height) >
 | 
						|
                   (screenFrame.origin.y + screenSize.height);
 | 
						|
  BOOL screenAboveMainScreen = false;
 | 
						|
 | 
						|
  if (inMenuBar) {
 | 
						|
    for (NSScreen* screen in [NSScreen screens]) {
 | 
						|
      NSRect currentScreenFrame = [screen frame];
 | 
						|
      BOOL isHigher = currentScreenFrame.origin.y > screenFrame.origin.y;
 | 
						|
 | 
						|
      // If there's another screen that is generally above the current screen,
 | 
						|
      // we'll draw a new rectangle that is just above the current screen. If
 | 
						|
      // the "higher" screen intersects with this rectangle, we'll allow drawing
 | 
						|
      // above the menubar.
 | 
						|
      if (isHigher) {
 | 
						|
        NSRect aboveScreenRect =
 | 
						|
            NSMakeRect(screenFrame.origin.x,
 | 
						|
                       screenFrame.origin.y + screenFrame.size.height - 10,
 | 
						|
                       screenFrame.size.width, 200);
 | 
						|
 | 
						|
        BOOL screenAboveIntersects =
 | 
						|
            NSIntersectsRect(currentScreenFrame, aboveScreenRect);
 | 
						|
 | 
						|
        if (screenAboveIntersects) {
 | 
						|
          screenAboveMainScreen = true;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Don't let window get dragged up under the menu bar
 | 
						|
  if (inMenuBar && !screenAboveMainScreen) {
 | 
						|
    newOrigin.y = screenFrame.origin.y +
 | 
						|
                  (screenFrame.size.height - windowFrame.size.height);
 | 
						|
  }
 | 
						|
 | 
						|
  // Move the window to the new location
 | 
						|
  [self.window setFrameOrigin:newOrigin];
 | 
						|
}
 | 
						|
 | 
						|
// Debugging tips:
 | 
						|
// Uncomment the following four lines to color DragRegionView bright red
 | 
						|
// #ifdef DEBUG_DRAG_REGIONS
 | 
						|
// - (void)drawRect:(NSRect)aRect
 | 
						|
// {
 | 
						|
//     [[NSColor redColor] set];
 | 
						|
//     NSRectFill([self bounds]);
 | 
						|
// }
 | 
						|
// #endif
 | 
						|
 | 
						|
@end
 | 
						|
 | 
						|
@interface ExcludeDragRegionView : NSView
 | 
						|
@end
 | 
						|
 | 
						|
@implementation ExcludeDragRegionView
 | 
						|
 | 
						|
- (BOOL)mouseDownCanMoveWindow {
 | 
						|
  return NO;
 | 
						|
}
 | 
						|
 | 
						|
// Debugging tips:
 | 
						|
// Uncomment the following four lines to color ExcludeDragRegionView bright red
 | 
						|
// #ifdef DEBUG_DRAG_REGIONS
 | 
						|
// - (void)drawRect:(NSRect)aRect
 | 
						|
// {
 | 
						|
//     [[NSColor greenColor] set];
 | 
						|
//     NSRectFill([self bounds]);
 | 
						|
// }
 | 
						|
// #endif
 | 
						|
 | 
						|
@end
 | 
						|
 | 
						|
namespace atom {
 | 
						|
 | 
						|
NativeBrowserViewMac::NativeBrowserViewMac(
 | 
						|
    brightray::InspectableWebContents* inspectable_web_contents)
 | 
						|
    : NativeBrowserView(inspectable_web_contents) {
 | 
						|
  auto* view = GetInspectableWebContentsView()->GetNativeView();
 | 
						|
  view.autoresizingMask = kDefaultAutoResizingMask;
 | 
						|
}
 | 
						|
 | 
						|
NativeBrowserViewMac::~NativeBrowserViewMac() {}
 | 
						|
 | 
						|
void NativeBrowserViewMac::SetAutoResizeFlags(uint8_t flags) {
 | 
						|
  NSAutoresizingMaskOptions autoresizing_mask = kDefaultAutoResizingMask;
 | 
						|
  if (flags & kAutoResizeWidth) {
 | 
						|
    autoresizing_mask |= NSViewWidthSizable;
 | 
						|
  }
 | 
						|
  if (flags & kAutoResizeHeight) {
 | 
						|
    autoresizing_mask |= NSViewHeightSizable;
 | 
						|
  }
 | 
						|
 | 
						|
  auto* view = GetInspectableWebContentsView()->GetNativeView();
 | 
						|
  view.autoresizingMask = autoresizing_mask;
 | 
						|
}
 | 
						|
 | 
						|
void NativeBrowserViewMac::SetBounds(const gfx::Rect& bounds) {
 | 
						|
  auto* view = GetInspectableWebContentsView()->GetNativeView();
 | 
						|
  auto* superview = view.superview;
 | 
						|
  const auto superview_height = superview ? superview.frame.size.height : 0;
 | 
						|
  view.frame =
 | 
						|
      NSMakeRect(bounds.x(), superview_height - bounds.y() - bounds.height(),
 | 
						|
                 bounds.width(), bounds.height());
 | 
						|
}
 | 
						|
 | 
						|
void NativeBrowserViewMac::SetBackgroundColor(SkColor color) {
 | 
						|
  auto* view = GetInspectableWebContentsView()->GetNativeView();
 | 
						|
  view.wantsLayer = YES;
 | 
						|
  view.layer.backgroundColor = skia::CGColorCreateFromSkColor(color);
 | 
						|
}
 | 
						|
 | 
						|
void NativeBrowserViewMac::UpdateDraggableRegions(
 | 
						|
    const std::vector<gfx::Rect>& drag_exclude_rects) {
 | 
						|
  NSView* web_view = GetWebContents()->GetNativeView();
 | 
						|
  NSView* inspectable_view = GetInspectableWebContentsView()->GetNativeView();
 | 
						|
  NSView* window_content_view = inspectable_view.superview;
 | 
						|
  const auto window_content_view_height = NSHeight(window_content_view.bounds);
 | 
						|
 | 
						|
  // Remove all DragRegionViews that were added last time. Note that we need
 | 
						|
  // to copy the `subviews` array to avoid mutation during iteration.
 | 
						|
  base::scoped_nsobject<NSArray> subviews([[web_view subviews] copy]);
 | 
						|
  for (NSView* subview in subviews.get()) {
 | 
						|
    if ([subview isKindOfClass:[DragRegionView class]]) {
 | 
						|
      [subview removeFromSuperview];
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Create one giant NSView that is draggable.
 | 
						|
  base::scoped_nsobject<NSView> drag_region_view(
 | 
						|
      [[DragRegionView alloc] initWithFrame:web_view.bounds]);
 | 
						|
  [web_view addSubview:drag_region_view];
 | 
						|
 | 
						|
  // Then, on top of that, add "exclusion zones"
 | 
						|
  for (const auto& rect : drag_exclude_rects) {
 | 
						|
    const auto window_content_view_exclude_rect =
 | 
						|
        NSMakeRect(rect.x(), window_content_view_height - rect.bottom(),
 | 
						|
                   rect.width(), rect.height());
 | 
						|
    const auto drag_region_view_exclude_rect =
 | 
						|
        [window_content_view convertRect:window_content_view_exclude_rect
 | 
						|
                                  toView:drag_region_view];
 | 
						|
 | 
						|
    base::scoped_nsobject<NSView> exclude_drag_region_view(
 | 
						|
        [[ExcludeDragRegionView alloc]
 | 
						|
            initWithFrame:drag_region_view_exclude_rect]);
 | 
						|
    [drag_region_view addSubview:exclude_drag_region_view];
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// static
 | 
						|
NativeBrowserView* NativeBrowserView::Create(
 | 
						|
    brightray::InspectableWebContents* inspectable_web_contents) {
 | 
						|
  return new NativeBrowserViewMac(inspectable_web_contents);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace atom
 |