diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2023-07-07 00:26:54 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2023-07-07 00:26:54 +0200 |
| commit | 179920fa402b81a3ae8cc3c4b415172f71eb8d11 (patch) | |
| tree | 33bd4cb4cceb58ef65ae19a6c8d0f3e4e0ea53ec /src/gui/mod.rs | |
| parent | 9b4aa4e9643b0a5b4a554e455eac269a2472b590 (diff) | |
add gui
Diffstat (limited to 'src/gui/mod.rs')
| -rw-r--r-- | src/gui/mod.rs | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/src/gui/mod.rs b/src/gui/mod.rs new file mode 100644 index 0000000..0c1b9cc --- /dev/null +++ b/src/gui/mod.rs @@ -0,0 +1,264 @@ +use std::io::Write; +use std::thread; +use std::time::Duration; + +use iced::theme::Theme; +use iced::widget::{text, scrollable, container, column, button, text_input, progress_bar}; +use iced::{window, Length}; +use iced::{Application, Element}; +use iced::{Command, Settings}; +use once_cell::sync::Lazy; +use std::sync::Mutex; + +use crate::index::Index; +use crate::searchresult::SearchResult; +use crate::splitter; + +mod easing; +mod circular; + +static SEARCH_ID: Lazy<text_input::Id> = Lazy::new(text_input::Id::unique); +static GENERATE_PROGRESS : Lazy<Mutex<u8>> = Lazy::new(|| { Mutex::new(0) }); + +pub fn run() -> iced::Result { + App::run(Settings { + window: window::Settings { + size: (500, 800), + ..window::Settings::default() + }, + ..Settings::default() + }) +} + +#[derive(Debug)] +enum App { + StartMenu, + Generating(GenState), + Loading, + Search(State), +} + +#[derive(Debug, Default)] +struct GenState { + progress: u8, +} + +#[derive(Debug, Default)] +struct State { + input_value: String, + index : Index, + results : Vec<SearchResult> +} + +struct SearchState { + search : String, + index : Index, +} + +impl SearchState { + pub async fn search(self) -> Vec<SearchResult> { + let search_args = splitter::split_to_words(self.search.clone()); + self.index.search(search_args) + } +} + +#[derive(Clone, Debug)] +enum Message { + Load, + Generate, + Loaded(Index), + GeneratingUpdate(u8), + InputChanged(String), + SearchSubmit, + SearchFinished(Vec<SearchResult>) +} + +async fn load_file() -> Index { + let file = rfd::FileDialog::new().add_filter("index", &["idxs"]) + .set_directory(".") + .set_title("Choose Index File") + .pick_file(); + let file = file.unwrap(); + let file = file.to_str(); + let file = file.unwrap(); + Index::from_file(file) +} + +async fn generate() -> Index { + let file = rfd::FileDialog::new().add_filter("index", &["idxs"]) + .set_directory(".") + .set_title("Choose Index File") + .save_file(); + let file = file.unwrap(); + let file = file.to_str(); + let file = file.unwrap(); + + let input = rfd::FileDialog::new() + .set_directory(".") + .set_title("Choose Directory to Index") + .pick_folder(); + let input = input.unwrap(); + let input = input.to_str(); + let input = input.unwrap(); + Index::generate(input, file, |counter, nof| { + let p = ((counter * 100) / nof) as u8; + *GENERATE_PROGRESS.lock().unwrap() = p; + eprint!("\r\x1b[2K{} of {} files indexed ({}%)", counter, nof, p); + std::io::stdout().flush().ok(); + }) +} + +async fn generate_update_timer() -> u8 { + thread::sleep(Duration::from_millis(100)); + let p; + { + p = *GENERATE_PROGRESS.lock().unwrap(); + } + p +} + +impl Application for App { + type Message = Message; + type Theme = Theme; + type Executor = iced::executor::Default; + type Flags = (); + + fn new(_flags: ()) -> (App, Command<Message>) { + ( + App::StartMenu, + Command::none() + ) + } + + fn title(&self) -> String { + "Index Search".to_string() + } + + fn update(&mut self, message: Message) -> Command<Message> { + match self { + App::StartMenu => { + match message { + Message::Load => { + *self = App::Loading; + Command::batch(vec![ + Command::perform(load_file(), Message::Loaded) + ]) + } + Message::Generate => { + *self = App::Generating(Default::default()); + Command::batch(vec![ + Command::perform(generate(), Message::Loaded), + Command::perform(generate_update_timer(), Message::GeneratingUpdate) + ]) + } + _ => { + Command::none() + } + } + } + App::Loading => { + match message { + Message::Loaded(index) => { + *self = App::Search(State { index, ..Default::default() }); + Command::none() + } + _ => { + Command::none() + } + } + } + App::Generating(state) => { + match message { + Message::Loaded(index) => { + *self = App::Search(State { index, ..Default::default() }); + Command::none() + } + Message::GeneratingUpdate(p) => { + state.progress = p; + Command::perform(generate_update_timer(), Message::GeneratingUpdate) + } + _ => { + Command::none() + } + } + } + App::Search(state) => { + match message { + Message::InputChanged(value) => { + state.input_value = value; + Command::none() + } + Message::SearchSubmit => { + Command::perform( + SearchState { + search: state.input_value.clone(), + index : state.index.clone() + }.search(), + Message::SearchFinished + ) + } + Message::SearchFinished(results) => { + state.results = results.clone(); + Command::none() + } + _ => { + Command::none() + } + } + } + } + } + + fn view(&self) -> Element<Message> { + let container = container(match self { + App::StartMenu => { + column![ + button("Open").on_press(Message::Load), + button("Generate").on_press(Message::Generate), + ].spacing(10).align_items(iced::Alignment::Center) + } + App::Loading => { + column![ + text("Loading"), + circular::Circular::new() + ].spacing(10).align_items(iced::Alignment::Center) + } + App::Generating(state) => { + column![ + text("Generating"), + progress_bar(0.0..=100.0, state.progress as f32) + ].spacing(10).align_items(iced::Alignment::Center) + } + App::Search(State { input_value, results, ..}) => { + let res : Element<Message> = if results.is_empty() { + column![text("There are no results")].spacing(10).into() + } else { + let lines : Vec<Element<_>> = results.iter().map(|r| text(r.path.clone()).into()).collect(); + column(lines).spacing(10).into() + }; + + column![ + text_input("Search", input_value) + .id(SEARCH_ID.clone()) + .on_submit(Message::SearchSubmit) + .on_input(Message::InputChanged) + .padding(5), + scrollable(res).width(Length::Fill) + ].spacing(10) + } + }).center_x() + .padding(10) + .width(Length::Fill) + .height(Length::Fill); + + match self { + App::Search(_) => { + container.into() + } + _ => { + container.center_y().into() + } + } + } +} + |