blob: 904dbeb2bcb705327758539118e048d01afa027f [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/layout/proposed_layout.h"
#include <algorithm>
#include <map>
#include <sstream>
#include <string>
#include "base/strings/strcat.h"
#include "ui/gfx/animation/tween.h"
#include "ui/views/view.h"
namespace views {
namespace {
SizeBound SizeBoundValueBetween(double value,
const SizeBound& start,
const SizeBound& target) {
return (start.is_bounded() && target.is_bounded())
? gfx::Tween::IntValueBetween(value, start.value(), target.value())
: target;
}
SizeBounds SizeBoundsBetween(double value,
const SizeBounds& start,
const SizeBounds& target) {
return {SizeBoundValueBetween(value, start.width(), target.width()),
SizeBoundValueBetween(value, start.height(), target.height())};
}
} // anonymous namespace
bool ChildLayout::operator==(const ChildLayout& other) const {
// Note: if the view is not visible, the bounds do not matter as they will not
// be set.
return child_view == other.child_view && visible == other.visible &&
(!visible || bounds == other.bounds);
}
ChildLayout* ProposedLayout::GetLayoutFor(const View* child_view) {
// Defer to the const implementation and then cast back.
return const_cast<ChildLayout*>(
const_cast<const ProposedLayout*>(this)->GetLayoutFor(child_view));
}
const ChildLayout* ProposedLayout::GetLayoutFor(const View* child_view) const {
const auto found = std::ranges::find_if(
child_layouts, [child_view](const auto& child_layout) {
return child_view == child_layout.child_view;
});
return found == child_layouts.end() ? nullptr : &*found;
}
std::string ChildLayout::ToString() const {
return base::StrCat({"{", child_view->GetClassName(),
(visible ? " visible " : " not visible "),
bounds.ToString(), " / ", available_size.ToString(),
"}"});
}
ProposedLayout::ProposedLayout() = default;
ProposedLayout::ProposedLayout(const ProposedLayout& other) = default;
ProposedLayout::ProposedLayout(ProposedLayout&& other) = default;
ProposedLayout::ProposedLayout(
const gfx::Size& size,
const std::initializer_list<ChildLayout>& children)
: host_size(size), child_layouts(children) {}
ProposedLayout::~ProposedLayout() = default;
ProposedLayout& ProposedLayout::operator=(const ProposedLayout& other) =
default;
ProposedLayout& ProposedLayout::operator=(ProposedLayout&& other) = default;
std::string ProposedLayout::ToString() const {
std::ostringstream oss;
oss << "{" << host_size.ToString() << " {";
bool first = true;
for (const auto& child_layout : child_layouts) {
if (first) {
first = false;
} else {
oss << ", ";
}
oss << child_layout.ToString();
}
oss << "}}";
return oss.str();
}
ProposedLayout ProposedLayoutBetween(double value,
const ProposedLayout& start,
const ProposedLayout& target) {
if (value >= 1.0) {
return target;
}
ProposedLayout layout;
// Interpolate the host size.
layout.host_size =
gfx::Tween::SizeValueBetween(value, start.host_size, target.host_size);
// The views may not be listed in the same order and some views might be
// omitted from either the |start| or |target| layout.
std::map<const views::View*, size_t> start_view_to_index;
for (size_t i = 0; i < start.child_layouts.size(); ++i) {
start_view_to_index.emplace(start.child_layouts[i].child_view, i);
}
for (const ChildLayout& target_child : target.child_layouts) {
// Try to match the view from the target with the view from the start.
const auto start_match = start_view_to_index.find(target_child.child_view);
if (start_match == start_view_to_index.end()) {
// If there is no match, make the view present but invisible.
layout.child_layouts.push_back({target_child.child_view, false});
} else {
// Tween the two layouts.
const ChildLayout& start_child = start.child_layouts[start_match->second];
layout.child_layouts.push_back(
{target_child.child_view, start_child.visible && target_child.visible,
gfx::Tween::RectValueBetween(value, start_child.bounds,
target_child.bounds),
SizeBoundsBetween(value, start_child.available_size,
target_child.available_size)});
}
}
return layout;
}
} // namespace views