diff --git a/res/icons/bundled/harddisk-symbolic.svg b/res/icons/bundled/harddisk-symbolic.svg
new file mode 100644
index 0000000..75214bd
--- /dev/null
+++ b/res/icons/bundled/harddisk-symbolic.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/res/icons/bundled/timer-sand-symbolic.svg b/res/icons/bundled/timer-sand-symbolic.svg
new file mode 100644
index 0000000..cab8855
--- /dev/null
+++ b/res/icons/bundled/timer-sand-symbolic.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/src/app.rs b/src/app.rs
index bd08a1f..bee1259 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -5,7 +5,7 @@ use std::collections::{HashMap, VecDeque};
use std::path::PathBuf;
use std::{env, process};
-use cosmic::app::{Command, Core};
+use cosmic::app::{message, Command, Core};
use cosmic::iced::alignment::{Horizontal, Vertical};
use cosmic::iced::{event, keyboard::Event as KeyEvent, window, Event, Subscription};
use cosmic::iced_core::keyboard::{Key, Modifiers};
@@ -24,7 +24,10 @@ use cosmic::{widget, Application, Apply, Element};
use crate::app::config::{AppTheme, Repository, CONFIG_VERSION};
use crate::fl;
+use self::icon_cache::IconCache;
+
pub mod config;
+pub mod icon_cache;
pub mod menu;
pub mod settings;
@@ -62,7 +65,7 @@ pub enum Message {
Modifiers(Modifiers),
WindowClose,
WindowNew,
- CreateRepository(String),
+ Repository(RepositoryAction),
CreateSnapshot,
OpenCreateRepositoryDialog,
OpenCreateSnapshotDialog,
@@ -70,6 +73,13 @@ pub enum Message {
DeleteSnapshotDialog,
}
+#[derive(Debug, Clone)]
+pub enum RepositoryAction {
+ Init(String),
+ Created(Repository),
+ Error(String),
+}
+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ContextPage {
About,
@@ -183,11 +193,17 @@ impl App {
.into()
}
- fn create_nav_item(&mut self, repository: Repository) -> EntityMut {
+ fn create_nav_item(
+ &mut self,
+ repository: Repository,
+ icon: &'static str,
+ ) -> EntityMut {
self.nav_model
.insert()
+ .icon(IconCache::get(icon, 18))
.text(repository.name.clone())
.data(repository.clone())
+ .activate()
}
}
@@ -235,7 +251,7 @@ impl Application for App {
let repositories = app.config.repositories.clone();
for repository in repositories {
- app.create_nav_item(repository);
+ app.create_nav_item(repository, "harddisk-symbolic");
}
(app, Command::none())
@@ -425,24 +441,42 @@ impl Application for App {
Message::OpenCreateSnapshotDialog => {
self.dialog_pages.push_back(DialogPage::CreateSnapshot);
}
- Message::CreateRepository(path) => match crate::backup::init(&path, "password") {
- Ok(_) => {
- let path = PathBuf::from(&path);
- let name = path
+ Message::Repository(state) => match state {
+ RepositoryAction::Init(path) => {
+ let init_path = path.clone();
+ let name = PathBuf::from(&path)
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string();
+ let repository = Repository {
+ name,
+ path: PathBuf::from(&path),
+ };
+ self.create_nav_item(repository.clone(), "timer-sand-symbolic");
+ return Command::perform(
+ async move { crate::backup::init(&init_path, "password") },
+ |result| match result {
+ Ok(_) => message::app(Message::Repository(RepositoryAction::Created(
+ repository,
+ ))),
+ Err(e) => message::app(Message::Repository(RepositoryAction::Error(
+ e.to_string(),
+ ))),
+ },
+ );
+ }
+ RepositoryAction::Created(repository) => {
+ if self.nav_model.active_data::().is_some() {
+ let entity = self.nav_model.active();
+ self.nav_model
+ .icon_set(entity, IconCache::get("harddisk-symbolic", 18));
+ }
let mut repositories = self.config.repositories.clone();
- let repository = Repository { name, path };
- repositories.push(repository.clone());
- self.create_nav_item(repository);
+ repositories.push(repository);
config_set!(repositories, repositories);
}
- Err(e) => {
- // TODO: Show error to user.
- eprintln!("failed to create repository: {}", e)
- }
+ RepositoryAction::Error(error) => log::error!("{}", error),
},
Message::DeleteRepositoryDialog => {
println!("Deleting repository");
@@ -470,8 +504,8 @@ impl Application for App {
Message::DialogComplete => {
if let Some(dialog_page) = self.dialog_pages.pop_front() {
match dialog_page {
- DialogPage::CreateRepository(name) => {
- return self.update(Message::CreateRepository(name));
+ DialogPage::CreateRepository(path) => {
+ return self.update(Message::Repository(RepositoryAction::Init(path)));
}
DialogPage::CreateSnapshot => {
return self.update(Message::CreateSnapshot);
diff --git a/src/app/icon_cache.rs b/src/app/icon_cache.rs
new file mode 100644
index 0000000..97e2202
--- /dev/null
+++ b/src/app/icon_cache.rs
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-3.0-only
+
+use cosmic::widget::icon;
+use std::collections::HashMap;
+use std::sync::{Mutex, OnceLock};
+
+pub(crate) static ICON_CACHE: OnceLock> = OnceLock::new();
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct IconCacheKey {
+ name: &'static str,
+ size: u16,
+}
+
+pub struct IconCache {
+ cache: HashMap,
+}
+
+impl IconCache {
+ pub fn new() -> Self {
+ let mut cache = HashMap::new();
+
+ macro_rules! bundle {
+ ($name:expr, $size:expr) => {
+ let data: &'static [u8] =
+ include_bytes!(concat!("../../res/icons/bundled/", $name, ".svg"));
+ cache.insert(
+ IconCacheKey {
+ name: $name,
+ size: $size,
+ },
+ icon::from_svg_bytes(data).symbolic(true),
+ );
+ };
+ }
+
+ bundle!("timer-sand-symbolic", 18);
+ bundle!("harddisk-symbolic", 18);
+
+ Self { cache }
+ }
+
+ fn get_icon(&mut self, name: &'static str, size: u16) -> icon::Icon {
+ let handle = self
+ .cache
+ .entry(IconCacheKey { name, size })
+ .or_insert_with(|| icon::from_name(name).size(size).handle())
+ .clone();
+ icon::icon(handle).size(size)
+ }
+
+ pub fn get(name: &'static str, size: u16) -> icon::Icon {
+ let mut icon_cache = ICON_CACHE.get().unwrap().lock().unwrap();
+ icon_cache.get_icon(name, size)
+ }
+}
diff --git a/src/app/settings.rs b/src/app/settings.rs
index be31310..bd24c87 100644
--- a/src/app/settings.rs
+++ b/src/app/settings.rs
@@ -1,10 +1,14 @@
+use std::sync::Mutex;
+
use super::config::CosmicBackupsConfig;
+use super::icon_cache::{IconCache, ICON_CACHE};
use crate::app::Flags;
use cosmic::app::Settings;
use cosmic::iced::{Limits, Size};
pub fn init() -> (Settings, Flags) {
set_logger();
+ set_icon_cache();
let settings = get_app_settings();
let flags = get_flags();
(settings, flags)
@@ -25,6 +29,10 @@ pub fn set_logger() {
tracing_subscriber::fmt().json().init();
}
+pub fn set_icon_cache() {
+ ICON_CACHE.get_or_init(|| Mutex::new(IconCache::new()));
+}
+
pub fn get_flags() -> Flags {
let (config_handler, config) = (
CosmicBackupsConfig::config_handler(),
diff --git a/src/backup/restore.rs b/src/backup/restore.rs
index ce694d0..0aa86ee 100644
--- a/src/backup/restore.rs
+++ b/src/backup/restore.rs
@@ -2,6 +2,7 @@ use rustic_backend::BackendOptions;
use rustic_core::{LocalDestination, LsOptions, Repository, RepositoryOptions, RestoreOptions};
use std::error::Error;
+#[allow(dead_code)]
pub fn restore(
repository: &str,
password: &str,