Files · Items: one icon map, file icon, file row and file grid
The crate had two maps from a file's name to its icon, in chat and in the file tree, and they disagreed. One map now lives in primitives, over the Lucide file-* icons, and chat, agent, the file tree and the directory listing use it. FileIcon draws it for a file or a folder. FileItem is a file's row, a ListItem with its size and when it changed. FileList is the directory listing. FileGrid lays tiles in the order given, with a cursor seeded from the owner's pick: arrows by tile and by row, Home, End, Enter and a double press to open. The count of what fits a width has one home, layout's columns_for, and fit builds on it.
This commit is contained in:
1 parent
e75171c995
commit
a846e10847
36 files changed
+1024
-77
No files matched your search
@@ -0,0 +1,169 @@
|
||||
use ely_gpui_component::{
|
||||
files::{FileGrid, FileIcon, FileItem},
|
||||
lists::{DirEntry, DirectoryListing, SelectableList},
|
||||
theme::{ActiveTheme, IconSize, TextSize},
|
||||
};
|
||||
use gpui::{AnyElement, App, IntoElement, ParentElement, SharedString, Styled, Window, div, px};
|
||||
use jiff::{Timestamp, ToSpan};
|
||||
|
||||
use super::Page;
|
||||
use crate::{
|
||||
probe::probe,
|
||||
script::Step,
|
||||
ui::{change, keep, section},
|
||||
};
|
||||
|
||||
pub const PAGE: Page = Page {
|
||||
number: 27,
|
||||
slug: "files",
|
||||
title: "Files",
|
||||
summary: "Files and folders: their icons, rows and tiles, and the listings, searches and transfers around them.",
|
||||
render,
|
||||
script: SCRIPT,
|
||||
};
|
||||
|
||||
const SCRIPT: &[Step] = &[
|
||||
Step::DownAt("files-grid", 150.0, 40.0),
|
||||
Step::UpAt("files-grid", 150.0, 40.0),
|
||||
Step::Key("right"),
|
||||
Step::Key("down"),
|
||||
Step::Wait(200),
|
||||
Step::Shot("grid-picked"),
|
||||
];
|
||||
|
||||
/// A project's entries, changed a few hours to a few weeks ago.
|
||||
fn entries() -> Vec<DirEntry> {
|
||||
let now = Timestamp::now();
|
||||
let ago = |hours: i64| now - hours.hours();
|
||||
vec![
|
||||
DirEntry::folder("Design", ago(3)),
|
||||
DirEntry::folder("Recordings", ago(30)),
|
||||
DirEntry::file("Brief.md", 4_800, ago(2)),
|
||||
DirEntry::file("Budget.csv", 18_300, ago(26)),
|
||||
DirEntry::file("Atrium.jpg", 2_140_000, ago(50)),
|
||||
DirEntry::file("Walkthrough.mov", 184_000_000, ago(120)),
|
||||
DirEntry::file("main.rs", 9_200, ago(1)),
|
||||
DirEntry::file("Archive.zip", 48_000_000, ago(700)),
|
||||
]
|
||||
}
|
||||
|
||||
fn icons(cx: &mut App) -> impl IntoElement + use<> {
|
||||
let theme = cx.theme();
|
||||
let (small, muted) = (theme.text_size(TextSize::Xs), theme.colors.fg_muted);
|
||||
let names = [
|
||||
"Brief.md",
|
||||
"main.rs",
|
||||
"build.sh",
|
||||
"config.toml",
|
||||
"data.json",
|
||||
"Atrium.jpg",
|
||||
"Theme.mp3",
|
||||
"Clip.mov",
|
||||
"Archive.zip",
|
||||
"Budget.csv",
|
||||
"Inter.ttf",
|
||||
"Makefile",
|
||||
];
|
||||
let tile = |icon: FileIcon, name: &'static str| {
|
||||
div()
|
||||
.w(px(88.))
|
||||
.flex()
|
||||
.flex_col()
|
||||
.items_center()
|
||||
.gap_1p5()
|
||||
.child(icon.size(IconSize::Xl))
|
||||
.child(div().text_size(small).text_color(muted).child(name))
|
||||
};
|
||||
section(
|
||||
"FileIcon",
|
||||
"A file's icon by its name's extension: code, text, pictures, sound, video, archives, tables, fonts and scripts; a folder shut or open.",
|
||||
cx,
|
||||
)
|
||||
.child(probe(
|
||||
"files-icons",
|
||||
div()
|
||||
.w(px(640.))
|
||||
.flex()
|
||||
.flex_wrap()
|
||||
.gap_y_4()
|
||||
.child(tile(FileIcon::folder(false), "Folder"))
|
||||
.child(tile(FileIcon::folder(true), "Open"))
|
||||
.children(names.map(|name| tile(FileIcon::file(name), name))),
|
||||
))
|
||||
}
|
||||
|
||||
fn items(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let picked = keep("files-items", || SharedString::from("Brief.md"), window, cx);
|
||||
let now = picked.read(cx).clone();
|
||||
let pick = picked.clone();
|
||||
let list =
|
||||
entries()
|
||||
.into_iter()
|
||||
.take(5)
|
||||
.fold(SelectableList::new("files-items"), |list, entry| {
|
||||
let key = entry.name().clone();
|
||||
list.row(
|
||||
key.clone(),
|
||||
FileItem::new(SharedString::from(format!("files-item-{key}")), entry).into(),
|
||||
)
|
||||
});
|
||||
section(
|
||||
"FileItem",
|
||||
"A file or folder as a row: its icon and name over its size and when it changed.",
|
||||
cx,
|
||||
)
|
||||
.child(probe(
|
||||
"files-items",
|
||||
div()
|
||||
.w(px(420.))
|
||||
.child(list.selected([now]).on_change(move |keys, _, cx| {
|
||||
if let Some(key) = keys.first() {
|
||||
change(&pick, cx, |now| *now = key.clone())
|
||||
}
|
||||
})),
|
||||
))
|
||||
}
|
||||
|
||||
fn listing(cx: &mut App) -> impl IntoElement + use<> {
|
||||
section(
|
||||
"FileList",
|
||||
"A folder's entries in columns, as lists::DirectoryListing draws them: sort by a column, climb the path, open with Enter or a double press.",
|
||||
cx,
|
||||
)
|
||||
.child(probe(
|
||||
"files-listing",
|
||||
div().w(px(640.)).h(px(360.)).child(
|
||||
DirectoryListing::new("files-listing", ["Home", "Projects", "Atrium"], entries())
|
||||
.on_open(|entry, _, _| log::info!("gallery: open {}", entry.name()))
|
||||
.on_climb(|level, _, _| log::info!("gallery: climb to {level}")),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn grid(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
|
||||
let picked = keep("files-grid", || None::<SharedString>, window, cx);
|
||||
let now = picked.read(cx).clone();
|
||||
let pick = picked.clone();
|
||||
let grid = FileGrid::new("files-grid", entries())
|
||||
.on_select(move |name, _, cx| change(&pick, cx, |now| *now = Some(name.clone())))
|
||||
.on_open(|entry, _, _| log::info!("gallery: open {}", entry.name()));
|
||||
let grid = match now {
|
||||
Some(name) => grid.selected(name),
|
||||
None => grid,
|
||||
};
|
||||
section(
|
||||
"FileGrid",
|
||||
"Files and folders as tiles. A press picks one and a double press opens it; with focus the arrows move by tile and by row, and Enter opens.",
|
||||
cx,
|
||||
)
|
||||
.child(probe("files-grid", div().w(px(640.)).child(grid)))
|
||||
}
|
||||
|
||||
fn render(window: &mut Window, cx: &mut App) -> AnyElement {
|
||||
div()
|
||||
.child(icons(cx))
|
||||
.child(items(window, cx))
|
||||
.child(listing(cx))
|
||||
.child(grid(window, cx))
|
||||
.into_any_element()
|
||||
}
|
||||
@@ -8,6 +8,7 @@ mod debug;
|
||||
mod documents;
|
||||
mod editor;
|
||||
mod feedback;
|
||||
mod files;
|
||||
mod finance;
|
||||
mod forms;
|
||||
mod generative;
|
||||
@@ -67,6 +68,7 @@ pub const ALL: &[Page] = &[
|
||||
agent::PAGE,
|
||||
generative::PAGE,
|
||||
media::PAGE,
|
||||
files::PAGE,
|
||||
theme::PAGE,
|
||||
];
|
||||
|
||||
|
||||
Reference in new issue
Block a user