From 40273cf37d24e1ebeaa8a17e26db7dcd1edd7169 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 5 Sep 2013 19:46:12 +0800 Subject: [PATCH 01/12] Add IPC messages and structs for passing draggable regions. --- atom.gyp | 2 ++ common/api/api_messages.h | 10 ++++++++++ common/draggable_region.cc | 13 +++++++++++++ common/draggable_region.h | 21 +++++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 common/draggable_region.cc create mode 100644 common/draggable_region.h diff --git a/atom.gyp b/atom.gyp index 72174d8f8bc..c08e57d8806 100644 --- a/atom.gyp +++ b/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', diff --git a/common/api/api_messages.h b/common/api/api_messages.h index 48f6d0e2970..ee031e6487e 100644 --- a/common/api/api_messages.h +++ b/common/api/api_messages.h @@ -7,6 +7,7 @@ #include #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 /* regions */) diff --git a/common/draggable_region.cc b/common/draggable_region.cc new file mode 100644 index 00000000000..e408c9b462e --- /dev/null +++ b/common/draggable_region.cc @@ -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 diff --git a/common/draggable_region.h b/common/draggable_region.h new file mode 100644 index 00000000000..6ec3d2d7cf7 --- /dev/null +++ b/common/draggable_region.h @@ -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_ From 4223867dbce4222710cb6644dea9d96c9a332069 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 5 Sep 2013 20:06:54 +0800 Subject: [PATCH 02/12] Send and receive the AtomViewHostMsg_UpdateDraggableRegions message. --- browser/native_window.cc | 2 ++ browser/native_window.h | 5 +++++ browser/native_window_mac.h | 3 +++ browser/native_window_mac.mm | 4 ++++ browser/native_window_win.cc | 4 ++++ browser/native_window_win.h | 3 +++ renderer/atom_render_view_observer.cc | 15 +++++++++++++++ renderer/atom_render_view_observer.h | 1 + 8 files changed, 37 insertions(+) diff --git a/browser/native_window.cc b/browser/native_window.cc index 18954736ad5..34bdefd6acc 100644 --- a/browser/native_window.cc +++ b/browser/native_window.cc @@ -298,6 +298,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() diff --git a/browser/native_window.h b/browser/native_window.h index b41fd4bdf3e..cde0ce1f494 100644 --- a/browser/native_window.h +++ b/browser/native_window.h @@ -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& regions) = 0; + // Implementations of content::WebContentsDelegate. virtual void WebContentsCreated(content::WebContents* source_contents, int64 source_frame_id, diff --git a/browser/native_window_mac.h b/browser/native_window_mac.h index d2684735455..a35e53d3295 100644 --- a/browser/native_window_mac.h +++ b/browser/native_window_mac.h @@ -57,6 +57,9 @@ class NativeWindowMac : public NativeWindow { void NotifyWindowBlur() { NativeWindow::NotifyWindowBlur(); } protected: + virtual void UpdateDraggableRegions( + const std::vector& regions) OVERRIDE; + // Implementations of content::WebContentsDelegate. virtual void HandleKeyboardEvent( content::WebContents*, diff --git a/browser/native_window_mac.mm b/browser/native_window_mac.mm index 194b38b7d39..acddd0c83b0 100644 --- a/browser/native_window_mac.mm +++ b/browser/native_window_mac.mm @@ -324,6 +324,10 @@ gfx::NativeWindow NativeWindowMac::GetNativeWindow() { return window(); } +void NativeWindowMac::UpdateDraggableRegions( + const std::vector& regions) { +} + void NativeWindowMac::HandleKeyboardEvent( content::WebContents*, const content::NativeWebKeyboardEvent& event) { diff --git a/browser/native_window_win.cc b/browser/native_window_win.cc index 0a84afd4d73..a3589158abe 100644 --- a/browser/native_window_win.cc +++ b/browser/native_window_win.cc @@ -221,6 +221,10 @@ gfx::NativeWindow NativeWindowWin::GetNativeWindow() { return window_->GetNativeView(); } +void NativeWindowWin::UpdateDraggableRegions( + const std::vector& regions) { +} + void NativeWindowWin::HandleKeyboardEvent( content::WebContents*, const content::NativeWebKeyboardEvent& event) { diff --git a/browser/native_window_win.h b/browser/native_window_win.h index 3d09615e59f..19d9a555299 100644 --- a/browser/native_window_win.h +++ b/browser/native_window_win.h @@ -61,6 +61,9 @@ class NativeWindowWin : public NativeWindow, virtual gfx::NativeWindow GetNativeWindow() OVERRIDE; protected: + virtual void UpdateDraggableRegions( + const std::vector& regions) OVERRIDE; + // Overridden from content::WebContentsDelegate: virtual void HandleKeyboardEvent( content::WebContents*, diff --git a/renderer/atom_render_view_observer.cc b/renderer/atom_render_view_observer.cc index 0c2c611d039..6a394e44737 100644 --- a/renderer/atom_render_view_observer.cc +++ b/renderer/atom_render_view_observer.cc @@ -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 webregions = + frame->document().draggableRegions(); + std::vector 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) diff --git a/renderer/atom_render_view_observer.h b/renderer/atom_render_view_observer.h index 55f1cdfb65d..fd984d01246 100644 --- a/renderer/atom_render_view_observer.h +++ b/renderer/atom_render_view_observer.h @@ -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, From a5eb9ea08f6398e4a8f077c9bc91f3e695c80926 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 5 Sep 2013 21:43:47 +0800 Subject: [PATCH 03/12] Add has_frame_ attribute for NativeWindow. --- browser/native_window.cc | 3 +++ browser/native_window.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/browser/native_window.cc b/browser/native_window.cc index 34bdefd6acc..154a1a60738 100644 --- a/browser/native_window.cc +++ b/browser/native_window.cc @@ -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); diff --git a/browser/native_window.h b/browser/native_window.h index cde0ce1f494..8fb0f118fdd 100644 --- a/browser/native_window.h +++ b/browser/native_window.h @@ -165,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(); From da2ded54530f0a100328f3fd32f703fc05123069 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 5 Sep 2013 23:52:29 +0800 Subject: [PATCH 04/12] Implement frameless window on OS X. Most of the code came from Chromium's packaged app window. --- browser/native_window_mac.h | 32 +++- browser/native_window_mac.mm | 317 ++++++++++++++++++++++++++++++++++- 2 files changed, 343 insertions(+), 6 deletions(-) diff --git a/browser/native_window_mac.h b/browser/native_window_mac.h index a35e53d3295..25bbb973512 100644 --- a/browser/native_window_mac.h +++ b/browser/native_window_mac.h @@ -10,6 +10,8 @@ #include "base/memory/scoped_ptr.h" #include "browser/native_window.h" +class SkRegion; + namespace atom { class NativeWindowMac : public NativeWindow { @@ -52,9 +54,15 @@ class NativeWindowMac : public NativeWindow { virtual bool IsKiosk() OVERRIDE; virtual gfx::NativeWindow GetNativeWindow() OVERRIDE; + void NotifyWindowBlur() { NativeWindow::NotifyWindowBlur(); } + + // Called to handle a mouse event. + void HandleMouseEvent(NSEvent* event); + NSWindow*& window() { return window_; } - void NotifyWindowBlur() { NativeWindow::NotifyWindowBlur(); } + bool use_system_drag() const { return use_system_drag_; } + SkRegion* draggable_region() const { return draggable_region_.get(); } protected: virtual void UpdateDraggableRegions( @@ -68,6 +76,12 @@ class NativeWindowMac : public NativeWindow { private: void InstallView(); void UninstallView(); + void InstallDraggableRegionViews(); + void UpdateDraggableRegionsForSystemDrag( + const std::vector& regions, + const DraggableRegion* draggable_area); + void UpdateDraggableRegionsForCustomDrag( + const std::vector& regions); NSWindow* window_; @@ -75,6 +89,22 @@ class NativeWindowMac : public NativeWindow { NSInteger attention_request_id_; // identifier from requestUserAttention + // Indicates whether system drag or custom drag should be used, depending on + // the complexity of draggable regions. + bool use_system_drag_; + + // For system drag, the whole window is draggable and the non-draggable areas + // have to been explicitly excluded. + std::vector system_drag_exclude_areas_; + + // For custom drag, the whole window is non-draggable and the draggable region + // has to been explicitly provided. + scoped_ptr 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_location_; + DISALLOW_COPY_AND_ASSIGN(NativeWindowMac); }; diff --git a/browser/native_window_mac.mm b/browser/native_window_mac.mm index acddd0c83b0..d46bad82b3d 100644 --- a/browser/native_window_mac.mm +++ b/browser/native_window_mac.mm @@ -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 { @private atom::NativeWindowMac* shell_; @@ -80,13 +93,94 @@ @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_->use_system_drag()) + return nil; + if (!shellWindow_->draggable_region() || + !shellWindow_->draggable_region()->contains(aPoint.x, aPoint.y)) { + 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, base::DictionaryValue* options) : NativeWindow(web_contents, options), is_kiosk_(false), - attention_request_id_(0) { + attention_request_id_(0), + use_system_drag_(true) { int width, height; options->GetInteger(switches::kWidth, &width); options->GetInteger(switches::kHeight, &height); @@ -100,7 +194,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 +219,16 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents, [window() setCollectionBehavior:collectionBehavior]; } + NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); + [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + // By default, the whole frameless window is not draggable. + if (!has_frame_) { + gfx::Rect window_bounds( + 0, 0, NSWidth(cocoa_bounds), NSHeight(cocoa_bounds)); + system_drag_exclude_areas_.push_back(window_bounds); + } + InstallView(); } @@ -324,8 +434,59 @@ gfx::NativeWindow NativeWindowMac::GetNativeWindow() { return window(); } +void NativeWindowMac::HandleMouseEvent(NSEvent* event) { + if ([event type] == NSLeftMouseDown) { + last_mouse_location_ = + [window() convertBaseToScreen:[event locationInWindow]]; + } else if ([event type] == NSLeftMouseDragged) { + NSPoint current_mouse_location = + [window() convertBaseToScreen:[event locationInWindow]]; + NSPoint frame_origin = [window() frame].origin; + frame_origin.x += current_mouse_location.x - last_mouse_location_.x; + frame_origin.y += current_mouse_location.y - last_mouse_location_.y; + [window() setFrameOrigin:frame_origin]; + last_mouse_location_ = current_mouse_location; + } +} + void NativeWindowMac::UpdateDraggableRegions( const std::vector& regions) { + // Draggable region is not supported for non-frameless window. + if (has_frame_) + return; + + // To use system drag, the window has to be marked as draggable with + // non-draggable areas being excluded via overlapping views. + // 1) If no draggable area is provided, the window is not draggable at all. + // 2) If only one draggable area is given, as this is the most common + // case, use the system drag. The non-draggable areas that are opposite of + // the draggable area are computed. + // 3) Otherwise, use the custom drag. As such, we lose the capability to + // support some features like snapping into other space. + + // Determine how to perform the drag by counting the number of draggable + // areas. + const DraggableRegion* draggable_area = NULL; + use_system_drag_ = true; + for (std::vector::const_iterator iter = regions.begin(); + iter != regions.end(); + ++iter) { + if (iter->draggable) { + // If more than one draggable area is found, use custom drag. + if (draggable_area) { + use_system_drag_ = false; + break; + } + draggable_area = &(*iter); + } + } + + if (use_system_drag_) + UpdateDraggableRegionsForSystemDrag(regions, draggable_area); + else + UpdateDraggableRegionsForCustomDrag(regions); + + InstallDraggableRegionViews(); } void NativeWindowMac::HandleKeyboardEvent( @@ -343,9 +504,25 @@ void NativeWindowMac::HandleKeyboardEvent( void NativeWindowMac::InstallView() { NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); - [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [view setFrame:[[window() contentView] bounds]]; - [[window() contentView] addSubview:view]; + NSView* web_view = GetWebContents()->GetView()->GetNativeView(); + if (has_frame_) { + [view setFrame:[[window() contentView] bounds]]; + [[window() contentView] addSubview:view]; + } else { + DCHECK([web_view respondsToSelector:@selector(setMouseDownCanMoveWindow:)]); + [web_view setMouseDownCanMoveWindow:YES]; + + 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]; + + InstallDraggableRegionViews(); + } } void NativeWindowMac::UninstallView() { @@ -353,6 +530,136 @@ void NativeWindowMac::UninstallView() { [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 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::const_iterator iter = + system_drag_exclude_areas_.begin(); + iter != system_drag_exclude_areas_.end(); + ++iter) { + scoped_nsobject controlRegion( + [[ControlRegionView alloc] initWithShellWindow:this]); + [controlRegion setFrame:NSMakeRect(iter->x(), + webViewHeight - iter->bottom(), + iter->width(), + iter->height())]; + [webView addSubview:controlRegion]; + } +} + +void NativeWindowMac::UpdateDraggableRegionsForSystemDrag( + const std::vector& regions, + const DraggableRegion* draggable_area) { + NSView* web_view = GetWebContents()->GetView()->GetNativeView(); + NSInteger web_view_width = NSWidth([web_view bounds]); + NSInteger web_view_height = NSHeight([web_view bounds]); + + system_drag_exclude_areas_.clear(); + + // The whole window is not draggable if no draggable area is given. + if (!draggable_area) { + gfx::Rect window_bounds(0, 0, web_view_width, web_view_height); + system_drag_exclude_areas_.push_back(window_bounds); + return; + } + + // Otherwise, there is only one draggable area. Compute non-draggable areas + // that are the opposite of the given draggable area, combined with the + // remaining provided non-draggable areas. + + // Copy all given non-draggable areas. + for (std::vector::const_iterator iter = regions.begin(); + iter != regions.end(); + ++iter) { + if (!iter->draggable) + system_drag_exclude_areas_.push_back(iter->bounds); + } + + gfx::Rect draggable_bounds = draggable_area->bounds; + gfx::Rect non_draggable_bounds; + + // Add the non-draggable area above the given draggable area. + if (draggable_bounds.y() > 0) { + non_draggable_bounds.SetRect(0, + 0, + web_view_width, + draggable_bounds.y() - 1); + system_drag_exclude_areas_.push_back(non_draggable_bounds); + } + + // Add the non-draggable area below the given draggable area. + if (draggable_bounds.bottom() < web_view_height) { + non_draggable_bounds.SetRect(0, + draggable_bounds.bottom() + 1, + web_view_width, + web_view_height - draggable_bounds.bottom()); + system_drag_exclude_areas_.push_back(non_draggable_bounds); + } + + // Add the non-draggable area to the left of the given draggable area. + if (draggable_bounds.x() > 0) { + non_draggable_bounds.SetRect(0, + draggable_bounds.y(), + draggable_bounds.x() - 1, + draggable_bounds.height()); + system_drag_exclude_areas_.push_back(non_draggable_bounds); + } + + // Add the non-draggable area to the right of the given draggable area. + if (draggable_bounds.right() < web_view_width) { + non_draggable_bounds.SetRect(draggable_bounds.right() + 1, + draggable_bounds.y(), + web_view_width - draggable_bounds.right(), + draggable_bounds.height()); + system_drag_exclude_areas_.push_back(non_draggable_bounds); + } +} + +void NativeWindowMac::UpdateDraggableRegionsForCustomDrag( + const std::vector& 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::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) { From b7c2295a1c0105400ee49473567f38ade824c489 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 6 Sep 2013 11:54:52 +0800 Subject: [PATCH 05/12] Don't use setMouseDownCanMoveWindow to implement draggable area. It would not work when we have the in-window devtools. --- browser/native_window_mac.h | 13 ++-- browser/native_window_mac.mm | 135 ++++------------------------------- 2 files changed, 19 insertions(+), 129 deletions(-) diff --git a/browser/native_window_mac.h b/browser/native_window_mac.h index 25bbb973512..6c485e79f7c 100644 --- a/browser/native_window_mac.h +++ b/browser/native_window_mac.h @@ -56,12 +56,14 @@ class NativeWindowMac : public NativeWindow { 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_; } - - bool use_system_drag() const { return use_system_drag_; } SkRegion* draggable_region() const { return draggable_region_.get(); } protected: @@ -77,9 +79,6 @@ class NativeWindowMac : public NativeWindow { void InstallView(); void UninstallView(); void InstallDraggableRegionViews(); - void UpdateDraggableRegionsForSystemDrag( - const std::vector& regions, - const DraggableRegion* draggable_area); void UpdateDraggableRegionsForCustomDrag( const std::vector& regions); @@ -89,10 +88,6 @@ class NativeWindowMac : public NativeWindow { NSInteger attention_request_id_; // identifier from requestUserAttention - // Indicates whether system drag or custom drag should be used, depending on - // the complexity of draggable regions. - bool use_system_drag_; - // For system drag, the whole window is draggable and the non-draggable areas // have to been explicitly excluded. std::vector system_drag_exclude_areas_; diff --git a/browser/native_window_mac.mm b/browser/native_window_mac.mm index d46bad82b3d..e293074e34b 100644 --- a/browser/native_window_mac.mm +++ b/browser/native_window_mac.mm @@ -154,10 +154,7 @@ } - (NSView*)hitTest:(NSPoint)aPoint { - if (shellWindow_->use_system_drag()) - return nil; - if (!shellWindow_->draggable_region() || - !shellWindow_->draggable_region()->contains(aPoint.x, aPoint.y)) { + if (!shellWindow_->IsWithinDraggableRegion(aPoint)) { return nil; } return self; @@ -179,8 +176,7 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents, base::DictionaryValue* options) : NativeWindow(web_contents, options), is_kiosk_(false), - attention_request_id_(0), - use_system_drag_(true) { + attention_request_id_(0) { int width, height; options->GetInteger(switches::kWidth, &width); options->GetInteger(switches::kHeight, &height); @@ -222,13 +218,6 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents, NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - // By default, the whole frameless window is not draggable. - if (!has_frame_) { - gfx::Rect window_bounds( - 0, 0, NSWidth(cocoa_bounds), NSHeight(cocoa_bounds)); - system_drag_exclude_areas_.push_back(window_bounds); - } - InstallView(); } @@ -434,6 +423,17 @@ 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) { if ([event type] == NSLeftMouseDown) { last_mouse_location_ = @@ -455,37 +455,7 @@ void NativeWindowMac::UpdateDraggableRegions( if (has_frame_) return; - // To use system drag, the window has to be marked as draggable with - // non-draggable areas being excluded via overlapping views. - // 1) If no draggable area is provided, the window is not draggable at all. - // 2) If only one draggable area is given, as this is the most common - // case, use the system drag. The non-draggable areas that are opposite of - // the draggable area are computed. - // 3) Otherwise, use the custom drag. As such, we lose the capability to - // support some features like snapping into other space. - - // Determine how to perform the drag by counting the number of draggable - // areas. - const DraggableRegion* draggable_area = NULL; - use_system_drag_ = true; - for (std::vector::const_iterator iter = regions.begin(); - iter != regions.end(); - ++iter) { - if (iter->draggable) { - // If more than one draggable area is found, use custom drag. - if (draggable_area) { - use_system_drag_ = false; - break; - } - draggable_area = &(*iter); - } - } - - if (use_system_drag_) - UpdateDraggableRegionsForSystemDrag(regions, draggable_area); - else - UpdateDraggableRegionsForCustomDrag(regions); - + UpdateDraggableRegionsForCustomDrag(regions); InstallDraggableRegionViews(); } @@ -504,14 +474,10 @@ void NativeWindowMac::HandleKeyboardEvent( void NativeWindowMac::InstallView() { NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); - NSView* web_view = GetWebContents()->GetView()->GetNativeView(); if (has_frame_) { [view setFrame:[[window() contentView] bounds]]; [[window() contentView] addSubview:view]; } else { - DCHECK([web_view respondsToSelector:@selector(setMouseDownCanMoveWindow:)]); - [web_view setMouseDownCanMoveWindow:YES]; - NSView* frameView = [[window() contentView] superview]; [view setFrame:[frameView bounds]]; [frameView addSubview:view]; @@ -520,13 +486,11 @@ void NativeWindowMac::InstallView() { [[window() standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES]; [[window() standardWindowButton:NSWindowCloseButton] setHidden:YES]; [[window() standardWindowButton:NSWindowFullScreenButton] setHidden:YES]; - - InstallDraggableRegionViews(); } } void NativeWindowMac::UninstallView() { - NSView* view = GetWebContents()->GetView()->GetNativeView(); + NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); [view removeFromSuperview]; } @@ -564,74 +528,6 @@ void NativeWindowMac::InstallDraggableRegionViews() { } } -void NativeWindowMac::UpdateDraggableRegionsForSystemDrag( - const std::vector& regions, - const DraggableRegion* draggable_area) { - NSView* web_view = GetWebContents()->GetView()->GetNativeView(); - NSInteger web_view_width = NSWidth([web_view bounds]); - NSInteger web_view_height = NSHeight([web_view bounds]); - - system_drag_exclude_areas_.clear(); - - // The whole window is not draggable if no draggable area is given. - if (!draggable_area) { - gfx::Rect window_bounds(0, 0, web_view_width, web_view_height); - system_drag_exclude_areas_.push_back(window_bounds); - return; - } - - // Otherwise, there is only one draggable area. Compute non-draggable areas - // that are the opposite of the given draggable area, combined with the - // remaining provided non-draggable areas. - - // Copy all given non-draggable areas. - for (std::vector::const_iterator iter = regions.begin(); - iter != regions.end(); - ++iter) { - if (!iter->draggable) - system_drag_exclude_areas_.push_back(iter->bounds); - } - - gfx::Rect draggable_bounds = draggable_area->bounds; - gfx::Rect non_draggable_bounds; - - // Add the non-draggable area above the given draggable area. - if (draggable_bounds.y() > 0) { - non_draggable_bounds.SetRect(0, - 0, - web_view_width, - draggable_bounds.y() - 1); - system_drag_exclude_areas_.push_back(non_draggable_bounds); - } - - // Add the non-draggable area below the given draggable area. - if (draggable_bounds.bottom() < web_view_height) { - non_draggable_bounds.SetRect(0, - draggable_bounds.bottom() + 1, - web_view_width, - web_view_height - draggable_bounds.bottom()); - system_drag_exclude_areas_.push_back(non_draggable_bounds); - } - - // Add the non-draggable area to the left of the given draggable area. - if (draggable_bounds.x() > 0) { - non_draggable_bounds.SetRect(0, - draggable_bounds.y(), - draggable_bounds.x() - 1, - draggable_bounds.height()); - system_drag_exclude_areas_.push_back(non_draggable_bounds); - } - - // Add the non-draggable area to the right of the given draggable area. - if (draggable_bounds.right() < web_view_width) { - non_draggable_bounds.SetRect(draggable_bounds.right() + 1, - draggable_bounds.y(), - web_view_width - draggable_bounds.right(), - draggable_bounds.height()); - system_drag_exclude_areas_.push_back(non_draggable_bounds); - } -} - void NativeWindowMac::UpdateDraggableRegionsForCustomDrag( const std::vector& regions) { // We still need one ControlRegionView to cover the whole window such that @@ -659,7 +555,6 @@ void NativeWindowMac::UpdateDraggableRegionsForCustomDrag( draggable_region_.reset(draggable_region); } - // static NativeWindow* NativeWindow::Create(content::WebContents* web_contents, base::DictionaryValue* options) { From b9d994dca280ddc231242b5d549d59ea4f310e2e Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 6 Sep 2013 12:12:17 +0800 Subject: [PATCH 06/12] Make sure the cursor doesn't drift away when dragging window. --- browser/native_window_mac.h | 2 +- browser/native_window_mac.mm | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/browser/native_window_mac.h b/browser/native_window_mac.h index 6c485e79f7c..ea359fa101b 100644 --- a/browser/native_window_mac.h +++ b/browser/native_window_mac.h @@ -98,7 +98,7 @@ class NativeWindowMac : public NativeWindow { // Mouse location since the last mouse event, in screen coordinates. This is // used in custom drag to compute the window movement. - NSPoint last_mouse_location_; + NSPoint last_mouse_offset_; DISALLOW_COPY_AND_ASSIGN(NativeWindowMac); }; diff --git a/browser/native_window_mac.mm b/browser/native_window_mac.mm index e293074e34b..d6858a191bb 100644 --- a/browser/native_window_mac.mm +++ b/browser/native_window_mac.mm @@ -435,17 +435,18 @@ bool NativeWindowMac::IsWithinDraggableRegion(NSPoint point) const { } void NativeWindowMac::HandleMouseEvent(NSEvent* event) { + NSPoint current_mouse_location = + [window() convertBaseToScreen:[event locationInWindow]]; + if ([event type] == NSLeftMouseDown) { - last_mouse_location_ = - [window() convertBaseToScreen:[event locationInWindow]]; - } else if ([event type] == NSLeftMouseDragged) { - NSPoint current_mouse_location = - [window() convertBaseToScreen:[event locationInWindow]]; NSPoint frame_origin = [window() frame].origin; - frame_origin.x += current_mouse_location.x - last_mouse_location_.x; - frame_origin.y += current_mouse_location.y - last_mouse_location_.y; - [window() setFrameOrigin:frame_origin]; - last_mouse_location_ = current_mouse_location; + 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)]; } } From a00bf3e1e17bab4338889628809a49c7b930866c Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 9 Sep 2013 10:49:28 +0800 Subject: [PATCH 07/12] Print stack when got error on startup. --- browser/default_app/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/default_app/main.js b/browser/default_app/main.js index fdf33882e74..862b59d58fe 100644 --- a/browser/default_app/main.js +++ b/browser/default_app/main.js @@ -9,6 +9,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 { From 3c0671c179a2ce5502c607f0a1f068a104c579af Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 9 Sep 2013 10:54:08 +0800 Subject: [PATCH 08/12] Quit when all windows are closed if running an app by passing it in command line. --- browser/default_app/main.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/browser/default_app/main.js b/browser/default_app/main.js index 862b59d58fe..4ca183fd586 100644 --- a/browser/default_app/main.js +++ b/browser/default_app/main.js @@ -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) { From f833423a2f2d04521350aa84cb6dc4d6658994ad Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 9 Sep 2013 12:12:17 +0800 Subject: [PATCH 09/12] win: Save draggable region. --- browser/native_window_win.cc | 20 ++++++++++++++++++++ browser/native_window_win.h | 4 ++++ vendor/apm | 2 +- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/browser/native_window_win.cc b/browser/native_window_win.cc index a3589158abe..63c27d57e2d 100644 --- a/browser/native_window_win.cc +++ b/browser/native_window_win.cc @@ -223,6 +223,26 @@ gfx::NativeWindow NativeWindowWin::GetNativeWindow() { void NativeWindowWin::UpdateDraggableRegions( const std::vector& 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::const_iterator iter = + regions.begin(); + iter != regions.end(); ++iter) { + const extensions::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); } void NativeWindowWin::HandleKeyboardEvent( diff --git a/browser/native_window_win.h b/browser/native_window_win.h index 19d9a555299..87f52231648 100644 --- a/browser/native_window_win.h +++ b/browser/native_window_win.h @@ -60,6 +60,8 @@ 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& regions) OVERRIDE; @@ -85,6 +87,8 @@ class NativeWindowWin : public NativeWindow, scoped_ptr window_; views::WebView* web_view_; // managed by window_. + scoped_ptr draggable_region_; + bool resizable_; string16 title_; gfx::Size minimum_size_; diff --git a/vendor/apm b/vendor/apm index 613d15242ac..2c9da12d10e 160000 --- a/vendor/apm +++ b/vendor/apm @@ -1 +1 @@ -Subproject commit 613d15242ac2286eafbfe19e3857580b8adeec8c +Subproject commit 2c9da12d10e0f8bbcf53bbfbd553ea2cdcb9b02a From cc62978ac3ec4ea9aafda7f1f81a2bc6fa08b9ad Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 9 Sep 2013 14:30:07 +0800 Subject: [PATCH 10/12] win: Add NativeWindowFramelessView as non-client view. --- browser/native_window_win.cc | 145 ++++++++++++++++++++++++++++++++++- browser/native_window_win.h | 2 + 2 files changed, 143 insertions(+), 4 deletions(-) diff --git a/browser/native_window_win.cc b/browser/native_window_win.cc index 63c27d57e2d..babb0e58508 100644 --- a/browser/native_window_win.cc +++ b/browser/native_window_win.cc @@ -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() { @@ -230,10 +334,9 @@ void NativeWindowWin::UpdateDraggableRegions( // By default, the whole window is non-draggable. We need to explicitly // include those draggable regions. - for (std::vector::const_iterator iter = - regions.begin(); + for (std::vector::const_iterator iter = regions.begin(); iter != regions.end(); ++iter) { - const extensions::DraggableRegion& region = *iter; + const DraggableRegion& region = *iter; draggable_region->op( region.bounds.x(), region.bounds.y(), @@ -243,6 +346,7 @@ void NativeWindowWin::UpdateDraggableRegions( } draggable_region_.reset(draggable_region); + OnViewWasResized(); } void NativeWindowWin::HandleKeyboardEvent( @@ -288,7 +392,40 @@ views::ClientView* NativeWindowWin::CreateClientView(views::Widget* widget) { views::NonClientFrameView* NativeWindowWin::CreateNonClientFrameView( views::Widget* widget) { - return new NativeWindowFrameView(widget, this); + 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 diff --git a/browser/native_window_win.h b/browser/native_window_win.h index 87f52231648..810b3eeb55e 100644 --- a/browser/native_window_win.h +++ b/browser/native_window_win.h @@ -84,6 +84,8 @@ class NativeWindowWin : public NativeWindow, views::Widget* widget) OVERRIDE; private: + void OnViewWasResized(); + scoped_ptr window_; views::WebView* web_view_; // managed by window_. From 8caf5fac0600e0235403adc477a51160f898f3d6 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 9 Sep 2013 14:52:46 +0800 Subject: [PATCH 11/12] doc: Document frameless window. --- docs/browser-window.md | 6 ++++ docs/frameless-window.md | 62 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 docs/frameless-window.md diff --git a/docs/browser-window.md b/docs/browser-window.md index f197e59acb7..54eee64af43 100644 --- a/docs/browser-window.md +++ b/docs/browser-window.md @@ -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 diff --git a/docs/frameless-window.md b/docs/frameless-window.md new file mode 100644 index 00000000000..5a2133acf9b --- /dev/null +++ b/docs/frameless-window.md @@ -0,0 +1,62 @@ +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 + + +``` + +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. From 8ddb85774ab6d8f3ae835aab21ff134927a505c6 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 9 Sep 2013 15:18:53 +0800 Subject: [PATCH 12/12] doc: Add titles for browser-window and frameless-window. --- docs/browser-window.md | 2 +- docs/frameless-window.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/browser-window.md b/docs/browser-window.md index 54eee64af43..fce37fb3d5f 100644 --- a/docs/browser-window.md +++ b/docs/browser-window.md @@ -1,4 +1,4 @@ -## Synopsis +# browser-window The `BrowserWindow` class gives you ability to create a browser window, an example is: diff --git a/docs/frameless-window.md b/docs/frameless-window.md index 5a2133acf9b..f3192def932 100644 --- a/docs/frameless-window.md +++ b/docs/frameless-window.md @@ -1,3 +1,5 @@ +# Frameless window + A frameless window is a window that has no chrome. ## Create a frameless window