1 // -*- C++ -*- 2 /* GG is a GUI for OpenGL. 3 Copyright (C) 2003-2008 T. Zachary Laine 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public License 7 as published by the Free Software Foundation; either version 2.1 8 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free 17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 18 02111-1307 USA 19 20 If you do not wish to comply with the terms of the LGPL please 21 contact the author as other terms are available for a fee. 22 23 Zach Laine 24 whatwasthataddress@gmail.com */ 25 26 /** \file Layout.h \brief Contains the Layout class, which is used to size and 27 align GG windows. */ 28 29 #ifndef _GG_Layout_h_ 30 #define _GG_Layout_h_ 31 32 #include <GG/AlignmentFlags.h> 33 #include <GG/Wnd.h> 34 35 36 namespace GG { 37 38 /** \brief An invisible Wnd subclass that arranges its child Wnds. 39 40 A Layout consists of a grid of cells. A cell may have at most one Wnd 41 covering it, but need not contain a Wnd at all. A Wnd may cover any 42 rectangular region of cells, though they will commonly only cover one. 43 The cells are arranged into rows and columns. Most attributes of the 44 layout are set for an entire row or column, but alignment is set for each 45 child in the layout separately. Rows and columns have two attributes: 46 "stretch", and "min" (minimum row width/minimum column height). Stretch 47 indicates a propotional factor by which each row/column is stretched when 48 the layout is resized. For example, if the sum of the row stretch factors 49 is 5, a row with a stretch of 2 will gain 2/5 of the increased space if 50 the layout grows vertically, or lose 2/5 of the decreased space if the 51 layout shrinks vertically. Note that this means that rows with a stretch 52 of 0 will not change size at all. The exception to this is when all rows 53 have a stretch of 0, in which case all the rows grow and shrink evenly. 54 Obviously, this applies to columns as well. The min sets a lower bound on 55 the height of a row or the width of a column. By default, no alignment 56 value is set for a child in the layout. If one is set, the child is not 57 grown and shrunk when the layout is resized, if this is possible. Aligned 58 children just sit there in the place they are aligned to. If the layout 59 becomes too small, aligned windows will be shrunk as necessary and if 60 possible. Note that the MinSize() and MaxSize() of a child will affect 61 how much it can be stretched when the layout is resized. 62 63 <p>Layouts are best used to arrange the children of another window, such 64 as arranging the controls of a dialog box. When used this way, the Layout 65 becomes the sole child of its parent, and contains the parent's children 66 as its own. This scheme allows Layouts to be easily nested, since all 67 Layouts are Wnd-derived. Like a Control, a Layout will forward all 68 MouseWheel(), Key*(), and dragged-child notification calls to its parent. 69 Clicks fall through as well, since Layouts are not constructed with the 70 Wnd::INTERACTIVE flag. 71 72 <p>There are two attributes that affect the spacing of all the layout's 73 child windows: border margin and cell margin. Border margin is the space 74 left around the entire layout, between the outer edges of the children in 75 the layout and the layout's outer edges. Cell margin is the space left 76 between individual Wnds in the layout, but does not add to the margin 77 around the outside of the layout. 78 79 <p>A note about how layout minimum sizes are determined: <br>The border 80 margin adds to the minimum size of the layout. Further, the cell margin 81 will have an effect on the minimum size of a cell, even an empty one, if 82 it is \a greater than the row or column minimum for that cell. So an 83 empty layout with 5 columns, a border margin of 3, and a cell margin of 2 84 will have a minumum width of 14. This is determined as follows: 5 columns 85 means 4 cell margins between the columns, so 4 * 2 = 8. The border margin 86 is added to both sides, which means the total minimum width is 8 + 2 * 3 = 87 14. Also, the minimum size of each child in the layout will affect the 88 minimum sizes of the rows and columns it covers. If a child covers more 89 than one row/column, its minimum size is distributed over the rows/columns 90 it covers, proportional to the stretch factor for each row/column. 91 Finally, the min values and stretch factors must both be satisfied 92 simultaneously. For example, if the row mins are set to be [1 2 3] and 93 the row stretch factors are set to be [1 2 3], the minimum width will be 6 94 (neglecting the margins). However, if the mins were instead set to be [4 95 2 3], the stretch factors would lead to effective minimums of [4 8 12] to 96 maintain proportionality, making the minimum width 24. 97 98 \see The Wnd documentation has further information about layouts attached 99 to Wnds. 100 */ 101 class GG_API Layout : public Wnd 102 { 103 public: 104 /** \name Structors */ ///@{ 105 /** Ctor. */ 106 Layout(X x, Y y, X w, Y h, std::size_t rows, std::size_t columns, 107 unsigned int border_margin = 0, unsigned int cell_margin = INVALID_CELL_MARGIN); 108 //@} 109 110 /** \name Accessors */ ///@{ 111 Pt MinUsableSize() const override; 112 113 std::size_t Rows() const; ///< returns the number of rows in the layout 114 std::size_t Columns() const; ///< returns the number of columns in the layout 115 Flags<Alignment> ChildAlignment(const Wnd* wnd) const; ///< returns the aligment of child \a wnd. \throw GG::Layout::NoSuchChild Throws if no such child exists. 116 unsigned int BorderMargin() const; ///< returns the number of pixels that the layout will leave between its edges and the windows it contains 117 unsigned int CellMargin() const; ///< returns the number of pixels the layout leaves between the edges of windows in adjacent cells 118 double RowStretch(std::size_t row) const; ///< returns the stretch factor for row \a row. Note that \a row is not range-checked. 119 double ColumnStretch(std::size_t column) const; ///< returns the stretch factor for column \a column. Note that \a column is not range-checked. 120 Y MinimumRowHeight(std::size_t row) const; ///< returns the minimum height allowed for row \a row. Note that \a row is not range-checked. 121 X MinimumColumnWidth(std::size_t column) const; ///< returns the minimum height allowed for column \a column. Note that \a column is not range-checked. 122 std::vector<std::vector<const Wnd*>> 123 Cells() const; ///< returns a matrix of the Wnds that can be found in each cell 124 std::vector<std::vector<Rect>> 125 CellRects() const; ///< returns a matrix of rectangles in screen space that cover the cells in which child Wnds are placed 126 std::vector<std::vector<Rect>> 127 RelativeCellRects() const; ///< returns a matrix of rectangles in layout client space that cover the cells in which child Wnds are placed 128 129 /** Returns true iff this layout will render an outline of itself; this is 130 sometimes useful for debugging purposes */ 131 bool RenderOutline() const; 132 133 /** Returns the outline color used to render this layout (this is only 134 used if RenderOutline() returns true). This is sometimes useful for 135 debugging purposes. */ 136 Clr OutlineColor() const; 137 //@} 138 139 /** \name Mutators */ ///@{ 140 void StartingChildDragDrop(const Wnd* wnd, const Pt& offset) override; 141 void CancellingChildDragDrop(const std::vector<const Wnd*>& wnds) override; 142 void ChildrenDraggedAway(const std::vector<Wnd*>& wnds, const Wnd* destination) override; 143 void SizeMove(const Pt& ul, const Pt& lr) override; 144 void Render() override; 145 146 /** Inserts \a w into the layout in the indicated cell, expanding the 147 layout grid as necessary. \throw GG::Layout::AttemptedOverwrite 148 Throws if there is already a Wnd in the given cell. */ 149 void Add(std::shared_ptr<Wnd> wnd, std::size_t row, std::size_t column, Flags<Alignment> alignment = ALIGN_NONE); 150 151 /** Inserts \a w into the layout, covering the indicated cell(s), 152 expanding the layout grid as necessary. The num_rows and num_columns 153 indicate how many rows and columns \a w covers, respectively. So 154 Add(foo, 1, 2, 2, 3) covers cells (1, 2) through (2, 4), inclusive. 155 Note that \a num_rows and \a num_columns must be positive, though this 156 is not checked. \throw GG::Layout::AttemptedOverwrite Throws if there 157 is already a Wnd in one of the given cells. */ 158 void Add(std::shared_ptr<Wnd> wnd, std::size_t row, std::size_t column, std::size_t num_rows, std::size_t num_columns, Flags<Alignment> alignment = ALIGN_NONE); 159 160 /** Removes \a w from the layout, recalculating the layout as needed. 161 Note that this causes the layout to relinquish responsibility for \a 162 wnd's memory management. */ 163 void Remove(Wnd* wnd); 164 165 /** Resets children to their original sizes and detaches them, so that a 166 removed Layout can leave the Wnds it lays out in their original 167 configuration when it is no longer useful. */ 168 void DetachAndResetChildren(); 169 170 /** Resizes the layout to be \a rows by \a columns. If the layout 171 shrinks, any contained windows are deleted. Each of \a rows and \a 172 columns must be greater than 0, though this is not checked. */ 173 void ResizeLayout(std::size_t rows, std::size_t columns); 174 175 /** Sets the aligment of child \a wnd to \a alignment. If no such child 176 exists, no action is taken. */ 177 void SetChildAlignment(const Wnd* wnd, Flags<Alignment> alignment); 178 179 /** Sets the number of pixels that the layout will leave between its edges 180 and the windows it contains */ 181 void SetBorderMargin(unsigned int margin); 182 183 /** Sets the number of pixels the layout leaves between the edges of 184 windows in adjacent cells */ 185 void SetCellMargin(unsigned int margin); 186 187 /** Sets the amount of stretching, relative to other rows, that \a row 188 will do when the layout is resized. 0.0 indicates that the row's size 189 will not change unless all rows have 0.0 stretch as well. Note that 190 \a row is not range-checked. */ 191 void SetRowStretch(std::size_t row, double stretch); 192 193 /** Sets the amount of stretching, relative to other columns, that \a 194 column will do when the layout is resized. 0.0 indicates that the 195 column's size will not change unless all columns have 0.0 stretch as 196 well. Note that \a column is not range-checked. */ 197 void SetColumnStretch(std::size_t column, double stretch); 198 199 /** Sets the minimum height of row \a row to \a height. Note that \a row 200 is not range-checked. */ 201 void SetMinimumRowHeight(std::size_t row, Y height); 202 203 /** Sets the minimum width of column \a column to \a width. Note that \a 204 column is not range-checked. */ 205 void SetMinimumColumnWidth(std::size_t column, X width); 206 207 /** Set this to true if this layout should render an outline of itself; 208 this is sometimes useful for debugging purposes */ 209 void RenderOutline(bool render_outline); 210 211 /** Sets the outline color used to render this layout (this is only used 212 if RenderOutline() returns true). This is sometimes useful for 213 debugging purposes. */ 214 void SetOutlineColor(Clr color); 215 //@} 216 217 /** \name Exceptions */ ///@{ 218 /** The base class for Layout exceptions. */ 219 GG_ABSTRACT_EXCEPTION(Exception); 220 221 /** Thrown when a negative margin is provided. */ 222 GG_CONCRETE_EXCEPTION(InvalidMargin, GG::Layout, Exception); 223 224 /** Thrown when a property of a nonexistent child is requested. */ 225 GG_CONCRETE_EXCEPTION(NoSuchChild, GG::Layout, Exception); 226 227 /** Thrown when an internal check of calculations made by the layout 228 algorithm fails. */ 229 GG_CONCRETE_EXCEPTION(FailedCalculationCheck, GG::Layout, Exception); 230 231 /** Thrown when an attempt is made to place a Wnd in a nonempty layout 232 cell. */ 233 GG_CONCRETE_EXCEPTION(AttemptedOverwrite, GG::Layout, Exception); 234 //@} 235 236 static const unsigned int INVALID_CELL_MARGIN; 237 238 protected: 239 /** \name Mutators */ ///@{ 240 void MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys) override; 241 void KeyPress(Key key, std::uint32_t key_code_point, Flags<ModKey> mod_keys) override; 242 void KeyRelease(Key key, std::uint32_t key_code_point, Flags<ModKey> mod_keys) override; 243 244 virtual void DoLayout(Pt ul, Pt lr); 245 246 /** Redo the layout. This is called internally when something changes and 247 it needs to redo the layout. 248 249 Bug: This does nothing if the size has not changed. Fixing it to use 250 call DoLayout() even when the size has not changed breaks all text boxes. 251 */ 252 virtual void RedoLayout(); 253 //@} 254 255 private: 256 struct GG_API RowColParams 257 { 258 RowColParams(); 259 260 double stretch; 261 unsigned int min; 262 unsigned int effective_min; ///< current effective minimum size of this row or column, based on min, layout margins, and layout cell contents 263 int current_origin; ///< current position of top or left side 264 unsigned int current_width; ///< current extent in downward or rightward direction 265 }; 266 267 struct GG_API WndPosition 268 { 269 WndPosition(); 270 WndPosition(std::size_t first_row_, std::size_t first_column_, 271 std::size_t last_row_, std::size_t last_column_, 272 Flags<Alignment> alignment_, const Pt& original_ul_, const Pt& original_size_); 273 274 std::size_t first_row; 275 std::size_t first_column; 276 std::size_t last_row; 277 std::size_t last_column; 278 Flags<Alignment> alignment; 279 Pt original_ul; 280 Pt original_size; 281 }; 282 283 double TotalStretch(const std::vector<RowColParams>& params_vec) const; 284 X TotalMinWidth() const; 285 Y TotalMinHeight() const; 286 void ValidateAlignment(Flags<Alignment>& alignment); 287 void ChildSizeOrMinSizeChanged(); 288 289 std::vector<std::vector<std::weak_ptr<Wnd>>> m_cells; 290 unsigned int m_border_margin; 291 unsigned int m_cell_margin; 292 std::vector<RowColParams> m_row_params; 293 std::vector<RowColParams> m_column_params; 294 std::map<Wnd*, WndPosition> m_wnd_positions; 295 Pt m_min_usable_size; 296 bool m_ignore_child_resize; 297 bool m_stop_resize_recursion; 298 bool m_render_outline; 299 Clr m_outline_color; 300 301 friend class Wnd; 302 }; 303 304 } // namespace GG 305 306 #endif 307