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 = Lazy::new(text_input::Id::unique); static GENERATE_PROGRESS : Lazy> = 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 } struct SearchState { search : String, index : Index, } impl SearchState { pub async fn search(self) -> Vec { 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) } 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; 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) { ( App::StartMenu, Command::none() ) } fn title(&self) -> String { "Index Search".to_string() } fn update(&mut self, message: Message) -> Command { 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; Command::none() } _ => { Command::none() } } } } } fn view(&self) -> Element { 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 = if results.is_empty() { column![text("There are no results")].spacing(10).into() } else { let lines : Vec> = 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() } } } }