mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-14 23:34:01 -04:00
lyrics: keep following through same-song refreshes
This commit is contained in:
+1
-1
Submodule dank-qml-common updated: 640b739e31...27e67d6a0c
Generated
+3
-3
@@ -3,11 +3,11 @@
|
||||
"dank-qml-common": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1790195634,
|
||||
"narHash": "sha256-s+9ZHGZmKtc1oyQgvi7obdjoASEnelQ25gNO7zL3wRM=",
|
||||
"lastModified": 1790208961,
|
||||
"narHash": "sha256-lCXDr+VgmazTsJx7FgrBdur70tSNK2TSV/PrL+UDHsE=",
|
||||
"owner": "AvengeMedia",
|
||||
"repo": "dank-qml-common",
|
||||
"rev": "640b739e31709d9aa7d5b9d26f511babc9b95daf",
|
||||
"rev": "27e67d6a0cc3d6ea211fe68197bf296ec387b0fd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -25,6 +25,7 @@ QtObject {
|
||||
readonly property real position: activePlayer?.position ?? 0
|
||||
readonly property int playbackState: activePlayer?.playbackState ?? MprisPlaybackState.Stopped
|
||||
readonly property bool playing: playbackState === MprisPlaybackState.Playing
|
||||
readonly property bool stopped: playbackState === MprisPlaybackState.Stopped
|
||||
readonly property real rate: activePlayer?.rate ?? 1
|
||||
|
||||
property var lines: []
|
||||
@@ -69,6 +70,7 @@ QtObject {
|
||||
onEnabledChanged: {
|
||||
if (!enabled) {
|
||||
cancel();
|
||||
stopClock();
|
||||
if (state === "loading") {
|
||||
requestedKey = "";
|
||||
state = "idle";
|
||||
@@ -93,20 +95,22 @@ QtObject {
|
||||
onPositionChanged: scheduleAnchor()
|
||||
onRateChanged: updatePlaybackClock()
|
||||
onPlayingChanged: updatePlaybackClock()
|
||||
onPlaybackStateChanged: {
|
||||
if (playbackState === MprisPlaybackState.Stopped && !anchorPlaying)
|
||||
updatePlaybackClock();
|
||||
Component.onDestruction: {
|
||||
cancel();
|
||||
stopClock();
|
||||
}
|
||||
Component.onDestruction: cancel()
|
||||
|
||||
function cancel() {
|
||||
serial++;
|
||||
if (requestId !== null)
|
||||
backend.cancelRequest(requestId);
|
||||
requestId = null;
|
||||
requestDelay.stop();
|
||||
}
|
||||
|
||||
function stopClock() {
|
||||
tick.stop();
|
||||
anchorUpdate.stop();
|
||||
requestDelay.stop();
|
||||
}
|
||||
|
||||
function songTitle() {
|
||||
@@ -124,6 +128,7 @@ QtObject {
|
||||
}
|
||||
|
||||
function clearContent() {
|
||||
stopClock();
|
||||
lines = [];
|
||||
plainLines = [];
|
||||
synced = false;
|
||||
@@ -188,8 +193,6 @@ QtObject {
|
||||
const resultKey = found ? JSON.stringify([result.instrumental === true, result.synced ?? [], result.voices ?? {}, result.plain ?? "", result.attribution ?? {}]) : "";
|
||||
if (refreshing && resultKey === shownResult)
|
||||
return;
|
||||
if (refreshing)
|
||||
clearContent();
|
||||
if (response.error) {
|
||||
useEmbeddedText("error");
|
||||
return;
|
||||
@@ -201,6 +204,7 @@ QtObject {
|
||||
shownSong = requestedSong;
|
||||
shownResult = resultKey;
|
||||
if (result.instrumental) {
|
||||
clearContent();
|
||||
state = "instrumental";
|
||||
return;
|
||||
}
|
||||
@@ -323,9 +327,9 @@ QtObject {
|
||||
function updatePlaybackClock() {
|
||||
const predicted = currentTime();
|
||||
const sampled = position + (anchorPlaying ? (Date.now() - positionWall) * anchorRate / 1000 : 0);
|
||||
const pendingSeek = anchorUpdate.running && Math.abs(sampled - predicted) >= DashMetrics.mediaLyricsPositionTolerance;
|
||||
const pendingSeek = !stopped && anchorUpdate.running && Math.abs(sampled - predicted) >= DashMetrics.mediaLyricsPositionTolerance;
|
||||
anchorUpdate.stop();
|
||||
anchorPosition = playbackState === MprisPlaybackState.Stopped ? 0 : pendingSeek ? sampled : predicted;
|
||||
anchorPosition = pendingSeek ? sampled : predicted;
|
||||
anchorWall = Date.now();
|
||||
anchorPlaying = playing;
|
||||
anchorRate = rate;
|
||||
@@ -334,7 +338,7 @@ QtObject {
|
||||
|
||||
function scheduleAnchor() {
|
||||
positionWall = Date.now();
|
||||
if (enabled && synced && !anchorUpdate.running)
|
||||
if (enabled && synced && !stopped && !anchorUpdate.running)
|
||||
anchorUpdate.start();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ FocusScope {
|
||||
readonly property int activeIndex: controller.activeIndex
|
||||
readonly property int firstFocusedIndex: controller.focusedGroups[0] ?? -1
|
||||
readonly property string trackKey: controller.trackKey
|
||||
readonly property string shownResult: controller.shownResult
|
||||
readonly property bool userScrolling: transcript.isUserScrolling || transcript.dragging
|
||||
readonly property real currentLineY: transcript.currentItem?.y ?? 0
|
||||
readonly property real shapeInset: Math.ceil(Math.min(radius, width / 2, height / 2) * (1 - Math.SQRT1_2))
|
||||
@@ -50,6 +51,7 @@ FocusScope {
|
||||
onActiveIndexChanged: followTimer.restart()
|
||||
onFirstFocusedIndexChanged: followTimer.restart()
|
||||
onReadyChanged: snapToCurrent()
|
||||
onShownResultChanged: snapToCurrent()
|
||||
onWidthChanged: snapToCurrent()
|
||||
onHeightChanged: snapToCurrent()
|
||||
onTrackKeyChanged: {
|
||||
|
||||
@@ -100,10 +100,10 @@ Item {
|
||||
if (!root.live || !root.lyricsOpener?.visible || !root.lyricsOpener.enabled)
|
||||
return;
|
||||
if (typeof root.lyricsOpener.requestFocus === "function") {
|
||||
root.lyricsOpener.requestFocus(false);
|
||||
root.lyricsOpener.requestFocus(false, Qt.OtherFocusReason);
|
||||
return;
|
||||
}
|
||||
root.lyricsOpener.forceActiveFocus(Qt.PopupFocusReason);
|
||||
root.lyricsOpener.forceActiveFocus(Qt.OtherFocusReason);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,12 @@ FocusScope {
|
||||
|
||||
function focusFace() {
|
||||
pageActions.clearFocus();
|
||||
(root.tab?.focusTarget ?? root).forceActiveFocus();
|
||||
const target = root.tab?.focusTarget ?? root;
|
||||
if (typeof target.requestFocus === "function") {
|
||||
target.requestFocus(false, Qt.OtherFocusReason);
|
||||
return true;
|
||||
}
|
||||
target.forceActiveFocus(Qt.OtherFocusReason);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -206,6 +206,20 @@ ShellRoot {
|
||||
check(!overlay.following && input.findChild(overlay, "followPlayback")?.visible, "browsing pauses automatic following");
|
||||
input.findChild(overlay, "followPlayback").click();
|
||||
check(overlay.following, "follow action resumes synchronized scrolling");
|
||||
const settledCalls = backend.calls;
|
||||
MprisController.stableAlbum = "Late album";
|
||||
waitFor(() => backend.calls === settledCalls + 1, "a late album refreshes the lookup");
|
||||
check(media.lyrics.state === "ready" && media.lyrics.tick.running, "the shown lyrics keep following during the refresh");
|
||||
backend.respond({
|
||||
result: {
|
||||
found: true,
|
||||
synced: root.timed
|
||||
}
|
||||
});
|
||||
check(media.lyrics.tick.running, "an unchanged refresh keeps the timer running");
|
||||
source.playbackState = MprisPlaybackState.Stopped;
|
||||
check(media.lyrics.activeIndex === 1, "stopped playback holds the lyric position");
|
||||
source.playbackState = MprisPlaybackState.Playing;
|
||||
source.playbackState = MprisPlaybackState.Paused;
|
||||
check(!media.lyrics.tick.running, "paused playback has no lyric timer");
|
||||
source.playbackState = MprisPlaybackState.Playing;
|
||||
@@ -229,7 +243,7 @@ ShellRoot {
|
||||
check(!media.lyrics.tick.running, "closing stops scheduling");
|
||||
media.lyricsOpen = true;
|
||||
waitFor(() => !!media.lyricsFocusTarget, "reopened lyrics load");
|
||||
check(backend.calls === 1 && media.lyrics.state === "ready", "reopening reuses loaded lyrics");
|
||||
check(backend.calls === settledCalls + 1 && media.lyrics.state === "ready", "reopening reuses loaded lyrics");
|
||||
media.lyrics.request();
|
||||
const stale = backend.pending[backend.nextId];
|
||||
MprisController.stableTitle = "Next track";
|
||||
|
||||
Reference in New Issue
Block a user