Every so often a mug appears at the cat's nose, it sits down, dips its head in and takes a few slow sips while steam rises. M01, optional stop. The mug lives in its own click-through window like the chip, or inside the overlay on Wayland. It goes away as soon as the cat is petted, dragged, sent to sleep or moves on to something else, so no mug is ever left standing. Also renames OverlayWindow's mode flag, which shadowed QWindow::active. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
615 lines
26 KiB
QML
615 lines
26 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
|
|
readonly property real unit: width / 100
|
|
|
|
// 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: 100
|
|
implicitHeight: 90
|
|
|
|
transform: Scale {
|
|
origin.x: cat.width / 2
|
|
xScale: cat.facingRight ? 1 : -1
|
|
}
|
|
|
|
Item {
|
|
id: art
|
|
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 }
|
|
}
|
|
}
|
|
|
|
// 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 }
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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()
|
|
|
|
// 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"
|
|
}
|