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 ListBox.h \brief Contains the ListBox class, a control that contains
27 rows of other controls, commonly TextControls. */
28
29 #ifndef _GG_ListBox_h_
30 #define _GG_ListBox_h_
31
32
33 #include <GG/AlignmentFlags.h>
34 #include <GG/ClrConstants.h>
35 #include <GG/Control.h>
36 #include <GG/Timer.h>
37
38 #include <boost/optional/optional.hpp>
39
40 #include <memory>
41 #include <set>
42 #include <unordered_set>
43
44
45 namespace GG {
46
47 class Font;
48 class Scroll;
49 class SubTexture;
50 class WndEvent;
51
52 /** Styles for ListBox controls. */
53 GG_FLAG_TYPE(ListBoxStyle);
54 extern GG_API const ListBoxStyle LIST_NONE; ///< Default style selected.
55 extern GG_API const ListBoxStyle LIST_VCENTER; ///< Cells are aligned with the top of the list box control.
56 extern GG_API const ListBoxStyle LIST_TOP; ///< Cells are aligned with the top of the list box control. This is the default.
57 extern GG_API const ListBoxStyle LIST_BOTTOM; ///< Cells are aligned with the bottom of the list box control.
58 extern GG_API const ListBoxStyle LIST_CENTER; ///< Cells are center-aligned.
59 extern GG_API const ListBoxStyle LIST_LEFT; ///< Cells are left-aligned. This is the default.
60 extern GG_API const ListBoxStyle LIST_RIGHT; ///< Cells are right-aligned.
61 extern GG_API const ListBoxStyle LIST_NOSORT; ///< List items are not sorted. Items are sorted by default. When used with drag-and-drop, this style allows arbitrary rearrangement of list elements by dragging.
62 extern GG_API const ListBoxStyle LIST_SORTDESCENDING; ///< Items are sorted based on item text in descending order. Ascending order is the default.
63 extern GG_API const ListBoxStyle LIST_NOSEL; ///< No selection, dragging, or dropping allowed. This makes the list box effectively read-only.
64 extern GG_API const ListBoxStyle LIST_SINGLESEL; ///< Only one item at a time can be selected. By default, multiple items may be selected.
65 extern GG_API const ListBoxStyle LIST_QUICKSEL; ///< Each click toggles an item without affecting any others; ignored when used with LIST_SINGLESEL.
66 extern GG_API const ListBoxStyle LIST_USERDELETE; ///< Allows user to remove selected items by pressing the delete key.
67 extern GG_API const ListBoxStyle LIST_BROWSEUPDATES; ///< Causes a signal to be emitted whenever the mouse moves over ("browses") a row.
68
69
70 /** \brief A flexible control that can contain rows and columns of other
71 controls, even other ListBoxes.
72
73 A ListBox consists of rows of controls, usually text or graphics. Each
74 row represents one item; rows can be added or removed, but not columns or
75 individual controls (though the individual controls can be removed from a
76 row by accessing it directly). Each Row in a ListBox must have the same
77 number of cells and the same cell widths as all the others. If you add a
78 row that has fewer cells than the ListBox you are adding it to, it will be
79 padded with empty cells; likewise, if it has too many cells to fit into
80 the Listbox, it will have cells removed. ListBoxes are designed to be
81 easy to use in common cases, and useful in uncommon cases with only a
82 little work. Adding a row to an empty ListBox will cause the ListBox to
83 take on the number of columns that the row has cells, and each column will
84 have an equal portion of the ListBox's width (any remainder is placed in
85 the last column). This allows you to just add rows to a ListBox without
86 worrying about setting up the ListBox in any way ahead of time. Use
87 LockColWidths() to prevent empty ListBoxes from taking on a new row's
88 number of columns. To create a ListBox with user-defined widths, use the
89 ctor designed for that, or call SetNumCols(), set individual widths with
90 SetColWidth(), and lock the column widths with LockColWidths(). To create a
91 ListBox and manually control the column widths and alignment use
92 ManuallyManageColProps() and set the number of columns with SetNumCols().
93 Use DefineColWidths(), DefineColAlignments() and
94 DefineColStretches() to set widths and alignments from an exemplar row.
95
96 <br>Note that Rows are stored by pointer. If you want to move a Row from
97 one ListBox to another, use GetRow() and Insert().
98
99 <br>Note that drag-and-drop support is a key part of ListBox's
100 functionality. As such, special effort has been made to make its use as
101 natural and flexible as possible. This includes allowing arbitrary
102 reordering of ListBox rows when the LIST_NOSORT is in effect.*/
103 class GG_API ListBox : public Control
104 {
105 public:
106 /** \brief A single item in a listbox.
107
108 A Row is primarily a container for Controls. Each cell in a Row
109 contains pointer to a Control-derived object. As always, each such
110 Control can be connected to arbitrary functionality using signals and
111 slots. During dragging and dropping, the data type associated with a
112 Row (DragDropDataType()) indicates to potential drop targets what type
113 of data the Row represents; the target may accept or decline the drop
114 based on the data type. Rows are stored in ListBoxes by reference,
115 not value; this means that you can subclass from Row to create your
116 own custom Row types. This is the recommended method for associating
117 a row with the non-GUI object that it represents. Note that all
118 subclasses of Row must declare a SortKeyType, if it differs from
119 std::string, and must provide a SortKey() method if it should differ
120 from the default SortKey() that Row provides. Note that SortKey is
121 not virtual; this part of its interface is used for compile-time
122 polymorphism -- whatever sorter is used with a Row subclass must know
123 the most-derived type of the Row subclass. \note The margin, column
124 alignment, and width cell data are included so that each Row has all
125 the necessary information with which to render itself (this is
126 primarily needed to facilitate drag-and-drop); these data are
127 duplicates of the margin, alignment, and column widths data found in
128 the owning ListBox, and may be overwritten by the ListBox at any
129 time. */
130 struct GG_API Row : public Control
131 {
132 /** the type of key used to sort rows */
133 typedef std::string SortKeyType;
134
135 /** \name Structors */ ///@{
136 Row();
137 Row(X w, Y h);
138 virtual ~Row();
139 //@}
140
141 void CompleteConstruction() override;
142
143 /** \name Accessors */ ///@{
144 /** Returns the string by which this row may be sorted. */
145 virtual SortKeyType SortKey(std::size_t column) const;
146 std::size_t size() const; ///< returns the number of Controls in this Row
147 bool empty() const; ///< returns true iff there are 0 Controls in this Row
148
149 /** Returns the Control in the \a nth cell of this Row
150 \throw std::range_error throws when size() <= \a n */
151 virtual Control* at(std::size_t n) const;
152
153 Alignment RowAlignment() const; ///< returns the vertical alignment of this Row
154 Alignment ColAlignment(std::size_t n) const; ///< returns the horizontal alignment of the Control in the \a nth cell of this Row; not range checked
155 X ColWidth(std::size_t n) const; ///< returns the width of the \a nth cell of this Row; not range checked
156 unsigned int Margin() const; ///< returns the amount of space left between the contents of adjacent cells, in pixels
157 /** Return true if the row is normalized. Used by ListBox to track normalization.*/
158 bool IsNormalized() const;
159 //@}
160
161 /** \name Mutators */ ///@{
162 void Render() override;
163
164 void push_back(std::shared_ptr<Control> c); ///< adds a given Control to the end of the Row; this Control becomes property of the Row
165 void clear(); ///< removes and deletes all cells in this Row
166 void resize(std::size_t n); ///< resizes the Row to have \a n cells
167
168 void SetCell(std::size_t n, const std::shared_ptr<Control>& c); ///< sets the Control in the \a nth cell of this Row, deleting any preexisting Control; not range checked
169 Control* RemoveCell(std::size_t n); ///< returns a pointer to the Control in the \a nth cell of this Row, and sets the contents of the cell to 0; not range checked
170 void SetRowAlignment(Alignment align); ///< sets the vertical alignment of this Row
171 void SetColAlignment(std::size_t n, Alignment align); ///< sets the horizontal alignment of the Control in the \a nth cell of this Row; not range checked
172 void SetColWidth(std::size_t n, X width); ///< sets the width of the \a nth cell of this Row; not range checked
173 void SetColAlignments(const std::vector<Alignment>& aligns); ///< sets the horizontal alignment of all the Controls in this Row; not range checked
174 void ClearColAlignments(); ///< Clear the horizontal alignments of the cells in this Row
175 void SetColWidths(const std::vector<X>& widths); ///< sets all the widths of the cells of this Row; not range checked
176 void ClearColWidths(); ///< Clear the minimum widths of the cells of this Row.
177 void SetColStretches(const std::vector<double>& stretches); ///< Set all column stretches.
178 void SetMargin(unsigned int margin); ///< sets the amount of space left between the contents of adjacent cells, in pixels
179 /** Set normalized. Used by ListBox to track normalization.*/
180 void SetNormalized(bool normalized);
181 //@}
182
183 boost::signals2::signal<void(const Pt&, GG::Flags<GG::ModKey>)> RightClickedSignal;
184
185 protected:
186 /** Add elements to m_col_widths, m_col_stretches and m_col_alignments until they reach size nn. */
187 void GrowWidthsStretchesAlignmentsTo(std::size_t nn);
188 void RClick(const Pt& pt, GG::Flags<GG::ModKey> mod) override;
189
190 std::vector<std::shared_ptr<Control>> m_cells; ///< the Controls in this Row (each may be null)
191 Alignment m_row_alignment; ///< row alignment; one of ALIGN_TOP, ALIGN_VCENTER, or ALIGN_BOTTOM
192 std::vector<Alignment> m_col_alignments; ///< column alignments; each is one of ALIGN_TOP, ALIGN_VCENTER, or ALIGN_BOTTOM
193 std::vector<X> m_col_widths; ///< column widths
194 std::vector<double> m_col_stretches; ///< the stretch factor of each column
195 unsigned int m_margin = DEFAULT_MARGIN; ///< the amount of space left between the contents of adjacent cells, in pixels
196 bool m_ignore_adjust_layout = false;
197 bool m_is_normalized = false;
198 };
199
200 typedef std::list<std::shared_ptr<Row>>::iterator iterator;
201 typedef std::list<std::shared_ptr<Row>>::const_iterator const_iterator;
202
203 /** \brief Sorts iterators to ListBox::Row*s from a container of
204 ListBox::Row*s.
205
206 For instance for use in a std::map<> or std::set<> (eg,
207 ListBox::SelectionSet). The iterators must refer to pointers to
208 ListBox::Rows that are laid out vertically (as in a ListBox). This
209 layout is used to define a y-ordering that is used to sort the
210 iterators. */
211 struct GG_API RowPtrIteratorLess
212 {
213 bool operator()(const iterator& lhs, const iterator& rhs) const;
214 };
215
216 struct IteratorHash
217 {
operatorIteratorHash218 std::size_t operator()(const iterator& it) const
219 { return boost::hash<const std::shared_ptr<Row>>()(*it); }
220 };
221
222 typedef std::unordered_set<iterator, IteratorHash> SelectionSet;
223
224 /** \name Signal Types */ ///@{
225 /** emitted when the list box is cleared */
226 typedef boost::signals2::signal<void ()> ClearedRowsSignalType;
227 /** emitted when one or more rows are selected or deselected */
228 typedef boost::signals2::signal<void (const SelectionSet&)> SelRowsChangedSignalType;
229 /** the signature of row-change-notification signals */
230 typedef boost::signals2::signal<void (iterator)> RowSignalType;
231 /** the signature of const row-change-notification signals */
232 typedef boost::signals2::signal<void (const_iterator)> ConstRowSignalType;
233 /** the signature of row-click-notification signals */
234 typedef boost::signals2::signal<void(iterator, const Pt&,const GG::Flags<GG::ModKey>&)> RowClickSignalType;
235 /** the signature of row-move-notification signals */
236 typedef boost::signals2::signal<void (iterator, iterator)> MovedRowSignalType;
237
238 typedef RowSignalType BeforeInsertRowSignalType; ///< emitted before a row is inserted into the list box
239 typedef RowSignalType AfterInsertRowSignalType; ///< emitted after a row is inserted into the list box
240 typedef RowSignalType DroppedRowSignalType; ///< emitted when a row is inserted into the list box via drag-and-drop
241 typedef ConstRowSignalType DropRowAcceptableSignalType; ///< emitted when a row may be inserted into the list box via drag-and-drop
242 typedef RowSignalType BeforeEraseRowSignalType; ///< emitted when a row in the listbox is erased; provides the deleted Row, and is emitted before the row is removed
243 typedef RowSignalType AfterEraseRowSignalType; ///< emitted when a row in the listbox is erased; provides the deleted Row, and is emitted after the row is removed
244 typedef RowSignalType BrowsedRowSignalType; ///< emitted when a row in the listbox is "browsed" (rolled over) by the cursor; provides the browsed row
245 //@}
246
247 /** \name Constants */ ///@{
248 static const int DEFAULT_MARGIN;
249 static const X DEFAULT_ROW_WIDTH;
250 static const Y DEFAULT_ROW_HEIGHT;
251 static const unsigned int BORDER_THICK; ///< the thickness with which to render the border of the control
252 //@}
253
254 /** \name Structors */ ///@{
255 ListBox(Clr color, Clr interior = CLR_ZERO);
256
257 virtual ~ListBox();
258 //@}
259
260 void CompleteConstruction() override;
261
262 /** \name Accessors */ ///@{
263 Pt MinUsableSize() const override;
264 Pt ClientUpperLeft() const override;
265 Pt ClientLowerRight() const override;
266
267 bool Empty() const; ///< returns true when the ListBox is empty
268 const_iterator begin() const; ///< returns an iterator to the first list row
269 const_iterator end() const; ///< returns an iterator to the imaginary row one past the last
270 const Row& GetRow(std::size_t n) const; ///< returns a const reference to the row at index \a n; not range-checked. \note This function is O(n).
271 iterator Caret() const; ///< returns the row that has the caret
272 const SelectionSet& Selections() const; ///< returns a const reference to the set row indexes that is currently selected
273 bool Selected(iterator it) const; ///< returns true if row \a it is selected
274 Clr InteriorColor() const; ///< returns the color painted into the client area of the control
275 Clr HiliteColor() const; ///< returns the color behind selected line items
276
277 /** Returns the style flags of the listbox \see GG::ListBoxStyle */
278 Flags<ListBoxStyle> Style() const;
279
280 const Row& ColHeaders() const; ///< returns the row containing the headings for the columns, if any. If undefined, the returned heading Row will have size() 0.
281 iterator FirstRowShown() const; ///< returns the first row visible in the listbox
282 std::size_t FirstColShown() const; ///< returns the index of the first column visible in the listbox
283
284 iterator LastVisibleRow() const; ///< returns the last row that could be drawn, taking into account the contents and the size of client area
285 std::size_t LastVisibleCol() const; ///< returns the index of the last column that could be drawn, taking into account the contents and the size of client area
286
287 std::size_t NumRows() const; ///< returns the total number of rows in the ListBox
288 std::size_t NumCols() const; ///< returns the total number of columns in the ListBox
289
290 /** Returns true iff column widths are fixed \see LockColWidths() */
291 bool KeepColWidths() const;
292
293 /** Return true if column width and alignment are not managed by ListBox. */
294 bool ManuallyManagingColProps() const;
295
296 /** Returns the index of the column used to sort rows, when sorting is
297 enabled. \note The sort column is not range checked when it is set by
298 the user; it may be < 0 or >= NumCols(). */
299 std::size_t SortCol() const;
300
301 X ColWidth(std::size_t n) const; ///< returns the width of column \a n in pixels; not range-checked
302 Alignment ColAlignment(std::size_t n) const; ///< returns the alignment of column \a n; must be ALIGN_LEFT, ALIGN_CENTER, or ALIGN_RIGHT; not range-checked
303 double ColStretch(std::size_t n) const; ///< Return the stretch factor of column \a n.
304 Alignment RowAlignment(iterator it) const; ///< returns the alignment of row \a it; must be ALIGN_TOP, ALIGN_VCENTER, or ALIGN_BOTTOM; not range-checked
305
306 /** Returns true iff \p type is allowed to be dropped over this ListBox
307 when drag-and-drop is enabled. */
308 bool AllowedDropType(const std::string& type) const;
309
310 /** Whether the list should autoscroll when the user is attempting to drop
311 an item into a location that is not currently visible. */
312 bool AutoScrollDuringDragDrops() const;
313
314 /** The thickness of the area around the border of the client area that will
315 provoke an auto-scroll, if AutoScrollDuringDragDrops() returns true. */
316 unsigned int AutoScrollMargin() const;
317
318 /** The number of milliseconds that elapse between row/column scrolls when
319 auto-scrolling. */
320 unsigned int AutoScrollInterval() const;
321
322 /** Return true if drops are allowed.*/
323 bool AllowingDrops();
324
325
326 mutable ClearedRowsSignalType ClearedRowsSignal; ///< the cleared signal object for this ListBox
327 mutable BeforeInsertRowSignalType BeforeInsertRowSignal; ///< the before insert signal object for this ListBox
328 mutable AfterInsertRowSignalType AfterInsertRowSignal; ///< the after insert signal object for this ListBox
329 mutable SelRowsChangedSignalType SelRowsChangedSignal; ///< the selection change signal object for this ListBox
330 mutable DroppedRowSignalType DroppedRowSignal; ///< the dropped signal object for this ListBox
331 mutable DropRowAcceptableSignalType DropRowAcceptableSignal; ///< the drop-acceptability signal object for this ListBox
332 mutable MovedRowSignalType MovedRowSignal; ///< the moved signal object for this ListBox
333 mutable RowClickSignalType LeftClickedRowSignal; ///< the left click signal object for this ListBox
334 mutable RowClickSignalType RightClickedRowSignal; ///< the right click signal object for this ListBox
335 mutable RowClickSignalType DoubleClickedRowSignal; ///< the double click signal object for this ListBox
336 mutable BeforeEraseRowSignalType BeforeEraseRowSignal; ///< the before erase signal object for this ListBox
337 mutable AfterEraseRowSignalType AfterEraseRowSignal; ///< the after erase signal object for this ListBox
338 mutable BrowsedRowSignalType BrowsedRowSignal; ///< the browsed signal object for this ListBox
339 //@}
340
341 /** \name Mutators */ ///@{
342 void StartingChildDragDrop(const Wnd* wnd, const GG::Pt& offset) override;
343 void AcceptDrops(const Pt& pt, std::vector<std::shared_ptr<Wnd>> wnds, Flags<ModKey> mod_keys) override;
344 void ChildrenDraggedAway(const std::vector<Wnd*>& wnds, const Wnd* destination) override;
345 void PreRender() override;
346 void Render() override;
347
348 /** Resizes the control, then resizes the scrollbars as needed. */
349 void SizeMove(const Pt& ul, const Pt& lr) override;
350
351 /** Show the list box. If \p show_children is true then show the rows that are within the
352 boundaries of the list box.*/
353 void Show() override;
354
355 void Disable(bool b = true) override;
356 void SetColor(Clr c) override;
357
358 /** Insertion sorts \a row into the ListBox if sorted, or inserts into an
359 unsorted ListBox before \a it; returns insertion point. This Row
360 becomes the property of this ListBox. */
361 iterator Insert(std::shared_ptr<Row> row, iterator it);
362
363 /** Insertion sorts \a row into the ListBox if sorted, or inserts into an
364 unsorted ListBox at the end of the list; returns insertion point.
365 This Row becomes the property of this ListBox. */
366 iterator Insert(std::shared_ptr<Row> row);
367
368 /** Insertion sorts \a rows into the ListBox if sorted, or inserts into an
369 unsorted ListBox before \a it. The Rows become the property of this
370 ListBox. */
371 void Insert(const std::vector<std::shared_ptr<Row>>& rows, iterator it);
372
373 /** Insertion sorts \a rows into the ListBox if sorted, or inserts into an
374 unsorted ListBox at the end of the list. The Rows become the property
375 of this ListBox. */
376 void Insert(const std::vector<std::shared_ptr<Row>>& rows);
377
378 std::shared_ptr<Row>Erase(iterator it, bool signal = false); ///< removes and returns the row that \a it points to from the ListBox, or 0 if no such row exists
379
380 void Clear(); ///< empties the ListBox
381 void SelectRow(iterator it, bool signal = false); ///< selects row \a it
382 void DeselectRow(iterator it, bool signal = false); ///< deselects row \a it
383 void SelectAll(bool signal = false); ///< selects all rows
384 void DeselectAll(bool signal = false); ///< deselects all rows
385 void SetSelections(const SelectionSet& s, bool signal = false); ///< sets the set of selected rows to \a s
386
387 iterator begin(); ///< returns an iterator to the first list row
388 iterator end(); ///< returns an iterator to the imaginary row one past the last one
389
390 Row& GetRow(std::size_t n); ///< returns a reference to the Row at row index \a n; not range-checked. \note This function is O(n).
391
392 void SetCaret(iterator it); ///< sets the position of the caret to \a it
393 void BringRowIntoView(iterator it); ///< moves the scrollbars so that row \a it is visible
394 void SetFirstRowShown(iterator it); ///< moves the scrollbars so that row \a it is the first visible
395
396 /** Sets how much to scroll when scrolled using the mousewheel. */
397 void SetVScrollWheelIncrement(unsigned int increment);
398 void SetHScrollWheelIncrement(unsigned int increment);
399
400 void SetInteriorColor(Clr c); ///< sets the color painted into the client area of the control
401 void SetHiliteColor(Clr c); ///< sets the color behind selected line items
402
403 /** sets the style flags for the ListBox to \a s. \see GG::ListBoxStyle */
404 void SetStyle(Flags<ListBoxStyle> s);
405
406 void SetColHeaders(const std::shared_ptr<Row>& r); ///< sets the row used as headings for the columns; this Row becomes property of the ListBox.
407 void RemoveColHeaders(); ///< removes any columns headings set
408
409 void SetColWidth(std::size_t n, X w); ///< sets the width of column \n to \a w; not range-checked
410 void SetNumCols(std::size_t n); ///< sets the number of columns in the ListBox to \a n; if no column widths exist before this call, proportional widths are calulated and set, otherwise no column widths are set
411 void SetSortCol(std::size_t n); ///< sets the index of the column used to sort rows when sorting is enabled; not range-checked
412
413 /** Sets the comparison function used to sort a given pair of Rows during
414 row sorting. Note that \a sort_cmp is assumed to produce an ascending
415 order when used to sort; setting the LIST_SORTDESCENDING style can be
416 used to produce a reverse sort. */
417 void SetSortCmp(const std::function<bool (const Row&, const Row&, std::size_t)>& sort_cmp);
418
419 /** Fixes the column widths; by default, an empty ListBox will take on the
420 number of columns of its first added row. \note The number of columns
421 and their widths may still be set via SetNumCols() and SetColWidth()
422 after this function has been called. */
423 void LockColWidths();
424
425 /** Allows the number of columns to be determined by the first row added to
426 an empty ListBox */
427 void UnLockColWidths();
428
429 /** Set ListBox to stop managing column widths and alignment. The number of columns must be
430 set with SetColWidth(), but widths of individual rows columns or the header will not be
431 managed by ListBox. */
432 void ManuallyManageColProps();
433
434 /** Sets the alignment of column \a n to \a align; not range-checked */
435 void SetColAlignment(std::size_t n, Alignment align);
436
437 /** Sets the stretch of column \a n to \a stretch; not range-checked */
438 void SetColStretch(std::size_t n, double stretch);
439
440 /** Sets the alignment of row \a it to \a align; not range-checked */
441 void SetRowAlignment(iterator it, Alignment align);
442
443 /** Sets whether to normalize rows when inserted (true) or leave them as
444 * they are. */
445 void NormalizeRowsOnInsert(bool enable = true);
446
447 /** Sets the column widths from an exemplar \p row.*/
448 void DefineColWidths(const Row& row);
449
450 /** Sets the column alignments from an exemplar \p row.*/
451 void DefineColAlignments(const Row& row);
452
453 /** Sets the column alignments from an exemplar \p row.*/
454 void DefineColStretches(const Row& row);
455
456 /** Sets whether to add padding at the end of the scrolls when the ListBox is
457 * bigger than the client area, so that any row can be scrolled all the way to
458 * the top (true), or only use as much space as it needs. */
459 void AddPaddingAtEnd(bool enable = true);
460
461 /** Allow drops if \p allow is true.*/
462 void AllowDrops(bool allow);
463
464 /** Allow all drop types if \p allow is true.*/
465 void AllowAllDropTypes(bool allow);
466
467 /** Allows Rows with data type \a str to be dropped over this ListBox when
468 drag-and-drop is enabled. \note Passing "" enables all drop types. */
469 void AllowDropType(const std::string& str);
470
471 /** Set this to determine whether the list should autoscroll when the user
472 is attempting to drop an item into a location that is not currently
473 visible. */
474 void AutoScrollDuringDragDrops(bool auto_scroll);
475
476 /** Sets the thickness of the area around the border of the client area that
477 will provoke an auto-scroll, if AutoScrollDuringDragDrops() returns
478 true. */
479 void SetAutoScrollMargin(unsigned int margin);
480
481 /** Sets the number of milliseconds that elapse between row/column scrolls
482 when auto-scrolling. */
483 void SetAutoScrollInterval(unsigned int interval);
484 //@}
485
486 /** \brief Sorts two Rows of a ListBox using operator<() on the
487 Row::SortKeyType provided by the rows' SortKey() methods.
488
489 If you want to use operator<() with a Row subclass DerivedRow that has
490 a custom SortKeyType, use DefaultRowCmp<DerivedRow>. */
491 template <typename RowType>
492 struct DefaultRowCmp
493 {
494 /** Returns true iff lhs.SortKey( \a column ) < rhs.SortKey( \a column ). */
495 bool operator()(const Row& lhs, const Row& rhs, std::size_t column) const;
496 };
497
498 /** \name Exceptions */ ///@{
499 //@}
500
501 protected:
502 /** \name Accessors */ ///@{
503 X RightMargin() const; ///< space skipped at right of client area for vertical scroll bar
504 Y BottomMargin() const; ///< space skipped at bottom of client area for horizontal scroll bar
505 unsigned int CellMargin() const; ///< the number of pixels left between the contents of each cell and the cell boundary
506
507 iterator RowUnderPt(const Pt& pt) const; ///< returns row under pt, if any; value must be checked (i.e. it may be end())
508
509 iterator OldSelRow() const; ///< returns the last row that was selected with a left-button mouse-down
510 iterator OldRDownRow() const; ///< returns the last row that was selected with a right-button mouse-down
511 iterator LClickRow() const; ///< returns the last row that was left-clicked
512 iterator RClickRow() const; ///< returns the last row that was right-clicked
513
514 bool AutoScrollingUp() const; ///< returns true iff the list is being autoscrolled up due to drag-and-drop
515 bool AutoScrollingDown() const; ///< returns true iff the list is being autoscrolled down due to drag-and-drop
516 bool AutoScrollingLeft() const; ///< returns true iff the list is being autoscrolled left due to drag-and-drop
517 bool AutoScrollingRight() const; ///< returns true iff the list is being autoscrolled right due to drag-and-drop
518 //@}
519
520 /** \name Mutators */ ///@{
521 void KeyPress(Key key, std::uint32_t key_code_point, Flags<ModKey> mod_keys) override;
522 void MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys) override;
523 void DragDropEnter(const Pt& pt, std::map<const Wnd*, bool>& drop_wnds_acceptable, Flags<ModKey> mod_keys) override;
524 void DragDropHere(const Pt& pt, std::map<const Wnd*, bool>& drop_wnds_acceptable, Flags<ModKey> mod_keys) override;
525 void DragDropLeave() override;
526 void CancellingChildDragDrop(const std::vector<const Wnd*>& wnds) override;
527 void TimerFiring(unsigned int ticks, Timer* timer) override;
528
529 bool EventFilter(Wnd* w, const WndEvent& event) override;
530
531 /** Define the number of columns, the column widths and alignment from \p row.*/
532 /** Insertion sorts into list, or inserts into an unsorted list before
533 \a it; returns insertion point. */
534 iterator Insert(std::shared_ptr<Row> row, iterator it, bool dropped);
535
536 /** Insertion sorts into list, or inserts into an unsorted list before
537 \a it; returns insertion point. */
538 void Insert(const std::vector<std::shared_ptr<Row>>& rows, iterator it, bool dropped);
539
540 std::shared_ptr<Row> Erase(iterator it, bool removing_duplicate, bool signal); ///< erases the row at index \a idx, handling it as a duplicate removal (such as for drag-and-drops within a single ListBox) if indicated
541 void BringCaretIntoView(); ///< makes sure caret is visible when scrolling occurs due to keystrokes etc.
542 void ResetAutoScrollVars(); ///< resets all variables related to auto-scroll to their initial values
543 void Resort(); ///< performs a full resort of all rows, using the current sort functor.
544 Row& ColHeaders(); ///< returns the row containing the headings for the columns, if any. If undefined, the returned heading Row will have size() 0. non-const for derivers
545 //@}
546
547 /** creates, destroys, or resizes scrolls to reflect size of data in listbox. \p force_scroll
548 forces the scroll bar to be added.*/
549 void AdjustScrolls(bool adjust_for_resize,
550 const std::pair<bool, bool>& force_scrolls = {false, false});
551
552 void DropsAcceptable(DropsAcceptableIter first, DropsAcceptableIter last,
553 const Pt& pt, Flags<ModKey> mod_keys) const override;
554 void HandleRowRightClicked(const Pt& pt, Flags<ModKey> mod);
555
556 private:
557 /** Show only rows that are within the visible list box area and hide all others. If
558 \p do_prerender is true then prerender the visible rows. Return true if prerender
559 resulted in any visible row changing size. */
560 bool ShowVisibleRows(bool do_prerender);
561 void ConnectSignals();
562 void ValidateStyle(); ///< reconciles inconsistencies in the style flags
563 void VScrolled(int tab_low, int tab_high, int low, int high);///< signals from the vertical scroll bar are caught here
564 void HScrolled(int tab_low, int tab_high, int low, int high);///< signals from the horizontal scroll bar are caught here
565 void ClickAtRow(iterator it, Flags<ModKey> mod_keys); ///< handles to a mouse-click or spacebar-click on \a it, modified by \a keys
566 void NormalizeRow(Row* row); ///< adjusts a Row so that it has the same number of cells as other rows, and that each cell has the correct width and alignment
567 iterator FirstRowShownWhenBottomIs(iterator bottom_row); ///< Returns the first row shown when the last row shown is \a bottom_row
568 std::size_t FirstColShownWhenRightIs(std::size_t right_col, X client_width); ///< Returns the index of the first column shown when the last column shown is \a right_col
569
570 struct SelectionCache;
571 /** Cache the selected, clicked and last browsed rows.*/
572 std::shared_ptr<SelectionCache> CacheSelections();
573 /** Restore cached selected, clicked and last browsed rows.*/
574 void RestoreCachedSelections(const SelectionCache& cache);
575
576 /** Return the client size excluding the scroll bar sizes, in order to determine if scroll bars
577 are needed. This is a private function that is a component of AdjustScrolls.*/
578 Pt ClientSizeExcludingScrolls() const;
579
580 /** Return a pair of optional X and/or Y dimensions of the scollable area iff vscroll and/or
581 hscroll are required. If scrollbars are needed, the scrollable extent will be larger than the
582 client size. If a scrollbar is not required in some dimension return boost::none
583 for that dimension. \p maybe_client_size might contain a precalculated client size.
584
585 This is a private function that is a component of AdjustScrolls. */
586 std::pair<boost::optional<X>, boost::optional<Y>>
587 CheckIfScrollsRequired(const std::pair<bool, bool>& force_scrolls = {false, false},
588 const boost::optional<Pt>& maybe_client_size = boost::none) const;
589
590 /** Add vscroll and/or hscroll if \p required_total_extents the x andor y dimension exists. The
591 value of \p required_total_extents is the full x and y dimensions of the underlying ListBox
592 requiring the scrollbar as calculated in CheckIfScrollsRequired. Return a pair of bools
593 indicating if vscroll and/or hscroll was added and/or removed. \p maybe_client_size might
594 contain a precalculated client size as calculated in ClientSizeExcludingScrolls.
595
596 This is a private function that is a component of AdjustScrolls. */
597 std::pair<bool, bool> AddOrRemoveScrolls(const std::pair<boost::optional<X>, boost::optional<Y>>& required_total_extents,
598 const boost::optional<Pt>& maybe_client_size = boost::none);
599
600 /// m_rows is mutable to enable returning end() from const functions in constant time.
601 mutable std::list<std::shared_ptr<Row>> m_rows; ///< line item data
602
603 std::shared_ptr<Scroll> m_vscroll; ///< vertical scroll bar on right
604 std::shared_ptr<Scroll> m_hscroll; ///< horizontal scroll bar at bottom
605 unsigned int m_vscroll_wheel_scroll_increment = 0;
606 unsigned int m_hscroll_wheel_scroll_increment = 0;
607
608 iterator m_caret; ///< the item currently selected, or the last item selected by the user
609 SelectionSet m_selections; ///< vector of indexes of selected items
610 iterator m_old_sel_row; ///< used to make sure clicks end on the same row where they started
611 bool m_old_sel_row_selected = false; ///< set to true if m_old_sel_row was selected at the point at which it was designated
612 iterator m_old_rdown_row; ///< the row that most recently recieved a right button down message
613 iterator m_lclick_row; ///< the row most recently left-clicked
614 iterator m_rclick_row; ///< the row most recently right-clicked
615 iterator m_last_row_browsed; ///< the last row sent out as having been browsed (used to prevent duplicate browse signals)
616
617 GG::Pt m_first_row_offset = {X(BORDER_THICK), Y(BORDER_THICK)};///< scrolled offset of the first row.
618 iterator m_first_row_shown; ///< index of row at top of visible area (always begin() for non-empty ListBox with LIST_NOSCROLL set)
619 std::size_t m_first_col_shown = 0; ///< like above, but index of column at left
620 std::size_t m_num_cols = 1; ///< the number of columns
621 std::vector<X> m_col_widths; ///< the width of each of the columns goes here
622
623 std::vector<Alignment> m_col_alignments; ///< the horizontal alignment of each of the columns goes here
624 std::vector<double> m_col_stretches; ///< the stretch factor of each column
625 unsigned int m_cell_margin; ///< the amount of space left between each edge of the cell and its contents, in pixels
626
627 Clr m_int_color; ///< color painted into the client area of the control
628 Clr m_hilite_color = CLR_SHADOW;///< color behind selected line items
629 Flags<ListBoxStyle> m_style = LIST_NONE; ///< composed of ListBoxStyles enums (see GUIBase.h)
630
631 std::shared_ptr<Row> m_header_row; ///< row of header text/graphics
632 bool m_keep_col_widths = false; ///< should we keep the column widths, once set?
633 bool m_clip_cells = false; ///< if true, the contents of each cell will be clipped to the visible area of that cell (TODO: currently unused)
634 std::size_t m_sort_col = 0; ///< the index of the column data used to sort the list
635
636 std::function<bool (const Row&, const Row&, std::size_t)>
637 m_sort_cmp; ///< the predicate used to sort the values in the m_sort_col column of two rows
638
639 bool m_allow_drops = false; ///< are we accepting drops
640
641 /** Set to boost::none to allow all types. Otherwise each string is an
642 allowed type.*/
643 boost::optional<std::unordered_set<std::string>>
644 m_allowed_drop_types = boost::none;
645
646 bool m_auto_scroll_during_drag_drops = true;
647 unsigned int m_auto_scroll_margin = 8;
648 bool m_auto_scrolling_up = false;
649 bool m_auto_scrolling_down = false;
650 bool m_auto_scrolling_left = false;
651 bool m_auto_scrolling_right = false;
652 Timer m_auto_scroll_timer{250};
653
654 bool m_normalize_rows_on_insert = true;
655 bool m_manage_column_props = true;
656 bool m_add_padding_at_end = true;
657
658 friend class DropDownList; ///< allow complete access to DropDownList, which relies on ListBox to do its rendering
659 };
660
661 } // namespace GG
662
663
664 // template implementations
665 template <typename RowType>
operator()666 bool GG::ListBox::DefaultRowCmp<RowType>::operator()(const GG::ListBox::Row& lhs, const GG::ListBox::Row& rhs, std::size_t column) const
667 {
668 return static_cast<const RowType&>(lhs).SortKey(column) < static_cast<const RowType&>(rhs).SortKey(column);
669 }
670
671 #endif
672