use std::any::Any; use std::iter::Enumerate; use egui::{widgets, TextStyle}; use crate::metadata::{AgeRating, LanguageISO, Metadata}; use eframe::egui; use strum::IntoEnumIterator; use crate::parsers::{MetadataSource, Parser}; mod metadata; mod parsers; const TEXT_SIZE_SMALL: f32 = 1.0; const TEXT_SIZE_MEDIUM: f32 = 1.2; const TEXT_SIZE_LARGE: f32 = 1.4; fn main() -> Result<(), eframe::Error> { env_logger::init(); let options = eframe::NativeOptions { initial_window_size: Some(egui::vec2(1280.0, 720.0)), ..Default::default() }; eframe::run_native( "Metadata Generator", options, Box::new(|cc| { cc.egui_ctx.set_pixels_per_point(TEXT_SIZE_MEDIUM); Box::<Generator>::default() }), ) } struct Generator<'a> { show_all: bool, metadata: Vec<Metadata<'a>>, selected_parser: Box<dyn Parser>, meta_path: Option<String>, bundle: bool, bundle_source: Option<String>, bundle_path: Option<String>, footer_height: f32, } impl Default for Generator<'_> { fn default() -> Self { Self { show_all: false, metadata: vec![Metadata::default()], selected_parser: Box::new(parsers::file::File::new()), meta_path: None, bundle: false, bundle_source: None, bundle_path: None, footer_height: 0.0, } } } impl eframe::App for Generator<'_> { fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { let ParserList: Vec<Box<dyn Parser>> = vec![ Box::new(parsers::fakku::Fakku::new()), Box::new(parsers::file::File::new()), Box::new(parsers::irodori::Irodori::new()), Box::new(parsers::jnovel::JNovel::new()), ]; // Non-Metadata Options egui::SidePanel::right("Options") .resizable(false) .min_width(ctx.screen_rect().width() / 3.0) .max_width(ctx.screen_rect().width() / 3.0) .show(ctx, |panel| { panel.heading("Options"); panel.horizontal(|row| { row.label("Parser: "); egui::containers::ComboBox::from_label("") .selected_text(self.selected_parser.get_display_string()) .show_ui(row, |ui| { for parser in ParserList { let text = parser.get_display_string().clone(); if ui.selectable_value(&mut self.selected_parser.get_display_string(), parser.get_display_string(), text).clicked() { self.selected_parser = parser; }; } }); }); let file_label; if self.selected_parser.source_is_file() { file_label = "File Path"; } else { file_label = "URL"; } text_input_option(panel, &mut self.meta_path, file_label, self.selected_parser.source_is_file()); // TODO this feels wrong, there must be a better way panel.add_space(self.footer_height * 0.1); panel.separator(); panel.add_space(self.footer_height * 0.1); panel.horizontal(|row| { row.label("Perform Bundle: "); row.add(widgets::Checkbox::new(&mut self.bundle, "")); }); if self.bundle { text_input_option(panel, &mut self.bundle_source, "Images Folder", true); text_input_option(panel, &mut self.bundle_path, "Output Path", true); } panel.add_space(self.footer_height * 2.0); panel.horizontal(|elem| { elem.vertical_centered(|centered| { if centered.button("Generate Metadata").clicked() { // Run Metadata Generation // DO NOT SAVE RESULTS YET ONLY POPULATE DATA println!("Ran Generation (not yet)"); } }) }); panel.add_space(panel.available_height() - self.footer_height); self.footer_height = panel .horizontal(|row| { let ppp = ctx.pixels_per_point(); row.label("Text Size:"); if row .add(widgets::RadioButton::new(ppp == TEXT_SIZE_SMALL, "Small")) .clicked() { ctx.set_pixels_per_point(TEXT_SIZE_SMALL); } if row .add(widgets::RadioButton::new(ppp == TEXT_SIZE_MEDIUM, "Medium")) .clicked() { ctx.set_pixels_per_point(TEXT_SIZE_MEDIUM); } if row .add(widgets::RadioButton::new(ppp == TEXT_SIZE_LARGE, "Large")) .clicked() { ctx.set_pixels_per_point(TEXT_SIZE_LARGE); } }) .response .rect .height(); }); // Metadata Options egui::CentralPanel::default().show(ctx, |ui| { ui.heading("Metadata"); // Title & Series text_input(ui, &mut self.metadata[0].series, "Series", false); text_input(ui, &mut self.metadata[0].title, "Title", false); // Volume and Chapter Info ui.horizontal(|row| { row.label("Volume:"); if row.button("-").clicked() { self.metadata[0].volume -= 1; } row.label(format!("{}", self.metadata[0].volume)); if row.button("+").clicked() { self.metadata[0].volume += 1; } row.label("Chapter:"); if row.button("-").clicked() { self.metadata[0].number -= 1; } row.label(format!("{}", self.metadata[0].number)); if row.button("+").clicked() { self.metadata[0].number += 1; } }); ui.horizontal(|row| { row.label("Series Chapter Count:"); if row.button("-").clicked() { self.metadata[0].count -= 1; } row.label(format!("{}", self.metadata[0].count)); if row.button("+").clicked() { self.metadata[0].count += 1; } }); ui.label("Summary Edit:"); let mut summary = self.metadata[0].summary.clone().unwrap_or("".to_string()); if ui.add(egui::TextEdit::multiline(&mut summary)).changed() { self.metadata[0].summary = Some(summary); }; // Date Inputs let glyph_width = ctx .fonts(|f| -> f32 { f.glyph_width(&TextStyle::Body.resolve(ui.style()), '0') }); let mut year_text = self.metadata[0].year.to_string(); ui.horizontal(|row| { row.label("Release Date:"); if row .add( widgets::text_edit::TextEdit::singleline(&mut year_text) .desired_width(5.0 * glyph_width), ) .changed() { let trimmed = year_text.trim(); if trimmed == "0" { self.metadata[0].year = -1; self.metadata[0].month = -1; self.metadata[0].day = -1; } else { self.metadata[0].year = trimmed.parse::<i16>().unwrap_or(self.metadata[0].year); } }; if self.metadata[0].year >= 1970 { let mut month_text = self.metadata[0].month.to_string(); if row .add( widgets::text_edit::TextEdit::singleline(&mut month_text) .desired_width(3.0 * glyph_width), ) .changed() { let trimmed = month_text.trim(); if trimmed == "0" { self.metadata[0].month = -1; self.metadata[0].day = -1; } else { self.metadata[0].month = month_text .trim() .parse::<i8>() .unwrap_or(self.metadata[0].month); } }; if self.metadata[0].month >= 0 { let mut day_text = self.metadata[0].day.to_string(); if row .add( widgets::text_edit::TextEdit::singleline(&mut day_text) .desired_width(3.0 * glyph_width), ) .changed() { let trimmed = day_text.trim(); if trimmed == "0" { self.metadata[0].day = -1; } else { self.metadata[0].day = day_text .trim() .parse::<i8>() .unwrap_or(self.metadata[0].day); } }; } } }); // People involved text_input_option(ui, &mut self.metadata[0].writer, "Author", false); text_input_option(ui, &mut self.metadata[0].letterer, "Typesetter", false); text_input_option(ui, &mut self.metadata[0].editor, "Editor", false); text_input_option(ui, &mut self.metadata[0].translator, "Translator", false); text_input_option(ui, &mut self.metadata[0].publisher, "Publisher", false); /* tags: Vec<String>, characters: Vec<String>, */ // TODO tag list (List of bordered labels with plus that opens a new text edit) ui.horizontal(|row| { row.label("Tags:"); for mut tag in &mut self.metadata[0].tags { text_input(row, &mut tag, "", false); } if row.button("+").clicked() { &self.metadata[0].tags.push("".to_string()); }; }); text_input(ui, &mut self.metadata[0].genre, "Genre", false); ui.horizontal(|row| { row.label("Page Count:"); if row.button("-").clicked() { self.metadata[0].page_count -= 1; } row.label(format!("{}", self.metadata[0].page_count)); if row.button("+").clicked() { self.metadata[0].page_count += 1; } }); //text_input(ui, &mut self.metadata[0].page_count, "Page Count", false); // Lang Drop Down ui.horizontal(|row| { row.label("Language: "); egui::containers::ComboBox::from_label("a") .selected_text(self.metadata[0].language.to_string()) .show_ui(row, |ui| { for language in LanguageISO::iter() { let text = language.to_string().clone(); ui.selectable_value(&mut self.metadata[0].language, language, text); } }); }); // TODO same as with Tags but for characters // Age Rating Drop Down ui.horizontal(|row| { row.label("Age Rating: "); egui::containers::ComboBox::from_label("b") .selected_text(self.metadata[0].age_rating.to_string()) .show_ui(row, |ui| { for rating in AgeRating::iter() { let text = rating.to_string().clone(); ui.selectable_value(&mut self.metadata[0].age_rating, rating, text); } }); }); ui.horizontal(|elem| { elem.vertical_centered(|centered| { if centered.button("Save Metadata").clicked() { // Run Metadata Generation // DO NOT SAVE RESULTS YET ONLY POPULATE DATA self.metadata[0].save_to_xml("/home/neshura/Repositories/comicinfo-editor/ComicInfo.xml") } }) }); if self.show_all { ui.label(format!("Not Implemented yet.")); } }); } } pub(crate) fn text_input_option( ui: &mut egui::Ui, option: &mut Option<String>, label: &str, file_picker: bool, ) -> egui::InnerResponse<()> { let zero_width = 2.0 * ui.fonts(|f| -> f32 { f.glyph_width(&TextStyle::Body.resolve(ui.style()), '0') }); ui.horizontal(|row| { row.label(format!("{}:", label)); if let Some(ref mut text) = option { let dynamic_width = zero_width + text .chars() .map(|c| { row.fonts(|f| -> f32 { f.glyph_width(&TextStyle::Body.resolve(row.style()), c) }) }) .sum::<f32>(); row.add( widgets::text_edit::TextEdit::singleline(text).desired_width( if dynamic_width >= zero_width * 3.0 { dynamic_width } else { zero_width * 3.0 }, ), ); if text == "" { *option = None; } } else { let mut init = "".to_string(); row.add( widgets::text_edit::TextEdit::singleline(&mut init).desired_width(zero_width * 3.0), ); if init != "" { *option = Some(init); } } if file_picker { if row.button("...").clicked() { if let Some(path) = rfd::FileDialog::new().pick_file() { *option = Some(path.display().to_string()); } } } }) } pub(crate) fn text_input( ui: &mut egui::Ui, option: &mut String, label: &str, file_picker: bool, ) -> egui::InnerResponse<()> { let zero_width = 2.0 * ui.fonts(|f| -> f32 { f.glyph_width(&TextStyle::Body.resolve(ui.style()), '0') }); ui.horizontal(|row| { row.label(format!("{}:", label)); let dynamic_width = zero_width + option .chars() .map(|c| { row.fonts(|f| -> f32 { f.glyph_width(&TextStyle::Body.resolve(row.style()), c) }) }) .sum::<f32>(); row.add( widgets::text_edit::TextEdit::singleline(option).desired_width( if dynamic_width >= zero_width * 3.0 { dynamic_width } else { zero_width * 3.0 }, ), ); if file_picker { if row.button("...").clicked() { if let Some(path) = rfd::FileDialog::new().pick_file() { *option = path.display().to_string(); } } } }) }