Finally fix the freaking OverlapLayout.

This commit is contained in:
Lukas Brübach 2025-02-11 20:39:04 +01:00
parent 6ee6d1b470
commit 8a5fbb25c0
6 changed files with 104 additions and 99 deletions

View file

@ -1,13 +1,16 @@
[Rules] [Rules]
# Uncomment a rule to disable logging for that category # Uncomment a rule to disable logging for that category
# main = false flow_layout = false
# qt_translator = false flow_widget.* = false
# window_main.* = false
# release_channel = false # main = true
# spoiler_background_updater = false qt_translator = false
# theme_manager = false window_main.* = false
# sound_engine = false release_channel = false
spoiler_background_updater = false
theme_manager = false
sound_engine = false
# tapped_out_interface = false # tapped_out_interface = false
# tab_game = false # tab_game = false
@ -33,15 +36,15 @@
# user_info_connection = false # user_info_connection = false
# picture_loader = false picture_loader = false
# picture_loader.worker = false picture_loader.worker = false
# picture_loader.card_back_cache_fail = false picture_loader.card_back_cache_fail = false
# picture_loader.picture_to_load = false picture_loader.picture_to_load = false
# deck_loader = false # deck_loader = false
# card_database = false # card_database = false
# card_database.loading = false card_database.loading = false
# card_database.loading.success_or_failure = false card_database.loading.success_or_failure = true
# cockatrice_xml.* = false cockatrice_xml.* = false
# cockatrice_xml.xml_3_parser = false # cockatrice_xml.xml_3_parser = false
# cockatrice_xml.xml_4_parser = false # cockatrice_xml.xml_4_parser = false
# card_info = false # card_info = false
@ -53,6 +56,8 @@ flow_layout.debug = false
flow_widget.debug = false flow_widget.debug = false
flow_widget.size.debug = false flow_widget.size.debug = false
overlap_layout.debug = false
# pixel_map_generator = false # pixel_map_generator = false
# filter_string = false # filter_string = false

View file

