diff options
Diffstat (limited to 'iced_builder/src/types')
| -rw-r--r-- | iced_builder/src/types/element_name.rs | 8 | ||||
| -rw-r--r-- | iced_builder/src/types/mod.rs | 9 | ||||
| -rw-r--r-- | iced_builder/src/types/project.rs | 26 | ||||
| -rwxr-xr-x | iced_builder/src/types/rendered_element.rs | 154 |
4 files changed, 135 insertions, 62 deletions
diff --git a/iced_builder/src/types/element_name.rs b/iced_builder/src/types/element_name.rs index 93e12a1..e172227 100644 --- a/iced_builder/src/types/element_name.rs +++ b/iced_builder/src/types/element_name.rs @@ -1,10 +1,9 @@ use serde::{Deserialize, Serialize}; -use crate::Error; - use super::rendered_element::{ button, column, container, image, row, svg, text, Action, RenderedElement, }; +use crate::{Error, Result}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ElementName { @@ -18,7 +17,7 @@ pub enum ElementName { } impl ElementName { - pub const ALL: [Self; 7] = [ + pub const ALL: &'static [Self; 7] = &[ Self::Text(String::new()), Self::Button(String::new()), Self::SVG(String::new()), @@ -32,7 +31,7 @@ impl ElementName { &self, element_tree: Option<&mut RenderedElement>, action: Action, - ) -> Result<Option<RenderedElement>, Error> { + ) -> Result<Option<RenderedElement>> { let element = match self { Self::Text(_) => text(""), Self::Button(_) => button(""), @@ -44,6 +43,7 @@ impl ElementName { }; match action { Action::Stop => Ok(None), + Action::Drop => Ok(None), Action::AddNew => Ok(Some(element)), Action::PushFront(id) => { element_tree diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs deleted file mode 100644 index a48a2d8..0000000 --- a/iced_builder/src/types/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod element_name; -pub mod project; -pub mod rendered_element; - -#[derive(Debug, Clone)] -pub enum DesignerPage { - Designer, - CodeView, -} diff --git a/iced_builder/src/types/project.rs b/iced_builder/src/types/project.rs index 52da41c..f4dbcc4 100644 --- a/iced_builder/src/types/project.rs +++ b/iced_builder/src/types/project.rs @@ -1,12 +1,11 @@ -use rust_format::{Config, Edition, Formatter, RustFmt}; use std::path::{Path, PathBuf}; use iced::Theme; +use rust_format::{Config, Edition, Formatter, RustFmt}; use serde::{Deserialize, Serialize}; -use crate::Error; - use super::rendered_element::RenderedElement; +use crate::{Error, Result}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Project { @@ -55,10 +54,10 @@ impl Project { } } - pub async fn from_file() -> Result<(PathBuf, Self), Error> { + pub async fn from_path() -> Result<(PathBuf, Self)> { let picked_file = rfd::AsyncFileDialog::new() .set_title("Open a JSON file...") - .add_filter("*.JSON, *.json", &["JSON", "json"]) + .add_filter("*.json, *.JSON", &["json", "JSON"]) .pick_file() .await .ok_or(Error::DialogClosed)?; @@ -71,13 +70,13 @@ impl Project { Ok((path, element)) } - pub async fn write_to_file(self, path: Option<PathBuf>) -> Result<PathBuf, Error> { + pub async fn write_to_file(self, path: Option<PathBuf>) -> Result<PathBuf> { let path = if let Some(p) = path { p } else { rfd::AsyncFileDialog::new() .set_title("Save to JSON file...") - .add_filter("*.JSON, *.json", &["JSON", "json"]) + .add_filter("*.json, *.JSON", &["json", "JSON"]) .save_file() .await .as_ref() @@ -86,24 +85,25 @@ impl Project { .ok_or(Error::DialogClosed)? }; - let contents = serde_json::to_string(&self.clone())?; + let contents = serde_json::to_string(&self)?; tokio::fs::write(&path, contents).await?; Ok(path) } - pub fn app_code(&self) -> Result<String, Error> { + pub fn app_code(&self) -> Result<String> { match self.element_tree { Some(ref element_tree) => { let (imports, view) = element_tree.codegen(); - let mut app_code = format!("use iced::{{widget::{{{imports}}},Element}};"); + let mut app_code = + format!("use iced::{{widget::{{{imports}}},Element}};"); app_code = format!( r#"// Automatically generated by iced Builder {app_code} fn main() -> iced::Result {{ - iced::application("{}", State::update, State::view).theme(iced::Theme::{}).run() + iced::application("{}", State::update, State::view).theme(State::theme).run() }} #[derive(Default)] @@ -115,6 +115,10 @@ impl Project { impl State {{ fn update(&mut self, _message: Message) {{}} + fn theme(&self) -> iced::Theme {{ + iced::Theme::{} + }} + fn view(&self) -> Element<Message> {{ {view}.into() }} diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 08d7ba3..d4d1a6c 100755 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -1,40 +1,38 @@ -use indexmap::IndexMap; +use std::collections::BTreeMap; +use blob_uuid::random_blob; use iced::advanced::widget::Id; use iced::{widget, Element, Length}; use serde::{Deserialize, Serialize}; -use unique_id::{string::StringGenerator, Generator}; -use crate::{Error, Message}; - -use super::element_name::ElementName; +use super::ElementName; +use crate::types::Message; +use crate::Result; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RenderedElement { id: String, - pub child_elements: Option<Vec<RenderedElement>>, - pub name: ElementName, - pub options: IndexMap<String, Option<String>>, + child_elements: Option<Vec<RenderedElement>>, + name: ElementName, + options: BTreeMap<String, Option<String>>, } impl RenderedElement { fn new(name: ElementName) -> Self { - let gen = StringGenerator::default(); Self { - id: gen.next_id(), + id: random_blob(), child_elements: None, name, - options: IndexMap::new(), + options: BTreeMap::new(), } } fn with(name: ElementName, child_elements: Vec<RenderedElement>) -> Self { - let gen = StringGenerator::default(); Self { - id: gen.next_id(), + id: random_blob(), child_elements: Some(child_elements), name, - options: IndexMap::new(), + options: BTreeMap::new(), } } @@ -58,7 +56,10 @@ impl RenderedElement { } } - pub fn find_parent(&mut self, child_element: &RenderedElement) -> Option<&mut Self> { + 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() { @@ -94,9 +95,12 @@ impl RenderedElement { } pub fn remove(&mut self, element: &RenderedElement) { - if let Some(child_elements) = self.child_elements.as_mut() { - if let Some(index) = child_elements.iter().position(|x| x == element) { - child_elements.remove(index); + 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); } } } @@ -109,7 +113,9 @@ impl RenderedElement { 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) { + 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()); @@ -121,17 +127,21 @@ impl RenderedElement { &self, element_tree: Option<&mut RenderedElement>, action: Action, - ) -> Result<(), Error> { + ) -> Result<()> { 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) => { - let old_parent = element_tree.find_parent(self).unwrap(); - old_parent.remove(self); + element_tree.remove(self); let new_parent = element_tree.find_by_id(id).unwrap(); new_parent.push_front(self); @@ -139,8 +149,7 @@ impl RenderedElement { Ok(()) } Action::InsertAfter(parent_id, target_id) => { - let old_parent = element_tree.find_parent(self).unwrap(); - old_parent.remove(self); + element_tree.remove(self); let new_parent = element_tree.find_by_id(parent_id).unwrap(); new_parent.insert_after(target_id, self); @@ -150,18 +159,19 @@ impl RenderedElement { } } - fn preset_options(mut self, options: Vec<&str>) -> Self { + fn preset_options<'a>(mut self, options: &[&'a str]) -> Self { for opt in options { - self.options.insert(opt.to_owned(), None); + let _ = self.options.insert(opt.to_string(), None); } self } - pub fn option<'a>(&mut self, option: &'a str, value: &'a str) -> Self { - self.options + 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.clone() + self } pub fn as_element<'a>(self) -> Element<'a, Message> { @@ -174,16 +184,21 @@ impl RenderedElement { } iced_drop::droppable( widget::container( - widget::column![widget::text(self.name.clone().to_string()), children] - .width(Length::Fill) - .spacing(10), + 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)) + .on_drop(move |point, rect| { + Message::MoveElement(self.clone(), point, rect) + }) .into() } @@ -299,11 +314,66 @@ impl std::fmt::Display for RenderedElement { } } +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_iter( + child_elements.into_iter().map(|el| el.into()), + ) + .padding(20) + .into(), + ElementName::Column => widget::Column::from_iter( + child_elements.into_iter().map(|el| el.into()), + ) + .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 { AddNew, PushFront(Id), InsertAfter(Id, Id), + Drop, Stop, } @@ -317,12 +387,16 @@ impl Action { 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()].clone(); - if ids.len() > 2 && ids[ids.clone().len() - 1] == element_id { + let element_id = + ids[ids.iter().position(|x| *x == id).unwrap()].clone(); + if ids.len() > 2 && ids[ids.clone().len() - 1] == element_id + { return Self::Stop; } element_id @@ -337,7 +411,8 @@ impl Action { // Element IS a parent but ISN'T a non-empty container match element.is_parent() - && !(element.name == ElementName::Container && !element.is_empty()) + && !(element.name == ElementName::Container + && !element.is_empty()) { true => { action = Self::PushFront(id); @@ -368,7 +443,7 @@ impl Action { } pub fn text(text: &str) -> RenderedElement { - RenderedElement::new(ElementName::Text(text.to_owned())).preset_options(vec