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)]
 | 
			
		||||
use iced::{
 | 
			
		||||
    Element,
 | 
			
		||||
    widget::{self, column, combo_box},
 | 
			
		||||
    clipboard, widget::{self, column, combo_box, row, text_input}, Element, Task
 | 
			
		||||
};
 | 
			
		||||
use std::{env, fs, path::PathBuf};
 | 
			
		||||
use texts::*;
 | 
			
		||||
@@ -11,6 +10,7 @@ mod texts;
 | 
			
		||||
pub const BIBLE_DIRECTORY: &str = "./Holy-Bible-XML-Format";
 | 
			
		||||
 | 
			
		||||
fn main() -> iced::Result {
 | 
			
		||||
    // CLI
 | 
			
		||||
    let arg = env::args().collect::<Vec<String>>();
 | 
			
		||||
    if let Some(query) = arg.get(1) {
 | 
			
		||||
        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(f) = bible::get(
 | 
			
		||||
                query,
 | 
			
		||||
                second_query,
 | 
			
		||||
                vec![format!("{BIBLE_DIRECTORY}/EnglishNASBBible.xml").into()],
 | 
			
		||||
            ) {
 | 
			
		||||
                println!("{}", f);
 | 
			
		||||
            }
 | 
			
		||||
            // if let Some(f) = bible::get( query, second_query, vec![format!("{BIBLE_DIRECTORY}/EnglishNASBBible.xml").into()],
 | 
			
		||||
            // ) {
 | 
			
		||||
            //   println!("{}", f);
 | 
			
		||||
            // }
 | 
			
		||||
            return Ok(())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // GUI
 | 
			
		||||
    iced::run("Bible Search", State::update, State::view)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
enum Message {
 | 
			
		||||
    InputChange(String),
 | 
			
		||||
    BibleSearchInput(String),
 | 
			
		||||
    BibleSelected(String),
 | 
			
		||||
    SubmitQuery,
 | 
			
		||||
    BibleSearchSubmit,
 | 
			
		||||
    CopyText,
 | 
			
		||||
    Clear,
 | 
			
		||||
    WordSearchInput(String),
 | 
			
		||||
    SubmitWordSearch,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct State {
 | 
			
		||||
    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>,
 | 
			
		||||
 | 
			
		||||
    word_search: String,
 | 
			
		||||
}
 | 
			
		||||
impl Default for State {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
@@ -74,55 +79,88 @@ impl Default for State {
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            files: combo_box::State::new(files),
 | 
			
		||||
            file_selected: None,
 | 
			
		||||
            selected_file: None,
 | 
			
		||||
            bible_selected: None,
 | 
			
		||||
 | 
			
		||||
            query: String::new(),
 | 
			
		||||
            bible_search: String::new(),
 | 
			
		||||
            body: None,
 | 
			
		||||
 | 
			
		||||
            word_search: String::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl State {
 | 
			
		||||
    fn update(&mut self, msg: Message) {
 | 
			
		||||
    fn update(&mut self, msg: Message) -> Task<Message> {
 | 
			
		||||
        match msg {
 | 
			
		||||
            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) => {
 | 
			
		||||
                // update self's state
 | 
			
		||||
                self.query = query;
 | 
			
		||||
            }
 | 
			
		||||
            Message::SubmitQuery => {
 | 
			
		||||
                let mut splits = self.query.split_whitespace();
 | 
			
		||||
            Message::BibleSearchInput(query) => self.bible_search = query,
 | 
			
		||||
            Message::WordSearchInput(query) => self.word_search = query,
 | 
			
		||||
            Message::BibleSearchSubmit => {
 | 
			
		||||
                let mut splits = self.bible_search.split_whitespace();
 | 
			
		||||
                let book = splits.next();
 | 
			
		||||
                let location = splits.next();
 | 
			
		||||
 | 
			
		||||
                if let (Some(book), Some(chap_and_ver), Some(file)) =
 | 
			
		||||
                    (book, location, &self.file_selected)
 | 
			
		||||
                if let (Some(book), Some(chap_and_ver), Some(bible)) =
 | 
			
		||||
                    (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> {
 | 
			
		||||
        let msg = if let Some(body) = self.body.clone() {
 | 
			
		||||
            parse(&body)
 | 
			
		||||
        } else {
 | 
			
		||||
            "None".to_string()
 | 
			
		||||
            String::new()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        column![
 | 
			
		||||
            widget::combo_box(
 | 
			
		||||
                &self.files,
 | 
			
		||||
                "Select Bible",
 | 
			
		||||
                self.file_selected.as_ref(),
 | 
			
		||||
                self.selected_file.as_ref(),
 | 
			
		||||
                Message::BibleSelected
 | 
			
		||||
            ),
 | 
			
		||||
            widget::text_input("Search query, ie: Gen 1:1", &self.query)
 | 
			
		||||
                .on_input(Message::InputChange)
 | 
			
		||||
                .on_submit(Message::SubmitQuery),
 | 
			
		||||
            widget::text_input("Search query, ie: Gen 1:1", &self.bible_search)
 | 
			
		||||
                .on_input(Message::BibleSearchInput)
 | 
			
		||||
                .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),
 | 
			
		||||
        ]
 | 
			
		||||
        .into()
 | 
			
		||||
@@ -135,7 +173,7 @@ fn parse(input: &str) -> String {
 | 
			
		||||
        .bytes()
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .enumerate()
 | 
			
		||||
        .map_windows(|[(ai, a), (bi, b)]| {
 | 
			
		||||
        .map_windows(|[(ai, a), (_, b)]| {
 | 
			
		||||
            // 27 91 is the start sequence for a color/style
 | 
			
		||||
            if *a == 27 && *b == 91 {
 | 
			
		||||
                Some(*ai)
 | 
			
		||||
@@ -208,3 +246,4 @@ fn parse(input: &str) -> String {
 | 
			
		||||
    
 | 
			
		||||
    modified
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
use inline_colorization::*;
 | 
			
		||||
use std::{fmt::Display, fs, path::PathBuf};
 | 
			
		||||
use std::fmt::Display;
 | 
			
		||||
 | 
			
		||||
use quick_xml::de::from_str;
 | 
			
		||||
use serde::Deserialize;
 | 
			
		||||
 | 
			
		||||
pub const BOOKS_IN_ORDER: [&str; 66] = [
 | 
			
		||||
@@ -73,7 +72,11 @@ pub const BOOKS_IN_ORDER: [&str; 66] = [
 | 
			
		||||
    "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:
 | 
			
		||||
    // Gen 1:2
 | 
			
		||||
    // 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
 | 
			
		||||
        .iter()
 | 
			
		||||
        .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)>>();
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    let books = bibles
 | 
			
		||||
        .iter()
 | 
			
		||||
        // Book are 1 indexed in the xml spec
 | 
			
		||||
        .map(|bible| (bible, bible.get_book_by_index(book_idx + 1)))
 | 
			
		||||
        .filter(|(_,e)| e.is_some())
 | 
			
		||||
        .map(|(bible,book)| (bible,book.unwrap()))
 | 
			
		||||
        .map(|(bible,book)| (*bible,book.unwrap()))
 | 
			
		||||
        .collect::<Vec<(&Bible,&Book)>>();
 | 
			
		||||
 | 
			
		||||
    // Select the chapter in each Bible
 | 
			
		||||
@@ -237,12 +234,17 @@ impl Chapter {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize)]
 | 
			
		||||
struct Bible {
 | 
			
		||||
pub struct Bible {
 | 
			
		||||
    #[serde(rename = "@translation")]
 | 
			
		||||
    translation_name: String,
 | 
			
		||||
    #[serde(rename = "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)]
 | 
			
		||||
struct Testament {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user