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…
Reference in a new issue