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
+69
-6
@@ -13,6 +13,16 @@ Item {
|
||||
property bool lifted: false
|
||||
property bool petted: false
|
||||
property point look: Qt.point(0, 0)
|
||||
property bool showCodes: true
|
||||
|
||||
// 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"
|
||||
: ""
|
||||
|
||||
readonly property bool dark: coat.hslLightness < 0.45
|
||||
readonly property color coatShade: Qt.darker(coat, 1.12)
|
||||
@@ -23,7 +33,7 @@ Item {
|
||||
readonly property color blush: "#f77d95"
|
||||
|
||||
readonly property bool walking: mood === "walking"
|
||||
readonly property bool running: mood === "running"
|
||||
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" || petted
|
||||
@@ -299,8 +309,8 @@ Item {
|
||||
id: eyeBall
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: eye.width * (cat.mood === "alert" ? 1.15 : 1)
|
||||
height: eye.height * eye.open * (cat.mood === "alert" ? 1.15 : 1)
|
||||
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
|
||||
@@ -449,14 +459,17 @@ Item {
|
||||
// speech bubble: "!" / "z" / "♥"
|
||||
Text {
|
||||
id: bubble
|
||||
readonly property string glyph: cat.mood === "alert" ? "!" : cat.sleeping ? "z" : (cat.mood === "loving" || cat.petted) ? "♥" : ""
|
||||
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" ? 22 : 16
|
||||
font.pixelSize: cat.mood === "alert" || cat.mood === "batting" ? 22 : 16
|
||||
font.bold: true
|
||||
font.family: "Helvetica Neue"
|
||||
color: cat.mood === "alert" ? "#f2a33c" : (glyph === "♥" ? "#f25f7a" : cat.ink)
|
||||
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
|
||||
|
||||
@@ -474,6 +487,50 @@ 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()
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "running"
|
||||
@@ -509,6 +566,12 @@ Item {
|
||||
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: "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
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
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"]
|
||||
|
||||
function name(mood) {
|
||||
return names[mood] !== undefined ? names[mood] : "sitting"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import TNCat
|
||||
|
||||
// One small window that is exactly the cat. Used wherever a client may place
|
||||
// its own window, which is everything except Wayland.
|
||||
Window {
|
||||
id: win
|
||||
|
||||
required property CatController cat
|
||||
required property AppSettings settings
|
||||
|
||||
signal settingsRequested()
|
||||
|
||||
width: settings.catSize
|
||||
height: Math.round(settings.catSize * 0.9)
|
||||
x: cat.x
|
||||
y: Screen.desktopAvailableHeight - height - settings.bottomMargin
|
||||
color: Platform.opaqueBackground ? settings.coat : "transparent"
|
||||
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool
|
||||
| Qt.NoDropShadowWindowHint | Qt.WindowDoesNotAcceptFocus
|
||||
| (Platform.bypassWindowManager ? Qt.X11BypassWindowManagerHint : 0)
|
||||
visible: true
|
||||
title: qsTr("TNCat")
|
||||
|
||||
Component.onCompleted: {
|
||||
cat.stageWidth = Screen.desktopAvailableWidth
|
||||
cat.catWidth = width
|
||||
}
|
||||
onWidthChanged: cat.catWidth = width
|
||||
Screen.onDesktopAvailableWidthChanged: cat.stageWidth = Screen.desktopAvailableWidth
|
||||
|
||||
// The eyes need to know where the face sits on the desktop.
|
||||
Binding {
|
||||
target: win.cat
|
||||
property: "catCenter"
|
||||
value: Qt.point(win.x + win.width / 2, win.y + win.height / 2)
|
||||
}
|
||||
|
||||
Cat {
|
||||
anchors.fill: parent
|
||||
coat: win.settings.coat
|
||||
facingRight: win.cat.facingRight
|
||||
mood: CatMoods.name(win.cat.mood)
|
||||
lifted: win.cat.dragging
|
||||
petted: win.cat.hovered && !win.cat.dragging
|
||||
look: win.cat.look
|
||||
showCodes: win.settings.showCodes
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("TNCat")
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
onHoveredChanged: win.cat.hovered = hovered
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
target: null
|
||||
onActiveChanged: win.cat.dragging = active
|
||||
onCentroidChanged: {
|
||||
if (active)
|
||||
win.cat.x = Math.max(0, Math.min(win.cat.stageWidth - win.width,
|
||||
win.x + centroid.position.x - centroid.pressPosition.x))
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onTapped: win.cat.pet()
|
||||
onDoubleTapped: win.settingsRequested()
|
||||
onLongPressed: win.settingsRequested()
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: win.settingsRequested()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
|
||||
// A curled steel chip, the kind that comes off a milling cutter.
|
||||
// Design space is 60 x 44 units.
|
||||
Item {
|
||||
id: chip
|
||||
|
||||
property int kick: 0
|
||||
|
||||
readonly property real unit: width / 60
|
||||
|
||||
implicitWidth: 60
|
||||
implicitHeight: 44
|
||||
|
||||
Item {
|
||||
width: 60
|
||||
height: 44
|
||||
transformOrigin: Item.TopLeft
|
||||
scale: chip.unit
|
||||
|
||||
Rectangle { // ground shadow
|
||||
x: 14; y: 36
|
||||
width: 32; height: 6
|
||||
radius: 3
|
||||
color: "#000000"
|
||||
opacity: 0.18
|
||||
antialiasing: true
|
||||
}
|
||||
|
||||
Item {
|
||||
id: curl
|
||||
x: 30; y: 22
|
||||
width: 0; height: 0
|
||||
|
||||
Shape {
|
||||
x: -30; y: -22
|
||||
width: 60; height: 44
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
ShapePath { // the curl
|
||||
strokeColor: "#8d99ae"
|
||||
strokeWidth: 5
|
||||
capStyle: ShapePath.RoundCap
|
||||
fillColor: "transparent"
|
||||
startX: 12; startY: 32
|
||||
PathCubic { control1X: 2; control1Y: 20; control2X: 14; control2Y: 6; x: 30; y: 10 }
|
||||
PathCubic { control1X: 44; control1Y: 14; control2X: 48; control2Y: 30; x: 38; y: 34 }
|
||||
PathCubic { control1X: 30; control1Y: 37; control2X: 26; control2Y: 28; x: 32; y: 24 }
|
||||
}
|
||||
ShapePath { // highlight along the outer edge
|
||||
strokeColor: "#dfe6ee"
|
||||
strokeWidth: 1.6
|
||||
capStyle: ShapePath.RoundCap
|
||||
fillColor: "transparent"
|
||||
startX: 13; startY: 29
|
||||
PathCubic { control1X: 5; control1Y: 19; control2X: 16; control2Y: 8; x: 30; y: 12 }
|
||||
}
|
||||
}
|
||||
|
||||
RotationAnimation on rotation {
|
||||
running: true
|
||||
loops: Animation.Infinite
|
||||
from: 0; to: 360
|
||||
duration: 2600
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import TNCat
|
||||
|
||||
// The chip gets its own little click-through window so it can skitter away
|
||||
// from the cat across the whole screen.
|
||||
Window {
|
||||
id: win
|
||||
|
||||
required property CatController cat
|
||||
required property AppSettings settings
|
||||
|
||||
readonly property int chipSize: Math.round(settings.catSize * 0.45)
|
||||
|
||||
width: chipSize
|
||||
height: Math.round(chipSize * 0.73)
|
||||
x: cat.chipX - width / 2
|
||||
y: Screen.desktopAvailableHeight - height - settings.bottomMargin
|
||||
color: "transparent"
|
||||
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool
|
||||
| Qt.NoDropShadowWindowHint | Qt.WindowDoesNotAcceptFocus
|
||||
| Qt.WindowTransparentForInput
|
||||
| (Platform.bypassWindowManager ? Qt.X11BypassWindowManagerHint : 0)
|
||||
visible: cat.chipVisible && !Platform.opaqueBackground
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation { duration: 320; easing.type: Easing.OutQuad }
|
||||
}
|
||||
|
||||
Chip {
|
||||
anchors.fill: parent
|
||||
kick: win.cat.chipKick
|
||||
}
|
||||
}
|
||||
+24
-69
@@ -1,87 +1,42 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import Workcat
|
||||
import TNCat
|
||||
|
||||
Window {
|
||||
id: root
|
||||
// Root is not a window: which windows exist depends on the platform.
|
||||
QtObject {
|
||||
id: app
|
||||
|
||||
required property CatController cat
|
||||
required property AppSettings settings
|
||||
|
||||
readonly property var moodNames: ["walking", "running", "sitting", "grooming", "stretching", "sleeping", "loving", "alert"]
|
||||
readonly property real floorY: Screen.desktopAvailableHeight - height
|
||||
|
||||
function openSettings() {
|
||||
settingsLoader.active = true
|
||||
if (settingsLoader.status === Loader.Ready)
|
||||
(settingsLoader.item as SettingsWindow).show()
|
||||
settingsWindow.show()
|
||||
settingsWindow.raise()
|
||||
settingsWindow.requestActivate()
|
||||
}
|
||||
|
||||
width: settings.catSize
|
||||
height: Math.round(settings.catSize * 0.9)
|
||||
x: cat.x
|
||||
y: floorY
|
||||
color: Platform.opaqueBackground ? settings.coat : "transparent"
|
||||
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool
|
||||
| Qt.NoDropShadowWindowHint | Qt.WindowDoesNotAcceptFocus
|
||||
| (Platform.bypassWindowManager ? Qt.X11BypassWindowManagerHint : 0)
|
||||
visible: true
|
||||
title: qsTr("Workcat")
|
||||
|
||||
Component.onCompleted: {
|
||||
cat.screenWidth = Screen.desktopAvailableWidth
|
||||
cat.catWidth = width
|
||||
}
|
||||
onWidthChanged: cat.catWidth = width
|
||||
Screen.onDesktopAvailableWidthChanged: cat.screenWidth = Screen.desktopAvailableWidth
|
||||
|
||||
Cat {
|
||||
id: sprite
|
||||
anchors.fill: parent
|
||||
coat: root.settings.coat
|
||||
facingRight: root.cat.facingRight
|
||||
mood: root.moodNames[root.cat.mood]
|
||||
lifted: root.cat.dragging
|
||||
petted: root.cat.hovered && !root.cat.dragging
|
||||
look: root.cat.look
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("Workcat")
|
||||
readonly property CatWindow catWindow: CatWindow {
|
||||
cat: app.cat
|
||||
settings: app.settings
|
||||
visible: !Platform.overlayMode
|
||||
onSettingsRequested: app.openSettings()
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
onHoveredChanged: root.cat.hovered = hovered
|
||||
readonly property ChipWindow chipWindow: ChipWindow {
|
||||
cat: app.cat
|
||||
settings: app.settings
|
||||
visible: !Platform.overlayMode && app.cat.chipVisible
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
target: null
|
||||
onActiveChanged: root.cat.dragging = active
|
||||
onCentroidChanged: {
|
||||
if (active)
|
||||
root.cat.x = Math.max(0, Math.min(root.cat.screenWidth - root.width,
|
||||
root.x + centroid.position.x - centroid.pressPosition.x))
|
||||
}
|
||||
readonly property OverlayWindow overlayWindow: OverlayWindow {
|
||||
cat: app.cat
|
||||
settings: app.settings
|
||||
active: Platform.overlayMode
|
||||
onSettingsRequested: app.openSettings()
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onTapped: root.cat.pet()
|
||||
onDoubleTapped: root.openSettings()
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: root.openSettings()
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: settingsLoader
|
||||
active: false
|
||||
sourceComponent: SettingsWindow {
|
||||
cat: root.cat
|
||||
settings: root.settings
|
||||
onClosing: settingsLoader.active = false
|
||||
}
|
||||
onLoaded: (item as SettingsWindow).show()
|
||||
readonly property SettingsWindow settingsWindow: SettingsWindow {
|
||||
cat: app.cat
|
||||
settings: app.settings
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import TNCat
|
||||
|
||||
// One screen-filling, click-through window that holds the cat and the chip.
|
||||
// Wayland does not let a client place its own window, so there the cat moves
|
||||
// inside this overlay instead of the window moving across the desktop.
|
||||
Window {
|
||||
id: win
|
||||
|
||||
required property CatController cat
|
||||
required property AppSettings settings
|
||||
|
||||
signal settingsRequested()
|
||||
|
||||
// Fullscreen is a visibility, not a visible flag, so the mode switch has to
|
||||
// go through visibility or the two fight each other.
|
||||
property bool active: false
|
||||
|
||||
readonly property int catW: settings.catSize
|
||||
readonly property int catH: Math.round(settings.catSize * 0.9)
|
||||
readonly property real groundY: height - catH - settings.bottomMargin
|
||||
|
||||
// Only the cat itself should swallow clicks; everything else falls through.
|
||||
function refreshInputRegion() {
|
||||
Platform.setInputRegion(win, Qt.rect(Math.round(cat.x), Math.round(groundY), catW, catH))
|
||||
}
|
||||
|
||||
visibility: active ? Window.FullScreen : Window.Hidden
|
||||
color: "transparent"
|
||||
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool
|
||||
| Qt.NoDropShadowWindowHint | Qt.WindowDoesNotAcceptFocus
|
||||
title: qsTr("TNCat")
|
||||
|
||||
onActiveChanged: {
|
||||
if (!active)
|
||||
return
|
||||
cat.stageWidth = width
|
||||
cat.catWidth = catW
|
||||
refreshInputRegion()
|
||||
}
|
||||
onWidthChanged: cat.stageWidth = width
|
||||
onCatWChanged: cat.catWidth = catW
|
||||
|
||||
Binding {
|
||||
target: win.cat
|
||||
property: "catCenter"
|
||||
value: Qt.point(win.x + win.cat.x + win.catW / 2, win.y + win.groundY + win.catH / 2)
|
||||
}
|
||||
|
||||
// Coalesce mask updates: one X11 round trip per frame of walking is plenty.
|
||||
Timer {
|
||||
interval: 33
|
||||
running: win.active
|
||||
repeat: true
|
||||
onTriggered: win.refreshInputRegion()
|
||||
}
|
||||
|
||||
Chip {
|
||||
x: win.cat.chipX - width / 2
|
||||
y: win.height - height - win.settings.bottomMargin
|
||||
width: Math.round(win.settings.catSize * 0.45)
|
||||
height: Math.round(width * 0.73)
|
||||
visible: win.cat.chipVisible
|
||||
kick: win.cat.chipKick
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation { duration: 320; easing.type: Easing.OutQuad }
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: catItem
|
||||
x: win.cat.x
|
||||
y: win.groundY
|
||||
width: win.catW
|
||||
height: win.catH
|
||||
|
||||
Cat {
|
||||
anchors.fill: parent
|
||||
coat: win.settings.coat
|
||||
facingRight: win.cat.facingRight
|
||||
mood: CatMoods.name(win.cat.mood)
|
||||
lifted: win.cat.dragging
|
||||
petted: win.cat.hovered && !win.cat.dragging
|
||||
look: win.cat.look
|
||||
showCodes: win.settings.showCodes
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: qsTr("TNCat")
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
onHoveredChanged: win.cat.hovered = hovered
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
target: null
|
||||
onActiveChanged: win.cat.dragging = active
|
||||
onCentroidChanged: {
|
||||
if (active)
|
||||
win.cat.x = Math.max(0, Math.min(win.cat.stageWidth - win.catW,
|
||||
catItem.x + centroid.position.x - centroid.pressPosition.x))
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onTapped: win.cat.pet()
|
||||
onDoubleTapped: win.settingsRequested()
|
||||
onLongPressed: win.settingsRequested()
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: win.settingsRequested()
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
-10
@@ -2,7 +2,7 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls.Basic
|
||||
import QtQuick.Layouts
|
||||
import Workcat
|
||||
import TNCat
|
||||
|
||||
Window {
|
||||
id: win
|
||||
@@ -20,10 +20,10 @@ Window {
|
||||
]
|
||||
|
||||
width: 440
|
||||
height: 600
|
||||
height: 700
|
||||
minimumWidth: 380
|
||||
minimumHeight: 520
|
||||
title: qsTr("Workcat")
|
||||
minimumHeight: 600
|
||||
title: qsTr("TNCat")
|
||||
color: "#faf7f2"
|
||||
flags: Qt.Dialog | Qt.WindowStaysOnTopHint
|
||||
|
||||
@@ -35,20 +35,24 @@ Window {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
Cat {
|
||||
Loader {
|
||||
Layout.preferredWidth: 110
|
||||
Layout.preferredHeight: 99
|
||||
coat: win.settings.coat
|
||||
mood: "sitting"
|
||||
petted: previewHover.hovered
|
||||
HoverHandler { id: previewHover }
|
||||
active: win.visible
|
||||
sourceComponent: Cat {
|
||||
coat: win.settings.coat
|
||||
mood: "sitting"
|
||||
showCodes: false
|
||||
petted: previewHover.hovered
|
||||
HoverHandler { id: previewHover }
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 4
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Workcat")
|
||||
text: qsTr("TNCat")
|
||||
font.pixelSize: 26
|
||||
font.bold: true
|
||||
color: "#2f2a36"
|
||||
@@ -122,6 +126,22 @@ Window {
|
||||
onMoved: win.settings.walkSpeed = value
|
||||
}
|
||||
|
||||
Label { text: qsTr("Ground line") }
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Slider {
|
||||
id: marginSlider
|
||||
Layout.fillWidth: true
|
||||
from: 0; to: 600; stepSize: 10
|
||||
value: win.settings.bottomMargin
|
||||
onMoved: win.settings.bottomMargin = value
|
||||
}
|
||||
Label {
|
||||
Layout.preferredWidth: 54
|
||||
text: qsTr("%1 px").arg(Math.round(marginSlider.value))
|
||||
}
|
||||
}
|
||||
|
||||
Label { text: qsTr("Eyes") }
|
||||
Switch {
|
||||
text: qsTr("Follow the mouse")
|
||||
@@ -129,6 +149,20 @@ Window {
|
||||
onToggled: win.settings.followCursor = checked
|
||||
}
|
||||
|
||||
Label { text: qsTr("Codes") }
|
||||
Switch {
|
||||
text: qsTr("Show G-code above the cat")
|
||||
checked: win.settings.showCodes
|
||||
onToggled: win.settings.showCodes = checked
|
||||
}
|
||||
|
||||
Label { text: qsTr("Chips") }
|
||||
Switch {
|
||||
text: qsTr("Let a chip skitter past now and then")
|
||||
checked: win.settings.chipsEnabled
|
||||
onToggled: win.settings.chipsEnabled = checked
|
||||
}
|
||||
|
||||
Label { text: qsTr("Cat") }
|
||||
Switch {
|
||||
text: checked ? qsTr("Napping") : qsTr("Awake")
|
||||
@@ -148,6 +182,7 @@ Window {
|
||||
Button { text: qsTr("Stretch"); onClicked: win.cat.doTrick(CatController.Stretching) }
|
||||
Button { text: qsTr("Dash"); onClicked: win.cat.doTrick(CatController.Running) }
|
||||
Button { text: qsTr("Nap"); onClicked: win.cat.doTrick(CatController.Sleeping) }
|
||||
Button { text: qsTr("Toss a chip"); onClicked: win.cat.tossChip() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user