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__
|