quickshell: redraw Archie at 2px, fix his paws, add a throwable ball and a bed

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-08-04 13:21:13 +01:00
parent d53e491812
commit 36360544ee

View file

@ -1125,12 +1125,13 @@ in
import QtQuick import QtQuick
// Archie a black and tan dachshund with a red collar, living // Archie a black and tan dachshund with a red collar, living
// along the bottom of the screen. He trots up and down, stops to // along the bottom of the screen. He trots about, wags, naps in
// wag, and every so often remembers his bowl and goes for a snack. // his bed, visits his bowl, and fetches a ball you can pick up
// and fling with the mouse.
// //
// The art is plain text, one character per pixel, so he can be // The art is plain text, one character per pixel, so he can be
// redrawn by editing the picture instead of editing code. He walks // redrawn by editing the picture instead of editing code. He walks
// on all fours and never limps; that was deliberate. // evenly on all fours and never limps; that was deliberate.
Item { Item {
id: archie id: archie
anchors.fill: parent anchors.fill: parent
@ -1138,190 +1139,369 @@ in
property bool primary: true property bool primary: true
visible: primary visible: primary
// 4px pixels, not the worm's 6: he reads better with more // 2px pixels: plenty of resolution for a proper pointed
// pixels in him, and at 112x48 he takes up a lot less of the // dachshund head, and he still lands smaller on screen than
// screen than the chunkier first pass did. // the chunkier drawing did.
readonly property int cell: 4 readonly property int cell: 2
readonly property int cols: 28 readonly property int cols: 44
readonly property int rows: 12 readonly property int rows: 19
// Not themed he looked how he looked. // Not themed he looked how he looked.
readonly property var pal: ({ readonly property var pal: ({
"K": "#2b2523", // black "K": "#2b2523", // black
"T": "#a86a30", // tan "T": "#a86a30", // tan
"R": "#b3322c", // collar, and his bowl "R": "#b3322c", // collar, and his bowl
"N": "#14100f", // nose "N": "#14100f", // nose and eye
"W": "#efe7dd", // catchlight "B": "#96602f", // kibble
"B": "#96602f" // kibble "G": "#5a667a", // bed
"D": "#3a4454", // bed cushion
"Y": "#c8d84a", // ball
"L": "#e4ee8c" // ball highlight
}) })
// Trotting: legs out at full stride, tail up. // Trotting: front leg forward, hind leg back, tail up. Paws
// point the way he is going drawn trailing behind the leg
// they read as feet on backwards, which is exactly how the
// first pass looked.
readonly property var walkA: [ readonly property var walkA: [
"....................KKKKK...", "...............................KKKKKKKK.....",
"...................KKTKKKK..", "KK............................KKKKKKKKKK....",
"KK.................KKNTTKKN.", "KKKK..........................KKKKKKKKKKK...",
".KKKKKKKKKKKKKKKKKRKKTTTTTN.", ".KKKK.........................KKKKK.KKKKK...",
"..KKKKKKKKKKKKKKKKRKKTTTT...", "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKK..TTKKKKK.",
"..KKKKKKKKKKKKKKKKRKKTT.....", ".KKKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTTTTTT.",
"...KKKKKKKKKKKKKKTTT........", "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTTTTT..",
"....TKKKKKKKKKKKTTT.........", "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTT.....",
"....TTKKKKKKKKKTTT..........", "...KKKKKKKKKKKKKKKKKKKKKKKKKTTKKKK..........",
"....TT..........TT..........", "...KKKKKKKKKKKKKKKKKKKKKKKKKTTTKKK..........",
"....TT..........TT..........", "...KKKKKKKKKKKKKKKKKKKKKKKKTTTT.............",
"...TTT.........TTT.........." ".....KKKKKKKKKKKKKKKKKKKKKTTTT..............",
"....TTKKKKKKKKKKKKKKKKKKKTTTT...............",
".....TTTTT..............TTTT................",
"......TTTT..............TTT.................",
".....TTT................TTT.................",
".....TTT................TTT.................",
".....TTT................TTT.................",
".....TTTTT..............TTTTT..............."
] ]
// Legs gathered underneath, tail level. // Legs gathered under him, tail level.
readonly property var walkB: [ readonly property var walkB: [
"....................KKKKK...", "...............................KKKKKKKK.....",
"...................KKTKKKK..", "..............................KKKKKKKKKK....",
"...................KKNTTKKN.", "KKKK..........................KKKKKKKKKKK...",
"KKKKKKKKKKKKKKKKKKRKKTTTTTN.", "KKKK..........................KKKKK.KKKKK...",
"..KKKKKKKKKKKKKKKKRKKTTTT...", "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKK..TTKKKKK.",
"..KKKKKKKKKKKKKKKKRKKTT.....", ".KKKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTTTTTT.",
"...KKKKKKKKKKKKKKTTT........", "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTTTTT..",
"....TKKKKKKKKKKKTTT.........", "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTT.....",
"....TTKKKKKKKKKTTT..........", "...KKKKKKKKKKKKKKKKKKKKKKKKKTTKKKK..........",
"......TT......TT............", "...KKKKKKKKKKKKKKKKKKKKKKKKKTTTKKK..........",
"......TT......TT............", "...KKKKKKKKKKKKKKKKKKKKKKKKTTTT.............",
".....TTT.....TTT............" ".....KKKKKKKKKKKKKKKKKKKKKTTTT..............",
"....TTKKKKKKKKKKKKKKKKKKKTTTT...............",
"........TTTTT........TTTT...................",
".........TTTT........TTT....................",
"........TTT..........TTT....................",
"........TTT..........TTT....................",
"........TTT..........TTT....................",
"........TTTTT........TTTTT.................."
] ]
// Standing still: walkA's planted legs, walkB's tail. Flipping // Standing: walkA's planted legs, walkB's tail. Flipping
// between this and walkA is the wag. // between this and walkA is the wag.
readonly property var wag: [ readonly property var wag: [
"....................KKKKK...", "...............................KKKKKKKK.....",
"...................KKTKKKK..", "..............................KKKKKKKKKK....",
"...................KKNTTKKN.", "KKKK..........................KKKKKKKKKKK...",
"KKKKKKKKKKKKKKKKKKRKKTTTTTN.", "KKKK..........................KKKKK.KKKKK...",
"..KKKKKKKKKKKKKKKKRKKTTTT...", "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKK..TTKKKKK.",
"..KKKKKKKKKKKKKKKKRKKTT.....", ".KKKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTTTTTT.",
"...KKKKKKKKKKKKKKTTT........", "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTTTTT..",
"....TKKKKKKKKKKKTTT.........", "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTT.....",
"....TTKKKKKKKKKTTT..........", "...KKKKKKKKKKKKKKKKKKKKKKKKKTTKKKK..........",
"....TT..........TT..........", "...KKKKKKKKKKKKKKKKKKKKKKKKKTTTKKK..........",
"....TT..........TT..........", "...KKKKKKKKKKKKKKKKKKKKKKKKTTTT.............",
"...TTT.........TTT.........." ".....KKKKKKKKKKKKKKKKKKKKKTTTT..............",
"....TTKKKKKKKKKKKKKKKKKKKTTTT...............",
".....TTTTT..............TTTT................",
"......TTTT..............TTT.................",
".....TTT................TTT.................",
".....TTT................TTT.................",
".....TTT................TTT.................",
".....TTTTT..............TTTTT..............."
] ]
// Head down in the bowl. // Head down in the bowl.
readonly property var eat: [ readonly property var eat: [
"............................", "............................................",
"............................", "KK..........................................",
"KK..........................", "KKKK........................................",
".KKKKKKKKKKKKKKKKKRKK.......", ".KKKK.......................................",
"..KKKKKKKKKKKKKKKKRKKK......", "..KKKKKKKKKKKKKKKKKKKKKKKKKKKKKK............",
"..KKKKKKKKKKKKKKKKRKKKK.....", ".KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK...........",
"...KKKKKKKKKKKKKKTTKKKKK....", "..KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK..........",
"....TKKKKKKKKKKKTTKKTKKK....", "..KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK..........",
"....TTKKKKKKKKKTTTKKNTTKN...", "...KKKKKKKKKKKKKKKKKKKKKKKKKTTKKKK..........",
"....TT..........TT.KKTTTTN..", "...KKKKKKKKKKKKKKKKKKKKKKKKKTTTKKKK.........",
"....TT..........TT..KTTN....", "...KKKKKKKKKKKKKKKKKKKKKKKKTTTTKKKKKKKK.....",
"...TTT.........TTT.........." ".....KKKKKKKKKKKKKKKKKKKKKTTTTKKKKKKKKKK....",
"....TTKKKKKKKKKKKKKKKKKKKTTTT.KKKKK.KKKK....",
".....TTTTT..............TTTT..KKKKK.TTTKK...",
"......TTTT..............TTT...KKKKTTTTTTTTT.",
".....TTT................TTT...KKKKKTTTTTTT..",
".....TTT................TTT....KKKKTTTT.....",
".....TTT................TTT.................",
".....TTTTT..............TTTTT..............."
]
// Flat out in his bed, muzzle on the cushion.
readonly property var sleepArt: [
"............................................",
"............................................",
"............................................",
"............................................",
"............................................",
"............................................",
"............................................",
"............................................",
"............................................",
"...............................KKKKKKKK.....",
"..............................KKKKKKKKKKK...",
"KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK.KKKKK...",
"..KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKTTTTTKKK..",
"..KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKTTTTTTTTT.",
"...KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKTTTTTTT...",
"...KKKKKKKKKKKKKKKKKKKKKKKKKKTTTT...........",
"....KKKKKKKKKKKKKKKKKKKKKKKKTTTT............",
".....TTTTTTTTKKKKKKKKKKKKTTTT...............",
"......TTTTTT..........TTTTTT................"
] ]
readonly property var bowlArt: [ readonly property var bowlArt: [
"...BBB..", ".....BBBBBB.....",
"..BBBBB.", "...BBBBBBBBBB...",
".RBBBBBR", ".BBBBBBBBBBBBBB.",
".RRRRRR.", "RRRRRRRRRRRRRRRR",
"..RRRR.." ".RRRRRRRRRRRRRR.",
"..RRRRRRRRRRRR..",
"...RRRRRRRRRR...",
"....RRRRRRRR....",
".....RRRRRR....."
]
readonly property var bedArt: [
"GGGGGGG........................................GGGGGGG",
"GGGGGGGGG....................................GGGGGGGGG",
"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG",
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG",
"..GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.."
]
readonly property var ballArt: [
"..YYY..",
".YLYYY.",
"YLYYYYY",
"YYYYYYY",
"YYYYYYY",
".YYYYY.",
"..YYY.."
] ]
property var frame: walkA property var frame: walkA
// Where he lives: the strip along the bottom, the same // His patch of floor
// floor line the worm lands on.
readonly property real floorY: archie.height - Theme.frameWidth readonly property real floorY: archie.height - Theme.frameWidth
readonly property real roamL: Theme.barWidth + 12 readonly property real roamL: Theme.barWidth + 12
readonly property real roamR: Math.max(roamL, readonly property real roamR: Math.max(roamL,
archie.width - Theme.frameWidth - cols * cell - 12) 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 - 120)
readonly property real bowlX: Math.max(roamL, roamR - 150) readonly property real bedX: Math.max(roamL, Math.min(bowlX - 500, roamL + 300))
// Stand so his muzzle lands in it rather than his shoulder: // Stand so his muzzle lands in the bowl, not his shoulder.
// head-down, his nose is about 22 cells along from his tail. readonly property real bowlStandX: bowlX - 32 * cell
readonly property real bowlStandX: bowlX - 18 * cell // Lying down, centred on the cushion.
readonly property real bedLieX: bedX + 5 * cell
property real px: roamL property real px: 0
property int facing: 1 // 1 = right, -1 = left property int facing: 1 // 1 = right, -1 = left
property string mode: "walk" property string mode: "walk"
property real targetX: 0 property real targetX: 0
property bool toBowl: false property string errand: "" // "" | "bowl" | "bed" | "ball"
property int hold: 0 property int hold: 0
property int tick: 0 // Legs swap on distance covered, not on a tick count, so the
// gait stays in step whether he is ambling or running for the
// ball. 3 cells is exactly how far the feet move in the art.
property real stride: 0
property bool stepped: false
// The ball
readonly property int ballSize: 7 * cell
property real ballX: 0
property real ballY: 0
property real ballVX: 0
property real ballVY: 0
property bool ballHeld: false // in your hand
property bool ballCarried: false // in his mouth
property bool ballThrown: false // he only fetches a fresh throw
property real lastBX: 0
property real lastBY: 0
property int carry: 0
// Where his mouth is, mirrored with him.
readonly property real mouthX: px + (facing > 0 ? 41 : 3) * cell
readonly property real mouthY: floorY - 13 * cell
// Exposed so Bar.qml can punch the ball out of the window's
// input mask without it the whole surface is click-through
// here and you could never grab the thing.
readonly property real hitX: ballX - 6
readonly property real hitY: ballY - 6
readonly property real hitW: primary ? ballSize + 12 : 0
// At rest on the floor after a throw: that is his cue.
readonly property bool ballLoose: ballThrown && !ballHeld && !ballCarried
&& Math.abs(ballVX) < 0.3 && Math.abs(ballVY) < 0.3
&& ballY >= floorY - ballSize - 1
function ballTick() {
if (ballCarried) {
ballX = mouthX - ballSize / 2;
ballY = mouthY - ballSize / 2;
return;
}
if (ballHeld) {
// Velocity comes from how fast you are dragging it, so
// letting go mid-swing throws it.
ballVX = Math.max(-45, Math.min(45, ballX - lastBX));
ballVY = Math.max(-45, Math.min(45, ballY - lastBY));
return;
}
ballVY += 0.55;
ballX += ballVX;
ballY += ballVY;
let ground = floorY - ballSize;
if (ballY >= ground) {
ballY = ground;
if (Math.abs(ballVY) > 1.2) ballVY = -ballVY * 0.55;
else ballVY = 0;
ballVX *= 0.92; // rolling friction
if (Math.abs(ballVX) < 0.08) ballVX = 0;
}
if (ballY < 0) { ballY = 0; ballVY = Math.abs(ballVY) * 0.4; }
let bl = Theme.barWidth;
let br = archie.width - Theme.frameWidth - ballSize;
if (ballX < bl) { ballX = bl; ballVX = -ballVX * 0.6; }
if (ballX > br) { ballX = br; ballVX = -ballVX * 0.6; }
}
// What he does next
function wander() { function wander() {
mode = "walk"; mode = "walk";
// Not if he is already standing at it otherwise he can errand = "";
// roll "go to the bowl" while stood in it and eat twice
// over without moving.
// Near enough to be worth it, but not already stood in it
// otherwise he either marches half a minute across the
// screen for a snack, or rolls "go to the bowl" while his
// head is in it and eats twice over without moving.
let dBowl = Math.abs(px - bowlStandX); let dBowl = Math.abs(px - bowlStandX);
toBowl = Math.random() < 0.3 && dBowl > 80 && dBowl < 900; let dBed = Math.abs(px - bedLieX);
// A short trip from wherever he is, not a random point on // Near enough to be worth it, and not already stood there
// the whole screen: picking uniformly meant single walks // otherwise he marches half a minute for a snack, or
// most of a minute long across a wide monitor, which reads // rolls "go to the bowl" with his head already in it. No
// as a patrol. Short hops read as pottering about. // errands while he has the ball; he is busy.
if (!ballCarried && Math.random() < 0.3 && dBowl > 80 && dBowl < 900) errand = "bowl";
else if (!ballCarried && Math.random() < 0.25 && dBed > 80 && dBed < 900) errand = "bed";
if (errand === "bowl") { targetX = bowlStandX; return; }
if (errand === "bed") { targetX = bedLieX; return; }
let dir = Math.random() < 0.5 ? -1 : 1; let dir = Math.random() < 0.5 ? -1 : 1;
// Turn away from an end he is already near. Clamping the // Turn away from an end he is already near; clamping the
// target instead just parks him against the wall, picking // target instead just parks him against the wall.
// the same spot over and over.
if (dir < 0 && px - roamL < 220) dir = 1; if (dir < 0 && px - roamL < 220) dir = 1;
if (dir > 0 && roamR - px < 220) dir = -1; if (dir > 0 && roamR - px < 220) dir = -1;
targetX = toBowl ? bowlStandX targetX = Math.max(roamL, Math.min(roamR,
: Math.max(roamL, Math.min(roamR, px + dir * (150 + Math.random() * 550)));
px + dir * (150 + Math.random() * 550))); }
function chase() {
mode = "walk";
errand = "ball";
// Line his mouth up with it, approaching from whichever
// side he is already on.
let bc = ballX + ballSize / 2;
targetX = (px + cols * cell / 2 < bc) ? bc - 41 * cell : bc - 3 * cell;
targetX = Math.max(roamL - 60, Math.min(roamR + 60, targetX));
} }
function arrive() { function arrive() {
if (toBowl) { if (errand === "ball") {
ballCarried = true;
ballThrown = false;
carry = 400 + Math.random() * 500; // 13-30s of pride
mode = "idle";
hold = 40;
} else if (errand === "bowl") {
mode = "eat"; mode = "eat";
hold = 100 + Math.random() * 80; hold = 100 + Math.random() * 80;
facing = 1; // the bowl is off his nose facing = 1;
} else if (errand === "bed") {
mode = "sleep";
hold = 600 + Math.random() * 900;
facing = 1;
} else { } else {
mode = "idle"; mode = "idle";
hold = 45 + Math.random() * 130; hold = 45 + Math.random() * 130;
} }
} }
// Placed on the first real width, not on completion: anchors // Placed on the first real width: anchors have not resolved at
// have not resolved yet at that point, so roamR collapses to // Component.onCompleted, so roamR collapses to roamL and he
// roamL and he spawns pinned to the left edge. // spawns pinned to the left edge.
property bool placed: false property bool placed: false
onWidthChanged: { onWidthChanged: {
if (placed || width <= 0) return; if (placed || width <= 0) return;
placed = true; placed = true;
px = roamL + Math.random() * (roamR - roamL); px = roamL + Math.random() * (roamR - roamL);
ballX = Math.min(roamR, px + 300);
ballY = floorY - ballSize;
wander(); wander();
} }
Timer { Timer {
interval: 33; running: archie.primary; repeat: true interval: 33; running: archie.primary; repeat: true
onTriggered: { onTriggered: {
archie.tick++; archie.ballTick();
archie.lastBX = archie.ballX;
archie.lastBY = archie.ballY;
// A thrown ball beats whatever else he was doing.
if (archie.ballLoose && archie.errand !== "ball") archie.chase();
if (archie.ballCarried && --archie.carry <= 0) {
archie.ballCarried = false;
archie.ballVX = 0;
archie.ballVY = 0;
}
if (archie.mode === "walk") { if (archie.mode === "walk") {
// Arrival first: checking after the step makes him // Arrival first: checking after the step makes him
// take a stride backwards when he is already there. // stride backwards when he is already there.
if (Math.abs(archie.targetX - archie.px) < 2) { if (Math.abs(archie.targetX - archie.px) < 2.5) {
archie.arrive(); archie.arrive();
} else { } else {
let dir = archie.targetX > archie.px ? 1 : -1; let dir = archie.targetX > archie.px ? 1 : -1;
let sp = archie.errand === "ball" ? 3.0 : 1.5;
archie.facing = dir; archie.facing = dir;
archie.px += dir * 1.6; archie.px += dir * sp;
// Swap legs every 5 ticks: at 1.6px a tick archie.stride += sp;
// that is exactly the 8px his feet move in the if (archie.stride >= 3 * archie.cell) {
// art (2 cells), so he does not moonwalk. archie.stride = 0;
archie.frame = archie.tick % 10 < 5 ? archie.walkA : archie.walkB; archie.stepped = !archie.stepped;
}
archie.frame = archie.stepped ? archie.walkB : archie.walkA;
} }
} else { } else {
archie.frame = archie.mode === "eat" ? archie.eat archie.frame = archie.mode === "eat" ? archie.eat
: (archie.tick % 26 < 13 ? archie.walkA : archie.wag); : archie.mode === "sleep" ? archie.sleepArt
: (archie.hold % 26 < 13 ? archie.walkA : archie.wag);
if (--archie.hold <= 0) archie.wander(); if (--archie.hold <= 0) archie.wander();
} }
} }
} }
// Sprite. One Rectangle per pixel, colour looked up per // Sprite: one Rectangle per pixel, colour looked up per
// frame; switching frames just rebinds them. // frame, so switching frames just rebinds them.
component Sprite: Item { component Sprite: Item {
id: sp id: sp
property var art: [] property var art: []
@ -1349,32 +1529,65 @@ in
} }
} }
// Bed first, so he lies in it rather than behind it.
Sprite {
art: archie.bedArt; w: 54; h: 7
x: Math.round(archie.bedX / archie.cell) * archie.cell
y: archie.floorY - height
}
Sprite { Sprite {
art: archie.frame art: archie.frame
w: archie.cols w: archie.cols
h: archie.rows h: archie.rows
// Snap to the pixel grid, same as the worm, or the art // Snap x to the pixel grid or the art shimmers as he
// shimmers as he walks. // walks. y is exact: he never moves vertically, and
// rounding sank his paws through the floor.
x: Math.round(archie.px / archie.cell) * archie.cell x: Math.round(archie.px / archie.cell) * archie.cell
// Exact, not grid-snapped: he never moves vertically, so
// there is nothing to shimmer, and rounding sank his paws
// a couple of pixels through the floor.
y: archie.floorY - height y: archie.floorY - height
// Mirror him when he heads back the other way. // Mirror him when he heads back the other way.
transform: Scale { origin.x: archie.cols * archie.cell / 2; xScale: archie.facing } transform: Scale { origin.x: archie.cols * archie.cell / 2; xScale: archie.facing }
} }
// Drawn after him, so at the bowl his nose goes behind it. // Bowl after him, so at dinner his nose goes behind it.
Sprite { Sprite {
art: archie.bowlArt art: archie.bowlArt; w: 16; h: 9
w: 8
h: 5
x: Math.round(archie.bowlX / archie.cell) * archie.cell x: Math.round(archie.bowlX / archie.cell) * archie.cell
// Exact, not grid-snapped: he never moves vertically, so
// there is nothing to shimmer, and rounding sank his paws
// a couple of pixels through the floor.
y: archie.floorY - height y: archie.floorY - height
} }
Sprite {
art: archie.ballArt; w: 7; h: 7
x: Math.round(archie.ballX / archie.cell) * archie.cell
y: Math.round(archie.ballY / archie.cell) * archie.cell
}
// Grab handle for the ball, a little larger than the ball so
// it is catchable while it rolls.
MouseArea {
x: archie.hitX; y: archie.hitY
width: archie.hitW; height: archie.hitW
cursorShape: Qt.PointingHandCursor
property real grabDX: 0
property real grabDY: 0
onPressed: (m) => {
let p = mapToItem(archie, m.x, m.y);
grabDX = p.x - archie.ballX;
grabDY = p.y - archie.ballY;
archie.ballCarried = false; // you may take it off him
archie.ballHeld = true;
}
onPositionChanged: (m) => {
if (!archie.ballHeld) return;
let p = mapToItem(archie, m.x, m.y);
archie.ballX = p.x - grabDX;
archie.ballY = p.y - grabDY;
}
onReleased: {
archie.ballHeld = false;
archie.ballThrown = true;
}
}
} }
''; '';
}; };
@ -1694,6 +1907,14 @@ in
width: launcherPanel.visible ? launcherPanel.width : 0 width: launcherPanel.visible ? launcherPanel.width : 0
height: launcherPanel.visible ? launcherPanel.height : 0 height: launcherPanel.visible ? launcherPanel.height : 0
} }
// Archie's ball. The rest of this surface is click-through,
// so without a hole here you could never pick it up.
Region {
x: archieDog.hitX
y: archieDog.hitY
width: archieDog.hitW
height: archieDog.hitW
}
} }
Item { Item {
@ -7035,7 +7256,7 @@ in
} }
// Archie. One dog, primary screen only. // Archie. One dog, primary screen only.
Archie { primary: bar.screen === Quickshell.screens[0] } Archie { id: archieDog; primary: bar.screen === Quickshell.screens[0] }
} }
''; '';
}; };