feat(lyrics): lite-fill display pref — static active-line highlight to cut CPU
The per-word karaoke sweep animates a clip rect ~60x/s, driving continuous full-window repaints (~4% CPU). Lite fill highlights the active line whole (no per-frame animation) so the surface only repaints on line changes. New toggle in the lyrics controls flyout, persisted via lyrics_prefs; default off (smooth karaoke). Kiosk / weak-GPU tiers force it on via ShellState.reduce-motion. cc
This commit is contained in:
4 files changed
+44
-7
No files matched your search
@@ -176,6 +176,21 @@ export component LyricsControlsPanel inherits Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// Lite fill (perf): highlight the active line whole instead of the
|
||||
// per-word karaoke sweep — stops the continuous full-window repaints.
|
||||
HorizontalLayout {
|
||||
height: 22px;
|
||||
spacing: 8px;
|
||||
RowLabel { text: @tr("Lite fill (saves CPU)"); }
|
||||
QbzToggle {
|
||||
checked: LyricsState.lite-fill;
|
||||
toggled(v) => {
|
||||
LyricsState.lite-fill = v;
|
||||
LyricsState.prefs-changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dimming (Tauri segmented Off/Soft/Strong -> select).
|
||||
HorizontalLayout {
|
||||
height: 30px;
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
import { Theme } from "../foundation/semantic-colors.slint";
|
||||
import { Typography } from "../foundation/typography.slint";
|
||||
import { LyricsState, LyricsSegment } from "../state.slint";
|
||||
import { LyricsState, LyricsSegment, ShellState } from "../state.slint";
|
||||
|
||||
// Q6 (SHIPPED): Tauri's four non-system lyric font families, embedded into
|
||||
// the binary at compile time (slint-build default EmbedAllResources for Rust
|
||||
@@ -218,17 +218,24 @@ component LyricsLineRow inherits VerticalLayout {
|
||||
// Bright (sung) overlay, clipped to this segment's LOCAL fill.
|
||||
// local = clamp((line-progress - start-weight) / width-weight, 0, 1)
|
||||
Rectangle {
|
||||
// Lite fill (pref) or reduce-motion (kiosk / weak GPU): the
|
||||
// active line is fully lit and STATIC — no per-frame clip
|
||||
// animation, so the surface stops repainting the whole window
|
||||
// ~60x/s just to sweep the karaoke fill.
|
||||
property <bool> cheap: ShellState.reduce-motion || LyricsState.lite-fill;
|
||||
x: 0;
|
||||
y: 0;
|
||||
width: segbase.width * Math.clamp(
|
||||
seg.width-weight > 0
|
||||
? (LyricsState.line-progress - seg.start-weight) / seg.width-weight
|
||||
: 0,
|
||||
0.0, 1.0);
|
||||
width: self.cheap
|
||||
? segbase.width
|
||||
: segbase.width * Math.clamp(
|
||||
seg.width-weight > 0
|
||||
? (LyricsState.line-progress - seg.start-weight) / seg.width-weight
|
||||
: 0,
|
||||
0.0, 1.0);
|
||||
height: segbase.height;
|
||||
clip: true;
|
||||
animate width {
|
||||
duration: LyricsState.fill-anim-ms * 1ms;
|
||||
duration: self.cheap ? 0ms : LyricsState.fill-anim-ms * 1ms;
|
||||
easing: linear;
|
||||
}
|
||||
Text {
|
||||
|
||||
@@ -4220,6 +4220,12 @@ export global LyricsState {
|
||||
// 0 off · 1 soft · 2 strong (Tauri default 'strong').
|
||||
in-out property <int> dimming-mode: 2;
|
||||
in-out property <bool> uppercase: false;
|
||||
// Lite fill (perf): when true, the active line is highlighted whole instead
|
||||
// of the per-word karaoke sweep — no per-frame clip animation, so the lyrics
|
||||
// surface stops driving continuous full-window repaints (~4% CPU -> idle
|
||||
// between line changes). Default false (smooth karaoke). Kiosk / weak-GPU
|
||||
// tiers force it on via ShellState.reduce-motion regardless of this pref.
|
||||
in-out property <bool> lite-fill: false;
|
||||
// Active (karaoke fill) color. `use-custom-color: false` = follow the theme
|
||||
// accent (Tauri activeColor: ''). The palette swatches set both (deviation
|
||||
// D6: swatches instead of a native color input — Slint has none).
|
||||
|
||||
@@ -52,6 +52,10 @@ pub struct LyricsPrefs {
|
||||
pub active_color: String, // "#RRGGBB" or "" = theme accent
|
||||
#[serde(default)]
|
||||
pub uppercase: bool,
|
||||
/// Lite fill (perf): highlight the active line whole instead of the per-word
|
||||
/// karaoke sweep, so the lyrics surface stops driving continuous repaints.
|
||||
#[serde(default)]
|
||||
pub lite_fill: bool,
|
||||
}
|
||||
|
||||
fn d_true() -> bool {
|
||||
@@ -77,6 +81,7 @@ impl Default for LyricsPrefs {
|
||||
dimming: d_dimming(),
|
||||
active_color: String::new(),
|
||||
uppercase: false,
|
||||
lite_fill: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,6 +217,7 @@ pub fn apply_to_ui(window: &AppWindow, prefs: &LyricsPrefs) {
|
||||
}
|
||||
None => state.set_use_custom_color(false),
|
||||
}
|
||||
state.set_lite_fill(prefs.lite_fill);
|
||||
}
|
||||
|
||||
/// Read the in-out props back into a pref set and persist — the
|
||||
@@ -241,6 +247,7 @@ pub fn persist_from_ui(window: &AppWindow) {
|
||||
String::new()
|
||||
},
|
||||
uppercase: state.get_uppercase(),
|
||||
lite_fill: state.get_lite_fill(),
|
||||
};
|
||||
save(&prefs);
|
||||
}
|
||||
@@ -283,6 +290,7 @@ mod tests {
|
||||
dimming: "max".into(),
|
||||
active_color: "purple".into(),
|
||||
uppercase: true,
|
||||
lite_fill: false,
|
||||
}
|
||||
.sanitized();
|
||||
assert!(!p.auto_follow); // bools pass through
|
||||
@@ -302,6 +310,7 @@ mod tests {
|
||||
dimming: "off".into(),
|
||||
active_color: "#8b5cf6".into(),
|
||||
uppercase: false,
|
||||
lite_fill: true,
|
||||
}
|
||||
.sanitized();
|
||||
assert_eq!(p.font, "line-seed-jp");
|
||||
|
||||
Reference in new issue
Block a user