Workcat: cute desktop cat, no window closing

Reworks the example into a pure desktop-pet: the cat wanders, sits, grooms,
stretches, dashes and naps, reacts to petting and dragging, and follows the
cursor with its eyes. The window-scanning and Accessibility code is gone.

The cat is drawn from rounded primitives plus Shape paths in a fixed 100x90
design space, so it scales cleanly to any size.

Linux/X11 is the primary target: the tray is optional, Wayland gets a warning,
and WORKCAT_X11_BYPASS / WORKCAT_OPAQUE cover uncooperative window managers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
JulianandClaude Opus 5 committed 2026-09-10 22:20:35 +02:00
commit acef398f49
17 files changed
+1584

No files matched your search

+101
View File
@@ -0,0 +1,101 @@
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <QTimer>
#include <QElapsedTimer>
#include <QPointF>
class AppSettings;
class QWindow;
// Drives the cat: wanders along the bottom of the screen, occasionally dashes,
// sits, grooms, stretches or naps, and reacts to petting and dragging.
class CatController : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("Provided by C++")
Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged)
Q_PROPERTY(qreal screenWidth READ screenWidth WRITE setScreenWidth NOTIFY screenWidthChanged)
Q_PROPERTY(qreal catWidth READ catWidth WRITE setCatWidth NOTIFY catWidthChanged)
Q_PROPERTY(bool facingRight READ facingRight NOTIFY facingRightChanged)
Q_PROPERTY(Mood mood READ mood NOTIFY moodChanged)
Q_PROPERTY(bool paused READ paused WRITE setPaused NOTIFY pausedChanged)
Q_PROPERTY(bool dragging READ dragging WRITE setDragging NOTIFY draggingChanged)
Q_PROPERTY(bool hovered READ hovered WRITE setHovered NOTIFY hoveredChanged)
Q_PROPERTY(QPointF look READ look NOTIFY lookChanged)
public:
enum class Mood { Walking, Running, Sitting, Grooming, Stretching, Sleeping, Loving, Alert };
Q_ENUM(Mood)
explicit CatController(AppSettings *settings, QObject *parent = nullptr);
void setWindow(QWindow *window);
qreal x() const { return m_x; }
void setX(qreal x);
qreal screenWidth() const { return m_screenWidth; }
void setScreenWidth(qreal width);
qreal catWidth() const { return m_catWidth; }
void setCatWidth(qreal width);
bool facingRight() const { return m_facingRight; }
Mood mood() const { return m_mood; }
bool paused() const { return m_paused; }
void setPaused(bool paused);
bool dragging() const { return m_dragging; }
void setDragging(bool dragging);
bool hovered() const { return m_hovered; }
void setHovered(bool hovered);
QPointF look() const { return m_look; }
Q_INVOKABLE void pet();
Q_INVOKABLE void doTrick(Mood mood);
signals:
void xChanged();
void screenWidthChanged();
void catWidthChanged();
void facingRightChanged();
void moodChanged();
void pausedChanged();
void draggingChanged();
void hoveredChanged();
void lookChanged();
private:
void tick();
void trackCursor();
void setMood(Mood mood);
void setFacingRight(bool right);
void pickNextMood();
void holdMood(Mood mood, int ms);
AppSettings *m_settings;
QWindow *m_window = nullptr;
QTimer m_tickTimer;
QTimer m_moodTimer;
QTimer m_cursorTimer;
QElapsedTimer m_clock;
qint64 m_lastTickMs = 0;
qreal m_x = 120;
qreal m_screenWidth = 1440;
qreal m_catWidth = 120;
bool m_facingRight = true;
Mood m_mood = Mood::Walking;
bool m_paused = false;
bool m_dragging = false;
bool m_hovered = false;
bool m_frozen = false;
QPointF m_look;
};