File: table.h

package info (click to toggle)
warzone2100 4.6.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 660,348 kB
  • sloc: cpp: 675,711; ansic: 387,204; javascript: 75,107; python: 16,628; php: 4,294; sh: 3,941; makefile: 2,330; lisp: 1,492; cs: 489; xml: 404; perl: 224; ruby: 156; java: 89
file content (230 lines) | stat: -rw-r--r-- 8,839 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/*
	This file is part of Warzone 2100.
	Copyright (C) 2020-2021  Warzone 2100 Project

	Warzone 2100 is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	Warzone 2100 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Warzone 2100; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
 * @file
 * Definitions for scrollable table functions.
 */

#ifndef __INCLUDED_LIB_WIDGET_TABLE_H__
#define __INCLUDED_LIB_WIDGET_TABLE_H__

#include "widget.h"
#include "button.h"
#include "scrollablelist.h"

#include <vector>
#include <unordered_set>

class TableRow: public W_BUTTON
{
protected:
	TableRow();
public:
	// Make a new TableRow, given a vector of widgets (for the columns)
	// If rowHeight <= 0, expand the TableRow height to match maximum height of any contained (column) widget
	// If rowHeight > 0, use it as a fixed row height, and set all contained (column) widgets to that height
	//
	// NOTE: A column widget should expect its width to be adjustable, unless it is associated with a TableColumn
	//       (in its parent ScrollableTableWidget) that has ResizeBehavior::FIXED_WIDTH.
	static std::shared_ptr<TableRow> make(const std::vector<std::shared_ptr<WIDGET>>& _columnWidgets, int rowHeight = 0);

	// Set whether the row background highlights on mouse-over
	void setHighlightsOnMouseOver(bool value);
	void setHighlightColor(PIELIGHT newHighlightColor);

	// Get the total idealWidth() returned by all column widgets in this row (does not include padding)
	int32_t getColumnTotalContentIdealWidth();

	// Set whether the row draws an outer border
	void setDrawBorder(optional<PIELIGHT> borderColor);

	// Set a background color for the row
	void setBackgroundColor(optional<PIELIGHT> backgroundColor);

	// Set whether row is "disabled"
	void setDisabled(bool disabled);
	// Set row disable overlay color
	void setDisabledColor(PIELIGHT disabledColor);
protected:
	virtual void display(int, int) override;
	virtual void displayRecursive(WidgetGraphicsContext const& context) override;
	virtual bool hitTest(int x, int y) const override;
public:
	virtual std::shared_ptr<WIDGET> findMouseTargetRecursive(W_CONTEXT *psContext, WIDGET_KEY key, bool wasPressed) override;
protected:
	friend class ScrollableTableWidget;
	virtual void resizeColumns(const std::vector<size_t>& columnWidths, int columnPadding);
	inline size_t numColumns() const { return columnWidgets.size(); }
	std::shared_ptr<WIDGET> getWidgetAtColumn(size_t col) const;
private:
	bool isMouseOverRowOrChildren() const;
private:
	bool highlightsOnMouseOver = false;
	bool disabledRow = false;
	std::vector<std::shared_ptr<WIDGET>> columnWidgets;
	optional<UDWORD> lastFrameMouseIsOverRowOrChildren = nullopt;
	optional<PIELIGHT> borderColor = nullopt;
	optional<PIELIGHT> backgroundColor = nullopt;
	PIELIGHT highlightColor;
	PIELIGHT disabledColor;
	int32_t maxDisplayedColumnX1 = 0;
};

class TableHeader; // forward-declare

struct TableColumn
{
	enum class ResizeBehavior
	{
		RESIZABLE,				// user-resizable column
		RESIZABLE_AUTOEXPAND,	// user-resizable column, that also automatically expands to fill extra space
		FIXED_WIDTH
	};

	std::shared_ptr<WIDGET> columnWidget;
	ResizeBehavior resizeBehavior;
};

class ScrollableTableWidget: public WIDGET
{
protected:
	ScrollableTableWidget();

public:
	// Make a new ScrollableTableWidget
	//
	// Note: The TableColumn.columnWidget widgets are expected to have pre-set widths, which are used to
	//       initialize the ScrollableTableWidget's columnWidths.
	static std::shared_ptr<ScrollableTableWidget> make(const std::vector<TableColumn>& columns, int headerHeight = -1);

	// Add a new table row
	// See: ``TableRow``
	void addRow(const std::shared_ptr<TableRow> &row);
	void clearRows();
	const std::vector<std::shared_ptr<TableRow>>& getRows() const { return rows; }

