blob: ff3737626b6921cde329a47f9816479a5816321f [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 <stddef.h>
#include <algorithm>
#include "ui/aura/client/transient_window_client.h"
#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.
auto it1 = ancestors1.rbegin();
auto it2 = ancestors2.rbegin();
for (; it1 != ancestors1.rend() && it2 != ancestors2.rend(); ++it1, ++it2) {
if (*it1 != *it2) {
*window1 = *it1;
*window2 = *it2;
break;
}
}
}
} // 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::GetIfExists(*child);
if (transient_manager && transient_manager->IsStackingTransient(*target))
return true;
if (!(*child)->parent()->ShouldRestackTransientChildren())
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];
}
return *child != *target;
}
} // namespace wm