Merge pull request #85 from atom/frameless-window
Add frameless window support, fixes #72.
This commit is contained in:
		
				commit
				
					
						eb6fa98ed0
					
				
			
		
					 15 changed files with 567 additions and 9 deletions
				
			
		
							
								
								
									
										2
									
								
								atom.gyp
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								atom.gyp
									
										
									
									
									
								
							|  | @ -139,6 +139,8 @@ | |||
|       'common/api/atom_extensions.h', | ||||
|       'common/api/object_life_monitor.cc', | ||||
|       'common/api/object_life_monitor.h', | ||||
|       'common/draggable_region.cc', | ||||
|       'common/draggable_region.h', | ||||
|       'common/node_bindings.cc', | ||||
|       'common/node_bindings.h', | ||||
|       'common/node_bindings_mac.cc', | ||||
|  |  | |||
|  | @ -1,7 +1,14 @@ | |||
| var app = require('app'); | ||||
| var argv = require('optimist').argv; | ||||
| var dialog = require('dialog'); | ||||
| var path = require('path'); | ||||
| 
 | ||||
| // Quit when all windows are closed and no other one is listening to this.
 | ||||
| app.on('window-all-closed', function() { | ||||
|   if (app.listeners('window-all-closed').length == 1) | ||||
|     app.quit(); | ||||
| }); | ||||
| 
 | ||||
| // Start the specified app if there is one specified in command line, otherwise
 | ||||
| // start the default app.
 | ||||
