2023-09-24 13:52:24 +02:00
use std ::any ::Any ;
2023-09-23 21:39:20 +02:00
use std ::iter ::Enumerate ;
use egui ::{ widgets , TextStyle } ;
2023-09-24 13:52:24 +02:00
use crate ::metadata ::{ AgeRating , LanguageISO , Metadata } ;
2023-09-23 21:39:20 +02:00
use eframe ::egui ;
use strum ::IntoEnumIterator ;
2023-09-24 13:52:24 +02:00
use crate ::parsers ::{ MetadataSource , Parser } ;
2023-09-23 21:39:20 +02:00
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 > > ,
2023-09-24 13:52:24 +02:00
selected_parser : Box < dyn Parser > ,
2023-09-23 21:39:20 +02:00
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 ( ) ] ,
2023-09-24 13:52:24 +02:00
selected_parser : Box ::new ( parsers ::file ::File ::new ( ) ) ,
2023-09-23 21:39:20 +02:00
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 ) {
2023-09-24 13:52:24 +02:00
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 ( ) ) ,
] ;
2023-09-23 21:39:20 +02:00
// 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 ( " " )
2023-09-24 13:52:24 +02:00
. selected_text ( self . selected_parser . get_display_string ( ) )
2023-09-23 21:39:20 +02:00
. show_ui ( row , | ui | {
2023-09-24 13:52:24 +02:00
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 ;
2023-09-23 21:39:20 +02:00
} ;
}
} ) ;
} ) ;
let file_label ;
2023-09-24 13:52:24 +02:00
if self . selected_parser . source_is_file ( ) {
2023-09-23 21:39:20 +02:00
file_label = " File Path " ;
} else {
file_label = " URL " ;
}
2023-09-24 13:52:24 +02:00
text_input_option ( panel , & mut self . meta_path , file_label , self . selected_parser . source_is_file ( ) ) ;
2023-09-23 21:39:20 +02:00
// 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 ;
}
} ) ;
2023-09-24 13:52:24 +02:00
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 ) ;
} ;
2023-09-23 21:39:20 +02:00
// 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)
2023-09-29 00:21:42 +02:00
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 ) ;
2023-09-23 21:39:20 +02:00
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
2023-09-24 13:52:24 +02:00
self . metadata [ 0 ] . save_to_xml ( " /home/neshura/Repositories/comicinfo-editor/ComicInfo.xml " )
2023-09-23 21:39:20 +02:00
}
} )
} ) ;
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 ( ) ;
}
}
}
} )
}