summaryrefslogtreecommitdiff
path: root/schist_desktop_gui/src/gui/screens
diff options
context:
space:
mode:
Diffstat (limited to 'schist_desktop_gui/src/gui/screens')
-rw-r--r--schist_desktop_gui/src/gui/screens/files_screen.rs58
-rw-r--r--schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs143
-rw-r--r--schist_desktop_gui/src/gui/screens/files_screen/view_files_screen.rs101
-rw-r--r--schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs3
-rw-r--r--schist_desktop_gui/src/gui/screens/main_screen/view.rs7
5 files changed, 147 insertions, 165 deletions
diff --git a/schist_desktop_gui/src/gui/screens/files_screen.rs b/schist_desktop_gui/src/gui/screens/files_screen.rs
index 68a9556..373b54f 100644
--- a/schist_desktop_gui/src/gui/screens/files_screen.rs
+++ b/schist_desktop_gui/src/gui/screens/files_screen.rs
@@ -3,20 +3,28 @@ mod view_files_screen;
use std::{fmt::Debug, path::PathBuf};
-use crate::DatabaseConnection;
+use crate::{
+ gui::components::{navigation, Navigation, Panel},
+ DatabaseConnection,
+};
pub struct FilesScreen {
- new_file_err: Option<(PathBuf, String)>,
- file_paths: Vec<PathBuf>,
- open_file_err: Option<(PathBuf, String)>,
+ options: Navigation<Panel<OptionId>>,
}
-#[derive(Clone, Debug)]
-pub enum Message {
- ImportFromActualbudget,
+#[derive(Clone, Debug, PartialEq)]
+pub enum OptionId {
NewFile,
- OpenFile(std::path::PathBuf),
PickFile,
+ ImportFromActualbudget,
+ OpenFile(std::path::PathBuf),
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum Message {
+ NavigationMessage(navigation::Message<Panel<OptionId>>),
+ NextOption,
+ PrevOption,
}
#[derive(Debug)]
@@ -27,10 +35,38 @@ pub enum Action {
impl<'a> FilesScreen {
pub fn new(file_paths: &'a [PathBuf]) -> Self {
+ let mut options = vec![
+ Panel::new(OptionId::NewFile, "New file", None, None),
+ Panel::new(OptionId::PickFile, "Open file...", None, None),
+ Panel::new(
+ OptionId::ImportFromActualbudget,
+ "Import from Actualbudget",
+ None,
+ None,
+ ),
+ ];
+ options.extend(
+ file_paths
+ .iter()
+ .map(|file_path| file_panel(file_path, None)),
+ );
+
Self {
- file_paths: file_paths.to_vec(),
- new_file_err: None,
- open_file_err: None,
+ options: Navigation::new(None, options),
}
}
}
+
+fn file_panel<'a>(file_path: &'a PathBuf, err: Option<String>) -> Panel<OptionId> {
+ let file_name = file_path
+ .file_name()
+ .map(|os_str| os_str.to_str().unwrap_or("[invalid Unicode]"))
+ .unwrap_or("[invalid filename]");
+
+ Panel::new(
+ OptionId::OpenFile(file_path.clone()),
+ file_name,
+ file_path.to_str().map(String::from),
+ err.map(|err| format!("Failed to open new file {:#?}: {:#?}", file_path, err)),
+ )
+}
diff --git a/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs b/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs
index 9c45d6e..82ca7b6 100644
--- a/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs
+++ b/schist_desktop_gui/src/gui/screens/files_screen/update_files_screen.rs
@@ -4,34 +4,20 @@ use itertools::Itertools;
use crate::{
database_connection::DatabaseConnectionResult,
dialog::{import_from_actualbudget_dialog, new_file_dialog, pick_file_dialog},
+ gui::components::{navigation, Panel},
paths::get_schist_database_directory,
traits::Component,
DatabaseConnection,
};
-use super::{Action, FilesScreen, Message};
+use super::{Action, FilesScreen, Message, OptionId};
impl<'a> Component<'a, Message, Action> for FilesScreen {
fn update(&mut self, message: Message) -> Action {
match message {
- Message::NewFile => match self.new_file() {
- Some(Ok(file)) => Action::OpenedFile(file),
- Some(Err(_)) | None => Action::None,
- },
-
- Message::OpenFile(file_path) => self
- .open_file(file_path)
- .map_or(Action::None, Action::OpenedFile),
-
- Message::PickFile => match self.pick_file() {
- Some(Ok(file)) => Action::OpenedFile(file),
- Some(Err(_)) | None => Action::None,
- },
-
- Message::ImportFromActualbudget => match self.import_from_actualbudget() {
- Some(Ok(file)) => Action::OpenedFile(file),
- Some(Err(_)) | None => Action::None,
- },
+ Message::NavigationMessage(message) => self.update_options(message),
+ Message::NextOption => self.update_options(navigation::Message::SelectNext),
+ Message::PrevOption => self.update_options(navigation::Message::SelectPrev),
}
}
}
@@ -41,13 +27,12 @@ impl FilesScreen {
&mut self,
) -> Option<Result<DatabaseConnection, (std::path::PathBuf, anyhow::Error)>> {
pick_file_dialog(get_schist_database_directory().ok()).map(|db_connection_result| {
- db_connection_result.ok().inspect_err(|(path, err)| {
- self.open_file_err = Some((path.clone(), err.to_string()));
- rfd::MessageDialog::new()
- .set_title("Failed to import from Actualbudget")
- .set_level(rfd::MessageLevel::Error)
- .set_description(format!("{}", err.chain().join("\n | ")))
- .show();
+ db_connection_result.ok().inspect_err(|(_path, err)| {
+ self.report_err(
+ super::OptionId::PickFile,
+ format!("{}", err.chain().join("\n | ")).as_str(),
+ "Failed to open file",
+ );
})
})
}
@@ -59,12 +44,11 @@ impl FilesScreen {
DatabaseConnection::establish(file_path.clone())
.ok()
.inspect_err(|(_path, err)| {
- self.open_file_err = Some((file_path, err.to_string()));
- rfd::MessageDialog::new()
- .set_title("Failed to import from Actualbudget")
- .set_level(rfd::MessageLevel::Error)
- .set_description(format!("{}", err.chain().join("\n | ")))
- .show();
+ self.report_err(
+ OptionId::OpenFile(file_path),
+ format!("{}", err.chain().join("\n | ")).as_str(),
+ "Failed to open file",
+ );
})
}
@@ -72,13 +56,12 @@ impl FilesScreen {
&mut self,
) -> Option<Result<DatabaseConnection, (std::path::PathBuf, anyhow::Error)>> {
new_file_dialog(get_schist_database_directory().ok()).map(|result| {
- result.ok().inspect_err(|(path, err)| {
- self.new_file_err = Some((path.clone(), err.to_string()));
- rfd::MessageDialog::new()
- .set_title("Failed to import from Actualbudget")
- .set_level(rfd::MessageLevel::Error)
- .set_description(format!("{}", err.chain().join("\n | ")))
- .show();
+ result.ok().inspect_err(|(_path, err)| {
+ self.report_err(
+ OptionId::NewFile,
+ "Failed to create new file",
+ format!("{}", err.chain().join("\n | ")).as_str(),
+ );
})
})
}
@@ -86,13 +69,12 @@ impl FilesScreen {
fn import_from_actualbudget(&mut self) -> Option<anyhow::Result<DatabaseConnection>> {
match import_from_actualbudget_dialog() {
Some(Ok(state)) => self.new_file_with_state(&state),
- Some(Err((path, err))) => Some({
- self.open_file_err = Some((path, err.to_string()));
- rfd::MessageDialog::new()
- .set_title("Failed to import from Actualbudget")
- .set_level(rfd::MessageLevel::Error)
- .set_description(format!("{}", err.chain().join("\n | ")))
- .show();
+ Some(Err((_path, err))) => Some({
+ self.report_err(
+ OptionId::ImportFromActualbudget,
+ "Failed to import from Actualbudget",
+ format!("{}", err.chain().join("\n | ")).as_str(),
+ );
Err(err)
}),
None => None,
@@ -110,16 +92,71 @@ impl FilesScreen {
export_schist_state(state, &mut file.connection).ok()?;
Ok(file)
}),
- Some(Err((path, err))) => Some({
- self.new_file_err = Some((path, err.to_string()));
- rfd::MessageDialog::new()
- .set_title("Failed to import from Actualbudget")
- .set_level(rfd::MessageLevel::Error)
- .set_description(format!("{}", err.chain().join("\n | ")))
- .show();
+ Some(Err((_path, err))) => Some({
+ self.report_err(
+ OptionId::NewFile,
+ format!("{}", err.chain().join("\n | ")).as_str(),
+ "Failed to create new file",
+ );
Err(err)
}),
None => None,
}
}
+
+ fn report_err(&mut self, id: OptionId, err: &str, dialog_title: &str) {
+ let old_panel = self.options.find(|o| o.id == id);
+ let file_panel_with_err = Panel::new(
+ id.clone(),
+ old_panel.as_ref().map(|p| p.name.as_str()).unwrap_or(""),
+ old_panel.as_ref().map(|p| p.small.clone()).flatten(),
+ Some(err.to_string()),
+ );
+ self.options
+ .replace_first(|o| o.id == id, &vec![file_panel_with_err]);
+
+ rfd::MessageDialog::new()
+ .set_title(dialog_title)
+ .set_level(rfd::MessageLevel::Error)
+ .set_description(err)
+ .show();
+ }
+
+ fn update_options(&mut self, message: navigation::Message<Panel<OptionId>>) -> Action {
+ match self.options.update(message) {
+ navigation::Action::ActivateOption(option) => match option {
+ Panel {
+ id: OptionId::NewFile,
+ ..
+ } => match self.new_file() {
+ Some(Ok(file)) => Action::OpenedFile(file),
+ Some(Err(_)) | None => Action::None,
+ },
+
+ Panel {
+ id: OptionId::OpenFile(file_path),
+ ..
+ } => self
+ .open_file(file_path.clone())
+ .map_or(Action::None, Action::OpenedFile),
+
+ Panel {
+ id: OptionId::PickFile,
+ ..
+ } => match self.pick_file() {
+ Some(Ok(file)) => Action::OpenedFile(file),
+ Some(Err(_)) | None => Action::None,
+ },
+
+ Panel {
+ id: OptionId::ImportFromActualbudget,
+ ..
+ } => match self.import_from_actualbudget() {
+ Some(Ok(file)) => Action::OpenedFile(file),
+ Some(Err(_)) | None => Action::None,
+ },
+ },
+ navigation::Action::SelectOption(_) | navigation::Action::None => Action::None,
+ }
+ }
}
diff --git a/schist_desktop_gui/src/gui/screens/files_screen/view_files_screen.rs b/schist_desktop_gui/src/gui/screens/files_screen/view_files_screen.rs
index 3cbd939..8ff9f63 100644
--- a/schist_desktop_gui/src/gui/screens/files_screen/view_files_screen.rs
+++ b/schist_desktop_gui/src/gui/screens/files_screen/view_files_screen.rs
@@ -1,106 +1,9 @@
-use std::path::PathBuf;
-
-use iced::widget::column;
-
-use crate::{
- gui::components::{panel_button, Text},
- style::*,
- traits::Viewable,
-};
+use crate::traits::Viewable;
use super::{FilesScreen, Message};
impl<'a> Viewable<'a, Message> for FilesScreen {
fn view(&'a self) -> iced::Element<'a, Message> {
- let mut cols = Vec::new();
- if let Some(col) = self.view_new_file_err() {
- cols.push(col);
- };
- if let Some(col) = self.view_open_file_err() {
- cols.push(col);
- };
- cols.push(self.view_new_file_button());
- cols.push(self.view_open_file_button());
- cols.push(self.view_import_from_actualbudget_button());
- cols.append(&mut self.view_files());
- column(cols).padding(SPACING_LG).spacing(SPACING_MD).into()
- }
-}
-
-impl<'a> FilesScreen {
- fn view_new_file_err(&'a self) -> Option<iced::Element<'a, Message>> {
- self.new_file_err.as_ref().map(|(path, err)| {
- let msg = format!("Failed to open new file {:#?}: {:#?}", path, err);
- Text::new(&msg).danger().into()
- })
- }
-
- fn view_open_file_err(&'a self) -> Option<iced::Element<'a, Message>> {
- self.open_file_err
- .as_ref()
- .filter(|(file_path, _err)| !self.file_paths.iter().any(|fp| *fp == *file_path))
- .map(|(file_path, err)| {
- let msg = format!("Failed to open new file {:#?}: {:#?}", file_path, err);
- Text::new(&msg).danger().into()
- })
- }
-
- fn view_files(&'a self) -> Vec<iced::Element<'a, Message>> {
- self.file_paths
- .iter()
- .map(|f| {
- view_file(
- f,
- self.open_file_err
- .clone()
- .filter(|(file, _err)| *file == *f)
- .map(|(_file, err)| err),
- )
- })
- .collect()
- }
-
- fn view_new_file_button(&'a self) -> iced::Element<'a, Message> {
- panel_button::<Text, Message>(Text::default("New file").into(), Message::NewFile).into()
- }
-
- fn view_open_file_button(&'a self) -> iced::Element<'a, Message> {
- panel_button::<Text, Message>(Text::default("Open file").into(), Message::PickFile).into()
- }
-
- fn view_import_from_actualbudget_button(&'a self) -> iced::Element<'a, Message> {
- panel_button::<Text, Message>(
- Text::default("Import from Actualbudget").into(),
- Message::ImportFromActualbudget,
- )
- .into()
+ self.options.view().map(Message::NavigationMessage)
}
}
-
-fn view_file<'a>(file_path: &'a PathBuf, err: Option<String>) -> iced::Element<'a, Message> {
- let mut cols: Vec<iced::Element<'a, Message>> = err
- .map(|err| {
- let msg = format!("Failed to open new file {:#?}: {:#?}", file_path, err);
- vec![Text::new(&msg).danger().small().into()]
- })
- .unwrap_or_else(Vec::new);
-
- cols.push(
- Text::default(file_path.to_str().unwrap_or(""))
- .small()
- .weak()
- .into(),
- );
-
- cols.push(
- Text::default(
- file_path
- .file_name()
- .map(|os_str| os_str.to_str().unwrap_or("[invalid Unicode]"))
- .unwrap_or("[invalid filename]"),
- )
- .into(),
- );
-
- panel_button(column(cols), Message::OpenFile(file_path.clone())).into()
-}
diff --git a/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs b/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs
index cddec32..5ed4b30 100644
--- a/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs
+++ b/schist_desktop_gui/src/gui/screens/main_screen/main_screen.rs
@@ -95,7 +95,8 @@ impl<'a> Component<'a, Message, Action> for MainScreen {
}
Message::ViewNavigationMessage(message) => match self.view_navigation.update(message) {
- navigation::Action::SelectOption(view) => {
+ navigation::Action::ActivateOption(view)
+ | navigation::Action::SelectOption(view) => {
self.active_view = view;
Action::None
}
diff --git a/schist_desktop_gui/src/gui/screens/main_screen/view.rs b/schist_desktop_gui/src/gui/screens/main_screen/view.rs
index 5f2df4e..2ff5753 100644
--- a/schist_desktop_gui/src/gui/screens/main_screen/view.rs
+++ b/schist_desktop_gui/src/gui/screens/main_screen/view.rs
@@ -1,6 +1,6 @@
use iced::{widget::Text, Element};
-use crate::gui::components::navigation;
+use crate::{gui::components::navigation, traits::Focusable};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum View {
@@ -46,3 +46,8 @@ impl<'a> From<&'a View> for Element<'a, navigation::Message<View>> {
Text::new(val.name()).into()
}
}
+
+impl Focusable for View {
+ fn focus(&mut self) {}
+ fn unfocus(&mut self) {}
+}