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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
|
// 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_H_
#define UI_VIEWS_LAYOUT_FLEX_LAYOUT_H_
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/optional.h"
#include "ui/base/class_property.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/layout/layout_manager_base.h"
#include "ui/views/views_export.h"
namespace views {
class NormalizedSize;
class NormalizedSizeBounds;
class View;
// Provides CSS-like layout for a one-dimensional (vertical or horizontal)
// arrangement of child views. Independent alignment can be specified for the
// main and cross axes.
//
// Per-View margins (provided by view property kMarginsKey) specify how much
// space to leave around each child view. The |interior_margin| says how much
// empty space to leave at the edges of the parent view. If |collapse_margins|
// is false, these values are additive; if true, the greater of the two values
// is used. The |default_child_margins| provides a fallback for views without
// kMarginsKey set.
//
// collapse_margins = false:
//
// | interior margin> <margin [view]...
// | <margin [view] margin>
//
// collapse_margins = true:
//
// | interior margin> <margin [view]
// | <margin [view] margin> ...
//
// Views can have their own internal padding, using the kInternalPaddingKey
// property, which is subtracted from the margin space between child views.
//
// Calling SetVisible(false) on a child view outside of the FlexLayout will
// result in the child view being hidden until SetVisible(true) is called. This
// is irrespective of whether the FlexLayout has set the child view to be
// visible or not based on, for example, flex rules.
//
// If you want the host view to maintain control over a child view, you can
// exclude it from the layout. Excluded views are completely ignored during
// layout and do not have their properties modified.
//
// FlexSpecification objects determine how child views are sized. You can set
// individual flex rules for each child view, or a default for any child views
// without individual flex rules set. If you don't set anything, each view will
// take up its preferred size in the layout.
//
// The core function of this class is contained in
// GetPreferredSize(maximum_size) and Layout(). In both cases, a layout will
// be cached and typically not recalculated as long as none of the layout's
// properties or the preferred size or visibility of any of its children has
// changed.
class VIEWS_EXPORT FlexLayout : public LayoutManagerBase {
public:
FlexLayout();
~FlexLayout() override;
// Note: setters provide a Builder-style interface, so you can type:
// layout.SetMainAxisAlignment()
// .SetCrossAxisAlignment()
// .SetDefaultFlex(...);
FlexLayout& SetOrientation(LayoutOrientation orientation);
FlexLayout& SetMainAxisAlignment(LayoutAlignment main_axis_alignment);
FlexLayout& SetCrossAxisAlignment(LayoutAlignment cross_axis_alignment);
FlexLayout& SetInteriorMargin(const gfx::Insets& interior_margin);
FlexLayout& SetMinimumCrossAxisSize(int size);
FlexLayout& SetCollapseMargins(bool collapse_margins);
FlexLayout& SetIncludeHostInsetsInLayout(bool include_host_insets_in_layout);
FlexLayout& SetIgnoreDefaultMainAxisMargins(
bool ignore_default_main_axis_margins);
FlexLayout& SetFlexAllocationOrder(FlexAllocationOrder flex_allocation_order);
LayoutOrientation orientation() const { return orientation_; }
bool collapse_margins() const { return collapse_margins_; }
LayoutAlignment main_axis_alignment() const { return main_axis_alignment_; }
LayoutAlignment cross_axis_alignment() const { return cross_axis_alignment_; }
const gfx::Insets& interior_margin() const { return interior_margin_; }
int minimum_cross_axis_size() const { return minimum_cross_axis_size_; }
bool include_host_insets_in_layout() const {
return include_host_insets_in_layout_;
}
bool ignore_default_main_axis_margins() const {
return ignore_default_main_axis_margins_;
}
FlexAllocationOrder flex_allocation_order() const {
return flex_allocation_order_;
}
// Returns a flex rule that allows flex layouts to be nested with expected
// behavior.
FlexRule GetDefaultFlexRule() const;
// Moves and uses |value| as the default value for layout property |key|.
template <class T, class U>
FlexLayout& SetDefault(const ui::ClassProperty<T>* key, U&& value) {
layout_defaults_.SetProperty(key, std::forward<U>(value));
return *this;
}
// Copies and uses |value| as the default value for layout property |key|.
template <class T, class U>
FlexLayout& SetDefault(const ui::ClassProperty<T>* key, const U& value) {
layout_defaults_.SetProperty(key, value);
return *this;
}
protected:
// LayoutManagerBase:
ProposedLayout CalculateProposedLayout(
const SizeBounds& size_bounds) const override;
private:
struct ChildLayoutParams;
class ChildViewSpacing;
struct FlexLayoutData;
class PropertyHandler : public ui::PropertyHandler {
public:
explicit PropertyHandler(FlexLayout* layout);
protected:
// ui::PropertyHandler:
void AfterPropertyChange(const void* key, int64_t old_value) override;
private:
FlexLayout* const layout_;
};
// Maps a flex order (lower = allocated first, and therefore higher priority)
// to the indices of child views within that order that can flex.
// See FlexSpecification::order().
using FlexOrderToViewIndexMap = std::map<int, std::vector<size_t>>;
// Returns the preferred size for a given |rule| and |child| given unbounded
// space, with the caveat that for vertical layouts the horizontal axis is
// bounded to |available_cross| to factor in height-for-width considerations.
// This corresponds to the FlexSpecification "preferred size".
NormalizedSize GetPreferredSizeForRule(
const FlexRule& rule,
const View* child,
const base::Optional<int>& available_cross) const;
// Returns the size for a given |rule| and |child| with |available| space.
NormalizedSize GetCurrentSizeForRule(
const FlexRule& rule,
const View* child,
const NormalizedSizeBounds& available) const;
// Fills out the child entries for |data| and generates some initial size
// and visibility data, and stores off information about which views can
// expand in |flex_order_to_index|.
void InitializeChildData(const NormalizedSizeBounds& bounds,
FlexLayoutData* data,
FlexOrderToViewIndexMap* flex_order_to_index) const;
// Caclulates the child bounds (in screen coordinates) for each visible child
// in the layout.
void CalculateChildBounds(const SizeBounds& size_bounds,
FlexLayoutData* data) const;
// Calculates available space for non-flex views.
void CalculateNonFlexAvailableSpace(
FlexLayoutData* data,
int available_space,
const ChildViewSpacing& child_spacing,
const FlexOrderToViewIndexMap& flex_views) const;
// Returns the combined margins across the cross axis of the host view, for a
// particular child view.
Inset1D GetCrossAxisMargins(const FlexLayoutData& layout,
size_t child_index) const;
// Calculates a margin between two child views based on each's margin,
// inter-child spacing, and any internal padding present in one or both
// elements. Uses properties of the layout, like whether adjacent margins
// should be collapsed.
int CalculateMargin(int margin1, int margin2, int internal_padding) const;
// Calculates the cross-layout space available to a view based on the
// available space and margins.
base::Optional<int> GetAvailableCrossAxisSize(
const FlexLayoutData& layout,
size_t child_index,
const NormalizedSizeBounds& bounds) const;
// Calculates the preferred spacing between two child views, or between a
// view edge and the first or last visible child views.
int CalculateChildSpacing(const FlexLayoutData& layout,
base::Optional<size_t> child1_index,
base::Optional<size_t> child2_index) const;
// Calculates the position of each child view and the size of the overall
// layout based on tentative visibilities and sizes for each child.
void UpdateLayoutFromChildren(const NormalizedSizeBounds& bounds,
FlexLayoutData* data,
ChildViewSpacing* child_spacing) const;
// Applies flex rules to each view in a layout, updating |data| and
// |child_spacing|.
//
// If |expandable_views| is specified, any view requesting more than its
// preferred size will be clamped to its preferred size and be added to
// |expandable_views| for later processing after all other flex space has been
// allocated.
//
// Typically, this method will be called once with |expandable_views| set and
// then again with it null to allocate the remaining space.
void AllocateFlexSpace(const NormalizedSizeBounds& bounds,
const FlexOrderToViewIndexMap& order_to_index,
FlexLayoutData* data,
ChildViewSpacing* child_spacing,
FlexOrderToViewIndexMap* expandable_views) const;
// Gets the default value for a particular layout property, which will be used
// if the property is not set on a child view being laid out (e.g.
// kMarginsKey).
template <class T>
T* GetDefault(const ui::ClassProperty<T>* key) const {
return layout_defaults_.GetProperty(key);
}
// Clears the default value for a particular layout property, which will be
// used if the property is not set on a child view being laid out (e.g.
// kMarginsKey).
template <class T>
FlexLayout& ClearDefault(const ui::ClassProperty<T>* key) {
layout_defaults_.ClearProperty(key);
return *this;
}
static gfx::Size DefaultFlexRuleImpl(const FlexLayout* flex_layout,
const View* view,
const SizeBounds& size_bounds);
LayoutOrientation orientation_ = LayoutOrientation::kHorizontal;
// Adjacent view margins should be collapsed.
bool collapse_margins_ = false;
// Spacing between child views and host view border.
gfx::Insets interior_margin_;
// The alignment of children in the main axis. This is start by default.
LayoutAlignment main_axis_alignment_ = LayoutAlignment::kStart;
// The alignment of children in the cross axis. This is stretch by default.
LayoutAlignment cross_axis_alignment_ = LayoutAlignment::kStretch;
// The minimum cross axis size for the layout.
int minimum_cross_axis_size_ = 0;
// Whether to include host insets in the layout. Use when e.g. the host has an
// empty border and you want to treat that empty space as part of the interior
// margin of the host view.
//
// Most useful in conjunction with |collapse_margins| so child margins can
// overlap with the host's insets.
//
// In the future, we might consider putting this as metadata on the host's
// border - e.g. an EmptyBorder would be included in host insets but a thick
// frame would not be.
bool include_host_insets_in_layout_ = false;
// Whether host |interior_margin| overrides default child margins at the
// leading and trailing edge of the host view.
//
// Example:
// layout->SetIgnoreDefaultMainAxisMargins(true)
// .SetCollapseMargins(true)
// .SetDefault(kMarginsKey, {5, 10})
// .SetInteriorMargin({5, 5});
//
// This produces a margin of 5 DIP on all edges of the host view, with 10 DIP
// between child views. If SetIgnoreDefaultMainAxisMargins(true) was not
// called, the default child margin of 10 would also apply on the leading and
// trailing edge of the host view.
bool ignore_default_main_axis_margins_ = false;
// Order in which the host's child views receive their flex allocation.
// Setting to reverse is useful when, for example, you want views to drop out
// left-to-right when there's insufficient space to display them all instead
// of right-to-left.
FlexAllocationOrder flex_allocation_order_ = FlexAllocationOrder::kNormal;
// Default properties for any views that don't have them explicitly set for
// this layout.
PropertyHandler layout_defaults_{this};
DISALLOW_COPY_AND_ASSIGN(FlexLayout);
};
} // namespace views
#endif // UI_VIEWS_LAYOUT_FLEX_LAYOUT_H_
|