diff options
author | oshima <oshima@chromium.org> | 2014-10-20 10:19:18 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-20 17:20:09 +0000 |
commit | 44bf2fd5fb11d3777e522fae68de024c8f6ce46f (patch) | |
tree | ee959cfe4065367147ab432ddd4b22a51f504004 /athena/screen | |
parent | 3f5d1eae967412133ff221d2aea6dd325b45928c (diff) | |
download | chromium_src-44bf2fd5fb11d3777e522fae68de024c8f6ce46f.zip chromium_src-44bf2fd5fb11d3777e522fae68de024c8f6ce46f.tar.gz chromium_src-44bf2fd5fb11d3777e522fae68de024c8f6ce46f.tar.bz2 |
Support modal windows
* New ContainerParams
- default_parent to specify the default parent when no
transient parent is specified.
- modal_container_priority now controls the modal container
used for a modal window created for the given container.
- if not specified, it will fallback downwards to find one.
- if window is specified as always_on_top, it will use top
most modal container.
* Changed network selector/shutdown dialog to use the new API.
* Other change:
Separated test windows to athena/test/base/test_windows.h
BUG=410499
TBR=sky@chromium.org,reed@chromium.org
TEST=coverd by unit tests
Committed: https://crrev.com/4903fd36b2b36f00efeb1b7bba81b7de6e9457a3
Cr-Commit-Position: refs/heads/master@{#300192}
Review URL: https://codereview.chromium.org/662763002
Cr-Commit-Position: refs/heads/master@{#300288}
Diffstat (limited to 'athena/screen')
-rw-r--r-- | athena/screen/DEPS | 3 | ||||
-rw-r--r-- | athena/screen/modal_window_controller.cc | 146 | ||||
-rw-r--r-- | athena/screen/modal_window_controller.h | 59 | ||||
-rw-r--r-- | athena/screen/modal_window_controller_unittest.cc | 144 | ||||
-rw-r--r-- | athena/screen/public/screen_manager.h | 35 | ||||
-rw-r--r-- | athena/screen/screen_accelerator_handler.h | 4 | ||||
-rw-r--r-- | athena/screen/screen_manager_impl.cc | 312 | ||||
-rw-r--r-- | athena/screen/screen_manager_impl.h | 72 | ||||
-rw-r--r-- | athena/screen/screen_manager_unittest.cc | 174 |
9 files changed, 724 insertions, 225 deletions
diff --git a/athena/screen/DEPS b/athena/screen/DEPS index 1809145..423ee38 100644 --- a/athena/screen/DEPS +++ b/athena/screen/DEPS @@ -5,4 +5,7 @@ include_rules = [ "+ui/events", "+ui/gfx", "+ui/wm", + "+third_party/skia/include", + # for tests. + "+athena/athena_export.h", ] diff --git a/athena/screen/modal_window_controller.cc b/athena/screen/modal_window_controller.cc new file mode 100644 index 0000000..71a19ff --- /dev/null +++ b/athena/screen/modal_window_controller.cc @@ -0,0 +1,146 @@ +// Copyright 2014 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 "athena/screen/modal_window_controller.h" + +#include "athena/screen/public/screen_manager.h" +#include "base/message_loop/message_loop.h" +#include "ui/aura/window.h" +#include "ui/aura/window_property.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_animation_observer.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/wm/core/window_animations.h" + +namespace athena { +namespace { + +DEFINE_OWNED_WINDOW_PROPERTY_KEY(ModalWindowController, + kModalWindowControllerKey, + NULL); + +} // namespace + +// static +ModalWindowController* ModalWindowController::Get(aura::Window* container) { + ModalWindowController* controller = + container->GetProperty(kModalWindowControllerKey); + CHECK(controller); + return controller; +} + +ModalWindowController::ModalWindowController(int priority) + : modal_container_(NULL), + dimmer_window_(new aura::Window(NULL)), + dimmed_(false) { + ScreenManager::ContainerParams params("ModalContainer", priority); + params.can_activate_children = true; + params.block_events = true; + modal_container_ = ScreenManager::Get()->CreateContainer(params); + modal_container_->SetProperty(kModalWindowControllerKey, this); + + dimmer_window_->SetType(ui::wm::WINDOW_TYPE_NORMAL); + dimmer_window_->Init(aura::WINDOW_LAYER_SOLID_COLOR); + dimmer_window_->layer()->SetColor(SK_ColorBLACK); + dimmer_window_->layer()->SetOpacity(0.0f); + dimmer_window_->Show(); + + modal_container_->AddChild(dimmer_window_); + modal_container_->AddObserver(this); + + UpdateDimmerWindowBounds(); +} + +ModalWindowController::~ModalWindowController() { + if (modal_container_) + modal_container_->RemoveObserver(this); +} + +void ModalWindowController::OnWindowAdded(aura::Window* child) { + DCHECK_NE(child, dimmer_window_); + if (IsChildWindow(child)) { + child->AddObserver(this); + UpdateDimming(NULL); + } +} + +void ModalWindowController::OnWindowVisibilityChanged(aura::Window* window, + bool visible) { + if (IsChildWindow(window)) + UpdateDimming(NULL); +} + +void ModalWindowController::OnWindowBoundsChanged(aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + if (window == modal_container_) + UpdateDimmerWindowBounds(); +} + +void ModalWindowController::OnWindowDestroyed(aura::Window* window) { + UpdateDimming(window); +} + +bool ModalWindowController::IsChildWindow(aura::Window* child) const { + return child->parent() == modal_container_ && child != dimmer_window_; +} + +void ModalWindowController::UpdateDimmerWindowBounds() { + gfx::Rect bounds(modal_container_->bounds().size()); + dimmer_window_->SetBounds(bounds); +} + +void ModalWindowController::UpdateDimming(aura::Window* ignore) { + if (!modal_container_ || !dimmer_window_) + return; + bool should_delete = true; + for (aura::Window* window : modal_container_->children()) { + if (window == dimmer_window_ || window == ignore) + continue; + should_delete = false; + if (window->TargetVisibility()) { + SetDimmed(true); + return; + } + } + SetDimmed(false); + + if (should_delete) { + // Remove the container from root so that the container becomes + // invisible, but don't delete it until next event execution + // because the call stack may still have and use the pointer. + modal_container_->RemoveObserver(this); + modal_container_->parent()->RemoveChild(modal_container_); + base::MessageLoopForUI::current()->DeleteSoon(FROM_HERE, modal_container_); + modal_container_ = NULL; + dimmer_window_ = NULL; + } +} + +void ModalWindowController::SetDimmed(bool dimmed) { + const float kDimmedOpacity = 0.4f; + + if (!dimmer_window_ || dimmed_ == dimmed) + return; + dimmed_ = dimmed; + + const int kDimmAnimationDurationMs = 500; + if (dimmed) { + ui::ScopedLayerAnimationSettings settings( + dimmer_window_->layer()->GetAnimator()); + settings.SetTransitionDuration( + base::TimeDelta::FromMilliseconds(kDimmAnimationDurationMs)); + dimmer_window_->layer()->SetOpacity(kDimmedOpacity); + } else { + // ScopedHidingAnimationSettings will detach the animating and + // recreate layers for the container so that animation can continue + // even if the container is removed immediately. + wm::ScopedHidingAnimationSettings settings(modal_container_); + settings.layer_animation_settings()->SetTransitionDuration( + base::TimeDelta::FromMilliseconds(kDimmAnimationDurationMs)); + modal_container_->layer()->SetOpacity(0.0f); + } +} + +} // namespace athena diff --git a/athena/screen/modal_window_controller.h b/athena/screen/modal_window_controller.h new file mode 100644 index 0000000..b4c7ec5 --- /dev/null +++ b/athena/screen/modal_window_controller.h @@ -0,0 +1,59 @@ +// Copyright 2014 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 ATHENA_SCREEN_MODAL_WINDOW_CONTROLLER_H_ +#define ATHENA_SCREEN_MODAL_WINDOW_CONTROLLER_H_ + +#include "athena/athena_export.h" +#include "ui/aura/window_observer.h" + +namespace athena { + +// ModalWindow controller manages the modal window and +// its container. This gets created when a modal window is opened +// and deleted when all modal windows are deleted. +class ATHENA_EXPORT ModalWindowController : public aura::WindowObserver { + public: + // Returns the ModalWindowController associated with the container. + static ModalWindowController* Get(aura::Window* container); + + explicit ModalWindowController(int container_priority); + virtual ~ModalWindowController(); + + aura::Window* modal_container() { return modal_container_; } + + bool dimmed() const { return dimmed_; } + + private: + // aura::WindowObserver: + virtual void OnWindowAdded(aura::Window* child) override; + virtual void OnWindowVisibilityChanged(aura::Window* window, + bool visible) override; + virtual void OnWindowBoundsChanged(aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) override; + virtual void OnWindowDestroyed(aura::Window* window) override; + + // Tells if the child is not a dimmer window and a child of the modal + // container. + bool IsChildWindow(aura::Window* child) const; + + void UpdateDimmerWindowBounds(); + + // Change dimming state based on the visible window in the container. + void UpdateDimming(aura::Window* ignore); + + // Note: changing true -> false will delete the modal_container_. + void SetDimmed(bool dimmed); + + aura::Window* modal_container_; // not owned. + aura::Window* dimmer_window_; // not owned. + + bool dimmed_; + DISALLOW_COPY_AND_ASSIGN(ModalWindowController); +}; + +} // namespace athena + +#endif // ATHENA_SCREEN_MODAL_WINDOW_CONTROLLER_H_ diff --git a/athena/screen/modal_window_controller_unittest.cc b/athena/screen/modal_window_controller_unittest.cc new file mode 100644 index 0000000..b73399a --- /dev/null +++ b/athena/screen/modal_window_controller_unittest.cc @@ -0,0 +1,144 @@ +// Copyright 2014 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 "athena/screen/modal_window_controller.h" +#include "athena/screen/screen_manager_impl.h" +#include "athena/test/base/athena_test_base.h" +#include "athena/test/base/test_windows.h" +#include "athena/util/container_priorities.h" +#include "ui/aura/test/test_window_delegate.h" +#include "ui/aura/window.h" + +namespace athena { + +typedef test::AthenaTestBase ModalWindowControllerTest; + +aura::Window* FindContainerByPriority(int priority) { + ScreenManagerImpl* screen_manager = + static_cast<ScreenManagerImpl*>(ScreenManager::Get()); + return screen_manager->FindContainerByPriority(priority); +} + +TEST_F(ModalWindowControllerTest, ModalContainer) { + aura::test::EventCountDelegate delegate; + scoped_ptr<aura::Window> modal(test::CreateTransientWindow( + &delegate, NULL, ui::MODAL_TYPE_SYSTEM, false)); + + aura::Window* modal_container = FindContainerByPriority(CP_SYSTEM_MODAL); + EXPECT_TRUE(modal_container); + + ModalWindowController* modal_controller = + ModalWindowController::Get(modal_container); + ASSERT_TRUE(modal_controller); + EXPECT_EQ(modal_container, modal->parent()); + EXPECT_FALSE(modal_controller->dimmed()); + modal->Show(); + EXPECT_TRUE(modal_controller->dimmed()); + + modal->Hide(); + EXPECT_TRUE(FindContainerByPriority(CP_SYSTEM_MODAL)); + EXPECT_FALSE(modal_controller->dimmed()); + + modal->Show(); + EXPECT_TRUE(FindContainerByPriority(CP_SYSTEM_MODAL)); + EXPECT_TRUE(modal_controller->dimmed()); + + modal.reset(); + EXPECT_FALSE(FindContainerByPriority(CP_SYSTEM_MODAL)); + + // Create two. + modal = test::CreateTransientWindow( + &delegate, NULL, ui::MODAL_TYPE_SYSTEM, false).Pass(); + scoped_ptr<aura::Window> modal2(test::CreateTransientWindow( + &delegate, NULL, ui::MODAL_TYPE_SYSTEM, false)); + + modal_container = FindContainerByPriority(CP_SYSTEM_MODAL); + EXPECT_TRUE(modal_container); + modal_controller = ModalWindowController::Get(modal_container); + ASSERT_TRUE(modal_controller); + EXPECT_EQ(modal_container, modal->parent()); + EXPECT_EQ(modal_container, modal2->parent()); + + EXPECT_FALSE(modal_controller->dimmed()); + modal->Show(); + EXPECT_TRUE(modal_controller->dimmed()); + modal2->Show(); + EXPECT_TRUE(modal_controller->dimmed()); + + modal->Hide(); + EXPECT_TRUE(modal_controller->dimmed()); + modal2->Hide(); + EXPECT_FALSE(modal_controller->dimmed()); + EXPECT_TRUE(FindContainerByPriority(CP_SYSTEM_MODAL)); + + modal2.reset(); + EXPECT_TRUE(FindContainerByPriority(CP_SYSTEM_MODAL)); + EXPECT_FALSE(modal_controller->dimmed()); + + modal.reset(); + EXPECT_FALSE(FindContainerByPriority(CP_SYSTEM_MODAL)); +} + +TEST_F(ModalWindowControllerTest, NestedModalWindows) { + ScreenManager::ContainerParams params("top", CP_END); + params.can_activate_children = true; + params.modal_container_priority = CP_END + 1; + aura::Window* top_container = ScreenManager::Get()->CreateContainer(params); + + aura::test::TestWindowDelegate delegate; + scoped_ptr<aura::Window> top_w1( + test::CreateNormalWindow(&delegate, top_container)); + EXPECT_EQ(top_container, top_w1->parent()); + + aura::Window* default_container = FindContainerByPriority(CP_DEFAULT); + EXPECT_TRUE(default_container); + + scoped_ptr<aura::Window> normal_w1(test::CreateNormalWindow(&delegate, NULL)); + EXPECT_EQ(default_container, normal_w1->parent()); + + scoped_ptr<aura::Window> normal_m1(test::CreateTransientWindow( + &delegate, normal_w1.get(), ui::MODAL_TYPE_SYSTEM, false)); + aura::Window* default_modal_container = + FindContainerByPriority(CP_SYSTEM_MODAL); + EXPECT_EQ(default_modal_container, normal_m1->parent()); + + // A modal window with the transient parent which doesn't specify + // the modal container should fallback the default container's modal + // container. + aura::Window* home_container = FindContainerByPriority(CP_DEFAULT); + EXPECT_TRUE(home_container); + + scoped_ptr<aura::Window> normal_m2(test::CreateTransientWindow( + &delegate, home_container, ui::MODAL_TYPE_SYSTEM, false)); + EXPECT_EQ(default_modal_container, normal_m2->parent()); + + // No modal container for top container yet. + EXPECT_FALSE(FindContainerByPriority(CP_END + 1)); + + // Creating a modal with always on top creates the modal dialog on top + // most dialog container. + scoped_ptr<aura::Window> top_m0(test::CreateTransientWindow( + &delegate, NULL, ui::MODAL_TYPE_SYSTEM, true)); + + aura::Window* top_modal_container = FindContainerByPriority(CP_END + 1); + EXPECT_EQ(top_modal_container, top_m0->parent()); + + // Creating a modal dialog with transient parent on the top container + // creates the winodw on the top container's modal container. + scoped_ptr<aura::Window> top_m1(test::CreateTransientWindow( + &delegate, top_w1.get(), ui::MODAL_TYPE_SYSTEM, true)); + EXPECT_EQ(top_modal_container, top_m1->parent()); + + normal_m1.reset(); + EXPECT_TRUE(FindContainerByPriority(CP_SYSTEM_MODAL)); + normal_m2.reset(); + EXPECT_FALSE(FindContainerByPriority(CP_SYSTEM_MODAL)); + + top_m0.reset(); + EXPECT_TRUE(FindContainerByPriority(CP_END + 1)); + top_m1.reset(); + EXPECT_FALSE(FindContainerByPriority(CP_END + 1)); +} + +} // namespace athena diff --git a/athena/screen/public/screen_manager.h b/athena/screen/public/screen_manager.h index e263782..11dda1f 100644 --- a/athena/screen/public/screen_manager.h +++ b/athena/screen/public/screen_manager.h @@ -29,11 +29,37 @@ class ATHENA_EXPORT ScreenManager { // True if the container can activate its child window. bool can_activate_children; - // True if the container will grab all of input events. - bool grab_inputs; + // True if the container will block evnets from containers behind it. + bool block_events; // Defines the z_order priority of the container. int z_order_priority; + + // True if this container should be used as a default parent. + bool default_parent; + + // The container priority used to open modal dialog window + // created with this container as a transient parent (Note: A modal window + // should + // use a trnasient parent, not a direct parent, or no transient parent.) + // + // Default is -1, and it will fallback to the container behind this + // container, + // that has the modal container proiroty. + // + // The modal container for modal window is selected as follows. + // 1) a window must be created with |aura::client::kModalKey| property + // without explicit parent set. + // 2.a) If aura::client::kAlwaysOnTopKey is NOT set, it uses the stand flow + // described above. (fallback to containers behind this). + // 2.b) If aura::client::kAlwaysOnTopKey is set, it searches the top most + // container which has |modal_container_priority| != -1. + // 3) Look for the container with |modal_container_priority|, and create + // one if it doesn't exist. + // + // Created modal container will self destruct if last modal window + // is deleted. + int modal_container_priority; }; // Creates, returns and deletes the singleton object of the ScreenManager @@ -44,11 +70,6 @@ class ATHENA_EXPORT ScreenManager { virtual ~ScreenManager() {} - // Creates the container window that is used when creating a normal - // widget without specific parent. - virtual aura::Window* CreateDefaultContainer( - const ContainerParams& params) = 0; - // Creates the container window on the screen. virtual aura::Window* CreateContainer(const ContainerParams& params) = 0; diff --git a/athena/screen/screen_accelerator_handler.h b/athena/screen/screen_accelerator_handler.h index ab57879..fcde448 100644 --- a/athena/screen/screen_accelerator_handler.h +++ b/athena/screen/screen_accelerator_handler.h @@ -1,6 +1,8 @@ // Copyright 2014 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 ATHENA_SCREEN_SCREEN_ACCELERATOR_HANDLER_H_ +#define ATHENA_SCREEN_SCREEN_ACCELERATOR_HANDLER_H_ #include "athena/input/public/accelerator_manager.h" @@ -31,3 +33,5 @@ class ScreenAcceleratorHandler : public AcceleratorHandler { }; } // namespace athena + +#endif // ATHENA_SCREEN_SCREEN_ACCELERATOR_HANDLER_H_ diff --git a/athena/screen/screen_manager_impl.cc b/athena/screen/screen_manager_impl.cc index 3ece198..5182b7e 100644 --- a/athena/screen/screen_manager_impl.cc +++ b/athena/screen/screen_manager_impl.cc @@ -2,23 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "athena/screen/public/screen_manager.h" +#include "athena/screen/screen_manager_impl.h" #include "athena/input/public/accelerator_manager.h" +#include "athena/screen/modal_window_controller.h" #include "athena/screen/screen_accelerator_handler.h" #include "athena/util/container_priorities.h" #include "athena/util/fill_layout_manager.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "ui/aura/client/aura_constants.h" #include "ui/aura/client/screen_position_client.h" -#include "ui/aura/client/window_tree_client.h" -#include "ui/aura/layout_manager.h" #include "ui/aura/test/test_screen.h" #include "ui/aura/window.h" #include "ui/aura/window_property.h" #include "ui/aura/window_targeter.h" #include "ui/aura/window_tree_host.h" -#include "ui/compositor/layer.h" #include "ui/gfx/display.h" #include "ui/gfx/screen.h" #include "ui/wm/core/base_focus_rules.h" @@ -26,6 +25,9 @@ #include "ui/wm/core/focus_controller.h" #include "ui/wm/core/window_util.h" +// This is to avoid creating type definitoin for kAlwaysOnTopKey. +DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(ATHENA_EXPORT, bool); + namespace athena { namespace { @@ -33,12 +35,38 @@ DEFINE_OWNED_WINDOW_PROPERTY_KEY(ScreenManager::ContainerParams, kContainerParamsKey, NULL); -ScreenManager* instance = NULL; +ScreenManagerImpl* instance = NULL; + +// A functor to find a container that has the higher priority. +struct HigherPriorityFinder { + HigherPriorityFinder(int p) : priority(p) {} + bool operator()(aura::Window* window) { + return window->GetProperty(kContainerParamsKey)->z_order_priority > + priority; + } + int priority; +}; -bool GrabsInput(aura::Window* container) { +bool BlockEvents(aura::Window* container) { ScreenManager::ContainerParams* params = container->GetProperty(kContainerParamsKey); - return params && params->grab_inputs; + return params && params->block_events; +} + +bool DefaultContainer(aura::Window* container) { + ScreenManager::ContainerParams* params = + container->GetProperty(kContainerParamsKey); + return params && params->default_parent; +} + +bool HasModalContainerPriority(aura::Window* container) { + ScreenManager::ContainerParams* params = + container->GetProperty(kContainerParamsKey); + return params && params->modal_container_priority != -1; +} + +bool IsSystemModal(aura::Window* window) { + return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM; } // Returns the container which contains |window|. @@ -61,47 +89,34 @@ class AthenaFocusRules : public wm::BaseFocusRules { return params && params->can_activate_children; } virtual bool CanActivateWindow(aura::Window* window) const override { - // Check if containers of higher z-order than |window| have 'grab_inputs' + if (!window) + return true; + + // Check if containers of higher z-order than |window| have 'block_events' // fields. - if (window) { + if (window->GetRootWindow()) { const aura::Window::Windows& containers = window->GetRootWindow()->children(); aura::Window::Windows::const_iterator iter = std::find(containers.begin(), containers.end(), GetContainer(window)); DCHECK(iter != containers.end()); for (++iter; iter != containers.end(); ++iter) { - if (GrabsInput(*iter)) + if (BlockEvents(*iter)) return false; } } return BaseFocusRules::CanActivateWindow(window); } - private: - DISALLOW_COPY_AND_ASSIGN(AthenaFocusRules); -}; - -class AthenaWindowTreeClient : public aura::client::WindowTreeClient { - public: - explicit AthenaWindowTreeClient(aura::Window* container) - : container_(container) {} - - private: - virtual ~AthenaWindowTreeClient() {} - - // aura::client::WindowTreeClient: - virtual aura::Window* GetDefaultParent(aura::Window* context, - aura::Window* window, - const gfx::Rect& bounds) override { - aura::Window* transient_parent = wm::GetTransientParent(window); - if (transient_parent) - return GetContainer(transient_parent); - return container_; + virtual aura::Window* GetNextActivatableWindow( + aura::Window* ignore) const override { + aura::Window* next = wm::BaseFocusRules::GetNextActivatableWindow(ignore); + // TODO(oshima): Search from activatable containers if |next| is NULL. + return next; } - aura::Window* container_; - - DISALLOW_COPY_AND_ASSIGN(AthenaWindowTreeClient); + private: + DISALLOW_COPY_AND_ASSIGN(AthenaFocusRules); }; class AthenaScreenPositionClient : public aura::client::ScreenPositionClient { @@ -140,93 +155,56 @@ class AthenaScreenPositionClient : public aura::client::ScreenPositionClient { DISALLOW_COPY_AND_ASSIGN(AthenaScreenPositionClient); }; -class AthenaEventTargeter : public aura::WindowTargeter, - public aura::WindowObserver { +class AthenaWindowTargeter : public aura::WindowTargeter { public: - explicit AthenaEventTargeter(aura::Window* container) - : container_(container) { - container_->AddObserver(this); - } + explicit AthenaWindowTargeter(aura::Window* root_window) + : root_window_(root_window) {} - virtual ~AthenaEventTargeter() { - // Removed before the container is removed. - if (container_) - container_->RemoveObserver(this); - } - - void SetPreviousEventTargeter(scoped_ptr<ui::EventTargeter> targeter) { - previous_root_event_targeter_ = targeter.Pass(); - } + virtual ~AthenaWindowTargeter() {} private: // aura::WindowTargeter: virtual bool SubtreeCanAcceptEvent( ui::EventTarget* target, const ui::LocatedEvent& event) const override { + const aura::Window::Windows& containers = root_window_->children(); + auto r_iter = + std::find_if(containers.rbegin(), containers.rend(), &BlockEvents); + if (r_iter == containers.rend()) + return aura::WindowTargeter::SubtreeCanAcceptEvent(target, event); + aura::Window* window = static_cast<aura::Window*>(target); - const aura::Window::Windows& containers = - container_->GetRootWindow()->children(); - aura::Window::Windows::const_iterator iter = - std::find(containers.begin(), containers.end(), container_); - DCHECK(iter != containers.end()); - for (; iter != containers.end(); ++iter) { - if ((*iter)->Contains(window)) - return true; + for (;; --r_iter) { + if ((*r_iter)->Contains(window)) + return aura::WindowTargeter::SubtreeCanAcceptEvent(target, event); + if (r_iter == containers.rbegin()) + break; } return false; } - // aura::WindowObserver: - virtual void OnWindowDestroying(aura::Window* window) override { - aura::Window* root_window = container_->GetRootWindow(); - DCHECK_EQ(window, container_); - DCHECK_EQ( - this, static_cast<ui::EventTarget*>(root_window)->GetEventTargeter()); - - container_->RemoveObserver(this); - container_ = NULL; - - // This will remove myself. - root_window->SetEventTargeter(previous_root_event_targeter_.Pass()); + virtual ui::EventTarget* FindTargetForLocatedEvent( + ui::EventTarget* root, + ui::LocatedEvent* event) override { + ui::EventTarget* target = + aura::WindowTargeter::FindTargetForLocatedEvent(root, event); + if (target) + return target; + // If the root target is blocking the event, return the container even if + // there is no target found so that windows behind it will not be searched. + const ScreenManager::ContainerParams* params = + static_cast<aura::Window*>(root)->GetProperty(kContainerParamsKey); + return (params && params->block_events) ? root : NULL; } - aura::Window* container_; - scoped_ptr<ui::EventTargeter> previous_root_event_targeter_; - - DISALLOW_COPY_AND_ASSIGN(AthenaEventTargeter); -}; - -class ScreenManagerImpl : public ScreenManager { - public: - explicit ScreenManagerImpl(aura::Window* root_window); - virtual ~ScreenManagerImpl(); - - void Init(); - - private: - // ScreenManager: - virtual aura::Window* CreateDefaultContainer( - const ContainerParams& params) override; - virtual aura::Window* CreateContainer(const ContainerParams& params) override; - virtual aura::Window* GetContext() override { return root_window_; } - virtual void SetRotation(gfx::Display::Rotation rotation) override; - virtual void SetRotationLocked(bool rotation_locked) override; - // Not owned. aura::Window* root_window_; - scoped_ptr<aura::client::FocusClient> focus_client_; - scoped_ptr<aura::client::WindowTreeClient> window_tree_client_; - scoped_ptr<AcceleratorHandler> accelerator_handler_; - scoped_ptr< ::wm::ScopedCaptureClient> capture_client_; - scoped_ptr<aura::client::ScreenPositionClient> screen_position_client_; - - gfx::Display::Rotation last_requested_rotation_; - bool rotation_locked_; - - DISALLOW_COPY_AND_ASSIGN(ScreenManagerImpl); + DISALLOW_COPY_AND_ASSIGN(AthenaWindowTargeter); }; +} // namespace + ScreenManagerImpl::ScreenManagerImpl(aura::Window* root_window) : root_window_(root_window), last_requested_rotation_(gfx::Display::ROTATE_0), @@ -266,77 +244,49 @@ void ScreenManagerImpl::Init() { root_window_->SetLayoutManager(new FillLayoutManager(root_window_)); capture_client_.reset(new ::wm::ScopedCaptureClient(root_window_)); accelerator_handler_.reset(new ScreenAcceleratorHandler(root_window_)); -} -aura::Window* ScreenManagerImpl::CreateDefaultContainer( - const ContainerParams& params) { - aura::Window* container = CreateContainer(params); - window_tree_client_.reset(new AthenaWindowTreeClient(container)); - aura::client::SetWindowTreeClient(root_window_, window_tree_client_.get()); + aura::client::SetWindowTreeClient(root_window_, this); screen_position_client_.reset(new AthenaScreenPositionClient()); aura::client::SetScreenPositionClient(root_window_, screen_position_client_.get()); - - return container; + root_window_->SetEventTargeter( + make_scoped_ptr(new AthenaWindowTargeter(root_window_))); } -// A functor to find a container that has the higher priority. -struct HigherPriorityFinder { - HigherPriorityFinder(int p) : priority(p) {} - bool operator()(aura::Window* window) { - return window->GetProperty(kContainerParamsKey)->z_order_priority > - priority; +aura::Window* ScreenManagerImpl::FindContainerByPriority(int priority) { + for (aura::Window* window : root_window_->children()) { + if (window->GetProperty(kContainerParamsKey)->z_order_priority == priority) + return window; } - int priority; -}; - -#if !defined(NDEBUG) -struct PriorityMatcher { - PriorityMatcher(int p) : priority(p) {} - bool operator()(aura::Window* window) { - return window->GetProperty(kContainerParamsKey)->z_order_priority == - priority; - } - int priority; -}; -#endif + return NULL; +} aura::Window* ScreenManagerImpl::CreateContainer( const ContainerParams& params) { + const aura::Window::Windows& children = root_window_->children(); + + if (params.default_parent) { + CHECK(std::find_if(children.begin(), children.end(), &DefaultContainer) == + children.end()); + } + // mmodal container's priority must be higher than the container's priority. + DCHECK(params.modal_container_priority == -1 || + params.modal_container_priority > params.z_order_priority); + // Default parent must specify modal_container_priority. + DCHECK(!params.default_parent || params.modal_container_priority != -1); + aura::Window* container = new aura::Window(NULL); CHECK_GE(params.z_order_priority, 0); container->Init(aura::WINDOW_LAYER_NOT_DRAWN); container->SetName(params.name); - const aura::Window::Windows& children = root_window_->children(); - -#if !defined(NDEBUG) - DCHECK(std::find_if(children.begin(), - children.end(), - PriorityMatcher(params.z_order_priority)) - == children.end()) - << "The container with the priority " - << params.z_order_priority << " already exists."; -#endif + DCHECK(!FindContainerByPriority(params.z_order_priority)) + << "The container with the priority " << params.z_order_priority + << " already exists."; container->SetProperty(kContainerParamsKey, new ContainerParams(params)); - // If another container is already grabbing the input, SetEventTargeter - // implicitly release the grabbing and remove the EventTargeter instance. - // TODO(mukai|oshima): think about the ideal behavior of multiple grabbing - // and implement it. - if (params.grab_inputs) { - DCHECK(std::find_if(children.begin(), children.end(), &GrabsInput) - == children.end()) - << "input has already been grabbed by another container"; - AthenaEventTargeter* athena_event_targeter = - new AthenaEventTargeter(container); - athena_event_targeter->SetPreviousEventTargeter( - root_window_->SetEventTargeter( - scoped_ptr<ui::EventTargeter>(athena_event_targeter))); - } - root_window_->AddChild(container); aura::Window::Windows::const_iterator iter = @@ -350,6 +300,10 @@ aura::Window* ScreenManagerImpl::CreateContainer( return container; } +aura::Window* ScreenManagerImpl::GetContext() { + return root_window_; +} + void ScreenManagerImpl::SetRotation(gfx::Display::Rotation rotation) { last_requested_rotation_ = rotation; if (rotation_locked_ || rotation == @@ -369,14 +323,62 @@ void ScreenManagerImpl::SetRotationLocked(bool rotation_locked) { SetRotation(last_requested_rotation_); } -} // namespace +int ScreenManagerImpl::GetModalContainerPriority(aura::Window* window, + aura::Window* parent) { + const aura::Window::Windows& children = root_window_->children(); + if (window->GetProperty(aura::client::kAlwaysOnTopKey)) { + // Use top most modal container. + auto iter = std::find_if( + children.rbegin(), children.rend(), &HasModalContainerPriority); + DCHECK(iter != children.rend()); + return (*iter)->GetProperty(kContainerParamsKey)->modal_container_priority; + } else { + // use the container closest to the parent which has modal + // container priority. + auto iter = std::find(children.rbegin(), children.rend(), parent); + DCHECK(iter != children.rend()); + iter = std::find_if(iter, children.rend(), &HasModalContainerPriority); + DCHECK(iter != children.rend()); + return (*iter)->GetProperty(kContainerParamsKey)->modal_container_priority; + } +} + +aura::Window* ScreenManagerImpl::GetDefaultParent(aura::Window* context, + aura::Window* window, + const gfx::Rect& bounds) { + aura::Window* parent = wm::GetTransientParent(window); + if (parent) + parent = GetContainer(parent); + else + parent = GetDefaultContainer(); + + if (IsSystemModal(window)) { + DCHECK(window->type() == ui::wm::WINDOW_TYPE_NORMAL || + window->type() == ui::wm::WINDOW_TYPE_POPUP); + int priority = GetModalContainerPriority(window, parent); + + parent = FindContainerByPriority(priority); + if (!parent) { + ModalWindowController* controller = new ModalWindowController(priority); + parent = controller->modal_container(); + } + } + return parent; +} + +aura::Window* ScreenManagerImpl::GetDefaultContainer() { + const aura::Window::Windows& children = root_window_->children(); + return *(std::find_if(children.begin(), children.end(), &DefaultContainer)); +} ScreenManager::ContainerParams::ContainerParams(const std::string& n, int priority) : name(n), can_activate_children(false), - grab_inputs(false), - z_order_priority(priority) { + block_events(false), + z_order_priority(priority), + default_parent(false), + modal_container_priority(-1) { } // static diff --git a/athena/screen/screen_manager_impl.h b/athena/screen/screen_manager_impl.h new file mode 100644 index 0000000..1738542 --- /dev/null +++ b/athena/screen/screen_manager_impl.h @@ -0,0 +1,72 @@ +// Copyright 2014 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 ATHENA_SCREEN_SCREEN_MANAGER_IMPL_H_ +#define ATHENA_SCREEN_SCREEN_MANAGER_IMPL_H_ + +#include "athena/athena_export.h" +#include "athena/screen/public/screen_manager.h" +#include "base/memory/scoped_ptr.h" +#include "ui/aura/client/window_tree_client.h" + +namespace aura { +namespace client { +class FocusClient; +class ScreenPositionClient; +} +} + +namespace wm { +class ScopedCaptureClient; +} + +namespace athena { +class AcceleratorHandler; + +class ATHENA_EXPORT ScreenManagerImpl : public ScreenManager, + public aura::client::WindowTreeClient { + public: + explicit ScreenManagerImpl(aura::Window* root_window); + virtual ~ScreenManagerImpl(); + + void Init(); + + // Returns a container which has |priority|. Null if such container + // doesn't exist. + aura::Window* FindContainerByPriority(int priority); + + private: + // ScreenManager: + virtual aura::Window* CreateContainer(const ContainerParams& params) override; + virtual aura::Window* GetContext() override; + virtual void SetRotation(gfx::Display::Rotation rotation) override; + virtual void SetRotationLocked(bool rotation_locked) override; + + // aura::client::WindowTreeClient: + virtual aura::Window* GetDefaultParent(aura::Window* context, + aura::Window* window, + const gfx::Rect& bounds) override; + + int GetModalContainerPriority(aura::Window* window, aura::Window* parent); + + // Returns a container with |params.default_parent| == true. + aura::Window* GetDefaultContainer(); + + // Not owned. + aura::Window* root_window_; + + scoped_ptr<aura::client::FocusClient> focus_client_; + scoped_ptr<AcceleratorHandler> accelerator_handler_; + scoped_ptr<::wm::ScopedCaptureClient> capture_client_; + scoped_ptr<aura::client::ScreenPositionClient> screen_position_client_; + + gfx::Display::Rotation last_requested_rotation_; + bool rotation_locked_; + + DISALLOW_COPY_AND_ASSIGN(ScreenManagerImpl); +}; + +} // namespace athena + +#endif // ATHENA_SCREEN_SCREEN_MANAGER_IMPL_H_ diff --git a/athena/screen/screen_manager_unittest.cc b/athena/screen/screen_manager_unittest.cc index ed8b248..c8787da 100644 --- a/athena/screen/screen_manager_unittest.cc +++ b/athena/screen/screen_manager_unittest.cc @@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "athena/screen/public/screen_manager.h" - #include <algorithm> #include <string> +#include "athena/screen/screen_manager_impl.h" #include "athena/test/base/athena_test_base.h" +#include "athena/test/base/test_windows.h" #include "athena/util/container_priorities.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/window.h" +#include "ui/aura/window_targeter.h" #include "ui/events/test/event_generator.h" #include "ui/wm/core/window_util.h" @@ -111,12 +112,83 @@ TEST_F(ScreenManagerTest, NonActivatableContainer) { EXPECT_TRUE(wm::CanActivateWindow(window.get())); } -TEST_F(ScreenManagerTest, GrabInputContainer) { +TEST_F(ScreenManagerTest, BlockInputsShouldNotBlockVirtualKeyboard) { + ScreenManager::ContainerParams block_params("blocking", kTestZOrderPriority); + block_params.can_activate_children = true; + block_params.block_events = true; + aura::Window* block_container = + ScreenManager::Get()->CreateContainer(block_params); + + aura::test::EventCountDelegate block_delegate; + scoped_ptr<aura::Window> block_window(CreateWindow( + block_container, &block_delegate, gfx::Rect(0, 0, 100, 100))); + EXPECT_TRUE(wm::CanActivateWindow(block_window.get())); + + // Create a normal container appearing over the |block_container|. This is + // essentially the case of virtual keyboard. + ScreenManager::ContainerParams vk_params("virtual keyboard", + kTestZOrderPriority + 1); + vk_params.can_activate_children = true; + aura::Window* vk_container = ScreenManager::Get()->CreateContainer(vk_params); + + aura::test::EventCountDelegate vk_delegate; + scoped_ptr<aura::Window> vk_window( + CreateWindow(vk_container, &vk_delegate, gfx::Rect(0, 20, 100, 80))); + EXPECT_TRUE(wm::CanActivateWindow(vk_window.get())); + + ui::test::EventGenerator event_generator(root_window()); + event_generator.MoveMouseTo(10, 25); + event_generator.ClickLeftButton(); + EXPECT_EQ("0 0", block_delegate.GetMouseButtonCountsAndReset()); + EXPECT_EQ("1 1", vk_delegate.GetMouseButtonCountsAndReset()); +} + +TEST_F(ScreenManagerTest, DefaultContainer) { + ScreenManagerImpl* impl = + static_cast<ScreenManagerImpl*>(ScreenManager::Get()); + aura::Window* original_default = impl->FindContainerByPriority(CP_DEFAULT); + aura::Window* parent = original_default->parent(); + // Temporarily remove the original default container from tree. + parent->RemoveChild(original_default); + + ScreenManager::ContainerParams params("new_default", CP_END + 1); + params.default_parent = true; + params.modal_container_priority = CP_END + 2; + aura::Window* new_default = ScreenManager::Get()->CreateContainer(params); + aura::Window* w = test::CreateNormalWindow(NULL, NULL).release(); + EXPECT_EQ(new_default, w->parent()); + delete new_default; + + // Add the original back to shutdown properly. + parent->AddChild(original_default); +} + +namespace { + +class ScreenManagerTargeterTest + : public athena::test::AthenaTestBase, + public testing::WithParamInterface<bool> { + public: + ScreenManagerTargeterTest() + : targeter_(GetParam() ? NULL : new aura::WindowTargeter) {} + virtual ~ScreenManagerTargeterTest() {} + + protected: + scoped_ptr<ui::EventTargeter> targeter_; + + private: + DISALLOW_COPY_AND_ASSIGN(ScreenManagerTargeterTest); +}; + +} // namespace + +TEST_P(ScreenManagerTargeterTest, BlockContainer) { ScreenManager::ContainerParams normal_params( "normal", kTestZOrderPriority); normal_params.can_activate_children = true; aura::Window* normal_container = ScreenManager::Get()->CreateContainer(normal_params); + normal_container->SetEventTargeter(targeter_.Pass()); aura::test::EventCountDelegate normal_delegate; scoped_ptr<aura::Window> normal_window(CreateWindow( @@ -132,76 +204,48 @@ TEST_F(ScreenManagerTest, GrabInputContainer) { event_generator.ReleaseKey(ui::VKEY_A, ui::EF_NONE); EXPECT_EQ("1 1", normal_delegate.GetKeyCountsAndReset()); - ScreenManager::ContainerParams grab_params( - "grabbing", kTestZOrderPriority + 1); - grab_params.can_activate_children = true; - grab_params.grab_inputs = true; - aura::Window* grab_container = - ScreenManager::Get()->CreateContainer(grab_params); + ScreenManager::ContainerParams block_params("blocking", + kTestZOrderPriority + 1); + block_params.can_activate_children = true; + block_params.block_events = true; + aura::Window* block_container = + ScreenManager::Get()->CreateContainer(block_params); EXPECT_FALSE(wm::CanActivateWindow(normal_window.get())); - aura::test::EventCountDelegate grab_delegate; - scoped_ptr<aura::Window> grab_window(CreateWindow( - grab_container, &grab_delegate, gfx::Rect(10, 10, 100, 100))); - EXPECT_TRUE(wm::CanActivateWindow(grab_window.get())); + aura::test::EventCountDelegate block_delegate; + scoped_ptr<aura::Window> block_window(CreateWindow( + block_container, &block_delegate, gfx::Rect(10, 10, 100, 100))); + EXPECT_TRUE(wm::CanActivateWindow(block_window.get())); - wm::ActivateWindow(grab_window.get()); + wm::ActivateWindow(block_window.get()); // (0, 0) is still on normal_window, but the event should not go there - // because grabbing_container prevents it. + // because blockbing_container prevents it. event_generator.MoveMouseTo(0, 0); event_generator.ClickLeftButton(); EXPECT_EQ("0 0", normal_delegate.GetMouseButtonCountsAndReset()); - EXPECT_EQ("0 0", grab_delegate.GetMouseButtonCountsAndReset()); + EXPECT_EQ("0 0", block_delegate.GetMouseButtonCountsAndReset()); + event_generator.MoveMouseWheel(0, 10); + // EXPECT_EQ(0, normal_event_counter.num_scroll_events()); event_generator.MoveMouseTo(20, 20); event_generator.ClickLeftButton(); - EXPECT_EQ("1 1", grab_delegate.GetMouseButtonCountsAndReset()); + EXPECT_EQ("1 1", block_delegate.GetMouseButtonCountsAndReset()); event_generator.PressKey(ui::VKEY_A, ui::EF_NONE); event_generator.ReleaseKey(ui::VKEY_A, ui::EF_NONE); EXPECT_EQ("0 0", normal_delegate.GetKeyCountsAndReset()); - EXPECT_EQ("1 1", grab_delegate.GetKeyCountsAndReset()); -} - -TEST_F(ScreenManagerTest, GrabShouldNotBlockVirtualKeyboard) { - ScreenManager::ContainerParams grab_params("grabbing", kTestZOrderPriority); - grab_params.can_activate_children = true; - grab_params.grab_inputs = true; - aura::Window* grab_container = - ScreenManager::Get()->CreateContainer(grab_params); - - aura::test::EventCountDelegate grab_delegate; - scoped_ptr<aura::Window> grab_window( - CreateWindow(grab_container, &grab_delegate, gfx::Rect(0, 0, 100, 100))); - EXPECT_TRUE(wm::CanActivateWindow(grab_window.get())); - - // Create a normal container appearing over the |grab_container|. This is - // essentially the case of virtual keyboard. - ScreenManager::ContainerParams vk_params( - "virtual keyboard", kTestZOrderPriority + 1); - vk_params.can_activate_children = true; - aura::Window* vk_container = ScreenManager::Get()->CreateContainer(vk_params); - - aura::test::EventCountDelegate vk_delegate; - scoped_ptr<aura::Window> vk_window( - CreateWindow(vk_container, &vk_delegate, gfx::Rect(0, 20, 100, 80))); - EXPECT_TRUE(wm::CanActivateWindow(vk_window.get())); - - ui::test::EventGenerator event_generator(root_window()); - event_generator.MoveMouseTo(10, 25); - event_generator.ClickLeftButton(); - EXPECT_EQ("0 0", grab_delegate.GetMouseButtonCountsAndReset()); - EXPECT_EQ("1 1", vk_delegate.GetMouseButtonCountsAndReset()); + EXPECT_EQ("1 1", block_delegate.GetKeyCountsAndReset()); } -TEST_F(ScreenManagerTest, GrabAndMouseCapture) { +TEST_P(ScreenManagerTargeterTest, BlockAndMouseCapture) { ScreenManager::ContainerParams normal_params( "normal", kTestZOrderPriority); normal_params.can_activate_children = true; aura::Window* normal_container = ScreenManager::Get()->CreateContainer(normal_params); + normal_container->SetEventTargeter(targeter_.Pass()); aura::test::EventCountDelegate normal_delegate; scoped_ptr<aura::Window> normal_window(CreateWindow( @@ -211,29 +255,33 @@ TEST_F(ScreenManagerTest, GrabAndMouseCapture) { event_generator.MoveMouseTo(0, 0); event_generator.PressLeftButton(); - // Creating grabbing container while mouse pressing. - ScreenManager::ContainerParams grab_params( - "grabbing", kTestZOrderPriority + 1); - grab_params.can_activate_children = true; - grab_params.grab_inputs = true; - aura::Window* grab_container = - ScreenManager::Get()->CreateContainer(grab_params); + // Creating blocking container while mouse pressing. + ScreenManager::ContainerParams block_params("blocking", + kTestZOrderPriority + 1); + block_params.can_activate_children = true; + block_params.block_events = true; + aura::Window* block_container = + ScreenManager::Get()->CreateContainer(block_params); - aura::test::EventCountDelegate grab_delegate; - scoped_ptr<aura::Window> grab_window(CreateWindow( - grab_container, &grab_delegate, gfx::Rect(10, 10, 100, 100))); + aura::test::EventCountDelegate block_delegate; + scoped_ptr<aura::Window> block_window(CreateWindow( + block_container, &block_delegate, gfx::Rect(10, 10, 100, 100))); // Release event should be sent to |normal_window| because it captures the // mouse event. event_generator.ReleaseLeftButton(); EXPECT_EQ("1 1", normal_delegate.GetMouseButtonCountsAndReset()); - EXPECT_EQ("0 0", grab_delegate.GetMouseButtonCountsAndReset()); + EXPECT_EQ("0 0", block_delegate.GetMouseButtonCountsAndReset()); // After release, further mouse events should not be sent to |normal_window| - // because grab_container grabs the input. + // because block_container blocks the input. event_generator.ClickLeftButton(); EXPECT_EQ("0 0", normal_delegate.GetMouseButtonCountsAndReset()); - EXPECT_EQ("0 0", grab_delegate.GetMouseButtonCountsAndReset()); + EXPECT_EQ("0 0", block_delegate.GetMouseButtonCountsAndReset()); } +INSTANTIATE_TEST_CASE_P(WithOrWithoutTargeter, + ScreenManagerTargeterTest, + testing::Values(false, true)); + } // namespace athena |