Merge pull request #6140 from electron/parent

Add support for child windows
This commit is contained in:
Cheng Zhao 2016-06-20 07:27:05 +00:00 committed by GitHub
commit 3428874907
16 changed files with 532 additions and 28 deletions

View file

@ -95,9 +95,16 @@ Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) {
mate::Dictionary(isolate, web_contents->GetWrapper()).Set(
"browserWindowOptions", options);
// The parent window.
mate::Handle<Window> parent;
if (options.Get("parent", &parent))
parent_window_.Reset(isolate, parent.ToV8());
// Creates BrowserWindow.
window_.reset(NativeWindow::Create(web_contents->managed_web_contents(),
options));
window_.reset(NativeWindow::Create(
web_contents->managed_web_contents(),
options,
parent.IsEmpty() ? nullptr : parent->window_.get()));
web_contents->SetOwnerWindow(window_.get());
window_->InitFromOptions(options);
window_->AddObserver(this);
@ -120,10 +127,32 @@ Window::~Window() {
base::MessageLoop::current()->DeleteSoon(FROM_HERE, window_.release());
}
void Window::AfterInit(v8::Isolate* isolate) {
mate::TrackableObject<Window>::AfterInit(isolate);
// We can only append this window to parent window's child windows after this
// window's JS wrapper gets initialized.
mate::Handle<Window> parent;
if (!parent_window_.IsEmpty() &&
mate::ConvertFromV8(isolate, GetParentWindow(), &parent))
parent->child_windows_.Set(isolate, ID(), GetWrapper());
}
void Window::WillCloseWindow(bool* prevent_default) {
*prevent_default = Emit("close");
}
void Window::WillDestoryNativeObject() {
// Close all child windows before closing current window.
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
for (v8::Local<v8::Value> value : child_windows_.Values(isolate())) {
mate::Handle<Window> child;
if (mate::ConvertFromV8(isolate(), value, &child))
child->window_->CloseImmediately();
}
}
void Window::OnWindowClosed() {
api_web_contents_->DestroyWebContents();
@ -136,6 +165,8 @@ void Window::OnWindowClosed() {
Emit("closed");
RemoveFromParentChildWindows();
// Destroy the native class when window is closed.
base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure());
}
@ -280,6 +311,10 @@ void Window::Show() {
}
void Window::ShowInactive() {
// This method doesn't make sense for modal window..
if (IsModal())
return;
window_->ShowInactive();
}
@ -291,6 +326,10 @@ bool Window::IsVisible() {
return window_->IsVisible();
}
bool Window::IsEnabled() {
return window_->IsEnabled();
}
void Window::Maximize() {
window_->Maximize();
}
@ -650,6 +689,42 @@ void Window::SetAspectRatio(double aspect_ratio, mate::Arguments* args) {
window_->SetAspectRatio(aspect_ratio, extra_size);
}
void Window::SetParentWindow(v8::Local<v8::Value> value,
mate::Arguments* args) {
if (IsModal()) {
args->ThrowError("Can not be called for modal window");
return;
}
mate::Handle<Window> parent;
if (value->IsNull()) {
RemoveFromParentChildWindows();
parent_window_.Reset();
window_->SetParentWindow(nullptr);
} else if (mate::ConvertFromV8(isolate(), value, &parent)) {
parent_window_.Reset(isolate(), value);
window_->SetParentWindow(parent->window_.get());
parent->child_windows_.Set(isolate(), ID(), GetWrapper());
} else {
args->ThrowError("Must pass BrowserWindow instance or null");
}
}
v8::Local<v8::Value> Window::GetParentWindow() const {
if (parent_window_.IsEmpty())
return v8::Null(isolate());
else
return v8::Local<v8::Value>::New(isolate(), parent_window_);
}
std::vector<v8::Local<v8::Object>> Window::GetChildWindows() const {
return child_windows_.Values(isolate());
}
bool Window::IsModal() const {
return window_->is_modal();
}
v8::Local<v8::Value> Window::GetNativeWindowHandle() {
gfx::AcceleratedWidget handle = window_->GetAcceleratedWidget();
return ToBuffer(
@ -675,6 +750,17 @@ v8::Local<v8::Value> Window::WebContents(v8::Isolate* isolate) {
return v8::Local<v8::Value>::New(isolate, web_contents_);
}
void Window::RemoveFromParentChildWindows() {
if (parent_window_.IsEmpty())
return;
mate::Handle<Window> parent;
if (!mate::ConvertFromV8(isolate(), GetParentWindow(), &parent))
return;
parent->child_windows_.Remove(ID());
}
// static
void Window::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype) {
@ -688,6 +774,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
.SetMethod("showInactive", &Window::ShowInactive)
.SetMethod("hide", &Window::Hide)
.SetMethod("isVisible", &Window::IsVisible)
.SetMethod("isEnabled", &Window::IsEnabled)
.SetMethod("maximize", &Window::Maximize)
.SetMethod("unmaximize", &Window::Unmaximize)
.SetMethod("isMaximized", &Window::IsMaximized)
@ -697,6 +784,12 @@ void Window::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setFullScreen", &Window::SetFullScreen)
.SetMethod("isFullScreen", &Window::IsFullscreen)
.SetMethod("setAspectRatio", &Window::SetAspectRatio)
#if !defined(OS_WIN)
.SetMethod("setParentWindow", &Window::SetParentWindow)
#endif
.SetMethod("getParentWindow", &Window::GetParentWindow)
.SetMethod("getChildWindows", &Window::GetChildWindows)
.SetMethod("isModal", &Window::IsModal)
.SetMethod("getNativeWindowHandle", &Window::GetNativeWindowHandle)
.SetMethod("getBounds", &Window::GetBounds)
.SetMethod("setBounds", &Window::SetBounds)

View file

@ -6,15 +6,16 @@
#define ATOM_BROWSER_API_ATOM_API_WINDOW_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "ui/gfx/image/image.h"
#include "atom/browser/api/trackable_object.h"
#include "atom/browser/native_window.h"
#include "atom/browser/native_window_observer.h"
#include "atom/common/api/atom_api_native_image.h"
#include "atom/common/key_weak_map.h"
#include "native_mate/handle.h"
class GURL;
@ -54,8 +55,12 @@ class Window : public mate::TrackableObject<Window>,
Window(v8::Isolate* isolate, const mate::Dictionary& options);
~Window() override;
// TrackableObject:
void AfterInit(v8::Isolate* isolate) override;
// NativeWindowObserver:
void WillCloseWindow(bool* prevent_default) override;
void WillDestoryNativeObject() override;
void OnWindowClosed() override;
void OnWindowBlur() override;
void OnWindowFocus() override;
@ -94,6 +99,7 @@ class Window : public mate::TrackableObject<Window>,
void ShowInactive();
void Hide();
bool IsVisible();
bool IsEnabled();
void Maximize();
void Unmaximize();
bool IsMaximized();
@ -159,6 +165,10 @@ class Window : public mate::TrackableObject<Window>,
void SetMenuBarVisibility(bool visible);
bool IsMenuBarVisible();
void SetAspectRatio(double aspect_ratio, mate::Arguments* args);
void SetParentWindow(v8::Local<v8::Value> value, mate::Arguments* args);
v8::Local<v8::Value> GetParentWindow() const;
std::vector<v8::Local<v8::Object>> GetChildWindows() const;
bool IsModal() const;
v8::Local<v8::Value> GetNativeWindowHandle();
#if defined(OS_WIN)
@ -181,6 +191,9 @@ class Window : public mate::TrackableObject<Window>,
int32_t ID() const;
v8::Local<v8::Value> WebContents(v8::Isolate* isolate);
// Remove this window from parent window's |child_windows_|.
void RemoveFromParentChildWindows();
#if defined(OS_WIN)
typedef std::map<UINT, MessageCallback> MessageCallbackMap;
MessageCallbackMap messages_callback_map_;
@ -188,6 +201,8 @@ class Window : public mate::TrackableObject<Window>,
v8::Global<v8::Value> web_contents_;
v8::Global<v8::Value> menu_;
v8::Global<v8::Value> parent_window_;
KeyWeakMap<int> child_windows_;
api::WebContents* api_web_contents_;

View file

@ -46,7 +46,8 @@ namespace atom {
NativeWindow::NativeWindow(
brightray::InspectableWebContents* inspectable_web_contents,
const mate::Dictionary& options)
const mate::Dictionary& options,
NativeWindow* parent)
: content::WebContentsObserver(inspectable_web_contents->GetWebContents()),
has_frame_(true),
transparent_(false),
@ -56,12 +57,17 @@ NativeWindow::NativeWindow(
sheet_offset_x_(0.0),
sheet_offset_y_(0.0),
aspect_ratio_(0.0),
parent_(parent),
is_modal_(false),
inspectable_web_contents_(inspectable_web_contents),
weak_factory_(this) {
options.Get(options::kFrame, &has_frame_);
options.Get(options::kTransparent, &transparent_);
options.Get(options::kEnableLargerThanScreen, &enable_larger_than_screen_);
if (parent)
options.Get("modal", &is_modal_);
// Tell the content module to initialize renderer widget with transparent
// mode.
ui::GpuSwitchingManager::SetTransparent(transparent_);
@ -292,6 +298,10 @@ bool NativeWindow::HasModalDialog() {
return has_dialog_attached_;
}
void NativeWindow::SetParentWindow(NativeWindow* parent) {
parent_ = parent;
}
void NativeWindow::FocusOnWebView() {
web_contents()->GetRenderViewHost()->GetWidget()->Focus();
}
@ -397,6 +407,9 @@ void NativeWindow::CloseContents(content::WebContents* source) {
inspectable_web_contents_ = nullptr;
Observe(nullptr);
FOR_EACH_OBSERVER(NativeWindowObserver, observers_,
WillDestoryNativeObject());
// When the web contents is gone, close the window immediately, but the
// memory will not be freed until you call delete.
// In this way, it would be safe to manage windows via smart pointers. If you
@ -611,7 +624,7 @@ void NativeWindow::ScheduleUnresponsiveEvent(int ms) {
void NativeWindow::NotifyWindowUnresponsive() {
window_unresposive_closure_.Cancel();
if (!is_closed_ && !HasModalDialog())
if (!is_closed_ && !HasModalDialog() && IsEnabled())
FOR_EACH_OBSERVER(NativeWindowObserver,
observers_,
OnRendererUnresponsive());

View file

@ -81,7 +81,8 @@ class NativeWindow : public base::SupportsUserData,
// managing the window's live.
static NativeWindow* Create(
brightray::InspectableWebContents* inspectable_web_contents,
const mate::Dictionary& options);
const mate::Dictionary& options,
NativeWindow* parent = nullptr);
// Find a window from its WebContents
static NativeWindow* FromWebContents(content::WebContents* web_contents);
@ -97,6 +98,7 @@ class NativeWindow : public base::SupportsUserData,
virtual void ShowInactive() = 0;
virtual void Hide() = 0;
virtual bool IsVisible() = 0;
virtual bool IsEnabled() = 0;
virtual void Maximize() = 0;
virtual void Unmaximize() = 0;
virtual bool IsMaximized() = 0;
@ -158,6 +160,7 @@ class NativeWindow : public base::SupportsUserData,
virtual void SetFocusable(bool focusable);
virtual void SetMenu(ui::MenuModel* menu);
virtual bool HasModalDialog();
virtual void SetParentWindow(NativeWindow* parent);
virtual gfx::NativeWindow GetNativeWindow() = 0;
virtual gfx::AcceleratedWidget GetAcceleratedWidget() = 0;
@ -255,9 +258,13 @@ class NativeWindow : public base::SupportsUserData,
has_dialog_attached_ = has_dialog_attached;
}
NativeWindow* parent() const { return parent_; }
bool is_modal() const { return is_modal_; }
protected:
NativeWindow(brightray::InspectableWebContents* inspectable_web_contents,
const mate::Dictionary& options);
const mate::Dictionary& options,
NativeWindow* parent);
// Convert draggable regions in raw format to SkRegion format. Caller is
// responsible for deleting the returned SkRegion instance.
@ -329,6 +336,12 @@ class NativeWindow : public base::SupportsUserData,
double aspect_ratio_;
gfx::Size aspect_ratio_extraSize_;
// The parent window, it is guaranteed to be valid during this window's life.
NativeWindow* parent_;
// Is this a modal window.
bool is_modal_;
// The page this window is viewing.
brightray::InspectableWebContents* inspectable_web_contents_;

View file

@ -22,7 +22,8 @@ namespace atom {
class NativeWindowMac : public NativeWindow {
public:
NativeWindowMac(brightray::InspectableWebContents* inspectable_web_contents,
const mate::Dictionary& options);
const mate::Dictionary& options,
NativeWindow* parent);
~NativeWindowMac() override;
// NativeWindow:
@ -34,6 +35,7 @@ class NativeWindowMac : public NativeWindow {
void ShowInactive() override;
void Hide() override;
bool IsVisible() override;
bool IsEnabled() override;
void Maximize() override;
void Unmaximize() override;
bool IsMaximized() override;
@ -78,6 +80,7 @@ class NativeWindowMac : public NativeWindow {
bool IsDocumentEdited() override;
void SetIgnoreMouseEvents(bool ignore) override;
bool HasModalDialog() override;
void SetParentWindow(NativeWindow* parent) override;
gfx::NativeWindow GetNativeWindow() override;
gfx::AcceleratedWidget GetAcceleratedWidget() override;
void SetProgressBar(double progress) override;
@ -144,9 +147,6 @@ class NativeWindowMac : public NativeWindow {
// The "titleBarStyle" option.
TitleBarStyle title_bar_style_;
// Whether to hide the native toolbar under fullscreen mode.
bool should_hide_native_toolbar_in_fullscreen_;
DISALLOW_COPY_AND_ASSIGN(NativeWindowMac);
};

View file

@ -429,8 +429,9 @@ namespace atom {
NativeWindowMac::NativeWindowMac(
brightray::InspectableWebContents* web_contents,
const mate::Dictionary& options)
: NativeWindow(web_contents, options),
const mate::Dictionary& options,
NativeWindow* parent)
: NativeWindow(web_contents, options, parent),
is_kiosk_(false),
attention_request_id_(0),
title_bar_style_(NORMAL) {
@ -501,6 +502,11 @@ NativeWindowMac::NativeWindowMac(
window_delegate_.reset([[AtomNSWindowDelegate alloc] initWithShell:this]);
[window_ setDelegate:window_delegate_];
// Only use native parent window for non-modal windows.
if (parent && !is_modal()) {
SetParentWindow(parent);
}
if (transparent()) {
// Setting the background color to clear will also hide the shadow.
[window_ setBackgroundColor:[NSColor clearColor]];
@ -595,6 +601,12 @@ NativeWindowMac::~NativeWindowMac() {
}
void NativeWindowMac::Close() {
// When this is a sheet showing, performClose won't work.
if (is_modal() && parent() && IsVisible()) {
CloseImmediately();
return;
}
if (!IsClosable()) {
WindowList::WindowCloseCancelled(this);
return;
@ -624,6 +636,12 @@ bool NativeWindowMac::IsFocused() {
}
void NativeWindowMac::Show() {
if (is_modal() && parent()) {
[parent()->GetNativeWindow() beginSheet:window_
completionHandler:^(NSModalResponse) {}];
return;
}
// This method is supposed to put focus on window, however if the app does not
// have focus then "makeKeyAndOrderFront" will only show the window.
[NSApp activateIgnoringOtherApps:YES];
@ -636,6 +654,12 @@ void NativeWindowMac::ShowInactive() {
}
void NativeWindowMac::Hide() {
if (is_modal() && parent()) {
[window_ orderOut:nil];
[parent()->GetNativeWindow() endSheet:window_];
return;
}
[window_ orderOut:nil];
}
@ -643,6 +667,10 @@ bool NativeWindowMac::IsVisible() {
return [window_ isVisible];
}
bool NativeWindowMac::IsEnabled() {
return [window_ attachedSheet] == nil;
}
void NativeWindowMac::Maximize() {
if (IsMaximized())
return;
@ -911,6 +939,21 @@ bool NativeWindowMac::HasModalDialog() {
return [window_ attachedSheet] != nil;
}
void NativeWindowMac::SetParentWindow(NativeWindow* parent) {
if (is_modal())
return;
NativeWindow::SetParentWindow(parent);
// Remove current parent window.
if ([window_ parentWindow])
[[window_ parentWindow] removeChildWindow:window_];
// Set new current window.
if (parent)
[parent->GetNativeWindow() addChildWindow:window_ ordered:NSWindowAbove];
}
gfx::NativeWindow NativeWindowMac::GetNativeWindow() {
return window_;
}
@ -1132,8 +1175,9 @@ void NativeWindowMac::SetCollectionBehavior(bool on, NSUInteger flag) {
// static
NativeWindow* NativeWindow::Create(
brightray::InspectableWebContents* inspectable_web_contents,
const mate::Dictionary& options) {
return new NativeWindowMac(inspectable_web_contents, options);
const mate::Dictionary& options,
NativeWindow* parent) {
return new NativeWindowMac(inspectable_web_contents, options, parent);
}
} // namespace atom

View file

@ -33,6 +33,9 @@ class NativeWindowObserver {
// Called when the window is gonna closed.
virtual void WillCloseWindow(bool* prevent_default) {}
// Called before the native window object is going to be destroyed.
virtual void WillDestoryNativeObject() {}
// Called when the window is closed.
virtual void OnWindowClosed() {}

View file

@ -29,6 +29,7 @@
#include "ui/views/window/client_view.h"
#include "ui/views/widget/native_widget_private.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/core/shadow_types.h"
#if defined(USE_X11)
@ -36,6 +37,7 @@
#include "atom/browser/ui/views/global_menu_bar_x11.h"
#include "atom/browser/ui/views/frameless_view.h"
#include "atom/browser/ui/views/native_frame_view.h"
#include "atom/browser/ui/x/event_disabler.h"
#include "atom/browser/ui/x/window_state_watcher.h"
#include "atom/browser/ui/x/x_window_utils.h"
#include "base/strings/string_util.h"
@ -125,14 +127,16 @@ class NativeWindowClientView : public views::ClientView {
NativeWindowViews::NativeWindowViews(
brightray::InspectableWebContents* web_contents,
const mate::Dictionary& options)
: NativeWindow(web_contents, options),
const mate::Dictionary& options,
NativeWindow* parent)
: NativeWindow(web_contents, options, parent),
window_(new views::Widget),
web_view_(inspectable_web_contents()->GetView()->GetView()),
menu_bar_autohide_(false),
menu_bar_visible_(false),
menu_bar_alt_pressed_(false),
keyboard_event_handler_(new views::UnhandledKeyboardEventHandler),
disable_count_(0),
use_content_size_(false),
movable_(true),
resizable_(true),
@ -186,6 +190,9 @@ NativeWindowViews::NativeWindowViews(
params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
#if defined(OS_WIN)
if (parent)
params.parent = parent->GetNativeWindow();
params.native_widget =
new views::DesktopNativeWidgetAura(window_.get());
atom_desktop_window_tree_host_win_ = new AtomDesktopWindowTreeHostWin(
@ -236,12 +243,23 @@ NativeWindowViews::NativeWindowViews(
state_atom_list.push_back(GetAtom("_NET_WM_STATE_FULLSCREEN"));
}
std::string window_type;
options.Get(options::kType, &window_type);
if (parent) {
SetParentWindow(parent);
// Force using dialog type for child window.
window_type = "dialog";
// Modal window needs the _NET_WM_STATE_MODAL hint.
if (is_modal())
state_atom_list.push_back(GetAtom("_NET_WM_STATE_MODAL"));
}
ui::SetAtomArrayProperty(GetAcceleratedWidget(), "_NET_WM_STATE", "ATOM",
state_atom_list);
// Set the _NET_WM_WINDOW_TYPE.
std::string window_type;
if (options.Get(options::kType, &window_type))
if (!window_type.empty())
SetWindowType(GetAcceleratedWidget(), window_type);
#endif
@ -337,6 +355,9 @@ bool NativeWindowViews::IsFocused() {
}
void NativeWindowViews::Show() {
if (is_modal() && NativeWindow::parent())
static_cast<NativeWindowViews*>(NativeWindow::parent())->SetEnabled(false);
window_->native_widget_private()->ShowWithWindowState(GetRestoredState());
NotifyWindowShow();
@ -359,6 +380,9 @@ void NativeWindowViews::ShowInactive() {
}
void NativeWindowViews::Hide() {
if (is_modal() && NativeWindow::parent())
static_cast<NativeWindowViews*>(NativeWindow::parent())->SetEnabled(true);
window_->Hide();
NotifyWindowHide();
@ -373,6 +397,14 @@ bool NativeWindowViews::IsVisible() {
return window_->IsVisible();
}
bool NativeWindowViews::IsEnabled() {
#if defined(OS_WIN)
return ::IsWindowEnabled(GetAcceleratedWidget());
#elif defined(USE_X11)
return !event_disabler_.get();
#endif
}
void NativeWindowViews::Maximize() {
if (IsVisible())
window_->Maximize();
@ -772,6 +804,32 @@ void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) {
Layout();
}
void NativeWindowViews::SetParentWindow(NativeWindow* parent) {
NativeWindow::SetParentWindow(parent);
#if defined(USE_X11)
XDisplay* xdisplay = gfx::GetXDisplay();
XSetTransientForHint(
xdisplay, GetAcceleratedWidget(),
parent? parent->GetAcceleratedWidget() : DefaultRootWindow(xdisplay));
#elif defined(OS_WIN) && defined(DEBUG)
// Should work, but does not, it seems that the views toolkit doesn't support
// reparenting on desktop.
if (parent) {
::SetParent(GetAcceleratedWidget(), parent->GetAcceleratedWidget());
views::Widget::ReparentNativeView(GetNativeWindow(),
parent->GetNativeWindow());
wm::AddTransientChild(parent->GetNativeWindow(), GetNativeWindow());
} else {
if (!GetNativeWindow()->parent())
return;
::SetParent(GetAcceleratedWidget(), NULL);
views::Widget::ReparentNativeView(GetNativeWindow(), nullptr);
wm::RemoveTransientChild(GetNativeWindow()->parent(), GetNativeWindow());
}
#endif
}
gfx::NativeWindow NativeWindowViews::GetNativeWindow() {
return window_->GetNativeWindow();
}
@ -867,6 +925,33 @@ void NativeWindowViews::SetIcon(const gfx::ImageSkia& icon) {
}
#endif
void NativeWindowViews::SetEnabled(bool enable) {
// Handle multiple calls of SetEnabled correctly.
if (enable) {
--disable_count_;
if (disable_count_ != 0)
return;
} else {
++disable_count_;
if (disable_count_ != 1)
return;
}
#if defined(OS_WIN)
::EnableWindow(GetAcceleratedWidget(), enable);
#elif defined(USE_X11)
views::DesktopWindowTreeHostX11* tree_host =
views::DesktopWindowTreeHostX11::GetHostForXID(GetAcceleratedWidget());
if (enable) {
tree_host->RemoveEventRewriter(event_disabler_.get());
event_disabler_.reset();
} else {
event_disabler_.reset(new EventDisabler);
tree_host->AddEventRewriter(event_disabler_.get());
}
#endif
}
void NativeWindowViews::OnWidgetActivationChanged(
views::Widget* widget, bool active) {
if (widget != window_.get())
@ -900,6 +985,15 @@ void NativeWindowViews::OnWidgetBoundsChanged(
}
void NativeWindowViews::DeleteDelegate() {
if (is_modal() && NativeWindow::parent()) {
NativeWindowViews* parent =
static_cast<NativeWindowViews*>(NativeWindow::parent());
// Enable parent window after current window gets closed.
parent->SetEnabled(true);
// Focus on parent window.
parent->Focus(true);
}
NotifyWindowClosed();
}
@ -1110,8 +1204,9 @@ ui::WindowShowState NativeWindowViews::GetRestoredState() {
// static
NativeWindow* NativeWindow::Create(
brightray::InspectableWebContents* inspectable_web_contents,
const mate::Dictionary& options) {
return new NativeWindowViews(inspectable_web_contents, options);
const mate::Dictionary& options,
NativeWindow* parent) {
return new NativeWindowViews(inspectable_web_contents, options, parent);
}
} // namespace atom

View file

@ -32,6 +32,8 @@ class WindowStateWatcher;
#if defined(OS_WIN)
class AtomDesktopWindowTreeHostWin;
#elif defined(USE_X11)
class EventDisabler;
#endif
class NativeWindowViews : public NativeWindow,
@ -42,7 +44,8 @@ class NativeWindowViews : public NativeWindow,
public views::WidgetObserver {
public:
NativeWindowViews(brightray::InspectableWebContents* inspectable_web_contents,
const mate::Dictionary& options);
const mate::Dictionary& options,
NativeWindow* parent);
~NativeWindowViews() override;
// NativeWindow:
@ -54,6 +57,7 @@ class NativeWindowViews : public NativeWindow,
void ShowInactive() override;
void Hide() override;
bool IsVisible() override;
bool IsEnabled() override;
void Maximize() override;
void Unmaximize() override;
bool IsMaximized() override;
@ -94,6 +98,7 @@ class NativeWindowViews : public NativeWindow,
void SetIgnoreMouseEvents(bool ignore) override;
void SetFocusable(bool focusable) override;
void SetMenu(ui::MenuModel* menu_model) override;
void SetParentWindow(NativeWindow* parent) override;
gfx::NativeWindow GetNativeWindow() override;
void SetOverlayIcon(const gfx::Image& overlay,
const std::string& description) override;
@ -113,6 +118,8 @@ class NativeWindowViews : public NativeWindow,
void SetIcon(const gfx::ImageSkia& icon);
#endif
void SetEnabled(bool enable);
views::Widget* widget() const { return window_.get(); }
#if defined(OS_WIN)
@ -187,6 +194,9 @@ class NativeWindowViews : public NativeWindow,
// Handles window state events.
std::unique_ptr<WindowStateWatcher> window_state_watcher_;
// To disable the mouse events.
std::unique_ptr<EventDisabler> event_disabler_;
// The "resizable" flag on Linux is implemented by setting size constraints,
// we need to make sure size constraints are restored when window becomes
// resizable again.
@ -220,6 +230,9 @@ class NativeWindowViews : public NativeWindow,
// Map from accelerator to menu item's command id.
accelerator_util::AcceleratorTable accelerator_table_;
// How many times the Disable has been called.
int disable_count_;
bool use_content_size_;
bool movable_;
bool resizable_;

View file

@ -0,0 +1,27 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/ui/x/event_disabler.h"
namespace atom {
EventDisabler::EventDisabler() {
}
EventDisabler::~EventDisabler() {
}
ui::EventRewriteStatus EventDisabler::RewriteEvent(
const ui::Event& event,
std::unique_ptr<ui::Event>* rewritten_event) {
return ui::EVENT_REWRITE_DISCARD;
}
ui::EventRewriteStatus EventDisabler::NextDispatchEvent(
const ui::Event& last_event,
std::unique_ptr<ui::Event>* new_event) {
return ui::EVENT_REWRITE_CONTINUE;
}
} // namespace atom

View file

@ -0,0 +1,32 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_X_EVENT_DISABLER_H_
#define ATOM_BROWSER_UI_X_EVENT_DISABLER_H_
#include "base/macros.h"
#include "ui/events/event_rewriter.h"
namespace atom {
class EventDisabler : public ui::EventRewriter {
public:
EventDisabler();
~EventDisabler() override;
// ui::EventRewriter:
ui::EventRewriteStatus RewriteEvent(
const ui::Event& event,
std::unique_ptr<ui::Event>* rewritten_event) override;
ui::EventRewriteStatus NextDispatchEvent(
const ui::Event& last_event,
std::unique_ptr<ui::Event>* new_event) override;
private:
DISALLOW_COPY_AND_ASSIGN(EventDisabler);
};
} // namespace atom
#endif // ATOM_BROWSER_UI_X_EVENT_DISABLER_H_

View file

@ -14,10 +14,6 @@
namespace atom {
namespace internal {
} // namespace internal
// Like ES6's WeakMap, but the key is Integer and the value is Weak Pointer.
template<typename K>
class KeyWeakMap {
@ -57,7 +53,7 @@ class KeyWeakMap {
}
// Returns all objects.
std::vector<v8::Local<v8::Object>> Values(v8::Isolate* isolate) {
std::vector<v8::Local<v8::Object>> Values(v8::Isolate* isolate) const {
std::vector<v8::Local<v8::Object>> keys;
keys.reserve(map_.size());
for (const auto& iter : map_) {

View file

@ -59,6 +59,39 @@ win.loadURL('https://github.com')
Note that even for apps that use `ready-to-show` event, it is still recommended
to set `backgroundColor` to make app feel more native.
## Parent and child windows
By using `parent` option, you can create child windows:
```javascript
let top = new BrowserWindow()
let child = new BrowserWindow({parent: top})
```
The `child` window will always show on top of the `top` window.
### Modal windows
A modal window is a child window that disables parent window, to create a modal
window, you have to set both `parent` and `modal` options:
```javascript
let child = new BrowserWindow({parent: top, modal: true, show: false})
child.loadURL('https://github.com')
child.once('ready-to-show', () => {
child.show()
})
```
### Platform notices
* On macOS the child windows will keep the relative position to parent window
when parent window moves, while on Windows and Linux child windows will not
move.
* On Windows it is not supported to change parent window dynamically.
* On Linux the type of modal windows will be changed to `dialog`.
* On Linux many desktop environments do not support hiding a modal window.
## Class: BrowserWindow
`BrowserWindow` is an
@ -116,6 +149,9 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
`true`.
* `frame` Boolean - Specify `false` to create a
[Frameless Window](frameless-window.md). Default is `true`.
* `parent` BrowserWindow - Specify parent window. Default is `null`.
* `modal` Boolean - Whether this is a modal window. This only works when the
window is a child window. Default is `false`.
* `acceptFirstMouse` Boolean - Whether the web view accepts a single
mouse-down event that simultaneously activates the window. Default is
`false`.
@ -532,6 +568,10 @@ Hides the window.
Returns a boolean, whether the window is visible to the user.
### `win.isModal()`
Returns whether current window is a modal window.
### `win.maximize()`
Maximizes the window.
@ -1017,3 +1057,18 @@ events.
Changes whether the window can be focused.
[blink-feature-string]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
### `win.setParentWindow(parent)` _Linux_ _macOS_
* `parent` BrowserWindow
Sets `parent` as current window's parent window, passing `null` will turn
current window into a top-level window.
### `win.getParentWindow()`
Returns the parent window.
### `win.getChildWindows()`
Returns all child windows.

View file

@ -286,6 +286,8 @@
'atom/browser/ui/win/notify_icon.h',
'atom/browser/ui/win/taskbar_host.cc',
'atom/browser/ui/win/taskbar_host.h',
'atom/browser/ui/x/event_disabler.cc',
'atom/browser/ui/x/event_disabler.h',
'atom/browser/ui/x/window_state_watcher.cc',
'atom/browser/ui/x/window_state_watcher.h',
'atom/browser/ui/x/x_window_utils.cc',

View file

@ -117,7 +117,6 @@ BrowserWindow.fromDevToolsWebContents = (webContents) => {
}
// Helpers.
Object.assign(BrowserWindow.prototype, {
loadURL (...args) {
return this.webContents.loadURL.apply(this.webContents, args)

View file

@ -836,6 +836,110 @@ describe('browser-window module', function () {
})
})
describe('parent window', function () {
let c = null
beforeEach(function () {
if (c != null) c.destroy()
c = new BrowserWindow({show: false, parent: w})
})
afterEach(function () {
if (c != null) c.destroy()
c = null
})
describe('parent option', function () {
it('sets parent window', function () {
assert.equal(c.getParentWindow(), w)
})
it('adds window to child windows of parent', function () {
assert.deepEqual(w.getChildWindows(), [c])
})
it('removes from child windows of parent when window is closed', function (done) {
c.once('closed', () => {
assert.deepEqual(w.getChildWindows(), [])
done()
})
c.close()
})
})
describe('win.setParentWindow(parent)', function () {
if (process.platform === 'win32') return
beforeEach(function () {
if (c != null) c.destroy()
c = new BrowserWindow({show: false})
})
it('sets parent window', function () {
assert.equal(w.getParentWindow(), null)
assert.equal(c.getParentWindow(), null)
c.setParentWindow(w)
assert.equal(c.getParentWindow(), w)
c.setParentWindow(null)
assert.equal(c.getParentWindow(), null)
})
it('adds window to child windows of parent', function () {
assert.deepEqual(w.getChildWindows(), [])
c.setParentWindow(w)
assert.deepEqual(w.getChildWindows(), [c])
c.setParentWindow(null)
assert.deepEqual(w.getChildWindows(), [])
})
it('removes from child windows of parent when window is closed', function (done) {
c.once('closed', () => {
assert.deepEqual(w.getChildWindows(), [])
done()
})
c.setParentWindow(w)
c.close()
})
})
describe('modal option', function () {
// The isEnabled API is not reliable on macOS.
if (process.platform === 'darwin') return
beforeEach(function () {
if (c != null) c.destroy()
c = new BrowserWindow({show: false, parent: w, modal: true})
})
it('disables parent window', function () {
assert.equal(w.isEnabled(), true)
c.show()
assert.equal(w.isEnabled(), false)
})
it('enables parent window when closed', function (done) {
c.once('closed', () => {
assert.equal(w.isEnabled(), true)
done()
})
c.show()
c.close()
})
it('disables parent window recursively', function () {
let c2 = new BrowserWindow({show: false, parent: w, modal: true})
c.show()
assert.equal(w.isEnabled(), false)
c2.show()
assert.equal(w.isEnabled(), false)
c.destroy()
assert.equal(w.isEnabled(), false)
c2.destroy()
assert.equal(w.isEnabled(), true)
})
})
})
describe('window.webContents.send(channel, args...)', function () {
it('throws an error when the channel is missing', function () {
assert.throws(function () {