1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
// 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.
#ifndef UI_VIEWS_LAYOUT_LAYOUT_TYPES_H_
#define UI_VIEWS_LAYOUT_LAYOUT_TYPES_H_
#include <algorithm>
#include <optional>
#include <ostream>
#include <string>
#include <tuple>
#include <utility>
#include "base/check.h"
#include "ui/base/metadata/base_type_conversion.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/views_export.h"
namespace views {
// Describes how elements should be aligned within a layout. Baseline alignment
// only makes sense on the vertical axis and is unsupported by most layout
// managers.
enum class LayoutAlignment { kStart, kCenter, kEnd, kStretch, kBaseline };
// Whether a layout is oriented horizontally or vertically.
enum class LayoutOrientation {
kHorizontal,
kVertical,
};
// A value used to bound a View during layout. May be a finite bound or
// "unbounded", which is treated as larger than any finite value.
class VIEWS_EXPORT SizeBound {
public:
// Method definitions below to avoid "complex constructor" warning. Marked
// explicitly inline because Clang currently doesn't realize that "constexpr"
// explicitly means "inline" and thus should count as "intentionally inlined
// and thus shouldn't be warned about".
// TODO(crbug.com/40116092): Remove "inline" if Clang's isInlineSpecified()
// learns about constexpr.
// TODO(crbug.com/40116093): Put method bodies here if complex constructor
// heuristic learns to peer into types to discover that e.g. Optional is not
// complex.
inline constexpr SizeBound();
inline constexpr SizeBound(int bound); // NOLINT
inline constexpr SizeBound(const SizeBound&);
inline constexpr SizeBound(SizeBound&&);
SizeBound& operator=(const SizeBound&) = default;
SizeBound& operator=(SizeBound&&) = default;
~SizeBound() = default;
constexpr bool is_bounded() const { return bound_.has_value(); }
// Must only be invoked when there is a bound, since otherwise the value to
// return is unknown.
constexpr int value() const {
DCHECK(is_bounded());
return *bound_;
}
constexpr int min_of(int value) const {
return is_bounded() ? std::min(this->value(), value) : value;
}
constexpr int value_or(int defaule_value) const {
return is_bounded() ? value() : defaule_value;
}
void operator+=(const SizeBound& rhs);
void operator-=(const SizeBound& rhs);
std::string ToString() const;
private:
friend constexpr bool operator==(const SizeBound&,
const SizeBound&) = default;
// nullopt represents "unbounded".
std::optional<int> bound_;
};
constexpr SizeBound::SizeBound() = default;
constexpr SizeBound::SizeBound(int bound) : bound_(bound) {}
constexpr SizeBound::SizeBound(const SizeBound&) = default;
constexpr SizeBound::SizeBound(SizeBound&&) = default;
VIEWS_EXPORT SizeBound operator+(const SizeBound& lhs, const SizeBound& rhs);
VIEWS_EXPORT SizeBound operator-(const SizeBound& lhs, const SizeBound& rhs);
// Note: These comparisons treat unspecified values similar to infinity, that
// is, larger than any specified value and equal to any other unspecified value.
// While one can argue that two unspecified values can't be compared (and thus
// all comparisons should return false), this isn't what any callers want and
// breaks things in subtle ways, e.g. "a = b; DCHECK(a == b)" may fail.
constexpr bool operator<(const SizeBound& lhs, const SizeBound& rhs) {
return lhs.is_bounded() && (!rhs.is_bounded() || (lhs.value() < rhs.value()));
}
constexpr bool operator>(const SizeBound& lhs, const SizeBound& rhs) {
return rhs.is_bounded() && (!lhs.is_bounded() || (lhs.value() > rhs.value()));
}
constexpr bool operator<=(const SizeBound& lhs, const SizeBound& rhs) {
return !(lhs > rhs);
}
constexpr bool operator>=(const SizeBound& lhs, const SizeBound& rhs) {
return !(lhs < rhs);
}
// Stores an optional width and height upper bound. Used when calculating the
// preferred size of a layout pursuant to a maximum available size.
class VIEWS_EXPORT SizeBounds {
public:
// See comments in SizeBound re: "inline" with definitions below.
inline constexpr SizeBounds();
inline constexpr SizeBounds(SizeBound width, SizeBound height);
inline constexpr explicit SizeBounds(const gfx::Size& size);
inline constexpr SizeBounds(const SizeBounds&);
inline constexpr SizeBounds(SizeBounds&&);
SizeBounds& operator=(const SizeBounds&) = default;
SizeBounds& operator=(SizeBounds&&) = default;
~SizeBounds() = default;
constexpr const SizeBound& width() const { return width_; }
constexpr SizeBound& width() { return width_; }
void set_width(SizeBound width) { width_ = std::move(width); }
constexpr const SizeBound& height() const { return height_; }
constexpr SizeBound& height() { return height_; }
void set_height(SizeBound height) { height_ = std::move(height); }
constexpr bool is_fully_bounded() const {
return width_.is_bounded() && height_.is_bounded();
}
// Enlarges (or shrinks, if negative) each upper bound that is present by the
// specified amounts.
void Enlarge(int width, int height);
// Shrink the SizeBounds by the given `insets`.
SizeBounds Inset(const gfx::Insets& inset) const;
std::string ToString() const;
friend constexpr bool operator==(const SizeBounds&,
const SizeBounds&) = default;
private:
SizeBound width_;
SizeBound height_;
};
constexpr SizeBounds::SizeBounds() = default;
constexpr SizeBounds::SizeBounds(SizeBound width, SizeBound height)
: width_(std::move(width)), height_(std::move(height)) {}
constexpr SizeBounds::SizeBounds(const gfx::Size& size)
: width_(size.width()), height_(size.height()) {}
constexpr SizeBounds::SizeBounds(const SizeBounds&) = default;
constexpr SizeBounds::SizeBounds(SizeBounds&&) = default;
constexpr bool operator<(const SizeBounds& lhs, const SizeBounds& rhs) {
return std::tie(lhs.height(), lhs.width()) <
std::tie(rhs.height(), rhs.width());
}
// Returns true if the specified |size| can fit in the specified |bounds|.
// Returns false if either the width or height of |bounds| is specified and is
// smaller than the corresponding element of |size|.
VIEWS_EXPORT bool CanFitInBounds(const gfx::Size& size,
const SizeBounds& bounds);
// These are declared here for use in gtest-based unit tests but is defined in
// the views_test_support target. Depend on that to use this in your unit test.
// This should not be used in production code - call ToString() instead.
void PrintTo(const SizeBounds& size_bounds, ::std::ostream* os);
void PrintTo(LayoutOrientation layout_orientation, ::std::ostream* os);
} // namespace views
EXPORT_ENUM_CONVERTERS(views::LayoutAlignment, VIEWS_EXPORT)
EXPORT_ENUM_CONVERTERS(views::LayoutOrientation, VIEWS_EXPORT)
#endif // UI_VIEWS_LAYOUT_LAYOUT_TYPES_H_
|