* 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:
ebbit1q 2025-09-20 14:35:52 +02:00 committed by GitHub
parent f484c98152
commit 17dcaf9afa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
337 changed files with 728 additions and 721 deletions

View file

@ -0,0 +1,186 @@
/**
* @file flow_widget.cpp
* @brief Implementation of the FlowWidget class for organizing widgets in a flow layout within a scrollable area.
*/
#include "flow_widget.h"
#include <QHBoxLayout>
#include <QResizeEvent>
#include <QWidget>
#include <qscrollarea.h>
#include <qsizepolicy.h>
/**
* @brief Constructs a FlowWidget with a scrollable layout.
*
* @param parent The parent widget of this FlowWidget.
* @param horizontalPolicy The horizontal scroll bar policy for the scroll area.
* @param verticalPolicy The vertical scroll bar policy for the scroll area.
*/
FlowWidget::FlowWidget(QWidget *parent,
const Qt::Orientation _flowDirection,
const Qt::ScrollBarPolicy horizontalPolicy,
const Qt::ScrollBarPolicy verticalPolicy)
: QWidget(parent), flowDirection(_flowDirection)
{
// Main Widget and Layout
if (_flowDirection == Qt::Horizontal) {
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
setMinimumWidth(0);
} else {
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
setMinimumHeight(0);
}
mainLayout = new QHBoxLayout(this);
setLayout(mainLayout);
if (horizontalPolicy != Qt::ScrollBarAlwaysOff || verticalPolicy != Qt::ScrollBarAlwaysOff) {
// Scroll Area, which should expand as much as possible, since it should be the only direct child widget.
scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
scrollArea->setMinimumSize(0, 0);
scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// Set scrollbar policies
scrollArea->setHorizontalScrollBarPolicy(horizontalPolicy);
scrollArea->setVerticalScrollBarPolicy(verticalPolicy);
} else {
scrollArea = nullptr;
}
// Flow Layout inside the scroll area
if (horizontalPolicy == Qt::ScrollBarAlwaysOff && verticalPolicy == Qt::ScrollBarAlwaysOff) {
container = new QWidget(this);
} else {
container = new QWidget(scrollArea);
}
flowLayout = new FlowLayout(container, flowDirection);
container->setLayout(flowLayout);
// The container should expand as much as possible, trusting the scrollArea to constrain it.
if (_flowDirection == Qt::Horizontal) {
container->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
container->setMinimumWidth(0);
} else {
container->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
container->setMinimumHeight(0);
}
// Use the FlowLayout container directly if we disable the ScrollArea
if (horizontalPolicy == Qt::ScrollBarAlwaysOff && verticalPolicy == Qt::ScrollBarAlwaysOff) {
mainLayout->addWidget(container);
} else {
scrollArea->setWidget(container);
mainLayout->addWidget(scrollArea);
}
}
/**
* @brief Adds a widget to the flow layout within the FlowWidget.
*
* Adjusts the widget's size policy based on the scroll bar policies.
*
* @param widget_to_add The widget to add to the flow layout.
*/
void FlowWidget::addWidget(QWidget *widget_to_add) const
{
flowLayout->addWidget(widget_to_add);
}
void FlowWidget::insertWidgetAtIndex(QWidget *toInsert, int index)
{
flowLayout->insertWidgetAtIndex(toInsert, index);
update();
}
void FlowWidget::removeWidget(QWidget *widgetToRemove) const
{
flowLayout->removeWidget(widgetToRemove);
}
/**
* @brief Clears all widgets from the flow layout.
*
* Deletes each widget and layout item, and recreates the flow layout if it was removed.
*/
void FlowWidget::clearLayout()
{
if (flowLayout != nullptr) {
QLayoutItem *item;
while ((item = flowLayout->takeAt(0)) != nullptr) {
item->widget()->deleteLater(); // Delete the widget
delete item; // Delete the layout item
}
} else {
flowLayout = new FlowLayout(container, flowDirection);
container->setLayout(flowLayout);
}
}
/**
* @brief Handles resize events for the FlowWidget.
*
* Triggers layout recalculation and adjusts the scroll area content size.
*
* @param event The resize event containing the new size information.
*/
void FlowWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
qCDebug(FlowWidgetSizeLog) << event->size();
// Trigger the layout to recalculate
if (flowLayout != nullptr) {
flowLayout->invalidate(); // Marks the layout as dirty and requires recalculation
flowLayout->activate(); // Recalculate the layout based on the new size
}
// Ensure the scroll area and its content adjust correctly
if (scrollArea != nullptr && scrollArea->widget() != nullptr) {
qCDebug(FlowWidgetSizeLog) << "Got a scrollarea: " << scrollArea->widget()->size();
scrollArea->widget()->adjustSize();
} else {
container->adjustSize();
}
}
/**
* @brief Sets the minimum size for all widgets inside the FlowWidget to the maximum sizeHint of all of them.
*/
void FlowWidget::setMinimumSizeToMaxSizeHint()
{
QSize maxSize(0, 0); // Initialize to a zero size
// Iterate over all widgets in the flow layout to find the maximum sizeHint
for (int i = 0; i < flowLayout->count(); ++i) {
if (QLayoutItem *item = flowLayout->itemAt(i)) {
if (QWidget *widget = item->widget()) {
// Update the max size based on the sizeHint of each widget
QSize widgetSizeHint = widget->sizeHint();
maxSize.setWidth(qMax(maxSize.width(), widgetSizeHint.width()));
maxSize.setHeight(qMax(maxSize.height(), widgetSizeHint.height()));
}
}
}
// Set the minimum size for all widgets to the max sizeHint
for (int i = 0; i < flowLayout->count(); ++i) {
if (QLayoutItem *item = flowLayout->itemAt(i)) {
if (QWidget *widget = item->widget()) {
widget->setMinimumSize(maxSize);
}
}
}
}
QLayoutItem *FlowWidget::itemAt(int index) const
{
return flowLayout->itemAt(index);
}
int FlowWidget::count() const
{
return flowLayout->count();
}

