working on tab... checkpoint

This commit is contained in:
Oliver Atkinson 2025-04-02 15:42:55 -06:00
parent 789c593f7e
commit 727b2f0b51

View File

@ -1,4 +1,7 @@
#![feature(iter_map_windows)] #![feature(iter_map_windows)]
use iced::keyboard::key::Named;
use iced::keyboard::{Key, Modifiers};
use iced::{event, task, Event, Subscription};
use iced::widget::scrollable; use iced::widget::scrollable;
use iced::{ use iced::{
Element, Length, Task, Theme, clipboard, color, Element, Length, Task, Theme, clipboard, color,
@ -43,32 +46,14 @@ fn main() -> iced::Result {
} }
} }
// ALL BUILTIN THEMES // THEMES
// Light // Dark - KanagawaDragon
// Dark // Default - Nord
// Dracula // Light - TokyoNightLight
// Nord
// SolarizedLight
// SolarizedDark
// GruvboxLight
// GruvboxDark
// CatppuccinLatte
// CatppuccinFrappe
// CatppuccinMacchiato
// CatppuccinMocha
// TokyoNight
// TokyoNightStorm
// TokyoNightLight
// KanagawaWave
// KanagawaDragon
// KanagawaLotus
// Moonfly
// Nightfly
// Oxocarbon
// Ferra
// GUI // GUI
iced::application("Bible Study", State::update, State::view) iced::application("Bible Study", State::update, State::view)
.subscription(State::subscription)
.theme(|_| Theme::Nord) .theme(|_| Theme::Nord)
.run() .run()
} }
@ -85,6 +70,10 @@ enum Message {
QuickSearch(usize, String, usize), QuickSearch(usize, String, usize),
AddColRight, AddColRight,
DeleteColumn(usize), DeleteColumn(usize),
ErrorSelectingBible(usize),
RecievedEvent(Event),
ColSelected(usize),
ColUnselected,
} }
struct ColState { struct ColState {
@ -96,6 +85,8 @@ struct ColState {
word_search: String, word_search: String,
word_search_results: Option<Vec<(String, bool)>>, word_search_results: Option<Vec<(String, bool)>>,
error: Option<String>,
} }
impl Default for ColState { impl Default for ColState {
@ -109,6 +100,41 @@ impl Default for ColState {
word_search: String::new(), word_search: String::new(),
word_search_results: None, word_search_results: None,
error: None,
}
}
}
#[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-",
} }
} }
} }
@ -117,6 +143,9 @@ struct State {
files: combo_box::State<String>, files: combo_box::State<String>,
states: Vec<ColState>, states: Vec<ColState>,
cols: usize, cols: usize,
/// Incase an event needs a best guess column
last_selected_column: Option<usize>,
last_selected_input: Option<SelectedInputBox>
} }
impl Default for State { impl Default for State {
@ -147,13 +176,74 @@ impl Default for State {
files: combo_box::State::new(files), files: combo_box::State::new(files),
states: vec![ColState::default()], states: vec![ColState::default()],
cols: 1, cols: 1,
last_selected_column: None,
last_selected_input: None,
} }
} }
} }
impl State { impl State {
fn subscription(&self) -> Subscription<Message> {
event::listen().map(Message::RecievedEvent)
}
fn update(&mut self, msg: Message) -> Task<Message> { fn update(&mut self, msg: Message) -> Task<Message> {
// handle tab ordering
match msg { 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
// TODO prev
return text_input::focus(format!("verse-{}", i));
}
}
}
}
}
}
}
Message::ErrorSelectingBible(i) => {
self.states[i].error = Some(String::new());
}
Message::DeleteColumn(idx) => { Message::DeleteColumn(idx) => {
self.cols -= 1; self.cols -= 1;
self.states.remove(idx); self.states.remove(idx);
@ -165,14 +255,26 @@ impl State {
self.states.push(ColState::default()) self.states.push(ColState::default())
} }
Message::BibleSelected(i, file) => { Message::BibleSelected(i, file) => {
if let Ok(contents) = fs::read_to_string(&file) { match fs::read_to_string(&file) {
if let Ok(bible) = quick_xml::de::from_str::<bible::Bible>(&contents) { Ok(contents) => {
// State is held technically in this variable, although match quick_xml::de::from_str::<bible::Bible>(&contents) {
// the Bible is held in memory in it's own variable. Ok(bible) => {
self.states[i].selected_file = Some(format!("{bible}")); // State is held technically in this variable, although
self.states[i].bible_selected = Some(bible); // the Bible is held in memory in it's own variable.
// automatically focus the search bar when done here self.states[i].selected_file = Some(format!("{bible}"));
// return text_input::focus("bible-search"); self.states[i].bible_selected = Some(bible);
// automatically focus the search bar when done here
// return text_input::focus("bible-search");
}
Err(err) => {
eprintln!("{}", err);
return self.update(Message::ErrorSelectingBible(i));
},
}
},
Err(err) => {
eprintln!("{}", err);
return self.update(Message::ErrorSelectingBible(i));
} }
} }
} }
@ -184,8 +286,12 @@ impl State {
} }
return self.update(Message::BibleSearchSubmit(i)); return self.update(Message::BibleSearchSubmit(i));
} }
Message::BibleSearchInput(i, query) => self.states[i].bible_search = query, Message::BibleSearchInput(i, query) => {
Message::WordSearchInput(i, query) => self.states[i].word_search = query, self.states[i].bible_search = query
},
Message::WordSearchInput(i, query) => {
self.states[i].word_search = query
},
Message::BibleSearchSubmit(i) => { Message::BibleSearchSubmit(i) => {
let mut splits = self.states[i].bible_search.split_whitespace(); let mut splits = self.states[i].bible_search.split_whitespace();
let book = splits.next(); let book = splits.next();
@ -218,13 +324,13 @@ impl State {
self.states[i].word_search_results = None; self.states[i].word_search_results = None;
self.states[i].bible_search = String::new(); self.states[i].bible_search = String::new();
self.states[i].word_search = String::new(); self.states[i].word_search = String::new();
// return text_input::focus("bible-search");
} }
}; };
Task::none() Task::none()
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {
//TODO error message
scrollable( scrollable(
row![ row![
row((0..self.cols).map(|col_index| { row((0..self.cols).map(|col_index| {
@ -239,14 +345,17 @@ impl State {
"Select Bible", "Select Bible",
self.states[col_index].selected_file.as_ref(), self.states[col_index].selected_file.as_ref(),
move |s| Message::BibleSelected(col_index, s) move |s| Message::BibleSelected(col_index, s)
), )
.on_open(Message::ColSelected(col_index)),
text_input( text_input(
"Search query, ie: Gen 1:1", "Search query, ie: Gen 1:1",
&self.states[col_index].bible_search &self.states[col_index].bible_search
) )
.id(format!("verse-{}", col_index))
.on_input(move |s| Message::BibleSearchInput(col_index, s)) .on_input(move |s| Message::BibleSearchInput(col_index, s))
.on_submit(Message::BibleSearchSubmit(col_index)), .on_submit(Message::BibleSearchSubmit(col_index)),
widget::text_input("Word Search", &self.states[col_index].word_search) widget::text_input("Word Search", &self.states[col_index].word_search)
.id(format!("word-{}", col_index))
.on_input(move |s| Message::WordSearchInput(col_index, s)) .on_input(move |s| Message::WordSearchInput(col_index, s))
.on_submit(Message::SubmitWordSearch(col_index)), .on_submit(Message::SubmitWordSearch(col_index)),
row![ row![