1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
4  *
5  * See the LICENSE file for terms of use.
6  */
7 #ifndef WGRID_LAYOUT_H_
8 #define WGRID_LAYOUT_H_
9 
10 #include <vector>
11 #include <Wt/WLayout.h>
12 #include <Wt/WLength.h>
13 #include <Wt/WWidget.h>
14 
15 namespace Wt {
16 
17   namespace Impl {
18 
19 struct Grid {
20   int horizontalSpacing_, verticalSpacing_;
21 
22   struct Section {
23     int stretch_;
24     bool resizable_;
25     WLength initialSize_;
26 
27     Section(int stretch = 0);
28   };
29 
30   struct Item {
31     std::unique_ptr<WLayoutItem> item_;
32     int rowSpan_;
33     int colSpan_;
34     bool update_;
35     WFlags<AlignmentFlag> alignment_;
36 
37     Item(std::unique_ptr<WLayoutItem> item = nullptr,
38 	 WFlags<AlignmentFlag> alignment = None);
39     Item(Item&& other) = default;
40     Item& operator=(Item&& other) = default;
41     ~Item();
42   };
43 
44   std::vector<Section> rows_;
45   std::vector<Section> columns_;
46   std::vector<std::vector<Item> > items_; // [row][column]
47 
48   Grid();
49   ~Grid();
50 
51   void clear();
52 };
53 
54   }
55 
56 /*! \class WGridLayout Wt/WGridLayout.h Wt/WGridLayout.h
57  *  \brief A layout manager which arranges widgets in a grid
58  *
59  * This layout manager arranges widgets in a grid.
60  *
61  * Each grid cell (row, column) may contain one widget or nested
62  * layout. Orientation::Horizontal and vertical space are divided so that each
63  * non-stretchable column/row is given its preferred size (if
64  * possible) and the remaining space is divided according to stretch
65  * factors among the columns/rows. If not all columns/rows can be
66  * given their preferred size (there is not enough room), then
67  * columns/rows are given a smaller size (down to a minimum size based
68  * on widget minimum sizes). If necessary, the container (or parent
69  * layout) of this layout is resized to meet minimum size
70  * requirements.
71  *
72  * The preferred width/height of a column/row is based on the natural
73  * size of the widgets, where they present their contents without
74  * overflowing. WWidget::resize() or (CSS <tt>width</tt>,
75  * <tt>height</tt> properties) can be used to adjust the preferred
76  * size of a widget.
77  *
78  * The minimum width/height of a column/row is based on the minimum
79  * dimensions of contained widgets or nested layouts. The default
80  * minimum height and width for a widget is 0. It can be specified
81  * using WWidget::setMinimumSize() or using CSS <tt>min-width</tt> and
82  * <tt>min-height</tt> properties.
83  *
84  * You should use \link WContainerWidget::setOverflow()
85  * WContainerWidget::setOverflow(OverflowAuto) \endlink to automatically
86  * show scrollbars for widgets inserted
87  * in the layout to cope with a size set by the layout manager that is
88  * smaller than the preferred size.
89  *
90  * When the container of a layout manager has a maximum size set using
91  * WWidget::setMaximumSize(), then the size of the container will be
92  * based on the preferred size of the contents, up to this maximum
93  * size, instead of the default behaviour of constraining the size of
94  * the children based on the size of the container.
95  *
96  * A layout manager may provide resize handles between columns or rows
97  * which allow the user to change the automatic layout provided by the
98  * layout manager (see setRowResizable() and
99  * setColumnResizable()).
100  *
101  * Columns and rows are separated using a constant spacing, which
102  * defaults to 6 pixels by default, and can be changed using
103  * setHorizontalSpacing() and setVerticalSpacing(). In addition, when
104  * this layout is a top-level layout (i.e. is not nested inside
105  * another layout), a margin is set around the contents. This margin
106  * defaults to 9 pixels, and can be changed using setContentsMargins().
107  *
108  * For each column or row, a stretch factor may be defined, which
109  * controls how remaining horizontal or vertical space is used. Each
110  * column and row is stretched using the stretch factor to fill the
111  * remaining space. When the stretch factor is 0, the height of the
112  * row and its contents is set to the preferred size (if
113  * possible). When the stretch factor is 1 or higher, these widgets
114  * will be given the remaining size, limited only by their minimum
115  * size (their preferred size is ignored).
116  *
117  * Usage example:
118  * \if cpp
119  * \code
120  * Wt::WContainerWidget *w = addWidget(std::make_unique<Wt::WContainerWidget>());
121  * w->resize(WLength::Auto, 600);
122  *
123  * auto layout = std::make_unique<Wt::WGridLayout>();
124  * layout->addWidget(std::make_unique<Wt::WText>("Item 0 0"), 0, 0);
125  * layout->addWidget(std::make_unique<Wt::WText>("Item 0 1"), 0, 1);
126  * layout->addWidget(std::make_unique<Wt::WText>("Item 1 0"), 1, 0);
127  * layout->addWidget(std::make_unique<Wt::WText>("Item 1 1"), 1, 1);
128  *
129  * w->setLayout(std::move(layout));
130  * \endcode
131  * \elseif java
132  * \code
133  * WContainerWidget w = new WContainerWidget(this);
134  * w.resize(WLength.Auto, new WLength(600));
135  *
136  * WGridLayout layout = new WGridLayout();
137  * layout.addWidget(new WText("Item 0 0"), 0, 0);
138  * layout.addWidget(new WText("Item 0 1"), 0, 1);
139  * layout.addWidget(new WText("Item 1 0"), 1, 0);
140  * layout.addWidget(new WText("Item 1 1"), 1, 1);
141  *
142  * w.setLayout(layout);
143  * \endcode
144  * \endif
145  *
146  * \note When JavaScript support is not available, not all functionality
147  * of the layout is available. In particular, vertical size management is
148  * not available.
149  *
150  * \note When a layout is used on a first page with progressive
151  * bootstrap, then the layout will progress only in a limited way to a
152  * full JavaScript-based layout. You can thus not rely on it to behave
153  * properly for example when dynamically adding or removing widgets.
154  */
155 class WT_API WGridLayout : public WLayout
156 {
157 public:
158   /*! \brief Create a new grid layout.
159    *
160    * The grid will grow dynamically as items are added.
161    *
162    * Use \p parent = \c 0 to create a layout manager that can be
163    * nested inside other layout managers or if you use
164    * WContainerWidget::setLayout() to add specify the container later.
165    */
166   WGridLayout();
167 
168   virtual ~WGridLayout() override;
169 
170   virtual void addItem(std::unique_ptr<WLayoutItem> item) override;
171   virtual std::unique_ptr<WLayoutItem> removeItem(WLayoutItem *item) override;
172   virtual WLayoutItem *itemAt(int index) const override;
173   virtual int count() const override;
174 
175   /*! \brief Adds a layout item to the grid.
176    *
177    * Adds the <i>item</i> at (<i>row</i>, \p column). If an item
178    * was already added to that location, it is replaced.
179    *
180    * An item may span several more rows or columns, which is
181    * controlled by <i>rowSpan</i> and \p columnSpan.
182    *
183    * The \p alignment specifies the vertical and horizontal
184    * alignment of the item. The default value 0 indicates that the
185    * item is stretched to fill the entire grid cell. The alignment can
186    * be specified as a logical combination of a horizontal alignment
187    * (Wt::AlignmentFlag::Left, Wt::AlignmentFlag::Center, or Wt::AlignmentFlag::Right) and a
188    * vertical alignment (Wt::AlignmentFlag::Top, Wt::AlignmentFlag::Middle, or
189    * Wt::AlignmentFlag::Bottom).
190    *
191    * \sa addLayout(), addWidget()
192    */
193   void addItem(std::unique_ptr<WLayoutItem> item, int row, int column,
194 	       int rowSpan = 1, int columnSpan = 1,
195 	       WFlags<AlignmentFlag> alignment = None);
196 
197   /*! \brief Adds a nested layout item to the grid.
198    *
199    * Adds the <i>layout</i> at (<i>row</i>, \p column). If an item
200    * was already added to that location, it is replaced (but not
201    * deleted).
202    *
203    * The \p alignment specifies the vertical and horizontal
204    * alignment of the item. The default value 0 indicates that the
205    * item is stretched to fill the entire grid cell. The alignment can
206    * be specified as a logical combination of a horizontal alignment
207    * (Wt::AlignmentFlag::Left, Wt::AlignmentFlag::Center, or Wt::AlignmentFlag::Right) and a
208    * vertical alignment (Wt::AlignmentFlag::Top, Wt::AlignmentFlag::Middle, or
209    * Wt::AlignmentFlag::Bottom).
210    *
211    * \sa addLayout(WLayout *, int, int, int, int, WFlags<AlignmentFlag>)
212    */
213   void addLayout(std::unique_ptr<WLayout> layout, int row, int column,
214 		 WFlags<AlignmentFlag> alignment = None);
215 
216   /*! \brief Adds a nested layout item to the grid.
217    *
218    * Adds the <i>layout</i> at (<i>row</i>, \p column). If an item
219    * was already added to that location, it is replaced (but not
220    * deleted).
221    *
222    * An item may span several more rows or columns, which is
223    * controlled by <i>rowSpan</i> and \p columnSpan.
224    *
225    * The \p alignment specifies the vertical and horizontal
226    * alignment of the item. The default value 0 indicates that the
227    * item is stretched to fill the entire grid cell. The alignment can
228    * be specified as a logical combination of a horizontal alignment
229    * (Wt::AlignmentFlag::Left, Wt::AlignmentFlag::Center, or Wt::AlignmentFlag::Right) and a
230    * vertical alignment (Wt::AlignmentFlag::Top, Wt::AlignmentFlag::Middle, or
231    * Wt::AlignmentFlag::Bottom).
232    *
233    * \sa addLayout(WLayout *, int, int, WFlags<AlignmentFlag>)
234    */
235   void addLayout(std::unique_ptr<WLayout> layout, int row, int column,
236 		 int rowSpan, int columnSpan,
237 		 WFlags<AlignmentFlag> alignment = None);
238 
239   /*! \brief Adds a widget to the grid.
240    *
241    * Adds the <i>widget</i> at (<i>row</i>, \p column). If an item
242    * was already added to that location, it is replaced (but not
243    * deleted).
244    *
245    * The \p alignment specifies the vertical and horizontal
246    * alignment of the item. The default value 0 indicates that the
247    * item is stretched to fill the entire grid cell. The alignment can
248    * be specified as a logical combination of a horizontal alignment
249    * (Wt::AlignmentFlag::Left, Wt::AlignmentFlag::Center, or Wt::AlignmentFlag::Right) and a
250    * vertical alignment (Wt::AlignmentFlag::Top, Wt::AlignmentFlag::Middle, or
251    * Wt::AlignmentFlag::Bottom).
252    *
253    * \sa addWidget(WWidget *, int, int, int, int, WFlags<AlignmentFlag>)
254    */
255 #ifndef WT_TARGET_JAVA
256   void addWidget(std::unique_ptr<WWidget> widget, int row, int column,
257                  WFlags<AlignmentFlag> alignment);
258 #else // WT_TARGET_JAVA
259   void addWidget(std::unique_ptr<WWidget> widget, int row, int column,
260                  WFlags<AlignmentFlag> alignment = None);
261 #endif // WT_TARGET_JAVA
262 
263 #ifndef WT_TARGET_JAVA
264   /*! \brief Adds a widget to the grid, returning a raw pointer to the widget.
265    *
266    * This is implemented as:
267    *
268    * \code
269    * Widget *result = widget.get();
270    * addWidget(std::unique_ptr<WWidget>(std::move(widget)), row, column, alignment);
271    * return result;
272    * \endcode
273    */
274   template <typename Widget>
275     Widget *addWidget(std::unique_ptr<Widget> widget, int row, int column,
276                       WFlags<AlignmentFlag> alignment = None)
277   {
278     Widget *result = widget.get();
279     addWidget(std::unique_ptr<WWidget>(std::move(widget)), row, column, alignment);
280     return result;
281   }
282 #endif // WT_TARGET_JAVA
283 
284   /*! \brief Adds a widget to the grid.
285    *
286    * Adds the <i>widget</i> at (<i>row</i>, \p column). If an item
287    * was already added to that location, it is replaced (but not
288    * deleted).
289    *
290    * The widget may span several more rows or columns, which is
291    * controlled by <i>rowSpan</i> and \p columnSpan.
292    *
293    * The \p alignment specifies the vertical and horizontal
294    * alignment of the item. The default value 0 indicates that the
295    * item is stretched to fill the entire grid cell. The alignment can
296    * be specified as a logical combination of a horizontal alignment
297    * (Wt::AlignmentFlag::Left, Wt::AlignmentFlag::Center, or Wt::AlignmentFlag::Right) and a
298    * vertical alignment (Wt::AlignmentFlag::Top, Wt::AlignmentFlag::Middle, or
299    * Wt::AlignmentFlag::Bottom).
300    *
301    * \sa addWidget(WWidget *, int, int, WFlags<AlignmentFlag>)
302    */
303 #ifndef WT_TARGET_JAVA
304   void addWidget(std::unique_ptr<WWidget> widget, int row, int column,
305 		 int rowSpan, int columnSpan,
306 		 WFlags<AlignmentFlag> alignment);
307 #else // WT_TARGET_JAVA
308   void addWidget(std::unique_ptr<WWidget> widget, int row, int column,
309 		 int rowSpan, int columnSpan,
310 		 WFlags<AlignmentFlag> alignment = None);
311 #endif // WT_TARGET_JAVA
312 
313 #ifndef WT_TARGET_JAVA
314   /*! \brief Adds a widget to the grid, returning a raw pointer to the widget.
315    *
316    * This is implemented as:
317    *
318    * \code
319    * Widget *result = widget.get();
320    * addWidget(std::unique_ptr<WWidget>(std::move(widget)), row, column,
321    *           rowSpan, columnSpan, alignment);
322    * return result;
323    * \endcode
324    */
325   template <typename Widget>
326     Widget *addWidget(std::unique_ptr<Widget> widget, int row, int column,
327                       int rowSpan, int columnSpan,
328                       WFlags<AlignmentFlag> alignment = None)
329   {
330     Widget *result = widget.get();
331     addWidget(std::unique_ptr<WWidget>(std::move(widget)), row, column,
332               rowSpan, columnSpan, alignment);
333     return result;
334   }
335 #endif // WT_TARGET_JAVA
336 
337   /*! \brief Sets the horizontal spacing.
338    *
339    * The default horizontal spacing is 9 pixels.
340    *
341    * \sa setVerticalSpacing(int)
342    */
343   void setHorizontalSpacing(int size);
344 
345   /*! \brief Returns the horizontal spacing.
346    *
347    * \sa setHorizontalSpacing(int)
348    */
horizontalSpacing()349   int horizontalSpacing() const { return grid_.horizontalSpacing_; }
350 
351   /*! \brief Sets the vertical spacing.
352    *
353    * The default vertical spacing is 9 pixels.
354    *
355    * \sa setHorizontalSpacing(int)
356    */
357   void setVerticalSpacing(int size);
358 
359   /*! \brief Returns the vertical spacing.
360    *
361    * \sa setVerticalSpacing(int)
362    */
verticalSpacing()363   int verticalSpacing() const { return grid_.verticalSpacing_; }
364 
365   /*! \brief Returns the column count.
366    *
367    * The grid dimensions change dynamically when adding contents to
368    * the grid.
369    *
370    * \sa rowCount()
371    */
372   int columnCount() const;
373 
374   /*! \brief Returns the row count.
375    *
376    * The grid dimensions change dynamically when adding contents to
377    * the grid.
378    *
379    * \sa columnCount()
380    */
381   int rowCount() const;
382 
383   /*! \brief Sets the column stretch.
384    *
385    * Sets the <i>stretch</i> factor for column \p column.
386    *
387    * \sa columnStretch()
388    */
389   void setColumnStretch(int column, int stretch);
390 
391   /*! \brief Returns the column stretch.
392    *
393    * \sa setColumnStretch(int, int)
394    */
395   int columnStretch(int column) const;
396 
397   /*! \brief Sets the row stretch.
398    *
399    * Sets the <i>stretch</i> factor for row \p row.
400    *
401    * \sa rowStretch()
402    */
403   void setRowStretch(int row, int stretch);
404 
405   /*! \brief Returns the column stretch.
406    *
407    * \sa setRowStretch(int, int)
408    */
409   int rowStretch(int row) const;
410 
411   /*! \brief Sets whether the user may drag a particular column border.
412    *
413    * This method sets whether the border that separates column
414    * <i>column</i> from the next column may be resized by the user,
415    * depending on the value of <i>enabled</i>.
416    *
417    * The default value is <i>false</i>.
418    *
419    * If an \p initialSize is given (that is not WLength::Auto), then
420    * this size is used for the width of the column, overriding the width
421    * it would be given by the layout manager.
422    */
423   void setColumnResizable(int column, bool enabled = true,
424 			  const WLength& initialSize = WLength::Auto);
425 
426   /*! \brief Returns whether the user may drag a particular column border.
427    *
428    * This method returns whether the border that separates column
429    * <i>column</i> from the next column may be resized by the user.
430    *
431    * \sa setColumnResizable()
432    */
433   bool columnIsResizable(int column) const;
434 
435   /*! \brief Sets whether the user may drag a particular row border.
436    *
437    * This method sets whether the border that separates row <i>row</i> from
438    * the next row may be resized by the user, depending on the value of
439    * <i>enabled</i>.
440    *
441    * The default value is <i>false</i>.
442    *
443    * If an \p initialSize is given (that is not WLength::Auto), then
444    * this size is used for the height of the row, overriding the height
445    * it would be given by the layout manager.
446    */
447   void setRowResizable(int row, bool enabled = true,
448 		       const WLength& initialSize = WLength::Auto);
449 
450   /*! \brief Returns whether the user may drag a particular row border.
451    *
452    * This method returns whether the border that separates row
453    * <i>row</i> from the next row may be resized by the user.
454    *
455    * \sa setRowResizable()
456    */
457   bool rowIsResizable(int row) const;
458 
459   virtual void iterateWidgets(const HandleWidgetMethod& method) const override;
460 
461 private:
462   Impl::Grid grid_;
463 
464   void expand(int row, int column, int rowSpan, int columnSpan);
465 
466   virtual void setParentWidget(WWidget *parent) override;
467 };
468 
469 }
470 
471 #endif // WGRID_LAYOUT_H_
472