blob: 509d230ea986c321d105a09c813d36649ad5eeac [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.
#ifndef UI_VIEWS_LAYOUT_FLEX_LAYOUT_TYPES_H_
#define UI_VIEWS_LAYOUT_FLEX_LAYOUT_TYPES_H_
#include <algorithm>
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/optional.h"
#include "ui/views/layout/layout_types.h"
#include "ui/views/views_export.h"
namespace gfx {
class Size;
}
namespace views {
class View;
// Describes how elements should be aligned within a layout.
enum class LayoutAlignment { kStart, kCenter, kEnd, kStretch };
// Specifies whether flex space is allocated in the same order as the children
// in the host view, or in reverse order. Reverse order is useful when you want
// child views to drop out from left to right instead of right to left if there
// isn't enough space to display them.
enum class FlexAllocationOrder { kNormal, kReverse };
// Callback used to specify the size of a child view based on its size bounds.
// Create your own custom rules, or use the Minimum|MaximumFlexSizeRule
// constants below for common behaviors.
//
// This callback takes two parameters: a child view, and a set of size bounds
// representing the available space for that child view to occupy. The function
// returns the preferred size of the view within those bounds, which may exceed
// them if the child is not capable of shrinking to the specified size. The
// callback may also return an empty size, which means the child view can drop
// out of the layout. Not specifying either bound means there is an unlimited
// amount of room for the child view in that dimension (and the child view
// should probably use its preferred size).
//
// We provide the ability to use an arbitrary function here because some views
// have complex sizing behavior; for example, they may shrink stepwise as their
// internal elements drop out due to lack of space.
using FlexRule =
base::RepeatingCallback<gfx::Size(const View*, const SizeBounds&)>;
// Describes a simple rule for how a child view should shrink in a layout when
// the available size for that view decreases.
enum class MinimumFlexSizeRule {
kScaleToZero, // Ignore minimum size and scale all the way down.
kScaleToMinimumSnapToZero, // Scale to minimum then snap to zero.
kPreferredSnapToZero, // Use preferred, then snap to zero.
kScaleToMinimum, // Resize down to minimum then stop.
kPreferredSnapToMinimum, // Use preferred, then snap to minimum.
kPreferred // Always use preferred size.
};
// Describes a simple rule for how a child view should grow in a layout when
// there is extra size avaialble for that view to occupy.
enum class MaximumFlexSizeRule {
kPreferred, // Don't resize above preferred size.
kUnbounded // Allow resize to arbitrary size.
};
// Specifies how a view should flex (i.e. grow or shrink) within its parent as
// the available space changes. Flex specifications have three components:
// - A |rule| which tells the layout manager how the child view resizes with
// available space.
// - A |weight| which specifies how much of the available space is allocated to
// a child view that can flex. The percentage of space allocated is the
// weight divided by the total weight of all views at this order (see below).
// - An |order| which specifies the priority with which available space is
// allocated. All available space is offered to child views at order 1, then
// any remaining space is offered to order 2, and so forth.
//
// For example, say there are three child controls in a horizontal layout, each
// of which has a flex rule that allows it to be between 0 and 20 DIPs wide.
// Child A is at order 2 with weight 2, child B is at order 1 with weight 1, and
// child C is at order 2 and weight 1. The parent control is 50 DIPs across and
// has no margins.
//
// All 50 DIPs are offered to child B, since it is first in order. It consumes
// 20 DIPs, its maximum size. Of the remaining 30, 20 are offered to child A and
// 10 are offered to child C, each of which they take - the 2:1 ratio is due to
// the different weights. (Also note that, if there were another child at order
// 3, it would be offered zero DIPs and might choose not to display itself.)
class VIEWS_EXPORT FlexSpecification {
public:
// Creates a flex specification with the default rule (no flex, always use the
// view's preferred size).
FlexSpecification();
// Creates a flex specification with a custom flex rule. Note that any copies
// or mutations of this specification will also inherit the rule.
explicit FlexSpecification(FlexRule rule);
// Creates a flex specification using the specififed minimum size and size
// bounds rules. If |adjust_height_for_width| is specified, extra calculations
// will be done to ensure that the view can become taller if it is made
// narrower (typically only useful for multiline text controls).
//
// NOTE: Minimum and maximum size rules apply to both main and cross axes of
// the view in the layout. If you only need the view to flex based on its main
// axis (width for horizontal layouts, height for vertical) consider using the
// FlexSpecification(LayoutOrientation, ...) constructor below.
explicit FlexSpecification(
MinimumFlexSizeRule minimum_size_rule,
MaximumFlexSizeRule maximum_size_rule = MaximumFlexSizeRule::kPreferred,
bool adjust_height_for_width = false);
// Creates a flex specification for a layout with |orientation| using the
// given minimum and maximum flex size rules along the main axis. You may also
// specify an optional cross-axis minimum size rule, but the default is to use
// the child view's preferred size. (There is no max cross size rule because
// unless a layout's cross-axis alignment is set to kStretch views will never
// receive more than their preferred size in the cross-axis dimension.)
FlexSpecification(LayoutOrientation orientation,
MinimumFlexSizeRule minimum_main_axis_rule,
MaximumFlexSizeRule maximum_main_axis_rule =
MaximumFlexSizeRule::kPreferred,
bool adjust_height_for_width = false,
MinimumFlexSizeRule minimum_cross_axis_rule =
MinimumFlexSizeRule::kPreferred);
FlexSpecification(const FlexSpecification& other);
FlexSpecification& operator=(const FlexSpecification& other);
~FlexSpecification();
// Makes a copy of this specification with a different order.
FlexSpecification WithOrder(int order) const;
// Makes a copy of this specification with a different weight.
// Specifying |weight| of zero means the view will take as much space as it
// needs.
FlexSpecification WithWeight(int weight) const;
// Makes a copy of this specification with a different alignment. The default
// is kStretch, which means the child view will always fill the bounds
// allocated for it; specifying kLeading, kTrailing, or kCenter will cause the
// view to grow to a maximum of its preferred size and then "float" to either
// the center, leading, or trailing edge of the allocated space.
FlexSpecification WithAlignment(LayoutAlignment alignment) const;
const FlexRule& rule() const { return rule_; }
int weight() const { return weight_; }
int order() const { return order_; }
LayoutAlignment alignment() const { return alignment_; }
private:
FlexRule rule_;
int order_ = 1;
int weight_ = 0;
LayoutAlignment alignment_ = LayoutAlignment::kStretch;
};
// Represents insets in a single dimension.
class VIEWS_EXPORT Inset1D {
public:
constexpr Inset1D() = default;
constexpr explicit Inset1D(int all) : leading_(all), trailing_(all) {}
constexpr Inset1D(int leading, int trailing)
: leading_(leading), trailing_(trailing) {}
constexpr int leading() const { return leading_; }
void set_leading(int leading) { leading_ = leading; }
constexpr int trailing() const { return trailing_; }
void set_trailing(int trailing) { trailing_ = trailing; }
constexpr int size() const { return leading_ + trailing_; }
void SetInsets(int leading, int trailing);
void Expand(int delta_leading, int delta_trailing);
constexpr bool is_empty() const { return leading_ == 0 && trailing_ == 0; }
bool operator==(const Inset1D& other) const;
bool operator!=(const Inset1D& other) const;
bool operator<(const Inset1D& other) const;
std::string ToString() const;
private:
int leading_ = 0;
int trailing_ = 0;
};
// Represents a line segment in one dimension with a starting point and length.
class VIEWS_EXPORT Span {
public:
constexpr Span() = default;
constexpr Span(int start, int length) : start_(start), length_(length) {}
constexpr int start() const { return start_; }
void set_start(int start) { start_ = start; }
constexpr int length() const { return length_; }
void set_length(int length) { length_ = std::max(0, length); }
constexpr int end() const { return start_ + length_; }
void set_end(int end) { set_length(end - start_); }
void SetSpan(int start, int length);
// Expands the span by |leading| at the front (reducing the value of start()
// if |leading| is positive) and by |trailing| at the end (increasing the
// value of end() if |trailing| is positive).
void Expand(int leading, int trailing);
// Opposite of Expand(). Shrinks each end of the span by the specified amount.
void Inset(int leading, int trailing);
void Inset(const Inset1D& insets);
// Centers the span in another span, with optional margins.
// Overflow is handled gracefully.
void Center(const Span& container, const Inset1D& margins = Inset1D());
// Aligns the span in another span, with optional margins, using the specified
// alignment. Overflow is handled gracefully.
void Align(const Span& container,
LayoutAlignment alignment,
const Inset1D& margins = Inset1D());
constexpr bool is_empty() const { return length_ == 0; }
bool operator==(const Span& other) const;
bool operator!=(const Span& other) const;
bool operator<(const Span& other) const;
std::string ToString() const;
private:
int start_ = 0;
int length_ = 0;
};
// 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(MinimumFlexSizeRule minimum_flex_size_rule, ::std::ostream* os);
void PrintTo(MaximumFlexSizeRule maximum_flex_size_rule, ::std::ostream* os);
} // namespace views
#endif // UI_VIEWS_LAYOUT_FLEX_LAYOUT_TYPES_H_