bar: clock to bottom above power; drop Archie's ball
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
2c65cf1138
commit
ee9e9ffbc7
1 changed files with 65 additions and 271 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue