summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorpml68 <contact@pml68.dev>2025-06-16 00:27:57 +0200
committerpml68 <contact@pml68.dev>2025-06-16 00:27:57 +0200
commiteff665a59aa5ce1efc59e567dadc91fef1ec0366 (patch)
treec29611429add80b9eb13e21818fe3c10d871711e /src
parentstyle: `ḋialogs` -> `dialog` (diff)
downloadiced-builder-eff665a59aa5ce1efc59e567dadc91fef1ec0366.tar.gz
feat: automatically save config updates
Diffstat (limited to '')
-rw-r--r--src/config.rs56
-rw-r--r--src/error.rs12
-rw-r--r--src/main.rs44
-rw-r--r--src/panes/code_view.rs4
-rw-r--r--src/panes/designer_view.rs5
-rw-r--r--src/types.rs22
6 files changed, 101 insertions, 42 deletions
diff --git a/src/config.rs b/src/config.rs
index 8e2a63b..537cf66 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -14,8 +14,15 @@ use crate::{Error, environment};
#[derive(Debug, Clone, Default)]
pub struct Config {
- appearance: Appearance,
- last_project: Option<PathBuf>,
+ pub appearance: Appearance,
+ pub last_project: Option<PathBuf>,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+struct ConfigRepr {
+ #[serde(default)]
+ pub theme: String,
+ pub last_project: Option<PathBuf>,
}
impl Config {
@@ -57,40 +64,20 @@ impl Config {
pub async fn load() -> Result<Self, Error> {
use tokio::fs;
- use tokio::io::AsyncWriteExt;
-
- #[derive(Deserialize, Serialize)]
- pub struct Configuration {
- #[serde(default)]
- pub theme: String,
- pub last_project: Option<PathBuf>,
- }
let path = Self::config_file_path();
if !path.try_exists()? {
- let mut config = fs::File::create(path)
+ Self::default()
+ .save()
.await
- .expect("expected permissions to create config file");
- let default_config = Configuration {
- theme: Theme::default().to_string(),
- last_project: None,
- };
-
- config
- .write_all(
- toml::to_string_pretty(&default_config)
- .expect("stringify default configuration")
- .as_bytes(),
- )
- .await
- .expect("expected config write operation to succeed");
+ .expect("expected permission to write config file");
return Err(Error::ConfigMissing);
}
let content = fs::read_to_string(path).await?;
- let Configuration {
+ let ConfigRepr {
theme,
last_project,
} = toml::from_str(content.as_ref())?;
@@ -146,4 +133,21 @@ impl Config {
all: all.into(),
})
}
+
+ pub async fn save(self) -> Result<(), Error> {
+ use tokio::fs;
+ use tokio::io::AsyncWriteExt;
+
+ let mut file = fs::File::create(Self::config_file_path()).await?;
+
+ let config = ConfigRepr {
+ theme: self.appearance.selected.to_string(),
+ last_project: self.last_project,
+ };
+
+ file.write_all(toml::to_string_pretty(&config)?.as_bytes())
+ .await?;
+
+ Ok(())
+ }
}
diff --git a/src/error.rs b/src/error.rs
index 8c405b3..b98d678 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -7,13 +7,15 @@ use thiserror::Error;
#[derive(Debug, Clone, Error)]
#[error(transparent)]
pub enum Error {
- IO(Arc<io::Error>),
+ Io(Arc<io::Error>),
#[error("Config file does not exist, so a default one was created")]
ConfigMissing,
#[error("JSON parsing error: {0}")]
- SerdeJSON(Arc<serde_json::Error>),
+ Json(Arc<serde_json::Error>),
#[error("TOML parsing error: {0}")]
- SerdeTOML(#[from] toml::de::Error),
+ TomlDe(#[from] toml::de::Error),
+ #[error("TOML serialization error: {0}")]
+ TomlSer(#[from] toml::ser::Error),
RustFmt(Arc<rust_format::Error>),
#[error("The element tree contains no matching element")]
NonExistentElement,
@@ -25,13 +27,13 @@ pub enum Error {
impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
- Self::IO(Arc::new(value))
+ Self::Io(Arc::new(value))
}
}
impl From<serde_json::Error> for Error {
fn from(value: serde_json::Error) -> Self {
- Self::SerdeJSON(Arc::new(value))
+ Self::Json(Arc::new(value))
}
}
diff --git a/src/main.rs b/src/main.rs
index d0dbd9e..f9352fc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -25,7 +25,9 @@ use iced_anim::transition::Easing;
use iced_anim::{Animated, Animation};
use material_theme::Theme;
use panes::{code_view, designer_view, element_list};
-use types::{Action, DesignerPane, Element, Message, Panes, Project};
+use types::{
+ Action, ConfigChangeType, DesignerPane, Element, Message, Panes, Project,
+};
fn main() -> iced::Result {
let version = std::env::args()
@@ -143,13 +145,38 @@ impl IcedBuilder {
}
Err(error) => self.dialog = Dialog::error(error),
},
- Message::SwitchTheme(event) => self.theme.update(event),
+ Message::ConfigWrite(result) => {
+ if let Err(error) = result {
+ self.dialog = Dialog::error(error);
+ }
+ }
+ Message::SaveConfigChanges(change) => {
+ match change {
+ ConfigChangeType::LastProject => {
+ self.config.last_project = self.project_path.clone();
+ }
+ ConfigChangeType::SelectedTheme => {
+ self.config.appearance.selected =
+ self.theme.target().clone();
+ }
+ }
+
+ return Task::perform(
+ self.config.clone().save(),
+ Message::ConfigWrite,
+ );
+ }
+ Message::SwitchTheme(event) => {
+ self.theme.update(event);
+
+ return self.update(ConfigChangeType::SelectedTheme.into());
+ }
Message::CopyCode => {
return clipboard::write(self.editor_content.text());
}
- Message::SwitchPage(page) => self.designer_page = page,
+ Message::SwitchPane(pane) => self.designer_page = pane,
Message::EditorAction(action) => {
- if let text_editor::Action::Scroll { lines: _ } = action {
+ if matches!(action, text_editor::Action::Scroll { .. }) {
self.editor_content.perform(action);
}
}
@@ -318,7 +345,11 @@ impl IcedBuilder {
Ok((path, project)) => {
self.project = project;
self.project_path = Some(path);
- return self.update(Message::RefreshEditorContent);
+
+ return Task::done(
+ ConfigChangeType::LastProject.into(),
+ )
+ .chain(Task::done(Message::RefreshEditorContent));
}
Err(error) => self.dialog = Dialog::error(error),
};
@@ -352,6 +383,9 @@ impl IcedBuilder {
Ok(path) => {
self.project_path = Some(path);
self.is_dirty = false;
+
+ return self
+ .update(ConfigChangeType::LastProject.into());
}
Err(error) => self.dialog = Dialog::error(error),
}
diff --git a/src/panes/code_view.rs b/src/panes/code_view.rs
index 5999b8f..b7aa760 100644
--- a/src/panes/code_view.rs
+++ b/src/panes/code_view.rs
@@ -43,7 +43,7 @@ pub fn view(
tip::Position::FollowCursor
),
button("Switch to Designer view")
- .on_press(Message::SwitchPage(DesignerPane::DesignerView))
+ .on_press(DesignerPane::DesignerView.into())
]
.spacing(20)
.align_y(Alignment::Center),
@@ -57,7 +57,7 @@ pub fn view(
tip::Position::FollowCursor
),
button(icon::switch())
- .on_press(Message::SwitchPage(DesignerPane::DesignerView))
+ .on_press(DesignerPane::DesignerView.into())
]
.spacing(20)
.align_y(Alignment::Center),
diff --git a/src/panes/designer_view.rs b/src/panes/designer_view.rs
index 0255b40..1525993 100644
--- a/src/panes/designer_view.rs
+++ b/src/panes/designer_view.rs
@@ -38,12 +38,11 @@ pub fn view<'a>(
.controls(pane_grid::Controls::dynamic(
row![
button("Switch to Code view")
- .on_press(Message::SwitchPage(DesignerPane::CodeView),)
+ .on_press(DesignerPane::CodeView.into(),)
]
.align_y(Alignment::Center),
row![
- button(icon::switch())
- .on_press(Message::SwitchPage(DesignerPane::CodeView),)
+ button(icon::switch()).on_press(DesignerPane::CodeView.into(),)
]
.align_y(Alignment::Center),
))
diff --git a/src/types.rs b/src/types.rs
index dfb85d8..64f10d0 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -22,9 +22,11 @@ pub type Element<'a, Message> = iced::Element<'a, Message, Theme>;
#[derive(Debug, Clone)]
pub enum Message {
ConfigLoad(Result<Config, Error>),
+ ConfigWrite(Result<(), Error>),
+ SaveConfigChanges(ConfigChangeType),
SwitchTheme(Event<Theme>),
CopyCode,
- SwitchPage(DesignerPane),
+ SwitchPane(DesignerPane),
EditorAction(text_editor::Action),
RefreshEditorContent,
DropNewElement(ElementName, iced::Point, iced::Rectangle),
@@ -59,3 +61,21 @@ pub enum DesignerPane {
DesignerView,
CodeView,
}
+
+impl From<DesignerPane> for Message {
+ fn from(pane: DesignerPane) -> Self {
+ Self::SwitchPane(pane)
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum ConfigChangeType {
+ LastProject,
+ SelectedTheme,
+}
+
+impl From<ConfigChangeType> for Message {
+ fn from(change: ConfigChangeType) -> Self {
+ Self::SaveConfigChanges(change)
+ }
+}