blob: f8dc42c51f03c1255d97f6330c98b0f55f2edd43 [file] [log] [blame]
// 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/wm/core/transient_window_stacking_client.h"
#include <algorithm>
#include "ui/wm/core/transient_window_manager.h"
#include "ui/wm/core/window_util.h"
using aura::Window;
namespace wm {
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 = GetTransientParent(window)) {
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::reverse_iterator it1 = ancestors1.rbegin();
Window::Windows::reverse_iterator it2 = ancestors2.rbegin();
for (; it1 != ancestors1.rend() && it2 != ancestors2.rend(); ++it1, ++it2) {
if (*it1 != *it2) {
*window1 = *it1;
*window2 = *it2;
break;
}
}
}
// Adjusts |target| so that we don't attempt to stack on top of a window with a
// NULL delegate.
void SkipNullDelegates(Window::StackDirection direction, Window** target) {
const Window::Windows& children((*target)->parent()->children());
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
// TransientWindowManagerTest.StackingMadrigal and StackOverClosingTransient
// for an explanation of this.
while (target_i > 0) {
const size_t index = direction == Window::STACK_ABOVE ?
target_i : target_i - 1;
if (!children[index]->layer() ||
children[index]->layer()->delegate() != NULL)
break;
--target_i;
}
*target = children[target_i];
}
} // namespace
// static
TransientWindowStackingClient* TransientWindowStackingClient::instance_ = NULL;
TransientWindowStackingClient::TransientWindowStackingClient() {
instance_ = this;
}
TransientWindowStackingClient::~TransientWindowStackingClient() {
if (instance_ == this)
instance_ = NULL;
}
bool TransientWindowStackingClient::AdjustStacking(
Window** child,
Window** target,
Window::StackDirection* direction) {
const TransientWindowManager* transient_manager =
TransientWindowManager::Get(static_cast<const Window*>(*child));
if (transient_manager && transient_manager->IsStackingTransient(*target))
return true;
// 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];
}
SkipNullDelegates(*direction, target);
// If we couldn't find a valid target position, don't move anything.
if (*direction == Window::STACK_ABOVE &&
((*target)->layer() && (*target)->layer()->delegate() == NULL)) {
return false;
}
return *child != *target;
}
} // namespace wm