Lists: a selectable list's cursor starts on the owner's pick; the waveform's fit is tested

SelectableList keeps the owner's last selection and the one it last
sent. A selection the owner makes, on first paint or later, moves the
cursor to its first row; the echo of the list's own pick leaves the
cursor where the pick put it. The playlist's keys start from the song
under way. The waveform's bar count and inset move into `fit`, tested
beside `resample`.
This commit is contained in:
ZacharyZhang-NY committed 2026-09-26 18:44:38 -04:00
1 parent 04f88812dc
commit cb6c78b90c
6 files changed
+102 -5

No files matched your search

+16 -3
View File
@@ -32,6 +32,13 @@ pub(crate) fn resample(peaks: &[f32], count: usize) -> Vec<f32> {
.collect()
}
/// How many bars `bar` wide with `gap` between fit across `width`, at least one, and the inset that centers them.
pub(crate) fn fit(width: f32, bar: f32, gap: f32) -> (usize, f32) {
let count = ((width + gap) / (bar + gap)).floor().max(1.0) as usize;
let used = (bar + gap) * count as f32 - gap;
(count, ((width - used) / 2.0).max(0.0))
}
/// A seeking drag on a waveform, marked with its waveform.
struct Seek(EntityId);
@@ -114,8 +121,7 @@ impl RenderOnce for AudioWaveform {
move |bounds, _, window, _| {
let width = f32::from(bounds.size.width);
let pitch = f32::from(bar + gap);
let count = ((width + f32::from(gap)) / pitch).floor().max(1.0) as usize;
let inset = (width - (pitch * count as f32 - f32::from(gap))) / 2.0;
let (count, inset) = fit(width, f32::from(bar), f32::from(gap));
for (ix, peak) in resample(&peaks, count).into_iter().enumerate() {
let left = inset + pitch * ix as f32;
let tall = (bounds.size.height * peak).max(bar);
@@ -242,7 +248,14 @@ impl RenderOnce for AudioSpectrum {
#[cfg(test)]
mod tests {
use super::resample;
use super::{fit, resample};
#[test]
fn the_bars_that_fit_sit_centered() {
assert_eq!(fit(100.0, 3.0, 2.0), (20, 1.0));
assert_eq!(fit(98.0, 3.0, 2.0), (20, 0.0));
assert_eq!(fit(2.0, 3.0, 2.0), (1, 0.0));
}
#[test]
fn peaks_gather_into_bars_by_their_loudest() {
+36
View File
@@ -304,6 +304,42 @@ fn down_and_enter_play_the_next_song(cx: &mut TestAppContext) {
assert_eq!(view.read_with(cx, |view, _| view.played.clone()), ["b"]);
}
/// Four songs with the third under way, and the songs it was told to play.
struct Midway {
played: Vec<SharedString>,
}
impl Render for Midway {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let owner = cx.entity();
let songs = [
track("a", "One"),
track("b", "Two"),
track("c", "Three"),
track("d", "Four"),
];
div().w(px(320.0)).child(
Playlist::new("list", songs)
.current("c", true)
.on_play(move |key, _, cx| {
owner.update(cx, |view, _| view.played.push(key.clone()))
}),
)
}
}
#[gpui::test]
fn the_keys_start_from_the_song_under_way(cx: &mut TestAppContext) {
setup(cx);
let (view, cx) = cx.add_window_view(|_, _| Midway { played: Vec::new() });
cx.update(|window, _| window.activate_window());
settle(cx);
tab_to(1, cx);
press("down", cx);
press("enter", cx);
assert_eq!(view.read_with(cx, |view, _| view.played.clone()), ["d"]);
}
#[test]
#[should_panic(expected = "no track c")]
fn the_song_under_way_is_one_of_the_list() {