diff --git a/Cargo.lock b/Cargo.lock index e1136fc7..7bf827ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3098,6 +3098,7 @@ dependencies = [ "luminol-components", "luminol-core", "luminol-data", + "luminol-filesystem", ] [[package]] diff --git a/crates/modals/Cargo.toml b/crates/modals/Cargo.toml index c658d78e..ce773265 100644 --- a/crates/modals/Cargo.toml +++ b/crates/modals/Cargo.toml @@ -24,3 +24,4 @@ camino.workspace = true luminol-core.workspace = true luminol-data.workspace = true luminol-components.workspace = true +luminol-filesystem.workspace = true diff --git a/crates/modals/src/graphic_picker.rs b/crates/modals/src/graphic_picker.rs index e52566bc..349ebce0 100644 --- a/crates/modals/src/graphic_picker.rs +++ b/crates/modals/src/graphic_picker.rs @@ -22,16 +22,126 @@ // terms of the Steamworks API by Valve Corporation, the licensors of this // Program grant you additional permission to convey the resulting work. -pub struct Modal {} +use luminol_filesystem::FileSystem; +pub struct Modal { + directory: camino::Utf8PathBuf, + entries: Option>, + selected_graphic: Option, + open: bool, +} + +impl Modal { + pub fn new(directory: impl Into) -> Self { + let directory = directory.into(); + Self { + directory, + entries: None, + selected_graphic: None, + open: false, + } + } +} + +// todo event specific graphic picker? impl luminol_core::Modal for Modal { - type Data = camino::Utf8PathBuf; + type Data = Option; fn button<'m>( &'m mut self, data: &'m mut Self::Data, update_state: &'m mut luminol_core::UpdateState<'_>, ) -> impl egui::Widget + 'm { - |ui: &mut egui::Ui| todo!() + |ui: &mut egui::Ui| { + // this isn't the cleanest but it works + self.entries.get_or_insert_with(|| { + let entries = update_state + .filesystem + .read_dir(&self.directory) + .expect("failed to read directory") + .into_iter() + .map(|m| { + m.path + .strip_prefix(self.directory.as_str()) + .unwrap_or(&m.path) + .to_path_buf() + }) + .collect(); + // check if the selected graphic exists + self.selected_graphic = data.clone().filter(|d| { + update_state + .filesystem + .exists(self.directory.join(d)) + .unwrap() + }); + + entries + }); + + let button_response = ui.button( + data.as_deref() + .map(camino::Utf8Path::as_str) + .unwrap_or("(None)"), + ); + if button_response.clicked() { + self.open = true; + } + self.show_window(ui.ctx(), data); + + button_response + } + } +} + +impl Modal { + fn show_window(&mut self, ctx: &egui::Context, data: &mut Option) { + let mut win_open = self.open; + let mut keep_open = true; + let mut needs_save = false; + + egui::Window::new("Graphic Picker") + .open(&mut win_open) + .show(ctx, |ui| { + let entries = self.entries.as_ref().unwrap(); // entries should be loaded by now + + ui.columns(2, |columns| { + egui::ScrollArea::vertical() + .max_height(364.) + .id_source("graphic_picker_entries") + .show(&mut columns[0], |ui| { + ui.selectable_value(&mut self.selected_graphic, None, "(None)"); + for entry in entries { + let opt_entry = Some(entry.clone()); + ui.selectable_value( + &mut self.selected_graphic, + opt_entry, + entry.as_str(), + ); + } + }); + + if let Some(graphic) = &self.selected_graphic { + let image = + egui::Image::new(format!("project://{}/{graphic}", self.directory)) + .texture_options(egui::TextureOptions::NEAREST) + .maintain_aspect_ratio(true) + .fit_to_original_size(ctx.pixels_per_point()) + .bg_fill(egui::Color32::DARK_GRAY); + egui::ScrollArea::both() + .max_height(364.) + .id_source("graphic_picker_event_display") + .show(&mut columns[1], |ui| ui.add(image)); + } + }); + ui.separator(); + + luminol_components::close_options_ui(ui, &mut keep_open, &mut needs_save); + }); + + if needs_save { + *data = self.selected_graphic.clone(); + } + + self.open = win_open && keep_open; } } diff --git a/crates/ui/src/windows/event_edit.rs b/crates/ui/src/windows/event_edit.rs index a15ea53f..a5d92133 100644 --- a/crates/ui/src/windows/event_edit.rs +++ b/crates/ui/src/windows/event_edit.rs @@ -24,6 +24,7 @@ use luminol_core::Modal; use luminol_data::rpg; +use luminol_modals::{graphic_picker, switch, variable}; /// The event editor window. pub struct Window { @@ -33,9 +34,10 @@ pub struct Window { tab: Tab, event: rpg::Event, - switch_1_modal: luminol_modals::switch::Modal, - switch_2_modal: luminol_modals::switch::Modal, - variable_modal: luminol_modals::variable::Modal, + switch_1_modal: switch::Modal, + switch_2_modal: switch::Modal, + variable_modal: variable::Modal, + graphic_picker: graphic_picker::Modal, } #[derive(strum::Display, strum::EnumIter, PartialEq)] @@ -55,9 +57,10 @@ impl Window { tab: Tab::Commands, event, - switch_1_modal: luminol_modals::switch::Modal::Closed, - switch_2_modal: luminol_modals::switch::Modal::Closed, - variable_modal: luminol_modals::variable::Modal::Closed, + switch_1_modal: switch::Modal::Closed, + switch_2_modal: switch::Modal::Closed, + variable_modal: variable::Modal::Closed, + graphic_picker: graphic_picker::Modal::new("Graphics/Characters"), } } } @@ -177,7 +180,21 @@ impl luminol_core::Window for Window { } Tab::Movement => {} Tab::Commands => {} - Tab::Graphic => {} + Tab::Graphic => { + ui.horizontal(|ui| { + ui.add( + self.graphic_picker + .button(&mut page.graphic.character_name, update_state), + ); + ui.add(luminol_components::EnumMenuButton::new( + &mut page.graphic.blend_type, + )); + ui.add(egui::Slider::new(&mut page.graphic.opacity, 0..=255)); + ui.add(egui::Slider::new(&mut page.graphic.character_hue, 0..=360)); + }); + ui.separator(); + // draw event here + } } ui.separator();