diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-12 22:13:27 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-12 22:13:27 +0000 |
commit | 9661035442518ea3fc5cefe2e9439fee8b735fc4 (patch) | |
tree | 5b64e8c05556aaa061742f3ed7117477221edba1 | |
parent | 75746b4b91172882cdaf1cc956147317627ad231 (diff) | |
download | chromium_src-9661035442518ea3fc5cefe2e9439fee8b735fc4.zip chromium_src-9661035442518ea3fc5cefe2e9439fee8b735fc4.tar.gz chromium_src-9661035442518ea3fc5cefe2e9439fee8b735fc4.tar.bz2 |
Moves transient stacking logic into WindowStackingClient
This is part of moving transient window logic out of Window.
BUG=none
TEST=covered by tests
R=ben@chromium.org
Review URL: https://codereview.chromium.org/103813006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@240440 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ash/shell/shell_main.cc | 15 | ||||
-rw-r--r-- | ash/test/ash_test_helper.cc | 7 | ||||
-rw-r--r-- | chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc | 10 | ||||
-rw-r--r-- | ui/aura/aura.gyp | 2 | ||||
-rw-r--r-- | ui/aura/client/window_stacking_client.cc | 26 | ||||
-rw-r--r-- | ui/aura/client/window_stacking_client.h | 34 | ||||
-rw-r--r-- | ui/aura/window.cc | 97 | ||||
-rw-r--r-- | ui/aura/window.h | 28 | ||||
-rw-r--r-- | ui/aura/window_unittest.cc | 137 | ||||
-rw-r--r-- | ui/views/corewm/transient_window_stacking_client.cc | 95 | ||||
-rw-r--r-- | ui/views/corewm/transient_window_stacking_client.h | 32 | ||||
-rw-r--r-- | ui/views/corewm/transient_window_stacking_client_unittest.cc | 175 | ||||
-rw-r--r-- | ui/views/test/views_test_base.cc | 5 | ||||
-rw-r--r-- | ui/views/views.gyp | 3 |
14 files changed, 445 insertions, 221 deletions
diff --git a/ash/shell/shell_main.cc b/ash/shell/shell_main.cc index 5648c81..b45ca5e 100644 --- a/ash/shell/shell_main.cc +++ b/ash/shell/shell_main.cc @@ -2,24 +2,37 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ash/shell/content_client/shell_main_delegate.h" #include "content/public/app/content_main.h" #include "sandbox/win/src/sandbox_types.h" -#include "ash/shell/content_client/shell_main_delegate.h" +#include "ui/views/corewm/transient_window_stacking_client.h" #if defined(OS_WIN) #include "content/public/app/startup_helper_win.h" #endif +namespace { + +void CommonInit() { + // SetWindowStackingClient() takes ownership of TransientWindowStackingClient. + aura::client::SetWindowStackingClient( + new views::corewm::TransientWindowStackingClient); +} + +} // namespace + #if defined(OS_WIN) int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t*, int) { sandbox::SandboxInterfaceInfo sandbox_info = {0}; content::InitializeSandboxInfo(&sandbox_info); ash::shell::ShellMainDelegate delegate; + CommonInit(); return content::ContentMain(instance, &sandbox_info, &delegate); } #else int main(int argc, const char** argv) { ash::shell::ShellMainDelegate delegate; + CommonInit(); return content::ContentMain(argc, argv, &delegate); } #endif diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc index 10ed2a2..6cff6ba 100644 --- a/ash/test/ash_test_helper.cc +++ b/ash/test/ash_test_helper.cc @@ -22,6 +22,7 @@ #include "ui/compositor/test/context_factories_for_test.h" #include "ui/message_center/message_center.h" #include "ui/views/corewm/capture_controller.h" +#include "ui/views/corewm/transient_window_stacking_client.h" #if defined(OS_CHROMEOS) #include "chromeos/audio/cras_audio_handler.h" @@ -87,6 +88,10 @@ void AshTestHelper::SetUp(bool start_session) { test_screenshot_delegate_ = new TestScreenshotDelegate(); shell->accelerator_controller()->SetScreenshotDelegate( scoped_ptr<ScreenshotDelegate>(test_screenshot_delegate_)); + + // SetWindowStackingClient() takes ownership of TransientWindowStackingClient. + aura::client::SetWindowStackingClient( + new views::corewm::TransientWindowStackingClient); } void AshTestHelper::TearDown() { @@ -111,6 +116,8 @@ void AshTestHelper::TearDown() { zero_duration_mode_.reset(); CHECK(!views::corewm::ScopedCaptureClient::IsActive()); + + aura::client::SetWindowStackingClient(NULL); } void AshTestHelper::RunAllPendingInMessageLoop() { diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc index 3bf186f..b6ecac4 100644 --- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc +++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc @@ -9,6 +9,10 @@ #include "chrome/common/chrome_switches.h" #include "ui/base/ui_base_switches.h" +#if defined(USE_AURA) +#include "ui/views/corewm/transient_window_stacking_client.h" +#endif + ChromeBrowserMainExtraPartsViews::ChromeBrowserMainExtraPartsViews() { } @@ -17,4 +21,10 @@ void ChromeBrowserMainExtraPartsViews::ToolkitInitialized() { // display the correct icon. if (!views::ViewsDelegate::views_delegate) views::ViewsDelegate::views_delegate = new ChromeViewsDelegate; + +#if defined(USE_AURA) + // SetWindowStackingClient() takes ownership of TransientWindowStackingClient. + aura::client::SetWindowStackingClient( + new views::corewm::TransientWindowStackingClient); +#endif } diff --git a/ui/aura/aura.gyp b/ui/aura/aura.gyp index 5cd9922..68164d4 100644 --- a/ui/aura/aura.gyp +++ b/ui/aura/aura.gyp @@ -71,6 +71,8 @@ 'client/visibility_client.h', 'client/window_move_client.cc', 'client/window_move_client.h', + 'client/window_stacking_client.cc', + 'client/window_stacking_client.h', 'client/window_tree_client.cc', 'client/window_tree_client.h', 'client/window_types.h', diff --git a/ui/aura/client/window_stacking_client.cc b/ui/aura/client/window_stacking_client.cc new file mode 100644 index 0000000..81da8d1 --- /dev/null +++ b/ui/aura/client/window_stacking_client.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2013 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/client/window_stacking_client.h" + +namespace aura { +namespace client { + +namespace { + +WindowStackingClient* instance = NULL; + +} // namespace + +void SetWindowStackingClient(WindowStackingClient* client) { + delete instance; + instance = client; +} + +AURA_EXPORT WindowStackingClient* GetWindowStackingClient() { + return instance; +} + +} // namespace client +} // namespace aura diff --git a/ui/aura/client/window_stacking_client.h b/ui/aura/client/window_stacking_client.h new file mode 100644 index 0000000..35772ed --- /dev/null +++ b/ui/aura/client/window_stacking_client.h @@ -0,0 +1,34 @@ +// Copyright (c) 2013 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_CLIENT_WINDOW_STACKING_DELEGATE_H_ +#define UI_AURA_CLIENT_WINDOW_STACKING_DELEGATE_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "ui/aura/aura_export.h" +#include "ui/aura/window.h" + +namespace aura { +namespace client { + +class AURA_EXPORT WindowStackingClient { + public: + virtual ~WindowStackingClient() {} + + // Invoked from the various Window stacking functions. Allows the + // WindowStackingClient to alter the source, target and/or direction to stack. + virtual void AdjustStacking(Window** child, + Window** target, + Window::StackDirection* direction) = 0; +}; + +// Sets/gets the WindowStackingClient. The setter takes ownership of |client|. +AURA_EXPORT void SetWindowStackingClient(WindowStackingClient* client); +AURA_EXPORT WindowStackingClient* GetWindowStackingClient(); + +} // namespace client +} // namespace aura + +#endif // UI_AURA_CLIENT_WINDOW_STACKING_DELEGATE_H_ diff --git a/ui/aura/window.cc b/ui/aura/window.cc index 5f70c0d..4836a15 100644 --- a/ui/aura/window.cc +++ b/ui/aura/window.cc @@ -20,6 +20,7 @@ #include "ui/aura/client/focus_client.h" #include "ui/aura/client/screen_position_client.h" #include "ui/aura/client/visibility_client.h" +#include "ui/aura/client/window_stacking_client.h" #include "ui/aura/env.h" #include "ui/aura/layout_manager.h" #include "ui/aura/root_window.h" @@ -1159,39 +1160,6 @@ void Window::OnParentChanged() { WindowObserver, observers_, OnWindowParentChanged(this, parent_)); } -bool Window::GetAllTransientAncestors(Window* window, - Windows* ancestors) const { - for (; window; window = window->transient_parent()) { - if (window->parent() == this) - ancestors->push_back(window); - } - return (!ancestors->empty()); -} - -void Window::FindCommonSiblings(Window** window1, Window** window2) const { - DCHECK(window1); - DCHECK(window2); - DCHECK(*window1); - DCHECK(*window2); - // Assemble chains of ancestors of both windows. - Windows ancestors1; - Windows ancestors2; - if (!GetAllTransientAncestors(*window1, &ancestors1) || - !GetAllTransientAncestors(*window2, &ancestors2)) { - return; - } - // Walk the two chains backwards and look for the first difference. - Windows::const_reverse_iterator it1 = ancestors1.rbegin(); - Windows::const_reverse_iterator it2 = ancestors2.rbegin(); - for (; it1 != ancestors1.rend() && it2 != ancestors2.rend(); ++it1, ++it2) { - if (*it1 != *it2) { - *window1 = *it1; - *window2 = *it2; - break; - } - } -} - bool Window::HasTransientAncestor(const Window* ancestor) const { if (transient_parent_ == ancestor) return true; @@ -1199,59 +1167,54 @@ bool Window::HasTransientAncestor(const Window* ancestor) const { transient_parent_->HasTransientAncestor(ancestor) : false; } -void Window::StackChildRelativeTo(Window* child, - Window* target, - StackDirection direction) { - DCHECK_NE(child, target); - DCHECK(child); - DCHECK(target); - DCHECK_EQ(this, child->parent()); - DCHECK_EQ(this, target->parent()); - - // Consider all transient children of both child's and target's ancestors - // up to the common ancestor if such exists and stack them as a unit. - // This prevents one transient group from being inserted in the middle of - // another. - FindCommonSiblings(&child, &target); - - const size_t target_i = - std::find(children_.begin(), children_.end(), target) - children_.begin(); - - // When stacking above skip to the topmost transient descendant of the target. - size_t final_target_i = target_i; - if (direction == STACK_ABOVE && !child->HasTransientAncestor(target)) { - while (final_target_i + 1 < children_.size() && - children_[final_target_i + 1]->HasTransientAncestor(target)) { - ++final_target_i; - } - } +void Window::SkipNullDelegatesForStacking(StackDirection direction, + Window** target) const { + DCHECK_EQ(this, (*target)->parent()); + size_t target_i = + std::find(children_.begin(), children_.end(), *target) - + children_.begin(); // By convention we don't stack on top of windows with layers with NULL // delegates. Walk backward to find a valid target window. // See tests WindowTest.StackingMadrigal and StackOverClosingTransient // for an explanation of this. - while (final_target_i > 0) { - const size_t index = direction == STACK_ABOVE ? - final_target_i : final_target_i - 1; + while (target_i > 0) { + const size_t index = direction == STACK_ABOVE ? target_i : target_i - 1; if (!children_[index]->layer_ || children_[index]->layer_->delegate() != NULL) break; - --final_target_i; + --target_i; } + *target = children_[target_i]; +} + +void Window::StackChildRelativeTo(Window* child, + Window* target, + StackDirection direction) { + DCHECK_NE(child, target); + DCHECK(child); + DCHECK(target); + DCHECK_EQ(this, child->parent()); + DCHECK_EQ(this, target->parent()); + + client::WindowStackingClient* stacking_client = + client::GetWindowStackingClient(); + if (stacking_client) + stacking_client->AdjustStacking(&child, &target, &direction); - Window* final_target = children_[final_target_i]; + SkipNullDelegatesForStacking(direction, &target); // If we couldn't find a valid target position, don't move anything. if (direction == STACK_ABOVE && - (final_target->layer_ && final_target->layer_->delegate() == NULL)) + (target->layer_ && target->layer_->delegate() == NULL)) return; // Don't try to stack a child above itself. - if (child == final_target) + if (child == target) return; // Move the child. - StackChildRelativeToImpl(child, final_target, direction); + StackChildRelativeToImpl(child, target, direction); // Stack any transient children that share the same parent to be in front of // 'child'. Preserve the existing stacking order by iterating in the order diff --git a/ui/aura/window.h b/ui/aura/window.h index 5ed3ea4..d69d88e 100644 --- a/ui/aura/window.h +++ b/ui/aura/window.h @@ -68,6 +68,12 @@ class AURA_EXPORT Window : public ui::LayerDelegate, public ui::EventTarget, public ui::GestureConsumer { public: + // Used when stacking windows. + enum StackDirection { + STACK_ABOVE, + STACK_BELOW + }; + typedef std::vector<Window*> Windows; explicit Window(WindowDelegate* delegate); @@ -378,14 +384,8 @@ class AURA_EXPORT Window : public ui::LayerDelegate, private: friend class test::WindowTestApi; friend class LayoutManager; - friend class WindowTargeter; friend class RootWindow; - - // Used when stacking windows. - enum StackDirection { - STACK_ABOVE, - STACK_BELOW - }; + friend class WindowTargeter; // Called by the public {Set,Get,Clear}Property functions. int64 SetPropertyInternal(const void* key, @@ -445,18 +445,14 @@ class AURA_EXPORT Window : public ui::LayerDelegate, // Called when this window's parent has changed. void OnParentChanged(); - // Populates |ancestors| with all transient ancestors of |window| that are - // children of |this|. Returns true if any ancestors were found, false if not. - bool GetAllTransientAncestors(Window* window, Windows* ancestors) const; - - // Replaces two windows |window1| and |window2| with their possible transient - // ancestors that are still siblings (have a common transient parent). - // |window1| and |window2| are not modified if such ancestors cannot be found. - void FindCommonSiblings(Window** window1, Window** window2) const; - // Returns true when |ancestor| is a transient ancestor of |this|. bool HasTransientAncestor(const Window* ancestor) const; + // Adjusts |target| so that we don't attempt to stack on top of a window with + // a NULL delegate. See implementation for details. + void SkipNullDelegatesForStacking(StackDirection direction, + Window** target) const; + // Determines the real location for stacking |child| and invokes // StackChildRelativeToImpl(). void StackChildRelativeTo(Window* child, diff --git a/ui/aura/window_unittest.cc b/ui/aura/window_unittest.cc index dfce60b..b791431 100644 --- a/ui/aura/window_unittest.cc +++ b/ui/aura/window_unittest.cc @@ -1618,143 +1618,6 @@ TEST_F(WindowTest, TransientChildren) { EXPECT_FALSE(w2->IsVisible()); } -// Tests that transient children are stacked as a unit when using stack above. -TEST_F(WindowTest, TransientChildrenGroupAbove) { - scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window())); - scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get())); - Window* w11 = CreateTestWindowWithId(11, parent.get()); - scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get())); - Window* w21 = CreateTestWindowWithId(21, parent.get()); - Window* w211 = CreateTestWindowWithId(211, parent.get()); - Window* w212 = CreateTestWindowWithId(212, parent.get()); - Window* w213 = CreateTestWindowWithId(213, parent.get()); - Window* w22 = CreateTestWindowWithId(22, parent.get()); - ASSERT_EQ(8u, parent->children().size()); - - w1->AddTransientChild(w11); // w11 is now owned by w1. - w2->AddTransientChild(w21); // w21 is now owned by w2. - w2->AddTransientChild(w22); // w22 is now owned by w2. - w21->AddTransientChild(w211); // w211 is now owned by w21. - w21->AddTransientChild(w212); // w212 is now owned by w21. - w21->AddTransientChild(w213); // w213 is now owned by w21. - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - // Stack w1 at the top (end), this should force w11 to be last (on top of w1). - parent->StackChildAtTop(w1.get()); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); - - // This tests that the order in children_ array rather than in - // transient_children_ array is used when reinserting transient children. - // If transient_children_ array was used '22' would be following '21'. - parent->StackChildAtTop(w2.get()); - EXPECT_EQ(w22, parent->children().back()); - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w11, w2.get()); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w21, w1.get()); - EXPECT_EQ(w22, parent->children().back()); - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w21, w22); - EXPECT_EQ(w213, parent->children().back()); - EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w11, w21); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w213, w21); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); - - // No change when stacking a transient parent above its transient child. - parent->StackChildAbove(w21, w211); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); - - // This tests that the order in children_ array rather than in - // transient_children_ array is used when reinserting transient children. - // If transient_children_ array was used '22' would be following '21'. - parent->StackChildAbove(w2.get(), w1.get()); - EXPECT_EQ(w212, parent->children().back()); - EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAbove(w11, w213); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); -} - -// Tests that transient children are stacked as a unit when using stack below. -TEST_F(WindowTest, TransientChildrenGroupBelow) { - scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window())); - scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get())); - Window* w11 = CreateTestWindowWithId(11, parent.get()); - scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get())); - Window* w21 = CreateTestWindowWithId(21, parent.get()); - Window* w211 = CreateTestWindowWithId(211, parent.get()); - Window* w212 = CreateTestWindowWithId(212, parent.get()); - Window* w213 = CreateTestWindowWithId(213, parent.get()); - Window* w22 = CreateTestWindowWithId(22, parent.get()); - ASSERT_EQ(8u, parent->children().size()); - - w1->AddTransientChild(w11); // w11 is now owned by w1. - w2->AddTransientChild(w21); // w21 is now owned by w2. - w2->AddTransientChild(w22); // w22 is now owned by w2. - w21->AddTransientChild(w211); // w211 is now owned by w21. - w21->AddTransientChild(w212); // w212 is now owned by w21. - w21->AddTransientChild(w213); // w213 is now owned by w21. - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - // Stack w2 at the bottom, this should force w11 to be last (on top of w1). - // This also tests that the order in children_ array rather than in - // transient_children_ array is used when reinserting transient children. - // If transient_children_ array was used '22' would be following '21'. - parent->StackChildAtBottom(w2.get()); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildAtBottom(w1.get()); - EXPECT_EQ(w22, parent->children().back()); - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w21, w1.get()); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w11, w2.get()); - EXPECT_EQ(w22, parent->children().back()); - EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w22, w21); - EXPECT_EQ(w213, parent->children().back()); - EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w21, w11); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w213, w211); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); - - // No change when stacking a transient parent below its transient child. - parent->StackChildBelow(w21, w211); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w1.get(), w2.get()); - EXPECT_EQ(w212, parent->children().back()); - EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get())); - - parent->StackChildBelow(w213, w11); - EXPECT_EQ(w11, parent->children().back()); - EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); -} - namespace { DEFINE_WINDOW_PROPERTY_KEY(int, kIntKey, -2); DEFINE_WINDOW_PROPERTY_KEY(const char*, kStringKey, "squeamish"); diff --git a/ui/views/corewm/transient_window_stacking_client.cc b/ui/views/corewm/transient_window_stacking_client.cc new file mode 100644 index 0000000..6a4f8ab --- /dev/null +++ b/ui/views/corewm/transient_window_stacking_client.cc @@ -0,0 +1,95 @@ +// Copyright (c) 2013 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/views/corewm/transient_window_stacking_client.h" + +#include <algorithm> + +using aura::Window; + +namespace views { +namespace corewm { + +namespace { + +// Populates |ancestors| with all transient ancestors of |window| that are +// siblings of |window|. Returns true if any ancestors were found, false if not. +bool GetAllTransientAncestors(Window* window, Window::Windows* ancestors) { + Window* parent = window->parent(); + for (; window; window = window->transient_parent()) { + if (window->parent() == parent) + ancestors->push_back(window); + } + return (!ancestors->empty()); +} + +// Replaces |window1| and |window2| with their possible transient ancestors that +// are still siblings (have a common transient parent). |window1| and |window2| +// are not modified if such ancestors cannot be found. +void FindCommonTransientAncestor(Window** window1, Window** window2) { + DCHECK(window1); + DCHECK(window2); + DCHECK(*window1); + DCHECK(*window2); + // Assemble chains of ancestors of both windows. + Window::Windows ancestors1; + Window::Windows ancestors2; + if (!GetAllTransientAncestors(*window1, &ancestors1) || + !GetAllTransientAncestors(*window2, &ancestors2)) { + return; + } + // Walk the two chains backwards and look for the first difference. + Window::Windows::const_reverse_iterator it1 = ancestors1.rbegin(); + Window::Windows::const_reverse_iterator it2 = ancestors2.rbegin(); + for (; it1 != ancestors1.rend() && it2 != ancestors2.rend(); ++it1, ++it2) { + if (*it1 != *it2) { + *window1 = *it1; + *window2 = *it2; + break; + } + } +} + +// Returns true if |window| has |ancestor| as a transient ancestor. A transient +// ancestor is found by following the transient parent chain of the window. +bool HasTransientAncestor(const Window* window, const Window* ancestor) { + if (window->transient_parent() == ancestor) + return true; + return window->transient_parent() ? + HasTransientAncestor(window->transient_parent(), ancestor) : false; +} + +} // namespace + +TransientWindowStackingClient::TransientWindowStackingClient() { +} + +TransientWindowStackingClient::~TransientWindowStackingClient() { +} + +void TransientWindowStackingClient::AdjustStacking( + Window** child, + Window** target, + Window::StackDirection* direction) { + // For windows that have transient children stack the transient ancestors that + // are siblings. This prevents one transient group from being inserted in the + // middle of another. + FindCommonTransientAncestor(child, target); + + // When stacking above skip to the topmost transient descendant of the target. + if (*direction == Window::STACK_ABOVE && + !HasTransientAncestor(*child, *target)) { + const Window::Windows& siblings((*child)->parent()->children()); + size_t target_i = + std::find(siblings.begin(), siblings.end(), *target) - siblings.begin(); + while (target_i + 1 < siblings.size() && + HasTransientAncestor(siblings[target_i + 1], *target)) { + ++target_i; + } + *target = siblings[target_i]; + } +} + +} // namespace corewm +} // namespace views diff --git a/ui/views/corewm/transient_window_stacking_client.h b/ui/views/corewm/transient_window_stacking_client.h new file mode 100644 index 0000000..1935836 --- /dev/null +++ b/ui/views/corewm/transient_window_stacking_client.h @@ -0,0 +1,32 @@ +// Copyright (c) 2013 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_VIEWS_COREWM_TRANSIENT_WINDOW_STACKING_CLIENT_H_ +#define UI_VIEWS_COREWM_TRANSIENT_WINDOW_STACKING_CLIENT_H_ + +#include "ui/aura/client/window_stacking_client.h" +#include "ui/views/views_export.h" + +namespace views { +namespace corewm { + +class VIEWS_EXPORT TransientWindowStackingClient + : public aura::client::WindowStackingClient { + public: + TransientWindowStackingClient(); + virtual ~TransientWindowStackingClient(); + + // WindowStackingClient: + virtual void AdjustStacking(aura::Window** child, + aura::Window** target, + aura::Window::StackDirection* direction) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(TransientWindowStackingClient); +}; + +} // namespace corewm +} // namespace views + +#endif // UI_VIEWS_COREWM_TRANSIENT_WINDOW_STACKING_CLIENT_H_ diff --git a/ui/views/corewm/transient_window_stacking_client_unittest.cc b/ui/views/corewm/transient_window_stacking_client_unittest.cc new file mode 100644 index 0000000..a84b832 --- /dev/null +++ b/ui/views/corewm/transient_window_stacking_client_unittest.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2013 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/views/corewm/transient_window_stacking_client.h" + +#include "base/memory/scoped_ptr.h" +#include "ui/aura/test/aura_test_base.h" +#include "ui/aura/test/test_windows.h" + +using aura::test::ChildWindowIDsAsString; +using aura::test::CreateTestWindowWithId; +using aura::Window; + +namespace views { +namespace corewm { + +class TransientWindowStackingClientTest : public aura::test::AuraTestBase { + public: + TransientWindowStackingClientTest() {} + virtual ~TransientWindowStackingClientTest() {} + + virtual void SetUp() OVERRIDE { + AuraTestBase::SetUp(); + aura::client::SetWindowStackingClient(new TransientWindowStackingClient); + } + + virtual void TearDown() OVERRIDE { + aura::client::SetWindowStackingClient(NULL); + AuraTestBase::TearDown(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TransientWindowStackingClientTest); +}; + +// Tests that transient children are stacked as a unit when using stack above. +TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupAbove) { + scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window())); + scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get())); + Window* w11 = CreateTestWindowWithId(11, parent.get()); + scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get())); + Window* w21 = CreateTestWindowWithId(21, parent.get()); + Window* w211 = CreateTestWindowWithId(211, parent.get()); + Window* w212 = CreateTestWindowWithId(212, parent.get()); + Window* w213 = CreateTestWindowWithId(213, parent.get()); + Window* w22 = CreateTestWindowWithId(22, parent.get()); + ASSERT_EQ(8u, parent->children().size()); + + w1->AddTransientChild(w11); // w11 is now owned by w1. + w2->AddTransientChild(w21); // w21 is now owned by w2. + w2->AddTransientChild(w22); // w22 is now owned by w2. + w21->AddTransientChild(w211); // w211 is now owned by w21. + w21->AddTransientChild(w212); // w212 is now owned by w21. + w21->AddTransientChild(w213); // w213 is now owned by w21. + EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); + + // Stack w1 at the top (end), this should force w11 to be last (on top of w1). + parent->StackChildAtTop(w1.get()); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); + + // This tests that the order in children_ array rather than in + // transient_children_ array is used when reinserting transient children. + // If transient_children_ array was used '22' would be following '21'. + parent->StackChildAtTop(w2.get()); + EXPECT_EQ(w22, parent->children().back()); + EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); + + parent->StackChildAbove(w11, w2.get()); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); + + parent->StackChildAbove(w21, w1.get()); + EXPECT_EQ(w22, parent->children().back()); + EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); + + parent->StackChildAbove(w21, w22); + EXPECT_EQ(w213, parent->children().back()); + EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get())); + + parent->StackChildAbove(w11, w21); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get())); + + parent->StackChildAbove(w213, w21); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); + + // No change when stacking a transient parent above its transient child. + parent->StackChildAbove(w21, w211); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); + + // This tests that the order in children_ array rather than in + // transient_children_ array is used when reinserting transient children. + // If transient_children_ array was used '22' would be following '21'. + parent->StackChildAbove(w2.get(), w1.get()); + EXPECT_EQ(w212, parent->children().back()); + EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get())); + + parent->StackChildAbove(w11, w213); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); +} + +// Tests that transient children are stacked as a unit when using stack below. +TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupBelow) { + scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window())); + scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get())); + Window* w11 = CreateTestWindowWithId(11, parent.get()); + scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get())); + Window* w21 = CreateTestWindowWithId(21, parent.get()); + Window* w211 = CreateTestWindowWithId(211, parent.get()); + Window* w212 = CreateTestWindowWithId(212, parent.get()); + Window* w213 = CreateTestWindowWithId(213, parent.get()); + Window* w22 = CreateTestWindowWithId(22, parent.get()); + ASSERT_EQ(8u, parent->children().size()); + + w1->AddTransientChild(w11); // w11 is now owned by w1. + w2->AddTransientChild(w21); // w21 is now owned by w2. + w2->AddTransientChild(w22); // w22 is now owned by w2. + w21->AddTransientChild(w211); // w211 is now owned by w21. + w21->AddTransientChild(w212); // w212 is now owned by w21. + w21->AddTransientChild(w213); // w213 is now owned by w21. + EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); + + // Stack w2 at the bottom, this should force w11 to be last (on top of w1). + // This also tests that the order in children_ array rather than in + // transient_children_ array is used when reinserting transient children. + // If transient_children_ array was used '22' would be following '21'. + parent->StackChildAtBottom(w2.get()); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); + + parent->StackChildAtBottom(w1.get()); + EXPECT_EQ(w22, parent->children().back()); + EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); + + parent->StackChildBelow(w21, w1.get()); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get())); + + parent->StackChildBelow(w11, w2.get()); + EXPECT_EQ(w22, parent->children().back()); + EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get())); + + parent->StackChildBelow(w22, w21); + EXPECT_EQ(w213, parent->children().back()); + EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get())); + + parent->StackChildBelow(w21, w11); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get())); + + parent->StackChildBelow(w213, w211); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); + + // No change when stacking a transient parent below its transient child. + parent->StackChildBelow(w21, w211); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); + + parent->StackChildBelow(w1.get(), w2.get()); + EXPECT_EQ(w212, parent->children().back()); + EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get())); + + parent->StackChildBelow(w213, w11); + EXPECT_EQ(w11, parent->children().back()); + EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get())); +} + +} // namespace corewm +} // namespace views diff --git a/ui/views/test/views_test_base.cc b/ui/views/test/views_test_base.cc index 1a6f5c9..cff7e9a 100644 --- a/ui/views/test/views_test_base.cc +++ b/ui/views/test/views_test_base.cc @@ -13,6 +13,7 @@ #include "ui/aura/root_window.h" #include "ui/aura/test/aura_test_helper.h" #include "ui/views/corewm/capture_controller.h" +#include "ui/views/corewm/transient_window_stacking_client.h" #endif namespace views { @@ -37,6 +38,9 @@ void ViewsTestBase::SetUp() { #if defined(USE_AURA) aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_)); aura_test_helper_->SetUp(); + // SetWindowStackingClient() takes ownership of TransientWindowStackingClient. + aura::client::SetWindowStackingClient( + new corewm::TransientWindowStackingClient); #endif // USE_AURA ui::InitializeInputMethodForTesting(); } @@ -53,6 +57,7 @@ void ViewsTestBase::TearDown() { ui::ShutdownInputMethodForTesting(); #if defined(USE_AURA) aura_test_helper_->TearDown(); + aura::client::SetWindowStackingClient(NULL); CHECK(!corewm::ScopedCaptureClient::IsActive()); #endif // USE_AURA } diff --git a/ui/views/views.gyp b/ui/views/views.gyp index e853e76..af835c22 100644 --- a/ui/views/views.gyp +++ b/ui/views/views.gyp @@ -262,6 +262,8 @@ 'corewm/tooltip_controller.h', 'corewm/tooltip_win.cc', 'corewm/tooltip_win.h', + 'corewm/transient_window_stacking_client.cc', + 'corewm/transient_window_stacking_client.h', 'corewm/visibility_controller.cc', 'corewm/visibility_controller.h', 'corewm/window_animations.cc', @@ -759,6 +761,7 @@ 'corewm/shadow_controller_unittest.cc', 'corewm/tooltip_aura_unittest.cc', 'corewm/tooltip_controller_unittest.cc', + 'corewm/transient_window_stacking_client_unittest.cc', 'corewm/visibility_controller_unittest.cc', 'corewm/window_animations_unittest.cc', 'corewm/window_util_unittest.cc', |