blob: c1707f3321cc2b4853c83826560f0724480cc1c1 [file] [log] [blame]
// Copyright 2018 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/views/layout/flex_layout_types_internal.h"
#include <algorithm>
#include <tuple>
#include "base/strings/stringprintf.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/layout/flex_layout_types.h"
namespace views {
namespace internal {
namespace {
std::string OptionalToString(const base::Optional<int>& opt) {
if (!opt.has_value())
return "_";
return base::StringPrintf("%d", opt.value());
}
} // namespace
// Span ------------------------------------------------------------------------
void Span::SetSpan(int start, int length) {
start_ = start;
length_ = std::max(0, length);
}
void Span::Expand(int leading, int trailing) {
const int end = this->end();
set_start(start_ - leading);
set_end(end + trailing);
}
void Span::Inset(int leading, int trailing) {
Expand(-leading, -trailing);
}
void Span::Inset(const Inset1D& insets) {
Inset(insets.leading(), insets.trailing());
}
void Span::Center(const Span& container, const Inset1D& margins) {
int remaining = container.length() - length();
// Case 1: no room for any margins. Just center the span in the container,
// with equal overflow on each side.
if (remaining <= 0) {
set_start(std::ceilf(remaining * 0.5f));
return;
}
// Case 2: room for only part of the margins.
if (margins.size() > remaining) {
float scale = float{remaining} / float{margins.size()};
set_start(std::roundf(scale * margins.leading()));
return;
}
// Case 3: room for both span and margins. Center the whole unit.
remaining -= margins.size();
set_start(remaining / 2 + margins.leading());
}
void Span::Align(const Span& container,
LayoutAlignment alignment,
const Inset1D& margins) {
switch (alignment) {
case LayoutAlignment::kStart:
set_start(container.start() + margins.leading());
break;
case LayoutAlignment::kEnd:
set_start(container.end() - (margins.trailing() + length()));
break;
case LayoutAlignment::kCenter:
Center(container, margins);
break;
case LayoutAlignment::kStretch:
SetSpan(container.start() + margins.leading(),
std::max(0, container.length() - margins.size()));
break;
}
}
bool Span::operator==(const Span& other) const {
return start_ == other.start_ && length_ == other.length_;
}
bool Span::operator!=(const Span& other) const {
return !(*this == other);
}
bool Span::operator<(const Span& other) const {
return std::tie(start_, length_) < std::tie(other.start_, other.length_);
}
std::string Span::ToString() const {
return base::StringPrintf("%d [%d]", start(), length());
}
// Inset1D ---------------------------------------------------------------------
void Inset1D::SetInsets(int leading, int trailing) {
leading_ = leading;
trailing_ = trailing;
}
void Inset1D::Expand(int delta_leading, int delta_trailing) {
leading_ += delta_leading;
trailing_ += delta_trailing;
}
bool Inset1D::operator==(const Inset1D& other) const {
return leading_ == other.leading_ && trailing_ == other.trailing_;
}
bool Inset1D::operator!=(const Inset1D& other) const {
return !(*this == other);
}
bool Inset1D::operator<(const Inset1D& other) const {
return std::tie(leading_, trailing_) <
std::tie(other.leading_, other.trailing_);
}
std::string Inset1D::ToString() const {
return base::StringPrintf("%d, %d", leading(), trailing());
}
// NormalizedPoint -------------------------------------------------------------
void NormalizedPoint::SetPoint(int main, int cross) {
main_ = main;
cross_ = cross;
}
void NormalizedPoint::Offset(int delta_main, int delta_cross) {
main_ += delta_main;
cross_ += delta_cross;
}
bool NormalizedPoint::operator==(const NormalizedPoint& other) const {
return main_ == other.main_ && cross_ == other.cross_;
}
bool NormalizedPoint::operator!=(const NormalizedPoint& other) const {
return !(*this == other);
}
bool NormalizedPoint::operator<(const NormalizedPoint& other) const {
return std::tie(main_, cross_) < std::tie(other.main_, other.cross_);
}
std::string NormalizedPoint::ToString() const {
return base::StringPrintf("%d, %d", main(), cross());
}
// NormalizedSize --------------------------------------------------------------
void NormalizedSize::SetSize(int main, int cross) {
main_ = std::max(0, main);
cross_ = std::max(0, cross);
}
void NormalizedSize::Enlarge(int delta_main, int delta_cross) {
main_ = std::max(0, main_ + delta_main);
cross_ = std::max(0, cross_ + delta_cross);
}
void NormalizedSize::SetToMax(int main, int cross) {
main_ = std::max(main_, main);
cross_ = std::max(cross_, cross);
}
void NormalizedSize::SetToMin(int main, int cross) {
main_ = std::max(0, std::min(main_, main));
cross_ = std::max(0, std::min(cross_, cross));
}
void NormalizedSize::SetToMax(const NormalizedSize& other) {
SetToMax(other.main(), other.cross());
}
void NormalizedSize::SetToMin(const NormalizedSize& other) {
SetToMin(other.main(), other.cross());
}
bool NormalizedSize::operator==(const NormalizedSize& other) const {
return main_ == other.main_ && cross_ == other.cross_;
}
bool NormalizedSize::operator!=(const NormalizedSize& other) const {
return !(*this == other);
}
bool NormalizedSize::operator<(const NormalizedSize& other) const {
return std::tie(main_, cross_) < std::tie(other.main_, other.cross_);
}
std::string NormalizedSize::ToString() const {
return base::StringPrintf("%d x %d", main(), cross());
}
// NormalizedInsets ------------------------------------------------------------
bool NormalizedInsets::operator==(const NormalizedInsets& other) const {
return main_ == other.main_ && cross_ == other.cross_;
}
bool NormalizedInsets::operator!=(const NormalizedInsets& other) const {
return !(*this == other);
}
bool NormalizedInsets::operator<(const NormalizedInsets& other) const {
return std::tie(main_, cross_) < std::tie(other.main_, other.cross_);
}
std::string NormalizedInsets::ToString() const {
return base::StringPrintf("main: [%s], cross: [%s]",
main().ToString().c_str(),
cross().ToString().c_str());
}
// NormalizedSizeBounds --------------------------------------------------------
NormalizedSizeBounds::NormalizedSizeBounds() = default;
NormalizedSizeBounds::NormalizedSizeBounds(const base::Optional<int>& main,
const base::Optional<int>& cross)
: main_(main), cross_(cross) {}
NormalizedSizeBounds::NormalizedSizeBounds(const NormalizedSizeBounds& other)
: main_(other.main()), cross_(other.cross()) {}
NormalizedSizeBounds::NormalizedSizeBounds(const NormalizedSize& other)
: main_(other.main()), cross_(other.cross()) {}
void NormalizedSizeBounds::Expand(int main, int cross) {
if (main_)
main_ = std::max(0, *main_ + main);
if (cross_)
cross_ = std::max(0, *cross_ + cross);
}
bool NormalizedSizeBounds::operator==(const NormalizedSizeBounds& other) const {
return main_ == other.main_ && cross_ == other.cross_;
}
bool NormalizedSizeBounds::operator!=(const NormalizedSizeBounds& other) const {
return !(*this == other);
}
bool NormalizedSizeBounds::operator<(const NormalizedSizeBounds& other) const {
return std::tie(main_, cross_) < std::tie(other.main_, other.cross_);
}
std::string NormalizedSizeBounds::ToString() const {
return base::StringPrintf("%s x %s", OptionalToString(main()).c_str(),
OptionalToString(cross()).c_str());
}
// NormalizedRect --------------------------------------------------------------
Span NormalizedRect::GetMainSpan() const {
return Span(origin_main(), size_main());
}
void NormalizedRect::SetMainSpan(const Span& span) {
set_origin_main(span.start());
set_size_main(span.length());
}
void NormalizedRect::AlignMain(const Span& container,
LayoutAlignment alignment,
const Inset1D& margins) {
Span temp = GetMainSpan();
temp.Align(container, alignment, margins);
SetMainSpan(temp);
}
Span NormalizedRect::GetCrossSpan() const {
return Span(origin_cross(), size_cross());
}
void NormalizedRect::SetCrossSpan(const Span& span) {
set_origin_cross(span.start());
set_size_cross(span.length());
}
void NormalizedRect::AlignCross(const Span& container,
LayoutAlignment alignment,
const Inset1D& margins) {
Span temp = GetCrossSpan();
temp.Align(container, alignment, margins);
SetCrossSpan(temp);
}
void NormalizedRect::SetRect(int origin_main,
int origin_cross,
int size_main,
int size_cross) {
origin_.SetPoint(origin_main, origin_cross);
size_.SetSize(size_main, size_cross);
}
void NormalizedRect::SetByBounds(int origin_main,
int origin_cross,
int max_main,
int max_cross) {
origin_.SetPoint(origin_main, origin_cross);
size_.SetSize(std::max(0, max_main - origin_main),
std::max(0, max_cross - origin_cross));
}
void NormalizedRect::Inset(const NormalizedInsets& insets) {
Inset(insets.main_leading(), insets.cross_leading(), insets.main_trailing(),
insets.cross_trailing());
}
void NormalizedRect::Inset(int main, int cross) {
Inset(main, cross, main, cross);
}
void NormalizedRect::Inset(int main_leading,
int cross_leading,
int main_trailing,
int cross_trailing) {}
void NormalizedRect::Offset(int main, int cross) {
origin_.Offset(main, cross);
}
bool NormalizedRect::operator==(const NormalizedRect& other) const {
return origin_ == other.origin_ && size_ == other.size_;
}
bool NormalizedRect::operator!=(const NormalizedRect& other) const {
return !(*this == other);
}
bool NormalizedRect::operator<(const NormalizedRect& other) const {
return std::tie(origin_, size_) < std::tie(other.origin_, other.size_);
}
std::string NormalizedRect::ToString() const {
return base::StringPrintf("(%s) [%s]", origin().ToString().c_str(),
size().ToString().c_str());
}
// Normalization and Denormalization -------------------------------------------
NormalizedPoint Normalize(LayoutOrientation orientation,
const gfx::Point& point) {
switch (orientation) {
case LayoutOrientation::kHorizontal:
return NormalizedPoint(point.x(), point.y());
case LayoutOrientation::kVertical:
return NormalizedPoint(point.y(), point.x());
default:
DCHECK(false);
return NormalizedPoint(point.x(), point.y());
}
}
gfx::Point Denormalize(LayoutOrientation orientation,
const NormalizedPoint& point) {
switch (orientation) {
case LayoutOrientation::kHorizontal:
return gfx::Point(point.main(), point.cross());
case LayoutOrientation::kVertical:
return gfx::Point(point.cross(), point.main());
default:
DCHECK(false);
return gfx::Point(point.main(), point.cross());
}
}
NormalizedSize Normalize(LayoutOrientation orientation, const gfx::Size& size) {
switch (orientation) {
case LayoutOrientation::kHorizontal:
return NormalizedSize(size.width(), size.height());
case LayoutOrientation::kVertical:
return NormalizedSize(size.height(), size.width());
default:
DCHECK(false);
return NormalizedSize(size.width(), size.height());
}
}
gfx::Size Denormalize(LayoutOrientation orientation,
const NormalizedSize& size) {
switch (orientation) {
case LayoutOrientation::kHorizontal:
return gfx::Size(size.main(), size.cross());
case LayoutOrientation::kVertical:
return gfx::Size(size.cross(), size.main());
default:
DCHECK(false);
return gfx::Size(size.main(), size.cross());
}
}
NormalizedSizeBounds Normalize(LayoutOrientation orientation,
const SizeBounds& bounds) {
switch (orientation) {
case LayoutOrientation::kHorizontal:
return NormalizedSizeBounds(bounds.width(), bounds.height());
case LayoutOrientation::kVertical:
return NormalizedSizeBounds(bounds.height(), bounds.width());
default:
DCHECK(false);
return NormalizedSizeBounds(bounds.width(), bounds.height());
}
}
SizeBounds Denormalize(LayoutOrientation orientation,
const NormalizedSizeBounds& bounds) {
switch (orientation) {
case LayoutOrientation::kHorizontal:
return SizeBounds(bounds.main(), bounds.cross());
case LayoutOrientation::kVertical:
return SizeBounds(bounds.cross(), bounds.main());
default:
DCHECK(false);
return SizeBounds(bounds.main(), bounds.cross());
}
}
NormalizedInsets Normalize(LayoutOrientation orientation,
const gfx::Insets& insets) {
switch (orientation) {
case LayoutOrientation::kHorizontal:
return NormalizedInsets(insets.left(), insets.top(), insets.right(),
insets.bottom());
case LayoutOrientation::kVertical:
return NormalizedInsets(insets.top(), insets.left(), insets.bottom(),
insets.right());
default:
DCHECK(false);
return NormalizedInsets(insets.left(), insets.top(), insets.right(),
insets.bottom());
}
}
gfx::Insets Denormalize(LayoutOrientation orientation,
const NormalizedInsets& insets) {
switch (orientation) {
case LayoutOrientation::kHorizontal:
return gfx::Insets(insets.cross_leading(), insets.main_leading(),
insets.cross_trailing(), insets.main_trailing());
case LayoutOrientation::kVertical:
return gfx::Insets(insets.main_leading(), insets.cross_leading(),
insets.main_trailing(), insets.cross_trailing());
default:
DCHECK(false);
return gfx::Insets(insets.cross_leading(), insets.main_leading(),
insets.cross_trailing(), insets.main_trailing());
}
}
NormalizedRect Normalize(LayoutOrientation orientation,
const gfx::Rect& bounds) {
return NormalizedRect(Normalize(orientation, bounds.origin()),
Normalize(orientation, bounds.size()));
}
gfx::Rect Denormalize(LayoutOrientation orientation,
const NormalizedRect& bounds) {
return gfx::Rect(Denormalize(orientation, bounds.origin()),
Denormalize(orientation, bounds.size()));
}
} // namespace internal
} // namespace views