New selected field movement
This commit is contained in:
1 parent
47bc83d4ae
commit
8fe4f3b8f4
3 files changed
+120
-19
No files matched your search
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user