diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-06 23:14:30 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-06 23:14:30 +0000 |
commit | 8037357607b0672d19d31f86c2c88b3b03d56dd2 (patch) | |
tree | b726b77eda02576ab9ce65bd4c913bcc916cffef /ash | |
parent | 16ae48403450f92836bf216e7782b74c827f4b66 (diff) | |
download | chromium_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.gyp | 3 | ||||
-rw-r--r-- | ash/shell.cc | 5 | ||||
-rw-r--r-- | ash/shell.h | 2 | ||||
-rw-r--r-- | ash/shell/window_type_launcher.cc | 54 | ||||
-rw-r--r-- | ash/shell/window_type_launcher.h | 4 | ||||
-rw-r--r-- | ash/shell_unittest.cc | 2 | ||||
-rw-r--r-- | ash/wm/activation_controller.cc | 25 | ||||
-rw-r--r-- | ash/wm/modal_container_layout_manager_unittest.cc | 2 | ||||
-rw-r--r-- | ash/wm/stacking_controller.cc | 9 | ||||
-rw-r--r-- | ash/wm/toplevel_window_event_filter.h | 1 | ||||
-rw-r--r-- | ash/wm/window_modality_controller.cc | 76 | ||||
-rw-r--r-- | ash/wm/window_modality_controller.h | 42 | ||||
-rw-r--r-- | ash/wm/window_modality_controller_unittest.cc | 148 |
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 |