mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-10 12:23:58 -07:00
* move message_log_widget to game * move files * update headers * fix cmakelists * oracle fixes * split implementation out to cpp * fix recursive import * fix main file * format
This commit is contained in:
parent
f484c98152
commit
17dcaf9afa
337 changed files with 728 additions and 721 deletions
496
cockatrice/src/interface/layouts/overlap_layout.cpp
Normal file
496
cockatrice/src/interface/layouts/overlap_layout.cpp
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
#include "overlap_layout.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QtMath>
|
||||
|
||||
/**
|
||||
* @class OverlapLayout
|
||||
* @brief Custom layout class to arrange widgets with overlapping positions.
|
||||
*
|
||||
* The OverlapLayout class is a QLayout subclass that arranges child widgets
|
||||
* in an overlapping configuration, allowing control over the overlap percentage
|
||||
* and the number of rows or columns based on the chosen layout direction. This
|
||||
* layout is particularly useful for visualizing elements that need to partially
|
||||
* stack over one another, either horizontally or vertically.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Constructs an OverlapLayout with the specified parameters.
|
||||
*
|
||||
* Initializes a new OverlapLayout with the given overlap percentage, row or column limit,
|
||||
* and layout direction. The overlap percentage determines how much each widget will
|
||||
* overlap with the previous one. If maxColumns or maxRows are set to zero, it implies
|
||||
* no limit in that respective dimension.
|
||||
*
|
||||
* @param overlapPercentage An integer representing the percentage of overlap between items (0-100).
|
||||
* @param maxColumns The maximum number of columns allowed in the layout when in horizontal orientation (0 for
|
||||
* unlimited).
|
||||
* @param maxRows The maximum number of rows allowed in the layout when in vertical orientation (0 for unlimited).
|
||||
* @param direction The orientation direction of the layout, either Qt::Horizontal or Qt::Vertical.
|
||||
* @param parent The parent widget of this layout.
|
||||
*/
|
||||
OverlapLayout::OverlapLayout(QWidget *parent,
|
||||
const int overlapPercentage,
|
||||
const int maxColumns,
|
||||
const int maxRows,
|
||||
const Qt::Orientation _overlapDirection,
|
||||
const Qt::Orientation _flowDirection)
|
||||
: QLayout(parent), overlapPercentage(overlapPercentage), maxColumns(maxColumns), maxRows(maxRows),
|
||||
overlapDirection(_overlapDirection), flowDirection(_flowDirection)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor for OverlapLayout, ensuring cleanup of all layout items.
|
||||
*
|
||||
* Iterates through all layout items and deletes them. This prevents memory
|
||||
* leaks by removing all child QLayoutItems stored in the layout.
|
||||
*/
|
||||
OverlapLayout::~OverlapLayout()
|
||||
{
|
||||
QLayoutItem *item;
|
||||
while ((item = OverlapLayout::takeAt(0))) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void OverlapLayout::insertWidgetAtIndex(QWidget *toInsert, int index)
|
||||
{
|
||||
addChildWidget(toInsert);
|
||||
int clampedIndex = qBound(0, index, qMax(0, static_cast<int>(itemList.size())));
|
||||
itemList.insert(clampedIndex, new QWidgetItem(toInsert));
|
||||
|
||||
for (int i = clampedIndex; i < itemList.size(); ++i) {
|
||||
dynamic_cast<QWidgetItem *>(itemList.at(i))->widget()->raise();
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a new item to the layout.
|
||||
*
|
||||
* Appends a QLayoutItem to the internal list, allowing it to be positioned within the
|
||||
* layout during the next geometry update. This method does not directly arrange the
|
||||
* items; it merely adds them to the layout’s tracking.
|
||||
*
|
||||
* @param item Pointer to the QLayoutItem being added to this layout.
|
||||
*/
|
||||
void OverlapLayout::addItem(QLayoutItem *item)
|
||||
{
|
||||
if (item != nullptr) {
|
||||
itemList.append(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieves the total count of items within the layout.
|
||||
*
|
||||
* Returns the number of items stored in the layout's internal item list.
|
||||
* This count reflects how many widgets or spacers the layout is currently managing.
|
||||
*
|
||||
* @return Integer count of items in the layout.
|
||||
*/
|
||||
int OverlapLayout::count() const
|
||||
{
|
||||
return static_cast<int>(itemList.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Provides access to a layout item at a specified index.
|
||||
*
|
||||
* Allows retrieval of a QLayoutItem from the layout’s internal list
|
||||
* by index. If the index is out of bounds, this function will return nullptr.
|
||||
*
|
||||
* @param index The index of the desired item.
|
||||
* @return Pointer to the QLayoutItem at the specified index, or nullptr if index is invalid.
|
||||
*/
|
||||
QLayoutItem *OverlapLayout::itemAt(const int index) const
|
||||
{
|
||||
return (index >= 0 && index < itemList.size()) ? itemList.value(index) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes and returns a layout item at the specified index.
|
||||
*
|
||||
* Removes a QLayoutItem from the layout at the given index, reducing the layout's count.
|
||||
* If the index is invalid, this function returns nullptr without any effect.
|
||||
*
|
||||
* @param index The index of the item to remove.
|
||||
* @return Pointer to the removed QLayoutItem, or nullptr if index is invalid.
|
||||
*/
|
||||
QLayoutItem *OverlapLayout::takeAt(const int index)
|
||||
{
|
||||
return (index >= 0 && index < itemList.size()) ? itemList.takeAt(index) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the geometry for the layout items, arranging them with the specified overlap.
|
||||
* @param rect The rectangle defining the area within which the layout should arrange items.
|
||||
*/
|
||||
void OverlapLayout::setGeometry(const QRect &rect)
|
||||
{
|
||||
// Call the base class implementation to ensure standard layout behavior.
|
||||
QLayout::setGeometry(rect);
|
||||
|
||||
// If there are no items to layout, exit early.
|
||||
if (itemList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the parent widget for size and margin calculations.
|
||||
const QWidget *parentWidget = this->parentWidget();
|
||||
if (!parentWidget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate available width and height, subtracting the parent's margins.
|
||||
int availableWidth = parentWidget->width();
|
||||
int availableHeight = parentWidget->height();
|
||||
const QMargins margins = parentWidget->contentsMargins();
|
||||
availableWidth -= margins.left() + margins.right();
|
||||
availableHeight -= margins.top() + margins.bottom();
|
||||
|
||||
// Determine the maximum item width and height among all layout items.
|
||||
int maxItemWidth = 0;
|
||||
int maxItemHeight = 0;
|
||||
for (QLayoutItem *item : itemList) {
|
||||
if (item != nullptr && item->widget()) {
|
||||
QSize itemSize = item->widget()->sizeHint();
|
||||
maxItemWidth = qMax(maxItemWidth, itemSize.width());
|
||||
maxItemHeight = qMax(maxItemHeight, itemSize.height());
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the overlap offsets based on the layout direction and overlap percentage.
|
||||
const int overlapOffsetWidth = (overlapDirection == Qt::Horizontal) ? (maxItemWidth * overlapPercentage / 100) : 0;
|
||||
const int overlapOffsetHeight = (overlapDirection == Qt::Vertical) ? (maxItemHeight * overlapPercentage / 100) : 0;
|
||||
|
||||
// Determine the number of columns based on layout constraints and available space.
|
||||
int columns;
|
||||
if (flowDirection == Qt::Horizontal) {
|
||||
if (maxColumns > 0) {
|
||||
// Calculate the maximum possible columns given the available width and overlap.
|
||||
const int availableColumns = (availableWidth + overlapOffsetWidth) / (maxItemWidth - overlapOffsetWidth);
|
||||
// Use the smaller of maxColumns and availableColumns.
|
||||
qCDebug(OverlapLayoutLog) << " Max Columns " << maxColumns << " available columns " << availableColumns;
|
||||
columns = qMin(maxColumns, availableColumns);
|
||||
if (columns < 1) {
|
||||
columns = 1;
|
||||
}
|
||||
} else {
|
||||
// If no maxColumns constraint, allow as many columns as possible.
|
||||
columns = INT_MAX;
|
||||
}
|
||||
} else {
|
||||
// If not a horizontal layout, column count is irrelevant.
|
||||
columns = INT_MAX;
|
||||
}
|
||||
|
||||
// Determine the number of rows based on layout constraints and available space.
|
||||
int rows;
|
||||
if (flowDirection == Qt::Vertical) {
|
||||
if (maxRows > 0) {
|
||||
// Calculate the maximum possible rows given the available height and overlap.
|
||||
const int availableRows = (availableHeight + overlapOffsetHeight) / (maxItemHeight - overlapOffsetHeight);
|
||||
// Use the smaller of maxRows and availableRows.
|
||||
qCDebug(OverlapLayoutLog) << " Max Rows " << maxRows << " available rows " << availableRows;
|
||||
rows = qMin(maxRows, availableRows);
|
||||
if (rows < 1) {
|
||||
rows = 1;
|
||||
}
|
||||
} else {
|
||||
// If no maxRows constraint, allow as many rows as possible.
|
||||
rows = INT_MAX;
|
||||
}
|
||||
} else {
|
||||
// If not a vertical layout, row count is irrelevant.
|
||||
rows = INT_MAX;
|
||||
}
|
||||
|
||||
// Initialize row and column indices.
|
||||
int currentRow = 0;
|
||||
int currentColumn = 0;
|
||||
|
||||
// Loop through all items and position them based on the calculated offsets.
|
||||
for (const auto item : itemList) {
|
||||
if (item == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate the position of the current item.
|
||||
const int xPos = rect.left() + currentColumn * (maxItemWidth - overlapOffsetWidth);
|
||||
const int yPos = rect.top() + currentRow * (maxItemHeight - overlapOffsetHeight);
|
||||
item->setGeometry(QRect(xPos, yPos, maxItemWidth, maxItemHeight));
|
||||
|
||||
// TODO: Figure this out properly or maybe adjust size hint to account for this?
|
||||
// Update row and column indices based on the layout direction.
|
||||
if (overlapDirection == Qt::Horizontal) {
|
||||
currentColumn++;
|
||||
if (currentColumn > qCeil(itemList.size() / rows)) {
|
||||
currentColumn = 0;
|
||||
currentRow++;
|
||||
}
|
||||
} else {
|
||||
currentRow++;
|
||||
if (currentRow > qCeil(itemList.size() / columns)) {
|
||||
currentRow = 0;
|
||||
currentColumn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the preferred size for the layout, considering overlap and orientation.
|
||||
* @return The preferred layout size as a QSize object.
|
||||
*/
|
||||
QSize OverlapLayout::calculatePreferredSize() const
|
||||
{
|
||||
// Get the parent widget for size and margin calculations.
|
||||
const QWidget *parentWidget = this->parentWidget();
|
||||
if (!parentWidget) {
|
||||
return QSize(0, 0);
|
||||
}
|
||||
|
||||
if (itemList.isEmpty()) {
|
||||
return QSize(0, 0);
|
||||
}
|
||||
|
||||
int availableWidth = parentWidget->width();
|
||||
int availableHeight = parentWidget->height();
|
||||
const QMargins margins = parentWidget->contentsMargins();
|
||||
availableWidth -= margins.left() + margins.right();
|
||||
availableHeight -= margins.top() + margins.bottom();
|
||||
|
||||
// Determine the maximum item width and height among all layout items.
|
||||
int maxItemWidth = 0;
|
||||
int maxItemHeight = 0;
|
||||
for (QLayoutItem *item : itemList) {
|
||||
if (item != nullptr && item->widget()) {
|
||||
QSize itemSize = item->widget()->sizeHint();
|
||||
maxItemWidth = qMax(maxItemWidth, itemSize.width());
|
||||
maxItemHeight = qMax(maxItemHeight, itemSize.height());
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the overlap offsets based on the layout direction and overlap percentage.
|
||||
const int overlapOffsetWidth = (overlapDirection == Qt::Horizontal) ? (maxItemWidth * overlapPercentage / 100) : 0;
|
||||
const int overlapOffsetHeight = (overlapDirection == Qt::Vertical) ? (maxItemHeight * overlapPercentage / 100) : 0;
|
||||
|
||||
// Determine the number of columns based on layout constraints and available space.
|
||||
int columns;
|
||||
if (flowDirection == Qt::Horizontal) {
|
||||
if (maxColumns > 0) {
|
||||
// Calculate the maximum possible columns given the available width and overlap.
|
||||
const int availableColumns = (availableWidth + overlapOffsetWidth) / (maxItemWidth - overlapOffsetWidth);
|
||||
// Use the smaller of maxColumns and availableColumns.
|
||||
qCDebug(OverlapLayoutLog) << " Max Columns " << maxColumns << " available columns " << availableColumns;
|
||||
columns = qMin(maxColumns, availableColumns);
|
||||
if (columns < 1) {
|
||||
columns = 1;
|
||||
}
|
||||
} else {
|
||||
// If no maxColumns constraint, allow as many columns as possible.
|
||||
columns = INT_MAX;
|
||||
}
|
||||
} else {
|
||||
// If not a horizontal layout, column count is irrelevant.
|
||||
columns = INT_MAX;
|
||||
}
|
||||
|
||||
// Determine the number of rows based on layout constraints and available space.
|
||||
int rows;
|
||||
if (flowDirection == Qt::Vertical) {
|
||||
if (maxRows > 0) {
|
||||
// Calculate the maximum possible rows given the available height and overlap.
|
||||
const int availableRows = (availableHeight + overlapOffsetHeight) / (maxItemHeight - overlapOffsetHeight);
|
||||
// Use the smaller of maxRows and availableRows.
|
||||
qCDebug(OverlapLayoutLog) << " Max Rows " << maxRows << " available rows " << availableRows;
|
||||
rows = qMin(maxRows, availableRows);
|
||||
if (rows < 1) {
|
||||
rows = 1;
|
||||
}
|
||||
} else {
|
||||
// If no maxRows constraint, allow as many rows as possible.
|
||||
rows = INT_MAX;
|
||||
}
|
||||
} else {
|
||||
// If not a vertical layout, row count is irrelevant.
|
||||
rows = INT_MAX;
|
||||
}
|
||||
|
||||
if (overlapDirection == Qt::Horizontal) {
|
||||
return QSize(maxItemWidth + ((qCeil(itemList.size() / rows)) * (maxItemWidth - overlapOffsetWidth)),
|
||||
rows * maxItemHeight);
|
||||
} else {
|
||||
return QSize(columns * maxItemWidth,
|
||||
maxItemHeight + ((qCeil(itemList.size() / columns)) * (maxItemHeight - overlapOffsetHeight)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the size hint for the layout, based on preferred size calculations.
|
||||
*
|
||||
* Provides a recommended size for the layout, useful for layouts that need to fit within
|
||||
* a specific parent container size. This takes into account the preferred size and
|
||||
* any specific item size requirements.
|
||||
*
|
||||
* @return The layout's recommended QSize.
|
||||
*/
|
||||
QSize OverlapLayout::sizeHint() const
|
||||
{
|
||||
return calculatePreferredSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Provides the minimum size hint for the layout, ensuring functionality within constraints.
|
||||
*
|
||||
* Defines a minimum workable size for the layout to prevent excessive compression
|
||||
* that could distort item arrangement.
|
||||
*
|
||||
* @return The minimum QSize for this layout.
|
||||
*/
|
||||
QSize OverlapLayout::minimumSize() const
|
||||
{
|
||||
return calculatePreferredSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the layout's orientation direction.
|
||||
*
|
||||
* @param _direction The new orientation direction (Qt::Horizontal or Qt::Vertical).
|
||||
*/
|
||||
void OverlapLayout::setDirection(const Qt::Orientation _direction)
|
||||
{
|
||||
overlapDirection = _direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the maximum number of columns for horizontal orientation.
|
||||
*
|
||||
* @param _maxColumns New maximum column count.
|
||||
*/
|
||||
void OverlapLayout::setMaxColumns(const int _maxColumns)
|
||||
{
|
||||
if (_maxColumns >= 0) {
|
||||
maxColumns = _maxColumns;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the maximum number of rows for vertical orientation.
|
||||
*
|
||||
* @param _maxRows New maximum row count.
|
||||
*/
|
||||
void OverlapLayout::setMaxRows(const int _maxRows)
|
||||
{
|
||||
if (_maxRows >= 0) {
|
||||
maxRows = _maxRows;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the maximum number of columns for a vertical overlap layout based on the current width.
|
||||
*
|
||||
* This function determines the maximum number of columns that can fit within the layout's width
|
||||
* given the overlap percentage and item size, based on the current layout direction.
|
||||
*
|
||||
* @return Maximum number of columns that can fit within the layout width.
|
||||
*/
|
||||
int OverlapLayout::calculateMaxColumns() const
|
||||
{
|
||||
if (overlapDirection != Qt::Vertical || itemList.isEmpty()) {
|
||||
return 1; // Only relevant if the layout direction is vertical
|
||||
}
|
||||
|
||||
// Determine maximum item width
|
||||
int maxItemWidth = 0;
|
||||
for (QLayoutItem *item : itemList) {
|
||||
if (item == nullptr || !item->widget()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QSize itemSize = item->widget()->sizeHint();
|
||||
maxItemWidth = qMax(maxItemWidth, itemSize.width());
|
||||
}
|
||||
|
||||
const int availableWidth = parentWidget() ? parentWidget()->width() : 0;
|
||||
// Determine the maximum number of columns that can fit
|
||||
const int columns = availableWidth / maxItemWidth;
|
||||
|
||||
return qMax(1, columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the maximum number of rows needed for a given number of columns in a vertical overlap layout.
|
||||
*
|
||||
* Determines how many rows are required to arrange all items given the calculated or specified number of columns.
|
||||
*
|
||||
* @param columns The number of columns available.
|
||||
* @return The total number of rows required.
|
||||
*/
|
||||
int OverlapLayout::calculateRowsForColumns(const int columns) const
|
||||
{
|
||||
if (overlapDirection != Qt::Vertical || itemList.isEmpty() || columns <= 0) {
|
||||
return 1; // Only relevant if the layout direction is vertical and there are items
|
||||
}
|
||||
|
||||
const int totalItems = static_cast<int>(itemList.size());
|
||||
|
||||
return qCeil(totalItems / columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the maximum number of rows for a horizontal overlap layout based on the current height.
|
||||
*
|
||||
* This function determines the maximum number of rows that can fit within the layout's height
|
||||
* given the overlap percentage and item size, based on the current layout direction.
|
||||
*
|
||||
* @return Maximum number of rows that can fit within the layout height.
|
||||
*/
|
||||
int OverlapLayout::calculateMaxRows() const
|
||||
{
|
||||
if (overlapDirection != Qt::Horizontal || itemList.isEmpty()) {
|
||||
return 1; // Only relevant if the layout direction is horizontal
|
||||
}
|
||||
|
||||
// Determine maximum item height
|
||||
int maxItemHeight = 0;
|
||||
for (QLayoutItem *item : itemList) {
|
||||
if (item == nullptr || !item->widget()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QSize itemSize = item->widget()->sizeHint();
|
||||
maxItemHeight = qMax(maxItemHeight, itemSize.height());
|
||||
}
|
||||
|
||||
// Calculate the effective height of each item with the overlap applied
|
||||
const int overlapOffsetHeight = (maxItemHeight * (100 - overlapPercentage)) / 100;
|
||||
const int availableHeight = parentWidget() ? parentWidget()->height() : 0;
|
||||
|
||||
// Determine the maximum number of rows that can fit
|
||||
const int rows = availableHeight / overlapOffsetHeight;
|
||||
|
||||
return qMax(1, rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the maximum number of columns needed for a given number of rows in a horizontal overlap layout.
|
||||
*
|
||||
* Determines how many columns are required to arrange all items given the calculated or specified number of rows.
|
||||
*
|
||||
* @param rows The number of rows available.
|
||||
* @return The total number of columns required.
|
||||
*/
|
||||
int OverlapLayout::calculateColumnsForRows(const int rows) const
|
||||
{
|
||||
if (overlapDirection != Qt::Horizontal || itemList.isEmpty() || rows <= 0) {
|
||||
return 1; // Only relevant if the layout direction is horizontal and there are items
|
||||
}
|
||||
|
||||
const int totalItems = static_cast<int>(itemList.size());
|
||||
|
||||
return qCeil(totalItems / rows);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue