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