T26c Media: video

Scrubber shows what has loaded, what has played and its chapters, names
the time under the pointer, seeks by press and gpui's drag, and steps by
the slider's keys; over_media lights it for a dark layer. VideoPlayer
shows the host's frame in its shape with a caption and a control bar over
it: the scrubber, play, the time, speed, captions, picture in picture and
full screen, each only with its handler; the bar rests after a still
pointer while it plays, on one wake from the executor's timer. Keys:
Space and K, J and L, the arrows, C and F. VideoThumbnailStrip lays frames
along the length under a playhead with a trim whose handles drag and whose
ends I and O set. SubtitleEditor edits cues in place: times as mm:ss.mmm,
kept in order, a time that does not read kept out with the reason shown.

InlineEdit is a Tab stop with the ring; it hands focus back after Enter or
Escape and keeps its text on Enter's release. overlays::media_button is
shared by the lightbox and the player's bar.
This commit is contained in:
ZacharyZhang-NY committed 2026-09-26 17:07:26 -04:00
1 parent 99b6ccd798
commit d125314d39
22 files changed
+2232 -25

No files matched your search

+55 -3
View File
@@ -1,11 +1,12 @@
use gpui::{
Context, IntoElement, Modifiers, MouseButton, Render, TestAppContext, Window, point, px,
Context, IntoElement, KeyBinding, KeyUpEvent, Keystroke, Modifiers, MouseButton, Render,
SharedString, TestAppContext, VisualTestContext, Window, point, px,
};
use super::setup;
use crate::{
forms::{IconPicker, SignaturePad, Stroke},
primitives::IconName,
forms::{IconPicker, InlineEdit, SignaturePad, Stroke},
primitives::{FocusNext, IconName},
};
struct Icons {
@@ -92,3 +93,54 @@ fn one_move_keeps_both_ends_of_the_stroke(cx: &mut TestAppContext) {
assert_eq!(stroke.len(), 2, "{stroke:?}");
assert_eq!(stroke[1] - stroke[0], point(px(60.0), px(-20.0)));
}
/// An inline edit and the texts it kept.
struct Titled(Vec<SharedString>);
impl Render for Titled {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let view = cx.entity();
let value = self.0.last().cloned().unwrap_or_else(|| "Draft".into());
InlineEdit::new("title", value).on_commit(move |text, _, cx| {
view.update(cx, |view, cx| {
view.0.push(text.clone());
cx.notify();
})
})
}
}
/// Presses and releases `key`, a drawn frame between.
fn release(key: &str, cx: &mut VisualTestContext) {
let redraw = |cx: &mut VisualTestContext| {
cx.run_until_parked();
cx.update(|window, _| window.refresh());
cx.run_until_parked();
};
cx.simulate_keystrokes(key);
redraw(cx);
cx.simulate_event(KeyUpEvent {
keystroke: Keystroke::parse(key).expect("a key"),
});
redraw(cx);
}
#[gpui::test]
fn an_inline_edit_takes_tab_opens_on_enter_and_takes_focus_back(cx: &mut TestAppContext) {
setup(cx);
cx.update(|cx| cx.bind_keys([KeyBinding::new("tab", FocusNext, None)]));
let (view, cx) = cx.add_window_view(|_, _| Titled(Vec::new()));
cx.update(|window, _| window.activate_window());
cx.update(|window, _| window.focus_next());
release("enter", cx);
cx.simulate_input("Stairs");
release("enter", cx);
release("enter", cx);
cx.simulate_input("Light");
release("enter", cx);
assert_eq!(
view.read_with(cx, |view, _| view.0.clone()),
["Stairs", "Light"],
"focus came back after the first"
);
}