aboutsummaryrefslogtreecommitdiff
path: root/src/gui/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/mod.rs')
-rw-r--r--src/gui/mod.rs264
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()
+ }
+ }
+ }
+}
+