T26a Media: images
A new chapter, src/media. ImageViewer fits a picture to its stage and zooms by the document steps, by keys, buttons and Cmd-scroll about the pointer; a drag through gpui's drag, a scroll or the arrows pan it once it overhangs; R turns it a quarter at a time. gpui paints pictures without a transform, so media::turn turns the decoded frames off the main thread, an asset keyed by the picture and the turn; `image` 0.25, which gpui already decodes with, is now a dependency. ImageThumbnail shows a picture in its own shape with a corner label and a ring when chosen, and is a Tab stop only with a handler. Lightbox / Gallery Viewer points to overlays::Lightbox, which the gallery opens from the thumbnails. T26 splits into T26a–e. AGENTS.md: the drag rule says drags that pan, turn or slide go through gpui's drag; pictures turn by their pixels.
This commit is contained in:
1 parent
14f67f8429
commit
9ed801b67d
14 files changed
+1171
-7
No files matched your search
@@ -0,0 +1,118 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use ely_gpui_component::{
|
||||
media::{ImageThumbnail, ImageViewer},
|
||||
overlays::{Lightbox, Slide},
|
||||
};
|
||||
use gpui::{App, IntoElement, ParentElement, SharedString, Styled, Window, div, px};
|
||||
|
||||
use crate::{
|
||||
probe::probe,
|
||||
ui::{keep, row, section, set},
|
||||
};
|
||||
|
||||
/// The gallery's pictures: a file, its width over height, a caption, and a corner label.
|
||||
const PICTURES: [(&str, f32, &str, &str); 5] = [
|
||||
(
|
||||
concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/examples/gallery/assets/atrium.jpg"
|
||||
),
|
||||
1.5,
|
||||
"An atrium, noon",
|
||||
"",
|
||||
),
|
||||
(
|
||||
concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/examples/gallery/assets/dunes.jpg"
|
||||
),
|
||||
1.5,
|
||||
"Dunes at first light",
|
||||
"",
|
||||
),
|
||||
(
|
||||
concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/examples/gallery/assets/atrium-olive.jpg"
|
||||
),
|
||||
1.5,
|
||||
"An olive tree by the stair",
|
||||
"HDR",
|
||||
),
|
||||
(
|
||||
concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/examples/gallery/assets/dunes-grass.jpg"
|
||||
),
|
||||
1.5,
|
||||
"Grass on the ridge",
|
||||
"",
|
||||
),
|
||||
(
|
||||
concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/examples/gallery/assets/dunes-square.jpg"
|
||||
),
|
||||
1.0,
|
||||
"The long shadow",
|
||||
"+2",
|
||||
),
|
||||
];
|
||||
|
||||
pub fn viewer(cx: &mut App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"ImageViewer",
|
||||
"A picture to look at closely. It fits at first; the buttons, plus and minus, or Cmd-scroll zoom it, and 0 and 1 go back to fit and actual size, as a double press does. Drag it or use the arrows once it overhangs; R turns it.",
|
||||
cx,
|
||||
)
|
||||
.child(probe(
|
||||
"media-viewer",
|
||||
div().w(px(640.)).h(px(440.)).child(ImageViewer::new("media-viewer", PathBuf::from(PICTURES[0].0))),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn thumbnails(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let shown = keep("media-shown", || None::<usize>, window, cx);
|
||||
let opened = keep("media-last", || 0usize, window, cx);
|
||||
let last = *opened.read(cx);
|
||||
let thumbs = PICTURES
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(ix, (path, ratio, _, label))| {
|
||||
let (open, mark) = (shown.clone(), opened.clone());
|
||||
let thumb = ImageThumbnail::new(
|
||||
SharedString::from(format!("media-thumb-{ix}")),
|
||||
Path::new(*path),
|
||||
*ratio,
|
||||
)
|
||||
.selected(ix == last)
|
||||
.on_click(move |_, cx| {
|
||||
set(&mark, ix, cx);
|
||||
set(&open, Some(ix), cx);
|
||||
});
|
||||
let thumb = if label.is_empty() {
|
||||
thumb
|
||||
} else {
|
||||
thumb.label(*label)
|
||||
};
|
||||
div().w(px(96. * ratio + 6.)).child(thumb)
|
||||
});
|
||||
let overlay = (*shown.read(cx)).map(|at| {
|
||||
let (step, close) = (shown.clone(), shown.clone());
|
||||
Lightbox::new(
|
||||
"media-lightbox",
|
||||
PICTURES.map(|(path, _, caption, _)| Slide::new(Path::new(path), caption)),
|
||||
at,
|
||||
move |_, cx| set(&close, None, cx),
|
||||
)
|
||||
.on_step(move |to, _, cx| set(&step, Some(to), cx))
|
||||
});
|
||||
section(
|
||||
"ImageThumbnail / Lightbox → overlays::Lightbox",
|
||||
"Pictures small, each in its own shape, with a label when one helps. The one last opened keeps a ring; a press or Enter opens the lightbox, which steps through them all.",
|
||||
cx,
|
||||
)
|
||||
.child(probe("media-thumbs", row().items_end().children(thumbs)))
|
||||
.children(overlay)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
mod images;
|
||||
|
||||
use gpui::{AnyElement, App, IntoElement, ParentElement, Window, div};
|
||||
|
||||
use super::Page;
|
||||
use crate::script::Step;
|
||||
|
||||
pub const PAGE: Page = Page {
|
||||
number: 26,
|
||||
slug: "media",
|
||||
title: "Media",
|
||||
summary: "Pictures, video and sound: looking closely, cropping and marking up, playing, and the controls and devices around them.",
|
||||
render,
|
||||
script: SCRIPT,
|
||||
};
|
||||
|
||||
const SCRIPT: &[Step] = &[
|
||||
Step::DownAt("media-viewer", 320.0, 240.0),
|
||||
Step::UpAt("media-viewer", 320.0, 240.0),
|
||||
Step::Key("="),
|
||||
Step::Key("="),
|
||||
Step::Wait(300),
|
||||
Step::Shot("zoomed"),
|
||||
Step::Key("r"),
|
||||
Step::Wait(500),
|
||||
Step::Shot("turned"),
|
||||
Step::DownAt("media-thumbs", 237.0, 50.0),
|
||||
Step::UpAt("media-thumbs", 237.0, 50.0),
|
||||
Step::Wait(500),
|
||||
Step::Shot("lightbox"),
|
||||
Step::Key("escape"),
|
||||
Step::Wait(300),
|
||||
];
|
||||
|
||||
fn render(window: &mut Window, cx: &mut App) -> AnyElement {
|
||||
div()
|
||||
.child(images::viewer(cx))
|
||||
.child(images::thumbnails(window, cx))
|
||||
.into_any_element()
|
||||
}
|
||||
@@ -14,6 +14,7 @@ mod generative;
|
||||
mod git;
|
||||
mod layout;
|
||||
mod lists;
|
||||
mod media;
|
||||
mod menus;
|
||||
mod motion;
|
||||
mod navigation;
|
||||
@@ -65,6 +66,7 @@ pub const ALL: &[Page] = &[
|
||||
chat::PAGE,
|
||||
agent::PAGE,
|
||||
generative::PAGE,
|
||||
media::PAGE,
|
||||
theme::PAGE,
|
||||
];
|
||||
|
||||
|
||||
Reference in new issue
Block a user