summaryrefslogtreecommitdiff
path: root/chromium/ui/views/layout/flex_layout_types.h
blob: 509d230ea986c321d105a09c813d36649ad5eeac (plain)
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
// 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_