improved search and focus
This commit is contained in:
		
							
								
								
									
										105
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -1,7 +1,6 @@
 | 
				
			|||||||
#![feature(iter_map_windows)]
 | 
					#![feature(iter_map_windows)]
 | 
				
			||||||
use iced::{
 | 
					use iced::{
 | 
				
			||||||
    Element,
 | 
					    clipboard, widget::{self, column, combo_box, row, text_input}, Element, Task
 | 
				
			||||||
    widget::{self, column, combo_box},
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use std::{env, fs, path::PathBuf};
 | 
					use std::{env, fs, path::PathBuf};
 | 
				
			||||||
use texts::*;
 | 
					use texts::*;
 | 
				
			||||||
@@ -11,6 +10,7 @@ mod texts;
 | 
				
			|||||||
pub const BIBLE_DIRECTORY: &str = "./Holy-Bible-XML-Format";
 | 
					pub const BIBLE_DIRECTORY: &str = "./Holy-Bible-XML-Format";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() -> iced::Result {
 | 
					fn main() -> iced::Result {
 | 
				
			||||||
 | 
					    // CLI
 | 
				
			||||||
    let arg = env::args().collect::<Vec<String>>();
 | 
					    let arg = env::args().collect::<Vec<String>>();
 | 
				
			||||||
    if let Some(query) = arg.get(1) {
 | 
					    if let Some(query) = arg.get(1) {
 | 
				
			||||||
        if query.starts_with("H") | query.starts_with("h") {
 | 
					        if query.starts_with("H") | query.starts_with("h") {
 | 
				
			||||||
@@ -20,33 +20,38 @@ fn main() -> iced::Result {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if let Some(second_query) = arg.get(2) {
 | 
					        if let Some(second_query) = arg.get(2) {
 | 
				
			||||||
            if let Some(f) = bible::get(
 | 
					            // if let Some(f) = bible::get( query, second_query, vec![format!("{BIBLE_DIRECTORY}/EnglishNASBBible.xml").into()],
 | 
				
			||||||
                query,
 | 
					            // ) {
 | 
				
			||||||
                second_query,
 | 
					            //   println!("{}", f);
 | 
				
			||||||
                vec![format!("{BIBLE_DIRECTORY}/EnglishNASBBible.xml").into()],
 | 
					            // }
 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                println!("{}", f);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return Ok(())
 | 
					            return Ok(())
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // GUI
 | 
				
			||||||
    iced::run("Bible Search", State::update, State::view)
 | 
					    iced::run("Bible Search", State::update, State::view)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
enum Message {
 | 
					enum Message {
 | 
				
			||||||
    InputChange(String),
 | 
					    BibleSearchInput(String),
 | 
				
			||||||
    BibleSelected(String),
 | 
					    BibleSelected(String),
 | 
				
			||||||
    SubmitQuery,
 | 
					    BibleSearchSubmit,
 | 
				
			||||||
 | 
					    CopyText,
 | 
				
			||||||
 | 
					    Clear,
 | 
				
			||||||
 | 
					    WordSearchInput(String),
 | 
				
			||||||
 | 
					    SubmitWordSearch,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct State {
 | 
					struct State {
 | 
				
			||||||
    files: combo_box::State<String>,
 | 
					    files: combo_box::State<String>,
 | 
				
			||||||
    file_selected: Option<String>,
 | 
					    selected_file: Option<String>,
 | 
				
			||||||
 | 
					    bible_selected: Option<bible::Bible>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    query: String,
 | 
					    bible_search: String,
 | 
				
			||||||
    body: Option<String>,
 | 
					    body: Option<String>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    word_search: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
impl Default for State {
 | 
					impl Default for State {
 | 
				
			||||||
    fn default() -> Self {
 | 
					    fn default() -> Self {
 | 
				
			||||||
@@ -74,55 +79,88 @@ impl Default for State {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            files: combo_box::State::new(files),
 | 
					            files: combo_box::State::new(files),
 | 
				
			||||||
            file_selected: None,
 | 
					            selected_file: None,
 | 
				
			||||||
 | 
					            bible_selected: None,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            query: String::new(),
 | 
					            bible_search: String::new(),
 | 
				
			||||||
            body: None,
 | 
					            body: None,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            word_search: String::new(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl State {
 | 
					impl State {
 | 
				
			||||||
    fn update(&mut self, msg: Message) {
 | 
					    fn update(&mut self, msg: Message) -> Task<Message> {
 | 
				
			||||||
        match msg {
 | 
					        match msg {
 | 
				
			||||||
            Message::BibleSelected(file) => {
 | 
					            Message::BibleSelected(file) => {
 | 
				
			||||||
                self.file_selected = Some(file);
 | 
					                if let Ok(contents) = fs::read_to_string(&file) {
 | 
				
			||||||
 | 
					                    if let Ok(bible) = quick_xml::de::from_str::<bible::Bible>(&contents) {
 | 
				
			||||||
 | 
					                        // State is held technically in this variable, although
 | 
				
			||||||
 | 
					                        // the Bible is held in memory in it's own variable.
 | 
				
			||||||
 | 
					                        self.selected_file = Some(format!("{bible}"));
 | 
				
			||||||
 | 
					                        self.bible_selected = Some(bible);
 | 
				
			||||||
 | 
					                        // automatically focus the search bar when done here
 | 
				
			||||||
 | 
					                        return text_input::focus("bible-search");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Message::InputChange(query) => {
 | 
					            Message::BibleSearchInput(query) => self.bible_search = query,
 | 
				
			||||||
                // update self's state
 | 
					            Message::WordSearchInput(query) => self.word_search = query,
 | 
				
			||||||
                self.query = query;
 | 
					            Message::BibleSearchSubmit => {
 | 
				
			||||||
            }
 | 
					                let mut splits = self.bible_search.split_whitespace();
 | 
				
			||||||
            Message::SubmitQuery => {
 | 
					 | 
				
			||||||
                let mut splits = self.query.split_whitespace();
 | 
					 | 
				
			||||||
                let book = splits.next();
 | 
					                let book = splits.next();
 | 
				
			||||||
                let location = splits.next();
 | 
					                let location = splits.next();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if let (Some(book), Some(chap_and_ver), Some(file)) =
 | 
					                if let (Some(book), Some(chap_and_ver), Some(bible)) =
 | 
				
			||||||
                    (book, location, &self.file_selected)
 | 
					                    (book, location, &self.bible_selected)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    self.body = bible::get(book, chap_and_ver, vec![file.into()]);
 | 
					                    self.body = bible::get(book, chap_and_ver, vec![bible]);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					            Message::SubmitWordSearch => {
 | 
				
			||||||
 | 
					                println!("todo: search for {}", self.word_search);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Message::CopyText => {
 | 
				
			||||||
 | 
					                if let Some(text) = &self.body {
 | 
				
			||||||
 | 
					                    return clipboard::write::<Message>(parse(text));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Message::Clear => {
 | 
				
			||||||
 | 
					                self.body = None;
 | 
				
			||||||
 | 
					                self.bible_search = String::new();
 | 
				
			||||||
 | 
					                return text_input::focus("bible-search");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Task::none()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn view(&self) -> Element<Message> {
 | 
					    fn view(&self) -> Element<Message> {
 | 
				
			||||||
        let msg = if let Some(body) = self.body.clone() {
 | 
					        let msg = if let Some(body) = self.body.clone() {
 | 
				
			||||||
            parse(&body)
 | 
					            parse(&body)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            "None".to_string()
 | 
					            String::new()
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        column![
 | 
					        column![
 | 
				
			||||||
            widget::combo_box(
 | 
					            widget::combo_box(
 | 
				
			||||||
                &self.files,
 | 
					                &self.files,
 | 
				
			||||||
                "Select Bible",
 | 
					                "Select Bible",
 | 
				
			||||||
                self.file_selected.as_ref(),
 | 
					                self.selected_file.as_ref(),
 | 
				
			||||||
                Message::BibleSelected
 | 
					                Message::BibleSelected
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            widget::text_input("Search query, ie: Gen 1:1", &self.query)
 | 
					            widget::text_input("Search query, ie: Gen 1:1", &self.bible_search)
 | 
				
			||||||
                .on_input(Message::InputChange)
 | 
					                .on_input(Message::BibleSearchInput)
 | 
				
			||||||
                .on_submit(Message::SubmitQuery),
 | 
					                .on_submit(Message::BibleSearchSubmit)
 | 
				
			||||||
 | 
					                .id("bible-search"),
 | 
				
			||||||
 | 
					            widget::text_input("Word Search", &self.word_search)
 | 
				
			||||||
 | 
					                .on_input(Message::WordSearchInput)
 | 
				
			||||||
 | 
					                .on_submit(Message::SubmitWordSearch)
 | 
				
			||||||
 | 
					                .id("word-search"),
 | 
				
			||||||
 | 
					            row![
 | 
				
			||||||
 | 
					                widget::button("Copy").on_press(Message::CopyText),
 | 
				
			||||||
 | 
					                widget::button("Clear").on_press(Message::Clear),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
            widget::text(msg),
 | 
					            widget::text(msg),
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
        .into()
 | 
					        .into()
 | 
				
			||||||
@@ -135,7 +173,7 @@ fn parse(input: &str) -> String {
 | 
				
			|||||||
        .bytes()
 | 
					        .bytes()
 | 
				
			||||||
        .into_iter()
 | 
					        .into_iter()
 | 
				
			||||||
        .enumerate()
 | 
					        .enumerate()
 | 
				
			||||||
        .map_windows(|[(ai, a), (bi, b)]| {
 | 
					        .map_windows(|[(ai, a), (_, b)]| {
 | 
				
			||||||
            // 27 91 is the start sequence for a color/style
 | 
					            // 27 91 is the start sequence for a color/style
 | 
				
			||||||
            if *a == 27 && *b == 91 {
 | 
					            if *a == 27 && *b == 91 {
 | 
				
			||||||
                Some(*ai)
 | 
					                Some(*ai)
 | 
				
			||||||
@@ -208,3 +246,4 @@ fn parse(input: &str) -> String {
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    modified
 | 
					    modified
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
use inline_colorization::*;
 | 
					use inline_colorization::*;
 | 
				
			||||||
use std::{fmt::Display, fs, path::PathBuf};
 | 
					use std::fmt::Display;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use quick_xml::de::from_str;
 | 
					 | 
				
			||||||
use serde::Deserialize;
 | 
					use serde::Deserialize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const BOOKS_IN_ORDER: [&str; 66] = [
 | 
					pub const BOOKS_IN_ORDER: [&str; 66] = [
 | 
				
			||||||
@@ -73,7 +72,11 @@ pub const BOOKS_IN_ORDER: [&str; 66] = [
 | 
				
			|||||||
    "Revelation",
 | 
					    "Revelation",
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn get(book: &str, chap_and_ver: &str, from_files: Vec<PathBuf>) -> Option<String> {
 | 
					pub fn search_for_word(word: &str, from_files: &Bible) -> Vec<String> {
 | 
				
			||||||
 | 
					    todo!()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn get(book: &str, chap_and_ver: &str, bibles: Vec<&Bible>) -> Option<String> {
 | 
				
			||||||
    // expecting query to be in format:
 | 
					    // expecting query to be in format:
 | 
				
			||||||
    // Gen 1:2
 | 
					    // Gen 1:2
 | 
				
			||||||
    // Gen 1:2-5
 | 
					    // Gen 1:2-5
 | 
				
			||||||
@@ -89,7 +92,7 @@ pub fn get(book: &str, chap_and_ver: &str, from_files: Vec<PathBuf>) -> Option<S
 | 
				
			|||||||
    let res = BOOKS_IN_ORDER
 | 
					    let res = BOOKS_IN_ORDER
 | 
				
			||||||
        .iter()
 | 
					        .iter()
 | 
				
			||||||
        .enumerate()
 | 
					        .enumerate()
 | 
				
			||||||
        .filter(|(_, lbook)| lbook.to_lowercase().contains(&book.trim().to_lowercase()))
 | 
					        .filter(|(_, lbook)| lbook.replace(" ", "").to_lowercase().starts_with(&book.trim().to_lowercase()))
 | 
				
			||||||
        .collect::<Vec<(usize, &&str)>>();
 | 
					        .collect::<Vec<(usize, &&str)>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let (book_idx, book_name) = match res.len() {
 | 
					    let (book_idx, book_name) = match res.len() {
 | 
				
			||||||
@@ -107,19 +110,13 @@ pub fn get(book: &str, chap_and_ver: &str, from_files: Vec<PathBuf>) -> Option<S
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Load Bibles into memory
 | 
					 | 
				
			||||||
    let bibles = from_files 
 | 
					 | 
				
			||||||
        .iter()
 | 
					 | 
				
			||||||
        .filter_map(|path| fs::read_to_string(path).ok())
 | 
					 | 
				
			||||||
        .filter_map(|contents| from_str::<Bible>(&contents).ok())
 | 
					 | 
				
			||||||
        .collect::<Vec<Bible>>();
 | 
					 | 
				
			||||||
    // Select the book in each Bible
 | 
					    // Select the book in each Bible
 | 
				
			||||||
    let books = bibles
 | 
					    let books = bibles
 | 
				
			||||||
        .iter()
 | 
					        .iter()
 | 
				
			||||||
        // Book are 1 indexed in the xml spec
 | 
					        // Book are 1 indexed in the xml spec
 | 
				
			||||||
        .map(|bible| (bible, bible.get_book_by_index(book_idx + 1)))
 | 
					        .map(|bible| (bible, bible.get_book_by_index(book_idx + 1)))
 | 
				
			||||||
        .filter(|(_,e)| e.is_some())
 | 
					        .filter(|(_,e)| e.is_some())
 | 
				
			||||||
        .map(|(bible,book)| (bible,book.unwrap()))
 | 
					        .map(|(bible,book)| (*bible,book.unwrap()))
 | 
				
			||||||
        .collect::<Vec<(&Bible,&Book)>>();
 | 
					        .collect::<Vec<(&Bible,&Book)>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Select the chapter in each Bible
 | 
					    // Select the chapter in each Bible
 | 
				
			||||||
@@ -237,12 +234,17 @@ impl Chapter {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Deserialize)]
 | 
					#[derive(Deserialize)]
 | 
				
			||||||
struct Bible {
 | 
					pub struct Bible {
 | 
				
			||||||
    #[serde(rename = "@translation")]
 | 
					    #[serde(rename = "@translation")]
 | 
				
			||||||
    translation_name: String,
 | 
					    translation_name: String,
 | 
				
			||||||
    #[serde(rename = "testament")]
 | 
					    #[serde(rename = "testament")]
 | 
				
			||||||
    testaments: Vec<Testament>,
 | 
					    testaments: Vec<Testament>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					impl Display for Bible {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        write!(f, "{}",self.translation_name)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Deserialize)]
 | 
					#[derive(Deserialize)]
 | 
				
			||||||
struct Testament {
 | 
					struct Testament {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user