447 lines
16 KiB
Rust
447 lines
16 KiB
Rust
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();
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|