aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-07 12:23:51 +0100
committerDanilo M. <danix@danix.xyz>2026-11-07 12:23:51 +0100
commit27e5bfb21627c73b53623c5ccbf93841a221bfb2 (patch)
tree8fce980fd08acf404c77156ca5f42869b0968679
parent1763a372be1aa7d9a9668fd6db76bd7dac63b658 (diff)
downloadquickshell-27e5bfb21627c73b53623c5ccbf93841a221bfb2.tar.gz
quickshell-27e5bfb21627c73b53623c5ccbf93841a221bfb2.zip
island: shuffle and repeat as two Sonix buttons
The one mode button cycled all five SonixLink modes and showed repeat-off as its first state, so shuffle looked missing. Shuffle is now a toggle on the left and repeat a cycle on the right. SonixLink has no shuffle with repeat one, so with shuffle on repeat goes off and all only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--desktop/island/README.md2
-rw-r--r--desktop/island/SonixPanel.qml10
-rw-r--r--desktop/modules/sonix/Sonix.js27
-rw-r--r--desktop/modules/sonix/test_sonix.js25
4 files changed, 50 insertions, 14 deletions
diff --git a/desktop/island/README.md b/desktop/island/README.md
index 7adf9ad..1b1a3f2 100644
--- a/desktop/island/README.md
+++ b/desktop/island/README.md
@@ -54,7 +54,7 @@ page now (`desktop/modules/perf/`).
- **Sonix player icon** shows while a HiBy R1 running Sonix Player is on
the network (its SonixLink beacon). A dot under it while it plays. Click
opens its panel: cover, title, a heart for the track's favourite,
- quality, seek, transport, mode, volume (sent once it settles), battery.
+ quality, seek, transport, shuffle and repeat, volume (sent once it settles), battery.
See `desktop/modules/sonix/README.md`.
- **Headphones icon** shows while Sony headphones are connected (a BlueZ
name starting `WH-`, `WF-` or `LinkBuds`). Click opens the headphones
diff --git a/desktop/island/SonixPanel.qml b/desktop/island/SonixPanel.qml
index 609afda..09e7b1c 100644
--- a/desktop/island/SonixPanel.qml
+++ b/desktop/island/SonixPanel.qml
@@ -281,9 +281,15 @@ Column {
}
TransportButton {
+ anchors { left: parent.left; verticalCenter: parent.verticalCenter }
+ glyph: panel.glyph(S.shuffleGlyph(panel.p.mode))
+ onClicked: Sonix.command({ do: "mode", value: S.toggleShuffle(panel.p.mode) }, null, panel.report)
+ }
+
+ TransportButton {
anchors { right: parent.right; verticalCenter: parent.verticalCenter }
- glyph: panel.glyph(S.modeGlyph(panel.p.mode))
- onClicked: Sonix.command({ do: "mode", value: S.nextMode(panel.p.mode) }, null, panel.report)
+ glyph: panel.glyph(S.repeatGlyph(panel.p.mode))
+ onClicked: Sonix.command({ do: "mode", value: S.nextRepeat(panel.p.mode) }, null, panel.report)
}
}
diff --git a/desktop/modules/sonix/Sonix.js b/desktop/modules/sonix/Sonix.js
index d4330ce..55b8ac4 100644
--- a/desktop/modules/sonix/Sonix.js
+++ b/desktop/modules/sonix/Sonix.js
@@ -39,12 +39,29 @@ function qualityLine(s) {
return parts.join(" · ");
}
-const MODES = ["normal", "repeat_all", "repeat_one", "shuffle", "shuffle_repeat"];
-// md-repeat_off, md-repeat, md-repeat_once, md-shuffle, md-shuffle (with repeat)
-const MODE_GLYPHS = [0xF0457, 0xF0456, 0xF0458, 0xF049D, 0xF049D];
+// SonixLink has one playback mode out of five; the panel shows it as a
+// shuffle toggle and a repeat cycle. There is no shuffle with repeat one, so
+// with shuffle on repeat only goes off and all, and turning shuffle on from
+// repeat one drops the repeat.
+function shuffled(m) { return m === "shuffle" || m === "shuffle_repeat"; }
+
+function toggleShuffle(m) {
+ if (m === "shuffle") return "normal";
+ if (m === "shuffle_repeat") return "repeat_all";
+ return m === "repeat_all" ? "shuffle_repeat" : "shuffle";
+}
+
+function nextRepeat(m) {
+ if (shuffled(m)) return m === "shuffle" ? "shuffle_repeat" : "shuffle";
+ return m === "repeat_all" ? "repeat_one" : m === "repeat_one" ? "normal" : "repeat_all";
+}
-function nextMode(m) { return MODES[(MODES.indexOf(m) + 1) % MODES.length]; }
-function modeGlyph(m) { return MODE_GLYPHS[Math.max(0, MODES.indexOf(m))]; }
+// md-shuffle / md-shuffle_disabled
+function shuffleGlyph(m) { return shuffled(m) ? 0xF049D : 0xF049E; }
+// md-repeat / md-repeat_once / md-repeat_off
+function repeatGlyph(m) {
+ return m === "repeat_all" || m === "shuffle_repeat" ? 0xF0456 : m === "repeat_one" ? 0xF0458 : 0xF0457;
+}
function clock(sec) {
sec = Math.max(0, Math.floor(sec));
diff --git a/desktop/modules/sonix/test_sonix.js b/desktop/modules/sonix/test_sonix.js
index 32da061..7847e41 100644
--- a/desktop/modules/sonix/test_sonix.js
+++ b/desktop/modules/sonix/test_sonix.js
@@ -19,7 +19,7 @@ const assert = require("assert");
const src = fs.readFileSync(__dirname + "/Sonix.js", "utf8")
.replace(/^\.pragma library$/m, "");
const S = new Function(src +
- "\nreturn { parseBeacon, qualityLine, nextMode, modeGlyph, clock, cover, centred, zoomAt," +
+ "\nreturn { parseBeacon, qualityLine, toggleShuffle, nextRepeat, shuffleGlyph, repeatGlyph, clock, cover, centred, zoomAt," +
" stamp, query, crumbs, parentOf, colour, offsetXY };")();
// Beacon: "SONIXLINK1 <ip> <port> <name with spaces>".
@@ -38,11 +38,24 @@ assert.strictEqual(S.qualityLine({ path: "x.mp3", sample_rate: 44100, bits: 0, b
assert.strictEqual(S.qualityLine({ path: "", sample_rate: 0, bits: 0, bitrate: 0 }), "");
// Mode cycle, unknown restarts at normal.
-assert.strictEqual(S.nextMode("normal"), "repeat_all");
-assert.strictEqual(S.nextMode("shuffle_repeat"), "normal");
-assert.strictEqual(S.nextMode("???"), "normal");
-assert.strictEqual(S.modeGlyph("repeat_one"), 0xF0458);
-assert.strictEqual(S.modeGlyph("???"), 0xF0457);
+// Shuffle and repeat as two buttons over SonixLink's five modes.
+assert.strictEqual(S.toggleShuffle("normal"), "shuffle");
+assert.strictEqual(S.toggleShuffle("repeat_all"), "shuffle_repeat");
+assert.strictEqual(S.toggleShuffle("repeat_one"), "shuffle");
+assert.strictEqual(S.toggleShuffle("shuffle"), "normal");
+assert.strictEqual(S.toggleShuffle("shuffle_repeat"), "repeat_all");
+assert.strictEqual(S.toggleShuffle("???"), "shuffle");
+assert.strictEqual(S.nextRepeat("normal"), "repeat_all");
+assert.strictEqual(S.nextRepeat("repeat_all"), "repeat_one");
+assert.strictEqual(S.nextRepeat("repeat_one"), "normal");
+assert.strictEqual(S.nextRepeat("shuffle"), "shuffle_repeat");
+assert.strictEqual(S.nextRepeat("shuffle_repeat"), "shuffle");
+assert.strictEqual(S.nextRepeat("???"), "repeat_all");
+assert.strictEqual(S.shuffleGlyph("shuffle_repeat"), 0xF049D);
+assert.strictEqual(S.shuffleGlyph("repeat_one"), 0xF049E);
+assert.strictEqual(S.repeatGlyph("repeat_one"), 0xF0458);
+assert.strictEqual(S.repeatGlyph("shuffle_repeat"), 0xF0456);
+assert.strictEqual(S.repeatGlyph("shuffle"), 0xF0457);
assert.strictEqual(S.clock(0), "0:00");
assert.strictEqual(S.clock(61.9), "1:01");