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>
624 lines
27 KiB
QML
624 lines
27 KiB
QML
pragma ComponentBehavior: Bound
|
|
import QtQuick
|
|
import QtQuick.Shapes
|
|
|
|
// A chibi cat drawn from primitives and vector paths.
|
|
// Design space is 100 x 90 units, ground line at y = 84, cat faces right.
|
|
Item {
|
|
id: cat
|
|
|
|
property color coat: "#f4b183"
|
|
property bool facingRight: true
|
|
property string mood: "walking" // walking running sitting grooming stretching sleeping loving alert
|
|
property bool lifted: false
|
|
property bool petted: false
|
|
property point look: Qt.point(0, 0)
|
|
property bool showCodes: true
|
|
property real sipDip: 0
|
|
|
|
// The cat announces what it is doing in the machine's own language.
|
|
readonly property string moodCode: mood === "running" || mood === "chasing" ? "G00"
|
|
: mood === "walking" ? "G01"
|
|
: mood === "sitting" ? "M00"
|
|
: mood === "grooming" ? "M08"
|
|
: mood === "stretching" ? "G28"
|
|
: mood === "sleeping" ? "M30"
|
|
: mood === "drinking" ? "M01"
|
|
: ""
|
|
|
|
readonly property bool dark: coat.hslLightness < 0.45
|
|
readonly property color coatShade: Qt.darker(coat, 1.12)
|
|
readonly property color coatLight: Qt.lighter(coat, dark ? 1.35 : 1.16)
|
|
readonly property color ink: dark ? Qt.lighter(coat, 1.9) : Qt.darker(coat, 1.7)
|
|
readonly property color eyeColor: dark ? "#f7d67c" : "#2f2a36"
|
|
readonly property color pink: "#f2929f"
|
|
readonly property color blush: "#f77d95"
|
|
|
|
readonly property bool walking: mood === "walking"
|
|
readonly property bool running: mood === "running" || mood === "chasing"
|
|
readonly property bool moving: (walking || running) && !lifted
|
|
readonly property bool sleeping: mood === "sleeping"
|
|
readonly property bool happy: mood === "loving" || mood === "grooming" || mood === "stretching"
|
|
|| mood === "drinking" || petted
|
|
// 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
|
|
property real tailC1Y: 66
|
|
property real tailC2X: 8
|
|
property real tailC2Y: 46
|
|
property real tailX: 13
|
|
property real tailY: 34
|
|
property real tailSwish: 0
|
|
Behavior on tailC1X { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
Behavior on tailC1Y { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
Behavior on tailC2X { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
Behavior on tailC2Y { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
Behavior on tailX { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
Behavior on tailY { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
|
|
|
|
implicitWidth: designWidth
|
|
implicitHeight: designHeight
|
|
|
|
transform: Scale {
|
|
origin.x: cat.width / 2
|
|
xScale: cat.facingRight ? 1 : -1
|
|
}
|
|
|
|
Item {
|
|
id: art
|
|
x: cat.artX * cat.unit
|
|
y: cat.artY * cat.unit
|
|
width: 100
|
|
height: 90
|
|
transformOrigin: Item.TopLeft
|
|
scale: cat.unit
|
|
|
|
// ground shadow
|
|
Rectangle {
|
|
id: shadow
|
|
x: 24; y: 79
|
|
width: 56; height: 8
|
|
radius: 4
|
|
color: "#000000"
|
|
opacity: 0.16
|
|
antialiasing: true
|
|
scale: 1 - torso.y * -0.02
|
|
}
|
|
|
|
Item {
|
|
id: torso
|
|
width: 100
|
|
height: 90
|
|
|
|
// tail
|
|
Shape {
|
|
id: tailShape
|
|
z: -1
|
|
anchors.fill: parent
|
|
preferredRendererType: Shape.CurveRenderer
|
|
ShapePath {
|
|
id: tail
|
|
strokeColor: cat.coatShade
|
|
strokeWidth: 7
|
|
capStyle: ShapePath.RoundCap
|
|
fillColor: "transparent"
|
|
startX: 30; startY: 60
|
|
PathCubic {
|
|
control1X: cat.tailC1X; control1Y: cat.tailC1Y
|
|
control2X: cat.tailC2X + cat.tailSwish * 0.5; control2Y: cat.tailC2Y
|
|
x: cat.tailX + cat.tailSwish; y: cat.tailY
|
|
}
|
|
}
|
|
|
|
SequentialAnimation {
|
|
running: !cat.sleeping && !cat.lifted
|
|
loops: Animation.Infinite
|
|
alwaysRunToEnd: true
|
|
NumberAnimation { target: cat; property: "tailSwish"; to: 6; duration: 900; easing.type: Easing.InOutSine }
|
|
NumberAnimation { target: cat; property: "tailSwish"; to: -4; duration: 900; easing.type: Easing.InOutSine }
|
|
}
|
|
}
|
|
|
|
// legs — the far pair sits behind the body, the near pair in front
|
|
Repeater {
|
|
id: legs
|
|
model: 4
|
|
Rectangle {
|
|
id: leg
|
|
required property int index
|
|
readonly property bool front: index >= 2
|
|
readonly property bool far: index % 2 === 0
|
|
readonly property real swing: cat.running ? 38 : 24
|
|
readonly property real restRotation: front ? (cat.mood === "stretching" ? (far ? 48 : 40)
|
|
: cat.lifted ? (far ? -12 : -18) : 0)
|
|
: (cat.mood === "stretching" ? -6 : 0)
|
|
z: far ? 0 : 2
|
|
x: front ? (cat.seated && !cat.lifted ? 60 : 56) + (far ? 0 : 8)
|
|
: (cat.seated && !cat.lifted ? 36 : 30) + (far ? 0 : 8)
|
|
y: cat.sleeping && !cat.lifted ? 74
|
|
: (front ? (cat.seated && !cat.lifted ? 60 : 63)
|
|
: (cat.seated && !cat.lifted ? 72 : 63))
|
|
width: 10
|
|
height: cat.sleeping && !cat.lifted ? 8
|
|
: (front ? (cat.seated && !cat.lifted ? 24 : 20)
|
|
: (cat.seated && !cat.lifted ? 12 : 20))
|
|
radius: 5
|
|
color: far ? cat.coatShade : cat.coat
|
|
antialiasing: true
|
|
transformOrigin: Item.Top
|
|
|
|
Behavior on x { NumberAnimation { duration: 400; easing.type: Easing.InOutQuad } }
|
|
Behavior on y { NumberAnimation { duration: 400; easing.type: Easing.InOutQuad } }
|
|
Behavior on height { NumberAnimation { duration: 400; easing.type: Easing.InOutQuad } }
|
|
|
|
// paw
|
|
Rectangle {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.bottom: parent.bottom
|
|
width: parent.width
|
|
height: 6.5
|
|
radius: 3.25
|
|
color: cat.coatLight
|
|
antialiasing: true
|
|
|
|
Rectangle { // toe line
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: 1.2
|
|
width: 4.5
|
|
height: 1
|
|
radius: 0.5
|
|
color: cat.coatShade
|
|
opacity: 0.7
|
|
visible: !leg.far && !cat.sleeping
|
|
}
|
|
}
|
|
|
|
// While walking the gait animation owns rotation; otherwise the pose does.
|
|
states: State {
|
|
name: "resting"
|
|
when: !cat.moving
|
|
PropertyChanges { leg.rotation: leg.restRotation }
|
|
}
|
|
transitions: Transition {
|
|
NumberAnimation { property: "rotation"; duration: 400; easing.type: Easing.OutBack }
|
|
}
|
|
|
|
SequentialAnimation on rotation {
|
|
running: cat.moving
|
|
loops: Animation.Infinite
|
|
alwaysRunToEnd: true
|
|
NumberAnimation { to: leg.far ? leg.swing : -leg.swing; duration: cat.running ? 130 : 240; easing.type: Easing.InOutSine }
|
|
NumberAnimation { to: leg.far ? -leg.swing : leg.swing; duration: cat.running ? 130 : 240; easing.type: Easing.InOutSine }
|
|
}
|
|
}
|
|
}
|
|
|
|
// body
|
|
Rectangle {
|
|
id: body
|
|
z: 1
|
|
x: 24; y: 46
|
|
width: 50; height: 32
|
|
radius: 16
|
|
color: cat.coat
|
|
antialiasing: true
|
|
|
|
Behavior on x { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on y { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on width { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on height { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on rotation { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
|
|
Rectangle { // belly
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: 3
|
|
width: parent.width * 0.5
|
|
height: parent.height * 0.42
|
|
radius: height / 2
|
|
color: cat.coatLight
|
|
antialiasing: true
|
|
}
|
|
}
|
|
|
|
// head
|
|
Item {
|
|
id: head
|
|
z: 3
|
|
x: 46; y: 12
|
|
width: 50; height: 44
|
|
transformOrigin: Item.Bottom
|
|
|
|
Behavior on x { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on y { NumberAnimation { duration: 450; easing.type: Easing.InOutQuad } }
|
|
Behavior on rotation { NumberAnimation { duration: 450; easing.type: Easing.InOutBack } }
|
|
|
|
// ears
|
|
Shape {
|
|
anchors.fill: parent
|
|
preferredRendererType: Shape.CurveRenderer
|
|
ShapePath { // left ear
|
|
fillColor: cat.coat
|
|
strokeColor: "transparent"
|
|
startX: 6; startY: 14
|
|
PathQuad { controlX: 2; controlY: -4; x: 8; y: -4 }
|
|
PathQuad { controlX: 14; controlY: -2; x: 24; y: 5 }
|
|
PathLine { x: 6; y: 14 }
|
|
}
|
|
ShapePath { // left inner
|
|
fillColor: cat.pink
|
|
strokeColor: "transparent"
|
|
startX: 9; startY: 11
|
|
PathQuad { controlX: 6; controlY: 2; x: 9; y: 1 }
|
|
PathQuad { controlX: 14; controlY: 3; x: 19; y: 6 }
|
|
PathLine { x: 9; y: 11 }
|
|
}
|
|
ShapePath { // right ear
|
|
fillColor: cat.coat
|
|
strokeColor: "transparent"
|
|
startX: 44; startY: 14
|
|
PathQuad { controlX: 48; controlY: -4; x: 42; y: -4 }
|
|
PathQuad { controlX: 36; controlY: -2; x: 26; y: 5 }
|
|
PathLine { x: 44; y: 14 }
|
|
}
|
|
ShapePath { // right inner
|
|
fillColor: cat.pink
|
|
strokeColor: "transparent"
|
|
startX: 41; startY: 11
|
|
PathQuad { controlX: 44; controlY: 2; x: 41; y: 1 }
|
|
PathQuad { controlX: 36; controlY: 3; x: 31; y: 6 }
|
|
PathLine { x: 41; y: 11 }
|
|
}
|
|
}
|
|
|
|
Rectangle { // face
|
|
x: 0; y: 4
|
|
width: 50; height: 40
|
|
radius: 21
|
|
color: cat.coat
|
|
antialiasing: true
|
|
}
|
|
|
|
// cheeks
|
|
Repeater {
|
|
model: 2
|
|
Rectangle {
|
|
required property int index
|
|
x: index === 0 ? 5 : 35
|
|
y: 30
|
|
width: 10; height: 5.5
|
|
radius: 3
|
|
color: cat.blush
|
|
opacity: cat.happy ? 0.75 : 0.4
|
|
antialiasing: true
|
|
Behavior on opacity { NumberAnimation { duration: 300 } }
|
|
}
|
|
}
|
|
|
|
// eyes
|
|
Repeater {
|
|
model: 2
|
|
Item {
|
|
id: eye
|
|
required property int index
|
|
property real open: 1
|
|
x: (index === 0 ? 12 : 29) + cat.look.x * 2.2
|
|
y: 17 + cat.look.y * 1.6
|
|
width: 9.5; height: 12
|
|
|
|
Behavior on open { NumberAnimation { duration: 110 } }
|
|
|
|
Rectangle {
|
|
id: eyeBall
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: eye.width * (cat.mood === "alert" || cat.mood === "batting" ? 1.15 : 1)
|
|
height: eye.height * eye.open * (cat.mood === "alert" || cat.mood === "batting" ? 1.15 : 1)
|
|
radius: width / 2
|
|
color: cat.eyeColor
|
|
visible: eye.open > 0.08
|
|
antialiasing: true
|
|
|
|
Rectangle { // highlight
|
|
x: parent.width * 0.5; y: parent.height * 0.14
|
|
width: 3.4; height: 3.4 * eye.open
|
|
radius: 1.7
|
|
color: "white"
|
|
antialiasing: true
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.22; y: parent.height * 0.6
|
|
width: 1.6; height: 1.6 * eye.open
|
|
radius: 0.8
|
|
color: "white"
|
|
antialiasing: true
|
|
}
|
|
}
|
|
|
|
Shape { // closed eye: happy arch or sleepy curve
|
|
anchors.fill: parent
|
|
visible: eye.open <= 0.08
|
|
preferredRendererType: Shape.CurveRenderer
|
|
ShapePath {
|
|
strokeColor: cat.ink
|
|
strokeWidth: 1.8
|
|
capStyle: ShapePath.RoundCap
|
|
fillColor: "transparent"
|
|
startX: 0.5; startY: 7
|
|
PathQuad {
|
|
controlX: 4.75
|
|
controlY: cat.happy ? 2 : 10
|
|
x: 9; y: 7
|
|
}
|
|
}
|
|
}
|
|
|
|
Timer { // blink
|
|
interval: 2600 + Math.random() * 3800
|
|
running: !cat.sleeping && !cat.happy && cat.mood !== "alert"
|
|
repeat: true
|
|
onTriggered: { interval = 2600 + Math.random() * 3800; blinkAnim.start() }
|
|
}
|
|
SequentialAnimation {
|
|
id: blinkAnim
|
|
NumberAnimation { target: eye; property: "open"; to: 0; duration: 80 }
|
|
NumberAnimation { target: eye; property: "open"; to: 1; duration: 120 }
|
|
}
|
|
|
|
states: State {
|
|
name: "closed"
|
|
when: cat.sleeping || cat.happy
|
|
PropertyChanges { eye.open: 0 }
|
|
}
|
|
}
|
|
}
|
|
|
|
// nose
|
|
Rectangle {
|
|
x: 22.5; y: 28
|
|
width: 5; height: 3.6
|
|
radius: 1.8
|
|
color: cat.pink
|
|
antialiasing: true
|
|
}
|
|
|
|
// mouth "ω"
|
|
Shape {
|
|
anchors.fill: parent
|
|
preferredRendererType: Shape.CurveRenderer
|
|
ShapePath {
|
|
strokeColor: cat.ink
|
|
strokeWidth: 1.1
|
|
capStyle: ShapePath.RoundCap
|
|
fillColor: "transparent"
|
|
startX: 20.5; startY: 32
|
|
PathQuad { controlX: 22.5; controlY: 35.2; x: 25; y: 32.4 }
|
|
PathQuad { controlX: 27.5; controlY: 35.2; x: 29.5; y: 32 }
|
|
}
|
|
}
|
|
|
|
// whiskers
|
|
Repeater {
|
|
model: 4
|
|
Rectangle {
|
|
required property int index
|
|
readonly property bool leftSide: index < 2
|
|
x: leftSide ? -7 : 45
|
|
y: 25 + (index % 2) * 4.5
|
|
width: 12
|
|
height: 1
|
|
radius: 0.5
|
|
color: cat.ink
|
|
opacity: 0.55
|
|
rotation: (leftSide ? 1 : -1) * ((index % 2) === 0 ? 7 : -7)
|
|
antialiasing: true
|
|
}
|
|
}
|
|
|
|
// raised paw while grooming
|
|
Rectangle {
|
|
x: 8; y: 26
|
|
width: 10; height: 16
|
|
radius: 5
|
|
color: cat.coat
|
|
antialiasing: true
|
|
visible: cat.mood === "grooming"
|
|
transformOrigin: Item.Bottom
|
|
SequentialAnimation on rotation {
|
|
running: cat.mood === "grooming"
|
|
loops: Animation.Infinite
|
|
NumberAnimation { to: 14; duration: 260; easing.type: Easing.InOutSine }
|
|
NumberAnimation { to: -6; duration: 260; easing.type: Easing.InOutSine }
|
|
}
|
|
Rectangle {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
y: 0
|
|
width: parent.width; height: 6
|
|
radius: 3
|
|
color: cat.coatLight
|
|
}
|
|
}
|
|
}
|
|
|
|
// walking bob
|
|
SequentialAnimation on y {
|
|
running: cat.moving
|
|
loops: Animation.Infinite
|
|
alwaysRunToEnd: true
|
|
NumberAnimation { to: cat.running ? -4 : -2; duration: cat.running ? 130 : 240; easing.type: Easing.InOutSine }
|
|
NumberAnimation { to: 0; duration: cat.running ? 130 : 240; easing.type: Easing.InOutSine }
|
|
}
|
|
|
|
// purr wiggle while petted / loving
|
|
SequentialAnimation on rotation {
|
|
running: cat.petted || cat.mood === "loving"
|
|
loops: Animation.Infinite
|
|
alwaysRunToEnd: true
|
|
NumberAnimation { to: 2.5; duration: 180; easing.type: Easing.InOutSine }
|
|
NumberAnimation { to: -2.5; duration: 180; easing.type: Easing.InOutSine }
|
|
}
|
|
}
|
|
|
|
// 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
|
|
readonly property string glyph: cat.mood === "alert" || cat.mood === "batting" ? "!"
|
|
: cat.sleeping ? "z"
|
|
: (cat.mood === "loving" || cat.petted) ? "♥" : ""
|
|
x: 82; y: 4
|
|
text: glyph
|
|
visible: glyph.length > 0
|
|
font.pixelSize: cat.mood === "alert" || cat.mood === "batting" ? 22 : 16
|
|
font.bold: true
|
|
font.family: "Helvetica Neue"
|
|
color: cat.mood === "alert" || cat.mood === "batting" ? "#f2a33c"
|
|
: (glyph === "♥" ? "#ff5b7f" : cat.ink)
|
|
transform: Scale { xScale: cat.facingRight ? 1 : -1; origin.x: bubble.width / 2 }
|
|
Accessible.ignored: true
|
|
|
|
SequentialAnimation on y {
|
|
running: bubble.visible
|
|
loops: Animation.Infinite
|
|
NumberAnimation { from: 10; to: -8; duration: cat.sleeping ? 1800 : 900; easing.type: Easing.OutQuad }
|
|
}
|
|
SequentialAnimation on opacity {
|
|
running: bubble.visible
|
|
loops: Animation.Infinite
|
|
NumberAnimation { from: 0; to: 1; duration: cat.sleeping ? 500 : 250 }
|
|
NumberAnimation { to: 0; duration: cat.sleeping ? 1300 : 650 }
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
onMoodCodeChanged: if (showCodes && moodCode.length > 0) codeFlash.restart()
|
|
|
|
// Head dips into the mug, holds, comes back up.
|
|
SequentialAnimation {
|
|
running: cat.mood === "drinking" && !cat.lifted
|
|
loops: Animation.Infinite
|
|
alwaysRunToEnd: true
|
|
NumberAnimation { target: cat; property: "sipDip"; to: 13; duration: 700; easing.type: Easing.InOutQuad }
|
|
PauseAnimation { duration: 900 }
|
|
NumberAnimation { target: cat; property: "sipDip"; to: 0; duration: 600; easing.type: Easing.InOutQuad }
|
|
PauseAnimation { duration: 1400 }
|
|
}
|
|
|
|
states: [
|
|
State {
|
|
name: "running"
|
|
when: cat.running && !cat.lifted
|
|
PropertyChanges { body.rotation: -5; head.y: 15; head.x: 48 }
|
|
PropertyChanges { cat.tailC1X: 8; cat.tailC1Y: 60; cat.tailC2X: 4; cat.tailC2Y: 56; cat.tailX: 0; cat.tailY: 52 }
|
|
},
|
|
State {
|
|
name: "sitting"
|
|
when: (cat.mood === "sitting" || cat.mood === "loving" || cat.mood === "alert") && !cat.lifted
|
|
PropertyChanges { body.x: 34; body.y: 40; body.width: 40; body.height: 40 }
|
|
PropertyChanges { head.y: 8; head.x: 40 }
|
|
PropertyChanges { cat.tailC1X: 26; cat.tailC1Y: 84; cat.tailC2X: 40; cat.tailC2Y: 88; cat.tailX: 52; cat.tailY: 80 }
|
|
},
|
|
State {
|
|
name: "grooming"
|
|
when: cat.mood === "grooming" && !cat.lifted
|
|
PropertyChanges { body.x: 34; body.y: 40; body.width: 40; body.height: 40 }
|
|
PropertyChanges { head.y: 10; head.x: 38; head.rotation: -10 }
|
|
PropertyChanges { cat.tailC1X: 26; cat.tailC1Y: 84; cat.tailC2X: 40; cat.tailC2Y: 88; cat.tailX: 52; cat.tailY: 80 }
|
|
},
|
|
State {
|
|
name: "stretching"
|
|
when: cat.mood === "stretching" && !cat.lifted
|
|
PropertyChanges { body.x: 16; body.y: 50; body.width: 64; body.height: 26; body.rotation: -8 }
|
|
PropertyChanges { head.y: 32; head.x: 52; head.rotation: 7 }
|
|
PropertyChanges { cat.tailC1X: 10; cat.tailC1Y: 48; cat.tailC2X: 6; cat.tailC2Y: 30; cat.tailX: 14; cat.tailY: 22 }
|
|
},
|
|
State {
|
|
name: "sleeping"
|
|
when: cat.sleeping && !cat.lifted
|
|
PropertyChanges { body.x: 22; body.y: 54; body.width: 56; body.height: 26 }
|
|
PropertyChanges { head.y: 24; head.x: 44; head.rotation: 4 }
|
|
PropertyChanges { cat.tailC1X: 24; cat.tailC1Y: 86; cat.tailC2X: 60; cat.tailC2Y: 90; cat.tailX: 76; cat.tailY: 78 }
|
|
},
|
|
State {
|
|
name: "drinking"
|
|
when: cat.mood === "drinking" && !cat.lifted
|
|
PropertyChanges { body.x: 32; body.y: 42; body.width: 42; body.height: 38 }
|
|
PropertyChanges { head.x: 50; head.y: 20 + cat.sipDip; head.rotation: 16 }
|
|
PropertyChanges { cat.tailC1X: 26; cat.tailC1Y: 84; cat.tailC2X: 40; cat.tailC2Y: 88; cat.tailX: 52; cat.tailY: 80 }
|
|
},
|
|
State {
|
|
name: "batting"
|
|
when: cat.mood === "batting" && !cat.lifted
|
|
PropertyChanges { body.rotation: -10; body.x: 26 }
|
|
PropertyChanges { head.y: 10; head.x: 50 }
|
|
},
|
|
State {
|
|
name: "lifted"
|
|
when: cat.lifted
|
|
PropertyChanges { torso.rotation: 8; torso.y: -6 }
|
|
PropertyChanges { body.x: 28; body.y: 40; body.width: 42; body.height: 40 }
|
|
PropertyChanges { head.y: 8; head.x: 40 }
|
|
PropertyChanges { cat.tailC1X: 22; cat.tailC1Y: 74; cat.tailC2X: 18; cat.tailC2Y: 86; cat.tailX: 24; cat.tailY: 92 }
|
|
PropertyChanges { shadow.opacity: 0.06; shadow.scale: 0.7 }
|
|
}
|
|
]
|
|
|
|
transitions: Transition {
|
|
NumberAnimation { target: torso; properties: "rotation,y"; duration: 260; easing.type: Easing.OutBack }
|
|
NumberAnimation { target: shadow; properties: "opacity,scale"; duration: 260 }
|
|
}
|
|
|
|
readonly property bool seated: mood === "sitting" || mood === "loving" || mood === "alert"
|
|
|| mood === "grooming" || mood === "drinking"
|
|
}
|