forked from Pissandshittium/pissandshittium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflex_layout.h
389 lines (336 loc) · 17 KB
/
flex_layout.h
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
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
// Copyright 2018 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_FLEX_LAYOUT_H_
#define UI_VIEWS_LAYOUT_FLEX_LAYOUT_H_
#include <list>
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "third_party/abseil-cpp/absl/types/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/view_class_properties.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(const FlexLayout&) = delete;
FlexLayout& operator=(const FlexLayout&) = delete;
~FlexLayout() override;
// Note: setters provide a Builder-style interface, so you can type:
// layout.SetMainAxisAlignment()
// .SetCrossAxisAlignment()
// .SetDefaultFlex(...);
// Note that cross-axis alignment can be overridden per-child using:
// child->SetProperty(kCrossAxisAlignmentKey, <value>);
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 *GetDefault(kCrossAxisAlignmentKey);
}
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:
const raw_ptr<FlexLayout> layout_;
};
using ChildIndices = std::list<size_t>;
// 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, ChildIndices>;
// Alignment used when the main-axis alignment is not specified.
static constexpr LayoutAlignment kDefaultMainAxisAlignment =
LayoutAlignment::kStart;
// Layout used when the cross-axis alignment is not specified.
static constexpr LayoutAlignment kDefaultCrossAxisAlignment =
LayoutAlignment::kStretch;
// 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 SizeBound& 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;
// 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.
SizeBound 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,
absl::optional<size_t> child1_index,
absl::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;
// 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 along the main axis for non-flex views and
// the values in |data.child_data|.
void CalculateNonFlexAvailableSpace(const SizeBound& available_space,
const FlexOrderToViewIndexMap& flex_views,
const ChildViewSpacing& child_spacing,
FlexLayoutData& data) const;
// Allocates space shortage (when the available space is less than the
// preferred size of the layout) across child views that can flex.
//
// Updates are made to |data| and |child_spacing|, and views that can still
// expand above their preferred size are added to |expandable_views| for later
// processing by AllocateFlexExcess().
void AllocateFlexShortage(const NormalizedSizeBounds& bounds,
const FlexOrderToViewIndexMap& order_to_index,
FlexLayoutData& data,
ChildViewSpacing& child_spacing,
FlexOrderToViewIndexMap& expandable_views) const;
// Allocates space above each child view's preferred size, based on remaining/
// excess space in the layout.
void AllocateFlexExcess(const NormalizedSizeBounds& bounds,
const FlexOrderToViewIndexMap& order_to_index,
FlexLayoutData& data,
ChildViewSpacing& child_spacing) const;
// Updates the available space for each flex child in |child_indices| in
// |data.child_data| based on |data.total_size|, |bounds|, and the margin data
// in |child_spacing|.
void CalculateFlexAvailableSpace(const NormalizedSizeBounds& bounds,
const ChildIndices& child_indices,
const ChildViewSpacing& child_spacing,
FlexLayoutData& data) const;
// Pre-allocates space associated with zero-weight views at a particular flex
// priority |flex_order|. Zero-weight child views are removed from
// |child_list| and their entries are updated in |data|. If |expandable_views|
// is specified, this is treated as the first pass, and space allocated to
// each view is capped at its preferred size; if the view would claim more
// space it is added to |expandable_views| (if specified).
void AllocateZeroWeightFlex(const NormalizedSizeBounds& bounds,
int flex_order,
ChildIndices& child_list,
FlexLayoutData& data,
ChildViewSpacing& child_spacing,
FlexOrderToViewIndexMap* expandable_views) const;
// Tries to allocate all the views in |child_list| in the available |bounds|.
// If successful, updates |data| and |expandable_views|. Returns the
// difference between the space needed by all of the views in |child_list| and
// the space provided by |bounds|.
SizeBound TryAllocateAll(const NormalizedSizeBounds& bounds,
int flex_order,
const ChildIndices& child_list,
FlexLayoutData& data,
ChildViewSpacing& child_spacing,
FlexOrderToViewIndexMap& expandable_views) const;
// Allocates flex excess |to_allocate| for a list of child views at the same
// priority order.
//
// It will attempt to do the entire allocation in one pass, removing all
// elements from |child_list| that it successfully allocates space for, but in
// the event a member of |child_list| does not take its full allocation, it
// will remove just that child and set aside its smaller size. At least one
// child will always be removed and |to_allocate| will be updated with the
// remaining space in the layout.
//
// This method should be called repeatedly until |child_list| is empty.
void AllocateFlexExcessAtOrder(const NormalizedSizeBounds& bounds,
SizeBound& to_allocate,
ChildIndices& child_list,
FlexLayoutData& data,
ChildViewSpacing& child_spacing) const;
// Allocates flex shortage for a list of child views at priority |order|.
//
// It will attempt to allocate the entire |child_list| in one pass, removing
// all elements that it successfully allocates space for, but in the event one
// or more members of |child_list| do not take their full allocation, those
// views will be allocated and removed from the list, and this method should
// be called again on the new, smaller list. At least one child is guaranteed
// to be allocated and removed each invocation.
//
// This method should be called repeatedly until |child_list| is empty.
void AllocateFlexShortageAtOrder(const NormalizedSizeBounds& bounds,
SizeBound deficit,
ChildIndices& child_list,
FlexLayoutData& data,
ChildViewSpacing& child_spacing) const;
// Returns the total weight for all children listed in |child_indices|.
static int CalculateFlexTotal(const FlexLayoutData& data,
const ChildIndices& child_indices);
// 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);
}
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_ = kDefaultMainAxisAlignment;
// 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};
};
} // namespace views
#endif // UI_VIEWS_LAYOUT_FLEX_LAYOUT_H_