summaryrefslogtreecommitdiff
path: root/chromium/ui/views/layout/box_layout.h
blob: 653d810eb9eabf5f9387db88143dbf60a153da36 (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
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
// Copyright (c) 2012 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_BOX_LAYOUT_H_
#define UI_VIEWS_LAYOUT_BOX_LAYOUT_H_

#include <map>

#include "base/compiler_specific.h"
#include "base/macros.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/layout/layout_manager.h"

namespace gfx {
class Rect;
class Size;
}

namespace views {

class View;

// A Layout manager that arranges child views vertically or horizontally in a
// side-by-side fashion with spacing around and between the child views. The
// child views are always sized according to their preferred size. If the
// host's bounds provide insufficient space, child views will be clamped.
// Excess space will not be distributed.
class VIEWS_EXPORT BoxLayout : public LayoutManager {
 public:
  enum Orientation {
    kHorizontal,
    kVertical,
  };

  // This specifies that the start/center/end of the collective child views is
  // aligned with the start/center/end of the host view. e.g. a horizontal
  // layout of MAIN_AXIS_ALIGNMENT_END will result in the child views being
  // right-aligned.
  enum MainAxisAlignment {
    MAIN_AXIS_ALIGNMENT_START,
    MAIN_AXIS_ALIGNMENT_CENTER,
    MAIN_AXIS_ALIGNMENT_END,
    // TODO(calamity): Add MAIN_AXIS_ALIGNMENT_JUSTIFY which spreads blank space
    // in-between the child views.
  };

  // This specifies where along the cross axis the children should be laid out.
  // e.g. a horizontal layout of CROSS_AXIS_ALIGNMENT_END will result in the
  // child views being bottom-aligned.
  enum CrossAxisAlignment {
    // This causes the child view to stretch to fit the host in the cross axis.
    CROSS_AXIS_ALIGNMENT_STRETCH,
    CROSS_AXIS_ALIGNMENT_START,
    CROSS_AXIS_ALIGNMENT_CENTER,
    CROSS_AXIS_ALIGNMENT_END,
  };

  // Use |inside_border_insets| to add additional space between the child
  // view area and the host view border. |between_child_spacing| controls the
  // space in between child views. Use view->SetProperty(kMarginsKey,
  // new gfx::Insets(xxx)) to add additional margins on a per-view basis. The
  // |collapse_margins_spacing| parameter controls whether or not adjacent
  // spacing/margins are collapsed based on the max of the two values. For the
  // cross axis, |collapse_margins_spacing| will collapse to the max of
  // inside_border_xxxxx_spacing and the corresponding margin edge from each
  // view.
  //
  // Given the following views where V = view bounds, M = Margins property,
  // B = between child spacing, S = inside border spacing and
  // <space> = added margins for alignment
  //
  // MMMMM  MMVVVVMM  MMMM
  // VVVVM            MMMM
  // VVVVM            MMMM
  // VVVVM            VVVV
  // MMMMM
  //
  // With collapse_margins_spacing = false, orientation = kHorizontal,
  // inside_border_spacing_horizontal = 2, inside_border_spacing_vertical = 2
  // and between_child_spacing = 1:
  //
  // -----------------------
  // SSSSSSSSSSSSSSSSSSSSSSS
  // SSSSSSSSSSSSSSSSSSSSSSS
  // SS    MBMM    MMBMMMMSS
  // SS    MBMM    MMBMMMMSS
  // SSMMMMMBMM    MMBMMMMSS
  // SSVVVVMBMMVVVVMMBVVVVSS
  // SSVVVVMBMMVVVVMMBVVVVSS
  // SSVVVVMBMMVVVVMMBVVVVSS
  // SSMMMMMBMMVVVVMMBVVVVSS
  // SSSSSSSSSSSSSSSSSSSSSSS
  // SSSSSSSSSSSSSSSSSSSSSSS
  // -----------------------
  //
  // Same as above except, collapse_margins_spacing = true.
  //
  // --------------------
  // SS          MMMMMMSS
  // SS          MMMMMMSS
  // SSMMMMMM    MMMMMMSS
  // SSVVVVMMVVVVMMVVVVSS
  // SSVVVVMMVVVVMMVVVVSS
  // SSVVVVMMVVVVMMVVVVSS
  // SSSSSSSSSSSSSSSSSSSS
  // SSSSSSSSSSSSSSSSSSSS
  // --------------------
  //
  BoxLayout(Orientation orientation,
            const gfx::Insets& inside_border_insets = gfx::Insets(),
            int between_child_spacing = 0,
            bool collapse_margins_spacing = false);
  ~BoxLayout() override;

  void set_main_axis_alignment(MainAxisAlignment main_axis_alignment) {
    main_axis_alignment_ = main_axis_alignment;
  }

  void set_cross_axis_alignment(CrossAxisAlignment cross_axis_alignment) {
    cross_axis_alignment_ = cross_axis_alignment;
  }

  void set_inside_border_insets(const gfx::Insets& insets) {
    inside_border_insets_ = insets;
  }

  void set_minimum_cross_axis_size(int size) {
    minimum_cross_axis_size_ = size;
  }

  // Sets the flex weight for the given |view|. Using the preferred size as
  // the basis, free space along the main axis is distributed to views in the
  // ratio of their flex weights. Similarly, if the views will overflow the
  // parent, space is subtracted in these ratios.
  // If true is passed in for |use_min_size|, the given view's minimum size
  // is then obtained from calling View::GetMinimumSize(). This will be the
  // minimum allowed size for the view along the main axis. False
  // for |use_min_size| (the default) will allow the |view| to be resized to a
  // minimum size of 0.
  //
  // A flex of 0 means this view is not resized. Flex values must not be
  // negative.
  void SetFlexForView(const View* view, int flex, bool use_min_size = false);

  // Clears the flex for the given |view|, causing it to use the default
  // flex.
  void ClearFlexForView(const View* view);

  // Sets the flex for views to use when none is specified.
  void SetDefaultFlex(int default_flex);

  // Overridden from views::LayoutManager:
  void Installed(View* host) override;
  void ViewRemoved(View* host, View* view) override;
  void Layout(View* host) override;
  gfx::Size GetPreferredSize(const View* host) const override;
  int GetPreferredHeightForWidth(const View* host, int width) const override;

 private:
  // This struct is used internally to "wrap" a child view in order to obviate
  // the need for the main layout logic to be fully aware of the per-view
  // margins when |collapse_margin_spacing_| is false. Since each view is a
  // rectangle of a certain size, this wrapper, coupled with any margins set
  // will increase the apparent size of the view along the main axis. All
  // necessary view size/position methods required for the layout logic add or
  // subtract the margins where appropriate to ensure the actual visible size of
  // the view doesn't include the margins. For the cross axis, the margins are
  // NOT included in the size/position calculations. BoxLayout will adjust the
  // bounding rectangle of the space used for layout using the maximum margin
  // for all views along the appropriate edge.
  // When |collapse_margin_spacing_| is true, this wrapper provides quick access
  // to the view's margins for use by the layout to collapse adjacent spacing
  // to the largest of the several values.
  class ViewWrapper {
   public:
    ViewWrapper();
    ViewWrapper(const BoxLayout* layout, View* view);
    ~ViewWrapper();

    int GetHeightForWidth(int width) const;
    const gfx::Insets& margins() const { return margins_; }
    gfx::Size GetPreferredSize() const;
    void SetBoundsRect(const gfx::Rect& bounds);
    View* view() const { return view_; }
    bool visible() const;

   private:
    View* view_;
    const BoxLayout* layout_;
    gfx::Insets margins_;

    DISALLOW_COPY_AND_ASSIGN(ViewWrapper);
  };

  struct Flex {
    int flex_weight;
    bool use_min_size;
  };

  using FlexMap = std::map<const View*, Flex>;

  // Returns the flex for the specified |view|.
  int GetFlexForView(const View* view) const;

  // Returns the minimum size for the specified |view|.
  int GetMinimumSizeForView(const View* view) const;

  // Returns the size and position along the main axis of |rect|.
  int MainAxisSize(const gfx::Rect& rect) const;
  int MainAxisPosition(const gfx::Rect& rect) const;

  // Sets the size and position along the main axis of |rect|.
  void SetMainAxisSize(int size, gfx::Rect* rect) const;
  void SetMainAxisPosition(int position, gfx::Rect* rect) const;

  // Returns the size and position along the cross axis of |rect|.
  int CrossAxisSize(const gfx::Rect& rect) const;
  int CrossAxisPosition(const gfx::Rect& rect) const;

  // Sets the size and position along the cross axis of |rect|.
  void SetCrossAxisSize(int size, gfx::Rect* rect) const;
  void SetCrossAxisPosition(int size, gfx::Rect* rect) const;

  // Returns the main axis size for the given view. |child_area_width| is needed
  // to calculate the height of the view when the orientation is vertical.
  int MainAxisSizeForView(const ViewWrapper& view, int child_area_width) const;

  // Returns the |left| or |top| edge of the given inset based on the value of
  // |orientation_|.
  int MainAxisLeadingInset(const gfx::Insets& insets) const;

  // Returns the |right| or |bottom| edge of the given inset based on the value
  // of |orientation_|.
  int MainAxisTrailingInset(const gfx::Insets& insets) const;

  // Returns the left (|x|) or top (|y|) edge of the given rect based on the
  // value of |orientation_|.
  int CrossAxisLeadingEdge(const gfx::Rect& rect) const;

  // Returns the |left| or |top| edge of the given inset based on the value of
  // |orientation_|.
  int CrossAxisLeadingInset(const gfx::Insets& insets) const;

  // Returns the |right| or |bottom| edge of the given inset based on the value
  // of |orientation_|.
  int CrossAxisTrailingInset(const gfx::Insets& insets) const;

  // Returns the main axis margin spacing between the two views which is the max
  // of the right margin from the |left| view, the left margin of the |right|
  // view and |between_child_spacing_|.
  int MainAxisMarginBetweenViews(const ViewWrapper& left,
                                 const ViewWrapper& right) const;

  // Returns the outer margin along the main axis as insets.
  gfx::Insets MainAxisOuterMargin() const;

  // Returns the maximum margin along the cross axis from all views as insets.
  gfx::Insets CrossAxisMaxViewMargin() const;

  // Adjusts the main axis for |rect| by collapsing the left or top margin of
  // the first view with corresponding side of |inside_border_insets_| and the
  // right or bottom margin of the last view with the corresponding side of
  // |inside_border_insets_|.
  void AdjustMainAxisForMargin(gfx::Rect* rect) const;

  // Adjust the cross axis for |rect| using the appropriate sides of
  // |inside_border_insets_|.
  void AdjustCrossAxisForInsets(gfx::Rect* rect) const;

  // Returns the cross axis size for the given view.
  int CrossAxisSizeForView(const ViewWrapper& view) const;

  // Returns the total margin width for the given view or 0 when
  // collapse_margins_spacing_ is true.
  int CrossAxisMarginSizeForView(const ViewWrapper& view) const;

  // Returns the Top or Left size of the margin for the given view or 0 when
  // collapse_margins_spacing_ is true.
  int CrossAxisLeadingMarginForView(const ViewWrapper& view) const;

  // Adjust the cross axis for |rect| using the given leading and trailing
  // values.
  void InsetCrossAxis(gfx::Rect* rect, int leading, int trailing) const;

  // The preferred size for the dialog given the width of the child area.
  gfx::Size GetPreferredSizeForChildWidth(const View* host,
                                          int child_area_width) const;

  // The amount of space the layout requires in addition to any space for the
  // child views.
  gfx::Size NonChildSize(const View* host) const;

  // The next visible view at > index. If no other views are visible, return
  // nullptr.
  View* NextVisibleView(int index) const;

  // Return the first visible view in the host or nullptr if none are visible.
  View* FirstVisibleView() const;

  // Return the last visible view in the host or nullptr if none are visible.
  View* LastVisibleView() const;

  const Orientation orientation_;

  // Spacing between child views and host view border.
  gfx::Insets inside_border_insets_;

  // Spacing to put in between child views.
  const int between_child_spacing_;

  // The alignment of children in the main axis. This is
  // MAIN_AXIS_ALIGNMENT_START by default.
  MainAxisAlignment main_axis_alignment_;

  // The alignment of children in the cross axis. This is
  // CROSS_AXIS_ALIGNMENT_STRETCH by default.
  CrossAxisAlignment cross_axis_alignment_;

  // A map of views to their flex weights.
  FlexMap flex_map_;

  // The flex weight for views if none is set. Defaults to 0.
  int default_flex_;

  // The minimum cross axis size for the layout.
  int minimum_cross_axis_size_;

  // Adjacent view margins and spacing should be collapsed.
  const bool collapse_margins_spacing_;

  // The view that this BoxLayout is managing the layout for.
  views::View* host_;

  DISALLOW_IMPLICIT_CONSTRUCTORS(BoxLayout);
};

} // namespace views

#endif  // UI_VIEWS_LAYOUT_BOX_LAYOUT_H_