diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-16 18:25:51 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-16 18:25:51 +0000 |
commit | e29014c26af112b0affdb84edfb59d249369d90b (patch) | |
tree | 2d6577d4fcbd7de015dba73c346296071cd17745 /ui/aura_shell | |
parent | 1ab4211da8b4f9cfee01dfd1c537e6b42a1426ea (diff) | |
download | chromium_src-e29014c26af112b0affdb84edfb59d249369d90b.zip chromium_src-e29014c26af112b0affdb84edfb59d249369d90b.tar.gz chromium_src-e29014c26af112b0affdb84edfb59d249369d90b.tar.bz2 |
Beginnings of Window Modality support.
http://crbug.com/93936
TEST=none yet
Review URL: http://codereview.chromium.org/8574033
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@110326 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/aura_shell')
21 files changed, 790 insertions, 28 deletions
diff --git a/ui/aura_shell/aura_shell.gyp b/ui/aura_shell/aura_shell.gyp index 240f49b..4b7769d 100644 --- a/ui/aura_shell/aura_shell.gyp +++ b/ui/aura_shell/aura_shell.gyp @@ -61,6 +61,11 @@ 'launcher/view_model.h', 'launcher/view_model_utils.cc', 'launcher/view_model_utils.h', + 'modal_container_layout_manager.cc', + 'modal_container_layout_manager.h', + 'modality_event_filter.cc', + 'modality_event_filter.h', + 'modality_event_filter_delegate.h', 'property_util.cc', 'property_util.h', 'shelf_layout_controller.cc', @@ -122,6 +127,7 @@ 'launcher/launcher_model_unittest.cc', 'launcher/view_model_unittest.cc', 'launcher/view_model_utils_unittest.cc', + 'modal_container_layout_manager_unittest.cc', 'run_all_unittests.cc', 'shell_unittest.cc', 'stacking_controller_unittest.cc', diff --git a/ui/aura_shell/desktop_event_filter.cc b/ui/aura_shell/desktop_event_filter.cc index d69ead1..dc4b4cf 100644 --- a/ui/aura_shell/desktop_event_filter.cc +++ b/ui/aura_shell/desktop_event_filter.cc @@ -70,19 +70,17 @@ bool DesktopEventFilter::PreHandleKeyEvent(aura::Window* target, bool DesktopEventFilter::PreHandleMouseEvent(aura::Window* target, aura::MouseEvent* event) { + // We must always update the cursor, otherwise the cursor can get stuck if an + // event filter registered with us consumes the event. + if (event->type() == ui::ET_MOUSE_MOVED) + UpdateCursor(target, event); + if (FilterMouseEvent(target, event)) return true; - switch (event->type()) { - case ui::ET_MOUSE_PRESSED: - ActivateIfNecessary(target, event); - break; - case ui::ET_MOUSE_MOVED: - HandleMouseMoved(target, event); - break; - default: - break; - } + if (event->type() == ui::ET_MOUSE_PRESSED) + ActivateIfNecessary(target, event); + return false; } @@ -112,8 +110,8 @@ void DesktopEventFilter::ActivateIfNecessary(aura::Window* window, } } -void DesktopEventFilter::HandleMouseMoved(aura::Window* target, - aura::MouseEvent* event) { +void DesktopEventFilter::UpdateCursor(aura::Window* target, + aura::MouseEvent* event) { gfx::NativeCursor cursor = target->GetCursor(event->location()); if (event->flags() & ui::EF_IS_NON_CLIENT) { int window_component = diff --git a/ui/aura_shell/desktop_event_filter.h b/ui/aura_shell/desktop_event_filter.h index a5cb2c8..e4d1c45 100644 --- a/ui/aura_shell/desktop_event_filter.h +++ b/ui/aura_shell/desktop_event_filter.h @@ -44,7 +44,7 @@ class AURA_SHELL_EXPORT DesktopEventFilter : public aura::EventFilter { // Updates the cursor if the target provides a custom one, and provides // default resize cursors for window edges. - void HandleMouseMoved(aura::Window* target, aura::MouseEvent* event); + void UpdateCursor(aura::Window* target, aura::MouseEvent* event); // Dispatches event to addtional filters. Returns false or // ui::TOUCH_STATUS_UNKNOWN if event is consumed. diff --git a/ui/aura_shell/examples/aura_shell_main.cc b/ui/aura_shell/examples/aura_shell_main.cc index 20a3623..dd2badc 100644 --- a/ui/aura_shell/examples/aura_shell_main.cc +++ b/ui/aura_shell/examples/aura_shell_main.cc @@ -60,7 +60,15 @@ class ShellDelegateImpl : public aura_shell::ShellDelegate { } }; -} // namesapce +} // namespace + +namespace aura_shell { +namespace examples { + +void InitWindowTypeLauncher(); + +} // namespace examples +} // namespace aura_shell int main(int argc, char** argv) { CommandLine::Init(argc, argv); diff --git a/ui/aura_shell/examples/window_type_launcher.cc b/ui/aura_shell/examples/window_type_launcher.cc index 37cc500..3ab4484 100644 --- a/ui/aura_shell/examples/window_type_launcher.cc +++ b/ui/aura_shell/examples/window_type_launcher.cc @@ -24,6 +24,120 @@ using views::MenuRunner; namespace aura_shell { namespace examples { +namespace { + +SkColor g_colors[] = { SK_ColorRED, + SK_ColorYELLOW, + SK_ColorBLUE, + SK_ColorGREEN }; +int g_color_index = 0; + +class ModalWindow : public views::WidgetDelegateView, + public views::ButtonListener { + public: + ModalWindow() + : 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); + AddChildView(open_button_); + } + virtual ~ModalWindow() { + } + + static void OpenModalWindow(aura::Window* parent) { + views::Widget* widget = + views::Widget::CreateWindowWithParent(new ModalWindow, parent); + widget->GetNativeView()->set_name("ModalWindow"); + widget->Show(); + } + + // Overridden from views::View: + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { + canvas->FillRect(color_, GetLocalBounds()); + } + virtual gfx::Size GetPreferredSize() OVERRIDE { + return gfx::Size(200, 200); + } + virtual void Layout() OVERRIDE { + gfx::Size open_ps = open_button_->GetPreferredSize(); + gfx::Rect local_bounds = GetLocalBounds(); + open_button_->SetBounds( + 5, local_bounds.bottom() - open_ps.height() - 5, + open_ps.width(), open_ps.height()); + } + + // Overridden from views::WidgetDelegate: + virtual views::View* GetContentsView() OVERRIDE { + return this; + } + virtual bool CanResize() const OVERRIDE { + return true; + } + virtual string16 GetWindowTitle() const OVERRIDE { + return ASCIIToUTF16("Modal Window"); + } + virtual bool IsModal() const OVERRIDE { + return true; + } + + // Overridden from views::ButtonListener: + virtual void ButtonPressed(views::Button* sender, + const views::Event& event) OVERRIDE { + DCHECK(sender == open_button_); + OpenModalWindow(GetWidget()->GetNativeView()); + } + + private: + SkColor color_; + views::NativeTextButton* open_button_; + + DISALLOW_COPY_AND_ASSIGN(ModalWindow); +}; + +class NonModalTransient : public views::WidgetDelegateView { + public: + NonModalTransient() + : color_(g_colors[g_color_index]) { + ++g_color_index %= arraysize(g_colors); + } + virtual ~NonModalTransient() { + } + + static void OpenNonModalTransient(aura::Window* parent) { + views::Widget* widget = + views::Widget::CreateWindowWithParent(new NonModalTransient, parent); + widget->GetNativeView()->set_name("NonModalTransient"); + widget->Show(); + } + + // Overridden from views::View: + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { + canvas->FillRect(color_, GetLocalBounds()); + } + virtual gfx::Size GetPreferredSize() OVERRIDE { + return gfx::Size(250, 250); + } + + // Overridden from views::WidgetDelegate: + virtual views::View* GetContentsView() OVERRIDE { + return this; + } + virtual bool CanResize() const OVERRIDE { + return true; + } + virtual string16 GetWindowTitle() const OVERRIDE { + return ASCIIToUTF16("Non-Modal Transient"); + } + + private: + SkColor color_; + + DISALLOW_COPY_AND_ASSIGN(NonModalTransient); +}; + +} // namespace + void InitWindowTypeLauncher() { views::Widget* widget = views::Widget::CreateWindowWithBounds(new WindowTypeLauncher, @@ -45,12 +159,20 @@ WindowTypeLauncher::WindowTypeLauncher() new views::NativeTextButton(this, ASCIIToUTF16("Lock Screen")))), ALLOW_THIS_IN_INITIALIZER_LIST(widgets_button_( new views::NativeTextButton( - this, ASCIIToUTF16("Show Example Widgets")))) { + this, ASCIIToUTF16("Show Example Widgets")))), + ALLOW_THIS_IN_INITIALIZER_LIST(modal_button_( + new views::NativeTextButton( + this, ASCIIToUTF16("Open Modal Window")))), + ALLOW_THIS_IN_INITIALIZER_LIST(transient_button_( + new views::NativeTextButton( + this, ASCIIToUTF16("Open Non-Modal Transient Window")))) { AddChildView(create_button_); AddChildView(create_nonresizable_button_); AddChildView(bubble_button_); AddChildView(lock_button_); AddChildView(widgets_button_); + AddChildView(modal_button_); + AddChildView(transient_button_); set_context_menu_controller(this); } @@ -88,6 +210,16 @@ void WindowTypeLauncher::Layout() { widgets_button_->SetBounds( 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 transient_ps = transient_button_->GetPreferredSize(); + transient_button_->SetBounds( + 5, modal_button_->y() - transient_ps.height() - 5, + transient_ps.width(), transient_ps.height()); } gfx::Size WindowTypeLauncher::GetPreferredSize() { @@ -129,6 +261,10 @@ void WindowTypeLauncher::ButtonPressed(views::Button* sender, CreateLock(); } else if (sender == widgets_button_) { CreateWidgetsWindow(); + } else if (sender == modal_button_) { + ModalWindow::OpenModalWindow(GetWidget()->GetNativeView()); + } else if (sender == transient_button_) { + NonModalTransient::OpenNonModalTransient(GetWidget()->GetNativeView()); } } diff --git a/ui/aura_shell/examples/window_type_launcher.h b/ui/aura_shell/examples/window_type_launcher.h index c4210a2..0644513 100644 --- a/ui/aura_shell/examples/window_type_launcher.h +++ b/ui/aura_shell/examples/window_type_launcher.h @@ -66,6 +66,8 @@ class WindowTypeLauncher : public views::WidgetDelegateView, views::NativeTextButton* bubble_button_; views::NativeTextButton* lock_button_; views::NativeTextButton* widgets_button_; + views::NativeTextButton* modal_button_; + views::NativeTextButton* transient_button_; scoped_ptr<views::MenuRunner> menu_runner_; DISALLOW_COPY_AND_ASSIGN(WindowTypeLauncher); diff --git a/ui/aura_shell/modal_container_layout_manager.cc b/ui/aura_shell/modal_container_layout_manager.cc new file mode 100644 index 0000000..4a42db2 --- /dev/null +++ b/ui/aura_shell/modal_container_layout_manager.cc @@ -0,0 +1,192 @@ +// Copyright (c) 2011 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 "ui/aura_shell/modal_container_layout_manager.h" + +#include "base/bind.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/desktop.h" +#include "ui/aura/event.h" +#include "ui/aura/window.h" +#include "ui/aura_shell/modality_event_filter.h" +#include "ui/aura_shell/shell.h" +#include "ui/aura_shell/stacking_controller.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/compositor/layer.h" +#include "ui/gfx/compositor/layer_animator.h" +#include "views/view.h" +#include "views/widget/widget.h" + +namespace aura_shell { +namespace internal { + +namespace { + +class ScreenView : public views::View { + public: + ScreenView() {} + virtual ~ScreenView() {} + + // Overridden from views::View: + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { + canvas->FillRect(SK_ColorBLACK, GetLocalBounds()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScreenView); +}; + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// ModalContainerLayoutManager, public: + +ModalContainerLayoutManager::ModalContainerLayoutManager( + aura::Window* container) + : container_(container), + modal_screen_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST( + modality_filter_(new ModalityEventFilter(container, this))) { +} + +ModalContainerLayoutManager::~ModalContainerLayoutManager() { +} + +//////////////////////////////////////////////////////////////////////////////// +// ModalContainerLayoutManager, aura::LayoutManager implementation: + +void ModalContainerLayoutManager::OnWindowResized() { + if (modal_screen_) { + modal_screen_->SetBounds(gfx::Rect(0, 0, container_->bounds().width(), + container_->bounds().height())); + } +} + +void ModalContainerLayoutManager::OnWindowAddedToLayout( + aura::Window* child) { + child->AddObserver(this); + if (child->GetIntProperty(aura::kModalKey)) + AddModalWindow(child); +} + +void ModalContainerLayoutManager::OnWillRemoveWindowFromLayout( + aura::Window* child) { + child->RemoveObserver(this); + if (child->GetIntProperty(aura::kModalKey)) + RemoveModalWindow(child); +} + +void ModalContainerLayoutManager::OnChildWindowVisibilityChanged( + aura::Window* child, + bool visible) { +} + +void ModalContainerLayoutManager::SetChildBounds( + aura::Window* child, + const gfx::Rect& requested_bounds) { + SetChildBoundsDirect(child, requested_bounds); +} + +//////////////////////////////////////////////////////////////////////////////// +// ModalContainerLayoutManager, aura::WindowObserver implementation: + +void ModalContainerLayoutManager::OnPropertyChanged(aura::Window* window, + const char* key, + void* old) { + if (key != aura::kModalKey) + return; + + if (window->GetIntProperty(aura::kModalKey)) { + AddModalWindow(window); + } else if (static_cast<int>(reinterpret_cast<intptr_t>(old))) { + RemoveModalWindow(window); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// ModalContainerLayoutManager, ui::LayerAnimationObserver implementation: + +void ModalContainerLayoutManager::OnLayerAnimationEnded( + const ui::LayerAnimationSequence* sequence) { + if (modal_screen_ && !modal_screen_->GetNativeView()->layer()->ShouldDraw()) + DestroyModalScreen(); +} + +void ModalContainerLayoutManager::OnLayerAnimationAborted( + const ui::LayerAnimationSequence* sequence) { +} + +void ModalContainerLayoutManager::OnLayerAnimationScheduled( + const ui::LayerAnimationSequence* sequence) { +} + +//////////////////////////////////////////////////////////////////////////////// +// ModalContainerLayoutManager, ModalityEventFilter::Delegate implementation: + +bool ModalContainerLayoutManager::CanWindowReceiveEvents( + aura::Window* window) { + return StackingController::GetActivatableWindow(window) == modal_window(); +} + +//////////////////////////////////////////////////////////////////////////////// +// ModalContainerLayoutManager, private: + +void ModalContainerLayoutManager::AddModalWindow(aura::Window* window) { + modal_windows_.push_back(window); + CreateModalScreen(); + container_->MoveChildToFront(window); + window->Activate(); +} + +void ModalContainerLayoutManager::RemoveModalWindow(aura::Window* window) { + aura::Window::Windows::iterator it = + std::find(modal_windows_.begin(), modal_windows_.end(), window); + if (it != modal_windows_.end()) + modal_windows_.erase(it); + + if (modal_windows_.empty()) + HideModalScreen(); + else + modal_window()->Activate(); +} + +void ModalContainerLayoutManager::CreateModalScreen() { + if (modal_screen_) + return; + modal_screen_ = new views::Widget; + views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL); + params.parent = container_; + params.bounds = gfx::Rect(0, 0, container_->bounds().width(), + container_->bounds().height()); + modal_screen_->Init(params); + modal_screen_->GetNativeView()->set_name( + "ModalContainerLayoutManager.ModalScreen"); + modal_screen_->SetContentsView(new ScreenView); + modal_screen_->GetNativeView()->layer()->SetOpacity(0.0f); + modal_screen_->GetNativeView()->layer()->GetAnimator()->AddObserver(this); + + Shell::GetInstance()->AddDesktopEventFilter(modality_filter_.get()); + + ui::LayerAnimator::ScopedSettings settings( + modal_screen_->GetNativeView()->layer()->GetAnimator()); + modal_screen_->Show(); + modal_screen_->GetNativeView()->layer()->SetOpacity(0.5f); + container_->MoveChildToFront(modal_screen_->GetNativeView()); +} + +void ModalContainerLayoutManager::DestroyModalScreen() { + modal_screen_->GetNativeView()->layer()->GetAnimator()->RemoveObserver(this); + modal_screen_->Close(); + modal_screen_ = NULL; +} + +void ModalContainerLayoutManager::HideModalScreen() { + Shell::GetInstance()->RemoveDesktopEventFilter(modality_filter_.get()); + ui::LayerAnimator::ScopedSettings settings( + modal_screen_->GetNativeView()->layer()->GetAnimator()); + modal_screen_->GetNativeView()->layer()->SetOpacity(0.0f); +} + +} // namespace internal +} // namespace aura_shell diff --git a/ui/aura_shell/modal_container_layout_manager.h b/ui/aura_shell/modal_container_layout_manager.h new file mode 100644 index 0000000..b359432 --- /dev/null +++ b/ui/aura_shell/modal_container_layout_manager.h @@ -0,0 +1,102 @@ +// Copyright (c) 2011 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 UI_AURA_SHELL_MODAL_CONTAINER_LAYOUT_MANAGER_H_ +#define UI_AURA_SHELL_MODAL_CONTAINER_LAYOUT_MANAGER_H_ +#pragma once + +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "ui/aura/layout_manager.h" +#include "ui/aura/window_observer.h" +#include "ui/aura_shell/aura_shell_export.h" +#include "ui/aura_shell/modality_event_filter_delegate.h" +#include "ui/gfx/compositor/layer_animation_observer.h" + +namespace aura { +class Window; +class EventFilter; +} +namespace gfx { +class Rect; +} +namespace views { +class Widget; +} + +namespace aura_shell { +namespace internal { + +// LayoutManager for the modal window container. +class AURA_SHELL_EXPORT ModalContainerLayoutManager + : public aura::LayoutManager, + public aura::WindowObserver, + public ui::LayerAnimationObserver, + public ModalityEventFilterDelegate { + public: + explicit ModalContainerLayoutManager(aura::Window* container); + virtual ~ModalContainerLayoutManager(); + + // Overridden from aura::LayoutManager: + virtual void OnWindowResized() OVERRIDE; + virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE; + virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE; + virtual void OnChildWindowVisibilityChanged(aura::Window* child, + bool visibile) OVERRIDE; + virtual void SetChildBounds(aura::Window* child, + const gfx::Rect& requested_bounds) OVERRIDE; + + // Overridden from aura::WindowObserver: + virtual void OnPropertyChanged(aura::Window* window, + const char* key, + void* old) OVERRIDE; + + // Overridden from ui::LayerAnimationObserver: + virtual void OnLayerAnimationEnded( + const ui::LayerAnimationSequence* sequence) OVERRIDE; + virtual void OnLayerAnimationAborted( + const ui::LayerAnimationSequence* sequence) OVERRIDE; + virtual void OnLayerAnimationScheduled( + const ui::LayerAnimationSequence* sequence) OVERRIDE; + + // Overridden from ModalityEventFilterDelegate: + virtual bool CanWindowReceiveEvents(aura::Window* window) OVERRIDE; + + private: + void AddModalWindow(aura::Window* window); + void RemoveModalWindow(aura::Window* window); + + void CreateModalScreen(); + void DestroyModalScreen(); + void HideModalScreen(); + + aura::Window* modal_window() { + return !modal_windows_.empty() ? modal_windows_.back() : NULL; + } + + // The container that owns the layout manager. + aura::Window* container_; + + // A "screen" widget that dims the windows behind the modal window(s) being + // shown in |container_|. + views::Widget* modal_screen_; + + // A stack of modal windows. Only the topmost can receive events. + std::vector<aura::Window*> modal_windows_; + + // An event filter that enforces the modality of the topmost window in + // |modal_windows_|. The event filter is attached when a modal window is + // added, and removed when the last is closed. + scoped_ptr<aura::EventFilter> modality_filter_; + + DISALLOW_COPY_AND_ASSIGN(ModalContainerLayoutManager); +}; + +} // namespace internal +} // namespace aura_shell + +#endif // UI_AURA_SHELL_MODAL_CONTAINER_LAYOUT_MANAGER_H_ diff --git a/ui/aura_shell/modal_container_layout_manager_unittest.cc b/ui/aura_shell/modal_container_layout_manager_unittest.cc new file mode 100644 index 0000000..0baf9d3 --- /dev/null +++ b/ui/aura_shell/modal_container_layout_manager_unittest.cc @@ -0,0 +1,173 @@ +// Copyright (c) 2011 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 "ui/aura_shell/modal_container_layout_manager.h" + +#include "base/compiler_specific.h" +#include "ui/aura/desktop.h" +#include "ui/aura/test/event_generator.h" +#include "ui/aura/window.h" +#include "ui/aura_shell/shell.h" +#include "ui/aura_shell/shell_window_ids.h" +#include "ui/aura_shell/test/aura_shell_test_base.h" +#include "views/widget/widget.h" +#include "views/widget/widget_delegate.h" + +namespace aura_shell { +namespace test { + +namespace { + +aura::Window* GetModalContainer() { + return Shell::GetInstance()->GetContainer( + aura_shell::internal::kShellWindowId_ModalContainer); +} + +aura::Window* GetDefaultContainer() { + return Shell::GetInstance()->GetContainer( + aura_shell::internal::kShellWindowId_DefaultContainer); +} + +class TestWindow : public views::WidgetDelegateView { + public: + explicit TestWindow(bool modal) : modal_(modal) {} + virtual ~TestWindow() {} + + static aura::Window* OpenTestWindow(aura::Window* parent, bool modal) { + DCHECK(!modal || (modal && parent)); + views::Widget* widget = + views::Widget::CreateWindowWithParent(new TestWindow(modal), parent); + widget->Show(); + return widget->GetNativeView(); + } + + // Overridden from views::View: + virtual gfx::Size GetPreferredSize() OVERRIDE { + return gfx::Size(50, 50); + } + + // Overridden from views::WidgetDelegate: + virtual views::View* GetContentsView() OVERRIDE { + return this; + } + virtual bool IsModal() const OVERRIDE { + return modal_; + } + + private: + bool modal_; + + DISALLOW_COPY_AND_ASSIGN(TestWindow); +}; + +class TransientWindowObserver : public aura::WindowObserver { + public: + TransientWindowObserver() : destroyed_(false) {} + virtual ~TransientWindowObserver() {} + + bool destroyed() const { return destroyed_; } + + // Overridden from aura::WindowObserver: + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE { + destroyed_ = true; + } + + private: + bool destroyed_; + + DISALLOW_COPY_AND_ASSIGN(TransientWindowObserver); +}; + +} // namespace + +typedef aura_shell::test::AuraShellTestBase ModalContainerLayoutManagerTest; + +TEST_F(ModalContainerLayoutManagerTest, NonModalTransient) { + scoped_ptr<aura::Window> parent(TestWindow::OpenTestWindow(NULL, false)); + aura::Window* transient = TestWindow::OpenTestWindow(parent.get(), false); + TransientWindowObserver destruction_observer; + transient->AddObserver(&destruction_observer); + + EXPECT_EQ(parent.get(), transient->transient_parent()); + EXPECT_EQ(GetDefaultContainer(), transient->parent()); + + // The transient should be destroyed with its parent. + parent.reset(); + EXPECT_TRUE(destruction_observer.destroyed()); +} + +TEST_F(ModalContainerLayoutManagerTest, ModalTransient) { + scoped_ptr<aura::Window> parent(TestWindow::OpenTestWindow(NULL, false)); + // parent should be active. + EXPECT_EQ(parent.get(), aura::Desktop::GetInstance()->active_window()); + + aura::Window* t1 = TestWindow::OpenTestWindow(parent.get(), true); + TransientWindowObserver do1; + t1->AddObserver(&do1); + + EXPECT_EQ(parent.get(), t1->transient_parent()); + EXPECT_EQ(GetModalContainer(), t1->parent()); + + // t1 should now be active. + EXPECT_EQ(t1, aura::Desktop::GetInstance()->active_window()); + + // Attempting to click the parent should result in no activation change. + aura::test::EventGenerator e1(parent.get()); + e1.ClickLeftButton(); + EXPECT_EQ(t1, aura::Desktop::GetInstance()->active_window()); + + // Now open another modal transient parented to the original modal transient. + aura::Window* t2 = TestWindow::OpenTestWindow(t1, true); + TransientWindowObserver do2; + t2->AddObserver(&do2); + + EXPECT_EQ(t2, aura::Desktop::GetInstance()->active_window()); + + EXPECT_EQ(t1, t2->transient_parent()); + EXPECT_EQ(GetModalContainer(), t2->parent()); + + // t2 should still be active, even after clicking on t1. + aura::test::EventGenerator e2(t1); + e2.ClickLeftButton(); + EXPECT_EQ(t2, aura::Desktop::GetInstance()->active_window()); + + // Both transients should be destroyed with parent. + parent.reset(); + EXPECT_TRUE(do1.destroyed()); + EXPECT_TRUE(do2.destroyed()); +} + +// Tests that we can activate an unrelated window after a modal window is closed +// for a window. +TEST_F(ModalContainerLayoutManagerTest, CanActivateAfterEndModalSession) { + scoped_ptr<aura::Window> unrelated(TestWindow::OpenTestWindow(NULL, false)); + unrelated->SetBounds(gfx::Rect(100, 100, 50, 50)); + scoped_ptr<aura::Window> parent(TestWindow::OpenTestWindow(NULL, false)); + // parent should be active. + EXPECT_EQ(parent.get(), aura::Desktop::GetInstance()->active_window()); + + scoped_ptr<aura::Window> transient( + TestWindow::OpenTestWindow(parent.get(), true)); + // t1 should now be active. + EXPECT_EQ(transient.get(), aura::Desktop::GetInstance()->active_window()); + + // Attempting to click the parent should result in no activation change. + aura::test::EventGenerator e1(parent.get()); + e1.ClickLeftButton(); + EXPECT_EQ(transient.get(), aura::Desktop::GetInstance()->active_window()); + + // Now close the transient. + transient.reset(); + + // parent should now be active again. + EXPECT_EQ(parent.get(), aura::Desktop::GetInstance()->active_window()); + + // Attempting to click unrelated should activate it. + aura::test::EventGenerator e2(unrelated.get()); + e2.ClickLeftButton(); + EXPECT_EQ(unrelated.get(), aura::Desktop::GetInstance()->active_window()); +} + +} // namespace test +} // namespace aura_shell diff --git a/ui/aura_shell/modality_event_filter.cc b/ui/aura_shell/modality_event_filter.cc new file mode 100644 index 0000000..0df6a53 --- /dev/null +++ b/ui/aura_shell/modality_event_filter.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2011 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 "ui/aura_shell/modality_event_filter.h" + +#include "ui/aura/event.h" +#include "ui/aura_shell/modality_event_filter_delegate.h" + +namespace aura_shell { +namespace internal { + +ModalityEventFilter::ModalityEventFilter(aura::Window* container, + ModalityEventFilterDelegate* delegate) + : EventFilter(container), + delegate_(delegate) { +} + +ModalityEventFilter::~ModalityEventFilter() { +} + +bool ModalityEventFilter::PreHandleKeyEvent(aura::Window* target, + aura::KeyEvent* event) { + return !delegate_->CanWindowReceiveEvents(target); +} + +bool ModalityEventFilter::PreHandleMouseEvent(aura::Window* target, + aura::MouseEvent* event) { + return !delegate_->CanWindowReceiveEvents(target); +} + +ui::TouchStatus ModalityEventFilter::PreHandleTouchEvent( + aura::Window* target, + aura::TouchEvent* event) { + // TODO(sadrul): ! + return ui::TOUCH_STATUS_UNKNOWN; +} + +} // namespace internal +} // namespace aura_shell diff --git a/ui/aura_shell/modality_event_filter.h b/ui/aura_shell/modality_event_filter.h new file mode 100644 index 0000000..91f2fc2 --- /dev/null +++ b/ui/aura_shell/modality_event_filter.h @@ -0,0 +1,42 @@ +// Copyright (c) 2011 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 UI_AURA_SHELL_MODALITY_EVENT_FILTER_H_ +#define UI_AURA_SHELL_MODALITY_EVENT_FILTER_H_ +#pragma once + +#include "base/compiler_specific.h" +#include "ui/aura/event_filter.h" +#include "ui/aura_shell/aura_shell_export.h" + +namespace aura_shell { +namespace internal { + +class ModalityEventFilterDelegate; + +class AURA_SHELL_EXPORT ModalityEventFilter : public aura::EventFilter { + public: + ModalityEventFilter(aura::Window* container, + ModalityEventFilterDelegate* delegate); + virtual ~ModalityEventFilter(); + + // 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: + ModalityEventFilterDelegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(ModalityEventFilter); +}; + +} // namespace internal +} // namespace aura_shell + +#endif // UI_AURA_SHELL_MODALITY_EVENT_FILTER_H_ diff --git a/ui/aura_shell/modality_event_filter_delegate.h b/ui/aura_shell/modality_event_filter_delegate.h new file mode 100644 index 0000000..7afed81 --- /dev/null +++ b/ui/aura_shell/modality_event_filter_delegate.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011 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 UI_AURA_SHELL_MODALITY_EVENT_FILTER_DELEGATE_H_ +#define UI_AURA_SHELL_MODALITY_EVENT_FILTER_DELEGATE_H_ +#pragma once + +#include "ui/aura_shell/aura_shell_export.h" + +namespace aura { +class Window; +} + +namespace aura_shell { +namespace internal { + +class AURA_SHELL_EXPORT ModalityEventFilterDelegate { + public: + // Returns true if |window| can receive the specified event. + virtual bool CanWindowReceiveEvents(aura::Window* window) = 0; +}; + +} // namespace internal +} // namespace aura_shell + +#endif // UI_AURA_SHELL_MODALITY_EVENT_FILTER_DELEGATE_H_ diff --git a/ui/aura_shell/shell.cc b/ui/aura_shell/shell.cc index dc40423..5f16a5b 100644 --- a/ui/aura_shell/shell.cc +++ b/ui/aura_shell/shell.cc @@ -15,6 +15,7 @@ #include "ui/aura_shell/desktop_event_filter.h" #include "ui/aura_shell/desktop_layout_manager.h" #include "ui/aura_shell/launcher/launcher.h" +#include "ui/aura_shell/modal_container_layout_manager.h" #include "ui/aura_shell/shelf_layout_controller.h" #include "ui/aura_shell/shell_delegate.h" #include "ui/aura_shell/shell_factory.h" @@ -60,6 +61,16 @@ void CreateSpecialContainers(aura::Window::Windows* containers) { launcher_container->set_id(internal::kShellWindowId_LauncherContainer); containers->push_back(launcher_container); + aura::Window* modal_container = new aura::Window(NULL); + modal_container->SetEventFilter( + new ToplevelWindowEventFilter(modal_container)); + modal_container->SetLayoutManager( + new internal::ModalContainerLayoutManager(modal_container)); + modal_container->set_id(internal::kShellWindowId_ModalContainer); + containers->push_back(modal_container); + + // TODO(beng): Figure out if we can make this use ModalityEventFilter instead + // of stops_event_propagation. aura::Window* lock_container = new aura::Window(NULL); lock_container->set_stops_event_propagation(true); lock_container->set_id(internal::kShellWindowId_LockScreenContainer); @@ -180,6 +191,16 @@ const aura::Window* Shell::GetContainer(int container_id) const { return aura::Desktop::GetInstance()->GetChildById(container_id); } +void Shell::AddDesktopEventFilter(aura::EventFilter* filter) { + static_cast<internal::DesktopEventFilter*>( + aura::Desktop::GetInstance()->event_filter())->AddFilter(filter); +} + +void Shell::RemoveDesktopEventFilter(aura::EventFilter* filter) { + static_cast<internal::DesktopEventFilter*>( + aura::Desktop::GetInstance()->event_filter())->RemoveFilter(filter); +} + void Shell::ToggleOverview() { if (workspace_controller_.get()) workspace_controller_->ToggleOverview(); diff --git a/ui/aura_shell/shell.h b/ui/aura_shell/shell.h index 1b4c719..75643c5 100644 --- a/ui/aura_shell/shell.h +++ b/ui/aura_shell/shell.h @@ -17,6 +17,7 @@ #include "ui/aura_shell/aura_shell_export.h" namespace aura { +class EventFilter; class Window; } namespace gfx { @@ -52,6 +53,10 @@ class AURA_SHELL_EXPORT Shell { aura::Window* GetContainer(int container_id); const aura::Window* GetContainer(int container_id) const; + // Adds or removes |filter| from the DesktopEventFilter. + void AddDesktopEventFilter(aura::EventFilter* filter); + void RemoveDesktopEventFilter(aura::EventFilter* filter); + // Toggles between overview mode and normal mode. void ToggleOverview(); diff --git a/ui/aura_shell/shell_factory.h b/ui/aura_shell/shell_factory.h index a29cc0a..58da655 100644 --- a/ui/aura_shell/shell_factory.h +++ b/ui/aura_shell/shell_factory.h @@ -6,6 +6,8 @@ #define UI_AURA_SHELL_SHELL_FACTORY_H_ #pragma once +#include "ui/aura_shell/aura_shell_export.h" + namespace views { class Widget; } @@ -14,13 +16,9 @@ class Widget; namespace aura_shell { -namespace examples { -void InitWindowTypeLauncher(); -} // namespace examples - namespace internal { views::Widget* CreateDesktopBackground(); -views::Widget* CreateStatusArea(); +AURA_SHELL_EXPORT views::Widget* CreateStatusArea(); } // namespace internal } // namespace aura_shell diff --git a/ui/aura_shell/shell_window_ids.h b/ui/aura_shell/shell_window_ids.h index f62b82b..2e0151a 100644 --- a/ui/aura_shell/shell_window_ids.h +++ b/ui/aura_shell/shell_window_ids.h @@ -24,14 +24,17 @@ const int kShellWindowId_AlwaysOnTopContainer = 2; // The container for the launcher. const int kShellWindowId_LauncherContainer = 3; +// The container for user-specific modal windows. +const int kShellWindowId_ModalContainer = 4; + // The container for the lock screen. -const int kShellWindowId_LockScreenContainer = 4; +const int kShellWindowId_LockScreenContainer = 5; // The container for the status area. -const int kShellWindowId_StatusContainer = 5; +const int kShellWindowId_StatusContainer = 6; // The container for menus and tooltips. -const int kShellWindowId_MenusAndTooltipsContainer = 6; +const int kShellWindowId_MenusAndTooltipsContainer = 7; } // namespace internal diff --git a/ui/aura_shell/stacking_controller.cc b/ui/aura_shell/stacking_controller.cc index 3a913c3..ffe5cb4 100644 --- a/ui/aura_shell/stacking_controller.cc +++ b/ui/aura_shell/stacking_controller.cc @@ -4,6 +4,7 @@ #include "ui/aura_shell/stacking_controller.h" +#include "ui/aura/client/aura_constants.h" #include "ui/aura/desktop.h" #include "ui/aura/window.h" #include "ui/aura_shell/always_on_top_controller.h" @@ -21,7 +22,12 @@ aura::Window* GetContainer(int id) { // Returns true if children of |window| can be activated. bool SupportsChildActivation(aura::Window* window) { return window->id() == kShellWindowId_DefaultContainer || - window->id() == kShellWindowId_AlwaysOnTopContainer; + window->id() == kShellWindowId_AlwaysOnTopContainer || + window->id() == kShellWindowId_ModalContainer; +} + +bool IsWindowModal(aura::Window* window) { + return window->transient_parent() && window->GetIntProperty(aura::kModalKey); } } // namespace @@ -68,6 +74,10 @@ void StackingController::AddChildToDefaultParent(aura::Window* window) { switch (window->type()) { case aura::WINDOW_TYPE_NORMAL: case aura::WINDOW_TYPE_POPUP: + if (IsWindowModal(window)) { + parent = GetContainer(internal::kShellWindowId_ModalContainer); + break; + } parent = always_on_top_controller_->GetContainer(window); break; case aura::WINDOW_TYPE_MENU: diff --git a/ui/aura_shell/status_area_view.cc b/ui/aura_shell/status_area_view.cc index 88b897d..1746ba7 100644 --- a/ui/aura_shell/status_area_view.cc +++ b/ui/aura_shell/status_area_view.cc @@ -32,7 +32,7 @@ void StatusAreaView::OnPaint(gfx::Canvas* canvas) { canvas->DrawBitmapInt(status_mock_, 0, 0); } -views::Widget* CreateStatusArea() { +AURA_SHELL_EXPORT views::Widget* CreateStatusArea() { StatusAreaView* status_area_view = new StatusAreaView; views::Widget* widget = new views::Widget; views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL); diff --git a/ui/aura_shell/toplevel_layout_manager_unittest.cc b/ui/aura_shell/toplevel_layout_manager_unittest.cc index 79d9b88..c02069f 100644 --- a/ui/aura_shell/toplevel_layout_manager_unittest.cc +++ b/ui/aura_shell/toplevel_layout_manager_unittest.cc @@ -49,7 +49,6 @@ class ToplevelLayoutManagerTest : public aura::test::AuraTestBase { scoped_ptr<aura::Window> container_; - private: DISALLOW_COPY_AND_ASSIGN(ToplevelLayoutManagerTest); }; diff --git a/ui/aura_shell/workspace/workspace.cc b/ui/aura_shell/workspace/workspace.cc index ebe2bd1..a810bd3 100644 --- a/ui/aura_shell/workspace/workspace.cc +++ b/ui/aura_shell/workspace/workspace.cc @@ -239,7 +239,7 @@ int Workspace::GetIndexOf(aura::Window* window) const { bool Workspace::CanAdd(aura::Window* window) const { // TODO(oshima): This should be based on available space and the // size of the |window|. - NOTIMPLEMENTED(); + //NOTIMPLEMENTED(); return windows_.size() < g_max_windows_per_workspace; } diff --git a/ui/aura_shell/workspace_controller.cc b/ui/aura_shell/workspace_controller.cc index d602be1..9131bee 100644 --- a/ui/aura_shell/workspace_controller.cc +++ b/ui/aura_shell/workspace_controller.cc @@ -79,7 +79,7 @@ void WorkspaceController::ActiveWorkspaceChanged(WorkspaceManager* manager, Workspace* old) { // TODO(oshima): Update Launcher and Status area state when the active // workspace's fullscreen state changes. - NOTIMPLEMENTED(); + //NOTIMPLEMENTED(); } //////////////////////////////////////////////////////////////////////////////// |