View file

@ -0,0 +1,44 @@
#ifndef FLOW_WIDGET_H
#define FLOW_WIDGET_H
#include "../../../layouts/flow_layout.h"
#include <QHBoxLayout>
#include <QLoggingCategory>
#include <QWidget>
#include <qscrollarea.h>
inline Q_LOGGING_CATEGORY(FlowWidgetLog, "flow_widget", QtInfoMsg);
inline Q_LOGGING_CATEGORY(FlowWidgetSizeLog, "flow_widget.size", QtInfoMsg);
class FlowWidget final : public QWidget
{
Q_OBJECT
public:
FlowWidget(QWidget *parent,
Qt::Orientation orientation,
Qt::ScrollBarPolicy horizontalPolicy,
Qt::ScrollBarPolicy verticalPolicy);
void addWidget(QWidget *widget_to_add) const;
void insertWidgetAtIndex(QWidget *toInsert, int index);
void removeWidget(QWidget *widgetToRemove) const;
void clearLayout();
[[nodiscard]] int count() const;
[[nodiscard]] QLayoutItem *itemAt(int index) const;
QScrollArea *scrollArea;
public slots:
void setMinimumSizeToMaxSizeHint();
protected:
void resizeEvent(QResizeEvent *event) override;
private:
Qt::Orientation flowDirection;
QHBoxLayout *mainLayout;
FlowLayout *flowLayout;
QWidget *container;
};
#endif // FLOW_WIDGET_H

View file

@ -0,0 +1,46 @@
#include "overlap_control_widget.h"
#include "overlap_widget.h"
OverlapControlWidget::OverlapControlWidget(int overlapPercentage,
int maxColumns,
int maxRows,
Qt::Orientation direction,
QWidget *parent)
: QWidget(parent), overlapPercentage(overlapPercentage), maxColumns(maxColumns), maxRows(maxRows),
direction(direction)
{
// Main Widget and Layout
this->setMinimumSize(0, 100);
// this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// this->setStyleSheet("border: 10px solid red;");
layout = new QHBoxLayout(this);
this->setLayout(layout);
card_size_slider = new QSlider(Qt::Horizontal);
card_size_slider->setRange(1, 10); // Example range for scaling, adjust as needed
amount_of_items_to_overlap = new LabeledInput(this, tr("Cards to overlap:"));
amount_of_items_to_overlap->addSpinBox(0, 999, 10);
overlap_percentage_input = new LabeledInput(this, tr("Overlap percentage:"));
overlap_percentage_input->addSpinBox(0, 100, 80);
overlap_direction = new LabeledInput(this, tr("Overlap direction:"));
overlap_direction->addDirectionComboBox();
layout->addWidget(card_size_slider);
layout->addWidget(amount_of_items_to_overlap);
layout->addWidget(overlap_percentage_input);
layout->addWidget(overlap_direction);
// TODO probably connect this to the parent
// connect(card_size_slider, &QSlider::valueChanged, display, &CardPicture::setScaleFactor);
}
void OverlapControlWidget::connectOverlapWidget(OverlapWidget *overlap_widget)
{
connect(amount_of_items_to_overlap, &LabeledInput::spinBoxValueChanged, overlap_widget,
&OverlapWidget::maxOverlapItemsChanged);
connect(overlap_direction, &LabeledInput::directionComboBoxChanged, overlap_widget,
&OverlapWidget::overlapDirectionChanged);
}

