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