From ee9e9ffbc7d0516f90f8f58aec8dc5588c45046f Mon Sep 17 00:00:00 2001 From: rope Date: Fri, 7 Aug 2026 21:15:01 +0100 Subject: [PATCH] bar: clock to bottom above power; drop Archie's ball Co-Authored-By: Claude Opus 5 --- settings/quickshell.nix | 336 ++++++++-------------------------------- 1 file changed, 65 insertions(+), 271 deletions(-) diff --git a/settings/quickshell.nix b/settings/quickshell.nix index 8408d46..480e56b 100644 --- a/settings/quickshell.nix +++ b/settings/quickshell.nix @@ -1126,8 +1126,7 @@ in // Archie — a black and tan dachshund with a red collar, living // along the bottom of the screen. He trots about, wags, naps in - // his bed, visits his bowl, and fetches a ball you can pick up - // and fling with the mouse. + // his bed and visits his bowl. // // The art is plain text, one character per pixel, so he can be // redrawn by editing the picture instead of editing code. He walks @@ -1154,9 +1153,7 @@ in "N": "#14100f", // nose and eye "B": "#96602f", // kibble "G": "#5a667a", // bed - "D": "#3a4454", // bed cushion - "Y": "#c8d84a", // ball - "L": "#e4ee8c" // ball highlight + "D": "#3a4454" // bed cushion }) // Trotting: front leg forward, hind leg back, tail up. Paws @@ -1206,52 +1203,6 @@ in "........TTT..........TTT....................", "........TTTTT........TTTTT.................." ] - // Galloping for the ball. A bound, not a trot: reaching, with - // the hind legs thrown back and the front legs stretched out - // ahead — then everything gathered under him. Same body and - // tail as walkB; it is entirely in the legs. - readonly property var runA: [ - "...............................KKKKKKKK.....", - "..............................KKKKKKKKKK....", - "KKKK..........................KKKKKKKKKKK...", - "KKKK..........................KKKKK.KKKKK...", - "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKK..TTKKKKK.", - ".KKKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTTTTTT.", - "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTTTTT..", - "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTT.....", - "...KKKKKKKKKKKKKKKKKKKKKKKKKTTKKKK..........", - "...KKKKKKKKKKKKKKKKKKKKKKKKKTTTKKK..........", - "...KKKKKKKKKKKKKKKKKKKKKKKKTTTT.............", - ".....KKKKKKKKKKKKKKKKKKKKKTTTT..............", - "....TTKKKKKKKKKKKKKKKKKKKTTTT...............", - ".....TTTTT..............TTTT................", - "....TTTTT................TTTT...............", - "...TTTTT..................TTTT..............", - "..TTTTT....................TTTT.............", - "..TTTTT....................TTTT.............", - ".TTTTTT....................TTTTTT..........." - ] - readonly property var runB: [ - "...............................KKKKKKKK.....", - "..............................KKKKKKKKKK....", - "KKKK..........................KKKKKKKKKKK...", - "KKKK..........................KKKKK.KKKKK...", - "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKK..TTKKKKK.", - ".KKKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTTTTTT.", - "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTTTTT..", - "..KKKKKKKKKKKKKKKKKKKKKKKKKKRRKKKKTTTTT.....", - "...KKKKKKKKKKKKKKKKKKKKKKKKKTTKKKK..........", - "...KKKKKKKKKKKKKKKKKKKKKKKKKTTTKKK..........", - "...KKKKKKKKKKKKKKKKKKKKKKKKTTTT.............", - ".....KKKKKKKKKKKKKKKKKKKKKTTTT..............", - "....TTKKKKKKKKKKKKKKKKKKKTTTT...............", - ".........TTTTT........TTTT..................", - "..........TTTTT......TTTT...................", - "...........TTTTT....TTTT....................", - "............TTTTT..TTTT.....................", - "............TTTTT..TTTT.....................", - "............TTTTTTTTTTTT...................." - ] // Standing: walkA's planted legs, walkB's tail. Flipping // between this and walkA is the wag. readonly property var wag: [ @@ -1363,15 +1314,6 @@ in "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", "..GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.." ] - readonly property var ballArt: [ - "..YYY..", - ".YLYYY.", - "YLYYYYY", - "YYYYYYY", - "YYYYYYY", - ".YYYYY.", - "..YYY.." - ] property var frame: walkA @@ -1395,79 +1337,18 @@ in property int facing: 1 // 1 = right, -1 = left property string mode: "walk" property real targetX: 0 - property string errand: "" // "" | "bowl" | "bed" | "ball" + property string errand: "" // "" | "bowl" | "bed" property int hold: 0 // Ticks since he last got up. Past `tired` he takes himself - // off to bed; the ball resets it, so a throw both wakes him - // and buys him a fresh stretch of being up. + // off to bed. property int awake: 0 readonly property int tired: 2700 // 90s on his feet // 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. + // gait stays in step. 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() { mode = "walk"; @@ -1475,7 +1356,7 @@ in // Bed is not a random errand any more, it is what he does // when he has been up long enough. A dog sleeps most of // the day; pottering about non-stop is the odd bit. - if (awake > tired && !ballCarried) { + if (awake > tired) { errand = "bed"; targetX = bedLieX; return; @@ -1483,9 +1364,8 @@ in let dBowl = Math.abs(px - bowlStandX); // Near enough to be worth it, and not already stood there // — otherwise he marches half a minute for a snack, or - // rolls "go to the bowl" with his head already in it. No - // errands while he has the ball; he is busy. - if (!ballCarried && Math.random() < 0.3 && dBowl > 80 && dBowl < 900) { + // rolls "go to the bowl" with his head already in it. + if (Math.random() < 0.3 && dBowl > 80 && dBowl < 900) { errand = "bowl"; targetX = bowlStandX; return; @@ -1500,24 +1380,8 @@ in 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() { - if (errand === "ball") { - ballCarried = true; - ballThrown = false; - carry = 400 + Math.random() * 500; // 13-30s of pride - mode = "idle"; - hold = 40; - } else if (errand === "bowl") { + if (errand === "bowl") { mode = "eat"; hold = 100 + Math.random() * 80; facing = 1; @@ -1541,32 +1405,14 @@ in if (placed || width <= 0) return; placed = true; px = roamL + Math.random() * (roamR - roamL); - ballX = Math.min(roamR, px + 300); - ballY = floorY - ballSize; wander(); } Timer { interval: 33; running: archie.primary; repeat: true onTriggered: { - archie.ballTick(); - archie.lastBX = archie.ballX; - archie.lastBY = archie.ballY; - - // A thrown ball beats whatever else he was doing, - // sleeping included — that is how you wake him. - if (archie.ballLoose && archie.errand !== "ball") { - archie.awake = 0; - archie.chase(); - } archie.awake = archie.mode === "sleep" ? 0 : archie.awake + 1; - if (archie.ballCarried && --archie.carry <= 0) { - archie.ballCarried = false; - archie.ballVX = 0; - archie.ballVY = 0; - } - if (archie.mode === "walk") { // Arrival first: checking after the step makes him // stride backwards when he is already there. @@ -1574,21 +1420,15 @@ in archie.arrive(); } else { let dir = archie.targetX > archie.px ? 1 : -1; - let run = archie.errand === "ball"; - let sp = run ? 3.0 : 1.5; + let sp = 1.5; archie.facing = dir; archie.px += dir * sp; archie.stride += sp; - // A gallop covers more ground per stride than - // a trot, so the frames hold longer even though - // he is moving faster. - if (archie.stride >= (run ? 6 : 3) * archie.cell) { + if (archie.stride >= 3 * archie.cell) { archie.stride = 0; archie.stepped = !archie.stepped; } - archie.frame = run - ? (archie.stepped ? archie.runB : archie.runA) - : (archie.stepped ? archie.walkB : archie.walkA); + archie.frame = archie.stepped ? archie.walkB : archie.walkA; } } else { // Breathing: in for a third of the cycle, then @@ -1659,39 +1499,6 @@ in x: Math.round(archie.bowlX / archie.cell) * archie.cell 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; - } - } } ''; }; @@ -2011,16 +1818,6 @@ in width: launcherPanel.visible ? launcherPanel.width : 0 height: launcherPanel.visible ? launcherPanel.height : 0 } - ${lib.optionalString (!isMacbook) '' - // 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 { @@ -4198,8 +3995,7 @@ in precision: SystemClock.Minutes } - // ── Top cluster: logo, clock, workspaces — the three groups - // that belong to "this machine, right now", split by hairlines. + // ── Top cluster: logo, workspaces — split by a hairline. // The server glance is NOT here: it anchors above the bottom // cluster instead, so adding a workspace grows this column // downward into empty space rather than shoving the stats. @@ -4244,58 +4040,6 @@ in BarSep {} - // Clock — HH over mm; "HH:mm" does not fit a 46px column. - Item { - id: clockText - width: Theme.barWidth - height: clockCol.height - property date now: sysClock.date - - Column { - id: clockCol - anchors.horizontalCenter: parent.horizontalCenter - spacing: 0 - - SText { - anchors.horizontalCenter: parent.horizontalCenter - text: clockText.now.toLocaleTimeString(Qt.locale(), "HH") - font.pixelSize: 15 - font.weight: Font.Medium - } - SText { - anchors.horizontalCenter: parent.horizontalCenter - text: clockText.now.toLocaleTimeString(Qt.locale(), "mm") - font.pixelSize: 15 - font.weight: Font.Medium - color: Theme.base04 - } - } - - // Anchors to the clock like every other dropdown - // anchors to its widget; the clamp in BarDropdown - // pulls it back on screen when it overhangs. - function openCalendar() { - bar.toggleDropdown(calPopup, function() { - let pos = clockText.mapToItem(bar.contentItem, 0, clockText.height / 2); - calPopup.dropdownY = pos.y; - calPopup.resetView(); - }); - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - onClicked: clockText.openCalendar() - onEntered: { - if (bar.activeDropdown) { - if (bar.activeDropdown !== calPopup) clockText.openCalendar(); - } - } - } - } - - BarSep {} - // Workspace dots: accent pill for the focused workspace, // dim dots otherwise. Stacked vertically now, so the // focused pill grows along HEIGHT rather than width. @@ -4537,7 +4281,7 @@ in } } - // ── Bottom cluster: status icons, tray, power button. Anchored + // ── Bottom cluster: status icons, tray, clock, power button. Anchored // to the bottom edge; the server glance sits just above it and // the OSD borrows that same slot while it is on screen. Column { @@ -5005,6 +4749,56 @@ in // as three clusters instead of one clump. BarSep {} + // Clock — HH over mm; "HH:mm" does not fit a 46px column. + Item { + id: clockText + width: Theme.barWidth + height: clockCol.height + property date now: sysClock.date + + Column { + id: clockCol + anchors.horizontalCenter: parent.horizontalCenter + spacing: 0 + + SText { + anchors.horizontalCenter: parent.horizontalCenter + text: clockText.now.toLocaleTimeString(Qt.locale(), "HH") + font.pixelSize: 15 + font.weight: Font.Medium + } + SText { + anchors.horizontalCenter: parent.horizontalCenter + text: clockText.now.toLocaleTimeString(Qt.locale(), "mm") + font.pixelSize: 15 + font.weight: Font.Medium + color: Theme.base04 + } + } + + // Anchors to the clock like every other dropdown + // anchors to its widget; the clamp in BarDropdown + // pulls it back on screen when it overhangs. + function openCalendar() { + bar.toggleDropdown(calPopup, function() { + let pos = clockText.mapToItem(bar.contentItem, 0, clockText.height / 2); + calPopup.dropdownY = pos.y; + calPopup.resetView(); + }); + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onClicked: clockText.openCalendar() + onEntered: { + if (bar.activeDropdown) { + if (bar.activeDropdown !== calPopup) clockText.openCalendar(); + } + } + } + } + // Power menu opener — bottom-left corner. Opens the same // session menu as Super+L, which now unfolds from here. Item {