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

+71
View File
@@ -0,0 +1,71 @@
#include "traycontroller.h"
#include "catcontroller.h"
#include <QCoreApplication>
#include <QPainter>
#include <QPainterPath>
#include <QLoggingCategory>
TrayController::TrayController(CatController *cat, QObject *parent)
: QObject(parent)
, m_cat(cat)
{
QMenu *tricks = m_menu.addMenu(tr("Tricks"));
const struct { const char *label; CatController::Mood mood; } entries[] = {
{ QT_TR_NOOP("Sit"), CatController::Mood::Sitting },
{ QT_TR_NOOP("Groom"), CatController::Mood::Grooming },
{ QT_TR_NOOP("Stretch"), CatController::Mood::Stretching },
{ QT_TR_NOOP("Dash"), CatController::Mood::Running },
{ QT_TR_NOOP("Nap"), CatController::Mood::Sleeping },
};
for (const auto &e : entries) {
const auto mood = e.mood;
tricks->addAction(tr(e.label), this, [this, mood] { m_cat->doTrick(mood); });
}
m_pauseAction = m_menu.addAction(tr("Let the cat sleep"));
m_pauseAction->setCheckable(true);
connect(m_pauseAction, &QAction::toggled, m_cat, &CatController::setPaused);
connect(m_cat, &CatController::pausedChanged, this, [this] {
m_pauseAction->setChecked(m_cat->paused());
});
m_menu.addAction(tr("Settings…"), this, &TrayController::settingsRequested);
m_menu.addSeparator();
m_menu.addAction(tr("Quit Workcat"), qApp, &QCoreApplication::quit);
m_tray.setIcon(makeIcon());
m_tray.setToolTip(tr("Workcat"));
m_tray.setContextMenu(&m_menu);
// Not every X11 desktop runs a status-notifier host. Without a tray the
// cat itself is the only entry point, so say so instead of failing mutely.
if (QSystemTrayIcon::isSystemTrayAvailable())
m_tray.show();
else
qWarning("Workcat: no system tray on this desktop. Right-click the cat for settings.");
}
QIcon TrayController::makeIcon()
{
QPixmap pm(44, 44);
pm.fill(Qt::transparent);
QPainter p(&pm);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setBrush(Qt::black);
QPainterPath path;
path.addRoundedRect(QRectF(5, 12, 34, 28), 14, 14);
path.moveTo(8, 22); path.lineTo(9, 5); path.lineTo(21, 14); path.closeSubpath();
path.moveTo(36, 22); path.lineTo(35, 5); path.lineTo(23, 14); path.closeSubpath();
p.drawPath(path);
p.setBrush(Qt::white);
p.drawEllipse(QRectF(13, 22, 6, 7));
p.drawEllipse(QRectF(25, 22, 6, 7));
QIcon icon(pm);
icon.setIsMask(true);
return icon;
}