blob: 85752c16ff5d07d98106fbb76a322989de6bb10e [file] [log] [blame]
// Copyright 2022 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_GFX_GEOMETRY_INSETS_OUTSETS_BASE_H_
#define UI_GFX_GEOMETRY_INSETS_OUTSETS_BASE_H_
#include <string>
#include <utility>
#include "base/component_export.h"
#include "base/numerics/clamped_math.h"
#include "ui/gfx/geometry/size.h"
namespace gfx {
// The common base template class for Insets and Outsets.
// Represents the widths of the four borders or margins of an unspecified
// rectangle. It stores the thickness of the top, left, bottom and right
// edges, without storing the actual size and position of the rectangle itself.
template <typename T>
class InsetsOutsetsBase {
public:
constexpr InsetsOutsetsBase() = default;
constexpr explicit InsetsOutsetsBase(int all)
: top_(all),
left_(all),
bottom_(ClampBottomOrRight(all, all)),
right_(ClampBottomOrRight(all, all)) {}
constexpr int top() const { return top_; }
constexpr int left() const { return left_; }
constexpr int bottom() const { return bottom_; }
constexpr int right() const { return right_; }
// Returns the total width taken up by the insets/outsets, which is the sum
// of the left and right insets/outsets.
constexpr int width() const { return left_ + right_; }
// Returns the total height taken up by the insets/outsets, which is the sum
// of the top and bottom insets/outsets.
constexpr int height() const { return top_ + bottom_; }
// Returns the sum of the left and right insets/outsets as the width,
// the sum of the top and bottom insets/outsets as the height.
constexpr Size size() const { return Size(width(), height()); }
// Returns true if the insets/outsets are empty.
bool IsEmpty() const { return width() == 0 && height() == 0; }
// Flips x- and y-axes.
void Transpose() {
using std::swap;
swap(top_, left_);
swap(bottom_, right_);
}
// These setters can be used together with the default constructor and the
// single-parameter constructor to construct Insets instances, for example:
// // T, L, B, R
// Insets a = Insets().set_top(2); // 2, 0, 0, 0
// Insets b = Insets().set_left(2).set_bottom(3); // 0, 2, 3, 0
// Insets c = Insets().set_left_right(1, 2).set_top_bottom(3, 4);
// // 3, 1, 4, 2
// Insets d = Insets(1).set_top(5); // 5, 1, 1, 1
constexpr T& set_top(int top) {
top_ = top;
bottom_ = ClampBottomOrRight(top_, bottom_);
return *static_cast<T*>(this);
}
constexpr T& set_left(int left) {
left_ = left;
right_ = ClampBottomOrRight(left_, right_);
return *static_cast<T*>(this);
}
constexpr T& set_bottom(int bottom) {
bottom_ = ClampBottomOrRight(top_, bottom);
return *static_cast<T*>(this);
}
constexpr T& set_right(int right) {
right_ = ClampBottomOrRight(left_, right);
return *static_cast<T*>(this);
}
// These are preferred to the above setters when setting a pair of edges
// because these have less clamping and better performance.
constexpr T& set_left_right(int left, int right) {
left_ = left;
right_ = ClampBottomOrRight(left_, right);
return *static_cast<T*>(this);
}
constexpr T& set_top_bottom(int top, int bottom) {
top_ = top;
bottom_ = ClampBottomOrRight(top_, bottom);
return *static_cast<T*>(this);
}
// In addition to the above, we can also use the following methods to
// construct Insets/Outsets.
// TLBR() is for Chomium UI code. We should not use it in blink code because
// the order of parameters is different from the normal orders used in blink.
// Blink code can use the above setters and VH().
static constexpr T TLBR(int top, int left, int bottom, int right) {
return T().set_top_bottom(top, bottom).set_left_right(left, right);
}
static constexpr T VH(int vertical, int horizontal) {
return TLBR(vertical, horizontal, vertical, horizontal);
}
// Sets each side to the maximum of the side and the corresponding side of
// |other|.
void SetToMax(const T& other) {
top_ = std::max(top_, other.top_);
left_ = std::max(left_, other.left_);
bottom_ = std::max(bottom_, other.bottom_);
right_ = std::max(right_, other.right_);
}
friend bool operator==(const InsetsOutsetsBase<T>&,
const InsetsOutsetsBase<T>&) = default;
void operator+=(const T& other) {
top_ = base::ClampAdd(top_, other.top_);
left_ = base::ClampAdd(left_, other.left_);
bottom_ = ClampBottomOrRight(top_, base::ClampAdd(bottom_, other.bottom_));
right_ = ClampBottomOrRight(left_, base::ClampAdd(right_, other.right_));
}
void operator-=(const T& other) {
top_ = base::ClampSub(top_, other.top_);
left_ = base::ClampSub(left_, other.left_);
bottom_ = ClampBottomOrRight(top_, base::ClampSub(bottom_, other.bottom_));
right_ = ClampBottomOrRight(left_, base::ClampSub(right_, other.right_));
}
T operator-() const {
return T()
.set_left_right(-base::MakeClampedNum(left_),
-base::MakeClampedNum(right_))
.set_top_bottom(-base::MakeClampedNum(top_),
-base::MakeClampedNum(bottom_));
}
// Returns a string representation of the insets/outsets.
COMPONENT_EXPORT(GEOMETRY) std::string ToString() const;
private:
// Clamp the bottom/right to avoid integer over/underflow in width() and
// height(). This returns the clamped bottom/right given a |top_or_left| and
// a |bottom_or_right|.
static constexpr int ClampBottomOrRight(int top_or_left,
int bottom_or_right) {
return base::ClampAdd(top_or_left, bottom_or_right) - top_or_left;
}
int top_ = 0;
int left_ = 0;
int bottom_ = 0;
int right_ = 0;
};
} // namespace gfx
#endif // UI_GFX_GEOMETRY_INSETS_OUTSETS_BASE_H_