	// Show / hide header
	void setHeaderVisible(bool visible);

	// Disable / enable a row
	void setRowDisabled(size_t row, bool disabled);

	// Get the maximum width that can be used by the column widths passed to changeColumnWidths, based on the current widget size (minus padding)
	size_t getMaxColumnTotalWidth(size_t numColumns) const;

	// Get the table width needed to display columns with the specified total minimum column width
	size_t getTableWidthNeededForTotalColumnWidth(size_t numColumns, size_t totalMinimumColumnWidth) const;

	// Get the current column widths
	const std::vector<size_t>& getColumnWidths() const { return columnWidths; }

	// Propose a new set of column widths
	// The ScrollableTableWidget will automatically adjust the submitted widths for various constraints
	// (like the minimum column widths, total maximum width, which columns can be resized / expanded-to-fill-space, fixed width sizes, etc)
	// It is expected that newColumnWidths.size() will be equal to the number of columns in the table.
	bool changeColumnWidths(const std::vector<size_t>& newColumnWidths, bool overrideUserColumnResizing = false);

	// Change a single column width
	// The ScrollableTableWidget will preferentially re-layout / flow the other column widths according to the table's column constraints.
	// However, it will fallback to adjusting the newColumnWidth for col if necessary to satisfy all constraints.
	// Returns:
	// - `nullopt` on failure (if there was no way to change the specified column to the newColumnWidth and layout the other columns)
	// - the actual newColumnWidth, on success, which may differ from the input newColumnWidth if required to satisfy layout constraints
	optional<size_t> changeColumnWidth(size_t col, size_t newColumnWidth, bool overrideUserColumnResizing = false);

	// Get the current minimum column width layout constraints
	const std::vector<size_t>& getMinimumColumnWidths() const { return minColumnWidths; }

	// Add minimum column width layout constraints for all columns
	void setMinimumColumnWidths(const std::vector<size_t>& newMinColumnWidths);

	// Add or update a minimum column width layout constraint for a single column
	// Note: This triggers a relayout, so if you are setting constraints on multiple columns you should use:
	// setMinimumColumnWidths(const std::vector<size_t>& newMinColumnWidths)
	void setMinimumColumnWidth(size_t col, size_t newMinColumnWidth);

	// Get the maximum idealWidth() returned by any of the row widgets in the specified column
	int32_t getColumnMaxContentIdealWidth(size_t col);

	inline size_t getNumRows() const { return rows.size(); }
	inline size_t getNumColumns() const { return tableColumns.size(); }

	// Change the table background color
	void setBackgroundColor(PIELIGHT const &color);

	// Configure whether to draw column lines for list rows
	void setDrawColumnLines(bool bEnabled);

	void setItemSpacing(uint32_t value);

	uint16_t getScrollPosition() const;
	void setScrollPosition(uint16_t newPosition);

	bool isUserDraggingColumnHeader() const;

	void setColumnPadding(Vector2i padding);
	const Vector2i& getColumnPadding();

	virtual int32_t idealHeight() override;

protected:
	friend class TableHeader;
	optional<size_t> header_changeColumnWidth(size_t col, size_t newColumnWidth);

protected:
	virtual void geometryChanged() override;
	virtual void displayRecursive(WidgetGraphicsContext const& context) override;

private:
	size_t totalPaddingWidthFor(size_t numColumns) const;
	bool relayoutColumns(std::vector<size_t> proposedColumnWidths, std::unordered_set<size_t> priorityIndexes = {});
	void updateColumnWidths();
	std::vector<size_t> getShrinkableColumnIndexes(const std::vector<size_t>& currentColumnWidths);
	std::vector<size_t> getExpandToFillColumnIndexes();
	inline std::vector<size_t> getColumnIndexes(const std::function<bool (size_t idx, const TableColumn&)>& func)
	{
		std::vector<size_t> columnIndexes;
		for (size_t i = 0; i < tableColumns.size(); i++)
		{
			if (func(i, tableColumns[i]))
			{
				columnIndexes.push_back(i);
			}
		}
		return columnIndexes;
	}

private:
	std::vector<TableColumn> tableColumns;
	std::vector<size_t> columnWidths;
	std::vector<size_t> minColumnWidths;
	Vector2i columnPadding;

	std::shared_ptr<TableHeader> header;
	std::shared_ptr<ScrollableListWidget> scrollableList;

	std::vector<std::shared_ptr<TableRow>> rows;

	std::vector<glm::ivec4> lines;
	bool drawColumnLines = false;

	bool userDidResizeColumnWidths = false;
};

#endif // __INCLUDED_LIB_WIDGET_TABLE_H__