summaryrefslogtreecommitdiffstats
path: root/ash
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-06 23:14:30 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-06 23:14:30 +0000
commit8037357607b0672d19d31f86c2c88b3b03d56dd2 (patch)
treeb726b77eda02576ab9ce65bd4c913bcc916cffef /ash
parent16ae48403450f92836bf216e7782b74c827f4b66 (diff)
downloadchromium_src-8037357607b0672d19d31f86c2c88b3b03d56dd2.zip
chromium_src-8037357607b0672d19d31f86c2c88b3b03d56dd2.tar.gz
chromium_src-8037357607b0672d19d31f86c2c88b3b03d56dd2.tar.bz2
Adds support for Window Modality to Ash.
http://crbug.com/109290 TEST=unittests Review URL: http://codereview.chromium.org/9123016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@116767 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r--ash/ash.gyp3
-rw-r--r--ash/shell.cc5
-rw-r--r--ash/shell.h2
-rw-r--r--ash/shell/window_type_launcher.cc54
-rw-r--r--ash/shell/window_type_launcher.h4
-rw-r--r--ash/shell_unittest.cc2
-rw-r--r--ash/wm/activation_controller.cc25
-rw-r--r--ash/wm/modal_container_layout_manager_unittest.cc2
-rw-r--r--ash/wm/stacking_controller.cc9
-rw-r--r--ash/wm/toplevel_window_event_filter.h1
-rw-r--r--ash/wm/window_modality_controller.cc76
-rw-r--r--ash/wm/window_modality_controller.h42
-rw-r--r--ash/wm/window_modality_controller_unittest.cc148
13 files changed, 344 insertions, 29 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp
index 6943a98..265e273 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -144,6 +144,8 @@
'wm/toplevel_window_event_filter.h',
'wm/window_frame.cc',
'wm/window_frame.h',
+ 'wm/window_modality_controller.cc',
+ 'wm/window_modality_controller.h',
'wm/window_properties.cc',
'wm/window_properties.h',
'wm/window_util.cc',
@@ -210,6 +212,7 @@
'wm/shelf_layout_manager_unittest.cc',
'wm/toplevel_layout_manager_unittest.cc',
'wm/toplevel_window_event_filter_unittest.cc',
+ 'wm/window_modality_controller_unittest.cc',
'wm/workspace_controller_unittest.cc',
'wm/workspace/workspace_manager_unittest.cc',
diff --git a/ash/shell.cc b/ash/shell.cc
index 7c7ad6b..5eb0072 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -32,6 +32,7 @@
#include "ash/wm/status_area_layout_manager.h"
#include "ash/wm/toplevel_layout_manager.h"
#include "ash/wm/toplevel_window_event_filter.h"
+#include "ash/wm/window_modality_controller.h"
#include "ash/wm/workspace_controller.h"
#include "base/bind.h"
#include "base/command_line.h"
@@ -142,6 +143,7 @@ Shell::Shell(ShellDelegate* delegate)
Shell::~Shell() {
RemoveRootWindowEventFilter(input_method_filter_.get());
+ RemoveRootWindowEventFilter(window_modality_controller_.get());
RemoveRootWindowEventFilter(accelerator_filter_.get());
// TooltipController needs a valid shell instance. We delete it before
@@ -230,6 +232,9 @@ void Shell::Init() {
input_method_filter_.reset(new internal::InputMethodEventFilter);
AddRootWindowEventFilter(input_method_filter_.get());
+ window_modality_controller_.reset(new internal::WindowModalityController);
+ AddRootWindowEventFilter(window_modality_controller_.get());
+
accelerator_filter_.reset(new internal::AcceleratorFilter);
AddRootWindowEventFilter(accelerator_filter_.get());
diff --git a/ash/shell.h b/ash/shell.h
index 05a0afa..7581676 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -44,6 +44,7 @@ class InputMethodEventFilter;
class ShadowController;
class StackingController;
class TooltipController;
+class WindowModalityController;
class WorkspaceController;
}
@@ -146,6 +147,7 @@ class ASH_EXPORT Shell {
scoped_ptr<internal::StackingController> stacking_controller_;
scoped_ptr<internal::ActivationController> activation_controller_;
+ scoped_ptr<internal::WindowModalityController> window_modality_controller_;
scoped_ptr<internal::DragDropController> drag_drop_controller_;
scoped_ptr<internal::WorkspaceController> workspace_controller_;
scoped_ptr<internal::ShadowController> shadow_controller_;
diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc
index 2113197..46a26fc 100644
--- a/ash/shell/window_type_launcher.cc
+++ b/ash/shell/window_type_launcher.cc
@@ -37,8 +37,9 @@ int g_color_index = 0;
class ModalWindow : public views::WidgetDelegateView,
public views::ButtonListener {
public:
- ModalWindow()
- : color_(g_colors[g_color_index]),
+ explicit ModalWindow(ui::ModalType modal_type)
+ : modal_type_(modal_type),
+ color_(g_colors[g_color_index]),
ALLOW_THIS_IN_INITIALIZER_LIST(open_button_(
new views::NativeTextButton(this, ASCIIToUTF16("Moar!")))) {
++g_color_index %= arraysize(g_colors);
@@ -47,9 +48,10 @@ class ModalWindow : public views::WidgetDelegateView,
virtual ~ModalWindow() {
}
- static void OpenModalWindow(aura::Window* parent) {
+ static void OpenModalWindow(aura::Window* parent, ui::ModalType modal_type) {
views::Widget* widget =
- views::Widget::CreateWindowWithParent(new ModalWindow, parent);
+ views::Widget::CreateWindowWithParent(new ModalWindow(modal_type),
+ parent);
widget->GetNativeView()->SetName("ModalWindow");
widget->Show();
}
@@ -80,17 +82,18 @@ class ModalWindow : public views::WidgetDelegateView,
return ASCIIToUTF16("Modal Window");
}
virtual ui::ModalType GetModalType() const OVERRIDE {
- return ui::MODAL_TYPE_WINDOW;
+ return modal_type_;
}
// Overridden from views::ButtonListener:
virtual void ButtonPressed(views::Button* sender,
const views::Event& event) OVERRIDE {
DCHECK(sender == open_button_);
- OpenModalWindow(GetWidget()->GetNativeView());
+ OpenModalWindow(GetWidget()->GetNativeView(), modal_type_);
}
private:
+ ui::ModalType modal_type_;
SkColor color_;
views::NativeTextButton* open_button_;
@@ -143,7 +146,7 @@ class NonModalTransient : public views::WidgetDelegateView {
void InitWindowTypeLauncher() {
views::Widget* widget =
views::Widget::CreateWindowWithBounds(new WindowTypeLauncher,
- gfx::Rect(120, 150, 400, 300));
+ gfx::Rect(120, 150, 400, 400));
widget->GetNativeView()->SetName("WindowTypeLauncher");
ash::internal::SetShadowType(widget->GetNativeView(),
ash::internal::SHADOW_TYPE_NONE);
@@ -164,9 +167,12 @@ WindowTypeLauncher::WindowTypeLauncher()
ALLOW_THIS_IN_INITIALIZER_LIST(widgets_button_(
new views::NativeTextButton(
this, ASCIIToUTF16("Show Example Widgets")))),
- ALLOW_THIS_IN_INITIALIZER_LIST(modal_button_(
+ ALLOW_THIS_IN_INITIALIZER_LIST(system_modal_button_(
new views::NativeTextButton(
- this, ASCIIToUTF16("Open Modal Window")))),
+ this, ASCIIToUTF16("Open System Modal Window")))),
+ ALLOW_THIS_IN_INITIALIZER_LIST(window_modal_button_(
+ new views::NativeTextButton(
+ this, ASCIIToUTF16("Open Window Modal Window")))),
ALLOW_THIS_IN_INITIALIZER_LIST(transient_button_(
new views::NativeTextButton(
this, ASCIIToUTF16("Open Non-Modal Transient Window")))),
@@ -178,7 +184,8 @@ WindowTypeLauncher::WindowTypeLauncher()
AddChildView(bubble_button_);
AddChildView(lock_button_);
AddChildView(widgets_button_);
- AddChildView(modal_button_);
+ AddChildView(system_modal_button_);
+ AddChildView(window_modal_button_);
AddChildView(transient_button_);
AddChildView(examples_button_);
set_context_menu_controller(this);
@@ -219,14 +226,19 @@ void WindowTypeLauncher::Layout() {
5, lock_button_->y() - widgets_ps.height() - 5,
widgets_ps.width(), widgets_ps.height());
- gfx::Size modal_ps = modal_button_->GetPreferredSize();
- modal_button_->SetBounds(
- 5, widgets_button_->y() - modal_ps.height() - 5,
- modal_ps.width(), modal_ps.height());
+ gfx::Size system_modal_ps = system_modal_button_->GetPreferredSize();
+ system_modal_button_->SetBounds(
+ 5, widgets_button_->y() - system_modal_ps.height() - 5,
+ system_modal_ps.width(), system_modal_ps.height());
+
+ gfx::Size window_modal_ps = window_modal_button_->GetPreferredSize();
+ window_modal_button_->SetBounds(
+ 5, system_modal_button_->y() - window_modal_ps.height() - 5,
+ window_modal_ps.width(), window_modal_ps.height());
gfx::Size transient_ps = transient_button_->GetPreferredSize();
transient_button_->SetBounds(
- 5, modal_button_->y() - transient_ps.height() - 5,
+ 5, window_modal_button_->y() - transient_ps.height() - 5,
transient_ps.width(), transient_ps.height());
gfx::Size examples_ps = examples_button_->GetPreferredSize();
@@ -235,10 +247,6 @@ void WindowTypeLauncher::Layout() {
examples_ps.width(), examples_ps.height());
}
-gfx::Size WindowTypeLauncher::GetPreferredSize() {
- return gfx::Size(300, 500);
-}
-
bool WindowTypeLauncher::OnMousePressed(const views::MouseEvent& event) {
// Overridden so we get OnMouseReleased and can show the context menu.
return true;
@@ -274,8 +282,12 @@ void WindowTypeLauncher::ButtonPressed(views::Button* sender,
CreateLockScreen();
} else if (sender == widgets_button_) {
CreateWidgetsWindow();
- } else if (sender == modal_button_) {
- ModalWindow::OpenModalWindow(GetWidget()->GetNativeView());
+ } else if (sender == system_modal_button_) {
+ ModalWindow::OpenModalWindow(GetWidget()->GetNativeView(),
+ ui::MODAL_TYPE_SYSTEM);
+ } else if (sender == window_modal_button_) {
+ ModalWindow::OpenModalWindow(GetWidget()->GetNativeView(),
+ ui::MODAL_TYPE_WINDOW);
} else if (sender == transient_button_) {
NonModalTransient::OpenNonModalTransient(GetWidget()->GetNativeView());
} else if (sender == examples_button_) {
diff --git a/ash/shell/window_type_launcher.h b/ash/shell/window_type_launcher.h
index 5e7fd6f..08ebf22 100644
--- a/ash/shell/window_type_launcher.h
+++ b/ash/shell/window_type_launcher.h
@@ -40,7 +40,6 @@ class WindowTypeLauncher : public views::WidgetDelegateView,
// Overridden from views::View:
virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
virtual void Layout() OVERRIDE;
- virtual gfx::Size GetPreferredSize() OVERRIDE;
virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE;
// Overridden from views::WidgetDelegate:
@@ -66,7 +65,8 @@ class WindowTypeLauncher : public views::WidgetDelegateView,
views::NativeTextButton* bubble_button_;
views::NativeTextButton* lock_button_;
views::NativeTextButton* widgets_button_;
- views::NativeTextButton* modal_button_;
+ views::NativeTextButton* system_modal_button_;
+ views::NativeTextButton* window_modal_button_;
views::NativeTextButton* transient_button_;
views::NativeTextButton* examples_button_;
scoped_ptr<views::MenuRunner> menu_runner_;
diff --git a/ash/shell_unittest.cc b/ash/shell_unittest.cc
index 22b3209..6604799 100644
--- a/ash/shell_unittest.cc
+++ b/ash/shell_unittest.cc
@@ -66,7 +66,7 @@ class ModalWindow : public views::WidgetDelegateView {
return ASCIIToUTF16("Modal Window");
}
virtual ui::ModalType GetModalType() const OVERRIDE {
- return ui::MODAL_TYPE_WINDOW;
+ return ui::MODAL_TYPE_SYSTEM;
}
private:
diff --git a/ash/wm/activation_controller.cc b/ash/wm/activation_controller.cc
index f379f5e..5ea7466 100644
--- a/ash/wm/activation_controller.cc
+++ b/ash/wm/activation_controller.cc
@@ -6,12 +6,15 @@
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
+#include "ash/wm/window_modality_controller.h"
#include "ash/wm/window_util.h"
#include "base/auto_reset.h"
#include "ui/aura/client/activation_delegate.h"
+#include "ui/aura/client/aura_constants.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
+#include "ui/base/ui_base_types.h"
namespace ash {
namespace internal {
@@ -43,6 +46,20 @@ bool CanActivateWindow(aura::Window* window) {
SupportsChildActivation(window->parent());
}
+// When a modal window is activated, we bring its entire transient parent chain
+// to the front. This function must be called before the modal transient is
+// stacked at the top to ensure correct stacking order.
+void StackTransientParentsBelowModalWindow(aura::Window* window) {
+ if (window->GetIntProperty(aura::client::kModalKey) != ui::MODAL_TYPE_WINDOW)
+ return;
+
+ aura::Window* transient_parent = window->transient_parent();
+ while (transient_parent) {
+ transient_parent->parent()->StackChildAtTop(transient_parent);
+ transient_parent = transient_parent->transient_parent();
+ }
+}
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -80,6 +97,13 @@ aura::Window* ActivationController::GetActivatableWindow(aura::Window* window) {
// ActivationController, aura::client::ActivationClient implementation:
void ActivationController::ActivateWindow(aura::Window* window) {
+ aura::Window* window_modal_transient =
+ WindowModalityController::GetWindowModalTransient(window);
+ if (window_modal_transient) {
+ ActivateWindow(window_modal_transient);
+ return;
+ }
+
// Prevent recursion when called from focus.
if (updating_activation_)
return;
@@ -107,6 +131,7 @@ void ActivationController::ActivateWindow(aura::Window* window) {
if (old_active && aura::client::GetActivationDelegate(old_active))
aura::client::GetActivationDelegate(old_active)->OnLostActive();
if (window) {
+ StackTransientParentsBelowModalWindow(window);
window->parent()->StackChildAtTop(window);
if (aura::client::GetActivationDelegate(window))
aura::client::GetActivationDelegate(window)->OnActivated();
diff --git a/ash/wm/modal_container_layout_manager_unittest.cc b/ash/wm/modal_container_layout_manager_unittest.cc
index 744bfb4..0ee0750 100644
--- a/ash/wm/modal_container_layout_manager_unittest.cc
+++ b/ash/wm/modal_container_layout_manager_unittest.cc
@@ -53,7 +53,7 @@ class TestWindow : public views::WidgetDelegateView {
return this;
}
virtual ui::ModalType GetModalType() const OVERRIDE {
- return modal_ ? ui::MODAL_TYPE_WINDOW : ui::MODAL_TYPE_NONE;
+ return modal_ ? ui::MODAL_TYPE_SYSTEM : ui::MODAL_TYPE_NONE;
}
private:
diff --git a/ash/wm/stacking_controller.cc b/ash/wm/stacking_controller.cc
index 570aa77..be368629 100644
--- a/ash/wm/stacking_controller.cc
+++ b/ash/wm/stacking_controller.cc
@@ -10,6 +10,7 @@
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
+#include "ui/base/ui_base_types.h"
namespace ash {
namespace internal {
@@ -19,9 +20,9 @@ aura::Window* GetContainer(int id) {
return Shell::GetInstance()->GetContainer(id);
}
-bool IsWindowModal(aura::Window* window) {
+bool IsSystemModal(aura::Window* window) {
return window->transient_parent() &&
- window->GetIntProperty(aura::client::kModalKey);
+ window->GetIntProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM;
}
} // namespace
@@ -47,7 +48,7 @@ aura::Window* StackingController::GetDefaultParent(aura::Window* window) {
switch (window->type()) {
case aura::client::WINDOW_TYPE_NORMAL:
case aura::client::WINDOW_TYPE_POPUP:
- if (IsWindowModal(window))
+ if (IsSystemModal(window))
return GetModalContainer(window);
return always_on_top_controller_->GetContainer(window);
case aura::client::WINDOW_TYPE_PANEL:
@@ -68,7 +69,7 @@ aura::Window* StackingController::GetDefaultParent(aura::Window* window) {
aura::Window* StackingController::GetModalContainer(
aura::Window* window) const {
- if (!IsWindowModal(window))
+ if (!IsSystemModal(window))
return NULL;
// If screen lock is not active, all modal windows are placed into the
diff --git a/ash/wm/toplevel_window_event_filter.h b/ash/wm/toplevel_window_event_filter.h
index ea3661d..4da26d8 100644
--- a/ash/wm/toplevel_window_event_filter.h
+++ b/ash/wm/toplevel_window_event_filter.h
@@ -9,6 +9,7 @@
#include <set>
#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
#include "ui/aura/client/window_move_client.h"
#include "ui/aura/event_filter.h"
#include "ash/ash_export.h"
diff --git a/ash/wm/window_modality_controller.cc b/ash/wm/window_modality_controller.cc
new file mode 100644
index 0000000..86e4c02
--- /dev/null
+++ b/ash/wm/window_modality_controller.cc
@@ -0,0 +1,76 @@
+// 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 "ash/wm/window_modality_controller.h"
+
+#include "ash/wm/window_util.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/event.h"
+#include "ui/aura/window.h"
+#include "ui/base/ui_base_types.h"
+
+namespace ash {
+namespace internal {
+
+namespace {
+
+bool TransientChildIsWindowModal(aura::Window* window) {
+ return window->GetIntProperty(aura::client::kModalKey) ==
+ ui::MODAL_TYPE_WINDOW;
+}
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowModalityController, public:
+
+WindowModalityController::WindowModalityController() : aura::EventFilter(NULL) {
+}
+
+WindowModalityController::~WindowModalityController() {
+}
+
+aura::Window* WindowModalityController::GetWindowModalTransient(
+ aura::Window* window) {
+ if (!window)
+ return NULL;
+
+ aura::Window::Windows::const_iterator it;
+ for (it = window->transient_children().begin();
+ it != window->transient_children().end();
+ ++it) {
+ if (TransientChildIsWindowModal(*it)) {
+ if (!(*it)->transient_children().empty())
+ return GetWindowModalTransient(*it);
+ return *it;
+ }
+ }
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowModalityController, aura::EventFilter implementation:
+
+bool WindowModalityController::PreHandleKeyEvent(aura::Window* target,
+ aura::KeyEvent* event) {
+ return !!GetWindowModalTransient(target);
+}
+
+bool WindowModalityController::PreHandleMouseEvent(aura::Window* target,
+ aura::MouseEvent* event) {
+ aura::Window* modal_transient_child = GetWindowModalTransient(target);
+ if (modal_transient_child && event->type() == ui::ET_MOUSE_PRESSED)
+ ActivateWindow(modal_transient_child);
+ return !!modal_transient_child;
+}
+
+ui::TouchStatus WindowModalityController::PreHandleTouchEvent(
+ aura::Window* target,
+ aura::TouchEvent* event) {
+ // TODO: make touch work with modals.
+ return ui::TOUCH_STATUS_UNKNOWN;
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/wm/window_modality_controller.h b/ash/wm/window_modality_controller.h
new file mode 100644
index 0000000..0b43965
--- /dev/null
+++ b/ash/wm/window_modality_controller.h
@@ -0,0 +1,42 @@
+// 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 ASH_WM_WINDOW_MODALITY_CONTROLLER_H_
+#define ASH_WM_WINDOW_MODALITY_CONTROLLER_H_
+#pragma once
+
+#include "base/compiler_specific.h"
+#include "ui/aura/event_filter.h"
+
+namespace ash {
+namespace internal {
+
+// WindowModalityController is an event filter that consumes events sent to
+// windows that are the transient parents of window-modal windows. This filter
+// must be added to the RootWindowEventFilter so that activation works properly.
+class WindowModalityController : public aura::EventFilter {
+ public:
+ WindowModalityController();
+ virtual ~WindowModalityController();
+
+ // Returns the window-modal transient child of |window|, or NULL if |window|
+ // does not have any window-modal transient children.
+ static aura::Window* GetWindowModalTransient(aura::Window* window);
+
+ // Overridden from aura::EventFilter:
+ virtual bool PreHandleKeyEvent(aura::Window* target,
+ aura::KeyEvent* event) OVERRIDE;
+ virtual bool PreHandleMouseEvent(aura::Window* target,
+ aura::MouseEvent* event) OVERRIDE;
+ virtual ui::TouchStatus PreHandleTouchEvent(aura::Window* target,
+ aura::TouchEvent* event) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WindowModalityController);
+};
+
+} // namespace internal
+} // namespace ash
+
+#endif // ASH_WM_WINDOW_MODALITY_CONTROLLER_H_
diff --git a/ash/wm/window_modality_controller_unittest.cc b/ash/wm/window_modality_controller_unittest.cc
new file mode 100644
index 0000000..2a0766c
--- /dev/null
+++ b/ash/wm/window_modality_controller_unittest.cc
@@ -0,0 +1,148 @@
+// 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 "ash/wm/window_modality_controller.h"
+
+#include "ash/test/aura_shell_test_base.h"
+#include "ash/wm/window_util.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/window.h"
+#include "ui/base/ui_base_types.h"
+
+namespace ash {
+namespace internal {
+
+typedef test::AuraShellTestBase WindowModalityControllerTest;
+
+namespace {
+
+bool ValidateStacking(aura::Window* parent, int ids[], int count) {
+ for (int i = 0; i < count; ++i) {
+ if (parent->children().at(i)->id() != ids[i])
+ return false;
+ }
+ return true;
+}
+
+}
+
+// Creates three windows, w1, w11, and w12. w11 is a non-modal transient, w12 is
+// a modal transient.
+// Validates:
+// - it should be possible to activate w12 even when w11 is open.
+// - activating w1 activates w12 and updates stacking order appropriately.
+TEST_F(WindowModalityControllerTest, BasicActivation) {
+ aura::test::TestWindowDelegate d;
+ scoped_ptr<aura::Window> w1(
+ aura::test::CreateTestWindowWithDelegate(&d, -1, gfx::Rect(), NULL));
+ scoped_ptr<aura::Window> w11(
+ aura::test::CreateTestWindowWithDelegate(&d, -11, gfx::Rect(), NULL));
+ scoped_ptr<aura::Window> w12(
+ aura::test::CreateTestWindowWithDelegate(&d, -12, gfx::Rect(), NULL));
+
+ w1->AddTransientChild(w11.get());
+ ActivateWindow(w1.get());
+ EXPECT_TRUE(IsActiveWindow(w1.get()));
+ ActivateWindow(w11.get());
+ EXPECT_TRUE(IsActiveWindow(w11.get()));
+
+ w12->SetIntProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
+ w1->AddTransientChild(w12.get());
+ ActivateWindow(w12.get());
+ EXPECT_TRUE(IsActiveWindow(w12.get()));
+
+ ActivateWindow(w11.get());
+ EXPECT_TRUE(IsActiveWindow(w11.get()));
+
+ int check1[] = { -1, -12, -11 };
+ EXPECT_TRUE(ValidateStacking(w1->parent(), check1, arraysize(check1)));
+
+ ActivateWindow(w1.get());
+ EXPECT_TRUE(IsActiveWindow(w12.get()));
+ // Transient children are always stacked above their transient parent, which
+ // is why this order is not -11, -1, -12.
+ int check2[] = { -1, -11, -12 };
+ EXPECT_TRUE(ValidateStacking(w1->parent(), check2, arraysize(check2)));
+}
+
+// Create two toplevel windows w1 and w2, and nest two modals w11 and w111 below
+// w1.
+// Validates:
+// - activating w1 while w11/w111 is showing always activates most deeply nested
+// descendant.
+TEST_F(WindowModalityControllerTest, NestedModals) {
+ aura::test::TestWindowDelegate d;
+ scoped_ptr<aura::Window> w1(
+ aura::test::CreateTestWindowWithDelegate(&d, -1, gfx::Rect(), NULL));
+ scoped_ptr<aura::Window> w11(
+ aura::test::CreateTestWindowWithDelegate(&d, -11, gfx::Rect(), NULL));
+ scoped_ptr<aura::Window> w111(
+ aura::test::CreateTestWindowWithDelegate(&d, -111, gfx::Rect(), NULL));
+ scoped_ptr<aura::Window> w2(
+ aura::test::CreateTestWindowWithDelegate(&d, -2, gfx::Rect(), NULL));
+
+ w1->AddTransientChild(w11.get());
+ w11->AddTransientChild(w111.get());
+
+ ActivateWindow(w1.get());
+ EXPECT_TRUE(IsActiveWindow(w1.get()));
+ ActivateWindow(w2.get());
+ EXPECT_TRUE(IsActiveWindow(w2.get()));
+
+ // Set up modality.
+ w11->SetIntProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
+ w111->SetIntProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
+
+ ActivateWindow(w1.get());
+ EXPECT_TRUE(IsActiveWindow(w111.get()));
+ int check1[] = { -2, -1, -11, -111 };
+ EXPECT_TRUE(ValidateStacking(w1->parent(), check1, arraysize(check1)));
+
+ ActivateWindow(w11.get());
+ EXPECT_TRUE(IsActiveWindow(w111.get()));
+ EXPECT_TRUE(ValidateStacking(w1->parent(), check1, arraysize(check1)));
+
+ ActivateWindow(w111.get());
+ EXPECT_TRUE(IsActiveWindow(w111.get()));
+ EXPECT_TRUE(ValidateStacking(w1->parent(), check1, arraysize(check1)));
+
+ ActivateWindow(w2.get());
+ EXPECT_TRUE(IsActiveWindow(w2.get()));
+ int check2[] = { -1, -11, -111, -2 };
+ EXPECT_TRUE(ValidateStacking(w1->parent(), check2, arraysize(check2)));
+}
+
+// Modality also prevents events from being passed to the transient parent.
+TEST_F(WindowModalityControllerTest, Events) {
+ aura::test::TestWindowDelegate d;
+ scoped_ptr<aura::Window> w1(aura::test::CreateTestWindowWithDelegate(&d, -1,
+ gfx::Rect(0, 0, 100, 100), NULL));
+ scoped_ptr<aura::Window> w11(aura::test::CreateTestWindowWithDelegate(&d, -11,
+ gfx::Rect(20, 20, 50, 50), NULL));
+
+ w1->AddTransientChild(w11.get());
+
+ {
+ // Clicking a point within w1 should activate that window.
+ aura::test::EventGenerator generator(gfx::Point(10, 10));
+ generator.ClickLeftButton();
+ EXPECT_TRUE(IsActiveWindow(w1.get()));
+ }
+
+ w11->SetIntProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
+
+ {
+ // Clicking a point within w1 should activate w11.
+ aura::test::EventGenerator generator(gfx::Point(10, 10));
+ generator.ClickLeftButton();
+ EXPECT_TRUE(IsActiveWindow(w11.get()));
+ }
+}
+
+
+} // namespace internal
+} // namespace ash \ No newline at end of file