From e174dc397f7f27ab7a281a83d5503a0dea646f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Thu, 14 May 2026 14:48:32 +0200 Subject: [PATCH] Make VDE tools flow Took 1 hour 23 minutes Took 5 seconds --- .../src/interface/layouts/flow_layout.cpp | 52 +++++++++++++++---- .../src/interface/layouts/flow_layout.h | 2 +- .../visual_deck_editor_widget.cpp | 28 ++++++---- .../visual_deck_editor_widget.h | 5 +- 4 files changed, 64 insertions(+), 23 deletions(-) diff --git a/cockatrice/src/interface/layouts/flow_layout.cpp b/cockatrice/src/interface/layouts/flow_layout.cpp index ed2e22c3f..36edd2c21 100644 --- a/cockatrice/src/interface/layouts/flow_layout.cpp +++ b/cockatrice/src/interface/layouts/flow_layout.cpp @@ -151,7 +151,7 @@ int FlowLayout::layoutAllRows(const int originX, const int originY, const int av if (!rowItems.isEmpty() && rowUsedWidth + spaceX + itemSize.width() > availableWidth) { // Current item does not fit — flush the current row, begin a new one. - layoutSingleRow(rowItems, originX, currentY); + layoutSingleRow(rowItems, originX, currentY, availableWidth); rowItems.clear(); currentY += rowHeight + verticalSpacing(); rowUsedWidth = 0; @@ -166,33 +166,67 @@ int FlowLayout::layoutAllRows(const int originX, const int originY, const int av rowHeight = qMax(rowHeight, itemSize.height()); } - layoutSingleRow(rowItems, originX, currentY); // Flush the final row. + layoutSingleRow(rowItems, originX, currentY, availableWidth); // Flush the final row. return currentY + rowHeight; } /** * @brief Sets the geometry for every item in @p rowItems, starting at (@p x, @p y). * - * Each item is placed at its sizeHint, clamped to its maximumSize. + * Items whose horizontal size policy includes the Expand or MinimumExpanding flag + * share the leftover row width proportionally (like QHBoxLayout stretch), so that + * e.g. a QLineEdit can fill remaining space while fixed-size buttons stay compact. + * + * Items without an expanding policy are placed at their sizeHint, clamped to maximumSize. */ -void FlowLayout::layoutSingleRow(const QVector &rowItems, int x, const int y) +void FlowLayout::layoutSingleRow(const QVector &rowItems, int x, const int y, const int availableWidth) { + if (rowItems.isEmpty()) + return; + + // ── Pass 1: measure fixed width and count expanding items ──────────────── + int fixedWidth = 0; + int expandingCount = 0; + int spacingTotal = (rowItems.size() - 1) * horizontalSpacing(); + for (QLayoutItem *item : rowItems) { if (!item || item->isEmpty()) { continue; } QWidget *widget = item->widget(); - if (!widget) { - continue; - } + const QSizePolicy::Policy hPolicy = widget ? widget->sizePolicy().horizontalPolicy() : QSizePolicy::Fixed; + if (hPolicy & QSizePolicy::ExpandFlag) { + ++expandingCount; + } else { + const int maxW = widget ? widget->maximumWidth() : QWIDGETSIZE_MAX; + fixedWidth += qMin(item->sizeHint().width(), maxW); + } + } + + // Extra pixels to share among expanding items (never negative). + const int extra = qMax(0, availableWidth - spacingTotal - fixedWidth); + const int expandingShare = (expandingCount > 0) ? extra / expandingCount : 0; + + // ── Pass 2: place items ────────────────────────────────────────────────── + for (QLayoutItem *item : rowItems) { + if (!item || item->isEmpty()) + continue; + + QWidget *widget = item->widget(); + if (!widget) + continue; + + const QSizePolicy::Policy hPolicy = widget->sizePolicy().horizontalPolicy(); const QSize maxSize = widget->maximumSize(); - const int itemWidth = qMin(item->sizeHint().width(), maxSize.width()); + const bool expands = hPolicy & QSizePolicy::ExpandFlag; + + const int itemWidth = + expands ? qMin(expandingShare, maxSize.width()) : qMin(item->sizeHint().width(), maxSize.width()); const int itemHeight = qMin(item->sizeHint().height(), maxSize.height()); item->setGeometry(QRect(QPoint(x, y), QSize(itemWidth, itemHeight))); - // Move the x-position to the right, leaving space for horizontal spacing x += itemWidth + horizontalSpacing(); } } diff --git a/cockatrice/src/interface/layouts/flow_layout.h b/cockatrice/src/interface/layouts/flow_layout.h index 701946f20..cc206afae 100644 --- a/cockatrice/src/interface/layouts/flow_layout.h +++ b/cockatrice/src/interface/layouts/flow_layout.h @@ -54,7 +54,7 @@ public: // Layout passes (virtual so subclasses can override placement logic) virtual int layoutAllRows(int originX, int originY, int availableWidth); - virtual void layoutSingleRow(const QVector &rowItems, int x, int y); + virtual void layoutSingleRow(const QVector &rowItems, int x, int y, int availableWidth); int layoutAllColumns(int originX, int originY, int availableHeight); void layoutSingleColumn(const QVector &colItems, int x, int y); diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp index e957eb304..f98daf7b1 100644 --- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.cpp @@ -69,7 +69,13 @@ VisualDeckEditorWidget::VisualDeckEditorWidget(QWidget *parent, void VisualDeckEditorWidget::initializeSearchBarAndCompleter() { - searchBar = new QLineEdit(this); + searchContainer = new QWidget(this); + searchContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + searchLayout = new QHBoxLayout(searchContainer); + searchContainer->setLayout(searchLayout); + + searchBar = new QLineEdit(searchContainer); + searchContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); connect(searchBar, &QLineEdit::returnPressed, this, [=, this]() { if (!searchBar->hasFocus()) return; @@ -80,6 +86,8 @@ void VisualDeckEditorWidget::initializeSearchBarAndCompleter() } }); + searchLayout->addWidget(searchBar); + setFocusProxy(searchBar); setFocusPolicy(Qt::ClickFocus); @@ -133,13 +141,15 @@ void VisualDeckEditorWidget::initializeSearchBarAndCompleter() }); // Search button functionality - searchPushButton = new QPushButton(this); + searchPushButton = new QPushButton(searchContainer); connect(searchPushButton, &QPushButton::clicked, this, [=, this]() { ExactCard card = CardDatabaseManager::query()->getCard({searchBar->text()}); if (card) { emit cardAdditionRequested(card); } }); + + searchLayout->addWidget(searchPushButton); } void VisualDeckEditorWidget::initializeDisplayOptionsWidget() @@ -156,18 +166,14 @@ void VisualDeckEditorWidget::initializeDisplayOptionsWidget() void VisualDeckEditorWidget::initializeDisplayOptionsAndSearchWidget() { initializeSearchBarAndCompleter(); - initializeDisplayOptionsWidget(); - displayOptionsAndSearch = new QWidget(this); - displayOptionsAndSearchLayout = new QHBoxLayout(displayOptionsAndSearch); - displayOptionsAndSearchLayout->setContentsMargins(0, 0, 0, 0); - displayOptionsAndSearchLayout->setAlignment(Qt::AlignLeft); - displayOptionsAndSearch->setLayout(displayOptionsAndSearchLayout); + displayOptionsAndSearch = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff); - displayOptionsAndSearchLayout->addWidget(displayOptionsWidget); - displayOptionsAndSearchLayout->addWidget(searchBar); - displayOptionsAndSearchLayout->addWidget(searchPushButton); + // We split into two sub-widgets here so that the searchBar and button wrap together. At this point, we've done + // pretty much all we can and have reached our minimum size. + displayOptionsAndSearch->addWidget(displayOptionsWidget); + displayOptionsAndSearch->addWidget(searchContainer); // Expanding — fills remainder of its row } void VisualDeckEditorWidget::initializeScrollAreaAndZoneContainer() diff --git a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.h b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.h index 13065d623..02501d8e8 100644 --- a/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.h +++ b/cockatrice/src/interface/widgets/visual_deck_editor/visual_deck_editor_widget.h @@ -90,9 +90,10 @@ private: CardDatabaseModel *cardDatabaseModel; CardDatabaseDisplayModel *cardDatabaseDisplayModel; CardCompleterProxyModel *proxyModel; + QWidget *searchContainer; + QHBoxLayout *searchLayout; QCompleter *completer; - QWidget *displayOptionsAndSearch; - QHBoxLayout *displayOptionsAndSearchLayout; + FlowWidget *displayOptionsAndSearch; VisualDeckDisplayOptionsWidget *displayOptionsWidget; QPushButton *searchPushButton; QScrollArea *scrollArea;