ui/dialogs/
open_project.rs1use crate::dialogs::RenderableDialog;
3use egui::{Context, TextEdit, Window};
4use egui_form::garde::{GardeReport, field_path};
5use egui_form::{Form, FormField};
6use garde::Validate;
7use std::path::PathBuf;
8
9#[derive(Default, Debug, Validate)]
11pub struct OpenProject {
12 #[garde(ascii, length(min = 3, max = 256), custom(path_exists))]
14 pub path: String,
15}
16
17#[allow(
22 clippy::ref_option,
23 reason = "Required by Garde's API to pass a reference"
24)]
25#[allow(
26 clippy::trivially_copy_pass_by_ref,
27 reason = "Required by Garde's API to pass a reference"
28)]
29fn path_exists(path: &String, _context: &&&()) -> garde::Result {
30 let path = PathBuf::from(path);
31
32 if !path.exists() {
33 return Err(garde::Error::new("Selected file does not exist"));
34 } else if path.is_dir() {
35 return Err(garde::Error::new(
36 "Folders are (currently) not supported. Please select a file instead.",
37 ));
38 }
39
40 Ok(())
41}
42
43impl RenderableDialog for OpenProject {
44 fn render(&mut self, context: &mut Context) -> bool {
45 let validation = self.validate();
46 let is_valid = validation.is_ok();
47 let mut form = Form::new().add_report(GardeReport::new(validation));
48
49 let mut keep_open = true;
50 let mut keep_open_inner = true;
52
53 Window::new("Open Project")
54 .open(&mut keep_open)
55 .show(context, |ui| {
56 ui.horizontal(|ui| {
57 FormField::new(&mut form, field_path!("path"))
58 .label("Location")
59 .ui(ui, TextEdit::singleline(&mut self.path));
60
61 if ui.button("…").clicked() {
62 self.path = rfd::FileDialog::new()
63 .pick_file()
65 .map_or_else(String::new, |path| path.to_string_lossy().to_string());
66 }
67 });
68
69 ui.add_enabled_ui(is_valid, |ui| {
70 if ui.button("Open").clicked() {
71 keep_open_inner = false;
72 }
73 });
74 });
75
76 keep_open && keep_open_inner
77 }
78}