summaryrefslogtreecommitdiff
path: root/schist_desktop_gui/src/gui/components/navigation
diff options
context:
space:
mode:
authorJoe Carstairs <me@joeac.net>2026-02-07 11:52:08 +0000
committerJoe Carstairs <me@joeac.net>2026-02-07 12:02:27 +0000
commit1664e741ad35ce52a01788c34029f0b2ea477e32 (patch)
treedd6bd7c83a7e834c75815a786d67b5188cdd1f48 /schist_desktop_gui/src/gui/components/navigation
parent179878d12ba10ac242da2044f4c6dfe4d4829e59 (diff)
task-001: can navigate file screen options with keyboard
Diffstat (limited to 'schist_desktop_gui/src/gui/components/navigation')
-rw-r--r--schist_desktop_gui/src/gui/components/navigation/dummy_navigation_option.rs9
-rw-r--r--schist_desktop_gui/src/gui/components/navigation/navigate_navigation.rs31
-rw-r--r--schist_desktop_gui/src/gui/components/navigation/navigation_find.rs16
-rw-r--r--schist_desktop_gui/src/gui/components/navigation/navigation_replace_options.rs5
-rw-r--r--schist_desktop_gui/src/gui/components/navigation/navigation_select_option.rs24
-rw-r--r--schist_desktop_gui/src/gui/components/navigation/navigation_set_options.rs9
-rw-r--r--schist_desktop_gui/src/gui/components/navigation/update_navigation.rs11
-rw-r--r--schist_desktop_gui/src/gui/components/navigation/view_navigation_button.rs13
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()
}
}