Fix the collapsed cat window on Linux, warn about the compositor

The CatDefs singleton did not resolve on the reporter's Linux build, which
showed up as "property mood of object CatDefs is not a function". The same
lookup failure made CatDefs.aspect undefined, so the cat window's height became
NaN and the cat rendered as a moving line. The mug survived because its height
used a literal ratio.

The singleton is gone rather than repaired, since nothing in it had to be
shared. The mood name now comes from C++, derived from the enum key, so there
is one source of truth instead of a list in QML that had to match. Window sizes
now follow the implicit size of the drawing they contain, so the proportions
live in the one file that defines them.

Also for X11: TNCat now warns, naming Xfce's compositor setting, when its
window comes up without an alpha channel, which is what a missing compositor
looks like and why the mug had a black background. Opaque mode no longer hides
the chip and the mug. TNCAT_DEBUG=1 logs window geometry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
JulianandClaude Opus 5 committed 2026-09-11 09:33:44 +02:00
1 parent 9106ce47f4
commit 0aceb3665d
12 files changed
+94 -36

No files matched your search

-19
View File
@@ -1,19 +0,0 @@
pragma Singleton
import QtQuick
// Shared facts about the drawing, so every window sizes the cat the same way.
QtObject {
// Cat.qml draws in a 100 x 90 box inside a 120 x 106 window box. The slack
// holds the whiskers, the tail and the glyphs that float above the head.
readonly property real aspect: 106 / 120
// Chip.qml draws in a 52 x 58 box, large enough to hold the tumbling curl.
readonly property real chipAspect: 58 / 52
readonly property var moodNames: ["walking", "running", "sitting", "grooming", "stretching",
"sleeping", "loving", "alert", "chasing", "batting", "drinking"]
function mood(value) {
return moodNames[value] !== undefined ? moodNames[value] : "sitting"
}
}
+22 -3
View File
@@ -13,7 +13,8 @@ Window {
signal settingsRequested()
width: settings.catSize
height: Math.round(settings.catSize * CatDefs.aspect)
// The drawing knows its own proportions; no constant to keep in sync.
height: Math.round(width * sprite.implicitHeight / sprite.implicitWidth)
x: cat.x
y: Screen.desktopAvailableHeight - height - settings.bottomMargin
color: Platform.opaqueBackground ? settings.coat : "transparent"
@@ -26,8 +27,25 @@ Window {
Component.onCompleted: {
cat.stageWidth = Screen.desktopAvailableWidth
cat.catWidth = width
Platform.checkTranslucency(win)
if (Platform.debug)
win.logGeometry("created")
}
function logGeometry(what) {
console.log("TNCat cat window", what,
"x", win.x, "y", win.y, "w", win.width, "h", win.height,
"| screen", Screen.width + "x" + Screen.height,
"available", Screen.desktopAvailableWidth + "x" + Screen.desktopAvailableHeight,
"| platform", Platform.name, "overlay", Platform.overlayMode)
}
onHeightChanged: if (Platform.debug) logGeometry("height changed")
onWidthChanged: {
cat.catWidth = width
if (Platform.debug)
logGeometry("width changed")
}
onWidthChanged: cat.catWidth = width
Screen.onDesktopAvailableWidthChanged: cat.stageWidth = Screen.desktopAvailableWidth
// The eyes need to know where the face sits on the desktop.
@@ -38,10 +56,11 @@ Window {
}
Cat {
id: sprite
anchors.fill: parent
coat: win.settings.coat
facingRight: win.cat.facingRight
mood: CatDefs.mood(win.cat.mood)
mood: win.cat.moodName
lifted: win.cat.dragging
petted: win.cat.hovered && !win.cat.dragging
look: win.cat.look
+4 -3
View File
@@ -13,21 +13,22 @@ Window {
readonly property int chipSize: Math.round(settings.catSize * 0.46)
width: chipSize
height: Math.round(chipSize * CatDefs.chipAspect)
height: Math.round(width * curl.implicitHeight / curl.implicitWidth)
x: cat.chipX - width / 2
y: Screen.desktopAvailableHeight - height - settings.bottomMargin
color: "transparent"
color: Platform.opaqueBackground ? "#d8d8d8" : "transparent"
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool
| Qt.NoDropShadowWindowHint | Qt.WindowDoesNotAcceptFocus
| Qt.WindowTransparentForInput
| (Platform.bypassWindowManager ? Qt.X11BypassWindowManagerHint : 0)
visible: cat.chipVisible && !Platform.opaqueBackground
visible: cat.chipVisible
Behavior on x {
NumberAnimation { duration: 320; easing.type: Easing.OutQuad }
}
Chip {
id: curl
anchors.fill: parent
spin: win.cat.chipSpin
}
+4 -3
View File
@@ -12,17 +12,18 @@ Window {
readonly property int cupSize: Math.round(settings.catSize * 0.38)
width: cupSize
height: Math.round(cupSize * 1.18)
height: Math.round(width * mug.implicitHeight / mug.implicitWidth)
x: cat.cupX - width / 2
y: Screen.desktopAvailableHeight - height - settings.bottomMargin
color: "transparent"
color: Platform.opaqueBackground ? "#d8d8d8" : "transparent"
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool
| Qt.NoDropShadowWindowHint | Qt.WindowDoesNotAcceptFocus
| Qt.WindowTransparentForInput
| (Platform.bypassWindowManager ? Qt.X11BypassWindowManagerHint : 0)
visible: cat.cupVisible && !Platform.opaqueBackground
visible: cat.cupVisible
Cup {
id: mug
anchors.fill: parent
}
}
+7 -4
View File
@@ -18,7 +18,7 @@ Window {
property bool overlayActive: false
readonly property int catW: settings.catSize
readonly property int catH: Math.round(settings.catSize * CatDefs.aspect)
readonly property int catH: Math.round(catW * sprite.implicitHeight / sprite.implicitWidth)
readonly property real groundY: height - catH - settings.bottomMargin
// Only the cat itself should swallow clicks; everything else falls through.
@@ -57,10 +57,11 @@ Window {
}
Chip {
id: curl
x: win.cat.chipX - width / 2
y: win.height - height - win.settings.bottomMargin
width: Math.round(win.settings.catSize * 0.46)
height: Math.round(width * CatDefs.chipAspect)
height: Math.round(width * curl.implicitHeight / curl.implicitWidth)
visible: win.cat.chipVisible
spin: win.cat.chipSpin
@@ -70,10 +71,11 @@ Window {
}
Cup {
id: mug
x: win.cat.cupX - width / 2
y: win.height - height - win.settings.bottomMargin
width: Math.round(win.settings.catSize * 0.38)
height: Math.round(width * 1.18)
height: Math.round(width * mug.implicitHeight / mug.implicitWidth)
visible: win.cat.cupVisible
}
@@ -85,10 +87,11 @@ Window {
height: win.catH
Cat {
id: sprite
anchors.fill: parent
coat: win.settings.coat
facingRight: win.cat.facingRight
mood: CatDefs.mood(win.cat.mood)
mood: win.cat.moodName
lifted: win.cat.dragging
petted: win.cat.hovered && !win.cat.dragging
look: win.cat.look
+1 -1
View File
@@ -52,7 +52,7 @@ Window {
Loader {
id: preview
width: 106
height: Math.round(106 * CatDefs.aspect)
height: Math.round(width * 106 / 120)
anchors.verticalCenter: parent.verticalCenter
active: win.visible
sourceComponent: Cat {