diff options
| author | pml68 <contact@pml68.dev> | 2025-01-04 14:25:14 +0100 |
|---|---|---|
| committer | pml68 <contact@pml68.dev> | 2025-01-04 14:25:14 +0100 |
| commit | e8d36bd018177e4ccc0ae27ce8f21984d31d948a (patch) | |
| tree | 7204d3bbfb5831d1e6d920b1179176898cb52c6e /iced_builder/src/types | |
| parent | refactor: apply clippy suggestions (diff) | |
| download | iced-builder-e8d36bd018177e4ccc0ae27ce8f21984d31d948a.tar.gz | |
feat: add custom theme codegen for `Project`
Diffstat (limited to 'iced_builder/src/types')
| -rw-r--r-- | iced_builder/src/types/element_name.rs | 12 | ||||
| -rw-r--r-- | iced_builder/src/types/project.rs | 73 | ||||
| -rwxr-xr-x | iced_builder/src/types/rendered_element.rs | 33 |
3 files changed, 75 insertions, 43 deletions
diff --git a/iced_builder/src/types/element_name.rs b/iced_builder/src/types/element_name.rs index e172227..0e8aa65 100644 --- a/iced_builder/src/types/element_name.rs +++ b/iced_builder/src/types/element_name.rs @@ -3,13 +3,13 @@ use serde::{Deserialize, Serialize}; use super::rendered_element::{ button, column, container, image, row, svg, text, Action, RenderedElement, }; -use crate::{Error, Result}; +use crate::Error; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ElementName { Text(String), Button(String), - SVG(String), + Svg(String), Image(String), Container, Row, @@ -20,7 +20,7 @@ impl ElementName { pub const ALL: &'static [Self; 7] = &[ Self::Text(String::new()), Self::Button(String::new()), - Self::SVG(String::new()), + Self::Svg(String::new()), Self::Image(String::new()), Self::Container, Self::Row, @@ -31,11 +31,11 @@ impl ElementName { &self, element_tree: Option<&mut RenderedElement>, action: Action, - ) -> Result<Option<RenderedElement>> { + ) -> Result<Option<RenderedElement>, Error> { let element = match self { Self::Text(_) => text(""), Self::Button(_) => button(""), - Self::SVG(_) => svg(""), + Self::Svg(_) => svg(""), Self::Image(_) => image(""), Self::Container => container(None), Self::Row => row(None), @@ -75,7 +75,7 @@ impl std::fmt::Display for ElementName { match self { Self::Text(_) => "Text", Self::Button(_) => "Button", - Self::SVG(_) => "SVG", + Self::Svg(_) => "SVG", Self::Image(_) => "Image", Self::Container => "Container", Self::Row => "Row", diff --git a/iced_builder/src/types/project.rs b/iced_builder/src/types/project.rs index 6f3b7ed..6479b1d 100644 --- a/iced_builder/src/types/project.rs +++ b/iced_builder/src/types/project.rs @@ -1,18 +1,23 @@ use std::path::{Path, PathBuf}; +extern crate fxhash; +use fxhash::FxHashMap; use iced::Theme; -use rust_format::{Config, Edition, Formatter, RustFmt}; +use rust_format::{Edition, Formatter, RustFmt}; use serde::{Deserialize, Serialize}; use super::rendered_element::RenderedElement; -use crate::theme::theme_from_str; -use crate::{Error, Result}; +use crate::config::Config; +use crate::theme::{theme_from_str, theme_index, theme_to_string}; +use crate::Error; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Project { pub title: Option<String>, pub theme: Option<String>, pub element_tree: Option<RenderedElement>, + #[serde(skip)] + theme_cache: FxHashMap<String, String>, } impl Default for Project { @@ -27,24 +32,42 @@ impl Project { title: None, theme: None, element_tree: None, + theme_cache: FxHashMap::default(), } } - pub fn get_theme(&self, config: &crate::config::Config) -> Theme { + pub fn get_theme(&self, config: &Config) -> Theme { match &self.theme { Some(theme) => theme_from_str(Some(config), theme), - None => Theme::Dark, + None => Theme::default(), } } - pub async fn from_path(path: PathBuf) -> Result<(PathBuf, Self)> { + fn theme_code(&mut self, theme: &Theme) -> String { + let theme_name = theme.to_string(); + if theme_index(&theme_name, Theme::ALL).is_none() { + self.theme_cache + .entry(theme_name) + .or_insert(theme_to_string(theme)) + .to_string() + } else { + theme_name.replace(" ", "") + } + } + + pub async fn from_path( + path: PathBuf, + config: Config, + ) -> Result<(PathBuf, Self), Error> { let contents = tokio::fs::read_to_string(&path).await?; - let element: Self = serde_json::from_str(&contents)?; + let mut project: Self = serde_json::from_str(&contents)?; + + let _ = project.theme_code(&project.get_theme(&config)); - Ok((path, element)) + Ok((path, project)) } - pub async fn from_file() -> Result<(PathBuf, Self)> { + pub async fn from_file(config: Config) -> Result<(PathBuf, Self), Error> { let picked_file = rfd::AsyncFileDialog::new() .set_title("Open a JSON file...") .add_filter("*.json, *.JSON", &["json", "JSON"]) @@ -54,10 +77,13 @@ impl Project { let path = picked_file.path().to_owned(); - Self::from_path(path).await + Self::from_path(path, config).await } - pub async fn write_to_file(self, path: Option<PathBuf>) -> Result<PathBuf> { + pub async fn write_to_file( + self, + path: Option<PathBuf>, + ) -> Result<PathBuf, Error> { let path = if let Some(p) = path { p } else { @@ -78,16 +104,25 @@ impl Project { Ok(path) } - pub fn app_code(&self, config: &crate::config::Config) -> Result<String> { + pub fn app_code(&mut self, config: &Config) -> Result<String, Error> { match self.element_tree { Some(ref element_tree) => { let (imports, view) = element_tree.codegen(); - let mut app_code = - format!("use iced::{{widget::{{{imports}}},Element}};"); - - app_code = format!( + let theme = self.get_theme(config); + let theme_code = self.theme_code(&theme); + let mut theme_imports = ""; + if theme_index(&theme.to_string(), Theme::ALL).is_none() { + if theme_code.contains("Extended") { + theme_imports = "use iced::{{color,theme::{{Palette,palette::{{Extended,Background,Primary,Secondary,Success,Danger,Pair}}}}}};\n"; + } else { + theme_imports = "use iced::{{color,theme::Palette}};\n" + } + } + + let app_code = format!( r#"// Automatically generated by iced Builder - {app_code} + use iced::{{widget::{{{imports}}},Element}}; + {theme_imports} fn main() -> iced::Result {{ iced::application("{}", State::update, State::view).theme(State::theme).run() @@ -114,9 +149,9 @@ impl Project { Some(ref t) => t, None => "New app", }, - self.get_theme(config).to_string().replace(" ", "") + theme_code ); - let config = Config::new_str() + let config = rust_format::Config::new_str() .edition(Edition::Rust2021) .option("trailing_comma", "Never") .option("imports_granularity", "Crate"); diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index d7efed1..177b43d 100755 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -7,7 +7,7 @@ use uuid::Uuid; use super::ElementName; use crate::types::Message; -use crate::Result; +use crate::Error; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RenderedElement { @@ -62,7 +62,7 @@ impl RenderedElement { child_element: &RenderedElement, ) -> Option<&mut Self> { if child_element == self { - Some(self) + return Some(self); } else if self.child_elements.is_some() { if self .child_elements @@ -71,20 +71,17 @@ impl RenderedElement { .contains(child_element) { return Some(self); - } else { - if let Some(child_elements) = self.child_elements.as_mut() { - for element in child_elements { - let element = element.find_parent(child_element); - if element.is_some() { - return element; - } + } + if let Some(child_elements) = self.child_elements.as_mut() { + for element in child_elements { + let element = element.find_parent(child_element); + if element.is_some() { + return element; } } - return None; } - } else { - return None; } + return None; } pub fn is_parent(&self) -> bool { @@ -128,7 +125,7 @@ impl RenderedElement { &self, element_tree: Option<&mut RenderedElement>, action: Action, - ) -> Result<()> { + ) -> Result<(), Error> { let element_tree = element_tree.unwrap(); match action { @@ -175,12 +172,12 @@ impl RenderedElement { self } - pub fn as_element<'a>(self) -> Element<'a, Message> { + pub fn into_element<'a>(self) -> Element<'a, Message> { let mut children = widget::column![]; if let Some(els) = self.child_elements.clone() { for el in els { - children = children.push(el.clone().as_element()); + children = children.push(el.clone().into_element()); } } iced_drop::droppable( @@ -263,7 +260,7 @@ impl RenderedElement { imports = format!("{imports}image,"); view = format!("{view}\nimage(\"{path}\"){options}"); } - ElementName::SVG(path) => { + ElementName::Svg(path) => { imports = format!("{imports}svg,"); view = format!("{view}\nsvg(\"{path}\"){options}"); } @@ -337,7 +334,7 @@ impl<'a> From<RenderedElement> for Element<'a, Message> { widget::button(widget::text(s)).into() } } - ElementName::SVG(p) => widget::svg(p).into(), + ElementName::Svg(p) => widget::svg(p).into(), ElementName::Image(p) => widget::image(p).into(), ElementName::Container => { widget::container(if child_elements.len() == 1 { @@ -457,7 +454,7 @@ pub fn button(text: &str) -> RenderedElement { } pub fn svg(path: &str) -> RenderedElement { - RenderedElement::new(ElementName::SVG(path.to_owned())) + RenderedElement::new(ElementName::Svg(path.to_owned())) } pub fn image(path: &str) -> RenderedElement { |
