quickshell: Archie

A black and tan dachshund who lives along the bottom of the screen, with a
bowl he visits for a snack.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-08-04 12:42:40 +01:00
parent 9b43f5f669
commit e11d6d4e57

View file

@ -375,6 +375,7 @@ in
Lock 1.0 Lock.qml
Cheatsheet 1.0 Cheatsheet.qml
Weird 1.0 Weird.qml
Archie 1.0 Archie.qml
'';
};
@ -1118,6 +1119,256 @@ in
'';
};
"quickshell/Archie.qml" = {
onChange = qsRestart;
text = ''
import QtQuick
// Archie a black and tan dachshund with a red collar, living
// along the bottom of the screen. He trots up and down, stops to
// wag, and every so often remembers his bowl and goes for a snack.
//
// The art is plain text, one character per pixel, so he can be
// redrawn by editing the picture instead of editing code. He walks
// on all fours and never limps; that was deliberate.
Item {
id: archie
anchors.fill: parent
// One dog. The bar is instantiated per monitor.
property bool primary: true
visible: primary
readonly property int cell: 6
readonly property int cols: 26
readonly property int rows: 13
// Not themed he looked how he looked.
readonly property var pal: ({
"K": "#2b2523", // black
"T": "#a86a30", // tan
"R": "#b3322c", // collar, and his bowl
"N": "#14100f", // nose
"W": "#efe7dd", // catchlight
"B": "#96602f" // kibble
})
// Trotting: legs out at full stride, tail up.
readonly property var walkA: [
"....................KKKK..",
"...................KKKKKK.",
"...................KKTKKK.",
"KK.................KKWTTK.",
".KK................KKTTTN.",
"..KKKKKKKKKKKKKKKKRKKTTTN.",
"..KKKKKKKKKKKKKKKKRKKTTT..",
"...KKKKKKKKKKKKKKKRKKT....",
"...KKKKKKKKKKKKKTTK.......",
"....TKKKKKKKKKKKTT........",
"....TT.........TTT........",
"....TT..........TT........",
"...TTT..........TTT......."
]
// Legs gathered underneath, tail level.
readonly property var walkB: [
"....................KKKK..",
"...................KKKKKK.",
"...................KKTKKK.",
"...................KKWTTK.",
"KKK................KKTTTN.",
"..KKKKKKKKKKKKKKKKRKKTTTN.",
"..KKKKKKKKKKKKKKKKRKKTTT..",
"...KKKKKKKKKKKKKKKRKKT....",
"...KKKKKKKKKKKKKTTK.......",
"....TKKKKKKKKKKKTT........",
"......TT......TT..........",
"......TT......TT..........",
".....TTT.....TTT.........."
]
// Standing still: walkA's planted legs, walkB's tail. Flipping
// between this and walkA is the wag.
readonly property var wag: [
"....................KKKK..",
"...................KKKKKK.",
"...................KKTKKK.",
"...................KKWTTK.",
"KKK................KKTTTN.",
"..KKKKKKKKKKKKKKKKRKKTTTN.",
"..KKKKKKKKKKKKKKKKRKKTTT..",
"...KKKKKKKKKKKKKKKRKKT....",
"...KKKKKKKKKKKKKTTK.......",
"....TKKKKKKKKKKKTT........",
"....TT.........TTT........",
"....TT..........TT........",
"...TTT..........TTT......."
]
// Head down in the bowl.
readonly property var eat: [
"..........................",
"..........................",
"..........................",
"KK........................",
".KK.......................",
"..KKKKKKKKKKKKKKKKRK......",
"..KKKKKKKKKKKKKKKKRKK.....",
"...KKKKKKKKKKKKKKKRKKKK...",
"...KKKKKKKKKKKKKTTKKTKKK..",
"....TKKKKKKKKKKKTTKKWTTK..",
"....TT.........TT..KKTTTN.",
"....TT.........TT...KTTN..",
"...TTT........TTT........."
]
readonly property var bowlArt: [
"...BBB..",
"..BBBBB.",
".RBBBBBR",
".RRRRRR.",
"..RRRR.."
]
property var frame: walkA
// Where he lives: the strip along the bottom, the same
// floor line the worm lands on.
readonly property real floorY: archie.height - Theme.frameWidth
readonly property real roamL: Theme.barWidth + 12
readonly property real roamR: Math.max(roamL,
archie.width - Theme.frameWidth - cols * cell - 12)
// Bowl parked over toward the right, clear of the launcher.
readonly property real bowlX: Math.max(roamL, roamR - 150)
// Stand so his muzzle lands in it rather than his shoulder.
readonly property real bowlStandX: bowlX - 18 * cell
property real px: roamL
property int facing: 1 // 1 = right, -1 = left
property string mode: "walk"
property real targetX: 0
property bool toBowl: false
property int hold: 0
property int tick: 0
function wander() {
mode = "walk";
// Not if he is already standing at it otherwise he can
// roll "go to the bowl" while stood in it and eat twice
// over without moving.
toBowl = Math.random() < 0.3 && Math.abs(px - bowlStandX) > 80;
// A short trip from wherever he is, not a random point on
// the whole screen: picking uniformly meant single walks
// most of a minute long across a wide monitor, which reads
// as a patrol. Short hops read as pottering about.
let dir = Math.random() < 0.5 ? -1 : 1;
// Turn away from an end he is already near. Clamping the
// target instead just parks him against the wall, picking
// the same spot over and over.
if (dir < 0 && px - roamL < 220) dir = 1;
if (dir > 0 && roamR - px < 220) dir = -1;
targetX = toBowl ? bowlStandX
: Math.max(roamL, Math.min(roamR,
px + dir * (150 + Math.random() * 550)));
}
function arrive() {
if (toBowl) {
mode = "eat";
hold = 100 + Math.random() * 80;
facing = 1; // the bowl is off his nose
} else {
mode = "idle";
hold = 45 + Math.random() * 130;
}
}
// Placed on the first real width, not on completion: anchors
// have not resolved yet at that point, so roamR collapses to
// roamL and he spawns pinned to the left edge.
property bool placed: false
onWidthChanged: {
if (placed || width <= 0) return;
placed = true;
px = roamL + Math.random() * (roamR - roamL);
wander();
}
Timer {
interval: 33; running: archie.primary; repeat: true
onTriggered: {
archie.tick++;
if (archie.mode === "walk") {
// Arrival first: checking after the step makes him
// take a stride backwards when he is already there.
if (Math.abs(archie.targetX - archie.px) < 2) {
archie.arrive();
} else {
let dir = archie.targetX > archie.px ? 1 : -1;
archie.facing = dir;
archie.px += dir * 1.8;
// Swap legs every 6 ticks: at 1.8px a tick
// that is ~11px of ground per half-stride,
// against the 12px the feet actually move in
// the art, so he does not moonwalk.
archie.frame = archie.tick % 12 < 6 ? archie.walkA : archie.walkB;
}
} else {
archie.frame = archie.mode === "eat" ? archie.eat
: (archie.tick % 26 < 13 ? archie.walkA : archie.wag);
if (--archie.hold <= 0) archie.wander();
}
}
}
// Sprite. One Rectangle per pixel, colour looked up per
// frame; switching frames just rebinds them.
component Sprite: Item {
id: sp
property var art: []
property int w: 0
property int h: 0
width: w * archie.cell
height: h * archie.cell
Repeater {
// Delegates parent to the Repeater's PARENT, so `sp`
// is the only reliable way back to the sprite here.
model: sp.w * sp.h
Rectangle {
required property int index
readonly property int col: index % sp.w
readonly property int row: Math.floor(index / sp.w)
readonly property string ch: sp.art[row][col]
visible: ch !== "."
color: archie.pal[ch] || "transparent"
x: col * archie.cell
y: row * archie.cell
width: archie.cell
height: archie.cell
antialiasing: false
}
}
}
Sprite {
art: archie.frame
w: archie.cols
h: archie.rows
// Snap to the pixel grid, same as the worm, or the art
// shimmers as he walks.
x: Math.round(archie.px / archie.cell) * archie.cell
y: Math.round((archie.floorY - height) / archie.cell) * archie.cell
// Mirror him when he heads back the other way.
transform: Scale { origin.x: archie.cols * archie.cell / 2; xScale: archie.facing }
}
// Drawn after him, so at the bowl his nose goes behind it.
Sprite {
art: archie.bowlArt
w: 8
h: 5
x: Math.round(archie.bowlX / archie.cell) * archie.cell
y: Math.round((archie.floorY - height) / archie.cell) * archie.cell
}
}
'';
};
"quickshell/Commands.qml" = {
onChange = qsRestart;
text = ''
@ -6772,6 +7023,9 @@ in
{ rect: chromeSdf.launcher, push: Qt.point(0, -1) }
]
}
// Archie. One dog, primary screen only.
Archie { primary: bar.screen === Quickshell.screens[0] }
}
'';
};