Files
Ely-GPUI-Components/examples/gallery/pages/media/edit.rs
T

165 lines
5.4 KiB
Rust

use std::path::PathBuf;
use ely_gpui_component::{
buttons::SegmentedControl,
media::{Crop, ImageAnnotator, ImageCropper, ImageUpload, Mark},
theme::ActiveTheme,
};
use gpui::{App, IntoElement, ParentElement, SharedString, Styled, Window, div, px};
use crate::{
probe::probe,
ui::{keep, picture, section, set},
};
const ATRIUM: &str = asset!("atrium.jpg");
const STAIR: &str = asset!("atrium-stair.jpg");
const DUNES: &str = asset!("dunes.jpg");
/// Crop shapes to pick from, width over height; none is free.
const ASPECTS: [(&str, Option<f32>); 4] = [
("Free", None),
("1:1", Some(1.0)),
("4:3", Some(4.0 / 3.0)),
("16:9", Some(16.0 / 9.0)),
];
pub fn cropper(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
let kept = keep(
"media-crop",
|| {
(
Crop {
x: 0.12,
y: 0.1,
w: 0.7,
h: 0.75,
},
"Free",
)
},
window,
cx,
);
let (crop, shape) = *kept.read(cx);
let aspect = ASPECTS
.iter()
.find(|(name, _)| *name == shape)
.expect("a listed shape")
.1;
let (reshaped, moved) = (kept.clone(), kept.clone());
let shapes = ASPECTS.iter().fold(
SegmentedControl::new("media-crop-shape", shape),
|control, (name, _)| control.segment(*name, *name, None),
);
let readout = format!(
"{:.0}% across, {:.0}% down · {:.0}% by {:.0}%",
crop.x * 100.0,
crop.y * 100.0,
crop.w * 100.0,
crop.h * 100.0
);
section(
"ImageCropper",
"A box to crop a picture by. Drag it to move it and its handles to size it; with a shape, only its corners size it. The arrows move it, Shift further.",
cx,
)
.child(
div()
.w(px(560.))
.flex()
.flex_col()
.gap_3()
.child(shapes.on_change(move |name, _, cx| {
let (crop, _) = *reshaped.read(cx);
let name: &'static str = ASPECTS.iter().find(|(each, _)| *each == name.as_ref()).expect("a listed shape").0;
let aspect = ASPECTS.iter().find(|(each, _)| *each == name).expect("a listed shape").1;
let crop = aspect.map_or(crop, |aspect| crop.fitted(aspect, 1.5));
set(&reshaped, (crop, name), cx);
}))
.child(probe(
"media-cropper",
div().w(px(560.)).child(
ImageCropper::new("media-cropper", picture(ATRIUM), 1.5, crop)
.aspect(aspect)
.on_change(move |crop, _, cx| {
let shape = moved.read(cx).1;
set(&moved, (crop, shape), cx)
}),
),
))
.child(div().text_size(cx.theme().text_size(ely_gpui_component::theme::TextSize::Sm)).text_color(cx.theme().colors.fg_muted).child(readout)),
)
}
pub fn upload(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
let kept = keep(
"media-upload",
|| (Some(PathBuf::from(DUNES)), None::<Crop>),
window,
cx,
);
let (picked, crop) = kept.read(cx).clone();
let (pick, cropped, removed) = (kept.clone(), kept.clone(), kept.clone());
section(
"ImageUpload",
"A picture to send: drop one or browse for it, then crop it. Replace picks another; Remove empties it.",
cx,
)
.child(probe(
"media-upload",
div().w(px(420.)).child(
ImageUpload::new("media-upload", picked, crop)
.aspect(Some(1.0))
.on_pick(move |path, _, cx| set(&pick, (Some(path), None), cx))
.on_crop(move |crop, _, cx| {
let picked = cropped.read(cx).0.clone();
set(&cropped, (picked, Some(crop)), cx)
})
.on_remove(move |_, cx| set(&removed, (None, None), cx)),
),
))
}
pub fn annotator(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
let kept = keep(
"media-marks",
|| {
vec![
Mark::Box {
from: (0.52, 0.18),
to: (0.9, 0.86),
color: 0,
},
Mark::Arrow {
from: (0.2, 0.3),
to: (0.36, 0.52),
color: 2,
},
Mark::Pin {
at: (0.3, 0.72),
color: 1,
},
]
},
window,
cx,
);
let marks = kept.read(cx).clone();
let count = SharedString::from(format!("{} marks", marks.len()));
let changed = kept.clone();
section(
"ImageAnnotator",
"Marks on a picture: boxes, arrows, lines drawn freehand, numbered pins. Pick a tool and a color above; with Select, a press chooses a mark and Delete removes it. Cmd-Z undoes.",
cx,
)
.child(probe(
"media-annotator",
div().w(px(560.)).child(
ImageAnnotator::new("media-annotator", picture(STAIR), 1.5, marks)
.on_change(move |marks, _, cx| set(&changed, marks.to_vec(), cx)),
),
))
.child(div().text_size(cx.theme().text_size(ely_gpui_component::theme::TextSize::Sm)).text_color(cx.theme().colors.fg_muted).child(count))
}