diff options
Diffstat (limited to 'iced_builder/src/types')
| -rw-r--r-- | iced_builder/src/types/element_name.rs | 85 | ||||
| -rw-r--r-- | iced_builder/src/types/project.rs | 165 | ||||
| -rwxr-xr-x | iced_builder/src/types/rendered_element.rs | 468 |
3 files changed, 0 insertions, 718 deletions
diff --git a/iced_builder/src/types/element_name.rs b/iced_builder/src/types/element_name.rs deleted file mode 100644 index 2687673..0000000 --- a/iced_builder/src/types/element_name.rs +++ /dev/null @@ -1,85 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use super::rendered_element::{ - button, column, container, image, row, svg, text, Action, RenderedElement, -}; -use crate::Error; - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub enum ElementName { - Text(String), - Button(String), - Svg(String), - Image(String), - Container, - Row, - Column, -} - -impl ElementName { - pub const ALL: &'static [Self; 7] = &[ - Self::Text(String::new()), - Self::Button(String::new()), - Self::Svg(String::new()), - Self::Image(String::new()), - Self::Container, - Self::Row, - Self::Column, - ]; - - pub fn handle_action( - &self, - element_tree: Option<&mut RenderedElement>, - action: Action, - ) -> Result<Option<RenderedElement>, Error> { - let element = match self { - Self::Text(_) => text(""), - Self::Button(_) => button(""), - Self::Svg(_) => svg(""), - Self::Image(_) => image(""), - Self::Container => container(None), - Self::Row => row(None), - Self::Column => column(None), - }; - match action { - Action::Stop | Action::Drop => Ok(None), - Action::AddNew => Ok(Some(element)), - Action::PushFront(id) => { - element_tree - .ok_or("the action was of kind `PushFront`, but no element tree was provided.")? - .find_by_id(id) - .ok_or(Error::NonExistentElement)? - .push_front(&element); - Ok(None) - } - Action::InsertAfter(parent_id, child_id) => { - element_tree - .ok_or( - "the action was of kind `InsertAfter`, but no element tree was provided.", - )? - .find_by_id(parent_id) - .ok_or(Error::NonExistentElement)? - .insert_after(child_id, &element); - Ok(None) - } - } - } -} - -impl std::fmt::Display for ElementName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::Text(_) => "Text", - Self::Button(_) => "Button", - Self::Svg(_) => "SVG", - Self::Image(_) => "Image", - Self::Container => "Container", - Self::Row => "Row", - Self::Column => "Column", - } - ) - } -} diff --git a/iced_builder/src/types/project.rs b/iced_builder/src/types/project.rs deleted file mode 100644 index 27c576b..0000000 --- a/iced_builder/src/types/project.rs +++ /dev/null @@ -1,165 +0,0 @@ -use std::path::{Path, PathBuf}; - -extern crate fxhash; -use fxhash::FxHashMap; -use iced::Theme; -use rust_format::{Edition, Formatter, RustFmt}; -use serde::{Deserialize, Serialize}; - -use super::rendered_element::RenderedElement; -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 { - fn default() -> Self { - Self::new() - } -} - -impl Project { - pub fn new() -> Self { - Self { - title: None, - theme: None, - element_tree: None, - theme_cache: FxHashMap::default(), - } - } - - pub fn get_theme(&self, config: &Config) -> Theme { - match &self.theme { - Some(theme) => theme_from_str(Some(config), theme), - None => Theme::default(), - } - } - - 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 mut project: Self = serde_json::from_str(&contents)?; - - let _ = project.theme_code(&project.get_theme(&config)); - - Ok((path, project)) - } - - 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"]) - .pick_file() - .await - .ok_or(Error::DialogClosed)?; - - let path = picked_file.path().to_owned(); - - Self::from_path(path, config).await - } - - pub async fn write_to_file( - self, - path: Option<PathBuf>, - ) -> Result<PathBuf, Error> { - let path = if let Some(p) = path { - p - } else { - rfd::AsyncFileDialog::new() - .set_title("Save to JSON file...") - .add_filter("*.json, *.JSON", &["json", "JSON"]) - .save_file() - .await - .as_ref() - .map(rfd::FileHandle::path) - .map(Path::to_owned) - .ok_or(Error::DialogClosed)? - }; - - let contents = serde_json::to_string(&self)?; - tokio::fs::write(&path, contents).await?; - - Ok(path) - } - - 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 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 - use iced::{{widget::{{{imports}}},Element}}; - {theme_imports} - - fn main() -> iced::Result {{ - iced::application("{}", State::update, State::view).theme(State::theme).run() - }} - - #[derive(Default)] - struct State; - - #[derive(Debug, Clone)] - enum Message {{}} - - impl State {{ - fn update(&mut self, _message: Message) {{}} - - fn theme(&self) -> iced::Theme {{ - iced::Theme::{} - }} - - fn view(&self) -> Element<Message> {{ - {view}.into() - }} - }}"#, - match self.title { - Some(ref t) => t, - None => "New app", - }, - theme_code - ); - let config = rust_format::Config::new_str() - .edition(Edition::Rust2021) - .option("trailing_comma", "Never") - .option("imports_granularity", "Crate"); - let rustfmt = RustFmt::from_config(config); - Ok(rustfmt.format_str(app_code)?) - } - None => Err("No element tree present".into()), - } - } -} diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs deleted file mode 100755 index b001556..0000000 --- a/iced_builder/src/types/rendered_element.rs +++ /dev/null @@ -1,468 +0,0 @@ -use std::collections::BTreeMap; - -use iced::advanced::widget::Id; -use iced::{widget, Element, Length}; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -use super::ElementName; -use crate::types::Message; -use crate::Error; - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct RenderedElement { - #[serde(skip, default = "Uuid::new_v4")] - id: Uuid, - child_elements: Option<Vec<RenderedElement>>, - name: ElementName, - options: BTreeMap<String, Option<String>>, -} - -impl RenderedElement { - fn new(name: ElementName) -> Self { - Self { - id: Uuid::new_v4(), - child_elements: None, - name, - options: BTreeMap::new(), - } - } - - fn with(name: ElementName, child_elements: Vec<RenderedElement>) -> Self { - Self { - id: Uuid::new_v4(), - child_elements: Some(child_elements), - name, - options: BTreeMap::new(), - } - } - - pub fn get_id(&self) -> Id { - Id::new(self.id.to_string()) - } - - pub fn find_by_id(&mut self, id: &Id) -> Option<&mut Self> { - if &self.get_id() == id { - Some(self) - } else if let Some(child_elements) = self.child_elements.as_mut() { - for element in child_elements { - let element = element.find_by_id(id); - if element.is_some() { - return element; - } - } - None - } else { - None - } - } - - pub fn find_parent( - &mut self, - child_element: &RenderedElement, - ) -> Option<&mut Self> { - if child_element == self { - return Some(self); - } else if self.child_elements.is_some() { - if self - .child_elements - .clone() - .unwrap_or_default() - .contains(child_element) - { - return Some(self); - } - 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; - } - } - } - } - None - } - - pub fn is_parent(&self) -> bool { - self.child_elements.is_some() - } - - pub fn is_empty(&self) -> bool { - self.child_elements == Some(vec![]) - } - - pub fn remove(&mut self, element: &RenderedElement) { - let parent = self.find_parent(element).unwrap(); - if let Some(child_elements) = parent.child_elements.as_mut() { - if let Some(index) = - child_elements.iter().position(|x| x == element) - { - let _ = child_elements.remove(index); - } - } - } - - pub fn push_front(&mut self, element: &RenderedElement) { - if let Some(child_elements) = self.child_elements.as_mut() { - child_elements.insert(0, element.clone()); - } - } - - pub fn insert_after(&mut self, id: &Id, element: &RenderedElement) { - if let Some(child_elements) = self.child_elements.as_mut() { - if let Some(index) = - child_elements.iter().position(|x| &x.get_id() == id) - { - child_elements.insert(index + 1, element.clone()); - } else { - child_elements.push(element.clone()); - } - } - } - - pub fn handle_action( - &self, - element_tree: Option<&mut RenderedElement>, - action: Action, - ) -> Result<(), Error> { - let element_tree = element_tree.unwrap(); - - match action { - Action::Stop => Ok(()), - Action::Drop => { - element_tree.remove(self); - - Ok(()) - } - Action::AddNew => Err( - "the action was of kind `AddNew`, but invoking it on an existing element tree is not possible".into(), - ), - Action::PushFront(id) => { - element_tree.remove(self); - - let new_parent = element_tree.find_by_id(id).unwrap(); - new_parent.push_front(self); - - Ok(()) - } - Action::InsertAfter(parent_id, target_id) => { - element_tree.remove(self); - - let new_parent = element_tree.find_by_id(parent_id).unwrap(); - new_parent.insert_after(target_id, self); - - Ok(()) - } - } - } - - fn preset_options(mut self, options: &[&str]) -> Self { - for opt in options { - let _ = self.options.insert(opt.to_string(), None); - } - self - } - - pub fn option<'a>(mut self, option: &'a str, value: &'a str) -> Self { - let _ = self - .options - .entry(option.to_owned()) - .and_modify(|opt| *opt = Some(value.to_owned())); - self - } - - 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().into_element()); - } - } - iced_drop::droppable( - widget::container( - widget::column![ - widget::text(self.name.clone().to_string()), - children - ] - .width(Length::Fill) - .spacing(10), - ) - .padding(10) - .style(widget::container::bordered_box), - ) - .id(self.get_id()) - .drag_hide(true) - .on_drop(move |point, rect| { - Message::MoveElement(self.clone(), point, rect) - }) - .into() - } - - pub fn codegen(&self) -> (String, String) { - let mut imports = String::new(); - let mut view = String::new(); - let mut options = String::new(); - - for (k, v) in self.options.clone() { - if let Some(v) = v { - options = format!("{options}.{k}({v})"); - } - } - - let mut elements = String::new(); - - if let Some(els) = &self.child_elements { - for element in els { - let (c_imports, children) = element.codegen(); - imports = format!("{imports}{c_imports}"); - elements = format!("{elements}{children},"); - } - } - - match &self.name { - ElementName::Container => { - imports = format!("{imports}container,"); - view = format!("{view}\ncontainer({elements}){options}"); - } - ElementName::Row => { - imports = format!("{imports}row,"); - view = format!("{view}\nrow![{elements}]{options}"); - } - ElementName::Column => { - imports = format!("{imports}column,"); - view = format!("{view}\ncolumn![{elements}]{options}"); - } - ElementName::Text(string) => { - imports = format!("{imports}text,"); - view = format!( - "{view}\ntext(\"{}\"){options}", - if *string == String::new() { - "New Text" - } else { - string - } - ); - } - ElementName::Button(string) => { - imports = format!("{imports}button,"); - view = format!( - "{view}\nbutton(\"{}\"){options}", - if *string == String::new() { - "New Button" - } else { - string - } - ); - } - ElementName::Image(path) => { - imports = format!("{imports}image,"); - view = format!("{view}\nimage(\"{path}\"){options}"); - } - ElementName::Svg(path) => { - imports = format!("{imports}svg,"); - view = format!("{view}\nsvg(\"{path}\"){options}"); - } - } - - (imports, view) - } -} - -impl std::fmt::Display for RenderedElement { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut has_options = false; - f.pad("")?; - f.write_fmt(format_args!("{:?}\n", self.name))?; - f.pad("")?; - f.write_str("Options: (")?; - for (k, v) in &self.options { - if let Some(value) = v { - has_options = true; - f.write_fmt(format_args!( - "\n{:width$.precision$}{}: {}", - "", - k, - value, - width = f.width().unwrap_or(0) + f.precision().unwrap_or(0), - precision = f.precision().unwrap_or(0) - ))?; - } - } - if has_options { - f.write_str("\n")?; - f.pad("")?; - } - f.write_str(")")?; - if let Some(els) = &self.child_elements { - f.write_str(" {\n")?; - for el in els { - f.write_fmt(format_args!( - "\n{:width$.precision$}\n", - el, - width = f.width().unwrap_or(0) + f.precision().unwrap_or(0), - precision = f.precision().unwrap_or(0) - ))?; - } - f.pad("")?; - f.write_str("}")?; - } - Ok(()) - } -} - -impl<'a> From<RenderedElement> for Element<'a, Message> { - fn from(value: RenderedElement) -> Self { - let child_elements = match value.child_elements { - Some(ref elements) => elements.clone(), - None => vec![], - }; - - let content: Element<'a, Message> = match value.name.clone() { - ElementName::Text(s) => { - if s == String::new() { - widget::text("New Text").into() - } else { - widget::text(s).into() - } - } - ElementName::Button(s) => { - if s == String::new() { - widget::button(widget::text("New Button")).into() - } else { - widget::button(widget::text(s)).into() - } - } - ElementName::Svg(p) => widget::svg(p).into(), - ElementName::Image(p) => widget::image(p).into(), - ElementName::Container => { - widget::container(if child_elements.len() == 1 { - child_elements[0].clone().into() - } else { - Element::from("") - }) - .padding(20) - .into() - } - ElementName::Row => widget::Row::from_vec( - child_elements.into_iter().map(Into::into).collect(), - ) - .padding(20) - .into(), - ElementName::Column => widget::Column::from_vec( - child_elements.into_iter().map(Into::into).collect(), - ) - .padding(20) - .into(), - }; - iced_drop::droppable(content) - .id(value.get_id()) - .drag_hide(true) - .on_drop(move |point, rect| { - Message::MoveElement(value.clone(), point, rect) - }) - .into() - } -} - -#[derive(Debug, Clone)] -pub enum Action<'a> { - AddNew, - PushFront(&'a Id), - InsertAfter(&'a Id, &'a Id), - Drop, - Stop, -} - -impl<'a> Action<'a> { - pub fn new( - ids: &'a [Id], - element_tree: &'a Option<RenderedElement>, - source_id: Option<Id>, - ) -> Self { - let mut action = Self::Stop; - if ids.len() == 1 { - if element_tree.is_none() { - action = Self::AddNew; - } else { - action = Self::Drop; - } - } else { - let id: &Id = match source_id { - Some(id) if ids.contains(&id) => { - let element_id = - &ids[ids.iter().position(|x| *x == id).unwrap()]; - if ids.len() > 2 && &ids[ids.len() - 1] == element_id { - return Self::Stop; - } - element_id - } - _ => ids.last().unwrap(), - }; - let mut element_tree = element_tree.clone().unwrap(); - let element = element_tree.find_by_id(id).unwrap(); - - // Element is a parent and isn't a non-empty container - if (element.is_empty() || !(element.name == ElementName::Container)) - && element.is_parent() - { - action = Self::PushFront(id); - } else if ids.len() > 2 { - let parent = - element_tree.find_by_id(&ids[ids.len() - 2]).unwrap(); - - if parent.name == ElementName::Container - && parent.child_elements != Some(vec![]) - { - action = Self::Stop; - } else { - action = Self::InsertAfter( - &ids[ids.len() - 2], - &ids[ids.len() - 1], - ); - } - } - } - action - } -} - -pub fn text(text: &str) -> RenderedElement { - RenderedElement::new(ElementName::Text(text.to_owned())).preset_options(&[ - "size", - "line_height", - "width", - "height", - ]) -} - -pub fn button(text: &str) -> RenderedElement { - RenderedElement::new(ElementName::Button(text.to_owned())) -} - -pub fn svg(path: &str) -> RenderedElement { - RenderedElement::new(ElementName::Svg(path.to_owned())) -} - -pub fn image(path: &str) -> RenderedElement { - RenderedElement::new(ElementName::Image(path.to_owned())) -} - -pub fn container(content: Option<RenderedElement>) -> RenderedElement { - match content { - Some(el) => RenderedElement::with(ElementName::Container, vec![el]), - None => RenderedElement::with(ElementName::Container, vec![]), - } -} - -pub fn row(child_elements: Option<Vec<RenderedElement>>) -> RenderedElement { - RenderedElement::with(ElementName::Row, child_elements.unwrap_or_default()) -} - -pub fn column(child_elements: Option<Vec<RenderedElement>>) -> RenderedElement { - RenderedElement::with( - ElementName::Column, - child_elements.unwrap_or_default(), - ) -} |
