fix(scene): the doge collects the coins, and no more slow first lap

Three things you spotted on the History banner:

- Slow first lap that restarted at speed. The dog and the money animated
  `to: scene.width`, and the QQuickWidget starts at a placeholder width: the
  first lap crawled across the wrong distance, then the real width arrived,
  re-bound `to`, and restarted the animation. Motion is now driven by a
  unitless 0..1 progress and mapped onto the current width each frame, so a
  resize repositions without restarting.

- Coins sat mid-sky and clipped the top border. They now rest on the grass
  with a low bob, at the doge's pickup height.

- The doge collects them. A coin is collected once the doge's snout reaches
  its centre: it pops, fades, and floats a "+1". The collected state is
  derived from the run position, so the coins simply come back when the lap
  restarts, with nothing to reset.

Reduced motion still gets a static field: doge parked at the left, every coin
present, money parked in the sky.

Verified by headless qml6 render at banner (1000x140, 620x120) and lock-screen
(900x380) sizes, motion and reduced-motion, with no QML warnings.
This commit is contained in:
jwinterm
2026-08-31 12:14:46 -04:00
parent 6942cbb459
commit 2a54663f76
3 changed files with 116 additions and 91 deletions
-46
View File
@@ -1,46 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: The Monero Project
#include "IdleDoge.h"
#include <QEvent>
#include <QMovie>
#include "utils/config.h"
IdleDoge::IdleDoge(QWidget *parent, int height)
: QLabel(parent)
, m_movie(new QMovie(":/assets/images/dog_sitting.gif", QByteArray(), this))
{
m_movie->jumpToFrame(0);
const QSize frame = m_movie->currentImage().size();
if (frame.height() > 0)
m_movie->setScaledSize(QSize(frame.width() * height / frame.height(), height));
this->setMovie(m_movie);
this->setAttribute(Qt::WA_TransparentForMouseEvents); // never block the form
this->adjustSize();
if (conf()->get(Config::reducedMotion).toBool())
m_movie->jumpToFrame(0); // still frame
else
m_movie->start();
if (parent) {
parent->installEventFilter(this);
this->reposition();
this->raise();
}
}
void IdleDoge::reposition() {
if (auto *p = parentWidget())
this->move(p->width() - this->width() - 14, p->height() - this->height() - 14);
}
bool IdleDoge::eventFilter(QObject *o, QEvent *e) {
if (o == parentWidget() && (e->type() == QEvent::Resize || e->type() == QEvent::Show)) {
this->reposition();
this->raise();
}
return QLabel::eventFilter(o, e);
}
-25
View File
@@ -1,25 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: The Monero Project
#ifndef WOWLET_IDLEDOGE_H
#define WOWLET_IDLEDOGE_H
#include <QLabel>
class QMovie;
// wowlet: a small sitting doge that floats, click-through, in the bottom-right
// corner of its parent — the "most quiet" ambient idle for task screens (Send,
// Receive). Freezes to a still frame when "Prefer reduced motion" is set.
class IdleDoge : public QLabel {
Q_OBJECT
public:
explicit IdleDoge(QWidget *parent, int height = 44);
protected:
bool eventFilter(QObject *o, QEvent *e) override;
private:
void reposition();
QMovie *m_movie;
};
#endif //WOWLET_IDLEDOGE_H