1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef UI_VIEWS_LAYOUT_BOX_LAYOUT_H_ 6 #define UI_VIEWS_LAYOUT_BOX_LAYOUT_H_ 7 8 #include <map> 9 10 #include "ui/gfx/geometry/insets.h" 11 #include "ui/views/layout/layout_manager.h" 12 #include "ui/views/view.h" 13 14 namespace gfx { 15 class Rect; 16 class Size; 17 } // namespace gfx 18 19 namespace views { 20 21 // A Layout manager that arranges child views vertically or horizontally in a 22 // side-by-side fashion with spacing around and between the child views. The 23 // child views are always sized according to their preferred size. If the 24 // host's bounds provide insufficient space, child views will be clamped. 25 // Excess space will not be distributed. 26 class VIEWS_EXPORT BoxLayout : public LayoutManager { 27 public: 28 enum class Orientation { 29 kHorizontal, 30 kVertical, 31 }; 32 33 // This specifies that the start/center/end of the collective child views is 34 // aligned with the start/center/end of the host view. e.g. a horizontal 35 // layout of MainAxisAlignment::kEnd will result in the child views being 36 // right-aligned. 37 enum class MainAxisAlignment { 38 kStart, 39 kCenter, 40 kEnd, 41 // TODO(calamity): Add MAIN_AXIS_ALIGNMENT_JUSTIFY which spreads blank space 42 // in-between the child views. 43 }; 44 45 // This specifies where along the cross axis the children should be laid out. 46 // e.g. a horizontal layout of kEnd will result in the child views being 47 // bottom-aligned. 48 enum class CrossAxisAlignment { 49 // This causes the child view to stretch to fit the host in the cross axis. 50 kStretch, 51 kStart, 52 kCenter, 53 kEnd, 54 }; 55 56 // Use |inside_border_insets| to add additional space between the child 57 // view area and the host view border. |between_child_spacing| controls the 58 // space in between child views. Use view->SetProperty(kMarginsKey, 59 // gfx::Insets(xxx)) to add additional margins on a per-view basis. The 60 // |collapse_margins_spacing| parameter controls whether or not adjacent 61 // spacing/margins are collapsed based on the max of the two values. For the 62 // cross axis, |collapse_margins_spacing| will collapse to the max of 63 // inside_border_xxxxx_spacing and the corresponding margin edge from each 64 // view. 65 // 66 // Given the following views where V = view bounds, M = Margins property, 67 // B = between child spacing, S = inside border spacing and 68 // <space> = added margins for alignment 69 // 70 // MMMMM MMVVVVMM MMMM 71 // VVVVM MMMM 72 // VVVVM MMMM 73 // VVVVM VVVV 74 // MMMMM 75 // 76 // With collapse_margins_spacing = false, orientation = kHorizontal, 77 // inside_border_spacing_horizontal = 2, inside_border_spacing_vertical = 2 78 // and between_child_spacing = 1: 79 // 80 // ----------------------- 81 // SSSSSSSSSSSSSSSSSSSSSSS 82 // SSSSSSSSSSSSSSSSSSSSSSS 83 // SS MBMM MMBMMMMSS 84 // SS MBMM MMBMMMMSS 85 // SSMMMMMBMM MMBMMMMSS 86 // SSVVVVMBMMVVVVMMBVVVVSS 87 // SSVVVVMBMMVVVVMMBVVVVSS 88 // SSVVVVMBMMVVVVMMBVVVVSS 89 // SSMMMMMBMMVVVVMMBVVVVSS 90 // SSSSSSSSSSSSSSSSSSSSSSS 91 // SSSSSSSSSSSSSSSSSSSSSSS 92 // ----------------------- 93 // 94 // Same as above except, collapse_margins_spacing = true. 95 // 96 // -------------------- 97 // SS MMMMMMSS 98 // SS MMMMMMSS 99 // SSMMMMMM MMMMMMSS 100 // SSVVVVMMVVVVMMVVVVSS 101 // SSVVVVMMVVVVMMVVVVSS 102 // SSVVVVMMVVVVMMVVVVSS 103 // SSSSSSSSSSSSSSSSSSSS 104 // SSSSSSSSSSSSSSSSSSSS 105 // -------------------- 106 // 107 explicit BoxLayout(Orientation orientation = Orientation::kHorizontal, 108 const gfx::Insets& inside_border_insets = gfx::Insets(), 109 int between_child_spacing = 0, 110 bool collapse_margins_spacing = false); 111 ~BoxLayout() override; 112 113 void SetOrientation(Orientation orientation); 114 Orientation GetOrientation() const; 115 116 // TODO(tluk): These class member setters should likely be calling 117 // LayoutManager::InvalidateLayout() . set_main_axis_alignment(MainAxisAlignment main_axis_alignment)118 void set_main_axis_alignment(MainAxisAlignment main_axis_alignment) { 119 main_axis_alignment_ = main_axis_alignment; 120 } main_axis_alignment()121 MainAxisAlignment main_axis_alignment() const { return main_axis_alignment_; } 122 set_cross_axis_alignment(CrossAxisAlignment cross_axis_alignment)123 void set_cross_axis_alignment(CrossAxisAlignment cross_axis_alignment) { 124 cross_axis_alignment_ = cross_axis_alignment; 125 } cross_axis_alignment()126 CrossAxisAlignment cross_axis_alignment() const { 127 return cross_axis_alignment_; 128 } 129 set_inside_border_insets(const gfx::Insets & insets)130 void set_inside_border_insets(const gfx::Insets& insets) { 131 inside_border_insets_ = insets; 132 } inside_border_insets()133 const gfx::Insets& inside_border_insets() const { 134 return inside_border_insets_; 135 } 136 set_minimum_cross_axis_size(int size)137 void set_minimum_cross_axis_size(int size) { 138 minimum_cross_axis_size_ = size; 139 } minimum_cross_axis_size()140 int minimum_cross_axis_size() const { return minimum_cross_axis_size_; } 141 set_between_child_spacing(int spacing)142 void set_between_child_spacing(int spacing) { 143 between_child_spacing_ = spacing; 144 } between_child_spacing()145 int between_child_spacing() const { return between_child_spacing_; } 146 147 void SetCollapseMarginsSpacing(bool collapse_margins_spacing); 148 bool GetCollapseMarginsSpacing() const; 149 150 // Sets the flex weight for the given |view|. Using the preferred size as 151 // the basis, free space along the main axis is distributed to views in the 152 // ratio of their flex weights. Similarly, if the views will overflow the 153 // parent, space is subtracted in these ratios. 154 // If true is passed in for |use_min_size|, the given view's minimum size 155 // is then obtained from calling View::GetMinimumSize(). This will be the 156 // minimum allowed size for the view along the main axis. False 157 // for |use_min_size| (the default) will allow the |view| to be resized to a 158 // minimum size of 0. 159 // 160 // A flex of 0 means this view is not resized. Flex values must not be 161 // negative. 162 void SetFlexForView(const View* view, int flex, bool use_min_size = false); 163 164 // Clears the flex for the given |view|, causing it to use the default 165 // flex. 166 void ClearFlexForView(const View* view); 167 168 // Sets the flex for views to use when none is specified. 169 void SetDefaultFlex(int default_flex); 170 int GetDefaultFlex() const; 171 172 // Overridden from views::LayoutManager: 173 void Installed(View* host) override; 174 void ViewRemoved(View* host, View* view) override; 175 void Layout(View* host) override; 176 gfx::Size GetPreferredSize(const View* host) const override; 177 int GetPreferredHeightForWidth(const View* host, int width) const override; 178 179 private: 180 // This struct is used internally to "wrap" a child view in order to obviate 181 // the need for the main layout logic to be fully aware of the per-view 182 // margins when |collapse_margin_spacing_| is false. Since each view is a 183 // rectangle of a certain size, this wrapper, coupled with any margins set 184 // will increase the apparent size of the view along the main axis. All 185 // necessary view size/position methods required for the layout logic add or 186 // subtract the margins where appropriate to ensure the actual visible size of 187 // the view doesn't include the margins. For the cross axis, the margins are 188 // NOT included in the size/position calculations. BoxLayout will adjust the 189 // bounding rectangle of the space used for layout using the maximum margin 190 // for all views along the appropriate edge. 191 // When |collapse_margin_spacing_| is true, this wrapper provides quick access 192 // to the view's margins for use by the layout to collapse adjacent spacing 193 // to the largest of the several values. 194 class ViewWrapper { 195 public: 196 ViewWrapper(); 197 ViewWrapper(const BoxLayout* layout, View* view); 198 ~ViewWrapper(); 199 200 int GetHeightForWidth(int width) const; margins()201 const gfx::Insets& margins() const { return margins_; } 202 gfx::Size GetPreferredSize() const; 203 void SetBoundsRect(const gfx::Rect& bounds); view()204 View* view() const { return view_; } 205 bool visible() const; 206 207 private: 208 View* view_ = nullptr; 209 const BoxLayout* layout_ = nullptr; 210 gfx::Insets margins_; 211 212 DISALLOW_COPY_AND_ASSIGN(ViewWrapper); 213 }; 214 215 struct Flex { 216 int flex_weight; 217 bool use_min_size; 218 }; 219 220 using FlexMap = std::map<const View*, Flex>; 221 222 // Returns the flex for the specified |view|. 223 int GetFlexForView(const View* view) const; 224 225 // Returns the minimum size for the specified |view|. 226 int GetMinimumSizeForView(const View* view) const; 227 228 // Returns the size and position along the main axis of |rect|. 229 int MainAxisSize(const gfx::Rect& rect) const; 230 int MainAxisPosition(const gfx::Rect& rect) const; 231 232 // Sets the size and position along the main axis of |rect|. 233 void SetMainAxisSize(int size, gfx::Rect* rect) const; 234 void SetMainAxisPosition(int position, gfx::Rect* rect) const; 235 236 // Returns the size and position along the cross axis of |rect|. 237 int CrossAxisSize(const gfx::Rect& rect) const; 238 int CrossAxisPosition(const gfx::Rect& rect) const; 239 240 // Sets the size and position along the cross axis of |rect|. 241 void SetCrossAxisSize(int size, gfx::Rect* rect) const; 242 void SetCrossAxisPosition(int size, gfx::Rect* rect) const; 243 244 // Returns the main axis size for the given view. |child_area_width| is needed 245 // to calculate the height of the view when the orientation is vertical. 246 int MainAxisSizeForView(const ViewWrapper& view, int child_area_width) const; 247 248 // Returns the |left| or |top| edge of the given inset based on the value of 249 // |orientation_|. 250 int MainAxisLeadingInset(const gfx::Insets& insets) const; 251 252 // Returns the |right| or |bottom| edge of the given inset based on the value 253 // of |orientation_|. 254 int MainAxisTrailingInset(const gfx::Insets& insets) const; 255 256 // Returns the left (|x|) or top (|y|) edge of the given rect based on the 257 // value of |orientation_|. 258 int CrossAxisLeadingEdge(const gfx::Rect& rect) const; 259 260 // Returns the |left| or |top| edge of the given inset based on the value of 261 // |orientation_|. 262 int CrossAxisLeadingInset(const gfx::Insets& insets) const; 263 264 // Returns the |right| or |bottom| edge of the given inset based on the value 265 // of |orientation_|. 266 int CrossAxisTrailingInset(const gfx::Insets& insets) const; 267 268 // Returns the main axis margin spacing between the two views which is the max 269 // of the right margin from the |left| view, the left margin of the |right| 270 // view and |between_child_spacing_|. 271 int MainAxisMarginBetweenViews(const ViewWrapper& left, 272 const ViewWrapper& right) const; 273 274 // Returns the outer margin along the main axis as insets. 275 gfx::Insets MainAxisOuterMargin() const; 276 277 // Returns the maximum margin along the cross axis from all views as insets. 278 gfx::Insets CrossAxisMaxViewMargin() const; 279 280 // Adjusts the main axis for |rect| by collapsing the left or top margin of 281 // the first view with corresponding side of |inside_border_insets_| and the 282 // right or bottom margin of the last view with the corresponding side of 283 // |inside_border_insets_|. 284 void AdjustMainAxisForMargin(gfx::Rect* rect) const; 285 286 // Adjust the cross axis for |rect| using the appropriate sides of 287 // |inside_border_insets_|. 288 void AdjustCrossAxisForInsets(gfx::Rect* rect) const; 289 290 // Returns the cross axis size for the given view. 291 int CrossAxisSizeForView(const ViewWrapper& view) const; 292 293 // Returns the total margin width for the given view or 0 when 294 // collapse_margins_spacing_ is true. 295 int CrossAxisMarginSizeForView(const ViewWrapper& view) const; 296 297 // Returns the Top or Left size of the margin for the given view or 0 when 298 // collapse_margins_spacing_ is true. 299 int CrossAxisLeadingMarginForView(const ViewWrapper& view) const; 300 301 // Adjust the cross axis for |rect| using the given leading and trailing 302 // values. 303 void InsetCrossAxis(gfx::Rect* rect, int leading, int trailing) const; 304 305 // The preferred size for the dialog given the width of the child area. 306 gfx::Size GetPreferredSizeForChildWidth(const View* host, 307 int child_area_width) const; 308 309 // The amount of space the layout requires in addition to any space for the 310 // child views. 311 gfx::Size NonChildSize(const View* host) const; 312 313 // The next visible view at or after pos. If no other views are visible, 314 // returns null. 315 View* NextVisibleView(View::Views::const_iterator pos) const; 316 317 // Return the first visible view in the host or nullptr if none are visible. 318 View* FirstVisibleView() const; 319 320 // Return the last visible view in the host or nullptr if none are visible. 321 View* LastVisibleView() const; 322 323 Orientation orientation_; 324 325 // Spacing between child views and host view border. 326 gfx::Insets inside_border_insets_; 327 328 // Spacing to put in between child views. 329 int between_child_spacing_; 330 331 // The alignment of children in the main axis. This is 332 // MainAxisAlignment::kStart by default. 333 MainAxisAlignment main_axis_alignment_ = MainAxisAlignment::kStart; 334 335 // The alignment of children in the cross axis. This is 336 // kStretch by default. 337 CrossAxisAlignment cross_axis_alignment_ = CrossAxisAlignment::kStretch; 338 339 // A map of views to their flex weights. 340 FlexMap flex_map_; 341 342 // The flex weight for views if none is set. Defaults to 0. 343 int default_flex_ = 0; 344 345 // The minimum cross axis size for the layout. 346 int minimum_cross_axis_size_ = 0; 347 348 // Adjacent view margins and spacing should be collapsed. 349 bool collapse_margins_spacing_; 350 351 // The view that this BoxLayout is managing the layout for. 352 views::View* host_ = nullptr; 353 }; 354 355 } // namespace views 356 357 #endif // UI_VIEWS_LAYOUT_BOX_LAYOUT_H_ 358