| if (argv._.length > 0) { | ||||
|  | @ -9,6 +16,7 @@ if (argv._.length > 0) { | |||
|     require(path.resolve(argv._[0])); | ||||
|   } catch(e) { | ||||
|     if (e.code == 'MODULE_NOT_FOUND') { | ||||
|       console.error(e.stack); | ||||
|       console.error('Specified app is invalid'); | ||||
|       process.exit(1); | ||||
|     } else { | ||||
|  |  | |||
|  | @ -39,10 +39,13 @@ namespace atom { | |||
| NativeWindow::NativeWindow(content::WebContents* web_contents, | ||||
|                            base::DictionaryValue* options) | ||||
|     : content::WebContentsObserver(web_contents), | ||||
|       has_frame_(true), | ||||
|       is_closed_(false), | ||||
|       not_responding_(false), | ||||
|       inspectable_web_contents_( | ||||
|           brightray::InspectableWebContents::Create(web_contents)) { | ||||
|   options->GetBoolean(switches::kFrame, &has_frame_); | ||||
| 
 | ||||
|   web_contents->SetDelegate(this); | ||||
| 
 | ||||
|   WindowList::AddWindow(this); | ||||
|  | @ -298,6 +301,8 @@ bool NativeWindow::OnMessageReceived(const IPC::Message& message) { | |||
|   IPC_BEGIN_MESSAGE_MAP(NativeWindow, message) | ||||
|     IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage) | ||||
|     IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message_Sync, OnRendererMessageSync) | ||||
|     IPC_MESSAGE_HANDLER(AtomViewHostMsg_UpdateDraggableRegions, | ||||
|                         UpdateDraggableRegions) | ||||
|     IPC_MESSAGE_UNHANDLED(handled = false) | ||||
|   IPC_END_MESSAGE_MAP() | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,6 +38,7 @@ class Size; | |||
| namespace atom { | ||||
| 
 | ||||
| class AtomJavaScriptDialogManager; | ||||
| struct DraggableRegion; | ||||
| 
 | ||||
| class NativeWindow : public brightray::DefaultWebContentsDelegate, | ||||
|                      public content::WebContentsObserver, | ||||
|  | @ -125,6 +126,10 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, | |||
|   void NotifyWindowClosed(); | ||||
|   void NotifyWindowBlur(); | ||||
| 
 | ||||
|   // Called when the window needs to update its draggable region.
 | ||||
|   virtual void UpdateDraggableRegions( | ||||
|       const std::vector<DraggableRegion>& regions) = 0; | ||||
| 
 | ||||
|   // Implementations of content::WebContentsDelegate.
 | ||||
|   virtual void WebContentsCreated(content::WebContents* source_contents, | ||||
|                                   int64 source_frame_id, | ||||
|  | @ -160,6 +165,9 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, | |||
|                        const content::NotificationSource& source, | ||||
|                        const content::NotificationDetails& details) OVERRIDE; | ||||
| 
 | ||||
|   // Whether window has standard frame.
 | ||||
|   bool has_frame_; | ||||
| 
 | ||||
|  private: | ||||
|   void RendererUnresponsiveDelayed(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,6 +10,8 @@ | |||
| #include "base/memory/scoped_ptr.h" | ||||
| #include "browser/native_window.h" | ||||
| 
 | ||||
| class SkRegion; | ||||
| 
 | ||||
| namespace atom { | ||||
| 
 | ||||
| class NativeWindowMac : public NativeWindow { | ||||
|  | @ -52,11 +54,22 @@ class NativeWindowMac : public NativeWindow { | |||
|   virtual bool IsKiosk() OVERRIDE; | ||||
|   virtual gfx::NativeWindow GetNativeWindow() OVERRIDE; | ||||
| 
 | ||||
|   NSWindow*& window() { return window_; } | ||||
| 
 | ||||
|   void NotifyWindowBlur() { NativeWindow::NotifyWindowBlur(); } | ||||
| 
 | ||||
|   // Returns true if |point| in local Cocoa coordinate system falls within
 | ||||
|   // the draggable region.
 | ||||
|   bool IsWithinDraggableRegion(NSPoint point) const; | ||||
| 
 | ||||
|   // Called to handle a mouse event.
 | ||||
|   void HandleMouseEvent(NSEvent* event); | ||||
| 
 | ||||
|   NSWindow*& window() { return window_; } | ||||
|   SkRegion* draggable_region() const { return draggable_region_.get(); } | ||||
| 
 | ||||
|  protected: | ||||
|   virtual void UpdateDraggableRegions( | ||||
|       const std::vector<DraggableRegion>& regions) OVERRIDE; | ||||
| 
 | ||||
|   // Implementations of content::WebContentsDelegate.
 | ||||
|   virtual void HandleKeyboardEvent( | ||||
|       content::WebContents*, | ||||
|  | @ -65,6 +78,9 @@ class NativeWindowMac : public NativeWindow { | |||
|  private: | ||||
|   void InstallView(); | ||||
|   void UninstallView(); | ||||
|   void InstallDraggableRegionViews(); | ||||
|   void UpdateDraggableRegionsForCustomDrag( | ||||
|       const std::vector<DraggableRegion>& regions); | ||||
| 
 | ||||
|   NSWindow* window_; | ||||
| 
 | ||||
|  | @ -72,6 +88,18 @@ class NativeWindowMac : public NativeWindow { | |||
| 
 | ||||
|   NSInteger attention_request_id_;  // identifier from requestUserAttention
 | ||||
| 
 | ||||
|   // For system drag, the whole window is draggable and the non-draggable areas
 | ||||
|   // have to been explicitly excluded.
 | ||||
|   std::vector<gfx::Rect> system_drag_exclude_areas_; | ||||
| 
 | ||||
|   // For custom drag, the whole window is non-draggable and the draggable region
 | ||||
|   // has to been explicitly provided.
 | ||||
|   scoped_ptr<SkRegion> draggable_region_;  // used in custom drag.
 | ||||
| 
 | ||||
|   // Mouse location since the last mouse event, in screen coordinates. This is
 | ||||
|   // used in custom drag to compute the window movement.
 | ||||
|   NSPoint last_mouse_offset_; | ||||
| 
 | ||||
|   DISALLOW_COPY_AND_ASSIGN(NativeWindowMac); | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,12 +16,25 @@ | |||
| #import "browser/atom_event_processing_window.h" | ||||
| #include "brightray/browser/inspectable_web_contents.h" | ||||
| #include "brightray/browser/inspectable_web_contents_view.h" | ||||
| #include "common/draggable_region.h" | ||||
| #include "common/options_switches.h" | ||||
| #include "content/public/browser/native_web_keyboard_event.h" | ||||
| #include "content/public/browser/web_contents.h" | ||||
| #include "content/public/browser/web_contents_view.h" | ||||
| #include "content/public/browser/render_view_host.h" | ||||
| 
 | ||||
| @interface NSWindow (NSPrivateApis) | ||||
| - (void)setBottomCornerRounded:(BOOL)rounded; | ||||
| @end | ||||
| 
 | ||||
| @interface NSView (WebContentsView) | ||||
| - (void)setMouseDownCanMoveWindow:(BOOL)can_move; | ||||
| @end | ||||
| 
 | ||||
| @interface NSView (PrivateMethods) | ||||
| - (CGFloat)roundedCornerRadius; | ||||
| @end | ||||
| 
 | ||||
| @interface AtomNSWindowDelegate : NSObject<NSWindowDelegate> { | ||||
|  @private | ||||
|   atom::NativeWindowMac* shell_; | ||||
|  | @ -80,6 +93,83 @@ | |||
| 
 | ||||
| @end | ||||
| 
 | ||||
| @interface AtomFramelessNSWindow : AtomNSWindow | ||||
| - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view; | ||||
| @end | ||||
| 
 | ||||
| @implementation AtomFramelessNSWindow | ||||
| 
 | ||||
| - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view { | ||||
|   [[NSBezierPath bezierPathWithRect:rect] addClip]; | ||||
|   [[NSColor clearColor] set]; | ||||
|   NSRectFill(rect); | ||||
| 
 | ||||
|   // Set up our clip. | ||||
|   CGFloat cornerRadius = 4.0; | ||||
|   if ([view respondsToSelector:@selector(roundedCornerRadius)]) | ||||
|     cornerRadius = [view roundedCornerRadius]; | ||||
|   [[NSBezierPath bezierPathWithRoundedRect:[view bounds] | ||||
|                                    xRadius:cornerRadius | ||||
|                                    yRadius:cornerRadius] addClip]; | ||||
|   [[NSColor whiteColor] set]; | ||||
|   NSRectFill(rect); | ||||
| } | ||||
| 
 | ||||
| + (NSRect)frameRectForContentRect:(NSRect)contentRect | ||||
|                         styleMask:(NSUInteger)mask { | ||||
|   return contentRect; | ||||
| } | ||||
| 
 | ||||
| + (NSRect)contentRectForFrameRect:(NSRect)frameRect | ||||
|                         styleMask:(NSUInteger)mask { | ||||
|   return frameRect; | ||||
| } | ||||
| 
 | ||||
| - (NSRect)frameRectForContentRect:(NSRect)contentRect { | ||||
|   return contentRect; | ||||
| } | ||||
| 
 | ||||
| - (NSRect)contentRectForFrameRect:(NSRect)frameRect { | ||||
|   return frameRect; | ||||
| } | ||||
| 
 | ||||
| @end | ||||
| 
 | ||||
| @interface ControlRegionView : NSView { | ||||
|  @private | ||||
|   atom::NativeWindowMac* shellWindow_;  // Weak; owns self. | ||||
| } | ||||
| @end | ||||
| 
 | ||||
| @implementation ControlRegionView | ||||
| 
 | ||||
| - (id)initWithShellWindow:(atom::NativeWindowMac*)shellWindow { | ||||
|   if ((self = [super init])) | ||||
|     shellWindow_ = shellWindow; | ||||
|   return self; | ||||
| } | ||||
| 
 | ||||
| - (BOOL)mouseDownCanMoveWindow { | ||||
|   return NO; | ||||
| } | ||||
| 
 | ||||
| - (NSView*)hitTest:(NSPoint)aPoint { | ||||
|   if (!shellWindow_->IsWithinDraggableRegion(aPoint)) { | ||||
|     return nil; | ||||
|   } | ||||
|   return self; | ||||
| } | ||||
| 
 | ||||
| - (void)mouseDown:(NSEvent*)event { | ||||
|   shellWindow_->HandleMouseEvent(event); | ||||
| } | ||||
| 
 | ||||
| - (void)mouseDragged:(NSEvent*)event { | ||||
|   shellWindow_->HandleMouseEvent(event); | ||||
| } | ||||
| 
 | ||||
| @end | ||||
| 
 | ||||
| namespace atom { | ||||
| 
 | ||||
| NativeWindowMac::NativeWindowMac(content::WebContents* web_contents, | ||||
|  | @ -100,7 +190,13 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents, | |||
|   NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask | | ||||
|                           NSMiniaturizableWindowMask | NSResizableWindowMask | | ||||
|                           NSTexturedBackgroundWindowMask; | ||||
|   AtomNSWindow* atom_window = [[AtomNSWindow alloc] | ||||
|   AtomNSWindow* atom_window = has_frame_ ? | ||||
|     [[AtomNSWindow alloc] | ||||
|       initWithContentRect:cocoa_bounds | ||||
|                 styleMask:style_mask | ||||
|                   backing:NSBackingStoreBuffered | ||||
|                     defer:YES] : | ||||
|     [[AtomNSWindow alloc] | ||||
|       initWithContentRect:cocoa_bounds | ||||
|                 styleMask:style_mask | ||||
|                   backing:NSBackingStoreBuffered | ||||
|  | @ -119,6 +215,9 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents, | |||
|     [window() setCollectionBehavior:collectionBehavior]; | ||||
|   } | ||||
| 
 | ||||
|   NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); | ||||
|   [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; | ||||
| 
 | ||||
|   InstallView(); | ||||
| } | ||||
| 
 | ||||
|  | @ -324,6 +423,43 @@ gfx::NativeWindow NativeWindowMac::GetNativeWindow() { | |||
|   return window(); | ||||
| } | ||||
| 
 | ||||
| bool NativeWindowMac::IsWithinDraggableRegion(NSPoint point) const { | ||||
|   if (!draggable_region_) | ||||
|     return false; | ||||
|   NSView* webView = web_contents()->GetView()->GetNativeView(); | ||||
|   NSInteger webViewHeight = NSHeight([webView bounds]); | ||||
|   // |draggable_region_| is stored in local platform-indepdent coordiate system | ||||
|   // while |point| is in local Cocoa coordinate system. Do the conversion | ||||
|   // to match these two. | ||||
|   return draggable_region_->contains(point.x, webViewHeight - point.y); | ||||
| } | ||||
| 
 | ||||
| void NativeWindowMac::HandleMouseEvent(NSEvent* event) { | ||||
|   NSPoint current_mouse_location = | ||||
|       [window() convertBaseToScreen:[event locationInWindow]]; | ||||
| 
 | ||||
|   if ([event type] == NSLeftMouseDown) { | ||||
|     NSPoint frame_origin = [window() frame].origin; | ||||
|     last_mouse_offset_ = NSMakePoint( | ||||
|         frame_origin.x - current_mouse_location.x, | ||||
|         frame_origin.y - current_mouse_location.y); | ||||
|   } else if ([event type] == NSLeftMouseDragged) { | ||||
|     [window() setFrameOrigin:NSMakePoint( | ||||
|         current_mouse_location.x + last_mouse_offset_.x, | ||||
|         current_mouse_location.y + last_mouse_offset_.y)]; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void NativeWindowMac::UpdateDraggableRegions( | ||||
|     const std::vector<DraggableRegion>& regions) { | ||||
|   // Draggable region is not supported for non-frameless window. | ||||
|   if (has_frame_) | ||||
|     return; | ||||
| 
 | ||||
|   UpdateDraggableRegionsForCustomDrag(regions); | ||||
|   InstallDraggableRegionViews(); | ||||
| } | ||||
| 
 | ||||
| void NativeWindowMac::HandleKeyboardEvent( | ||||
|     content::WebContents*, | ||||
|     const content::NativeWebKeyboardEvent& event) { | ||||
|  | @ -339,16 +475,87 @@ void NativeWindowMac::HandleKeyboardEvent( | |||
| 
 | ||||
| void NativeWindowMac::InstallView() { | ||||
|   NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); | ||||
|   [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; | ||||
|   if (has_frame_) { | ||||
|     [view setFrame:[[window() contentView] bounds]]; | ||||
|     [[window() contentView] addSubview:view]; | ||||
|   } else { | ||||
|     NSView* frameView = [[window() contentView] superview]; | ||||
|     [view setFrame:[frameView bounds]]; | ||||
|     [frameView addSubview:view]; | ||||
| 
 | ||||
|     [[window() standardWindowButton:NSWindowZoomButton] setHidden:YES]; | ||||
|     [[window() standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES]; | ||||
|     [[window() standardWindowButton:NSWindowCloseButton] setHidden:YES]; | ||||
|     [[window() standardWindowButton:NSWindowFullScreenButton] setHidden:YES]; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void NativeWindowMac::UninstallView() { | ||||
|   NSView* view = GetWebContents()->GetView()->GetNativeView(); | ||||
|   NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); | ||||
|   [view removeFromSuperview]; | ||||
| } | ||||
| 
 | ||||
| void NativeWindowMac::InstallDraggableRegionViews() { | ||||
|   DCHECK(!has_frame_); | ||||
| 
 | ||||
|   // All ControlRegionViews should be added as children of the WebContentsView, | ||||
|   // because WebContentsView will be removed and re-added when entering and | ||||
|   // leaving fullscreen mode. | ||||
|   NSView* webView = GetWebContents()->GetView()->GetNativeView(); | ||||
|   NSInteger webViewHeight = NSHeight([webView bounds]); | ||||
| 
 | ||||
|   // Remove all ControlRegionViews that are added last time. | ||||
|   // Note that [webView subviews] returns the view's mutable internal array and | ||||
|   // it should be copied to avoid mutating the original array while enumerating | ||||
|   // it. | ||||
|   scoped_nsobject<NSArray> subviews([[webView subviews] copy]); | ||||
|   for (NSView* subview in subviews.get()) | ||||
|     if ([subview isKindOfClass:[ControlRegionView class]]) | ||||
|       [subview removeFromSuperview]; | ||||
| 
 | ||||
|   // Create and add ControlRegionView for each region that needs to be excluded | ||||
|   // from the dragging. | ||||
|   for (std::vector<gfx::Rect>::const_iterator iter = | ||||
|            system_drag_exclude_areas_.begin(); | ||||
|        iter != system_drag_exclude_areas_.end(); | ||||
|        ++iter) { | ||||
|     scoped_nsobject<NSView> controlRegion( | ||||
|         [[ControlRegionView alloc] initWithShellWindow:this]); | ||||
|     [controlRegion setFrame:NSMakeRect(iter->x(), | ||||
|                                        webViewHeight - iter->bottom(), | ||||
|                                        iter->width(), | ||||
|                                        iter->height())]; | ||||
|     [webView addSubview:controlRegion]; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void NativeWindowMac::UpdateDraggableRegionsForCustomDrag( | ||||
|     const std::vector<DraggableRegion>& regions) { | ||||
|   // We still need one ControlRegionView to cover the whole window such that | ||||
|   // mouse events could be captured. | ||||
|   NSView* web_view = GetWebContents()->GetView()->GetNativeView(); | ||||
|   gfx::Rect window_bounds( | ||||
|       0, 0, NSWidth([web_view bounds]), NSHeight([web_view bounds])); | ||||
|   system_drag_exclude_areas_.clear(); | ||||
|   system_drag_exclude_areas_.push_back(window_bounds); | ||||
| 
 | ||||
|   // Aggregate the draggable areas and non-draggable areas such that hit test | ||||
|   // could be performed easily. | ||||
|   SkRegion* draggable_region = new SkRegion; | ||||
|   for (std::vector<DraggableRegion>::const_iterator iter = regions.begin(); | ||||
|        iter != regions.end(); | ||||
|        ++iter) { | ||||
|     const DraggableRegion& region = *iter; | ||||
|     draggable_region->op( | ||||
|         region.bounds.x(), | ||||
|         region.bounds.y(), | ||||
|         region.bounds.right(), | ||||
|         region.bounds.bottom(), | ||||
|         region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op); | ||||
|   } | ||||
|   draggable_region_.reset(draggable_region); | ||||
| } | ||||
| 
 | ||||
| // static | ||||
| NativeWindow* NativeWindow::Create(content::WebContents* web_contents, | ||||
|                                    base::DictionaryValue* options) { | ||||
|  |  | |||
|  | @ -6,8 +6,14 @@ | |||
| 
 | ||||
| #include "base/strings/utf_string_conversions.h" | ||||
| #include "base/values.h" | ||||
| #include "common/draggable_region.h" | ||||
| #include "common/options_switches.h" | ||||
| #include "content/public/browser/native_web_keyboard_event.h" | ||||
| #include "content/public/browser/render_view_host.h" | ||||
| #include "content/public/browser/render_widget_host_view.h" | ||||
| #include "content/public/browser/web_contents.h" | ||||
| #include "content/public/browser/web_contents_view.h" | ||||
| #include "ui/gfx/path.h" | ||||
| #include "ui/views/controls/webview/webview.h" | ||||
| #include "ui/views/widget/widget.h" | ||||
| #include "ui/views/window/client_view.h" | ||||
|  | @ -17,6 +23,9 @@ namespace atom { | |||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| const int kResizeInsideBoundsSize = 5; | ||||
| const int kResizeAreaCornerSize = 16; | ||||
| 
 | ||||
| class NativeWindowClientView : public views::ClientView { | ||||
|  public: | ||||
|   NativeWindowClientView(views::Widget* widget, | ||||
|  | @ -60,6 +69,99 @@ class NativeWindowFrameView : public views::NativeFrameView { | |||
|   DISALLOW_COPY_AND_ASSIGN(NativeWindowFrameView); | ||||
| }; | ||||
| 
 | ||||
| class NativeWindowFramelessView : public views::NonClientFrameView { | ||||
|  public: | ||||
|   explicit NativeWindowFramelessView(views::Widget* frame, | ||||
|                                      NativeWindowWin* shell) | ||||
|       : frame_(frame), | ||||
|         shell_(shell) { | ||||
|   } | ||||
|   virtual ~NativeWindowFramelessView() {} | ||||
| 
 | ||||
|   // views::NonClientFrameView implementations:
 | ||||
|   virtual gfx::Rect NativeWindowFramelessView::GetBoundsForClientView() const | ||||
|       OVERRIDE { | ||||
|     return bounds(); | ||||
|   } | ||||
| 
 | ||||
|   virtual gfx::Rect NativeWindowFramelessView::GetWindowBoundsForClientBounds( | ||||
|         const gfx::Rect& client_bounds) const OVERRIDE { | ||||
|     gfx::Rect window_bounds = client_bounds; | ||||
|     // Enforce minimum size (1, 1) in case that client_bounds is passed with
 | ||||
|     // empty size. This could occur when the frameless window is being
 | ||||
|     // initialized.
 | ||||
|     if (window_bounds.IsEmpty()) { | ||||
|       window_bounds.set_width(1); | ||||
|       window_bounds.set_height(1); | ||||
|     } | ||||
|     return window_bounds; | ||||
|   } | ||||
| 
 | ||||
|   virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE { | ||||
|     if (frame_->IsFullscreen()) | ||||
|       return HTCLIENT; | ||||
| 
 | ||||
|     // Check the frame first, as we allow a small area overlapping the contents
 | ||||
|     // to be used for resize handles.
 | ||||
|     bool can_ever_resize = frame_->widget_delegate() ? | ||||
|         frame_->widget_delegate()->CanResize() : | ||||
|         false; | ||||
|     // Don't allow overlapping resize handles when the window is maximized or
 | ||||
|     // fullscreen, as it can't be resized in those states.
 | ||||
|     int resize_border = | ||||
|         frame_->IsMaximized() || frame_->IsFullscreen() ? 0 : | ||||
|         kResizeInsideBoundsSize; | ||||
|     int frame_component = GetHTComponentForFrame(point, | ||||
|                                                  resize_border, | ||||
|                                                  resize_border, | ||||
|                                                  kResizeAreaCornerSize, | ||||
|                                                  kResizeAreaCornerSize, | ||||
|                                                  can_ever_resize); | ||||
|     if (frame_component != HTNOWHERE) | ||||
|       return frame_component; | ||||
| 
 | ||||
|     // Check for possible draggable region in the client area for the frameless
 | ||||
|     // window.
 | ||||
|     if (shell_->draggable_region() && | ||||
|         shell_->draggable_region()->contains(point.x(), point.y())) | ||||
|       return HTCAPTION; | ||||
| 
 | ||||
|     int client_component = frame_->client_view()->NonClientHitTest(point); | ||||
|     if (client_component != HTNOWHERE) | ||||
|       return client_component; | ||||
| 
 | ||||
|     // Caption is a safe default.
 | ||||
|     return HTCAPTION; | ||||
|   } | ||||
| 
 | ||||
|   virtual void GetWindowMask(const gfx::Size& size, | ||||
|                              gfx::Path* window_mask) OVERRIDE {} | ||||
|   virtual void ResetWindowControls() OVERRIDE {} | ||||
|   virtual void UpdateWindowIcon() OVERRIDE {} | ||||
|   virtual void UpdateWindowTitle() OVERRIDE {} | ||||
| 
 | ||||
|   // views::View implementations:
 | ||||
|   virtual gfx::Size NativeWindowFramelessView::GetPreferredSize() OVERRIDE { | ||||
|     gfx::Size pref = frame_->client_view()->GetPreferredSize(); | ||||
|     gfx::Rect bounds(0, 0, pref.width(), pref.height()); | ||||
|     return frame_->non_client_view()->GetWindowBoundsForClientBounds( | ||||
|         bounds).size(); | ||||
|   } | ||||
| 
 | ||||
|   virtual gfx::Size GetMinimumSize() OVERRIDE { | ||||
|     return shell_->GetMinimumSize(); | ||||
|   } | ||||
| 
 | ||||
|   virtual gfx::Size GetMaximumSize() OVERRIDE { | ||||
|     return shell_->GetMaximumSize(); | ||||
|   } | ||||
| 
 | ||||
|  private: | ||||
|   views::Widget* frame_; | ||||
|   NativeWindowWin* shell_; | ||||
| 
 | ||||
|   DISALLOW_COPY_AND_ASSIGN(NativeWindowFramelessView); | ||||
| }; | ||||
| 
 | ||||
| }  // namespace
 | ||||
| 
 | ||||
|  | @ -71,6 +173,7 @@ NativeWindowWin::NativeWindowWin(content::WebContents* web_contents, | |||
|       resizable_(true) { | ||||
|   views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); | ||||
|   params.delegate = this; | ||||
|   params.remove_standard_frame = !has_frame_; | ||||
|   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | ||||
|   window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_NATIVE); | ||||
|   window_->Init(params); | ||||
|  | @ -83,6 +186,7 @@ NativeWindowWin::NativeWindowWin(content::WebContents* web_contents, | |||
|   window_->CenterWindow(size); | ||||
| 
 | ||||
|   web_view_->SetWebContents(web_contents); | ||||
|   OnViewWasResized(); | ||||
| } | ||||
| 
 | ||||
| NativeWindowWin::~NativeWindowWin() { | ||||
|  | @ -221,6 +325,30 @@ gfx::NativeWindow NativeWindowWin::GetNativeWindow() { | |||
|   return window_->GetNativeView(); | ||||
| } | ||||
| 
 | ||||
| void NativeWindowWin::UpdateDraggableRegions( | ||||
|     const std::vector<DraggableRegion>& regions) { | ||||
|   if (has_frame_) | ||||
|     return; | ||||
| 
 | ||||
|   SkRegion* draggable_region = new SkRegion; | ||||
| 
 | ||||
|   // By default, the whole window is non-draggable. We need to explicitly
 | ||||
|   // include those draggable regions.
 | ||||
|   for (std::vector<DraggableRegion>::const_iterator iter = regions.begin(); | ||||
|        iter != regions.end(); ++iter) { | ||||
|     const DraggableRegion& region = *iter; | ||||
|     draggable_region->op( | ||||
|         region.bounds.x(), | ||||
|         region.bounds.y(), | ||||
|         region.bounds.right(), | ||||
|         region.bounds.bottom(), | ||||
|         region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op); | ||||
|   } | ||||
| 
 | ||||
|   draggable_region_.reset(draggable_region); | ||||
|   OnViewWasResized(); | ||||
| } | ||||
| 
 | ||||
| void NativeWindowWin::HandleKeyboardEvent( | ||||
|     content::WebContents*, | ||||
|     const content::NativeWebKeyboardEvent& event) { | ||||
|  | @ -264,7 +392,40 @@ views::ClientView* NativeWindowWin::CreateClientView(views::Widget* widget) { | |||
| 
 | ||||
| views::NonClientFrameView* NativeWindowWin::CreateNonClientFrameView( | ||||
|     views::Widget* widget) { | ||||
|   if (has_frame_) | ||||
|     return new NativeWindowFrameView(widget, this); | ||||
| 
 | ||||
|   return new NativeWindowFramelessView(widget, this); | ||||
| } | ||||
|   | ||||
| void NativeWindowWin::OnViewWasResized() { | ||||
|   // Set the window shape of the RWHV.
 | ||||
|   gfx::Size sz = web_view_->size(); | ||||
|   int height = sz.height(), width = sz.width(); | ||||
|   gfx::Path path; | ||||
|   path.addRect(0, 0, width, height); | ||||
|   SetWindowRgn(web_contents()->GetView()->GetNativeView(), | ||||
|                path.CreateNativeRegion(), | ||||
|                1); | ||||
| 
 | ||||
|   SkRegion* rgn = new SkRegion; | ||||
|   if (!window_->IsFullscreen() && !window_->IsMaximized()) { | ||||
|     if (draggable_region()) | ||||
|       rgn->op(*draggable_region(), SkRegion::kUnion_Op); | ||||
| 
 | ||||
|     if (!has_frame_ && CanResize()) { | ||||
|       rgn->op(0, 0, width, kResizeInsideBoundsSize, SkRegion::kUnion_Op); | ||||
|       rgn->op(0, 0, kResizeInsideBoundsSize, height, SkRegion::kUnion_Op); | ||||
|       rgn->op(width - kResizeInsideBoundsSize, 0, width, height, | ||||
|           SkRegion::kUnion_Op); | ||||
|       rgn->op(0, height - kResizeInsideBoundsSize, width, height, | ||||
|           SkRegion::kUnion_Op); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   content::WebContents* web_contents = GetWebContents(); | ||||
|   if (web_contents->GetRenderViewHost()->GetView()) | ||||
|     web_contents->GetRenderViewHost()->GetView()->SetClickthroughRegion(rgn); | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
|  |  | |||
|  | @ -60,7 +60,12 @@ class NativeWindowWin : public NativeWindow, | |||
|   virtual bool IsKiosk() OVERRIDE; | ||||
|   virtual gfx::NativeWindow GetNativeWindow() OVERRIDE; | ||||
| 
 | ||||
|   SkRegion* draggable_region() { return draggable_region_.get(); } | ||||
| 
 | ||||
|  protected: | ||||
|   virtual void UpdateDraggableRegions( | ||||
|       const std::vector<DraggableRegion>& regions) OVERRIDE; | ||||
| 
 | ||||
|   // Overridden from content::WebContentsDelegate:
 | ||||
|   virtual void HandleKeyboardEvent( | ||||
|       content::WebContents*, | ||||
|  | @ -79,9 +84,13 @@ class NativeWindowWin : public NativeWindow, | |||
|       views::Widget* widget) OVERRIDE; | ||||
| 
 | ||||
|  private: | ||||
|   void OnViewWasResized(); | ||||
| 
 | ||||
|   scoped_ptr<views::Widget> window_; | ||||
|   views::WebView* web_view_;  // managed by window_.
 | ||||
| 
 | ||||
|   scoped_ptr<SkRegion> draggable_region_; | ||||
| 
 | ||||
|   bool resizable_; | ||||
|   string16 title_; | ||||
|   gfx::Size minimum_size_; | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include <string> | ||||
| 
 | ||||
| #include "base/values.h" | ||||
| #include "common/draggable_region.h" | ||||
| #include "content/public/common/common_param_traits.h" | ||||
| #include "ipc/ipc_message_macros.h" | ||||
| 
 | ||||
|  | @ -15,6 +16,11 @@ | |||
| 
 | ||||
| #define IPC_MESSAGE_START ShellMsgStart | ||||
| 
 | ||||
| IPC_STRUCT_TRAITS_BEGIN(atom::DraggableRegion) | ||||
|   IPC_STRUCT_TRAITS_MEMBER(draggable) | ||||
|   IPC_STRUCT_TRAITS_MEMBER(bounds) | ||||
| IPC_STRUCT_TRAITS_END() | ||||
| 
 | ||||
| IPC_MESSAGE_ROUTED2(AtomViewHostMsg_Message, | ||||
|                     std::string /* channel */, | ||||
|                     ListValue /* arguments */) | ||||
|  | @ -27,3 +33,7 @@ IPC_SYNC_MESSAGE_ROUTED2_1(AtomViewHostMsg_Message_Sync, | |||
| IPC_MESSAGE_ROUTED2(AtomViewMsg_Message, | ||||
|                     std::string /* channel */, | ||||
|                     ListValue /* arguments */) | ||||
| 
 | ||||
| // Sent by the renderer when the draggable regions are updated.
 | ||||
| IPC_MESSAGE_ROUTED1(AtomViewHostMsg_UpdateDraggableRegions, | ||||
|                     std::vector<atom::DraggableRegion> /* regions */) | ||||
|  |  | |||
							
								
								
									
										13
									
								
								common/draggable_region.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								common/draggable_region.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| // Copyright (c) 2012 The Chromium Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style license that can be
 | ||||
| // found in the LICENSE file.
 | ||||
| 
 | ||||
| #include "common/draggable_region.h" | ||||
| 
 | ||||
| namespace atom { | ||||
| 
 | ||||
| DraggableRegion::DraggableRegion() | ||||
|     : draggable(false) { | ||||
| } | ||||
| 
 | ||||
| }  // namespace atom
 | ||||
							
								
								
									
										21
									
								
								common/draggable_region.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								common/draggable_region.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| // Copyright (c) 2012 The Chromium Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style license that can be
 | ||||
| // found in the LICENSE file.
 | ||||
| 
 | ||||
| #ifndef ATOM_COMMON_DRAGGABLE_REGION_H_ | ||||
| #define ATOM_COMMON_DRAGGABLE_REGION_H_ | ||||
| 
 | ||||
| #include "ui/gfx/rect.h" | ||||
| 
 | ||||
| namespace atom { | ||||
| 
 | ||||
| struct DraggableRegion { | ||||
|   bool draggable; | ||||
|   gfx::Rect bounds; | ||||
| 
 | ||||
|   DraggableRegion(); | ||||
| }; | ||||
| 
 | ||||
| }  // namespace atom
 | ||||
| 
 | ||||
| #endif  // ATOM_COMMON_DRAGGABLE_REGION_H_
 | ||||
|  | @ -1,4 +1,4 @@ | |||
| ## Synopsis | ||||
| # browser-window | ||||
| 
 | ||||
| The `BrowserWindow` class gives you ability to create a browser window, an | ||||
| example is: | ||||
|  | @ -15,6 +15,10 @@ win.loadUrl('https://github.com'); | |||
| win.show(); | ||||
| ``` | ||||
| 
 | ||||
| You can also create a window without chrome by using | ||||
| [Frameless Window](frameless-window.md) API. | ||||
| 
 | ||||
| 
 | ||||
| **Note:** Be careful not to use `window` as the variable name. | ||||
| 
 | ||||
| ## Class: BrowserWindow | ||||
|  | @ -38,6 +42,8 @@ win.show(); | |||
|   * `kiosk` Boolean - The kiosk mode | ||||
|   * `title` String - Default window title | ||||
|   * `show` Boolean - Whether window should be shown when created | ||||
|   * `frame` Boolean - Specify `false` to create a | ||||
|     [Frameless Window](frameless-window.md) | ||||
| 
 | ||||
| Creates a new `BrowserWindow` with native properties set by the `options`. | ||||
| Usually you only need to set the `width` and `height`, other properties will | ||||
|  |  | |||
							
								
								
									
										64
									
								
								docs/frameless-window.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								docs/frameless-window.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | |||
| # Frameless window | ||||
| 
 | ||||
| A frameless window is a window that has no chrome. | ||||
| 
 | ||||
| ## Create a frameless window | ||||
| 
 | ||||
| To create a frameless window, you only need to specify `frame` to `false` in | ||||
| [BrowserWindow](browser-window.md)'s `options`: | ||||
| 
 | ||||
| 
 | ||||
| ```javascript | ||||
| var BrowserWindow = require('browser-window'); | ||||
| var win = new BrowserWindow({ width: 800, height: 600, frame: false }); | ||||
| ``` | ||||
| 
 | ||||
| ## Draggable region | ||||
| 
 | ||||
| By default, the frameless window is non-draggable. Apps need to specify | ||||
| `-webkit-app-region: drag` in CSS to tell atom-shell which regions are draggable | ||||
| (like the OS's standard titlebar), and apps can also use | ||||
| `-webkit-app-region: no-drag` to exclude the non-draggable area from the | ||||
|  draggable region. Note that only rectangular shape is currently supported. | ||||
| 
 | ||||
| To make the whole window draggable, you can add `-webkit-app-region: drag` as | ||||
| `body`'s style: | ||||
| 
 | ||||
| ```html | ||||
| <body style="-webkit-app-region: drag"> | ||||
| </body> | ||||
| ``` | ||||
| 
 | ||||
| And note that if you have made the whole window draggable, you must also mark | ||||
| buttons as non-draggable, otherwise it would be impossible for users to click on | ||||
| them: | ||||
| 
 | ||||
| ```css | ||||
| button { | ||||
|   -webkit-app-region: no-drag; | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| If you're only using a custom titlebar, you also need to make buttons in | ||||
| titlebar non-draggable. | ||||
| 
 | ||||
| ## Text selection | ||||
| 
 | ||||
| One thing on frameless window is that the dragging behaviour may conflict with | ||||
| selecting text, for example, when you drag the titlebar, you may accidentally | ||||
| select the text on titlebar. To prevent this, you need to disable text | ||||
| selection on dragging area like this: | ||||
| 
 | ||||
| ```css | ||||
| .titlebar { | ||||
|   -webkit-user-select: none; | ||||
|   -webkit-app-region: drag; | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ##Context menu | ||||
| 
 | ||||
| On some platforms, the draggable area would be treated as non-client frame, so | ||||
| when you right click on it a system menu would be popuped. To make context menu | ||||
| behave correctly on all platforms, you should never custom context menu on | ||||
| draggable areas. | ||||
|  | @ -12,6 +12,8 @@ | |||
| #include "ipc/ipc_message_macros.h" | ||||
| #include "renderer/api/atom_renderer_bindings.h" | ||||
| #include "renderer/atom_renderer_client.h" | ||||
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | ||||
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebDraggableRegion.h" | ||||
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | ||||
| #include "v8/include/v8.h" | ||||
| 
 | ||||
|  | @ -76,6 +78,19 @@ void AtomRenderViewObserver::FrameWillClose(WebFrame* frame) { | |||
|   vec.erase(std::remove(vec.begin(), vec.end(), frame), vec.end()); | ||||
| } | ||||
| 
 | ||||
| void AtomRenderViewObserver::DraggableRegionsChanged(WebKit::WebFrame* frame) { | ||||
|   WebKit::WebVector<WebKit::WebDraggableRegion> webregions = | ||||
|       frame->document().draggableRegions(); | ||||
|   std::vector<DraggableRegion> regions; | ||||
|   for (size_t i = 0; i < webregions.size(); ++i) { | ||||
|     DraggableRegion region; | ||||
|     region.bounds = webregions[i].bounds; | ||||
|     region.draggable = webregions[i].draggable; | ||||
|     regions.push_back(region); | ||||
|   } | ||||
|   Send(new AtomViewHostMsg_UpdateDraggableRegions(routing_id(), regions)); | ||||
| } | ||||
| 
 | ||||
| bool AtomRenderViewObserver::OnMessageReceived(const IPC::Message& message) { | ||||
|   bool handled = true; | ||||
|   IPC_BEGIN_MESSAGE_MAP(AtomRenderViewObserver, message) | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ class AtomRenderViewObserver : content::RenderViewObserver { | |||
| 
 | ||||
|  private: | ||||
|   // content::RenderViewObserver implementation.
 | ||||
|   virtual void DraggableRegionsChanged(WebKit::WebFrame* frame) OVERRIDE; | ||||
|   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; | ||||
| 
 | ||||
|   void OnBrowserMessage(const std::string& channel, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Cheng Zhao
				Cheng Zhao