diff --git a/atom.gyp b/atom.gyp index 756a140c0b..676f03325d 100644 --- a/atom.gyp +++ b/atom.gyp @@ -13,6 +13,12 @@ 'browser/atom_browser_main_parts.cc', 'browser/atom_browser_main_parts.h', 'browser/atom_browser_main_parts_mac.mm', + 'browser/native_window.cc', + 'browser/native_window.h', + 'browser/native_window_mac.h', + 'browser/native_window_mac.mm', + 'common/options_switches.cc', + 'common/options_switches.h', 'renderer/atom_render_view_observer.cc', 'renderer/atom_render_view_observer.h', 'renderer/atom_renderer_client.cc', diff --git a/browser/atom_browser_main_parts_mac.mm b/browser/atom_browser_main_parts_mac.mm index f62ae7be71..ae331803ec 100644 --- a/browser/atom_browser_main_parts_mac.mm +++ b/browser/atom_browser_main_parts_mac.mm @@ -4,8 +4,8 @@ #include "browser/atom_browser_main_parts.h" -#import - +#include "base/values.h" +#include "browser/native_window.h" #include "brightray/browser/browser_context.h" #include "brightray/browser/default_web_contents_delegate.h" #include "brightray/browser/inspectable_web_contents.h" @@ -16,32 +16,16 @@ namespace atom { void AtomBrowserMainParts::PreMainMessageLoopRun() { brightray::BrowserMainParts::PreMainMessageLoopRun(); - auto contentRect = NSMakeRect(0, 0, 800, 600); - auto styleMask = NSTitledWindowMask | - NSClosableWindowMask | - NSMiniaturizableWindowMask | - NSResizableWindowMask; - auto window = [[NSWindow alloc] initWithContentRect:contentRect - styleMask:styleMask - backing:NSBackingStoreBuffered - defer:YES]; - window.title = @"Atom"; + scoped_ptr options(new base::DictionaryValue); + options->SetInteger("width", 800); + options->SetInteger("height", 600); + options->SetString("title", "Atom"); - // FIXME: We're leaking this object (see #3). - auto contents = brightray::InspectableWebContents::Create(content::WebContents::CreateParams(browser_context())); - // FIXME: And this one! - contents->GetWebContents()->SetDelegate( - new brightray::DefaultWebContentsDelegate()); - auto contentsView = contents->GetView()->GetNativeView(); + // FIXME: Leak object here. + NativeWindow* window = NativeWindow::Create(browser_context(), options.get()); + window->InitFromOptions(options.get()); - contentsView.frame = [window.contentView bounds]; - contentsView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; - - [window.contentView addSubview:contentsView]; - [window makeFirstResponder:contentsView]; - [window makeKeyAndOrderFront:nil]; - - contents->GetWebContents()->GetController().LoadURL( + window->GetWebContents()->GetController().LoadURL( GURL("http://adam.roben.org/brightray_example/start.html"), content::Referrer(), content::PAGE_TRANSITION_AUTO_TOPLEVEL, diff --git a/browser/native_window.cc b/browser/native_window.cc new file mode 100644 index 0000000000..c6f61a6e6b --- /dev/null +++ b/browser/native_window.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser/native_window.h" + +#include + +#include "base/values.h" +#include "brightray/browser/browser_context.h" +#include "brightray/browser/default_web_contents_delegate.h" +#include "brightray/browser/inspectable_web_contents.h" +#include "brightray/browser/inspectable_web_contents_view.h" +#include "common/options_switches.h" +#include "ui/gfx/point.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/size.h" + +namespace atom { + +NativeWindow::NativeWindow(content::BrowserContext* browser_context, + base::DictionaryValue* options) + : inspectable_web_contents_(brightray::InspectableWebContents::Create( + content::WebContents::CreateParams(browser_context))) { + GetWebContents()->SetDelegate(new brightray::DefaultWebContentsDelegate()); +} + +NativeWindow::~NativeWindow() { +} + +void NativeWindow::InitFromOptions(base::DictionaryValue* options) { + // Setup window from options. + int x, y; + std::string position; + if (options->GetInteger(switches::kX, &x) && + options->GetInteger(switches::kY, &y)) { + int width, height; + options->GetInteger(switches::kWidth, &width); + options->GetInteger(switches::kHeight, &height); + Move(gfx::Rect(x, y, width, height)); + } else if (options->GetString(switches::kPosition, &position)) { + SetPosition(position); + } + int min_height, min_width; + if (options->GetInteger(switches::kMinHeight, &min_height) && + options->GetInteger(switches::kMinWidth, &min_width)) { + SetMinimumSize(min_width, min_height); + } + int max_height, max_width; + if (options->GetInteger(switches::kMaxHeight, &max_height) && + options->GetInteger(switches::kMaxWidth, &max_width)) { + SetMaximumSize(max_width, max_height); + } + bool resizable; + if (options->GetBoolean(switches::kResizable, &resizable)) { + SetResizable(resizable); + } + bool top; + if (options->GetBoolean(switches::kAlwaysOnTop, &top) && top) { + SetAlwaysOnTop(true); + } + bool fullscreen; + if (options->GetBoolean(switches::kFullscreen, &fullscreen) && fullscreen) { + SetFullscreen(true); + } + bool kiosk; + if (options->GetBoolean(switches::kKiosk, &kiosk) && kiosk) { + SetKiosk(kiosk); + } + std::string title("Atom Shell"); + options->GetString(switches::kTitle, &title); + SetTitle(title); + + // Then show it. + bool show = true; + options->GetBoolean(switches::kShow, &show); + if (show) + Show(); +} + +content::WebContents* NativeWindow::GetWebContents() const { + return inspectable_web_contents_->GetWebContents(); +} + +} // namespace atom diff --git a/browser/native_window.h b/browser/native_window.h new file mode 100644 index 0000000000..3898383d68 --- /dev/null +++ b/browser/native_window.h @@ -0,0 +1,83 @@ +// Copyright (c) 2013 GitHub, Inc. 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_BROWSER_NATIVE_WINDOW_H_ +#define ATOM_BROWSER_NATIVE_WINDOW_H_ + +#include + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" + +namespace base { +class DictionaryValue; +} + +namespace brightray { +class InspectableWebContents; +} + +namespace content { +class BrowserContext; +class WebContents; +} + +namespace gfx { +class Point; +class Rect; +class Size; +} + +namespace atom { + +class NativeWindow { + public: + virtual ~NativeWindow(); + + static NativeWindow* Create(content::BrowserContext* browser_context, + base::DictionaryValue* options); + + void InitFromOptions(base::DictionaryValue* options); + + virtual void Close() = 0; + virtual void Move(const gfx::Rect& pos) = 0; + virtual void Focus(bool focus) = 0; + virtual void Show() = 0; + virtual void Hide() = 0; + virtual void Maximize() = 0; + virtual void Unmaximize() = 0; + virtual void Minimize() = 0; + virtual void Restore() = 0; + virtual void SetFullscreen(bool fullscreen) = 0; + virtual bool IsFullscreen() = 0; + virtual void SetSize(const gfx::Size& size) = 0; + virtual gfx::Size GetSize() = 0; + virtual void SetMinimumSize(int width, int height) = 0; + virtual void SetMaximumSize(int width, int height) = 0; + virtual void SetResizable(bool resizable) = 0; + virtual void SetAlwaysOnTop(bool top) = 0; + virtual void SetPosition(const std::string& position) = 0; + virtual void SetPosition(const gfx::Point& position) = 0; + virtual gfx::Point GetPosition() = 0; + virtual void SetTitle(const std::string& title) = 0; + virtual void FlashFrame(bool flash) = 0; + virtual void SetKiosk(bool kiosk) = 0; + virtual bool IsKiosk() = 0; + + content::WebContents* GetWebContents() const; + + protected: + explicit NativeWindow(content::BrowserContext* browser_context, + base::DictionaryValue* options); + + private: + scoped_ptr inspectable_web_contents_; + + DISALLOW_COPY_AND_ASSIGN(NativeWindow); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_NATIVE_WINDOW_H_ diff --git a/browser/native_window_mac.h b/browser/native_window_mac.h new file mode 100644 index 0000000000..d243bd46bc --- /dev/null +++ b/browser/native_window_mac.h @@ -0,0 +1,71 @@ +// Copyright (c) 2013 GitHub, Inc. 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_BROWSER_NATIVE_WINDOW_MAC_H_ +#define ATOM_BROWSER_NATIVE_WINDOW_MAC_H_ + +#import + +#include "base/memory/scoped_ptr.h" +#include "browser/native_window.h" + +namespace atom { + +class NativeWindowMac : public NativeWindow { + public: + explicit NativeWindowMac(content::BrowserContext* browser_context, + base::DictionaryValue* options); + virtual ~NativeWindowMac(); + + // NativeWindow implementation. + virtual void Close() OVERRIDE; + virtual void Move(const gfx::Rect& pos) OVERRIDE; + virtual void Focus(bool focus) OVERRIDE; + virtual void Show() OVERRIDE; + virtual void Hide() OVERRIDE; + virtual void Maximize() OVERRIDE; + virtual void Unmaximize() OVERRIDE; + virtual void Minimize() OVERRIDE; + virtual void Restore() OVERRIDE; + virtual void SetFullscreen(bool fullscreen) OVERRIDE; + virtual bool IsFullscreen() OVERRIDE; + virtual void SetSize(const gfx::Size& size) OVERRIDE; + virtual gfx::Size GetSize() OVERRIDE; + virtual void SetMinimumSize(int width, int height) OVERRIDE; + virtual void SetMaximumSize(int width, int height) OVERRIDE; + virtual void SetResizable(bool resizable) OVERRIDE; + virtual void SetAlwaysOnTop(bool top) OVERRIDE; + virtual void SetPosition(const std::string& position) OVERRIDE; + virtual void SetPosition(const gfx::Point& position) OVERRIDE; + virtual gfx::Point GetPosition() OVERRIDE; + virtual void SetTitle(const std::string& title) OVERRIDE; + virtual void FlashFrame(bool flash) OVERRIDE; + virtual void SetKiosk(bool kiosk) OVERRIDE; + virtual bool IsKiosk() OVERRIDE; + + void set_is_fullscreen(bool fullscreen) { is_fullscreen_ = fullscreen; } + + NSWindow* window() const { return window_; } + + protected: + void SetNonLionFullscreen(bool fullscreen); + + private: + void InstallView(); + void UninstallView(); + + NSWindow* window_; + + bool is_fullscreen_; + bool is_kiosk_; + NSRect restored_bounds_; + + NSInteger attention_request_id_; // identifier from requestUserAttention + + DISALLOW_COPY_AND_ASSIGN(NativeWindowMac); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_NATIVE_WINDOW_MAC_H_ diff --git a/browser/native_window_mac.mm b/browser/native_window_mac.mm new file mode 100644 index 0000000000..3f0acc6c8c --- /dev/null +++ b/browser/native_window_mac.mm @@ -0,0 +1,289 @@ +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser/native_window_mac.h" + +// FIXME: The foundation_util.h is aborting our compilation, do not +// include it. +#define BASE_MAC_FOUNDATION_UTIL_H_ + +#include "base/mac/mac_util.h" +#include "base/sys_string_conversions.h" +#include "base/values.h" +#include "common/options_switches.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" + +namespace atom { + +NativeWindowMac::NativeWindowMac(content::BrowserContext* browser_context, + base::DictionaryValue* options) + : NativeWindow(browser_context, options), + is_fullscreen_(false), + is_kiosk_(false), + attention_request_id_(0) { + int width, height; + options->GetInteger(switches::kWidth, &width); + options->GetInteger(switches::kHeight, &height); + + NSRect main_screen_rect = [[[NSScreen screens] objectAtIndex:0] frame]; + NSRect cocoa_bounds = NSMakeRect( + (NSWidth(main_screen_rect) - width) / 2, + (NSHeight(main_screen_rect) - height) / 2, + width, + height); + NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask | NSResizableWindowMask | + NSTexturedBackgroundWindowMask; + window_ = [[NSWindow alloc] initWithContentRect:cocoa_bounds + styleMask:style_mask + backing:NSBackingStoreBuffered + defer:YES]; + + // Disable fullscreen button when 'fullscreen' is specified to false. + bool fullscreen; + if (!(options->GetBoolean(switches::kFullscreen, &fullscreen) && + !fullscreen)) { + NSUInteger collectionBehavior = [window() collectionBehavior]; + collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary; + [window() setCollectionBehavior:collectionBehavior]; + } + + NSView* view = GetWebContents()->GetView()->GetNativeView(); + [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + InstallView(); +} + +NativeWindowMac::~NativeWindowMac() { +} + +void NativeWindowMac::Close() { + [window() performClose:nil]; +} + +void NativeWindowMac::Move(const gfx::Rect& pos) { + NSRect cocoa_bounds = NSMakeRect(pos.x(), 0, + pos.width(), + pos.height()); + // Flip coordinates based on the primary screen. + NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; + cocoa_bounds.origin.y = + NSHeight([screen frame]) - pos.height() - pos.y(); + + [window() setFrame:cocoa_bounds display:YES]; +} + +void NativeWindowMac::Focus(bool focus) { + if (focus && [window() isVisible]) + [window() makeKeyAndOrderFront:nil]; + else + [window() orderBack:nil]; +} + +void NativeWindowMac::Show() { + [window() makeKeyAndOrderFront:nil]; +} + +void NativeWindowMac::Hide() { + [window() orderOut:nil]; +} + +void NativeWindowMac::Maximize() { + [window() zoom:nil]; +} + +void NativeWindowMac::Unmaximize() { + [window() zoom:nil]; +} + +void NativeWindowMac::Minimize() { + [window() miniaturize:nil]; +} + +void NativeWindowMac::Restore() { + [window() deminiaturize:nil]; +} + +void NativeWindowMac::SetFullscreen(bool fullscreen) { + if (fullscreen == is_fullscreen_) + return; + + if (base::mac::IsOSLionOrLater()) { + is_fullscreen_ = fullscreen; + [window() toggleFullScreen:nil]; + return; + } + + DCHECK(base::mac::IsOSSnowLeopard()); + + SetNonLionFullscreen(fullscreen); +} + +bool NativeWindowMac::IsFullscreen() { + return is_fullscreen_; +} + +void NativeWindowMac::SetNonLionFullscreen(bool fullscreen) { + if (fullscreen == is_fullscreen_) + return; + + is_fullscreen_ = fullscreen; + + // Fade to black. + const CGDisplayReservationInterval kFadeDurationSeconds = 0.6; + bool did_fade_out = false; + CGDisplayFadeReservationToken token; + if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token) == + kCGErrorSuccess) { + did_fade_out = true; + CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal, + kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true); + } + + // Since frameless windows insert the WebContentsView into the NSThemeFrame + // ([[window contentView] superview]), and since that NSThemeFrame is + // destroyed and recreated when we change the styleMask of the window, we + // need to remove the view from the window when we change the style, and + // add it back afterwards. + UninstallView(); + if (fullscreen) { + restored_bounds_ = [window() frame]; + [window() setStyleMask:NSBorderlessWindowMask]; + [window() setFrame:[window() + frameRectForContentRect:[[window() screen] frame]] + display:YES]; + base::mac::RequestFullScreen(base::mac::kFullScreenModeAutoHideAll); + } else { + base::mac::ReleaseFullScreen(base::mac::kFullScreenModeAutoHideAll); + NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask | NSResizableWindowMask | + NSTexturedBackgroundWindowMask; + [window() setStyleMask:style_mask]; + [window() setFrame:restored_bounds_ display:YES]; + } + InstallView(); + + // Fade back in. + if (did_fade_out) { + CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor, + kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false); + CGReleaseDisplayFadeReservation(token); + } + + is_fullscreen_ = fullscreen; +} + +void NativeWindowMac::SetSize(const gfx::Size& size) { + NSRect frame = [window_ frame]; + frame.origin.y -= size.height() - frame.size.height; + frame.size.width = size.width(); + frame.size.height = size.height(); + + [window() setFrame:frame display:YES]; +} + +gfx::Size NativeWindowMac::GetSize() { + NSRect frame = [window_ frame]; + return gfx::Size(frame.size.width, frame.size.height); +} + +void NativeWindowMac::SetMinimumSize(int width, int height) { + NSSize min_size = NSMakeSize(width, height); + NSView* content = [window() contentView]; + [window() setContentMinSize:[content convertSize:min_size toView:nil]]; +} + +void NativeWindowMac::SetMaximumSize(int width, int height) { + NSSize max_size = NSMakeSize(width, height); + NSView* content = [window() contentView]; + [window() setContentMaxSize:[content convertSize:max_size toView:nil]]; +} + +void NativeWindowMac::SetResizable(bool resizable) { + if (resizable) { + [[window() standardWindowButton:NSWindowZoomButton] setEnabled:YES]; + [window() setStyleMask:window().styleMask | NSResizableWindowMask]; + } else { + [[window() standardWindowButton:NSWindowZoomButton] setEnabled:NO]; + [window() setStyleMask:window().styleMask ^ NSResizableWindowMask]; + } +} + +void NativeWindowMac::SetAlwaysOnTop(bool top) { + [window() setLevel:(top ? NSFloatingWindowLevel : NSNormalWindowLevel)]; +} + +void NativeWindowMac::SetPosition(const std::string& position) { + if (position == "center") + [window() center]; +} + +void NativeWindowMac::SetPosition(const gfx::Point& position) { + Move(gfx::Rect(position, GetSize())); +} + +gfx::Point NativeWindowMac::GetPosition() { + NSRect frame = [window_ frame]; + NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; + + return gfx::Point(frame.origin.x, + NSHeight([screen frame]) - frame.origin.y - frame.size.height); +} + +void NativeWindowMac::SetTitle(const std::string& title) { + [window() setTitle:base::SysUTF8ToNSString(title)]; +} + +void NativeWindowMac::FlashFrame(bool flash) { + if (flash) { + attention_request_id_ = [NSApp requestUserAttention:NSInformationalRequest]; + } else { + [NSApp cancelUserAttentionRequest:attention_request_id_]; + attention_request_id_ = 0; + } +} + +void NativeWindowMac::SetKiosk(bool kiosk) { + if (kiosk) { + NSApplicationPresentationOptions options = + NSApplicationPresentationHideDock + + NSApplicationPresentationHideMenuBar + + NSApplicationPresentationDisableAppleMenu + + NSApplicationPresentationDisableProcessSwitching + + NSApplicationPresentationDisableForceQuit + + NSApplicationPresentationDisableSessionTermination + + NSApplicationPresentationDisableHideApplication; + [NSApp setPresentationOptions:options]; + is_kiosk_ = true; + SetNonLionFullscreen(true); + } else { + [NSApp setPresentationOptions:[NSApp currentSystemPresentationOptions]]; + is_kiosk_ = false; + SetNonLionFullscreen(false); + } +} + +bool NativeWindowMac::IsKiosk() { + return is_kiosk_; +} + +void NativeWindowMac::InstallView() { + NSView* view = GetWebContents()->GetView()->GetNativeView(); + [view setFrame:[[window() contentView] bounds]]; + [[window() contentView] addSubview:view]; +} + +void NativeWindowMac::UninstallView() { + NSView* view = GetWebContents()->GetView()->GetNativeView(); + [view removeFromSuperview]; +} + +// static +NativeWindow* NativeWindow::Create(content::BrowserContext* browser_context, + base::DictionaryValue* options) { + return new NativeWindowMac(browser_context, options); +} + +} // namespace atom diff --git a/common/options_switches.cc b/common/options_switches.cc new file mode 100644 index 0000000000..b2a09429f9 --- /dev/null +++ b/common/options_switches.cc @@ -0,0 +1,38 @@ +// Copyright (c) 2013 GitHub, Inc. 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/options_switches.h" + +namespace atom { + +namespace switches { + +const char kTitle[] = "title"; +const char kToolbar[] = "toolbar"; +const char kIcon[] = "icon"; +const char kFrame[] = "frame"; +const char kShow[] = "show"; +const char kPosition[] = "position"; +const char kX[] = "x"; +const char kY[] = "y"; +const char kWidth[] = "width"; +const char kHeight[] = "height"; +const char kMinWidth[] = "min_width"; +const char kMinHeight[] = "min_height"; +const char kMaxWidth[] = "max_width"; +const char kMaxHeight[] = "max_height"; +const char kResizable[] = "resizable"; +const char kAsDesktop[] = "as_desktop"; +const char kFullscreen[] = "fullscreen"; + +// Start with the kiosk mode, see Opera's page for description: +// http://www.opera.com/support/mastering/kiosk/ +const char kKiosk[] = "kiosk"; + +// Make windows stays on the top of all other windows. +const char kAlwaysOnTop[] = "always-on-top"; + +} // namespace switches + +} // namespace atom diff --git a/common/options_switches.h b/common/options_switches.h new file mode 100644 index 0000000000..6dcb0f82ea --- /dev/null +++ b/common/options_switches.h @@ -0,0 +1,36 @@ +// Copyright (c) 2013 GitHub, Inc. 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_OPTIONS_SWITCHES_ +#define ATOM_COMMON_OPTIONS_SWITCHES_ + +namespace atom { + +namespace switches { + +extern const char kTitle[]; +extern const char kToolbar[]; +extern const char kIcon[]; +extern const char kFrame[]; +extern const char kShow[]; +extern const char kPosition[]; +extern const char kX[]; +extern const char kY[]; +extern const char kWidth[]; +extern const char kHeight[]; +extern const char kMinWidth[]; +extern const char kMinHeight[]; +extern const char kMaxWidth[]; +extern const char kMaxHeight[]; +extern const char kResizable[]; +extern const char kAsDesktop[]; +extern const char kFullscreen[]; +extern const char kKiosk[]; +extern const char kAlwaysOnTop[]; + +} // namespace switches + +} // namespace atom + +#endif // ATOM_COMMON_OPTIONS_SWITCHES_