summaryrefslogtreecommitdiffstats
path: root/ui/aura_shell
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-16 18:25:51 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-16 18:25:51 +0000
commite29014c26af112b0affdb84edfb59d249369d90b (patch)
tree2d6577d4fcbd7de015dba73c346296071cd17745 /ui/aura_shell
parent1ab4211da8b4f9cfee01dfd1c537e6b42a1426ea (diff)
downloadchromium_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')
-rw-r--r--ui/aura_shell/aura_shell.gyp6
-rw-r--r--ui/aura_shell/desktop_event_filter.cc22
-rw-r--r--ui/aura_shell/desktop_event_filter.h2
-rw-r--r--ui/aura_shell/examples/aura_shell_main.cc10
-rw-r--r--ui/aura_shell/examples/window_type_launcher.cc138
-rw-r--r--ui/aura_shell/examples/window_type_launcher.h2
-rw-r--r--ui/aura_shell/modal_container_layout_manager.cc192
-rw-r--r--ui/aura_shell/modal_container_layout_manager.h102
-rw-r--r--ui/aura_shell/modal_container_layout_manager_unittest.cc173
-rw-r--r--ui/aura_shell/modality_event_filter.cc40
-rw-r--r--ui/aura_shell/modality_event_filter.h42
-rw-r--r--ui/aura_shell/modality_event_filter_delegate.h27
-rw-r--r--ui/aura_shell/shell.cc21
-rw-r--r--ui/aura_shell/shell.h5
-rw-r--r--ui/aura_shell/shell_factory.h8
-rw-r--r--ui/aura_shell/shell_window_ids.h9
-rw-r--r--ui/aura_shell/stacking_controller.cc12
-rw-r--r--ui/aura_shell/status_area_view.cc2
-rw-r--r--ui/aura_shell/toplevel_layout_manager_unittest.cc1
-rw-r--r--ui/aura_shell/workspace/workspace.cc2
-rw-r--r--ui/aura_shell/workspace_controller.cc2
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();
}
////////////////////////////////////////////////////////////////////////////////