launcher/widgets/
gallery.rs1use eframe::egui;
2use std::collections::HashMap;
3use std::path::PathBuf;
4
5#[derive(serde::Serialize, serde::Deserialize)]
6#[serde(default)]
7pub struct GalleryState {
8 pub tile_size: f32,
9 #[serde(skip)]
10 pub selected_item: Option<PathBuf>,
11 #[serde(skip)]
12 pub textures: HashMap<String, egui::TextureHandle>,
13}
14
15impl Default for GalleryState {
16 fn default() -> Self {
17 Self {
18 tile_size: 205.0,
19 selected_item: None,
20 textures: HashMap::new(),
21 }
22 }
23}
24
25pub struct GalleryConfig<'a> {
26 pub id: &'a str,
27 pub title: &'a str,
28 pub info_text: Option<&'a str>,
29 pub open_folder_path: Option<&'a PathBuf>,
30}
31
32pub fn gallery_grid<T, F>(
33 ui: &mut egui::Ui,
34 config: GalleryConfig<'_>,
35 state: &mut GalleryState,
36 items: &[T],
37 header_extra: impl FnOnce(&mut egui::Ui),
38 mut draw_tile: F,
39) where
40 F: FnMut(&mut egui::Ui, &T, f32, &mut GalleryState),
41{
42 ui.horizontal(|ui| {
43 ui.heading(config.title);
44 if let Some(info) = config.info_text {
45 ui.label(egui::RichText::new("ℹ").size(16.0))
46 .on_hover_text(info);
47 }
48 if let Some(path) = config.open_folder_path
49 && ui
50 .button(egui::RichText::new("📂").size(16.0))
51 .on_hover_text("Open directory")
52 .clicked()
53 && let Err(e) = open::that(path)
54 {
55 eprintln!("Failed to open directory {}: {e}", path.display());
56 }
57 ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
58 ui.add_space(8.0);
59 ui.add(egui::Slider::new(&mut state.tile_size, 192.0..=512.0).show_value(false));
60 ui.label("Tile size:");
61 header_extra(ui);
62 });
63 });
64
65 ui.add_space(10.0);
66
67 egui::ScrollArea::vertical()
68 .auto_shrink([false, false])
69 .show(ui, |ui| {
70 ui.add_space(10.0);
71
72 let tile_spacing = 16.0;
74 let available_width = ui.available_width();
75
76 let column_width = state.tile_size + tile_spacing;
77 let num_columns = ((available_width / column_width).floor()) as usize;
78 let num_columns = num_columns.max(1);
79
80 egui::Grid::new(config.id)
81 .spacing(egui::vec2(tile_spacing, tile_spacing))
82 .min_col_width(state.tile_size)
83 .show(ui, |ui| {
84 for (i, item) in items.iter().enumerate() {
85 if i > 0 && i % num_columns == 0 {
86 ui.end_row();
87 }
88 draw_tile(ui, item, state.tile_size, state);
89 }
90 });
91 });
92}