New selected field movement

This commit is contained in:
micialware committed 2026-09-11 00:41:36 +03:00
1 parent 47bc83d4ae
commit 8fe4f3b8f4
3 files changed
+120 -19

No files matched your search

+4
View File
@@ -215,6 +215,10 @@ impl<T: EntityData + Clone + Send + Sync, R: Repository<T, D>, D: DbLayer<T>> Qu
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.cache.len() self.cache.len()
} }
pub fn get(&self, index: usize) -> Option<EntityHandle<T>> {
self.cache.get(index).cloned()
}
} }
impl<T: EntityData + Clone + Send + Sync, R: Repository<T, D>, D: DbLayer<T>> Index<usize> impl<T: EntityData + Clone + Send + Sync, R: Repository<T, D>, D: DbLayer<T>> Index<usize>
+1
View File
@@ -244,6 +244,7 @@ impl ScreenState {
if let Some(page) = self.stack.last_mut() { if let Some(page) = self.stack.last_mut() {
return match page { return match page {
Repetition(page) => page.press(&message), Repetition(page) => page.press(&message),
Dictionary(page) => page.press(&message),
_ => Task::none(), _ => Task::none(),
}; };
} }
+115 -19
View File
@@ -6,7 +6,7 @@ use crate::data_provider::word_groups::{WordGroupDataLayer, WordGroupRepositoryI
use crate::data_provider::words::WordDataLayer; use crate::data_provider::words::WordDataLayer;
use crate::data_provider::words::WordRepositoryInner; use crate::data_provider::words::WordRepositoryInner;
use crate::navigation::Page::{Import, Word}; use crate::navigation::Page::{Import, Word};
use crate::navigation::{NavigatedPage, Page, RootMessage}; use crate::navigation::{KeyPressedPage, NavigatedPage, Page, RootMessage};
use crate::pages::dictionary::DictionaryMessage::*; use crate::pages::dictionary::DictionaryMessage::*;
use crate::pages::dictionary_quiz::DictionaryQuizState; use crate::pages::dictionary_quiz::DictionaryQuizState;
use crate::pages::import::ImportState; use crate::pages::import::ImportState;
@@ -27,11 +27,17 @@ use iced::widget::text_input::default;
use iced::widget::*; use iced::widget::*;
use iced::{Border, Color, Shadow, Task}; use iced::{Border, Color, Shadow, Task};
use iced_core::Length::Fill; use iced_core::Length::Fill;
use iced_core::keyboard;
use iced_core::keyboard::Event;
use iced_core::keyboard::Key::Named;
use iced_core::keyboard::key::Physical::Code;
use log::info; use log::info;
use rand::random_range; use rand::random_range;
use std::ops::Add; use std::ops::Add;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use crate::pages::dictionary::BasicWordFields::*;
use crate::pages::dictionary::SelectionChangeDirection::*;
pub struct DictionaryState { pub struct DictionaryState {
state: AppState, state: AppState,
@@ -46,8 +52,7 @@ pub struct DictionaryState {
auto_save_queue: HashMap<EntityHandle<WordData>, DateTime<Utc>>, auto_save_queue: HashMap<EntityHandle<WordData>, DateTime<Utc>>,
displayed_words_query: Query<WordData, WordRepositoryInner, WordDataLayer>, displayed_words_query: Query<WordData, WordRepositoryInner, WordDataLayer>,
groups_query: Query<WordGroup, WordGroupRepositoryInner, WordGroupDataLayer>, groups_query: Query<WordGroup, WordGroupRepositoryInner, WordGroupDataLayer>,
selected_cell: Option<(EntityHandle<WordData>, BasicWordFields)>, selected_cell: Option<(EntityHandle<WordData>, BasicWordFields, usize)>,
hovered_cell: Option<(EntityHandle<WordData>, BasicWordFields)>,
} }
#[derive(Clone)] #[derive(Clone)]
@@ -73,8 +78,17 @@ pub enum DictionaryMessage {
ChangeDirection, ChangeDirection,
TrySave(EntityHandle<WordData>), TrySave(EntityHandle<WordData>),
GotoImport, GotoImport,
SelectField(EntityHandle<WordData>, BasicWordFields), SelectField(EntityHandle<WordData>, BasicWordFields, usize),
EditField(String), EditField(String),
ChangeSelectedField(SelectionChangeDirection),
}
#[derive(Clone, PartialEq, Eq, Hash, Copy)]
pub enum SelectionChangeDirection {
Next,
Previous,
Forward,
Backward,
} }
#[derive(Clone, PartialEq, Eq, Hash, Copy)] #[derive(Clone, PartialEq, Eq, Hash, Copy)]
@@ -84,6 +98,8 @@ pub enum BasicWordFields {
Tags, Tags,
} }
const FIELDS: &[BasicWordFields] = &[Key, Value, Tags];
impl NavigatedPage<DictionaryMessage> for DictionaryState { impl NavigatedPage<DictionaryMessage> for DictionaryState {
fn navigate(&mut self, message: &DictionaryMessage) -> Option<Page> { fn navigate(&mut self, message: &DictionaryMessage) -> Option<Page> {
if let Back = message { if let Back = message {
@@ -281,9 +297,9 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
} }
} }
GotoImport => {} GotoImport => {}
SelectField(e, f) => { SelectField(e, f, index) => {
info!("Editing word {}", e.id); info!("Editing word {}", e.id);
self.selected_cell = Some((e, f)); self.selected_cell = Some((e, f, index));
return focus(iced::widget::Id::new("editor")); return focus(iced::widget::Id::new("editor"));
} }
EditField(value) => { EditField(value) => {
@@ -307,6 +323,51 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
return self.launch_auto_save_offset(selected.0); return self.launch_auto_save_offset(selected.0);
} }
} }
ChangeSelectedField(change) => {
return if let Some(selected) = self.selected_cell.clone() {
let update_vertical = move |mut dir: bool| {
if self.reverse_list {
dir = !dir;
}
let new_index = if dir { selected.2.saturating_add(1) } else { selected.2.saturating_sub(1) };
if new_index < self.displayed_words_query.len() {
return SelectField(self.displayed_words_query.get(new_index).unwrap(), selected.1, new_index);
}
SelectField(selected.0, selected.1, selected.2)
};
let update_horizontal = move |dir: bool| {
let current = selected.1;
let delta = if dir { 1 } else { -1 };
let mut index = FIELDS.iter().position(|&f| f == current).unwrap() as i8 + delta;
if index < 0 {
index = (FIELDS.len() - 1) as i8;
}
if index >= FIELDS.len() as i8 {
index = 0;
}
SelectField(selected.0, FIELDS[index as usize], selected.2)
};
Task::done(RootMessage::Dictionary(match change {
Next => {
update_vertical(true)
}
Previous => {
update_vertical(false)
}
Forward => {
update_horizontal(true)
}
Backward => {
update_horizontal(false)
}
}))
} else {
Task::none()
}
}
} }
Task::none() Task::none()
@@ -375,7 +436,6 @@ impl DictionaryState {
displayed_words_query: words_query, displayed_words_query: words_query,
groups_query, groups_query,
selected_cell: None, selected_cell: None,
hovered_cell: None,
}; };
result.init(); result.init();
@@ -435,7 +495,6 @@ impl DictionaryState {
} }
let mut selection = None; let mut selection = None;
let mut hovered = None;
if let Some(cell) = self.selected_cell.as_ref() { if let Some(cell) = self.selected_cell.as_ref() {
if cell.0 == *word_handle { if cell.0 == *word_handle {
@@ -443,12 +502,6 @@ impl DictionaryState {
} }
} }
if let Some(cell) = self.hovered_cell.as_ref() {
if cell.0 == *word_handle {
hovered = Some(cell.1);
}
}
let word_line_data = WordLineState { let word_line_data = WordLineState {
is_included: self.included.contains(word_handle), is_included: self.included.contains(word_handle),
key: word.key.clone().into(), key: word.key.clone().into(),
@@ -456,7 +509,7 @@ impl DictionaryState {
tags: word.tags.clone().into(), tags: word.tags.clone().into(),
handle: *word_handle, handle: *word_handle,
selection, selection,
hover: hovered, number: i,
}; };
let lazy_line = lazy(word_line_data, Self::word_line); let lazy_line = lazy(word_line_data, Self::word_line);
@@ -499,7 +552,7 @@ impl DictionaryState {
&data.key, &data.key,
data.selection.is_some() && data.selection.unwrap() == BasicWordFields::Key, data.selection.is_some() && data.selection.unwrap() == BasicWordFields::Key,
)) ))
.on_press(SelectField(data.handle, BasicWordFields::Key)) .on_press(SelectField(data.handle, BasicWordFields::Key, data.number)),
); );
line = line.push( line = line.push(
hard_mouse_area(field( hard_mouse_area(field(
@@ -507,7 +560,11 @@ impl DictionaryState {
&data.value, &data.value,
data.selection.is_some() && data.selection.unwrap() == BasicWordFields::Value, data.selection.is_some() && data.selection.unwrap() == BasicWordFields::Value,
)) ))
.on_press(SelectField(data.handle, BasicWordFields::Value)) .on_press(SelectField(
data.handle,
BasicWordFields::Value,
data.number,
)),
); );
line = line.push( line = line.push(
@@ -516,7 +573,7 @@ impl DictionaryState {
&data.tags, &data.tags,
data.selection.is_some() && data.selection.unwrap() == BasicWordFields::Tags, data.selection.is_some() && data.selection.unwrap() == BasicWordFields::Tags,
)) ))
.on_press(SelectField(data.handle, BasicWordFields::Tags)) .on_press(SelectField(data.handle, BasicWordFields::Tags, data.number)),
); );
let line_button = || { let line_button = || {
@@ -715,6 +772,45 @@ impl DictionaryState {
} }
} }
impl KeyPressedPage for DictionaryState {
fn press(&mut self, message: &Event) -> Task<RootMessage> {
if let Event::KeyPressed {
key: _,
modified_key: modifier,
physical_key: pk,
location: _,
modifiers: _,
text: _,
repeat: _,
} = message
&& let Code(code) = pk
{
return match code {
keyboard::key::Code::ArrowDown => Task::done(RootMessage::Dictionary(
ChangeSelectedField(SelectionChangeDirection::Next),
)),
keyboard::key::Code::ArrowUp => Task::done(RootMessage::Dictionary(
ChangeSelectedField(SelectionChangeDirection::Previous),
)),
keyboard::key::Code::Tab => {
if let Named(keyboard::key::Named::Alt) = modifier {
Task::done(RootMessage::Dictionary(ChangeSelectedField(
SelectionChangeDirection::Backward,
)))
} else {
Task::done(RootMessage::Dictionary(ChangeSelectedField(
SelectionChangeDirection::Forward,
)))
}
}
_ => Task::none(),
};
}
Task::none()
}
}
pub fn split_with_coma(ts: &str) -> Vec<String> { pub fn split_with_coma(ts: &str) -> Vec<String> {
ts.split(',') ts.split(',')
.map(|ts| ts.to_lowercase().trim().to_string()) .map(|ts| ts.to_lowercase().trim().to_string())
@@ -730,5 +826,5 @@ struct WordLineState {
tags: Box<str>, tags: Box<str>,
handle: EntityHandle<WordData>, handle: EntityHandle<WordData>,
selection: Option<BasicWordFields>, selection: Option<BasicWordFields>,
hover: Option<BasicWordFields>, number: usize,
} }