mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-01-20 00:31:15 -08:00
Size things correctly.
Took 32 minutes Took 10 seconds
This commit is contained in:
@@ -14,34 +14,16 @@ BubbleWidget::BubbleWidget(QWidget *parent) : QFrame(parent)
|
||||
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);
|
||||
|
||||
@@ -11,11 +11,6 @@ class BubbleWidget : public QFrame
|
||||
public:
|
||||
QLabel *textLabel;
|
||||
QLabel *counterLabel;
|
||||
QPushButton *closeButton;
|
||||
QPushButton *previousSequenceButton;
|
||||
QPushButton *previousStepButton;
|
||||
QPushButton *nextStepButton;
|
||||
QPushButton *nextSequenceButton;
|
||||
|
||||
BubbleWidget(QWidget *parent);
|
||||
void setText(const QString &text);
|
||||
|
||||
@@ -9,21 +9,18 @@
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QPushButton>
|
||||
#include <QResizeEvent>
|
||||
|
||||
TutorialOverlay::TutorialOverlay(QWidget *parent) : QWidget(parent, Qt::Window)
|
||||
{
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents, false);
|
||||
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents, false);
|
||||
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||
|
||||
if (parent) {
|
||||
parent->installEventFilter(this);
|
||||
|
||||
QRect r = parent->rect();
|
||||
QPoint globalTopLeft = parent->mapToGlobal(QPoint(0, 0));
|
||||
r.moveTopLeft(globalTopLeft);
|
||||
setGeometry(r);
|
||||
parentResized();
|
||||
}
|
||||
|
||||
// ---- Control bar -------------------------------------------------
|
||||
@@ -68,6 +65,16 @@ TutorialOverlay::TutorialOverlay(QWidget *parent) : QWidget(parent, Qt::Window)
|
||||
// ---- Bubble ------------------------------------------------------
|
||||
bubble = new BubbleWidget(this);
|
||||
bubble->hide();
|
||||
|
||||
controlBar->hide();
|
||||
}
|
||||
|
||||
void TutorialOverlay::showEvent(QShowEvent *event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
|
||||
// Queue geometry correction after the window is fully shown
|
||||
QMetaObject::invokeMethod(this, [this]() { parentResized(); }, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void TutorialOverlay::setTitle(const QString &title)
|
||||
@@ -91,21 +98,9 @@ void TutorialOverlay::setTargetWidget(QWidget *w)
|
||||
if (targetWidget)
|
||||
targetWidget->installEventFilter(this);
|
||||
|
||||
updateHoleRect();
|
||||
recomputeLayout();
|
||||
}
|
||||
|
||||
void TutorialOverlay::updateHoleRect()
|
||||
{
|
||||
if (!targetWidget || !targetWidget->isVisible()) {
|
||||
cachedHoleRect = QRect();
|
||||
} else {
|
||||
QPoint globalTopLeft = targetWidget->mapToGlobal(QPoint(0, 0));
|
||||
QPoint localTopLeft = mapFromGlobal(globalTopLeft);
|
||||
cachedHoleRect = QRect(localTopLeft, targetWidget->size()).adjusted(-6, -6, 6, 6);
|
||||
}
|
||||
}
|
||||
|
||||
void TutorialOverlay::setText(const QString &t)
|
||||
{
|
||||
tutorialText = t;
|
||||
@@ -114,6 +109,16 @@ void TutorialOverlay::setText(const QString &t)
|
||||
recomputeLayout();
|
||||
}
|
||||
|
||||
QRect TutorialOverlay::currentHoleRect() const
|
||||
{
|
||||
if (!targetWidget || !targetWidget->isVisible())
|
||||
return QRect();
|
||||
|
||||
QPoint globalTopLeft = targetWidget->mapToGlobal(QPoint(0, 0));
|
||||
QPoint localTopLeft = mapFromGlobal(globalTopLeft);
|
||||
return QRect(localTopLeft, targetWidget->size()).adjusted(-6, -6, 6, 6);
|
||||
}
|
||||
|
||||
void TutorialOverlay::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
recomputeLayout();
|
||||
@@ -125,9 +130,13 @@ bool TutorialOverlay::eventFilter(QObject *obj, QEvent *event)
|
||||
parentResized();
|
||||
}
|
||||
|
||||
if (obj == targetWidget && (event->type() == QEvent::Show || event->type() == QEvent::Hide)) {
|
||||
updateHoleRect();
|
||||
recomputeLayout();
|
||||
if (obj == targetWidget) {
|
||||
if (event->type() == QEvent::Show) {
|
||||
// Defer layout recalculation to give Qt time to finalize geometry
|
||||
QMetaObject::invokeMethod(this, [this]() { recomputeLayout(); }, Qt::QueuedConnection);
|
||||
} else if (event->type() == QEvent::Hide || event->type() == QEvent::Move || event->type() == QEvent::Resize) {
|
||||
recomputeLayout();
|
||||
}
|
||||
}
|
||||
|
||||
return QWidget::eventFilter(obj, event);
|
||||
@@ -138,86 +147,101 @@ void TutorialOverlay::parentResized()
|
||||
if (!parentWidget())
|
||||
return;
|
||||
|
||||
QRect r = parentWidget()->rect();
|
||||
QPoint globalTopLeft = parentWidget()->mapToGlobal(QPoint(0, 0));
|
||||
QWidget *w = parentWidget();
|
||||
|
||||
// Get the parent rect in local coordinates
|
||||
QRect r = w->rect();
|
||||
|
||||
// Map top-left to global coordinates
|
||||
QPoint globalTopLeft = w->mapToGlobal(QPoint(0, 0));
|
||||
|
||||
// Set overlay geometry in screen coords, exactly matching the parent widget
|
||||
r.moveTopLeft(globalTopLeft);
|
||||
setGeometry(r);
|
||||
|
||||
updateHoleRect();
|
||||
recomputeLayout();
|
||||
}
|
||||
|
||||
// Recompute layout for bubble and control bar
|
||||
void TutorialOverlay::recomputeLayout()
|
||||
{
|
||||
updateHoleRect();
|
||||
QRect hole = currentHoleRect();
|
||||
|
||||
if (cachedHoleRect.isEmpty()) {
|
||||
bubble->hide();
|
||||
controlBar->hide();
|
||||
if (hole.isEmpty()) {
|
||||
if (bubble) {
|
||||
bubble->hide();
|
||||
}
|
||||
if (controlBar) {
|
||||
controlBar->hide();
|
||||
}
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
|
||||
show();
|
||||
raise();
|
||||
bubble->show();
|
||||
controlBar->show();
|
||||
|
||||
// ---- Bubble ----
|
||||
QSize bsize = bubble->sizeHint().expandedTo(QSize(160, 60));
|
||||
highlightBubbleRect = computeBubbleRect(cachedHoleRect, bsize);
|
||||
highlightBubbleRect = computeBubbleRect(hole, bsize);
|
||||
bubble->setGeometry(highlightBubbleRect);
|
||||
bubble->show();
|
||||
bubble->raise();
|
||||
|
||||
// ---- Control bar ----
|
||||
controlBar->adjustSize();
|
||||
controlBar->show();
|
||||
|
||||
const int margin = 8;
|
||||
QRect r = rect();
|
||||
|
||||
QList<QPoint> positions = {
|
||||
{r.right() - controlBar->width() - margin, r.bottom() - controlBar->height() - margin}, // bottom-right
|
||||
{r.right() - controlBar->width() - margin, margin}, // top-right
|
||||
{margin, r.bottom() - controlBar->height() - margin}, // bottom-left
|
||||
{margin, margin} // top-left
|
||||
};
|
||||
QList<QPoint> positions = {{r.right() - controlBar->width() - margin, r.bottom() - controlBar->height() - margin},
|
||||
{r.right() - controlBar->width() - margin, margin},
|
||||
{margin, r.bottom() - controlBar->height() - margin},
|
||||
{margin, margin}};
|
||||
|
||||
for (const QPoint &pos : positions) {
|
||||
QRect proposed(pos, controlBar->size());
|
||||
if (!proposed.intersects(cachedHoleRect)) {
|
||||
if (!proposed.intersects(hole)) {
|
||||
controlBar->move(pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
controlBar->raise();
|
||||
update();
|
||||
}
|
||||
|
||||
QRect TutorialOverlay::computeBubbleRect(const QRect &hole, const QSize &bubbleSize) const
|
||||
{
|
||||
const int margin = 16;
|
||||
QRect r = rect();
|
||||
QRect bubble;
|
||||
|
||||
if (hole.isEmpty()) {
|
||||
return QRect(r.center().x() - bubbleSize.width() / 2, r.center().y() - bubbleSize.height() / 2,
|
||||
bubbleSize.width(), bubbleSize.height());
|
||||
bubble = QRect(r.center() - QPoint(bubbleSize.width() / 2, bubbleSize.height() / 2), bubbleSize);
|
||||
} else {
|
||||
bubble = QRect(hole.right() + margin, hole.top(), bubbleSize.width(), bubbleSize.height());
|
||||
|
||||
if (!r.contains(bubble))
|
||||
bubble.moveLeft(hole.left() - margin - bubbleSize.width());
|
||||
|
||||
if (!r.contains(bubble)) {
|
||||
bubble.moveLeft(hole.center().x() - bubbleSize.width() / 2);
|
||||
bubble.moveTop(hole.top() - margin - bubbleSize.height());
|
||||
}
|
||||
|
||||
if (!r.contains(bubble))
|
||||
bubble.moveTop(hole.bottom() + margin);
|
||||
}
|
||||
|
||||
// Prefer right, left, top, bottom
|
||||
QRect tryRect(hole.right() + margin, hole.top(), bubbleSize.width(), bubbleSize.height());
|
||||
if (r.contains(tryRect))
|
||||
return tryRect;
|
||||
// Final clamp to overlay bounds - ensure min <= max for qBound
|
||||
int maxLeft = qMax(r.left(), r.right() - bubble.width());
|
||||
int maxTop = qMax(r.top(), r.bottom() - bubble.height());
|
||||
|
||||
tryRect.moveLeft(hole.left() - margin - bubbleSize.width());
|
||||
if (r.contains(tryRect))
|
||||
return tryRect;
|
||||
bubble.moveLeft(qBound(r.left(), bubble.left(), maxLeft));
|
||||
bubble.moveTop(qBound(r.top(), bubble.top(), maxTop));
|
||||
|
||||
tryRect.moveLeft(hole.center().x() - bubbleSize.width() / 2);
|
||||
tryRect.moveTop(hole.top() - margin - bubbleSize.height());
|
||||
if (r.contains(tryRect))
|
||||
return tryRect;
|
||||
|
||||
tryRect.moveTop(hole.bottom() + margin);
|
||||
return tryRect;
|
||||
return bubble;
|
||||
}
|
||||
|
||||
void TutorialOverlay::paintEvent(QPaintEvent *)
|
||||
@@ -227,9 +251,10 @@ void TutorialOverlay::paintEvent(QPaintEvent *)
|
||||
|
||||
p.fillRect(rect(), QColor(0, 0, 0, 160));
|
||||
|
||||
if (!cachedHoleRect.isEmpty()) {
|
||||
QRect hole = currentHoleRect();
|
||||
if (!hole.isEmpty()) {
|
||||
QPainterPath holePath;
|
||||
holePath.addRoundedRect(cachedHoleRect, 8, 8);
|
||||
holePath.addRoundedRect(hole, 8, 8);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Clear);
|
||||
p.fillPath(holePath, Qt::transparent);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ class QFrame;
|
||||
class TutorialOverlay : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
public:
|
||||
explicit TutorialOverlay(QWidget *parent = nullptr);
|
||||
@@ -17,6 +19,7 @@ public:
|
||||
void setTargetWidget(QWidget *w);
|
||||
void updateHoleRect();
|
||||
void setText(const QString &t);
|
||||
QRect currentHoleRect() const;
|
||||
void setTitle(const QString &title);
|
||||
void setBlocking(bool blockInput);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user