diff options
| author | Joe Carstairs <me@joeac.net> | 2026-02-07 11:52:08 +0000 |
|---|---|---|
| committer | Joe Carstairs <me@joeac.net> | 2026-02-07 12:02:27 +0000 |
| commit | 1664e741ad35ce52a01788c34029f0b2ea477e32 (patch) | |
| tree | dd6bd7c83a7e834c75815a786d67b5188cdd1f48 /schist_desktop_gui/src/gui/components/navigation | |
| parent | 179878d12ba10ac242da2044f4c6dfe4d4829e59 (diff) | |
task-001: can navigate file screen options with keyboard
Diffstat (limited to 'schist_desktop_gui/src/gui/components/navigation')
8 files changed, 85 insertions, 33 deletions
diff --git a/schist_desktop_gui/src/gui/components/navigation/dummy_navigation_option.rs b/schist_desktop_gui/src/gui/components/navigation/dummy_navigation_option.rs index ae5799f..6875305 100644 --- a/schist_desktop_gui/src/gui/components/navigation/dummy_navigation_option.rs +++ b/schist_desktop_gui/src/gui/components/navigation/dummy_navigation_option.rs @@ -2,17 +2,22 @@ use std::fmt::Display; use iced::Element; -use crate::gui::components::{navigation, Text}; +use crate::{ + gui::components::{navigation, Text}, + impl_focusable, +}; #[derive(Clone, Debug, PartialEq)] pub struct DummyNavigationOption { pub name: String, + is_focused: bool, } impl DummyNavigationOption { pub fn new(name: &str) -> Self { Self { name: String::from(name), + is_focused: false, } } } @@ -30,3 +35,5 @@ impl Display for DummyNavigationOption { f.write_str(&self.name) } } + +impl_focusable!(DummyNavigationOption); diff --git a/schist_desktop_gui/src/gui/components/navigation/navigate_navigation.rs b/schist_desktop_gui/src/gui/components/navigation/navigate_navigation.rs index f399cd0..72bc503 100644 --- a/schist_desktop_gui/src/gui/components/navigation/navigate_navigation.rs +++ b/schist_desktop_gui/src/gui/components/navigation/navigate_navigation.rs @@ -1,24 +1,39 @@ use iced::Element; -use crate::traits::{Navigable, NavigationResult}; +use crate::traits::{Focusable, Navigable, NavigationResult}; use super::{Message, Navigation}; impl<'a, TOption> Navigable<TOption> for Navigation<TOption> where - TOption: Clone + PartialEq + 'a, + TOption: Clone + Focusable + PartialEq + 'a, Element<'a, Message<TOption>>: From<&'a TOption>, { fn move_next(&mut self) -> NavigationResult<TOption> { - if let Some(next) = self.options_after_active.clone().first() { + if let Some(next) = self.options_after_active.clone().first_mut() { self.options_before_active = vec![ self.options_before_active.clone(), - self.active_option.clone().map_or(vec![], |o| vec![o]), + self.active_option.clone().map_or_else(Vec::new, |mut o| { + o.unfocus(); + vec![o] + }), ] .concat(); + next.focus(); self.active_option = Some(next.clone()); - self.options_after_active = self.options_after_active.split_at(1).1.to_vec(); + self.options_after_active.remove(0); NavigationResult::Moved(next.clone()) + } else if let Some(first) = self + .options_before_active + .clone() + .first_mut() + .filter(|_| self.active_option.is_none()) + { + self.options_after_active = self.options_before_active.split_at(1).1.to_vec(); + self.options_before_active = Vec::new(); + first.focus(); + self.active_option = Some(first.clone()); + NavigationResult::Moved(first.clone()) } else { NavigationResult::LastOption } @@ -27,11 +42,15 @@ where fn move_prev(&mut self) -> NavigationResult<TOption> { if let Some(prev) = self.options_before_active.clone().last() { self.options_after_active = vec![ - self.active_option.clone().map_or(vec![], |o| vec![o]), + self.active_option.clone().map_or(vec![], |mut o| { + o.unfocus(); + vec![o] + }), self.options_after_active.clone(), ] .concat(); self.active_option = Some(prev.clone()); + self.active_option.iter_mut().for_each(TOption::focus); self.options_before_active = self .options_before_active .split_last() diff --git a/schist_desktop_gui/src/gui/components/navigation/navigation_find.rs b/schist_desktop_gui/src/gui/components/navigation/navigation_find.rs new file mode 100644 index 0000000..79c8ab0 --- /dev/null +++ b/schist_desktop_gui/src/gui/components/navigation/navigation_find.rs @@ -0,0 +1,16 @@ +impl<'a, TOption> super::Navigation<TOption> +where + TOption: Clone + PartialEq + 'a, +{ + pub fn find<P>(&self, pred: P) -> Option<TOption> + where + P: Fn(&TOption) -> bool, + { + self.options_before_active + .iter() + .find(|o| pred(o)) + .cloned() + .or_else(|| self.active_option.clone().filter(|o| pred(o))) + .or_else(|| self.options_after_active.iter().find(|o| pred(o)).cloned()) + } +} diff --git a/schist_desktop_gui/src/gui/components/navigation/navigation_replace_options.rs b/schist_desktop_gui/src/gui/components/navigation/navigation_replace_options.rs index e24059d..496250d 100644 --- a/schist_desktop_gui/src/gui/components/navigation/navigation_replace_options.rs +++ b/schist_desktop_gui/src/gui/components/navigation/navigation_replace_options.rs @@ -1,6 +1,8 @@ use iced::Element; use itertools::Itertools; +use crate::traits::Focusable; + use super::{Message, Navigation}; #[derive(PartialEq)] @@ -11,7 +13,7 @@ pub enum ReplaceOptionResult { impl<'a, TOption> Navigation<TOption> where - TOption: Clone + PartialEq + 'a, + TOption: Clone + Focusable + PartialEq + 'a, Element<'a, Message<TOption>>: From<&'a TOption>, { pub fn replace_first<P>(&'a mut self, pred: P, new: &'a Vec<TOption>) -> ReplaceOptionResult @@ -92,6 +94,7 @@ where } self.active_option = new.first().cloned(); + self.active_option.iter_mut().for_each(|o| o.focus()); self.options_after_active .splice(0..0, new.split_at(1).1.to_vec()); ReplaceOptionResult::ReplacedOption diff --git a/schist_desktop_gui/src/gui/components/navigation/navigation_select_option.rs b/schist_desktop_gui/src/gui/components/navigation/navigation_select_option.rs index 4192059..5aa96cd 100644 --- a/schist_desktop_gui/src/gui/components/navigation/navigation_select_option.rs +++ b/schist_desktop_gui/src/gui/components/navigation/navigation_select_option.rs @@ -1,5 +1,7 @@ use iced::Element; +use crate::traits::Focusable; + use super::{Message, Navigation}; pub enum SelectOptionResult<TOption> { @@ -10,7 +12,7 @@ pub enum SelectOptionResult<TOption> { impl<'a, TOption> Navigation<TOption> where - TOption: Clone + PartialEq + 'a, + TOption: Clone + Focusable + PartialEq + 'a, Element<'a, Message<TOption>>: From<&'a TOption>, { pub fn select_option(&mut self, option: TOption) -> SelectOptionResult<TOption> @@ -30,15 +32,17 @@ where { let split = self.options_before_active.split_at(index); self.options_after_active = vec![ - self.active_option - .clone() - .map_or(vec![], |active_option| vec![active_option]), + self.active_option.as_mut().map_or(vec![], |active_option| { + active_option.unfocus(); + vec![active_option.clone()] + }), split.1.split_at(1).1.to_vec(), self.options_after_active.clone(), ] .concat(); self.options_before_active = split.0.to_vec(); self.active_option = Some(option.clone()); + self.active_option.iter_mut().for_each(TOption::focus); SelectOptionResult::SelectedExistingOption(option) } else if let Some(index) = self .options_after_active @@ -49,21 +53,25 @@ where self.options_before_active = vec![ self.options_before_active.clone(), split.0.to_vec(), - self.active_option - .clone() - .map_or(vec![], |active_option| vec![active_option]), + self.active_option.as_mut().map_or(vec![], |active_option| { + active_option.unfocus(); + vec![active_option.clone()] + }), ] .concat(); self.active_option = Some(option.clone()); + self.active_option.iter_mut().for_each(TOption::focus); self.options_after_active = split.1.split_at(1).1.to_vec(); SelectOptionResult::SelectedExistingOption(option) } else { - if let Some(active_option) = &self.active_option { + if let Some(active_option) = &mut self.active_option { + active_option.unfocus(); self.options_before_active.push(active_option.clone()); } self.options_before_active .append(&mut self.options_after_active); self.active_option = Some(option.clone()); + self.active_option.iter_mut().for_each(TOption::focus); self.options_after_active = vec![]; SelectOptionResult::SelectedNewOption(option) } diff --git a/schist_desktop_gui/src/gui/components/navigation/navigation_set_options.rs b/schist_desktop_gui/src/gui/components/navigation/navigation_set_options.rs index c2c9952..3eb455c 100644 --- a/schist_desktop_gui/src/gui/components/navigation/navigation_set_options.rs +++ b/schist_desktop_gui/src/gui/components/navigation/navigation_set_options.rs @@ -1,5 +1,7 @@ use iced::Element; +use crate::traits::Focusable; + use super::{Message, Navigation}; pub enum SetOptionsResult<TOption> { @@ -9,10 +11,12 @@ pub enum SetOptionsResult<TOption> { impl<'a, TOption> Navigation<TOption> where - TOption: Clone + PartialEq + 'a, + TOption: Clone + Focusable + PartialEq + 'a, Element<'a, Message<TOption>>: From<&'a TOption>, { - pub fn set_options(&mut self, options: Vec<TOption>) -> SetOptionsResult<TOption> { + pub fn set_options(&mut self, options: &mut Vec<TOption>) -> SetOptionsResult<TOption> { + options.iter_mut().for_each(TOption::unfocus); + let active_option_index = self.active_option .clone() @@ -31,6 +35,7 @@ where .split_last() .map_or_else(Vec::new, |(_last, rest)| rest.to_vec()); self.active_option = options.last().cloned(); + self.active_option.iter_mut().for_each(TOption::focus); options.last().cloned().map_or( SetOptionsResult::SetOptionsAndKeptSelection, SetOptionsResult::SetOptionsAndSetSelectedOption, diff --git a/schist_desktop_gui/src/gui/components/navigation/update_navigation.rs b/schist_desktop_gui/src/gui/components/navigation/update_navigation.rs index 1d5a734..7bcef98 100644 --- a/schist_desktop_gui/src/gui/components/navigation/update_navigation.rs +++ b/schist_desktop_gui/src/gui/components/navigation/update_navigation.rs @@ -4,14 +4,14 @@ use crate::{ gui::components::navigation::{ navigation_select_option::SelectOptionResult, navigation_set_options::SetOptionsResult, }, - traits::{Component, Navigable, NavigationResult}, + traits::{Component, Focusable, Navigable, NavigationResult}, }; use super::{Action, Message, Navigation}; impl<'a, TOption> Component<'a, Message<TOption>, Action<TOption>> for Navigation<TOption> where - TOption: Clone + PartialEq + 'a, + TOption: Clone + Focusable + PartialEq + 'a, Element<'a, Message<TOption>>: From<&'a TOption>, { fn update(&mut self, message: Message<TOption>) -> Action<TOption> { @@ -21,23 +21,22 @@ where | SelectOptionResult::SelectedNewOption(option) => Action::SelectOption(option), SelectOptionResult::AlreadySelected => Action::None, }, - - Message::SetOptions(options) => match self.set_options(options) { + Message::SetOptions(mut options) => match self.set_options(&mut options) { SetOptionsResult::SetOptionsAndKeptSelection => Action::None, SetOptionsResult::SetOptionsAndSetSelectedOption(option) => { Action::SelectOption(option) } }, - Message::SelectNext => match self.move_next() { NavigationResult::Moved(option) => Action::SelectOption(option), NavigationResult::LastOption => Action::None, }, - Message::SelectPrev => match self.move_prev() { NavigationResult::Moved(option) => Action::SelectOption(option), NavigationResult::LastOption => Action::None, }, + + Message::ActivateOption(option) => Action::ActivateOption(option), } } } diff --git a/schist_desktop_gui/src/gui/components/navigation/view_navigation_button.rs b/schist_desktop_gui/src/gui/components/navigation/view_navigation_button.rs index d65328c..80d6d59 100644 --- a/schist_desktop_gui/src/gui/components/navigation/view_navigation_button.rs +++ b/schist_desktop_gui/src/gui/components/navigation/view_navigation_button.rs @@ -1,6 +1,6 @@ use iced::Element; -use crate::gui::components::{active_button, inactive_button}; +use crate::gui::components::button; use super::{Message, Navigation}; @@ -13,15 +13,10 @@ where &'a self, option: &'a TOption, ) -> Element<'a, Message<TOption>> { - if self + let focus = self .active_option .as_ref() - .is_some_and(|o| TOption::eq(o, option)) - { - active_button(option) - } else { - inactive_button(option, Message::SelectOption(option.clone())) - } - .into() + .is_some_and(|o| TOption::eq(o, option)); + button(option, Message::ActivateOption(option.clone()), focus).into() } } |
