OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/views/corewm/transient_window_stacking_client.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "ui/views/corewm/transient_window_manager.h" | |
10 #include "ui/views/corewm/window_util.h" | |
11 | |
12 using aura::Window; | |
13 | |
14 namespace views { | |
15 namespace corewm { | |
16 | |
17 namespace { | |
18 | |
19 // Populates |ancestors| with all transient ancestors of |window| that are | |
20 // siblings of |window|. Returns true if any ancestors were found, false if not. | |
21 bool GetAllTransientAncestors(Window* window, Window::Windows* ancestors) { | |
22 Window* parent = window->parent(); | |
23 for (; window; window = GetTransientParent(window)) { | |
24 if (window->parent() == parent) | |
25 ancestors->push_back(window); | |
26 } | |
27 return (!ancestors->empty()); | |
28 } | |
29 | |
30 // Replaces |window1| and |window2| with their possible transient ancestors that | |
31 // are still siblings (have a common transient parent). |window1| and |window2| | |
32 // are not modified if such ancestors cannot be found. | |
33 void FindCommonTransientAncestor(Window** window1, Window** window2) { | |
34 DCHECK(window1); | |
35 DCHECK(window2); | |
36 DCHECK(*window1); | |
37 DCHECK(*window2); | |
38 // Assemble chains of ancestors of both windows. | |
39 Window::Windows ancestors1; | |
40 Window::Windows ancestors2; | |
41 if (!GetAllTransientAncestors(*window1, &ancestors1) || | |
42 !GetAllTransientAncestors(*window2, &ancestors2)) { | |
43 return; | |
44 } | |
45 // Walk the two chains backwards and look for the first difference. | |
46 Window::Windows::reverse_iterator it1 = ancestors1.rbegin(); | |
47 Window::Windows::reverse_iterator it2 = ancestors2.rbegin(); | |
48 for (; it1 != ancestors1.rend() && it2 != ancestors2.rend(); ++it1, ++it2) { | |
49 if (*it1 != *it2) { | |
50 *window1 = *it1; | |
51 *window2 = *it2; | |
52 break; | |
53 } | |
54 } | |
55 } | |
56 | |
57 // Adjusts |target| so that we don't attempt to stack on top of a window with a | |
58 // NULL delegate. | |
59 void SkipNullDelegates(Window::StackDirection direction, Window** target) { | |
60 const Window::Windows& children((*target)->parent()->children()); | |
61 size_t target_i = | |
62 std::find(children.begin(), children.end(), *target) - | |
63 children.begin(); | |
64 | |
65 // By convention we don't stack on top of windows with layers with NULL | |
66 // delegates. Walk backward to find a valid target window. See tests | |
67 // TransientWindowManagerTest.StackingMadrigal and StackOverClosingTransient | |
68 // for an explanation of this. | |
69 while (target_i > 0) { | |
70 const size_t index = direction == Window::STACK_ABOVE ? | |
71 target_i : target_i - 1; | |
72 if (!children[index]->layer() || | |
73 children[index]->layer()->delegate() != NULL) | |
74 break; | |
75 --target_i; | |
76 } | |
77 *target = children[target_i]; | |
78 } | |
79 | |
80 } // namespace | |
81 | |
82 // static | |
83 TransientWindowStackingClient* TransientWindowStackingClient::instance_ = NULL; | |
84 | |
85 TransientWindowStackingClient::TransientWindowStackingClient() { | |
86 instance_ = this; | |
87 } | |
88 | |
89 TransientWindowStackingClient::~TransientWindowStackingClient() { | |
90 if (instance_ == this) | |
91 instance_ = NULL; | |
92 } | |
93 | |
94 bool TransientWindowStackingClient::AdjustStacking( | |
95 Window** child, | |
96 Window** target, | |
97 Window::StackDirection* direction) { | |
98 const TransientWindowManager* transient_manager = | |
99 TransientWindowManager::Get((*child)->parent()); | |
100 if (transient_manager && | |
101 transient_manager->IsStackingTransient(*child, *target)) | |
102 return true; | |
103 | |
104 // For windows that have transient children stack the transient ancestors that | |
105 // are siblings. This prevents one transient group from being inserted in the | |
106 // middle of another. | |
107 FindCommonTransientAncestor(child, target); | |
108 | |
109 // When stacking above skip to the topmost transient descendant of the target. | |
110 if (*direction == Window::STACK_ABOVE && | |
111 !HasTransientAncestor(*child, *target)) { | |
112 const Window::Windows& siblings((*child)->parent()->children()); | |
113 size_t target_i = | |
114 std::find(siblings.begin(), siblings.end(), *target) - siblings.begin(); | |
115 while (target_i + 1 < siblings.size() && | |
116 HasTransientAncestor(siblings[target_i + 1], *target)) { | |
117 ++target_i; | |
118 } | |
119 *target = siblings[target_i]; | |
120 } | |
121 | |
122 SkipNullDelegates(*direction, target); | |
123 | |
124 // If we couldn't find a valid target position, don't move anything. | |
125 if (*direction == Window::STACK_ABOVE && | |
126 ((*target)->layer() && (*target)->layer()->delegate() == NULL)) { | |
127 return false; | |
128 } | |
129 | |
130 return *child != *target; | |
131 } | |
132 | |
133 } // namespace corewm | |
134 } // namespace views | |
OLD | NEW |