mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-11 16:44:48 -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
|
|
@ -0,0 +1,3 @@
|
|||
#include "background_sources.h"
|
||||
|
||||
// Required so moc generates Q_OBJECT macros
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
#ifndef COCKATRICE_BACKGROUND_SOURCES_H
|
||||
#define COCKATRICE_BACKGROUND_SOURCES_H
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class BackgroundSources
|
||||
{
|
||||
Q_GADGET
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Theme,
|
||||
RandomCardArt,
|
||||
DeckFileArt
|
||||
};
|
||||
Q_ENUM(Type)
|
||||
|
||||
struct Entry
|
||||
{
|
||||
Type type;
|
||||
const char *id; // stable ID for settings
|
||||
const char *trKey; // key for translation
|
||||
};
|
||||
|
||||
static QList<Entry> all()
|
||||
{
|
||||
return {{Theme, "theme", QT_TR_NOOP("Theme")},
|
||||
{RandomCardArt, "random_card_art", QT_TR_NOOP("Art crop of random card")},
|
||||
{DeckFileArt, "deck_file_art", QT_TR_NOOP("Art crop of background.cod deck file")}};
|
||||
}
|
||||
|
||||
static QString toId(Type type)
|
||||
{
|
||||
for (const auto &e : all()) {
|
||||
if (e.type == type)
|
||||
return e.id;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static Type fromId(const QString &id)
|
||||
{
|
||||
for (const auto &e : all()) {
|
||||
if (id == e.id)
|
||||
return e.type;
|
||||
}
|
||||
return Theme; // default
|
||||
}
|
||||
|
||||
static QString toDisplay(Type type)
|
||||
{
|
||||
for (const auto &e : all()) {
|
||||
if (e.type == type)
|
||||
return QObject::tr(e.trKey);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_BACKGROUND_SOURCES_H
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
#include "banner_widget.h"
|
||||
|
||||
#include "../../../pixel_map_generator.h"
|
||||
|
||||
#include <QLinearGradient>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
BannerWidget::BannerWidget(QWidget *parent, const QString &text, Qt::Orientation orientation, int transparency)
|
||||
: QWidget(parent), gradientOrientation(orientation), transparency(qBound(0, transparency, 100))
|
||||
{
|
||||
auto layout = new QHBoxLayout(this);
|
||||
|
||||
iconLabel = new QLabel(this);
|
||||
iconLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
|
||||
// Create the banner label and set properties
|
||||
bannerLabel = new QLabel(text, this);
|
||||
bannerLabel->setAlignment(Qt::AlignCenter);
|
||||
bannerLabel->setStyleSheet("font-size: 24px; font-weight: bold; color: white;");
|
||||
|
||||
layout->addWidget(iconLabel);
|
||||
layout->addWidget(bannerLabel);
|
||||
layout->addWidget(new QLabel(this)); // add dummy label to force text label to be centered
|
||||
setLayout(layout);
|
||||
|
||||
// Set minimum height for the widget
|
||||
setMinimumHeight(50);
|
||||
connect(this, &BannerWidget::buddyVisibilityChanged, this, &BannerWidget::toggleBuddyVisibility);
|
||||
|
||||
updateDropdownIconState();
|
||||
}
|
||||
|
||||
void BannerWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
QWidget::mousePressEvent(event);
|
||||
if (clickable) {
|
||||
emit buddyVisibilityChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void BannerWidget::setText(const QString &text) const
|
||||
{
|
||||
bannerLabel->setText(text);
|
||||
}
|
||||
|
||||
void BannerWidget::setClickable(bool _clickable)
|
||||
{
|
||||
clickable = _clickable;
|
||||
updateDropdownIconState();
|
||||
}
|
||||
|
||||
void BannerWidget::setBuddy(QWidget *_buddy)
|
||||
{
|
||||
buddy = _buddy;
|
||||
updateDropdownIconState();
|
||||
}
|
||||
|
||||
void BannerWidget::toggleBuddyVisibility() const
|
||||
{
|
||||
if (buddy) {
|
||||
buddy->setVisible(!buddy->isVisible());
|
||||
updateDropdownIconState();
|
||||
}
|
||||
}
|
||||
|
||||
void BannerWidget::updateDropdownIconState() const
|
||||
{
|
||||
if (clickable && buddy) {
|
||||
iconLabel->setPixmap(DropdownIconPixmapGenerator::generatePixmap(24, !buddy->isHidden()));
|
||||
} else {
|
||||
// we cannot directly hide the iconLabel, since it's needed to center the text; set an empty image instead
|
||||
iconLabel->setPixmap(QPixmap());
|
||||
}
|
||||
}
|
||||
|
||||
void BannerWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
QPainter painter(this);
|
||||
|
||||
// Calculate alpha based on transparency percentage
|
||||
int alpha = (255 * transparency) / 100;
|
||||
|
||||
// Determine gradient direction
|
||||
QLinearGradient gradient;
|
||||
if (gradientOrientation == Qt::Vertical) {
|
||||
gradient = QLinearGradient(rect().topLeft(), rect().bottomLeft());
|
||||
} else {
|
||||
gradient = QLinearGradient(rect().topLeft(), rect().topRight());
|
||||
}
|
||||
|
||||
// Set neutral gradient colors with calculated transparency
|
||||
gradient.setColorAt(0, QColor(200, 200, 200, alpha)); // Light grey with alpha
|
||||
gradient.setColorAt(1, QColor(100, 100, 100, alpha / 1.5)); // Darker grey, slightly more transparent
|
||||
|
||||
// Fill the widget background with the gradient
|
||||
painter.fillRect(rect(), gradient);
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef BANNER_WIDGET_H
|
||||
#define BANNER_WIDGET_H
|
||||
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class BannerWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BannerWidget(QWidget *parent,
|
||||
const QString &text,
|
||||
Qt::Orientation orientation = Qt::Vertical,
|
||||
int transparency = 80);
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void setText(const QString &text) const;
|
||||
void setClickable(bool _clickable);
|
||||
void setBuddy(QWidget *_buddy);
|
||||
QString getText() const
|
||||
{
|
||||
return bannerLabel->text();
|
||||
}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QLabel *iconLabel;
|
||||
QLabel *bannerLabel;
|
||||
Qt::Orientation gradientOrientation;
|
||||
int transparency; // Transparency percentage for the gradient
|
||||
QWidget *buddy = nullptr;
|
||||
bool clickable = true;
|
||||
signals:
|
||||
void buddyVisibilityChanged();
|
||||
private slots:
|
||||
void toggleBuddyVisibility() const;
|
||||
void updateDropdownIconState() const;
|
||||
};
|
||||
|
||||
#endif // BANNER_WIDGET_H
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
#include "bar_widget.h"
|
||||
|
||||
#include <QFontMetrics>
|
||||
#include <QPainter>
|
||||
|
||||
BarWidget::BarWidget(QString label, int value, int total, QColor barColor, QWidget *parent)
|
||||
: QWidget(parent), label(std::move(label)), value(value), total(total), barColor(barColor)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
}
|
||||
|
||||
QSize BarWidget::sizeHint() const
|
||||
{
|
||||
QFontMetrics metrics(font());
|
||||
int labelHeight = metrics.height();
|
||||
int valueHeight = metrics.height();
|
||||
|
||||
// Calculate the height dynamically based on the total
|
||||
int barHeight = (total > 0) ? (value * 200 / total) : 20; // Scale height proportionally
|
||||
int totalHeight = barHeight + labelHeight + valueHeight + 30; // Extra space for text
|
||||
return QSize(60, totalHeight); // Allow width to expand
|
||||
}
|
||||
|
||||
void BarWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
int widgetWidth = width();
|
||||
int widgetHeight = height();
|
||||
|
||||
// Calculate bar dimensions
|
||||
int barWidth = widgetWidth * 0.8; // Use 80% of the available width
|
||||
int fullBarHeight = widgetHeight - 40; // Leave space for labels
|
||||
int valueBarHeight = (total > 0) ? (value * fullBarHeight / total) : 0;
|
||||
|
||||
// Draw full bar background (gray)
|
||||
painter.setBrush(QColor(200, 200, 200));
|
||||
painter.drawRect((widgetWidth - barWidth) / 2, 10, barWidth, fullBarHeight);
|
||||
|
||||
// Draw the value-specific bar using the assigned color
|
||||
painter.setBrush(barColor);
|
||||
painter.drawRect((widgetWidth - barWidth) / 2, 10 + fullBarHeight - valueBarHeight, barWidth, valueBarHeight);
|
||||
|
||||
// Draw the CMC label
|
||||
painter.setPen(Qt::white);
|
||||
QRect textRect(0, widgetHeight - 30, widgetWidth, 20);
|
||||
painter.drawText(textRect, Qt::AlignCenter, label);
|
||||
|
||||
// Draw the value count
|
||||
painter.setPen(Qt::black);
|
||||
QRect valueRect(0, 10, widgetWidth, 20);
|
||||
painter.drawText(valueRect, Qt::AlignCenter, QString::number(value));
|
||||
|
||||
(void)event; // Suppress unused parameter warning
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef BAR_WIDGET_H
|
||||
#define BAR_WIDGET_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
class BarWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BarWidget(QString label, int value, int total, QColor barColor = Qt::blue, QWidget *parent = nullptr);
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QString label;
|
||||
int value;
|
||||
int total;
|
||||
QColor barColor; // Store the bar color
|
||||
};
|
||||
|
||||
#endif // BAR_WIDGET_H
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
#include "dynamic_font_size_label.h"
|
||||
#define FONT_PRECISION (0.5)
|
||||
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
DynamicFontSizeLabel::DynamicFontSizeLabel(QWidget *parent, Qt::WindowFlags f) : QLabel(parent, f)
|
||||
{
|
||||
setIndent(0);
|
||||
}
|
||||
|
||||
void DynamicFontSizeLabel::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
void DynamicFontSizeLabel::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
// QElapsedTimer timer;
|
||||
// timer.start();
|
||||
|
||||
QFont newFont = font();
|
||||
float fontSize = getWidgetMaximumFontSize(this, this->text());
|
||||
newFont.setPointSizeF(fontSize);
|
||||
setFont(newFont);
|
||||
// qDebug() << "Font size set to" << fontSize;
|
||||
|
||||
QLabel::paintEvent(event);
|
||||
// LOG(true, "Paint delay" << ((float)timer.nsecsElapsed())/1000000.0 << " mS");
|
||||
}
|
||||
|
||||
float DynamicFontSizeLabel::getWidgetMaximumFontSize(QWidget *widget, const QString &text)
|
||||
{
|
||||
QFont font = widget->font();
|
||||
const QRect widgetRect = widget->contentsRect();
|
||||
const float widgetWidth = widgetRect.width();
|
||||
const float widgetHeight = widgetRect.height();
|
||||
|
||||
QRectF newFontSizeRect;
|
||||
float currentSize = font.pointSizeF();
|
||||
|
||||
float step = currentSize / 2.0;
|
||||
|
||||
/* If too small, increase step */
|
||||
if (step <= FONT_PRECISION) {
|
||||
step = FONT_PRECISION * 4.0;
|
||||
}
|
||||
|
||||
float lastTestedSize = currentSize;
|
||||
|
||||
float currentHeight = 0;
|
||||
float currentWidth = 0;
|
||||
if (text == "") {
|
||||
return currentSize;
|
||||
}
|
||||
|
||||
if (currentSize < 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Only stop when step is small enough and new size is smaller than QWidget */
|
||||
while (step > FONT_PRECISION || (currentHeight > widgetHeight) || (currentWidth > widgetWidth)) {
|
||||
/* Keep last tested value */
|
||||
lastTestedSize = currentSize;
|
||||
|
||||
/* Test label with its font */
|
||||
font.setPointSizeF(currentSize);
|
||||
/* Use font metrics to test */
|
||||
QFontMetricsF fm(font);
|
||||
|
||||
/* Check if widget is QLabel */
|
||||
QLabel *label = qobject_cast<QLabel *>(widget);
|
||||
if (label) {
|
||||
newFontSizeRect =
|
||||
fm.boundingRect(widgetRect, (label->wordWrap() ? Qt::TextWordWrap : 0) | label->alignment(), text);
|
||||
} else {
|
||||
newFontSizeRect = fm.boundingRect(widgetRect, 0, text);
|
||||
}
|
||||
|
||||
currentHeight = newFontSizeRect.height();
|
||||
currentWidth = newFontSizeRect.width();
|
||||
|
||||
/* If new font size is too big, decrease it */
|
||||
if ((currentHeight > widgetHeight) || (currentWidth > widgetWidth)) {
|
||||
// qDebug() << "-- contentsRect()" << label->contentsRect() << "rect"<< label->rect() << " newFontSizeRect"
|
||||
// << newFontSizeRect << "Tight" << text << currentSize;
|
||||
currentSize -= step;
|
||||
/* if step is small enough, keep it constant, so it converge to biggest font size */
|
||||
if (step > FONT_PRECISION) {
|
||||
step /= 2.0;
|
||||
}
|
||||
/* Do not allow negative size */
|
||||
if (currentSize <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* If new font size is smaller than maximum possible size, increase it */
|
||||
else {
|
||||
// qDebug() << "++ contentsRect()" << label->contentsRect() << "rect"<< label->rect() << " newFontSizeRect"
|
||||
// << newFontSizeRect << "Tight" << text << currentSize;
|
||||
currentSize += step;
|
||||
}
|
||||
}
|
||||
return lastTestedSize;
|
||||
}
|
||||
|
||||
void DynamicFontSizeLabel::setTextColor(QColor color)
|
||||
{
|
||||
if (color.isValid() && color != textColor) {
|
||||
textColor = color;
|
||||
setStyleSheet("color : " + color.name() + ";");
|
||||
}
|
||||
}
|
||||
|
||||
QColor DynamicFontSizeLabel::getTextColor()
|
||||
{
|
||||
return textColor;
|
||||
}
|
||||
|
||||
void DynamicFontSizeLabel::setTextAndColor(const QString &text, QColor color)
|
||||
{
|
||||
setTextColor(color);
|
||||
setText(text);
|
||||
}
|
||||
|
||||
/* Do not give any size hint as it it changes during paintEvent */
|
||||
QSize DynamicFontSizeLabel::minimumSizeHint() const
|
||||
{
|
||||
return QWidget::minimumSizeHint();
|
||||
}
|
||||
|
||||
/* Do not give any size hint as it it changes during paintEvent */
|
||||
QSize DynamicFontSizeLabel::sizeHint() const
|
||||
{
|
||||
return QWidget::sizeHint();
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef DYNAMICFONTSIZELABEL_H
|
||||
#define DYNAMICFONTSIZELABEL_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QLabel>
|
||||
|
||||
class DynamicFontSizeLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DynamicFontSizeLabel(QWidget *parent = NULL, Qt::WindowFlags f = Qt::WindowFlags());
|
||||
|
||||
~DynamicFontSizeLabel()
|
||||
{
|
||||
}
|
||||
|
||||
static float getWidgetMaximumFontSize(QWidget *widget, const QString &text);
|
||||
|
||||
/* This method overwrite stylesheet */
|
||||
void setTextColor(QColor color);
|
||||
QColor getTextColor();
|
||||
void setTextAndColor(const QString &text, QColor color = QColor::Invalid);
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event);
|
||||
QColor textColor;
|
||||
|
||||
// QWidget interface
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event);
|
||||
|
||||
// QWidget interface
|
||||
public:
|
||||
QSize minimumSizeHint() const;
|
||||
QSize sizeHint() const;
|
||||
};
|
||||
|
||||
#endif // DYNAMICFONTSIZELABEL_H
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#include "dynamic_font_size_push_button.h"
|
||||
|
||||
#include "dynamic_font_size_label.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
|
||||
DynamicFontSizePushButton::DynamicFontSizePushButton(QWidget *parent) : QPushButton(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void DynamicFontSizePushButton::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
// Call the base class paintEvent to preserve any other painting behavior
|
||||
QPushButton::paintEvent(event);
|
||||
|
||||
// Adjust the font size dynamically based on the text
|
||||
QFont newFont = font();
|
||||
float fontSize = DynamicFontSizeLabel::getWidgetMaximumFontSize(this, this->text());
|
||||
newFont.setPointSizeF(fontSize);
|
||||
setFont(newFont);
|
||||
|
||||
// Get painter for custom painting
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
// Paint the background with a linear gradient (normal state)
|
||||
QLinearGradient gradient(0, 0, 0, height());
|
||||
if (isDown()) {
|
||||
// Pressed state
|
||||
gradient.setColorAt(0, QColor(128, 128, 128));
|
||||
gradient.setColorAt(1, QColor(64, 64, 64));
|
||||
} else if (underMouse()) {
|
||||
// Hover state
|
||||
gradient.setColorAt(0, QColor(96, 96, 96));
|
||||
gradient.setColorAt(1, QColor(48, 48, 48));
|
||||
} else {
|
||||
// Normal state
|
||||
gradient.setColorAt(0, QColor(64, 64, 64)); // start color
|
||||
gradient.setColorAt(1, QColor(32, 32, 32)); // end color
|
||||
}
|
||||
painter.setBrush(gradient);
|
||||
painter.setPen(Qt::NoPen); // No border
|
||||
painter.drawRect(rect());
|
||||
|
||||
// Paint the button text
|
||||
painter.setPen(QPen(textColor.isValid() ? textColor : QColor(255, 255, 255))); // Set text color
|
||||
painter.drawText(rect(), Qt::AlignCenter, text());
|
||||
}
|
||||
|
||||
void DynamicFontSizePushButton::setTextColor(QColor color)
|
||||
{
|
||||
if (color.isValid() && color != textColor) {
|
||||
textColor = color;
|
||||
update(); // Request a repaint to update the text color
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicFontSizePushButton::setTextAndColor(const QString &text, QColor color)
|
||||
{
|
||||
setTextColor(color);
|
||||
setText(text);
|
||||
}
|
||||
|
||||
QColor DynamicFontSizePushButton::getTextColor()
|
||||
{
|
||||
return textColor;
|
||||
}
|
||||
|
||||
/* Do not give any size hint as it it changes during paintEvent */
|
||||
QSize DynamicFontSizePushButton::minimumSizeHint() const
|
||||
{
|
||||
return QWidget::minimumSizeHint();
|
||||
}
|
||||
|
||||
/* Do not give any size hint as it it changes during paintEvent */
|
||||
QSize DynamicFontSizePushButton::sizeHint() const
|
||||
{
|
||||
return QWidget::sizeHint();
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef DYNAMICFONTSIZEPUSHBUTTON_H
|
||||
#define DYNAMICFONTSIZEPUSHBUTTON_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
class DynamicFontSizePushButton : public QPushButton
|
||||
{
|
||||
public:
|
||||
explicit DynamicFontSizePushButton(QWidget *parent = NULL);
|
||||
|
||||
/* This method overwrite stylesheet */
|
||||
void setTextColor(QColor color);
|
||||
QColor getTextColor();
|
||||
void setTextAndColor(const QString &text, QColor color = QColor::Invalid);
|
||||
|
||||
// QWidget interface
|
||||
QSize minimumSizeHint() const;
|
||||
QSize sizeHint() const;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event);
|
||||
|
||||
private:
|
||||
QColor textColor;
|
||||
};
|
||||
|
||||
#endif // DYNAMICFONTSIZEPUSHBUTTON_H
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#include "labeled_input.h"
|
||||
|
||||
LabeledInput::LabeledInput(QWidget *parent, const QString &labelText) : QWidget(parent)
|
||||
{
|
||||
label = new QLabel(labelText, this);
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->addWidget(label);
|
||||
}
|
||||
|
||||
QSpinBox *LabeledInput::addSpinBox(const int minValue, const int maxValue, const int defaultValue)
|
||||
{
|
||||
auto *spinBox = new QSpinBox(this);
|
||||
spinBox->setRange(minValue, maxValue);
|
||||
spinBox->setValue(defaultValue);
|
||||
layout->addWidget(spinBox);
|
||||
connect(spinBox, SIGNAL(valueChanged(int)), this, SIGNAL(spinBoxValueChanged(int)));
|
||||
return spinBox;
|
||||
}
|
||||
|
||||
// Add a QComboBox (for arbitrary selections)
|
||||
QComboBox *LabeledInput::addComboBox(const QStringList &items, const QString &defaultItem)
|
||||
{
|
||||
auto *comboBox = new QComboBox(this);
|
||||
comboBox->addItems(items);
|
||||
if (!defaultItem.isEmpty()) {
|
||||
comboBox->setCurrentText(defaultItem);
|
||||
}
|
||||
layout->addWidget(comboBox);
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
// Add a QComboBox specifically for Qt Directions
|
||||
QComboBox *LabeledInput::addDirectionComboBox()
|
||||
{
|
||||
const QStringList directions = {"Qt::Horizontal", "Qt::Vertical"};
|
||||
const auto comboBox = addComboBox(directions, "Qt::Vertical");
|
||||
connect(comboBox, SIGNAL(currentTextChanged(QString)), this, SIGNAL(directionComboBoxChanged(QString)));
|
||||
return comboBox;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef LABELED_INPUT_H
|
||||
#define LABELED_INPUT_H
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QSpinBox>
|
||||
#include <QWidget>
|
||||
|
||||
class LabeledInput final : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LabeledInput(QWidget *parent, const QString &labelText);
|
||||
|
||||
// Add a QSpinBox (for arbitrary numbers)
|
||||
QSpinBox *addSpinBox(int minValue, int maxValue, int defaultValue = 0);
|
||||
|
||||
// Add a QComboBox (for arbitrary selections)
|
||||
QComboBox *addComboBox(const QStringList &items, const QString &defaultItem = QString());
|
||||
|
||||
// Add a QComboBox specifically for Qt Directions
|
||||
QComboBox *addDirectionComboBox();
|
||||
|
||||
signals:
|
||||
void spinBoxValueChanged(int newValue); // Declare the valueChanged signal
|
||||
void comboBoxValueChanged(int newValue);
|
||||
void directionComboBoxChanged(QString newDirection);
|
||||
|
||||
private:
|
||||
QLabel *label;
|
||||
QHBoxLayout *layout;
|
||||
};
|
||||
|
||||
#endif // LABELED_INPUT_H
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#include "percent_bar_widget.h"
|
||||
|
||||
PercentBarWidget::PercentBarWidget(QWidget *parent, double initialValue) : QWidget(parent), valueToDisplay(initialValue)
|
||||
{
|
||||
setMinimumSize(50, 10);
|
||||
}
|
||||
|
||||
void PercentBarWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
QPainter painter(this);
|
||||
QRect rect = this->rect();
|
||||
|
||||
const int midX = rect.width() / 2;
|
||||
const int height = rect.height();
|
||||
|
||||
// Draw background border (no fill)
|
||||
painter.setPen(QPen(Qt::black, 1));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.drawRect(rect.adjusted(0, 0, -1, -1)); // Avoid right/bottom overflow
|
||||
|
||||
const double halfWidth = rect.width() / 2.0;
|
||||
|
||||
const int barLength = static_cast<int>((qAbs(valueToDisplay) / 100.0) * halfWidth);
|
||||
|
||||
QRect fillRect;
|
||||
if (valueToDisplay > 0.0) {
|
||||
fillRect = QRect(midX, 0, barLength, height);
|
||||
painter.fillRect(fillRect, Qt::green);
|
||||
} else if (valueToDisplay < 0.0) {
|
||||
fillRect = QRect(midX - barLength, 0, barLength, height);
|
||||
painter.fillRect(fillRect, Qt::red);
|
||||
}
|
||||
|
||||
// Draw center line at 0
|
||||
painter.fillRect(midX - 1, 0, 3, height, Qt::white);
|
||||
|
||||
// Draw tick marks every 10%
|
||||
const int tickHeight = 4;
|
||||
|
||||
for (int percent = -100; percent <= 100; percent += 10) {
|
||||
int x = midX + static_cast<int>((percent / 100.0) * halfWidth);
|
||||
painter.drawLine(x, height - tickHeight, x, height);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef PERCENT_BAR_WIDGET_H
|
||||
#define PERCENT_BAR_WIDGET_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QPainter>
|
||||
#include <QWidget>
|
||||
|
||||
class PercentBarWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PercentBarWidget(QWidget *parent, double initialValue);
|
||||
|
||||
void setValue(double newValue)
|
||||
{
|
||||
valueToDisplay = qBound(-100.0, newValue, 100.0); // Clamp to [-100, 100]
|
||||
update(); // Trigger repaint
|
||||
}
|
||||
|
||||
double value() const
|
||||
{
|
||||
return valueToDisplay;
|
||||
}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
double valueToDisplay; // Ranges from -100 to 100
|
||||
};
|
||||
|
||||
#endif // PERCENT_BAR_WIDGET_H
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
#include "shadow_background_label.h"
|
||||
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
|
||||
/**
|
||||
* @class ShadowBackgroundLabel
|
||||
* @brief A QLabel with a semi-transparent black shadowed background and rounded corners.
|
||||
*
|
||||
* This label provides a styled appearance with centered white text and a translucent
|
||||
* rounded background, making it suitable for overlay or emphasis in a UI.
|
||||
*/
|
||||
ShadowBackgroundLabel::ShadowBackgroundLabel(QWidget *parent, const QString &text) : QLabel(parent)
|
||||
{
|
||||
setAttribute(Qt::WA_TranslucentBackground); // Allows transparency.
|
||||
setText("<font color='white'>" + text + "</font>"); ///< Ensures the text is rendered in white.
|
||||
setAlignment(Qt::AlignCenter); ///< Centers the text within the label.
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); ///< Ensures minimum size constraints.
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles resizing of the label.
|
||||
*
|
||||
* Ensures the label updates its appearance when resized by triggering a repaint.
|
||||
*
|
||||
* @param event The resize event containing new size information.
|
||||
*/
|
||||
void ShadowBackgroundLabel::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QLabel::resizeEvent(event);
|
||||
update(); // Repaint borders explicitly.
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Custom paint event for drawing the label's background.
|
||||
*
|
||||
* Renders a semi-transparent black rounded rectangle as the background
|
||||
* and then delegates text rendering to QLabel.
|
||||
*
|
||||
* @param event The paint event for the widget.
|
||||
*/
|
||||
void ShadowBackgroundLabel::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
// Enable antialiasing for smoother edges.
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
// Set semi-transparent black brush and disable border pen.
|
||||
painter.setBrush(QColor(0, 0, 0, 128)); // Semi-transparent black.
|
||||
painter.setPen(Qt::NoPen); // No border.
|
||||
|
||||
// Adjust the rectangle to account for margins.
|
||||
QRect adjustedRect = this->rect();
|
||||
int margin = contentsMargins().left(); // Assuming equal margins.
|
||||
adjustedRect.adjust(margin, margin, -margin, -margin);
|
||||
|
||||
// Draw a rounded rectangle with a corner radius of 5.
|
||||
painter.drawRoundedRect(adjustedRect, 5, 5);
|
||||
|
||||
// Delegate text rendering to QLabel.
|
||||
QLabel::paintEvent(event);
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef STYLEDLABEL_H
|
||||
#define STYLEDLABEL_H
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
class ShadowBackgroundLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ShadowBackgroundLabel(QWidget *parent, const QString &text);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override; // Custom painting logic
|
||||
};
|
||||
|
||||
#endif // STYLEDLABEL_H
|
||||
105
cockatrice/src/interface/widgets/general/home_styled_button.cpp
Normal file
105
cockatrice/src/interface/widgets/general/home_styled_button.cpp
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#include "home_styled_button.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <qgraphicseffect.h>
|
||||
#include <qstyleoption.h>
|
||||
|
||||
HomeStyledButton::HomeStyledButton(const QString &text, QPair<QColor, QColor> _gradientColors, QWidget *parent)
|
||||
: QPushButton(text, parent), gradientColors(_gradientColors)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
setMinimumHeight(50);
|
||||
setStyleSheet(generateButtonStylesheet(gradientColors));
|
||||
}
|
||||
|
||||
void HomeStyledButton::updateStylesheet(const QPair<QColor, QColor> &colors)
|
||||
{
|
||||
gradientColors = colors;
|
||||
setStyleSheet(generateButtonStylesheet(gradientColors));
|
||||
}
|
||||
|
||||
QString HomeStyledButton::generateButtonStylesheet(const QPair<QColor, QColor> &colors)
|
||||
{
|
||||
QColor baseGradientStart = colors.first;
|
||||
QColor baseGradientEnd = colors.second;
|
||||
|
||||
QColor hoverGradientStart = baseGradientStart.lighter(120); // 20% lighter
|
||||
QColor hoverGradientEnd = baseGradientEnd.lighter(120);
|
||||
|
||||
QColor pressedGradientStart = baseGradientStart.darker(130); // 30% darker
|
||||
QColor pressedGradientEnd = baseGradientEnd.darker(130);
|
||||
|
||||
// Disabled: more gray, less saturated
|
||||
QColor disabledGradientStart = baseGradientStart.toHsv();
|
||||
disabledGradientStart.setHsv(disabledGradientStart.hue(), disabledGradientStart.saturation() * 0.2,
|
||||
disabledGradientStart.value() * 0.6);
|
||||
QColor disabledGradientEnd = baseGradientEnd.toHsv();
|
||||
disabledGradientEnd.setHsv(disabledGradientEnd.hue(), disabledGradientEnd.saturation() * 0.2,
|
||||
disabledGradientEnd.value() * 0.6);
|
||||
|
||||
return QString(R"(
|
||||
QPushButton {
|
||||
font-size: 34px;
|
||||
padding: 30px;
|
||||
color: white;
|
||||
border: 2px solid %1;
|
||||
border-radius: 20px;
|
||||
background: qlineargradient(x1:0, y1:1, x2:0, y2:0,
|
||||
stop:0 %2, stop:1 %3);
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: qlineargradient(x1:0, y1:1, x2:0, y2:0,
|
||||
stop:0 %4, stop:1 %5);
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 %6, stop:1 %7);
|
||||
}
|
||||
QPushButton:disabled {
|
||||
color: #aaaaaa;
|
||||
border: 2px solid #888888;
|
||||
background: qlineargradient(x1:0, y1:1, x2:0, y2:0,
|
||||
stop:0 %8, stop:1 %9);
|
||||
}
|
||||
)")
|
||||
.arg(baseGradientStart.name())
|
||||
.arg(baseGradientStart.name())
|
||||
.arg(baseGradientEnd.name())
|
||||
.arg(hoverGradientStart.name())
|
||||
.arg(hoverGradientEnd.name())
|
||||
.arg(pressedGradientStart.name())
|
||||
.arg(pressedGradientEnd.name())
|
||||
.arg(disabledGradientStart.name())
|
||||
.arg(disabledGradientEnd.name());
|
||||
}
|
||||
|
||||
void HomeStyledButton::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event); // Event is just used for update clipping, we redraw the whole widget.
|
||||
QStyleOptionButton opt;
|
||||
initStyleOption(&opt);
|
||||
opt.text.clear(); // prevent style from drawing text
|
||||
|
||||
QPainter painter(this);
|
||||
style()->drawControl(QStyle::CE_PushButton, &opt, &painter, this);
|
||||
|
||||
// Draw white text with a black outline
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setRenderHint(QPainter::TextAntialiasing);
|
||||
|
||||
QFont font = this->font();
|
||||
font.setBold(true);
|
||||
painter.setFont(font);
|
||||
|
||||
QFontMetrics fm(font);
|
||||
QSize textSize = fm.size(Qt::TextSingleLine, this->text());
|
||||
QPointF center((width() - textSize.width()) / 2.0, (height() + textSize.height() / 2.0) / 2.0);
|
||||
|
||||
QPainterPath path;
|
||||
path.addText(center, font, this->text());
|
||||
|
||||
painter.setPen(QPen(Qt::black, 2.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||
painter.setBrush(Qt::white);
|
||||
painter.drawPath(path);
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// Created by ascor on 6/15/25.
|
||||
//
|
||||
|
||||
#ifndef HOME_STYLED_BUTTON_H
|
||||
#define HOME_STYLED_BUTTON_H
|
||||
#include <QPushButton>
|
||||
|
||||
class HomeStyledButton : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HomeStyledButton(const QString &text, QPair<QColor, QColor> gradientColors, QWidget *parent = nullptr);
|
||||
void updateStylesheet(const QPair<QColor, QColor> &colors);
|
||||
QString generateButtonStylesheet(const QPair<QColor, QColor> &colors);
|
||||
public slots:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QPair<QColor, QColor> gradientColors;
|
||||
};
|
||||
|
||||
#endif // HOME_STYLED_BUTTON_H
|
||||
313
cockatrice/src/interface/widgets/general/home_widget.cpp
Normal file
313
cockatrice/src/interface/widgets/general/home_widget.cpp
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
#include "home_widget.h"
|
||||
|
||||
#include "../../../database/card_database_manager.h"
|
||||
#include "../../../server/remote/remote_client.h"
|
||||
#include "../../../settings/cache_settings.h"
|
||||
#include "../../../tabs/tab_supervisor.h"
|
||||
#include "../../window_main.h"
|
||||
#include "background_sources.h"
|
||||
#include "home_styled_button.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
|
||||
: QWidget(parent), tabSupervisor(_tabSupervisor), background("theme:backgrounds/home"), overlay("theme:cockatrice")
|
||||
{
|
||||
layout = new QGridLayout(this);
|
||||
|
||||
backgroundSourceCard = new CardInfoPictureArtCropWidget(this);
|
||||
backgroundSourceDeck = new DeckLoader();
|
||||
|
||||
backgroundSourceDeck->loadFromFile(SettingsCache::instance().getDeckPath() + "background.cod",
|
||||
DeckLoader::CockatriceFormat, false);
|
||||
|
||||
gradientColors = extractDominantColors(background);
|
||||
|
||||
layout->addWidget(createButtons(), 1, 1, Qt::AlignVCenter | Qt::AlignHCenter);
|
||||
|
||||
layout->setRowStretch(0, 1);
|
||||
layout->setRowStretch(2, 1);
|
||||
layout->setColumnStretch(0, 1);
|
||||
layout->setColumnStretch(2, 1);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
cardChangeTimer = new QTimer(this);
|
||||
connect(cardChangeTimer, &QTimer::timeout, this, &HomeWidget::updateRandomCard);
|
||||
|
||||
initializeBackgroundFromSource();
|
||||
|
||||
updateConnectButton(tabSupervisor->getClient()->getStatus());
|
||||
|
||||
connect(tabSupervisor->getClient(), &RemoteClient::statusChanged, this, &HomeWidget::updateConnectButton);
|
||||
connect(&SettingsCache::instance(), &SettingsCache::homeTabBackgroundSourceChanged, this,
|
||||
&HomeWidget::initializeBackgroundFromSource);
|
||||
connect(&SettingsCache::instance(), &SettingsCache::homeTabBackgroundShuffleFrequencyChanged, this,
|
||||
&HomeWidget::onBackgroundShuffleFrequencyChanged);
|
||||
}
|
||||
|
||||
void HomeWidget::initializeBackgroundFromSource()
|
||||
{
|
||||
if (CardDatabaseManager::getInstance()->getLoadStatus() != LoadStatus::Ok) {
|
||||
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
|
||||
&HomeWidget::initializeBackgroundFromSource);
|
||||
return;
|
||||
}
|
||||
|
||||
auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
|
||||
|
||||
cardChangeTimer->stop();
|
||||
|
||||
switch (backgroundSourceType) {
|
||||
case BackgroundSources::Theme:
|
||||
background = QPixmap("theme:backgrounds/home");
|
||||
updateButtonsToBackgroundColor();
|
||||
update();
|
||||
break;
|
||||
case BackgroundSources::RandomCardArt:
|
||||
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
|
||||
break;
|
||||
case BackgroundSources::DeckFileArt:
|
||||
backgroundSourceDeck->loadFromFile(SettingsCache::instance().getDeckPath() + "background.cod",
|
||||
DeckLoader::CockatriceFormat, false);
|
||||
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HomeWidget::updateRandomCard()
|
||||
{
|
||||
auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
|
||||
|
||||
ExactCard newCard;
|
||||
|
||||
switch (backgroundSourceType) {
|
||||
case BackgroundSources::Theme:
|
||||
break;
|
||||
case BackgroundSources::RandomCardArt:
|
||||
do {
|
||||
newCard = CardDatabaseManager::getInstance()->getRandomCard();
|
||||
} while (newCard == backgroundSourceCard->getCard() &&
|
||||
newCard.getCardPtr()->getProperty("layout") != "normal");
|
||||
break;
|
||||
case BackgroundSources::DeckFileArt:
|
||||
QList<CardRef> cardRefs = backgroundSourceDeck->getCardRefList();
|
||||
ExactCard oldCard = backgroundSourceCard->getCard();
|
||||
|
||||
if (!cardRefs.empty()) {
|
||||
if (cardRefs.size() == 1) {
|
||||
newCard = CardDatabaseManager::getInstance()->getCard(cardRefs.first());
|
||||
} else {
|
||||
// Keep picking until different
|
||||
do {
|
||||
int idx = QRandomGenerator::global()->bounded(cardRefs.size());
|
||||
newCard = CardDatabaseManager::getInstance()->getCard(cardRefs.at(idx));
|
||||
} while (newCard == oldCard);
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
newCard = CardDatabaseManager::getInstance()->getRandomCard();
|
||||
} while (newCard == oldCard);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!newCard)
|
||||
return;
|
||||
|
||||
connect(newCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &HomeWidget::updateBackgroundProperties);
|
||||
backgroundSourceCard->setCard(newCard);
|
||||
background = backgroundSourceCard->getProcessedBackground(size());
|
||||
|
||||
if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() <= 0) {
|
||||
cardChangeTimer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void HomeWidget::onBackgroundShuffleFrequencyChanged()
|
||||
{
|
||||
cardChangeTimer->stop();
|
||||
if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() <= 0) {
|
||||
return;
|
||||
}
|
||||
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
|
||||
}
|
||||
|
||||
void HomeWidget::updateBackgroundProperties()
|
||||
{
|
||||
background = backgroundSourceCard->getProcessedBackground(size());
|
||||
updateButtonsToBackgroundColor();
|
||||
update(); // Triggers repaint
|
||||
}
|
||||
|
||||
void HomeWidget::updateButtonsToBackgroundColor()
|
||||
{
|
||||
gradientColors = extractDominantColors(background);
|
||||
for (HomeStyledButton *button : findChildren<HomeStyledButton *>()) {
|
||||
button->updateStylesheet(gradientColors);
|
||||
button->update();
|
||||
}
|
||||
}
|
||||
|
||||
QGroupBox *HomeWidget::createButtons()
|
||||
{
|
||||
QGroupBox *box = new QGroupBox(this);
|
||||
box->setStyleSheet(R"(
|
||||
QGroupBox {
|
||||
font-size: 20px;
|
||||
color: white; /* Title text color */
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QGroupBox::title {
|
||||
color: white;
|
||||
subcontrol-origin: margin;
|
||||
subcontrol-position: top center; /* or top left / right */
|
||||
}
|
||||
)");
|
||||
box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
QVBoxLayout *boxLayout = new QVBoxLayout;
|
||||
boxLayout->setAlignment(Qt::AlignHCenter);
|
||||
|
||||
QLabel *logoLabel = new QLabel;
|
||||
logoLabel->setPixmap(overlay.scaledToWidth(200, Qt::SmoothTransformation));
|
||||
logoLabel->setAlignment(Qt::AlignCenter);
|
||||
boxLayout->addWidget(logoLabel);
|
||||
boxLayout->addSpacing(25);
|
||||
|
||||
connectButton = new HomeStyledButton("Connect/Play", gradientColors);
|
||||
boxLayout->addWidget(connectButton, 1);
|
||||
|
||||
auto visualDeckEditorButton = new HomeStyledButton(tr("Create New Deck"), gradientColors);
|
||||
connect(visualDeckEditorButton, &QPushButton::clicked, tabSupervisor,
|
||||
[this] { tabSupervisor->openDeckInNewTab(nullptr); });
|
||||
boxLayout->addWidget(visualDeckEditorButton);
|
||||
auto visualDeckStorageButton = new HomeStyledButton(tr("Browse Decks"), gradientColors);
|
||||
connect(visualDeckStorageButton, &QPushButton::clicked, tabSupervisor,
|
||||
[this] { tabSupervisor->actTabVisualDeckStorage(true); });
|
||||
boxLayout->addWidget(visualDeckStorageButton);
|
||||
auto visualDatabaseDisplayButton = new HomeStyledButton(tr("Browse Card Database"), gradientColors);
|
||||
connect(visualDatabaseDisplayButton, &QPushButton::clicked, tabSupervisor,
|
||||
&TabSupervisor::addVisualDatabaseDisplayTab);
|
||||
boxLayout->addWidget(visualDatabaseDisplayButton);
|
||||
auto edhrecButton = new HomeStyledButton(tr("Browse EDHRec"), gradientColors);
|
||||
connect(edhrecButton, &QPushButton::clicked, tabSupervisor, &TabSupervisor::addEdhrecMainTab);
|
||||
boxLayout->addWidget(edhrecButton);
|
||||
auto replaybutton = new HomeStyledButton(tr("View Replays"), gradientColors);
|
||||
connect(replaybutton, &QPushButton::clicked, tabSupervisor, [this] { tabSupervisor->actTabReplays(true); });
|
||||
boxLayout->addWidget(replaybutton);
|
||||
if (qobject_cast<MainWindow *>(tabSupervisor->parentWidget())) {
|
||||
auto exitButton = new HomeStyledButton(tr("Quit"), gradientColors);
|
||||
connect(exitButton, &QPushButton::clicked, qobject_cast<MainWindow *>(tabSupervisor->parentWidget()),
|
||||
&MainWindow::actExit);
|
||||
boxLayout->addWidget(exitButton);
|
||||
}
|
||||
|
||||
box->setLayout(boxLayout);
|
||||
return box;
|
||||
}
|
||||
|
||||
void HomeWidget::updateConnectButton(const ClientStatus status)
|
||||
{
|
||||
disconnect(connectButton, &QPushButton::clicked, nullptr, nullptr);
|
||||
switch (status) {
|
||||
case StatusConnecting:
|
||||
connectButton->setText(tr("Connecting..."));
|
||||
connectButton->setEnabled(false);
|
||||
break;
|
||||
case StatusDisconnected:
|
||||
connectButton->setText(tr("Connect"));
|
||||
connectButton->setEnabled(true);
|
||||
connect(connectButton, &QPushButton::clicked, qobject_cast<MainWindow *>(tabSupervisor->parentWidget()),
|
||||
&MainWindow::actConnect);
|
||||
break;
|
||||
case StatusLoggedIn:
|
||||
connectButton->setText(tr("Play"));
|
||||
connectButton->setEnabled(true);
|
||||
connect(connectButton, &QPushButton::clicked, tabSupervisor,
|
||||
&TabSupervisor::switchToFirstAvailableNetworkTab);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QPair<QColor, QColor> HomeWidget::extractDominantColors(const QPixmap &pixmap)
|
||||
{
|
||||
if (SettingsCache::instance().getThemeName() == "Default" &&
|
||||
SettingsCache::instance().getHomeTabBackgroundSource() == BackgroundSources::toId(BackgroundSources::Theme)) {
|
||||
return QPair<QColor, QColor>(QColor::fromRgb(20, 140, 60), QColor::fromRgb(120, 200, 80));
|
||||
}
|
||||
|
||||
// Step 1: Downscale image for performance
|
||||
QImage image = pixmap.toImage()
|
||||
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)
|
||||
.convertToFormat(QImage::Format_RGB32);
|
||||
|
||||
QMap<QRgb, int> colorCount;
|
||||
|
||||
// Step 2: Count quantized colors
|
||||
for (int y = 0; y < image.height(); ++y) {
|
||||
const QRgb *scanLine = reinterpret_cast<const QRgb *>(image.scanLine(y));
|
||||
for (int x = 0; x < image.width(); ++x) {
|
||||
QColor color = QColor::fromRgb(scanLine[x]);
|
||||
|
||||
int r = color.red() & 0xF0;
|
||||
int g = color.green() & 0xF0;
|
||||
int b = color.blue() & 0xF0;
|
||||
|
||||
QRgb quantized = qRgb(r, g, b);
|
||||
colorCount[quantized]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Sort by frequency
|
||||
QVector<QPair<QRgb, int>> sortedColors;
|
||||
for (auto it = colorCount.constBegin(); it != colorCount.constEnd(); ++it) {
|
||||
sortedColors.append(qMakePair(it.key(), it.value()));
|
||||
}
|
||||
|
||||
std::sort(sortedColors.begin(), sortedColors.end(),
|
||||
[](const QPair<QRgb, int> &a, const QPair<QRgb, int> &b) { return a.second > b.second; });
|
||||
|
||||
// Step 4: Pick top two distinct colors
|
||||
QColor first = QColor(sortedColors.value(0).first);
|
||||
QColor second = first;
|
||||
|
||||
for (int i = 1; i < sortedColors.size(); ++i) {
|
||||
QColor candidate = QColor(sortedColors[i].first);
|
||||
if (candidate != first) {
|
||||
second = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QPair<QColor, QColor>(first, second);
|
||||
}
|
||||
|
||||
void HomeWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
background = background.scaled(size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
|
||||
// Draw already-scaled background centered
|
||||
QSize widgetSize = size();
|
||||
QSize bgSize = background.size();
|
||||
QPoint topLeft((widgetSize.width() - bgSize.width()) / 2, (widgetSize.height() - bgSize.height()) / 2);
|
||||
|
||||
painter.drawPixmap(topLeft, background);
|
||||
|
||||
// Draw translucent black overlay with rounded corners
|
||||
QRectF overlayRect(5, 5, width() - 10, height() - 10); // 5px inset
|
||||
QPainterPath roundedRectPath;
|
||||
roundedRectPath.addRoundedRect(overlayRect, 20, 20); // 20px corner radius
|
||||
|
||||
QColor semiTransparentBlack(0, 0, 0, static_cast<int>(255 * 0.33)); // 33% opacity
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.fillPath(roundedRectPath, semiTransparentBlack);
|
||||
|
||||
QWidget::paintEvent(event);
|
||||
}
|
||||
43
cockatrice/src/interface/widgets/general/home_widget.h
Normal file
43
cockatrice/src/interface/widgets/general/home_widget.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef HOME_WIDGET_H
|
||||
#define HOME_WIDGET_H
|
||||
#include "../../../server/abstract_client.h"
|
||||
#include "../../../tabs/tab_supervisor.h"
|
||||
#include "../cards/card_info_picture_art_crop_widget.h"
|
||||
#include "home_styled_button.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QWidget>
|
||||
|
||||
class HomeWidget : public QWidget
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HomeWidget(QWidget *parent, TabSupervisor *tabSupervisor);
|
||||
void updateRandomCard();
|
||||
QPair<QColor, QColor> extractDominantColors(const QPixmap &pixmap);
|
||||
|
||||
public slots:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void initializeBackgroundFromSource();
|
||||
void onBackgroundShuffleFrequencyChanged();
|
||||
void updateBackgroundProperties();
|
||||
void updateButtonsToBackgroundColor();
|
||||
QGroupBox *createButtons();
|
||||
void updateConnectButton(const ClientStatus status);
|
||||
|
||||
private:
|
||||
QGridLayout *layout;
|
||||
QTimer *cardChangeTimer;
|
||||
TabSupervisor *tabSupervisor;
|
||||
QPixmap background;
|
||||
CardInfoPictureArtCropWidget *backgroundSourceCard = nullptr;
|
||||
DeckLoader *backgroundSourceDeck;
|
||||
QPixmap overlay;
|
||||
QPair<QColor, QColor> gradientColors;
|
||||
HomeStyledButton *connectButton;
|
||||
};
|
||||
|
||||
#endif // HOME_WIDGET_H
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue