blob: 06deeb6d9e0b790a59325e7834ae35733ee05657 [file] [log] [blame]
// Copyright 2015 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 SERVICES_UI_COMMON_TRANSIENT_WINDOW_UTILS_H_
#define SERVICES_UI_COMMON_TRANSIENT_WINDOW_UTILS_H_
#include <stddef.h>
#include <vector>
#include "services/ui/public/interfaces/mus_constants.mojom.h"
namespace ui {
// Returns true if |window| has |ancestor| as a transient ancestor. A transient
// ancestor is found by following the transient parent chain of the window.
template <class T>
bool HasTransientAncestor(const T* window, const T* ancestor) {
const T* transient_parent = window->transient_parent();
if (transient_parent == ancestor)
return true;
return transient_parent ? HasTransientAncestor(transient_parent, ancestor)
: false;
}
// Populates |ancestors| with all transient ancestors of |window| that are
// siblings of |window|. Returns true if any ancestors were found, false if not.
template <class T>
bool GetAllTransientAncestors(T* window, std::vector<T*>* ancestors) {
T* 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.
template <class T>
void FindCommonTransientAncestor(T** window1, T** window2) {
DCHECK(window1);
DCHECK(window2);
DCHECK(*window1);
DCHECK(*window2);
// Assemble chains of ancestors of both windows.
std::vector<T*> ancestors1;
std::vector<T*> 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;
}
}
}
template <class T>
bool AdjustStackingForTransientWindows(T** child,
T** target,
mojom::OrderDirection* direction,
T* stacking_target) {
if (stacking_target == *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 == mojom::OrderDirection::ABOVE &&
!HasTransientAncestor(*child, *target)) {
const std::vector<T*>& 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;
}
// Stacks transient descendants of |window| that are its siblings just above it.
// |GetStackingTarget| is a function that returns a marker associated with a
// Window that indicates the current Window being stacked.
// |Reorder| is a function that takes in two windows and orders the first
// relative to the second based on the provided OrderDirection.
template <class T>
void RestackTransientDescendants(T* window,
T** (*GetStackingTarget)(T*),
void (*Reorder)(T*,
T*,
mojom::OrderDirection)) {
T* parent = window->parent();
if (!parent)
return;
// stack any transient children that share the same parent to be in front of
// |window_|. the existing stacking order is preserved by iterating backwards
// and always stacking on top.
std::vector<T*> children(parent->children());
for (auto it = children.rbegin(); it != children.rend(); ++it) {
if ((*it) != window && HasTransientAncestor(*it, window)) {
T* old_stacking_target = *GetStackingTarget(*it);
*GetStackingTarget(*it) = window;
Reorder(*it, window, mojom::OrderDirection::ABOVE);
*GetStackingTarget(*it) = old_stacking_target;
}
}
}
} // namespace ui
#endif // SERVICES_UI_COMMON_TRANSIENT_WINDOW_UTILS_H_