@ -33,9 +33,10 @@ OverlapLayout::OverlapLayout(QWidget *parent,
const int overlapPercentage, const int overlapPercentage,
const int maxColumns, const int maxColumns,
const int maxRows, const int maxRows,
const Qt::Orientation direction) const Qt::Orientation _overlapDirection,
const Qt::Orientation _flowDirection)
: QLayout(parent), overlapPercentage(overlapPercentage), maxColumns(maxColumns), maxRows(maxRows), : QLayout(parent), overlapPercentage(overlapPercentage), maxColumns(maxColumns), maxRows(maxRows),
direction(direction) overlapDirection(_overlapDirection), flowDirection(_flowDirection)
{ {
} }
@ -149,17 +150,17 @@ void OverlapLayout::setGeometry(const QRect &rect)
} }
// Calculate the overlap offsets based on the layout direction and overlap percentage. // Calculate the overlap offsets based on the layout direction and overlap percentage.
const int overlapOffsetWidth = (direction == Qt::Horizontal) ? (maxItemWidth * overlapPercentage / 100) : 0; const int overlapOffsetWidth = (overlapDirection == Qt::Horizontal) ? (maxItemWidth * overlapPercentage / 100) : 0;
const int overlapOffsetHeight = (direction == Qt::Vertical) ? (maxItemHeight * 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. // Determine the number of columns based on layout constraints and available space.
int columns; int columns;
if (direction == Qt::Horizontal) { if (flowDirection == Qt::Horizontal) {
if (maxColumns > 0) { if (maxColumns > 0) {
// Calculate the maximum possible columns given the available width and overlap. // Calculate the maximum possible columns given the available width and overlap.
const int availableColumns = (availableWidth + overlapOffsetWidth) / (maxItemWidth - overlapOffsetWidth); const int availableColumns = (availableWidth + overlapOffsetWidth) / (maxItemWidth - overlapOffsetWidth);
// Use the smaller of maxColumns and availableColumns. // Use the smaller of maxColumns and availableColumns.
qDebug() << " Max Columns " << maxColumns << " available columns " << availableColumns; qCDebug(OverlapLayoutLog) << " Max Columns " << maxColumns << " available columns " << availableColumns;
columns = qMin(maxColumns, availableColumns); columns = qMin(maxColumns, availableColumns);
} else { } else {
// If no maxColumns constraint, allow as many columns as possible. // If no maxColumns constraint, allow as many columns as possible.
@ -172,12 +173,12 @@ void OverlapLayout::setGeometry(const QRect &rect)
// Determine the number of rows based on layout constraints and available space. // Determine the number of rows based on layout constraints and available space.
int rows; int rows;
if (direction == Qt::Vertical) { if (flowDirection == Qt::Vertical) {
if (maxRows > 0) { if (maxRows > 0) {
// Calculate the maximum possible rows given the available height and overlap. // Calculate the maximum possible rows given the available height and overlap.
const int availableRows = (availableHeight + overlapOffsetHeight) / (maxItemHeight - overlapOffsetHeight); const int availableRows = (availableHeight + overlapOffsetHeight) / (maxItemHeight - overlapOffsetHeight);
// Use the smaller of maxRows and availableRows. // Use the smaller of maxRows and availableRows.
qDebug() << " Max Rows " << maxRows << " available rows " << availableRows; qCDebug(OverlapLayoutLog) << " Max Rows " << maxRows << " available rows " << availableRows;
rows = qMin(maxRows, availableRows); rows = qMin(maxRows, availableRows);
} else { } else {
// If no maxRows constraint, allow as many rows as possible. // If no maxRows constraint, allow as many rows as possible.
@ -204,7 +205,7 @@ void OverlapLayout::setGeometry(const QRect &rect)
item->setGeometry(QRect(xPos, yPos, maxItemWidth, maxItemHeight)); item->setGeometry(QRect(xPos, yPos, maxItemWidth, maxItemHeight));
// Update row and column indices based on the layout direction. // Update row and column indices based on the layout direction.
if (direction == Qt::Horizontal) { if (flowDirection == Qt::Horizontal) {
currentColumn++; currentColumn++;
if (currentColumn >= columns) { if (currentColumn >= columns) {
currentColumn = 0; currentColumn = 0;
@ -226,88 +227,82 @@ void OverlapLayout::setGeometry(const QRect &rect)
*/ */
QSize OverlapLayout::calculatePreferredSize() const QSize OverlapLayout::calculatePreferredSize() const
{ {
if (itemList.isEmpty()) {
return QSize(0, 0);
}
// Determine the maximum item dimensions.
int maxItemWidth = 0; int maxItemWidth = 0;
int maxItemHeight = 0; int maxItemHeight = 0;
for (QLayoutItem *item : itemList) {
if (item == nullptr) {
continue;
}
if (!item->widget()) {
continue;
}
QSize itemSize = item->widget()->sizeHint(); for (const auto &item : itemList) {
maxItemWidth = qMax(maxItemWidth, itemSize.width()); if (item) {
maxItemHeight = qMax(maxItemHeight, itemSize.height()); int itemWidth = item->sizeHint().width();
int itemHeight = item->sizeHint().height();
maxItemWidth = qMax(maxItemWidth, itemWidth);
maxItemHeight = qMax(maxItemHeight, itemHeight);
}
} }
// Calculate the overlap offsets. // Compute overlap offsets
const int extra_for_overlap = (100 - overlapPercentage);
const int overlapOffsetWidth = const int overlapOffsetWidth =
(direction == Qt::Horizontal) ? qRound((maxItemWidth / 100.0) * extra_for_overlap) : 0; (overlapDirection == Qt::Horizontal) ? qRound(maxItemWidth * (overlapPercentage / 100.0)) : 0;
const int overlapOffsetHeight = const int overlapOffsetHeight =
(direction == Qt::Vertical) ? qRound((maxItemHeight / 100.0) * extra_for_overlap) : 0; (overlapDirection == Qt::Vertical) ? qRound(maxItemHeight * (overlapPercentage / 100.0)) : 0;
// Variables to hold the total dimensions based on layout orientation. int numWidgets = itemList.size();
int totalWidth = 0; int numColumns = 1;
int totalHeight = 0; int numRows = 1;
// Calculate the total size based on the layout direction and constraints. if (flowDirection == Qt::Horizontal) {
if (direction == Qt::Horizontal) { if (maxColumns > 0) {
// Determine the number of columns: // Calculate the number of columns based on maxColumns and available width
// - Use maxColumns if it is greater than 0 (constraint set by the user). const int availableWidth = parentWidget()->width() - parentWidget()->contentsMargins().left() -
// - Otherwise, set the number of columns to the total number of items in the layout. parentWidget()->contentsMargins().right();
const int numColumns = (maxColumns > 0) ? static_cast<int>(maxColumns) : static_cast<int>(itemList.size()); const int availableColumns = (availableWidth + overlapOffsetWidth) / (maxItemWidth - overlapOffsetWidth);
numColumns = qMin(maxColumns, availableColumns);
} else {
// If no maxColumns constraint, allow as many columns as possible
numColumns = numWidgets;
}
// Calculate the extra space required for overlaps between columns: if (numColumns == 0) {
// - Each overlap reduces the effective width of subsequent items. numColumns = 1;
const int extra_space_for_overlaps = overlapOffsetWidth * (numColumns - 1); }
// Total width: // Calculate the number of rows by dividing the number of widgets by the number of columns
// - The first item's width is fully included (maxItemWidth). numRows = (numWidgets + numColumns - 1) / numColumns; // Correct rounding up
// - Add the space for all overlaps between adjacent items. } else if (flowDirection == Qt::Vertical) {
totalWidth = maxItemWidth + extra_space_for_overlaps; if (maxRows > 0) {
// Calculate the number of rows based on maxRows and available height
const int availableHeight = parentWidget()->height() - parentWidget()->contentsMargins().top() -
parentWidget()->contentsMargins().bottom();
const int availableRows = (availableHeight + overlapOffsetHeight) / (maxItemHeight - overlapOffsetHeight);
numRows = qMin(maxRows, availableRows);
} else {
// If no maxRows constraint, allow as many rows as needed
numRows = numWidgets;
}
// Determine the number of rows: if (numRows == 0) {
// - Use maxRows if maxColumns is set (to constrain the number of rows). numRows = 1;
// - Otherwise, assume a single row. }
const int numRows =
(maxColumns > 0) ? qCeil(static_cast<double>(itemList.size()) / static_cast<double>(numColumns)) : 1;
// Total height: // Calculate the number of columns by dividing the number of widgets by the number of rows
// - Multiply the number of rows by the item height (maxItemHeight). numColumns = (numWidgets + numRows - 1) / numRows; // Correct rounding up
// - Subtract the height overlap between rows to avoid double-counting.
totalHeight = maxItemHeight * numRows - (numRows - 1) * overlapOffsetHeight;
} else if (direction == Qt::Vertical) {
// Determine the number of columns:
// - Use maxRows to calculate how many columns are needed if a row constraint exists.
// - Otherwise, assume a single column.
const int numColumns =
(maxRows != 0) ? qCeil(static_cast<double>(itemList.size()) / static_cast<double>(maxRows)) : 1;
// Total width:
// - Multiply the number of columns by the item width (maxItemWidth).
// - Subtract the width overlap between columns to avoid double-counting.
totalWidth = maxItemWidth * numColumns - (numColumns - 1) * overlapOffsetWidth;
// Determine the number of rows:
// - Use maxRows if it is greater than 0 (constraint set by the user).
// - Otherwise, set the number of rows to the total number of items in the layout.
const int numRows = (maxRows > 0) ? static_cast<int>(maxRows) : static_cast<int>(itemList.size());
// Calculate the extra space required for overlaps between rows:
// - Each overlap reduces the effective height of subsequent items.
const int extraSpaceForOverlaps = overlapOffsetHeight * (numRows - 1);
// Total height:
// - The first item's height is fully included (maxItemHeight).
// - Add the space for all overlaps between adjacent items.
totalHeight = maxItemHeight + extraSpaceForOverlaps;
} }
return {totalWidth, totalHeight}; bool hasPartialRow = (numWidgets % numColumns != 0); // True if last row is incomplete
int totalWidth = maxItemWidth + ((numColumns - 1) * (maxItemWidth - overlapOffsetWidth));
int totalHeight = maxItemHeight + ((numRows - 1) * (maxItemHeight - overlapOffsetHeight));
if (hasPartialRow && overlapDirection == Qt::Horizontal) {
totalHeight += overlapOffsetHeight; // Ensure last row gets full space
} else if (hasPartialRow && overlapDirection == Qt::Vertical) {
totalWidth += overlapOffsetWidth; // Ensure last column gets full space
}
return QSize(totalWidth, totalHeight);
} }
/** /**
@ -344,7 +339,7 @@ QSize OverlapLayout::minimumSize() const
*/ */
void OverlapLayout::setDirection(const Qt::Orientation _direction) void OverlapLayout::setDirection(const Qt::Orientation _direction)
{ {
direction = _direction; overlapDirection = _direction;
} }
/** /**
@ -381,7 +376,7 @@ void OverlapLayout::setMaxRows(const int _maxRows)
*/ */
int OverlapLayout::calculateMaxColumns() const int OverlapLayout::calculateMaxColumns() const
{ {
if (direction != Qt::Vertical || itemList.isEmpty()) { if (overlapDirection != Qt::Vertical || itemList.isEmpty()) {
return 1; // Only relevant if the layout direction is vertical return 1; // Only relevant if the layout direction is vertical
} }
@ -413,7 +408,7 @@ int OverlapLayout::calculateMaxColumns() const
*/ */
int OverlapLayout::calculateRowsForColumns(const int columns) const int OverlapLayout::calculateRowsForColumns(const int columns) const
{ {
if (direction != Qt::Vertical || itemList.isEmpty() || columns <= 0) { if (overlapDirection != Qt::Vertical || itemList.isEmpty() || columns <= 0) {
return 1; // Only relevant if the layout direction is vertical and there are items return 1; // Only relevant if the layout direction is vertical and there are items
} }
@ -432,7 +427,7 @@ int OverlapLayout::calculateRowsForColumns(const int columns) const
*/ */
int OverlapLayout::calculateMaxRows() const int OverlapLayout::calculateMaxRows() const
{ {
if (direction != Qt::Horizontal || itemList.isEmpty()) { if (overlapDirection != Qt::Horizontal || itemList.isEmpty()) {
return 1; // Only relevant if the layout direction is horizontal return 1; // Only relevant if the layout direction is horizontal
} }
@ -467,7 +462,7 @@ int OverlapLayout::calculateMaxRows() const
*/ */
int OverlapLayout::calculateColumnsForRows(const int rows) const int OverlapLayout::calculateColumnsForRows(const int rows) const
{ {
if (direction != Qt::Horizontal || itemList.isEmpty() || rows <= 0) { if (overlapDirection != Qt::Horizontal || itemList.isEmpty() || rows <= 0) {
return 1; // Only relevant if the layout direction is horizontal and there are items return 1; // Only relevant if the layout direction is horizontal and there are items
} }

View file

@ -3,8 +3,11 @@
#include <QLayout> #include <QLayout>
#include <QList> #include <QList>
#include <QLoggingCategory>
#include <QWidget> #include <QWidget>
inline Q_LOGGING_CATEGORY(OverlapLayoutLog, "overlap_layout");
class OverlapLayout : public QLayout class OverlapLayout : public QLayout
{ {
public: public:
@ -12,7 +15,8 @@ public:
int overlapPercentage = 10, int overlapPercentage = 10,
int maxColumns = 2, int maxColumns = 2,
int maxRows = 2, int maxRows = 2,
Qt::Orientation direction = Qt::Horizontal); Qt::Orientation overlapDirection = Qt::Vertical,
Qt::Orientation flowDirection = Qt::Horizontal);
~OverlapLayout(); ~OverlapLayout();
void addItem(QLayoutItem *item) override; void addItem(QLayoutItem *item) override;
@ -35,7 +39,8 @@ private:
int overlapPercentage; int overlapPercentage;
int maxColumns; int maxColumns;
int maxRows; int maxRows;
Qt::Orientation direction; Qt::Orientation overlapDirection;
Qt::Orientation flowDirection;
// Calculate the preferred size of the layout // Calculate the preferred size of the layout
QSize calculatePreferredSize() const; QSize calculatePreferredSize() const;

View file

@ -25,7 +25,7 @@ void DeckCardZoneDisplayWidget::resizeEvent(QResizeEvent *event)
for (QObject *child : layout->children()) { for (QObject *child : layout->children()) {
QWidget *widget = qobject_cast<QWidget *>(child); QWidget *widget = qobject_cast<QWidget *>(child);
if (widget) { if (widget) {
widget->resize(event->size()); widget->setMaximumWidth(width());
} }
} }
} }

View file

@ -41,8 +41,7 @@ OverlapWidget::OverlapWidget(QWidget *parent,
: QWidget(parent), overlapPercentage(overlapPercentage), maxColumns(maxColumns), maxRows(maxRows), : QWidget(parent), overlapPercentage(overlapPercentage), maxColumns(maxColumns), maxRows(maxRows),
direction(direction), adjustOnResize(adjustOnResize) direction(direction), adjustOnResize(adjustOnResize)
{ {
this->setMinimumSize(0, 0); overlapLayout = new OverlapLayout(this, overlapPercentage, maxColumns, maxRows, direction, Qt::Horizontal);
overlapLayout = new OverlapLayout(this, overlapPercentage, maxColumns, maxRows, direction);
this->setLayout(overlapLayout); this->setLayout(overlapLayout);
} }

View file

@ -89,6 +89,7 @@ void VisualDeckEditorWidget::updateDisplay()
// Clear the layout first // Clear the layout first
populateCards(); populateCards();
zoneContainer = new QWidget(scrollArea); zoneContainer = new QWidget(scrollArea);
zoneContainer->setMaximumWidth(scrollArea->viewport()->width());
zoneContainerLayout = new QVBoxLayout(zoneContainer); zoneContainerLayout = new QVBoxLayout(zoneContainer);
DeckCardZoneDisplayWidget *mainBoardWidget = DeckCardZoneDisplayWidget *mainBoardWidget =