mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-29 18:13:55 -07:00
Follow parent window properly.
Took 19 seconds
This commit is contained in:
parent
7168802bbf
commit
e9e3515628
4 changed files with 112 additions and 65 deletions
|
|
@ -17,6 +17,10 @@ public:
|
||||||
void setProgress(int stepNum, int totalSteps, int overallStep, int overallTotal);
|
void setProgress(int stepNum, int totalSteps, int overallStep, int overallTotal);
|
||||||
void setInteractionHint(const QString &hint);
|
void setInteractionHint(const QString &hint);
|
||||||
void setValidationHint(const QString &hint);
|
void setValidationHint(const QString &hint);
|
||||||
|
QGridLayout *getLayout() const
|
||||||
|
{
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void clearValidationHint();
|
void clearValidationHint();
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,8 @@ void TutorialController::start()
|
||||||
QTimer::singleShot(0, this, [this]() {
|
QTimer::singleShot(0, this, [this]() {
|
||||||
QWidget *win = tutorializedWidget->window();
|
QWidget *win = tutorializedWidget->window();
|
||||||
|
|
||||||
// Reparent to make absolutely sure
|
tutorialOverlay->setParent(win); // triggers changeEvent and installs filter
|
||||||
tutorialOverlay->setParent(win);
|
tutorialOverlay->setGeometry(win->rect());
|
||||||
tutorialOverlay->setGeometry(0, 0, win->width(), win->height());
|
|
||||||
|
|
||||||
// Stack order
|
|
||||||
tutorialOverlay->stackUnder(nullptr);
|
|
||||||
tutorialOverlay->show();
|
tutorialOverlay->show();
|
||||||
tutorialOverlay->raise();
|
tutorialOverlay->raise();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ TutorialOverlay::TutorialOverlay(QWidget *parent) : QWidget(parent)
|
||||||
{
|
{
|
||||||
setAttribute(Qt::WA_TranslucentBackground, true);
|
setAttribute(Qt::WA_TranslucentBackground, true);
|
||||||
setAttribute(Qt::WA_TransparentForMouseEvents, false);
|
setAttribute(Qt::WA_TransparentForMouseEvents, false);
|
||||||
|
setAttribute(Qt::WA_StaticContents, false);
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent, false);
|
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||||
setAutoFillBackground(false);
|
setAutoFillBackground(false);
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
|
|
@ -81,14 +81,28 @@ void TutorialOverlay::showEvent(QShowEvent *event)
|
||||||
QWidget::showEvent(event);
|
QWidget::showEvent(event);
|
||||||
|
|
||||||
if (parentWidget()) {
|
if (parentWidget()) {
|
||||||
QWidget *parent = parentWidget();
|
setGeometry(0, 0, parentWidget()->width(), parentWidget()->height());
|
||||||
setGeometry(0, 0, parent->width(), parent->height());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
raise();
|
raise();
|
||||||
parentResized();
|
parentResized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TutorialOverlay::changeEvent(QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::ParentChange) {
|
||||||
|
// Remove filter from old parent (Qt has already changed parentWidget() by now,
|
||||||
|
// so iterate sender list isn't easy — reinstall unconditionally on the new parent)
|
||||||
|
QWidget *p = parentWidget();
|
||||||
|
if (p) {
|
||||||
|
p->installEventFilter(this); // safe to call multiple times
|
||||||
|
setGeometry(p->rect());
|
||||||
|
raise();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QWidget::changeEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
void TutorialOverlay::setTitle(const QString &title)
|
void TutorialOverlay::setTitle(const QString &title)
|
||||||
{
|
{
|
||||||
titleLabel->setText(title);
|
titleLabel->setText(title);
|
||||||
|
|
@ -107,23 +121,14 @@ void TutorialOverlay::setInteractive(bool interactive, bool clickThrough)
|
||||||
|
|
||||||
if (nextButton) {
|
if (nextButton) {
|
||||||
nextButton->setEnabled(!interactive);
|
nextButton->setEnabled(!interactive);
|
||||||
if (interactive) {
|
nextButton->setToolTip(interactive ? "Complete the highlighted action to continue" : "Next step");
|
||||||
nextButton->setToolTip("Complete the highlighted action to continue");
|
|
||||||
} else {
|
|
||||||
nextButton->setToolTip("Next step");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextSeqButton) {
|
if (nextSeqButton) {
|
||||||
nextSeqButton->setEnabled(!interactive);
|
nextSeqButton->setEnabled(!interactive);
|
||||||
if (interactive) {
|
nextSeqButton->setToolTip(interactive ? "Complete the highlighted action to continue" : "Next chapter");
|
||||||
nextSeqButton->setToolTip("Complete the highlighted action to continue");
|
|
||||||
} else {
|
|
||||||
nextSeqButton->setToolTip("Next chapter");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update mask when clickThrough changes
|
|
||||||
updateMask();
|
updateMask();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,6 +159,7 @@ void TutorialOverlay::setProgress(int stepNum,
|
||||||
|
|
||||||
void TutorialOverlay::setTargetWidget(QWidget *w)
|
void TutorialOverlay::setTargetWidget(QWidget *w)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (targetWidget) {
|
if (targetWidget) {
|
||||||
targetWidget->removeEventFilter(this);
|
targetWidget->removeEventFilter(this);
|
||||||
}
|
}
|
||||||
|
|
@ -169,6 +175,7 @@ void TutorialOverlay::setTargetWidget(QWidget *w)
|
||||||
|
|
||||||
void TutorialOverlay::setText(const QString &t)
|
void TutorialOverlay::setText(const QString &t)
|
||||||
{
|
{
|
||||||
|
|
||||||
tutorialText = t;
|
tutorialText = t;
|
||||||
bubble->setText(t);
|
bubble->setText(t);
|
||||||
bubble->adjustSize();
|
bubble->adjustSize();
|
||||||
|
|
@ -181,56 +188,54 @@ QRect TutorialOverlay::currentHoleRect() const
|
||||||
return QRect();
|
return QRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
QPoint targetGlobal = targetWidget->mapToGlobal(QPoint(0, 0));
|
QPoint p = targetWidget->mapToGlobal(QPoint(0, 0));
|
||||||
QPoint targetInOverlay = mapFromGlobal(targetGlobal);
|
QPoint local = mapFromGlobal(p);
|
||||||
|
|
||||||
return QRect(targetInOverlay, targetWidget->size()).adjusted(-6, -6, 6, 6);
|
QRect r(local, targetWidget->size());
|
||||||
|
r = r.adjusted(-6, -6, 6, 6);
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TutorialOverlay::mousePressEvent(QMouseEvent *event)
|
void TutorialOverlay::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
QRect hole = currentHoleRect();
|
QRect hole = currentHoleRect();
|
||||||
|
|
||||||
// Check if click is in the highlighted area
|
|
||||||
if (hole.contains(event->pos())) {
|
if (hole.contains(event->pos())) {
|
||||||
// For non-clickthrough steps, emit targetClicked for advancement
|
|
||||||
if (!allowClickThrough && isInteractive && !qobject_cast<QLineEdit *>(targetWidget) &&
|
if (!allowClickThrough && isInteractive && !qobject_cast<QLineEdit *>(targetWidget) &&
|
||||||
!qobject_cast<QTextEdit *>(targetWidget) && !qobject_cast<QPlainTextEdit *>(targetWidget) &&
|
!qobject_cast<QTextEdit *>(targetWidget) && !qobject_cast<QPlainTextEdit *>(targetWidget) &&
|
||||||
!qobject_cast<QComboBox *>(targetWidget)) {
|
!qobject_cast<QComboBox *>(targetWidget)) {
|
||||||
QTimer::singleShot(100, this, [this]() { emit targetClicked(); });
|
QTimer::singleShot(100, this, [this]() { emit targetClicked(); });
|
||||||
}
|
}
|
||||||
// If allowClickThrough, the mask ensures events pass through
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click outside highlighted area - block it
|
|
||||||
event->accept();
|
event->accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TutorialOverlay::updateMask()
|
void TutorialOverlay::updateMask()
|
||||||
{
|
{
|
||||||
if (allowClickThrough) {
|
clearMask();
|
||||||
QRect hole = currentHoleRect();
|
|
||||||
if (!hole.isEmpty()) {
|
if (!isVisible() || !parentWidget()) {
|
||||||
// Create a mask that excludes the hole area
|
return;
|
||||||
QRegion fullRegion(rect());
|
}
|
||||||
QRegion holeRegion(hole);
|
if (!allowClickThrough) {
|
||||||
QRegion maskRegion = fullRegion.subtracted(holeRegion);
|
return;
|
||||||
setMask(maskRegion);
|
}
|
||||||
} else {
|
|
||||||
clearMask();
|
QRect hole = currentHoleRect();
|
||||||
}
|
|
||||||
} else {
|
if (!hole.isEmpty()) {
|
||||||
clearMask();
|
QRegion full(rect());
|
||||||
|
QRegion cut(hole);
|
||||||
|
setMask(full.subtracted(cut));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TutorialOverlay::event(QEvent *event)
|
bool TutorialOverlay::event(QEvent *event)
|
||||||
{
|
{
|
||||||
// Update mask on any event that might change geometry
|
|
||||||
if (event->type() == QEvent::Move || event->type() == QEvent::Resize) {
|
|
||||||
updateMask();
|
|
||||||
}
|
|
||||||
return QWidget::event(event);
|
return QWidget::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,15 +246,29 @@ void TutorialOverlay::resizeEvent(QResizeEvent *)
|
||||||
|
|
||||||
bool TutorialOverlay::eventFilter(QObject *obj, QEvent *event)
|
bool TutorialOverlay::eventFilter(QObject *obj, QEvent *event)
|
||||||
{
|
{
|
||||||
if (obj == parentWidget() && (event->type() == QEvent::Resize || event->type() == QEvent::Move)) {
|
if (obj == parentWidget()) {
|
||||||
parentResized();
|
switch (event->type()) {
|
||||||
|
case QEvent::Resize:
|
||||||
|
case QEvent::Move: // window dragged to another monitor / position
|
||||||
|
parentResized();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj == targetWidget) {
|
if (obj == targetWidget) {
|
||||||
if (event->type() == QEvent::Show) {
|
switch (event->type()) {
|
||||||
QMetaObject::invokeMethod(this, [this]() { recomputeLayout(); }, Qt::QueuedConnection);
|
case QEvent::Show:
|
||||||
} else if (event->type() == QEvent::Hide || event->type() == QEvent::Move || event->type() == QEvent::Resize) {
|
QMetaObject::invokeMethod(this, [this] { recomputeLayout(); }, Qt::QueuedConnection);
|
||||||
recomputeLayout();
|
break;
|
||||||
|
case QEvent::Hide:
|
||||||
|
case QEvent::Move:
|
||||||
|
case QEvent::Resize:
|
||||||
|
recomputeLayout();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,22 +281,38 @@ void TutorialOverlay::parentResized()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setGeometry(0, 0, parentWidget()->width(), parentWidget()->height());
|
const QSize s = parentWidget()->size();
|
||||||
|
|
||||||
|
setGeometry(0, 0, s.width(), s.height());
|
||||||
|
|
||||||
|
clearMask();
|
||||||
recomputeLayout();
|
recomputeLayout();
|
||||||
|
|
||||||
|
setAttribute(Qt::WA_NoSystemBackground, false);
|
||||||
|
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||||
|
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TutorialOverlay::recomputeLayout()
|
void TutorialOverlay::recomputeLayout()
|
||||||
{
|
{
|
||||||
|
if (!parentWidget()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(parentWidget()->window()->geometry().size());
|
||||||
|
|
||||||
|
bubble->adjustSize();
|
||||||
|
|
||||||
QRect hole = currentHoleRect();
|
QRect hole = currentHoleRect();
|
||||||
|
|
||||||
if (hole.isEmpty()) {
|
if (hole.isEmpty()) {
|
||||||
if (bubble) {
|
bubble->hide();
|
||||||
bubble->hide();
|
controlBar->hide();
|
||||||
}
|
|
||||||
if (controlBar) {
|
|
||||||
controlBar->hide();
|
|
||||||
}
|
|
||||||
hide();
|
hide();
|
||||||
|
|
||||||
|
update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,9 +321,9 @@ void TutorialOverlay::recomputeLayout()
|
||||||
|
|
||||||
QSize bsize = bubble->sizeHint().expandedTo(QSize(160, 60));
|
QSize bsize = bubble->sizeHint().expandedTo(QSize(160, 60));
|
||||||
highlightBubbleRect = computeBubbleRect(hole, bsize);
|
highlightBubbleRect = computeBubbleRect(hole, bsize);
|
||||||
|
|
||||||
bubble->setGeometry(highlightBubbleRect);
|
bubble->setGeometry(highlightBubbleRect);
|
||||||
bubble->show();
|
bubble->show();
|
||||||
bubble->raise();
|
|
||||||
|
|
||||||
controlBar->adjustSize();
|
controlBar->adjustSize();
|
||||||
controlBar->show();
|
controlBar->show();
|
||||||
|
|
@ -301,28 +336,39 @@ void TutorialOverlay::recomputeLayout()
|
||||||
{margin, r.bottom() - controlBar->height() - margin},
|
{margin, r.bottom() - controlBar->height() - margin},
|
||||||
{margin, margin}};
|
{margin, margin}};
|
||||||
|
|
||||||
|
QRect placedRect;
|
||||||
|
|
||||||
for (const QPoint &pos : positions) {
|
for (const QPoint &pos : positions) {
|
||||||
QRect proposed(pos, controlBar->size());
|
QRect proposed(pos, controlBar->size());
|
||||||
if (!proposed.intersects(hole)) {
|
|
||||||
controlBar->move(pos);
|
if (!proposed.intersects(hole) && !proposed.intersects(highlightBubbleRect)) {
|
||||||
|
placedRect = proposed;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
controlBar->raise();
|
if (!placedRect.isValid()) {
|
||||||
|
placedRect = QRect(QPoint(margin, margin), controlBar->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
controlBar->move(placedRect.topLeft());
|
||||||
|
controlBar->show();
|
||||||
|
|
||||||
|
updateMask();
|
||||||
update();
|
update();
|
||||||
updateMask(); // Update mask after layout changes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect TutorialOverlay::computeBubbleRect(const QRect &hole, const QSize &bubbleSize) const
|
QRect TutorialOverlay::computeBubbleRect(const QRect &hole, const QSize &bubbleSize) const
|
||||||
{
|
{
|
||||||
const int margin = 16;
|
|
||||||
QRect r = rect();
|
QRect r = rect();
|
||||||
|
|
||||||
QRect bubble;
|
QRect bubble;
|
||||||
|
|
||||||
if (hole.isEmpty()) {
|
if (hole.isEmpty()) {
|
||||||
bubble = QRect(r.center() - QPoint(bubbleSize.width() / 2, bubbleSize.height() / 2), bubbleSize);
|
bubble = QRect(r.center() - QPoint(bubbleSize.width() / 2, bubbleSize.height() / 2), bubbleSize);
|
||||||
} else {
|
} else {
|
||||||
|
const int margin = 16;
|
||||||
|
|
||||||
bubble = QRect(hole.right() + margin, hole.top(), bubbleSize.width(), bubbleSize.height());
|
bubble = QRect(hole.right() + margin, hole.top(), bubbleSize.width(), bubbleSize.height());
|
||||||
|
|
||||||
if (!r.contains(bubble)) {
|
if (!r.contains(bubble)) {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ public:
|
||||||
|
|
||||||
void parentResized();
|
void parentResized();
|
||||||
QRect currentHoleRect() const;
|
QRect currentHoleRect() const;
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void nextStep();
|
void nextStep();
|
||||||
|
|
@ -42,7 +43,7 @@ protected:
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
void updateMask();
|
void updateMask();
|
||||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
void changeEvent(QEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void recomputeLayout();
|
void recomputeLayout();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue