mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2025-12-12 07:40:30 -08:00
Bubbling.
Took 16 seconds # Commit time for manual adjustment: # Took 8 seconds Took 16 seconds # Commit time for manual adjustment: # Took 6 seconds Took 9 seconds # Commit time for manual adjustment: # Took 8 seconds Took 14 seconds
This commit is contained in:
@@ -168,6 +168,7 @@ set(cockatrice_SOURCES
|
||||
src/interface/widgets/general/layout_containers/flow_widget.cpp
|
||||
src/interface/widgets/general/layout_containers/overlap_control_widget.cpp
|
||||
src/interface/widgets/general/layout_containers/overlap_widget.cpp
|
||||
src/interface/widgets/general/tutorial/tutorial_bubble_widget.cpp
|
||||
src/interface/widgets/general/tutorial/tutorial_controller.cpp
|
||||
src/interface/widgets/general/tutorial/tutorial_overlay.cpp
|
||||
src/interface/widgets/menus/deck_editor_menu.cpp
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "tutorial_bubble_widget.h"
|
||||
|
||||
BubbleWidget::BubbleWidget(QWidget *parent) : QFrame(parent)
|
||||
{
|
||||
setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
|
||||
setStyleSheet("background:white; border-radius:8px;");
|
||||
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
layout->setContentsMargins(12, 10, 12, 10);
|
||||
layout->setHorizontalSpacing(8);
|
||||
layout->setVerticalSpacing(8);
|
||||
|
||||
counterLabel = new QLabel(this);
|
||||
counterLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
closeButton = new QPushButton("✕", this);
|
||||
closeButton->setFixedSize(20, 20);
|
||||
|
||||
textLabel = new QLabel(this);
|
||||
textLabel->setWordWrap(true);
|
||||
textLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
textLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
textLabel->setStyleSheet("color:black;"); // guard against global styles
|
||||
|
||||
// nav buttons
|
||||
previousSequenceButton = new QPushButton("<<", this);
|
||||
previousStepButton = new QPushButton("<", this);
|
||||
nextStepButton = new QPushButton(">", this);
|
||||
nextSequenceButton = new QPushButton(">>", this);
|
||||
|
||||
QHBoxLayout *navLayout = new QHBoxLayout;
|
||||
navLayout->addStretch();
|
||||
navLayout->addWidget(previousSequenceButton);
|
||||
navLayout->addWidget(previousStepButton);
|
||||
navLayout->addWidget(nextStepButton);
|
||||
navLayout->addWidget(nextSequenceButton);
|
||||
|
||||
// Layout
|
||||
layout->addWidget(counterLabel, 0, 0, Qt::AlignLeft | Qt::AlignVCenter);
|
||||
layout->addItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 1);
|
||||
layout->addWidget(closeButton, 0, 2, Qt::AlignRight);
|
||||
layout->addWidget(textLabel, 1, 0, 1, 3);
|
||||
layout->addLayout(navLayout, 2, 0, 1, 3);
|
||||
|
||||
// Make column 1 take extra space so text gets room to expand/wrap
|
||||
layout->setColumnStretch(1, 1);
|
||||
|
||||
// sensible default maximum width for bubble so text will wrap
|
||||
setMaximumWidth(420);
|
||||
}
|
||||
|
||||
void BubbleWidget::setText(const QString &text)
|
||||
{
|
||||
textLabel->setText(text);
|
||||
update();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef COCKATRICE_TUTORIAL_BUBBLE_WIDGET_H
|
||||
#define COCKATRICE_TUTORIAL_BUBBLE_WIDGET_H
|
||||
#include <QFrame>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
|
||||
class BubbleWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QLabel *textLabel;
|
||||
QLabel *counterLabel;
|
||||
QPushButton *closeButton;
|
||||
QPushButton *previousSequenceButton;
|
||||
QPushButton *previousStepButton;
|
||||
QPushButton *nextStepButton;
|
||||
QPushButton *nextSequenceButton;
|
||||
|
||||
BubbleWidget(QWidget *parent);
|
||||
void setText(const QString &text);
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_TUTORIAL_BUBBLE_WIDGET_H
|
||||
@@ -15,7 +15,11 @@ TutorialController::TutorialController(QWidget *_tutorializedWidget)
|
||||
tutorialOverlay->hide();
|
||||
|
||||
connect(tutorialOverlay, &TutorialOverlay::nextStep, this, &TutorialController::nextStep);
|
||||
connect(tutorialOverlay, &TutorialOverlay::skipTutorial, this, [this]() { tutorialOverlay->hide(); });
|
||||
connect(tutorialOverlay, &TutorialOverlay::prevStep, this, &TutorialController::prevStep);
|
||||
connect(tutorialOverlay, &TutorialOverlay::nextSequence, this, &TutorialController::nextSequence);
|
||||
connect(tutorialOverlay, &TutorialOverlay::prevSequence, this, &TutorialController::prevSequence);
|
||||
|
||||
connect(tutorialOverlay, &TutorialOverlay::skipTutorial, this, &TutorialController::exitTutorial);
|
||||
}
|
||||
|
||||
void TutorialController::addSequence(const TutorialSequence &seq)
|
||||
@@ -100,12 +104,13 @@ void TutorialController::prevSequence()
|
||||
{
|
||||
if (currentSequence <= 0) {
|
||||
// already at first sequence -> stay
|
||||
currentStep = 0;
|
||||
showStep();
|
||||
return;
|
||||
}
|
||||
|
||||
// go to last step of previous sequence
|
||||
currentSequence--;
|
||||
currentStep = sequences[currentSequence].steps.size() - 1;
|
||||
currentStep = 0;
|
||||
showStep();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,15 @@ TutorialOverlay::TutorialOverlay(QWidget *parent) : QWidget(parent, Qt::Window)
|
||||
|
||||
setGeometry(r);
|
||||
}
|
||||
|
||||
bubble = new BubbleWidget(this);
|
||||
bubble->hide();
|
||||
|
||||
connect(bubble->nextStepButton, &QPushButton::clicked, this, &TutorialOverlay::nextStep);
|
||||
connect(bubble->previousStepButton, &QPushButton::clicked, this, &TutorialOverlay::prevStep);
|
||||
connect(bubble->previousSequenceButton, &QPushButton::clicked, this, &TutorialOverlay::prevSequence);
|
||||
connect(bubble->nextSequenceButton, &QPushButton::clicked, this, &TutorialOverlay::nextSequence);
|
||||
connect(bubble->closeButton, &QPushButton::clicked, this, &TutorialOverlay::skipTutorial);
|
||||
}
|
||||
|
||||
void TutorialOverlay::setTargetWidget(QWidget *w)
|
||||
@@ -35,6 +44,26 @@ void TutorialOverlay::setTargetWidget(QWidget *w)
|
||||
void TutorialOverlay::setText(const QString &t)
|
||||
{
|
||||
tutorialText = t;
|
||||
bubble->setText(tutorialText);
|
||||
bubble->adjustSize(); // let layout recalc sizes
|
||||
QSize bsize = bubble->sizeHint();
|
||||
|
||||
const QSize minSize(160, 60);
|
||||
if (bsize.width() < minSize.width()) {
|
||||
bsize.setWidth(minSize.width());
|
||||
}
|
||||
if (bsize.height() < minSize.height()) {
|
||||
bsize.setHeight(minSize.height());
|
||||
}
|
||||
|
||||
// Compute the bubble rect from the current target hole
|
||||
QRect hole = targetRectOnOverlay().adjusted(-6, -6, 6, 6);
|
||||
highlightBubbleRect = computeBubbleRect(hole, bsize);
|
||||
|
||||
bubble->setGeometry(highlightBubbleRect);
|
||||
bubble->raise();
|
||||
bubble->show();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -63,42 +92,42 @@ QRect TutorialOverlay::targetRectOnOverlay() const
|
||||
return QRect(localTopLeft, targetWidget->size());
|
||||
}
|
||||
|
||||
QRect TutorialOverlay::computeBubbleRect(const QRect &hole) const
|
||||
QRect TutorialOverlay::computeBubbleRect(const QRect &hole, const QSize &bubbleSize) const
|
||||
{
|
||||
const int bubbleW = 250;
|
||||
const int bubbleH = 120;
|
||||
const int margin = 16;
|
||||
|
||||
QRect r = rect(); // overlay bounds
|
||||
QRect bubble;
|
||||
|
||||
// Try right
|
||||
bubble = QRect(hole.right() + margin, hole.top(), bubbleW, bubbleH);
|
||||
bubble = QRect(hole.right() + margin, hole.top(), bubbleSize.width(), bubbleSize.height());
|
||||
if (r.contains(bubble)) {
|
||||
return bubble;
|
||||
}
|
||||
|
||||
// Try left
|
||||
bubble = QRect(hole.left() - margin - bubbleW, hole.top(), bubbleW, bubbleH);
|
||||
bubble = QRect(hole.left() - margin - bubbleSize.width(), hole.top(), bubbleSize.width(), bubbleSize.height());
|
||||
if (r.contains(bubble)) {
|
||||
return bubble;
|
||||
}
|
||||
|
||||
// Try above, centered
|
||||
bubble = QRect(hole.center().x() - bubbleW / 2, hole.top() - margin - bubbleH, bubbleW, bubbleH);
|
||||
bubble = QRect(hole.center().x() - bubbleSize.width() / 2, hole.top() - margin - bubbleSize.height(),
|
||||
bubbleSize.width(), bubbleSize.height());
|
||||
if (r.contains(bubble)) {
|
||||
return bubble;
|
||||
}
|
||||
|
||||
// Try below, centered
|
||||
bubble = QRect(hole.center().x() - bubbleW / 2, hole.bottom() + margin, bubbleW, bubbleH);
|
||||
bubble = QRect(hole.center().x() - bubbleSize.width() / 2, hole.bottom() + margin, bubbleSize.width(),
|
||||
bubbleSize.height());
|
||||
if (r.contains(bubble)) {
|
||||
return bubble;
|
||||
}
|
||||
|
||||
// Last-resort: clamp inside overlay
|
||||
bubble.moveLeft(std::max(r.left(), std::min(bubble.left(), r.right() - bubbleW)));
|
||||
bubble.moveTop(std::max(r.top(), std::min(bubble.top(), r.bottom() - bubbleH)));
|
||||
bubble.moveLeft(std::max(r.left(), std::min(bubble.left(), r.right() - bubbleSize.width())));
|
||||
bubble.moveTop(std::max(r.top(), std::min(bubble.top(), r.bottom() - bubbleSize.height())));
|
||||
bubble.setSize(bubbleSize);
|
||||
return bubble;
|
||||
}
|
||||
|
||||
@@ -117,11 +146,9 @@ void TutorialOverlay::paintEvent(QPaintEvent *)
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
// Darken the screen
|
||||
QColor overlay(0, 0, 0, 160);
|
||||
p.fillRect(rect(), overlay);
|
||||
|
||||
// Highlight hole
|
||||
QRect hole = targetRectOnOverlay().adjusted(-6, -6, 6, 6);
|
||||
if (!hole.isEmpty()) {
|
||||
QPainterPath path;
|
||||
@@ -135,25 +162,17 @@ void TutorialOverlay::paintEvent(QPaintEvent *)
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
}
|
||||
|
||||
// Draw bubble
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(QColor(255, 255, 255));
|
||||
highlightBubbleRect = computeBubbleRect(hole);
|
||||
p.drawRoundedRect(highlightBubbleRect, 8, 8);
|
||||
// recompute bubble size/position in case available geometry changed:
|
||||
bubble->adjustSize();
|
||||
QSize bsize = bubble->sizeHint();
|
||||
const QSize minSize(160, 60);
|
||||
if (bsize.width() < minSize.width())
|
||||
bsize.setWidth(minSize.width());
|
||||
if (bsize.height() < minSize.height())
|
||||
bsize.setHeight(minSize.height());
|
||||
|
||||
// Text
|
||||
p.setPen(Qt::black);
|
||||
p.drawText(highlightBubbleRect.adjusted(10, 10, -10, -10), Qt::TextWordWrap, tutorialText);
|
||||
}
|
||||
|
||||
void TutorialOverlay::mousePressEvent(QMouseEvent *ev)
|
||||
{
|
||||
// Clicks inside bubble → next
|
||||
if (highlightBubbleRect.contains(ev->pos())) {
|
||||
emit nextStep();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click anywhere else means skip
|
||||
emit skipTutorial();
|
||||
highlightBubbleRect = computeBubbleRect(hole, bsize);
|
||||
bubble->setGeometry(highlightBubbleRect);
|
||||
bubble->raise();
|
||||
bubble->show();
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef COCKATRICE_TUTORIAL_OVERLAY_H
|
||||
#define COCKATRICE_TUTORIAL_OVERLAY_H
|
||||
|
||||
#include "tutorial_bubble_widget.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <QWidget>
|
||||
|
||||
@@ -16,22 +18,25 @@ public:
|
||||
|
||||
signals:
|
||||
void nextStep();
|
||||
void prevStep();
|
||||
void nextSequence();
|
||||
void prevSequence();
|
||||
void skipTutorial();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
void resizeEvent(QResizeEvent *) override;
|
||||
void showEvent(QShowEvent *) override;
|
||||
void mousePressEvent(QMouseEvent *) override;
|
||||
|
||||
private:
|
||||
QRect targetRectOnOverlay() const;
|
||||
QRect computeBubbleRect(const QRect &hole) const;
|
||||
QRect computeBubbleRect(const QRect &hole, const QSize &bubbleSize) const;
|
||||
|
||||
QPointer<QWidget> targetWidget;
|
||||
QString tutorialText;
|
||||
|
||||
QRect highlightBubbleRect;
|
||||
BubbleWidget *bubble;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_TUTORIAL_OVERLAY_H
|
||||
|
||||
Reference in New Issue
Block a user