View file

@ -0,0 +1,34 @@
#ifndef OVERLAP_CONTROL_WIDGET_H
#define OVERLAP_CONTROL_WIDGET_H
#include "../display/labeled_input.h"
#include "overlap_widget.h"
#include <QHBoxLayout>
#include <QSlider>
#include <QWidget>
class OverlapControlWidget final : public QWidget
{
Q_OBJECT
public:
OverlapControlWidget(int overlapPercentage,
int maxColumns,
int maxRows,
Qt::Orientation direction,
QWidget *parent);
void connectOverlapWidget(OverlapWidget *overlap_widget);
private:
QHBoxLayout *layout;
QSlider *card_size_slider;
LabeledInput *amount_of_items_to_overlap;
LabeledInput *overlap_percentage_input;
LabeledInput *overlap_direction;
int overlapPercentage;
int maxColumns;
int maxRows;
Qt::Orientation direction;
};
#endif // OVERLAP_CONTROL_WIDGET_H

View file

@ -0,0 +1,204 @@
#include "overlap_widget.h"
#include "../../../../deck/deck_list_model.h"
#include "../../../layouts/flow_layout.h"
#include <QWidget>
/**
* @class OverlapWidget
* @brief A widget for managing overlapping child widgets.
*
* The OverlapWidget class is a QWidget subclass that utilizes the OverlapLayout
* to arrange its child widgets in an overlapping manner. This widget allows
* configuration of overlap percentage, maximum columns, maximum rows, and layout
* direction, making it suitable for displaying elements that can partially stack
* over each other. The widget automatically manages resizing and re-layout of its
* child widgets based on the available space and specified parameters.
*/
/**
* @brief Constructs an OverlapWidget with specified layout parameters.
*
* Initializes the OverlapWidget with the given overlap percentage, maximum number
* of columns and rows, and layout direction. Sets size policies to ensure the widget
* can expand as needed. A new OverlapLayout is created and assigned to manage the
* layout of child widgets.
*
* @param overlapPercentage The percentage of overlap between child widgets (0-100).
* @param maxColumns The maximum number of columns for the layout (0 for unlimited).
* @param maxRows The maximum number of rows for the layout (0 for unlimited).
* @param direction The orientation of the layout, either Qt::Horizontal or Qt::Vertical.
* @param adjustOnResize If the overlap widgets should adjust its max columns/rows on resize to fit.
* @param parent The parent widget of this OverlapWidget.
*/
OverlapWidget::OverlapWidget(QWidget *parent,
const int overlapPercentage,
const int maxColumns,
const int maxRows,
const Qt::Orientation direction,
const bool adjustOnResize)
: QWidget(parent), overlapPercentage(overlapPercentage), maxColumns(maxColumns), maxRows(maxRows),
direction(direction), adjustOnResize(adjustOnResize)
{
overlapLayout = new OverlapLayout(this, overlapPercentage, maxColumns, maxRows, direction, Qt::Horizontal);
setLayout(overlapLayout);
}
/**
* @brief Adds a widget to the overlap layout.
*
* This method appends the specified widget to the internal OverlapLayout, allowing
* it to be arranged with the existing child widgets. The widget's visibility and
* behavior will be managed by the layout.
*
* @param widgetToAdd A pointer to the QWidget to be added to the layout.
*/
void OverlapWidget::addWidget(QWidget *widgetToAdd) const
{
overlapLayout->addWidget(widgetToAdd);
}
void OverlapWidget::insertWidgetAtIndex(QWidget *toInsert, int index)
{
overlapLayout->insertWidgetAtIndex(toInsert, index);
update();
}
void OverlapWidget::removeWidget(QWidget *widgetToRemove) const
{
overlapLayout->removeWidget(widgetToRemove);
}
/**
* @brief Clears all widgets from the layout and deletes them.
*
* This method removes all child widgets from the OverlapLayout, deleting both the
* widget instances and their corresponding layout items. This ensures that the layout
* is empty and can be reused or refreshed as needed.
*/
void OverlapWidget::clearLayout()
{
if (overlapLayout != nullptr) {
QLayoutItem *item;
while ((item = overlapLayout->takeAt(0)) != nullptr) {
item->widget()->deleteLater();
delete item;
}
}
// If layout is null, create a new layout; otherwise, reuse the existing one
if (overlapLayout == nullptr) {
overlapLayout = new OverlapLayout(this, overlapPercentage, maxColumns, maxRows, direction);
this->setLayout(overlapLayout);
}
}
/**
* @brief Handles resizing events for the widget.
*
* This overridden method is called when the widget is resized. It invokes layout
* recalculation to ensure that the child widgets are correctly arranged based on the
* new dimensions. It marks the layout as dirty and activates it to reflect the changes.
*
* @param event The resize event containing the new size information.
*/
void OverlapWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
// Trigger the layout to recalculate
if (overlapLayout != nullptr) {
overlapLayout->invalidate(); // Marks the layout as dirty and requires recalculation
overlapLayout->activate(); // Recalculate the layout based on the new size
}
if (adjustOnResize) {
adjustMaxColumnsAndRows();
}
}
/**
* @brief Dynamically adjusts maxColumns and maxRows based on widget size and layout direction.
*
* This function calculates the maximum number of columns or rows that can fit within
* the widget's width and height, depending on the layout direction. It then updates
* the OverlapLayout with these calculated values to ensure the layout is optimized.
*/
void OverlapWidget::adjustMaxColumnsAndRows()
{
if (direction == Qt::Vertical) {
// Calculate columns based on width for vertical layout
const int calculatedColumns = overlapLayout->calculateMaxColumns();
maxColumns = calculatedColumns;
overlapLayout->setMaxColumns(calculatedColumns);
// Calculate rows based on total item count and columns
const int calculatedRows = overlapLayout->calculateRowsForColumns(calculatedColumns);
maxRows = calculatedRows;
overlapLayout->setMaxRows(calculatedRows);
} else {
// Calculate rows based on height for horizontal layout
const int calculatedRows = overlapLayout->calculateMaxRows();
maxRows = calculatedRows;
overlapLayout->setMaxRows(calculatedRows);
// Calculate columns based on total item count and rows
const int calculatedColumns = overlapLayout->calculateColumnsForRows(calculatedRows);
maxColumns = calculatedColumns;
overlapLayout->setMaxColumns(calculatedColumns);
}
overlapLayout->invalidate();
overlapLayout->activate();
}
/**
* @brief Updates the maximum number of overlapping items based on new value.
*
* This method updates the maximum number of columns or rows for the overlap layout
* based on the given new value. It adjusts the layout direction accordingly and
* triggers a size adjustment for the widget, ensuring the layout reflects the changes.
*
* @param newValue The new maximum number of overlapping items allowed in the layout.
*/
void OverlapWidget::maxOverlapItemsChanged(const int newValue)
{
if (direction == Qt::Horizontal) {
maxRows = 0;
overlapLayout->setMaxRows(0);
maxColumns = newValue;
overlapLayout->setMaxColumns(newValue);
} else {
maxRows = newValue;
overlapLayout->setMaxRows(newValue);
maxColumns = 0;
overlapLayout->setMaxColumns(0);
}
this->adjustSize();
overlapLayout->invalidate();
}
/**
* @brief Changes the layout direction based on the specified new direction.
*
* This method modifies the layout direction of the OverlapLayout based on the input
* string. It updates the direction and triggers a size adjustment for the widget.
* Valid inputs are "Qt::Horizontal" and "Qt::Vertical".
*
* @param newDirection The new layout direction as a QString.
*/
void OverlapWidget::overlapDirectionChanged(const QString &newDirection)
{
if (newDirection.compare("Qt::Horizontal", Qt::CaseInsensitive) == 0) {
direction = Qt::Horizontal;
overlapLayout->setDirection(direction);
} else if (newDirection.compare("Qt::Vertical", Qt::CaseInsensitive) == 0) {
direction = Qt::Vertical;
overlapLayout->setDirection(direction);
}
this->adjustSize();
overlapLayout->invalidate();
}

View file

@ -0,0 +1,41 @@
#ifndef OVERLAP_WIDGET_H
#define OVERLAP_WIDGET_H
#include "../../../layouts/overlap_layout.h"
#include <QWidget>
class OverlapWidget final : public QWidget
{
Q_OBJECT
public:
OverlapWidget(QWidget *parent,
int overlapPercentage,
int maxColumns,
int maxRows,
Qt::Orientation direction,
bool adjustOnResize = false);
void addWidget(QWidget *widgetToAdd) const;
void insertWidgetAtIndex(QWidget *toInsert, int index);
void removeWidget(QWidget *widgetToRemove) const;
void clearLayout();
void adjustMaxColumnsAndRows();
public slots:
void maxOverlapItemsChanged(int newValue);
void overlapDirectionChanged(const QString &newDirection);
protected:
void resizeEvent(QResizeEvent *event) override;
private:
OverlapLayout *overlapLayout;
int overlapPercentage;
int maxColumns;
int maxRows;
Qt::Orientation direction;
bool adjustOnResize = false;
};
#endif // OVERLAP_WIDGET_H