Tabbing now works, impelemented preliminary error message handling
This commit is contained in:
		
							
								
								
									
										155
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										155
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
			
		||||
#![feature(iter_map_windows)]
 | 
			
		||||
use iced::keyboard::key::Named;
 | 
			
		||||
use iced::keyboard::{Key, Modifiers};
 | 
			
		||||
use iced::{event, task, Event, Subscription};
 | 
			
		||||
use iced::{event, Event, Subscription};
 | 
			
		||||
use iced::widget::scrollable;
 | 
			
		||||
use iced::{
 | 
			
		||||
    Element, Length, Task, Theme, clipboard, color,
 | 
			
		||||
@@ -12,6 +12,7 @@ use iced::{
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
use std::{env, fs, path::PathBuf};
 | 
			
		||||
use texts::*;
 | 
			
		||||
 | 
			
		||||
@@ -32,7 +33,7 @@ fn main() -> iced::Result {
 | 
			
		||||
        if let Some(second_query) = arg.get(2) {
 | 
			
		||||
            if let Ok(contents) = fs::read_to_string(&format!("{BIBLE_DIRECTORY}/EnglishNASBBible.xml")) {
 | 
			
		||||
                if let Ok(bible) = quick_xml::de::from_str::<bible::Bible>(&contents) {
 | 
			
		||||
                    if let Some(f) = bible::get(
 | 
			
		||||
                    if let Ok(f) = bible::get(
 | 
			
		||||
                        query,
 | 
			
		||||
                        second_query,
 | 
			
		||||
                        vec![&bible],
 | 
			
		||||
@@ -70,10 +71,8 @@ enum Message {
 | 
			
		||||
    QuickSearch(usize, String, usize),
 | 
			
		||||
    AddColRight,
 | 
			
		||||
    DeleteColumn(usize),
 | 
			
		||||
    ErrorSelectingBible(usize),
 | 
			
		||||
    SetErrorMessage(usize, String),
 | 
			
		||||
    RecievedEvent(Event),
 | 
			
		||||
    ColSelected(usize),
 | 
			
		||||
    ColUnselected,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ColState {
 | 
			
		||||
@@ -106,46 +105,11 @@ impl Default for ColState {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(PartialEq, Eq)]
 | 
			
		||||
enum SelectedInputBox {
 | 
			
		||||
    Bible,
 | 
			
		||||
    VerseSearch,
 | 
			
		||||
    WordSearch,
 | 
			
		||||
}
 | 
			
		||||
impl SelectedInputBox {
 | 
			
		||||
    fn in_order() -> Vec<Self> {
 | 
			
		||||
        vec![
 | 
			
		||||
            SelectedInputBox::Bible,
 | 
			
		||||
            SelectedInputBox::VerseSearch,
 | 
			
		||||
            SelectedInputBox::WordSearch
 | 
			
		||||
            ]
 | 
			
		||||
    }
 | 
			
		||||
    fn next(current: &Option<Self>) -> Self {
 | 
			
		||||
        if let Some(input) = current {
 | 
			
		||||
            if let Some(found) = Self::in_order().into_iter().find(|a| a==input) {
 | 
			
		||||
                return found
 | 
			
		||||
            } 
 | 
			
		||||
        }
 | 
			
		||||
        // I don't see why this would panic
 | 
			
		||||
        Self::in_order().into_iter().next().unwrap()
 | 
			
		||||
    }
 | 
			
		||||
    fn as_id_prefix(&self) -> &str {
 | 
			
		||||
        // magic values that relate to id() as setup in the GUI
 | 
			
		||||
        match self {
 | 
			
		||||
            SelectedInputBox::Bible => "",
 | 
			
		||||
            SelectedInputBox::VerseSearch => "verse-",
 | 
			
		||||
            SelectedInputBox::WordSearch => "word-",
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct State {
 | 
			
		||||
    files: combo_box::State<String>,
 | 
			
		||||
    states: Vec<ColState>,
 | 
			
		||||
    cols: usize,
 | 
			
		||||
    /// Incase an event needs a best guess column
 | 
			
		||||
    last_selected_column: Option<usize>,
 | 
			
		||||
    last_selected_input: Option<SelectedInputBox>
 | 
			
		||||
    tab_index: Option<i32>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for State {
 | 
			
		||||
@@ -176,8 +140,7 @@ impl Default for State {
 | 
			
		||||
            files: combo_box::State::new(files),
 | 
			
		||||
            states: vec![ColState::default()],
 | 
			
		||||
            cols: 1,
 | 
			
		||||
            last_selected_column: None,
 | 
			
		||||
            last_selected_input: None,
 | 
			
		||||
            tab_index: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -189,51 +152,27 @@ impl State {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn update(&mut self, msg: Message) -> Task<Message> {
 | 
			
		||||
        // handle tab ordering
 | 
			
		||||
        match msg {
 | 
			
		||||
            Message::WordSearchInput(_, _) => self.last_selected_input = Some(SelectedInputBox::WordSearch),
 | 
			
		||||
            Message::BibleSearchInput(_, _) => self.last_selected_input = Some(SelectedInputBox::VerseSearch),
 | 
			
		||||
            Message::DeleteColumn(_) => self.last_selected_input = None,
 | 
			
		||||
            _ => {}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // try to set last_selected_column for messages with side effects
 | 
			
		||||
        match msg {
 | 
			
		||||
            // All events that give column index
 | 
			
		||||
            Message::ErrorSelectingBible(i) |
 | 
			
		||||
            Message::BibleSelected(i, _) |
 | 
			
		||||
            Message::QuickSearch(i, _, _) |
 | 
			
		||||
            Message::BibleSearchInput(i,_) |
 | 
			
		||||
            Message::WordSearchInput(i, _) |
 | 
			
		||||
            Message::BibleSearchSubmit(i) |
 | 
			
		||||
            Message::SubmitWordSearch(i) |
 | 
			
		||||
            Message::CopyText(i) |
 | 
			
		||||
            Message::Clear(i)
 | 
			
		||||
            => self.last_selected_column = Some(i),
 | 
			
		||||
            Message::DeleteColumn(_) => self.last_selected_column = None,
 | 
			
		||||
            _ => {}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // match normal messages
 | 
			
		||||
        match msg {
 | 
			
		||||
            Message::ColSelected(i) => self.last_selected_column = Some(i),
 | 
			
		||||
            Message::ColUnselected => self.last_selected_column = None,
 | 
			
		||||
            Message::RecievedEvent(event) => {
 | 
			
		||||
                if let Event::Keyboard(kbd) = event {
 | 
			
		||||
                    if let iced::keyboard::Event::KeyReleased {key, modifiers, ..} = kbd {
 | 
			
		||||
                        if let Key::Named(n) = key {
 | 
			
		||||
                            if let Named::Tab = n {
 | 
			
		||||
                                if let Some(i) = self.last_selected_column {
 | 
			
		||||
                                    if modifiers == Modifiers::SHIFT {
 | 
			
		||||
                                        // Shirft + Tab
 | 
			
		||||
                                        let next = SelectedInputBox::next(&self.last_selected_input);
 | 
			
		||||
                                        let id = next.as_id_prefix(); 
 | 
			
		||||
                                        return text_input::focus(format!("{id}{}", i));
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        // Tab
 | 
			
		||||
                                if let Some(idx) = self.tab_index {
 | 
			
		||||
                                    let new_idx = if modifiers == Modifiers::SHIFT { idx-1 } else { idx+1 };
 | 
			
		||||
 | 
			
		||||
                                        // TODO prev
 | 
			
		||||
                                        return text_input::focus(format!("verse-{}", i));
 | 
			
		||||
                                    // 2 because two buttons on each col
 | 
			
		||||
                                    let new_idx = new_idx.clamp(0, (self.cols*2) as i32 -1);
 | 
			
		||||
 | 
			
		||||
                                    self.tab_index = Some(new_idx);
 | 
			
		||||
 | 
			
		||||
                                    let id = format!("tabId-{}", new_idx);
 | 
			
		||||
                                    return text_input::focus(id);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    if self.cols > 0 {
 | 
			
		||||
                                        self.tab_index = Some(0);
 | 
			
		||||
                                        return text_input::focus("tabId-0");
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
@@ -241,8 +180,8 @@ impl State {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Message::ErrorSelectingBible(i) => {
 | 
			
		||||
                self.states[i].error = Some(String::new());
 | 
			
		||||
            Message::SetErrorMessage(i, m) => {
 | 
			
		||||
                self.states[i].error = Some(m);
 | 
			
		||||
            }
 | 
			
		||||
            Message::DeleteColumn(idx) => {
 | 
			
		||||
                self.cols -= 1;
 | 
			
		||||
@@ -263,18 +202,18 @@ impl State {
 | 
			
		||||
                                // the Bible is held in memory in it's own variable.
 | 
			
		||||
                                self.states[i].selected_file = Some(format!("{bible}"));
 | 
			
		||||
                                self.states[i].bible_selected = Some(bible);
 | 
			
		||||
                                // automatically focus the search bar when done here
 | 
			
		||||
                                // return text_input::focus("bible-search");
 | 
			
		||||
                                self.states[i].error = None;
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                            Err(err) => {
 | 
			
		||||
                                eprintln!("{}", err);
 | 
			
		||||
                                return self.update(Message::ErrorSelectingBible(i));
 | 
			
		||||
                                return self.update(Message::SetErrorMessage(i, err.to_string()));
 | 
			
		||||
                            },
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    Err(err) => {
 | 
			
		||||
                        eprintln!("{}", err);
 | 
			
		||||
                        return self.update(Message::ErrorSelectingBible(i));
 | 
			
		||||
                        return self.update(Message::SetErrorMessage(i, err.to_string()));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -286,12 +225,8 @@ impl State {
 | 
			
		||||
                }
 | 
			
		||||
                return self.update(Message::BibleSearchSubmit(i));
 | 
			
		||||
            }
 | 
			
		||||
            Message::BibleSearchInput(i, query) => {
 | 
			
		||||
                self.states[i].bible_search = query
 | 
			
		||||
            },
 | 
			
		||||
            Message::WordSearchInput(i, query) => {
 | 
			
		||||
                self.states[i].word_search = query
 | 
			
		||||
            },
 | 
			
		||||
            Message::BibleSearchInput(i, query) => self.states[i].bible_search = query,
 | 
			
		||||
            Message::WordSearchInput(i, query) => self.states[i].word_search = query,
 | 
			
		||||
            Message::BibleSearchSubmit(i) => {
 | 
			
		||||
                let mut splits = self.states[i].bible_search.split_whitespace();
 | 
			
		||||
                let book = splits.next();
 | 
			
		||||
@@ -300,7 +235,25 @@ impl State {
 | 
			
		||||
                if let (Some(book), Some(chap_and_ver), Some(bible)) =
 | 
			
		||||
                    (book, location, &self.states[i].bible_selected)
 | 
			
		||||
                {
 | 
			
		||||
                    self.states[i].scripture_body = bible::get(book, chap_and_ver, vec![bible]);
 | 
			
		||||
                    self.states[i].scripture_body = match bible::get(book, chap_and_ver, vec![bible]) {
 | 
			
		||||
                        Ok(s) => {
 | 
			
		||||
                            self.states[i].error = None;
 | 
			
		||||
                            Some(s)
 | 
			
		||||
                        },
 | 
			
		||||
                        Err(s) => {
 | 
			
		||||
                            return self.update(Message::SetErrorMessage(i, s)); 
 | 
			
		||||
                        },
 | 
			
		||||
                    };
 | 
			
		||||
                } else {
 | 
			
		||||
                    if let None = book {
 | 
			
		||||
                        return self.update(Message::SetErrorMessage(i, format!("Error selecting book {:?}", book)));
 | 
			
		||||
                    }
 | 
			
		||||
                    if let None = location {
 | 
			
		||||
                        return self.update(Message::SetErrorMessage(i, format!("Error getting chapter and/or verse {:?}", location)));
 | 
			
		||||
                    }
 | 
			
		||||
                    if let None = &self.states[i].bible_selected {
 | 
			
		||||
                        return self.update(Message::SetErrorMessage(i, format!("Error getting selected bible"))); 
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Message::SubmitWordSearch(i) => {
 | 
			
		||||
@@ -336,26 +289,21 @@ impl State {
 | 
			
		||||
                row((0..self.cols).map(|col_index| {
 | 
			
		||||
                    column![
 | 
			
		||||
                        // header
 | 
			
		||||
                        button("Delete Column")
 | 
			
		||||
                            .on_press_with(move || Message::DeleteColumn(col_index))
 | 
			
		||||
                            .width(Length::Fill)
 | 
			
		||||
                            .style(button::danger),
 | 
			
		||||
                        combo_box(
 | 
			
		||||
                            &self.files,
 | 
			
		||||
                            "Select Bible",
 | 
			
		||||
                            self.states[col_index].selected_file.as_ref(),
 | 
			
		||||
                            move |s| Message::BibleSelected(col_index, s)
 | 
			
		||||
                        )
 | 
			
		||||
                        .on_open(Message::ColSelected(col_index)),
 | 
			
		||||
                        ),
 | 
			
		||||
                        text_input(
 | 
			
		||||
                            "Search query, ie: Gen 1:1",
 | 
			
		||||
                            &self.states[col_index].bible_search
 | 
			
		||||
                        )
 | 
			
		||||
                        .id(format!("verse-{}", col_index))
 | 
			
		||||
                        .id(format!("tabId-{}", (col_index*2)+0))
 | 
			
		||||
                        .on_input(move |s| Message::BibleSearchInput(col_index, s))
 | 
			
		||||
                        .on_submit(Message::BibleSearchSubmit(col_index)),
 | 
			
		||||
                        widget::text_input("Word Search", &self.states[col_index].word_search)
 | 
			
		||||
                            .id(format!("word-{}", col_index))
 | 
			
		||||
                            .id(format!("tabId-{}", (col_index*2)+1))
 | 
			
		||||
                            .on_input(move |s| Message::WordSearchInput(col_index, s))
 | 
			
		||||
                            .on_submit(Message::SubmitWordSearch(col_index)),
 | 
			
		||||
                        row![
 | 
			
		||||
@@ -365,6 +313,11 @@ impl State {
 | 
			
		||||
                            button("Copy Scripture")
 | 
			
		||||
                                .on_press_with(move || Message::CopyText(col_index))
 | 
			
		||||
                                .style(button::primary),
 | 
			
		||||
                            button("Delete Column")
 | 
			
		||||
                                .on_press_with(move || Message::DeleteColumn(col_index))
 | 
			
		||||
                                .style(button::danger),
 | 
			
		||||
                            text(format!("{:?}", self.states[col_index].error))
 | 
			
		||||
                                .style(text::danger),
 | 
			
		||||
                        ]
 | 
			
		||||
                        .spacing(5),
 | 
			
		||||
                        row![
 | 
			
		||||
 
 | 
			
		||||
@@ -102,7 +102,7 @@ pub fn search_for_word(word: &str, from_files: &Bible) -> Vec<String> {
 | 
			
		||||
    found
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn get(book: &str, chap_and_ver: &str, bibles: Vec<&Bible>) -> Option<String> {
 | 
			
		||||
pub fn get(book: &str, chap_and_ver: &str, bibles: Vec<&Bible>) -> Result<String, String> {
 | 
			
		||||
    // expecting query to be in format:
 | 
			
		||||
    // Gen 1:2
 | 
			
		||||
    // Gen 1:2-5
 | 
			
		||||
@@ -135,11 +135,11 @@ pub fn get(book: &str, chap_and_ver: &str, bibles: Vec<&Bible>) -> Option<String
 | 
			
		||||
            for (_, i) in &res {
 | 
			
		||||
                eprintln!("\t{i}");
 | 
			
		||||
            }
 | 
			
		||||
            return None;
 | 
			
		||||
            return Err(format!("Ambigious input '{book}'"));
 | 
			
		||||
        }
 | 
			
		||||
        _ => {
 | 
			
		||||
            eprintln!("'{book}' is not a book");
 | 
			
		||||
            return None;
 | 
			
		||||
            return Err(format!("'{book}' is not a book"));
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -162,10 +162,10 @@ pub fn get(book: &str, chap_and_ver: &str, bibles: Vec<&Bible>) -> Option<String
 | 
			
		||||
                .map(|(bible,book,chapter)| (*bible,*book,chapter.unwrap()))
 | 
			
		||||
                .collect::<Vec<(&Bible, &Book, &Chapter)>>()
 | 
			
		||||
        } else {
 | 
			
		||||
            return None;
 | 
			
		||||
            return Err(format!("Chapter number could not be parsed from {chapter}"));
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return None;
 | 
			
		||||
        return Err(format!("A chapter was not passed"));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Get the verse in each Bible
 | 
			
		||||
@@ -190,9 +190,9 @@ pub fn get(book: &str, chap_and_ver: &str, bibles: Vec<&Bible>) -> Option<String
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        return Some(buf);
 | 
			
		||||
                        return Ok(buf);
 | 
			
		||||
                    }
 | 
			
		||||
                    return None;
 | 
			
		||||
                    return Err(format!("Could not parse '{sn}' or '{en}' into numbers"));
 | 
			
		||||
                }
 | 
			
		||||
                // only one verse
 | 
			
		||||
                (Some(ver_num), None) => {
 | 
			
		||||
@@ -206,13 +206,13 @@ pub fn get(book: &str, chap_and_ver: &str, bibles: Vec<&Bible>) -> Option<String
 | 
			
		||||
                                );
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        return Some(buf);
 | 
			
		||||
                        return Ok(buf);
 | 
			
		||||
                    }
 | 
			
		||||
                    return None;
 | 
			
		||||
                    return Err(format!("Could not parse '{ver_num}' into a number"));
 | 
			
		||||
                }
 | 
			
		||||
                _ => {
 | 
			
		||||
                    // couldn't parse verse
 | 
			
		||||
                    return None;
 | 
			
		||||
                    return Err(format!("Couldn't parse '{:?}' or '{:?}' into verse numbers", start, end));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -226,7 +226,7 @@ pub fn get(book: &str, chap_and_ver: &str, bibles: Vec<&Bible>) -> Option<String
 | 
			
		||||
                );
 | 
			
		||||
                buf += &format!("{}", chapter);
 | 
			
		||||
            }
 | 
			
		||||
            return Some(buf);
 | 
			
		||||
            return Ok(buf);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user