diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index 65260f82187e..3c4b7bbf8334 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -181,6 +181,18 @@ std::vector Window::GetSize() { return result; } +void Window::SetContentSize(int width, int height) { + window_->SetContentSize(gfx::Size(width, height)); +} + +std::vector Window::GetContentSize() { + std::vector result(2); + gfx::Size size = window_->GetContentSize(); + result[0] = size.width(); + result[1] = size.height(); + return result; +} + void Window::SetMinimumSize(int width, int height) { window_->SetMinimumSize(gfx::Size(width, height)); } @@ -332,6 +344,8 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("isFullScreen", &Window::IsFullscreen) .SetMethod("getSize", &Window::GetSize) .SetMethod("setSize", &Window::SetSize) + .SetMethod("getContentSize", &Window::GetContentSize) + .SetMethod("setContentSize", &Window::SetContentSize) .SetMethod("setMinimumSize", &Window::SetMinimumSize) .SetMethod("getMinimumSize", &Window::GetMinimumSize) .SetMethod("setMaximumSize", &Window::SetMaximumSize) diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index b6c8cfa07ca1..239673672d2b 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -73,6 +73,8 @@ class Window : public mate::EventEmitter, bool IsFullscreen(); void SetSize(int width, int height); std::vector GetSize(); + void SetContentSize(int width, int height); + std::vector GetContentSize(); void SetMinimumSize(int width, int height); std::vector GetMinimumSize(); void SetMaximumSize(int width, int height); diff --git a/atom/browser/default_app/default_app.js b/atom/browser/default_app/default_app.js index 63e5f996206e..0c2ddcfc8208 100644 --- a/atom/browser/default_app/default_app.js +++ b/atom/browser/default_app/default_app.js @@ -14,13 +14,11 @@ app.on('window-all-closed', function() { app.on('ready', function() { app.commandLine.appendSwitch('js-flags', '--harmony_collections'); - var height = 600; - if (process.platform == 'win32') - height += 60; - else if (process.platform == 'linux') - height += 30; - - mainWindow = new BrowserWindow({ width: 800, height: height }); + mainWindow = new BrowserWindow({ + width: 800, + height: 600, + 'use-content-size': true, + }); mainWindow.loadUrl('file://' + __dirname + '/index.html'); if (process.platform == 'darwin') { diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index f70b68e22eca..7c2b60806713 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -111,6 +111,8 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, virtual bool IsFullscreen() = 0; virtual void SetSize(const gfx::Size& size) = 0; virtual gfx::Size GetSize() = 0; + virtual void SetContentSize(const gfx::Size& size) = 0; + virtual gfx::Size GetContentSize() = 0; virtual void SetMinimumSize(const gfx::Size& size) = 0; virtual gfx::Size GetMinimumSize() = 0; virtual void SetMaximumSize(const gfx::Size& size) = 0; diff --git a/atom/browser/native_window_gtk.cc b/atom/browser/native_window_gtk.cc index 67323c2df01b..0b49b0420c7f 100644 --- a/atom/browser/native_window_gtk.cc +++ b/atom/browser/native_window_gtk.cc @@ -114,8 +114,9 @@ NativeWindowGtk::NativeWindowGtk(content::WebContents* web_contents, options->GetInteger(switches::kWidth, &width); options->GetInteger(switches::kHeight, &height); - // Fixup the initial window size. - if (has_frame_) + bool use_content_size = false; + options->GetBoolean(switches::kUseContentSize, &use_content_size); + if (has_frame_ && !use_content_size) SubstractBorderSize(&width, &height); // Force a size allocation so the web page of hidden window can have correct @@ -261,6 +262,24 @@ gfx::Size NativeWindowGtk::GetSize() { return gfx::Size(frame_extents.width, frame_extents.height); } +void NativeWindowGtk::SetContentSize(const gfx::Size& size) { + if (!has_frame_ || !has_ever_been_shown_) { + gtk_window_util::SetWindowSize(window_, size); + } else { + gfx::Size large = GetSize(); + gfx::Size small = GetContentSize(); + gfx::Size target(size.width() + large.width() - small.width(), + size.height() + large.height() - small.height()); + gtk_window_util::SetWindowSize(window_, target); + } +} + +gfx::Size NativeWindowGtk::GetContentSize() { + gint width, height; + gtk_window_get_size(window_, &width, &height); + return gfx::Size(width, height); +} + void NativeWindowGtk::SetMinimumSize(const gfx::Size& size) { minimum_size_ = size; diff --git a/atom/browser/native_window_gtk.h b/atom/browser/native_window_gtk.h index a01eefbba405..d73381de3382 100644 --- a/atom/browser/native_window_gtk.h +++ b/atom/browser/native_window_gtk.h @@ -46,6 +46,8 @@ class NativeWindowGtk : public NativeWindow, virtual bool IsFullscreen() OVERRIDE; virtual void SetSize(const gfx::Size& size) OVERRIDE; virtual gfx::Size GetSize() OVERRIDE; + virtual void SetContentSize(const gfx::Size& size) OVERRIDE; + virtual gfx::Size GetContentSize() OVERRIDE; virtual void SetMinimumSize(const gfx::Size& size) OVERRIDE; virtual gfx::Size GetMinimumSize() OVERRIDE; virtual void SetMaximumSize(const gfx::Size& size) OVERRIDE; diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index 59559689a860..d60da8df7629 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -38,6 +38,8 @@ class NativeWindowMac : public NativeWindow { virtual bool IsFullscreen() OVERRIDE; virtual void SetSize(const gfx::Size& size) OVERRIDE; virtual gfx::Size GetSize() OVERRIDE; + virtual void SetContentSize(const gfx::Size& size) OVERRIDE; + virtual gfx::Size GetContentSize() OVERRIDE; virtual void SetMinimumSize(const gfx::Size& size) OVERRIDE; virtual gfx::Size GetMinimumSize() OVERRIDE; virtual void SetMaximumSize(const gfx::Size& size) OVERRIDE; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 2b73fb8c100c..e8da6b523e11 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -6,18 +6,18 @@ #include +#import "atom/browser/ui/cocoa/event_processing_window.h" +#include "atom/common/draggable_region.h" +#include "atom/common/options_switches.h" #include "base/mac/mac_util.h" #include "base/strings/sys_string_conversions.h" #include "base/values.h" -#import "atom/browser/ui/cocoa/event_processing_window.h" -#include "brightray/browser/inspectable_web_contents.h" -#include "brightray/browser/inspectable_web_contents_view.h" -#include "atom/common/draggable_region.h" -#include "atom/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" +#include "vendor/brightray/browser/inspectable_web_contents.h" +#include "vendor/brightray/browser/inspectable_web_contents_view.h" static const CGFloat kAtomWindowCornerRadius = 4.0; @@ -179,6 +179,12 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents, // We will manage window's lifetime ourselves. [window_ setReleasedWhenClosed:NO]; + // On OS X the initial window size doesn't include window frame. + bool use_content_size = false; + options->GetBoolean(switches::kUseContentSize, &use_content_size); + if (has_frame_ && !use_content_size) + SetSize(gfx::Size(width, height)); + // Enable the NSView to accept first mouse event. bool acceptsFirstMouse = false; options->GetBoolean(switches::kAcceptFirstMouse, &acceptsFirstMouse); @@ -299,6 +305,24 @@ gfx::Size NativeWindowMac::GetSize() { return gfx::Size(frame.size.width, frame.size.height); } +void NativeWindowMac::SetContentSize(const gfx::Size& size) { + NSRect frame_nsrect = [window_ frame]; + NSSize frame = frame_nsrect.size; + NSSize content = [window_ contentRectForFrameRect:frame_nsrect].size; + + int width = size.width() + frame.width - content.width; + int height = size.height() + frame.height - content.height; + frame_nsrect.origin.y -= height - frame_nsrect.size.height; + frame_nsrect.size.width = width; + frame_nsrect.size.height = height; + [window_ setFrame:frame_nsrect display:YES]; +} + +gfx::Size NativeWindowMac::GetContentSize() { + NSRect bounds = [[window_ contentView] bounds]; + return gfx::Size(bounds.size.width, bounds.size.height); +} + void NativeWindowMac::SetMinimumSize(const gfx::Size& size) { NSSize min_size = NSMakeSize(size.width(), size.height()); NSView* content = [window_ contentView]; diff --git a/atom/browser/native_window_win.cc b/atom/browser/native_window_win.cc index f8d62be31eba..28dd6dea9185 100644 --- a/atom/browser/native_window_win.cc +++ b/atom/browser/native_window_win.cc @@ -203,6 +203,7 @@ NativeWindowWin::NativeWindowWin(content::WebContents* web_contents, : NativeWindow(web_contents, options), window_(new views::Widget), web_view_(new views::WebView(NULL)), + use_content_size_(false), resizable_(true) { views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); params.delegate = this; @@ -217,8 +218,11 @@ NativeWindowWin::NativeWindowWin(content::WebContents* web_contents, options->GetInteger(switches::kHeight, &height); gfx::Size size(width, height); - window_->CenterWindow(size); + options->GetBoolean(switches::kUseContentSize, &use_content_size_); + if (has_frame_ && use_content_size_) + ClientAreaSizeToWindowSize(&size); + window_->CenterWindow(size); window_->UpdateWindowIcon(); web_view_->SetWebContents(web_contents); @@ -295,6 +299,16 @@ gfx::Size NativeWindowWin::GetSize() { return window_->GetWindowBoundsInScreen().size(); } +void NativeWindowWin::SetContentSize(const gfx::Size& size) { + gfx::Size resized(size); + ClientAreaSizeToWindowSize(&resized); + SetSize(resized); +} + +gfx::Size NativeWindowWin::GetContentSize() { + return window_->GetClientAreaBoundsInScreen().size(); +} + void NativeWindowWin::SetMinimumSize(const gfx::Size& size) { minimum_size_ = size; } @@ -374,6 +388,13 @@ void NativeWindowWin::SetMenu(ui::MenuModel* menu_model) { menu_.reset(new atom::Menu2(menu_model, true)); ::SetMenu(GetNativeWindow(), menu_->GetNativeMenu()); RegisterAccelerators(); + + // Resize the window so SetMenu won't change client area size. + if (use_content_size_) { + gfx::Size size = GetSize(); + size.set_height(size.height() + GetSystemMetrics(SM_CYMENU)); + SetSize(size); + } } void NativeWindowWin::UpdateDraggableRegions( @@ -492,6 +513,13 @@ views::NonClientFrameView* NativeWindowWin::CreateNonClientFrameView( return new NativeWindowFramelessView(widget, this); } +void NativeWindowWin::ClientAreaSizeToWindowSize(gfx::Size* size) { + gfx::Size window = window_->GetWindowBoundsInScreen().size(); + gfx::Size client = window_->GetClientAreaBoundsInScreen().size(); + size->set_width(size->width() + window.width() - client.width()); + size->set_height(size->height() + window.height() - client.height()); +} + void NativeWindowWin::OnViewWasResized() { // Set the window shape of the RWHV. gfx::Size sz = web_view_->size(); diff --git a/atom/browser/native_window_win.h b/atom/browser/native_window_win.h index 7b88b18b69de..ce4c829f24a6 100644 --- a/atom/browser/native_window_win.h +++ b/atom/browser/native_window_win.h @@ -53,6 +53,8 @@ class NativeWindowWin : public NativeWindow, virtual bool IsFullscreen() OVERRIDE; virtual void SetSize(const gfx::Size& size) OVERRIDE; virtual gfx::Size GetSize() OVERRIDE; + virtual void SetContentSize(const gfx::Size& size) OVERRIDE; + virtual gfx::Size GetContentSize() OVERRIDE; virtual void SetMinimumSize(const gfx::Size& size) OVERRIDE; virtual gfx::Size GetMinimumSize() OVERRIDE; virtual void SetMaximumSize(const gfx::Size& size) OVERRIDE; @@ -113,6 +115,8 @@ class NativeWindowWin : public NativeWindow, typedef struct { int position; ui::MenuModel* model; } MenuItem; typedef std::map AcceleratorTable; + void ClientAreaSizeToWindowSize(gfx::Size* size); + void OnViewWasResized(); // Register accelerators supported by the menu model. @@ -129,6 +133,8 @@ class NativeWindowWin : public NativeWindow, scoped_ptr draggable_region_; + bool use_content_size_; + bool resizable_; string16 title_; gfx::Size minimum_size_; diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index 8466cd0e5324..270fc1f48315 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -36,6 +36,9 @@ const char kNodeIntegration[] = "node-integration"; // Enable the NSView to accept first mouse event. const char kAcceptFirstMouse[] = "accept-first-mouse"; +// Whether window size should include window frame. +const char kUseContentSize[] = "use-content-size"; + } // namespace switches } // namespace atom diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index cd6e6acfec36..8fa105684dc2 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -28,6 +28,7 @@ extern const char kKiosk[]; extern const char kAlwaysOnTop[]; extern const char kNodeIntegration[]; extern const char kAcceptFirstMouse[]; +extern const char kUseContentSize[]; } // namespace switches diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 9aba1c326bd7..7b143b8cca60 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -28,13 +28,19 @@ You can also create a window without chrome by using * `options` Object * `x` Integer - Window's left offset to screen * `y` Integer - Window's top offset to screen + * `width` Integer - Window's width + * `height` Integer - Window's height + * `use-content-size` Boolean - The `width` and `height` would be used as web + page's size, which means the actual window's size will include window + frame's size and be slightly larger. * `center` Boolean - Show window in the center of the screen * `min-width` Integer - Minimum width * `min-height` Integer - Minimum height * `max-width` Integer - Maximum width * `max-height` Integer - Maximum height * `resizable` Boolean - Whether window is resizable - * `always-on-top` Boolean - Whether the window should always stay on top of other windows + * `always-on-top` Boolean - Whether the window should always stay on top of + other windows * `fullscreen` Boolean - Whether the window should show in fullscreen * `kiosk` Boolean - The kiosk mode * `title` String - Default window title @@ -223,6 +229,17 @@ Resizes the window to `width` and `height`. Returns an array that contains window's width and height. +### BrowserWindow.setContentSize(width, height) + +* `width` Integer +* `height` Integer + +Resizes the window's client area (e.g. the web page) to `width` and `height`. + +### BrowserWindow.getContentSize() + +Returns an array that contains window's client area's width and height. + ### BrowserWindow.setMinimumSize(width, height) * `width` Integer diff --git a/spec/api-browser-window-spec.coffee b/spec/api-browser-window-spec.coffee index 87e3a5249cda..b515a2a0bd3e 100644 --- a/spec/api-browser-window-spec.coffee +++ b/spec/api-browser-window-spec.coffee @@ -11,7 +11,7 @@ describe 'browser-window module', -> w = null beforeEach -> w.destroy() if w? - w = new BrowserWindow(show: false) + w = new BrowserWindow(show: false, width: 400, height: 400) afterEach -> w.destroy() if w? w = null @@ -68,6 +68,44 @@ describe 'browser-window module', -> assert.equal image.constructor.name, 'Buffer' done() + describe 'BrowserWindow.setSize(width, height)', -> + it 'sets the window size', -> + # No way to reliably set size when window has not been shown on Linux. + return if process.platform is 'linux' + + size = [400, 400] + w.setSize size[0], size[1] + after = w.getSize() + assert.equal after[0], size[0] + assert.equal after[1], size[1] + + describe 'BrowserWindow.setContentSize(width, height)', -> + it 'sets the content size', -> + # No way to reliably set size when window has not been shown on Linux. + return if process.platform is 'linux' + + size = [400, 400] + w.setContentSize size[0], size[1] + after = w.getContentSize() + assert.equal after[0], size[0] + assert.equal after[1], size[1] + + describe '"use-content-size" option', -> + it 'make window created with content size when used', -> + w.destroy() + w = new BrowserWindow(show: false, width: 400, height: 400, 'use-content-size': true) + contentSize = w.getContentSize() + assert.equal contentSize[0], 400 + assert.equal contentSize[1], 400 + + it 'make window created with window size when not used', -> + # No way to reliably set size when window has not been shown on Linux. + return if process.platform is 'linux' + + contentSize = w.getSize() + assert.equal contentSize[0], 400 + assert.equal contentSize[1], 400 + describe 'beforeunload handler', -> it 'returning true would not prevent close', (done) -> w.on 'closed', ->