Rename to TNCat, add chip chase, G-codes and a Wayland layout
The project is now TNCat, after HEIDENHAIN's TNC controls. Binary, QML module URI, settings path, bundle id and .desktop entry follow; the environment switches are TNCAT_*. Entertainment: a curled steel chip skitters in every minute or two, the cat runs it down and bats it away a few times before sitting back looking pleased. Mood changes flash the matching machine code above its head, G00 for a sprint, G01 for a stroll, M08 while it washes, G28 for a stretch, M30 for a nap. Window handling was split into two layouts. X11 and macOS keep one small window per object, positioned by the app. Wayland gets a screen-filling overlay whose input region is masked down to the cat, since a Wayland client cannot place its own window. Overlay mode is automatic there and available elsewhere via TNCAT_OVERLAY=1. Also adds a ground-line setting so the cat can walk above a softkey row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
acef398f49
commit
0f1df75a1d
19 files changed
+817
-185
No files matched your search
@@ -59,6 +59,46 @@ void AppSettings::setFollowCursor(bool follow)
|
||||
emit followCursorChanged();
|
||||
}
|
||||
|
||||
int AppSettings::bottomMargin() const
|
||||
{
|
||||
return m_settings.value("bottomMargin", 0).toInt();
|
||||
}
|
||||
|
||||
void AppSettings::setBottomMargin(int pixels)
|
||||
{
|
||||
pixels = qBound(0, pixels, 600);
|
||||
if (pixels == bottomMargin())
|
||||
return;
|
||||
m_settings.setValue("bottomMargin", pixels);
|
||||
emit bottomMarginChanged();
|
||||
}
|
||||
|
||||
bool AppSettings::showCodes() const
|
||||
{
|
||||
return m_settings.value("showCodes", true).toBool();
|
||||
}
|
||||
|
||||
void AppSettings::setShowCodes(bool show)
|
||||
{
|
||||
if (show == showCodes())
|
||||
return;
|
||||
m_settings.setValue("showCodes", show);
|
||||
emit showCodesChanged();
|
||||
}
|
||||
|
||||
bool AppSettings::chipsEnabled() const
|
||||
{
|
||||
return m_settings.value("chipsEnabled", true).toBool();
|
||||
}
|
||||
|
||||
void AppSettings::setChipsEnabled(bool enabled)
|
||||
{
|
||||
if (enabled == chipsEnabled())
|
||||
return;
|
||||
m_settings.setValue("chipsEnabled", enabled);
|
||||
emit chipsEnabledChanged();
|
||||
}
|
||||
|
||||
int AppSettings::petCount() const
|
||||
{
|
||||
return m_settings.value("petCount", 0).toInt();
|
||||
|
||||
@@ -15,6 +15,9 @@ class AppSettings : public QObject
|
||||
Q_PROPERTY(int catSize READ catSize WRITE setCatSize NOTIFY catSizeChanged)
|
||||
Q_PROPERTY(qreal walkSpeed READ walkSpeed WRITE setWalkSpeed NOTIFY walkSpeedChanged)
|
||||
Q_PROPERTY(bool followCursor READ followCursor WRITE setFollowCursor NOTIFY followCursorChanged)
|
||||
Q_PROPERTY(int bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged)
|
||||
Q_PROPERTY(bool showCodes READ showCodes WRITE setShowCodes NOTIFY showCodesChanged)
|
||||
Q_PROPERTY(bool chipsEnabled READ chipsEnabled WRITE setChipsEnabled NOTIFY chipsEnabledChanged)
|
||||
Q_PROPERTY(int petCount READ petCount NOTIFY petCountChanged)
|
||||
|
||||
public:
|
||||
@@ -32,6 +35,15 @@ public:
|
||||
bool followCursor() const;
|
||||
void setFollowCursor(bool follow);
|
||||
|
||||
int bottomMargin() const;
|
||||
void setBottomMargin(int pixels);
|
||||
|
||||
bool showCodes() const;
|
||||
void setShowCodes(bool show);
|
||||
|
||||
bool chipsEnabled() const;
|
||||
void setChipsEnabled(bool enabled);
|
||||
|
||||
int petCount() const;
|
||||
void incrementPetCount();
|
||||
|
||||
@@ -40,6 +52,9 @@ signals:
|
||||
void catSizeChanged();
|
||||
void walkSpeedChanged();
|
||||
void followCursorChanged();
|
||||
void bottomMarginChanged();
|
||||
void showCodesChanged();
|
||||
void chipsEnabledChanged();
|
||||
void petCountChanged();
|
||||
|
||||
private:
|
||||
|
||||
+128
-18
@@ -4,7 +4,12 @@
|
||||
#include <QCursor>
|
||||
#include <QMetaEnum>
|
||||
#include <QRandomGenerator>
|
||||
#include <QWindow>
|
||||
|
||||
namespace {
|
||||
constexpr qreal kChipReach = 26.0; // how close the nose has to get
|
||||
constexpr int kChipMinDelayMs = 45000;
|
||||
constexpr int kChipMaxDelayMs = 150000;
|
||||
}
|
||||
|
||||
CatController::CatController(AppSettings *settings, QObject *parent)
|
||||
: QObject(parent)
|
||||
@@ -23,8 +28,11 @@ CatController::CatController(AppSettings *settings, QObject *parent)
|
||||
connect(&m_cursorTimer, &QTimer::timeout, this, &CatController::trackCursor);
|
||||
m_cursorTimer.start();
|
||||
|
||||
// WORKCAT_POSE=Sleeping freezes the cat in one mood (handy for screenshots).
|
||||
const QByteArray pose = qgetenv("WORKCAT_POSE");
|
||||
m_chipTimer.setSingleShot(true);
|
||||
connect(&m_chipTimer, &QTimer::timeout, this, &CatController::tossChip);
|
||||
|
||||
// TNCAT_POSE=Sleeping freezes the cat in one mood (handy for screenshots).
|
||||
const QByteArray pose = qgetenv("TNCAT_POSE");
|
||||
if (!pose.isEmpty()) {
|
||||
const QMetaEnum me = QMetaEnum::fromType<Mood>();
|
||||
bool ok = false;
|
||||
@@ -34,13 +42,15 @@ CatController::CatController(AppSettings *settings, QObject *parent)
|
||||
setMood(static_cast<Mood>(value));
|
||||
}
|
||||
}
|
||||
if (!m_frozen)
|
||||
if (!m_frozen) {
|
||||
pickNextMood();
|
||||
scheduleChip();
|
||||
}
|
||||
}
|
||||
|
||||
void CatController::setWindow(QWindow *window)
|
||||
qreal CatController::catNose() const
|
||||
{
|
||||
m_window = window;
|
||||
return m_facingRight ? m_x + m_catWidth * 0.78 : m_x + m_catWidth * 0.22;
|
||||
}
|
||||
|
||||
void CatController::setX(qreal x)
|
||||
@@ -51,12 +61,12 @@ void CatController::setX(qreal x)
|
||||
emit xChanged();
|
||||
}
|
||||
|
||||
void CatController::setScreenWidth(qreal width)
|
||||
void CatController::setStageWidth(qreal width)
|
||||
{
|
||||
if (qFuzzyCompare(width, m_screenWidth))
|
||||
if (qFuzzyCompare(width, m_stageWidth))
|
||||
return;
|
||||
m_screenWidth = width;
|
||||
emit screenWidthChanged();
|
||||
m_stageWidth = width;
|
||||
emit stageWidthChanged();
|
||||
}
|
||||
|
||||
void CatController::setCatWidth(qreal width)
|
||||
@@ -67,6 +77,14 @@ void CatController::setCatWidth(qreal width)
|
||||
emit catWidthChanged();
|
||||
}
|
||||
|
||||
void CatController::setCatCenter(const QPointF ¢er)
|
||||
{
|
||||
if (center == m_catCenter)
|
||||
return;
|
||||
m_catCenter = center;
|
||||
emit catCenterChanged();
|
||||
}
|
||||
|
||||
void CatController::setPaused(bool paused)
|
||||
{
|
||||
if (paused == m_paused)
|
||||
@@ -75,9 +93,12 @@ void CatController::setPaused(bool paused)
|
||||
emit pausedChanged();
|
||||
if (paused) {
|
||||
m_moodTimer.stop();
|
||||
m_chipTimer.stop();
|
||||
endChase();
|
||||
setMood(Mood::Sleeping);
|
||||
} else {
|
||||
pickNextMood();
|
||||
scheduleChip();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +131,7 @@ void CatController::pet()
|
||||
if (m_paused || m_frozen)
|
||||
return;
|
||||
m_settings->incrementPetCount();
|
||||
endChase();
|
||||
holdMood(Mood::Loving, 1800);
|
||||
}
|
||||
|
||||
@@ -118,6 +140,7 @@ void CatController::doTrick(Mood mood)
|
||||
if (m_paused)
|
||||
return;
|
||||
m_frozen = false;
|
||||
endChase();
|
||||
holdMood(mood, mood == Mood::Sleeping ? 15000 : 3500);
|
||||
}
|
||||
|
||||
@@ -127,17 +150,85 @@ void CatController::holdMood(Mood mood, int ms)
|
||||
m_moodTimer.start(ms);
|
||||
}
|
||||
|
||||
// A curled metal chip skitters in from one side and the cat goes after it.
|
||||
void CatController::tossChip()
|
||||
{
|
||||
if (m_paused || m_frozen || m_chipVisible)
|
||||
return;
|
||||
if (!m_settings->chipsEnabled()) {
|
||||
scheduleChip();
|
||||
return;
|
||||
}
|
||||
|
||||
auto *rng = QRandomGenerator::global();
|
||||
const bool fromLeft = rng->bounded(2) == 0;
|
||||
const qreal margin = qMin(200.0, m_stageWidth * 0.2);
|
||||
setChipX(fromLeft ? margin : m_stageWidth - margin);
|
||||
|
||||
m_batsLeft = rng->bounded(2, 5);
|
||||
m_chipVisible = true;
|
||||
emit chipVisibleChanged();
|
||||
|
||||
m_moodTimer.stop();
|
||||
setMood(Mood::Chasing);
|
||||
}
|
||||
|
||||
void CatController::batChip()
|
||||
{
|
||||
auto *rng = QRandomGenerator::global();
|
||||
++m_chipKick;
|
||||
emit chipKickChanged();
|
||||
|
||||
const qreal distance = rng->bounded(140, 380);
|
||||
qreal next = m_chipX + (m_facingRight ? distance : -distance);
|
||||
const qreal edge = 40.0;
|
||||
if (next < edge || next > m_stageWidth - edge) {
|
||||
next = qBound(edge, next, m_stageWidth - edge);
|
||||
--m_batsLeft; // a chip against the wall is nearly done for
|
||||
}
|
||||
setChipX(next);
|
||||
|
||||
if (--m_batsLeft <= 0) {
|
||||
endChase();
|
||||
holdMood(Mood::Sitting, 2500); // pleased with itself
|
||||
scheduleChip();
|
||||
} else {
|
||||
holdMood(Mood::Batting, 420);
|
||||
}
|
||||
}
|
||||
|
||||
void CatController::endChase()
|
||||
{
|
||||
if (!m_chipVisible)
|
||||
return;
|
||||
m_chipVisible = false;
|
||||
m_batsLeft = 0;
|
||||
emit chipVisibleChanged();
|
||||
}
|
||||
|
||||
void CatController::scheduleChip()
|
||||
{
|
||||
m_chipTimer.start(QRandomGenerator::global()->bounded(kChipMinDelayMs, kChipMaxDelayMs));
|
||||
}
|
||||
|
||||
void CatController::setChipX(qreal x)
|
||||
{
|
||||
if (qFuzzyCompare(x, m_chipX))
|
||||
return;
|
||||
m_chipX = x;
|
||||
emit chipXChanged();
|
||||
}
|
||||
|
||||
void CatController::trackCursor()
|
||||
{
|
||||
if (!m_window || !m_settings->followCursor()) {
|
||||
if (m_catCenter.isNull() || !m_settings->followCursor()) {
|
||||
if (!m_look.isNull()) {
|
||||
m_look = {};
|
||||
emit lookChanged();
|
||||
}
|
||||
return;
|
||||
}
|
||||
const QPointF center = m_window->geometry().center();
|
||||
QPointF delta = QCursor::pos() - center;
|
||||
QPointF delta = QCursor::pos() - m_catCenter;
|
||||
if (!m_facingRight)
|
||||
delta.setX(-delta.x());
|
||||
const qreal range = 400.0;
|
||||
@@ -154,14 +245,28 @@ void CatController::tick()
|
||||
const qreal dt = qMin(now - m_lastTickMs, qint64(100)) / 1000.0;
|
||||
m_lastTickMs = now;
|
||||
|
||||
const bool moving = m_mood == Mood::Walking || m_mood == Mood::Running;
|
||||
if (!moving || m_dragging || m_paused || m_frozen)
|
||||
if (m_dragging || m_paused || m_frozen)
|
||||
return;
|
||||
|
||||
const qreal maxX = qMax(0.0, m_stageWidth - m_catWidth);
|
||||
|
||||
if (m_mood == Mood::Chasing) {
|
||||
const qreal delta = m_chipX - catNose();
|
||||
setFacingRight(delta >= 0);
|
||||
if (qAbs(delta) <= kChipReach) {
|
||||
batChip();
|
||||
return;
|
||||
}
|
||||
const qreal step = m_settings->walkSpeed() * 4.0 * dt;
|
||||
setX(qBound(0.0, m_x + (delta > 0 ? step : -step), maxX));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_mood != Mood::Walking && m_mood != Mood::Running)
|
||||
return;
|
||||
|
||||
const qreal speed = m_settings->walkSpeed() * (m_mood == Mood::Running ? 4.5 : 1.0);
|
||||
const qreal step = speed * dt;
|
||||
qreal next = m_x + (m_facingRight ? step : -step);
|
||||
const qreal maxX = qMax(0.0, m_screenWidth - m_catWidth);
|
||||
qreal next = m_x + (m_facingRight ? speed * dt : -speed * dt);
|
||||
if (next >= maxX) {
|
||||
next = maxX;
|
||||
setFacingRight(false);
|
||||
@@ -196,6 +301,11 @@ void CatController::pickNextMood()
|
||||
{
|
||||
if (m_paused || m_dragging || m_frozen)
|
||||
return;
|
||||
if (m_chipVisible) { // a chase is still on, keep after the chip
|
||||
setMood(Mood::Chasing);
|
||||
return;
|
||||
}
|
||||
|
||||
auto *rng = QRandomGenerator::global();
|
||||
const int roll = rng->bounded(100);
|
||||
|
||||
|
||||
+38
-12
@@ -7,10 +7,9 @@
|
||||
#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.
|
||||
// Drives the cat: wanders along its ground line, sits, grooms, stretches and
|
||||
// naps, chases the odd metal chip, and reacts to petting and dragging.
|
||||
class CatController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -18,8 +17,9 @@ class CatController : public QObject
|
||||
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 stageWidth READ stageWidth WRITE setStageWidth NOTIFY stageWidthChanged)
|
||||
Q_PROPERTY(qreal catWidth READ catWidth WRITE setCatWidth NOTIFY catWidthChanged)
|
||||
Q_PROPERTY(QPointF catCenter READ catCenter WRITE setCatCenter NOTIFY catCenterChanged)
|
||||
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)
|
||||
@@ -27,23 +27,29 @@ class CatController : public QObject
|
||||
Q_PROPERTY(bool hovered READ hovered WRITE setHovered NOTIFY hoveredChanged)
|
||||
Q_PROPERTY(QPointF look READ look NOTIFY lookChanged)
|
||||
|
||||
Q_PROPERTY(bool chipVisible READ chipVisible NOTIFY chipVisibleChanged)
|
||||
Q_PROPERTY(qreal chipX READ chipX NOTIFY chipXChanged)
|
||||
Q_PROPERTY(int chipKick READ chipKick NOTIFY chipKickChanged)
|
||||
|
||||
public:
|
||||
enum class Mood { Walking, Running, Sitting, Grooming, Stretching, Sleeping, Loving, Alert };
|
||||
enum class Mood { Walking, Running, Sitting, Grooming, Stretching, Sleeping, Loving, Alert, Chasing, Batting };
|
||||
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 stageWidth() const { return m_stageWidth; }
|
||||
void setStageWidth(qreal width);
|
||||
|
||||
qreal catWidth() const { return m_catWidth; }
|
||||
void setCatWidth(qreal width);
|
||||
|
||||
// Where the cat's face is on the desktop, so the eyes can find the cursor.
|
||||
QPointF catCenter() const { return m_catCenter; }
|
||||
void setCatCenter(const QPointF ¢er);
|
||||
|
||||
bool facingRight() const { return m_facingRight; }
|
||||
Mood mood() const { return m_mood; }
|
||||
|
||||
@@ -58,39 +64,54 @@ public:
|
||||
|
||||
QPointF look() const { return m_look; }
|
||||
|
||||
bool chipVisible() const { return m_chipVisible; }
|
||||
qreal chipX() const { return m_chipX; }
|
||||
int chipKick() const { return m_chipKick; }
|
||||
|
||||
Q_INVOKABLE void pet();
|
||||
Q_INVOKABLE void doTrick(Mood mood);
|
||||
Q_INVOKABLE void tossChip();
|
||||
|
||||
signals:
|
||||
void xChanged();
|
||||
void screenWidthChanged();
|
||||
void stageWidthChanged();
|
||||
void catWidthChanged();
|
||||
void catCenterChanged();
|
||||
void facingRightChanged();
|
||||
void moodChanged();
|
||||
void pausedChanged();
|
||||
void draggingChanged();
|
||||
void hoveredChanged();
|
||||
void lookChanged();
|
||||
void chipVisibleChanged();
|
||||
void chipXChanged();
|
||||
void chipKickChanged();
|
||||
|
||||
private:
|
||||
void tick();
|
||||
void trackCursor();
|
||||
void setMood(Mood mood);
|
||||
void setFacingRight(bool right);
|
||||
void setChipX(qreal x);
|
||||
void pickNextMood();
|
||||
void holdMood(Mood mood, int ms);
|
||||
void batChip();
|
||||
void endChase();
|
||||
void scheduleChip();
|
||||
qreal catNose() const;
|
||||
|
||||
AppSettings *m_settings;
|
||||
QWindow *m_window = nullptr;
|
||||
QTimer m_tickTimer;
|
||||
QTimer m_moodTimer;
|
||||
QTimer m_cursorTimer;
|
||||
QTimer m_chipTimer;
|
||||
QElapsedTimer m_clock;
|
||||
qint64 m_lastTickMs = 0;
|
||||
|
||||
qreal m_x = 120;
|
||||
qreal m_screenWidth = 1440;
|
||||
qreal m_stageWidth = 1440;
|
||||
qreal m_catWidth = 120;
|
||||
QPointF m_catCenter;
|
||||
bool m_facingRight = true;
|
||||
Mood m_mood = Mood::Walking;
|
||||
bool m_paused = false;
|
||||
@@ -98,4 +119,9 @@ private:
|
||||
bool m_hovered = false;
|
||||
bool m_frozen = false;
|
||||
QPointF m_look;
|
||||
|
||||
bool m_chipVisible = false;
|
||||
qreal m_chipX = 0;
|
||||
int m_chipKick = 0;
|
||||
int m_batsLeft = 0;
|
||||
};
|
||||
+5
-8
@@ -1,7 +1,6 @@
|
||||
#include <QApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQuickStyle>
|
||||
#include <QQuickWindow>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "appsettings.h"
|
||||
@@ -10,9 +9,8 @@
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication::setOrganizationName("workcat");
|
||||
QCoreApplication::setOrganizationDomain("workcat.app");
|
||||
QCoreApplication::setApplicationName("Workcat");
|
||||
QCoreApplication::setOrganizationName("TNCat");
|
||||
QCoreApplication::setApplicationName("TNCat");
|
||||
QApplication::setQuitOnLastWindowClosed(false);
|
||||
|
||||
// QApplication (not QGuiApplication) so QSystemTrayIcon can show a menu.
|
||||
@@ -24,7 +22,7 @@ int main(int argc, char *argv[])
|
||||
TrayController tray(&cat);
|
||||
|
||||
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) {
|
||||
qWarning("Workcat: Wayland does not let a client place its own window. "
|
||||
qWarning("TNCat: Wayland does not let a client place its own window. "
|
||||
"Run with QT_QPA_PLATFORM=xcb for the cat to walk.");
|
||||
}
|
||||
|
||||
@@ -35,10 +33,9 @@ int main(int argc, char *argv[])
|
||||
});
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
|
||||
[] { QCoreApplication::exit(1); }, Qt::QueuedConnection);
|
||||
engine.loadFromModule("Workcat", "Main");
|
||||
engine.loadFromModule("TNCat", "Main");
|
||||
|
||||
if (auto *root = qobject_cast<QQuickWindow *>(engine.rootObjects().value(0))) {
|
||||
cat.setWindow(root);
|
||||
if (QObject *root = engine.rootObjects().value(0)) {
|
||||
QObject::connect(&tray, &TrayController::settingsRequested, root,
|
||||
[root] { QMetaObject::invokeMethod(root, "openSettings"); });
|
||||
}
|
||||
|
||||
+11
-2
@@ -1,11 +1,20 @@
|
||||
#include "platforminfo.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QRegion>
|
||||
|
||||
PlatformInfo::PlatformInfo(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_name(QGuiApplication::platformName())
|
||||
, m_bypass(qEnvironmentVariableIntValue("WORKCAT_X11_BYPASS") != 0)
|
||||
, m_opaque(qEnvironmentVariableIntValue("WORKCAT_OPAQUE") != 0)
|
||||
, m_overlay(qEnvironmentVariableIntValue("TNCAT_OVERLAY") != 0)
|
||||
, m_bypass(qEnvironmentVariableIntValue("TNCAT_X11_BYPASS") != 0)
|
||||
, m_opaque(qEnvironmentVariableIntValue("TNCAT_OPAQUE") != 0)
|
||||
{
|
||||
}
|
||||
|
||||
void PlatformInfo::setInputRegion(QQuickWindow *window, const QRectF ®ion)
|
||||
{
|
||||
if (!window)
|
||||
return;
|
||||
window->setMask(QRegion(region.toAlignedRect()));
|
||||
}
|
||||
+18
-3
@@ -2,9 +2,12 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QRectF>
|
||||
#include <QString>
|
||||
#include <QQuickWindow>
|
||||
|
||||
// Small facts about the host that QML needs in order to pick window flags.
|
||||
// Small facts about the host that QML needs in order to pick window flags,
|
||||
// plus the input-region call that makes an overlay window click-through.
|
||||
class PlatformInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -14,6 +17,7 @@ class PlatformInfo : public QObject
|
||||
Q_PROPERTY(QString name READ name CONSTANT)
|
||||
Q_PROPERTY(bool isX11 READ isX11 CONSTANT)
|
||||
Q_PROPERTY(bool isWayland READ isWayland CONSTANT)
|
||||
Q_PROPERTY(bool overlayMode READ overlayMode CONSTANT)
|
||||
Q_PROPERTY(bool bypassWindowManager READ bypassWindowManager CONSTANT)
|
||||
Q_PROPERTY(bool opaqueBackground READ opaqueBackground CONSTANT)
|
||||
|
||||
@@ -24,16 +28,27 @@ public:
|
||||
bool isX11() const { return m_name == QLatin1String("xcb"); }
|
||||
bool isWayland() const { return m_name.startsWith(QLatin1String("wayland")); }
|
||||
|
||||
// WORKCAT_X11_BYPASS=1 turns the cat into an override-redirect window.
|
||||
// Wayland forbids a client from placing its own window, so there the cat
|
||||
// lives inside one screen-filling overlay and only moves within it.
|
||||
// TNCAT_OVERLAY=1 forces the same mode elsewhere.
|
||||
bool overlayMode() const { return m_overlay || isWayland(); }
|
||||
|
||||
// TNCAT_X11_BYPASS=1 turns the cat into an override-redirect window.
|
||||
// Useful on window managers that refuse to keep a Qt::Tool window on top.
|
||||
bool bypassWindowManager() const { return m_bypass; }
|
||||
|
||||
// WORKCAT_OPAQUE=1 paints a solid background instead of relying on a
|
||||
// TNCAT_OPAQUE=1 paints a solid background instead of relying on a
|
||||
// compositor. Without compositing X11 renders transparency as black.
|
||||
bool opaqueBackground() const { return m_opaque; }
|
||||
|
||||
// Restricts the window's input region to one rectangle so clicks land on
|
||||
// whatever is behind the rest of the overlay. Implemented by the X11 and
|
||||
// Wayland plugins; a no-op on platforms that ignore the mask.
|
||||
Q_INVOKABLE void setInputRegion(QQuickWindow *window, const QRectF ®ion);
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
bool m_overlay = false;
|
||||
bool m_bypass = false;
|
||||
bool m_opaque = false;
|
||||
};
|
||||
@@ -23,6 +23,8 @@ TrayController::TrayController(CatController *cat, QObject *parent)
|
||||
tricks->addAction(tr(e.label), this, [this, mood] { m_cat->doTrick(mood); });
|
||||
}
|
||||
|
||||
m_menu.addAction(tr("Toss a chip"), m_cat, &CatController::tossChip);
|
||||
|
||||
m_pauseAction = m_menu.addAction(tr("Let the cat sleep"));
|
||||
m_pauseAction->setCheckable(true);
|
||||
connect(m_pauseAction, &QAction::toggled, m_cat, &CatController::setPaused);
|
||||
@@ -32,10 +34,10 @@ TrayController::TrayController(CatController *cat, QObject *parent)
|
||||
|
||||
m_menu.addAction(tr("Settings…"), this, &TrayController::settingsRequested);
|
||||
m_menu.addSeparator();
|
||||
m_menu.addAction(tr("Quit Workcat"), qApp, &QCoreApplication::quit);
|
||||
m_menu.addAction(tr("Quit TNCat"), qApp, &QCoreApplication::quit);
|
||||
|
||||
m_tray.setIcon(makeIcon());
|
||||
m_tray.setToolTip(tr("Workcat"));
|
||||
m_tray.setToolTip(tr("TNCat"));
|
||||
m_tray.setContextMenu(&m_menu);
|
||||
|
||||
// Not every X11 desktop runs a status-notifier host. Without a tray the
|
||||
@@ -43,7 +45,7 @@ TrayController::TrayController(CatController *cat, QObject *parent)
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable())
|
||||
m_tray.show();
|
||||
else
|
||||
qWarning("Workcat: no system tray on this desktop. Right-click the cat for settings.");
|
||||
qWarning("TNCat: no system tray on this desktop. Right-click the cat for settings.");
|
||||
}
|
||||
|
||||
QIcon TrayController::makeIcon()
|
||||
|
||||
Reference in new issue
Block a user