Stop clipping the cat at the window edge

The cat was drawn in a 100 x 90 box that was exactly the window, so anything
reaching past it was cut off: the right whiskers always, and more of them in
poses that push the head forward, up to art x 109 while stretching.

The window box is now 120 x 106 with the drawing inset at (7, 14). The slack is
symmetric on purpose, because facing left mirrors the art about the box centre,
so asymmetric slack would clip the other side instead.

The mood-name mapper grew the shared aspect ratio and became CatDefs, so every
window sizes the cat the same way. Nose and mug offsets follow the new inset.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
JulianandClaude Opus 5 committed 2026-09-11 03:10:04 +02:00
1 parent 403ac30b3a
commit 6b9553f6d4
8 files changed
+78 -66

No files matched your search

+53 -44
View File
@@ -40,7 +40,15 @@ Item {
readonly property bool sleeping: mood === "sleeping"
readonly property bool happy: mood === "loving" || mood === "grooming" || mood === "stretching"
|| mood === "drinking" || petted
readonly property real unit: width / 100
// The cat is drawn in a 100 x 90 box. The window box is larger so whiskers,
// tail and the glyphs above the head stay inside it in every pose. The slack
// has to be symmetric: facing left mirrors the art about the box centre.
// Widest reach is the right whiskers while stretching, art x 109.
readonly property real designWidth: 120
readonly property real designHeight: 106
readonly property real artX: 7
readonly property real artY: 14
readonly property real unit: width / designWidth
// tail pose (design units); swish is added on top by the idle animation
property real tailC1X: 14
@@ -57,8 +65,8 @@ Item {
Behavior on tailX { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
Behavior on tailY { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
implicitWidth: 100
implicitHeight: 90
implicitWidth: designWidth
implicitHeight: designHeight
transform: Scale {
origin.x: cat.width / 2
@@ -67,6 +75,8 @@ Item {
Item {
id: art
x: cat.artX * cat.unit
y: cat.artY * cat.unit
width: 100
height: 90
transformOrigin: Item.TopLeft
@@ -459,6 +469,46 @@ Item {
}
}
// Machine code tag, flashed in the headroom whenever the mood changes
Item {
id: codeTag
x: 16; y: -12
width: codeBg.width
height: codeBg.height
opacity: 0
visible: cat.showCodes && opacity > 0.01
transform: Scale { xScale: cat.facingRight ? 1 : -1; origin.x: codeTag.width / 2 }
z: 10
Accessible.ignored: true
Rectangle {
id: codeBg
width: codeText.implicitWidth + 8
height: codeText.implicitHeight + 4
radius: 3
color: "#242833"
opacity: 0.92
antialiasing: true
Text {
id: codeText
anchors.centerIn: parent
text: cat.moodCode
color: "#8fd6ff"
font.pixelSize: 10
font.bold: true
font.family: "Menlo"
}
}
SequentialAnimation {
id: codeFlash
NumberAnimation { target: codeTag; property: "opacity"; to: 1; duration: 160 }
PauseAnimation { duration: 1500 }
NumberAnimation { target: codeTag; property: "opacity"; to: 0; duration: 500 }
}
}
// speech bubble: "!" / "z" / "♥"
Text {
id: bubble
@@ -490,47 +540,6 @@ Item {
}
}
// Machine code tag above the head, flashed whenever the mood changes
Item {
id: codeTag
x: 30; y: 2
width: codeBg.width
height: codeBg.height
opacity: 0
visible: cat.showCodes && opacity > 0.01
transformOrigin: Item.Center
transform: Scale { xScale: cat.facingRight ? 1 : -1; origin.x: codeTag.width / 2 }
z: 10
scale: cat.unit
Accessible.ignored: true
Rectangle {
id: codeBg
width: codeText.implicitWidth + 10
height: codeText.implicitHeight + 6
radius: 4
color: "#242833"
opacity: 0.92
antialiasing: true
Text {
id: codeText
anchors.centerIn: parent
text: cat.moodCode
color: "#8fd6ff"
font.pixelSize: 11
font.bold: true
font.family: "Menlo"
}
}
SequentialAnimation {
id: codeFlash
NumberAnimation { target: codeTag; property: "opacity"; to: 1; duration: 160 }
PauseAnimation { duration: 1500 }
NumberAnimation { target: codeTag; property: "opacity"; to: 0; duration: 500 }
}
}
onMoodCodeChanged: if (showCodes && moodCode.length > 0) codeFlash.restart()
+16
View File
@@ -0,0 +1,16 @@
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
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"
}
}
-13
View File
@@ -1,13 +0,0 @@
pragma Singleton
import QtQuick
import TNCat
// Maps CatController::Mood onto the strings Cat.qml understands.
QtObject {
readonly property var names: ["walking", "running", "sitting", "grooming", "stretching",
"sleeping", "loving", "alert", "chasing", "batting", "drinking"]
function name(mood) {
return names[mood] !== undefined ? names[mood] : "sitting"
}
}
+2 -2
View File
@@ -13,7 +13,7 @@ Window {
signal settingsRequested()
width: settings.catSize
height: Math.round(settings.catSize * 0.9)
height: Math.round(settings.catSize * CatDefs.aspect)
x: cat.x
y: Screen.desktopAvailableHeight - height - settings.bottomMargin
color: Platform.opaqueBackground ? settings.coat : "transparent"
@@ -41,7 +41,7 @@ Window {
anchors.fill: parent
coat: win.settings.coat
facingRight: win.cat.facingRight
mood: CatMoods.name(win.cat.mood)
mood: CatDefs.mood(win.cat.mood)
lifted: win.cat.dragging
petted: win.cat.hovered && !win.cat.dragging
look: win.cat.look
+2 -2
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 * 0.9)
readonly property int catH: Math.round(settings.catSize * CatDefs.aspect)
readonly property real groundY: height - catH - settings.bottomMargin
// Only the cat itself should swallow clicks; everything else falls through.
@@ -88,7 +88,7 @@ Window {
anchors.fill: parent
coat: win.settings.coat
facingRight: win.cat.facingRight
mood: CatMoods.name(win.cat.mood)
mood: CatDefs.mood(win.cat.mood)
lifted: win.cat.dragging
petted: win.cat.hovered && !win.cat.dragging
look: win.cat.look
+1 -1
View File
@@ -37,7 +37,7 @@ Window {
spacing: 16
Loader {
Layout.preferredWidth: 110
Layout.preferredHeight: 99
Layout.preferredHeight: Math.round(110 * CatDefs.aspect)
active: win.visible
sourceComponent: Cat {
coat: win.settings.coat