From 58d05fc93c118661a09688f530b5c62339eba6e0 Mon Sep 17 00:00:00 2001 From: pml68 Date: Mon, 2 Sep 2024 23:36:51 +0200 Subject: feat: start codegen --- src/main.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index 6ee558b..16a1daa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod codegen; mod types; use iced::{ -- cgit v1.2.3 From e8792ea151e0c26fd3497a09446f1b846df1c87c Mon Sep 17 00:00:00 2001 From: pml68 Date: Tue, 3 Sep 2024 21:55:34 +0200 Subject: feat: finish codegen for elements **without** props feat: wow --- Cargo.lock | 43 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/codegen/mod.rs | 40 ++++++++++++++++++++------------------- src/main.rs | 2 +- src/types/mod.rs | 34 ++++++--------------------------- src/types/props.rs | 44 +++++++++++++++++++++++++++++++++++++++++++ src/types/rendered_element.rs | 36 +++++++++++++++++++++++++++++++++++ 7 files changed, 152 insertions(+), 48 deletions(-) create mode 100644 src/types/props.rs create mode 100644 src/types/rendered_element.rs (limited to 'src/main.rs') diff --git a/Cargo.lock b/Cargo.lock index 2d33a26..b519b92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -157,6 +157,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -211,6 +217,16 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "blob-uuid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc15853171b33280f5614e77f5fa4debd33f51a86c44daa4ba3d759674c561" +dependencies = [ + "base64 0.13.1", + "uuid", +] + [[package]] name = "block" version = "0.1.6" @@ -1186,6 +1202,7 @@ dependencies = [ "iced_aw", "rust-format", "tokio", + "unique_id", ] [[package]] @@ -1526,6 +1543,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "lebe" version = "0.5.2" @@ -3163,6 +3186,17 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +[[package]] +name = "unique_id" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae605c39dfbdec433798d4a8b03ffbac711dc51cdeb1ba5c725bdcaf24e464cc" +dependencies = [ + "blob-uuid", + "lazy_static", + "uuid", +] + [[package]] name = "usvg" version = "0.36.0" @@ -3224,6 +3258,15 @@ dependencies = [ "tiny-skia-path", ] +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom", +] + [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index 36eabed..301ecfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ iced = { version = "0.12.1", features = [ "image","svg","canvas","qr_code","adva iced_aw = { version = "0.9.3", default-features = false, features = ["menu","color_picker"] } tokio = { version = "1.40.0", features = ["fs"] } rust-format = "0.3.4" +unique_id = "0.1.5" diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 8e31dc3..5ce8425 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1,38 +1,40 @@ -use crate::types::{ElementName, RenderedElement}; +use crate::types::{props::Props, rendered_element::RenderedElement, ElementName}; impl RenderedElement { - pub fn codegen(&self) -> Result<(String, String), &str> { + pub fn codegen(&self) -> (String, String) { let mut imports = String::new(); let mut view = String::new(); let mut props = String::new(); let mut elements = String::new(); - match self.children { - Some(els) => { - for el in els { - let mut children = String::new(); + for element in &self.child_elements { + let mut children = String::new(); - match el.codegen() { - Ok(e) => (children, imports) = e, - Err(err) => return Err(err), - } - elements = format!("{elements},{}", children); - } - } - None => {} + (imports, children) = element.codegen(); + elements = format!("{elements}{},", children); } - match self.name { + match &self.name { + ElementName::Container => { + imports = format!("{imports}\nuse iced::widget::container;"); + view = if self.child_elements.len() < 2 { + format!("{view}\ncontainer({elements}){props}") + } else { + format!("{view}\ncontainer(){props}") + }; + } ElementName::Row => { imports = format!("{imports}\nuse iced::widget::row;"); - view = format!("{view}\nrow![{elements}]{props};"); + view = format!("{view}\nrow![{elements}]{props}"); } - ElementName::Container => { - imports = format!("{imports}\nuse iced::widget::container;"); - view = format!("{view}\ncontainer({elements}){props};"); + ElementName::Text(string) => { + imports = format!("{imports}\nuse iced::widget::text;"); + view = format!("{view}\ntext(\"{string}\"){props}"); } _ => {} } + + (imports, view) } } diff --git a/src/main.rs b/src/main.rs index 16a1daa..b195c27 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use iced::{ Alignment, Application, Color, Command, Element, Font, Length, Settings, }; use rust_format::{Formatter, RustFmt}; -use types::{DesignerPage, DesignerState}; +use types::{rendered_element::RenderedElement, DesignerPage, DesignerState}; fn main() -> iced::Result { App::run(Settings::default()) diff --git a/src/types/mod.rs b/src/types/mod.rs index 09b0f07..60b28f7 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,24 +1,17 @@ -use std::path::PathBuf; +pub mod props; +pub mod rendered_element; -use iced::{ - alignment::{Horizontal, Vertical}, - widget::{text::Shaping, text_editor}, - Alignment, ContentFit, Font, Length, -}; +use iced::widget::text_editor; +use rendered_element::RenderedElement; +use std::path::PathBuf; pub struct DesignerState { pub designer_content: Vec, pub designer_page: DesignerPage, } -pub struct RenderedElement { - pub id: String, - pub children: Option>, - pub name: ElementName, - pub props: Props, -} - pub enum ElementName { + App(String, iced::Theme), Text(String), Button(String), TextEditor(text_editor::Content), @@ -29,21 +22,6 @@ pub enum ElementName { Column, } -pub struct Props { - pub align_items: Option, - pub align_x: Option, - pub align_y: Option, - pub horizontal_alignment: Option, - pub vertical_alignment: Option, - pub height: Option, - pub width: Option, - pub font: Option, - pub padding: Option, - pub spacing: Option, - pub content_fit: Option, - pub shaping: Option, -} - pub enum DesignerPage { Designer, CodeView, diff --git a/src/types/props.rs b/src/types/props.rs new file mode 100644 index 0000000..6dbb0a4 --- /dev/null +++ b/src/types/props.rs @@ -0,0 +1,44 @@ +use core::f32; + +use iced::{ + alignment::{Horizontal, Vertical}, + widget::text::Shaping, + Alignment, ContentFit, Font, Length, Padding, +}; +pub struct Props { + pub align_items: Option, + pub align_x: Option, + pub align_y: Option, + pub horizontal_alignment: Option, + pub vertical_alignment: Option, + pub width: Option, + pub height: Option, + pub max_width: Option, + pub max_height: Option, + pub font: Option, + pub padding: Option, + pub spacing: Option, + pub content_fit: Option, + pub shaping: Option, +} + +impl Default for Props { + fn default() -> Self { + Self { + align_items: None, + align_x: None, + align_y: None, + horizontal_alignment: None, + vertical_alignment: None, + width: None, + height: None, + max_width: None, + max_height: None, + font: None, + padding: None, + spacing: None, + content_fit: None, + shaping: None, + } + } +} diff --git a/src/types/rendered_element.rs b/src/types/rendered_element.rs new file mode 100644 index 0000000..e909ac5 --- /dev/null +++ b/src/types/rendered_element.rs @@ -0,0 +1,36 @@ +use unique_id::{string::StringGenerator, Generator}; + +use super::{props::Props, ElementName}; +pub struct RenderedElement { + pub id: String, + pub child_elements: Vec, + pub name: ElementName, + pub props: Props, +} + +impl RenderedElement { + pub fn new(name: ElementName) -> Self { + let gen = StringGenerator::default(); + Self { + id: gen.next_id(), + child_elements: vec![], + name, + props: Props::default(), + } + } + + pub fn from_vec(name: ElementName, child_elements: Vec) -> Self { + let gen = StringGenerator::default(); + Self { + id: gen.next_id(), + child_elements, + name, + props: Props::default(), + } + } + + pub fn push(mut self, element: RenderedElement) -> Self { + self.child_elements.push(element); + self + } +} -- cgit v1.2.3 From a01402e4c0d226118335a6744ee7cda4f7be552b Mon Sep 17 00:00:00 2001 From: pml68 Date: Wed, 4 Sep 2024 00:37:25 +0200 Subject: feat: finish codegen with limited props and elements --- src/codegen/mod.rs | 71 +++++++++++++++++++++++++++++++++++++--- src/main.rs | 95 +++++++++++++++++++++++++++--------------------------- src/types/mod.rs | 1 - 3 files changed, 113 insertions(+), 54 deletions(-) (limited to 'src/main.rs') diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 5ce8425..c8b789b 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1,10 +1,34 @@ +use rust_format::{Config, Edition, Formatter, RustFmt}; + use crate::types::{props::Props, rendered_element::RenderedElement, ElementName}; +impl Props { + pub fn codegen(&self) -> String { + let mut props_string = String::new(); + + match self.spacing { + Some(s) => { + props_string = format!("{props_string}.spacing({s})"); + } + None => {} + } + + match self.max_height { + Some(h) => { + props_string = format!("{props_string}.max_height({h})"); + } + None => {} + } + + props_string + } +} + impl RenderedElement { - pub fn codegen(&self) -> (String, String) { + fn codegen(&self) -> (String, String) { let mut imports = String::new(); let mut view = String::new(); - let mut props = String::new(); + let props = self.props.codegen(); let mut elements = String::new(); @@ -17,7 +41,7 @@ impl RenderedElement { match &self.name { ElementName::Container => { - imports = format!("{imports}\nuse iced::widget::container;"); + imports = format!("{imports}widget::container,"); view = if self.child_elements.len() < 2 { format!("{view}\ncontainer({elements}){props}") } else { @@ -25,11 +49,11 @@ impl RenderedElement { }; } ElementName::Row => { - imports = format!("{imports}\nuse iced::widget::row;"); + imports = format!("{imports}widget::row,"); view = format!("{view}\nrow![{elements}]{props}"); } ElementName::Text(string) => { - imports = format!("{imports}\nuse iced::widget::text;"); + imports = format!("{imports}widget::text,"); view = format!("{view}\ntext(\"{string}\"){props}"); } _ => {} @@ -37,4 +61,41 @@ impl RenderedElement { (imports, view) } + + pub fn app_code(&self, title: String) -> Result> { + let (imports, view) = self.codegen(); + let mut app_code = format!("use iced::{{{imports}Sandbox,Settings,Element}};"); + + app_code = format!( + r#"{app_code} + + fn main() -> iced::Result {{ + App::run(Settings::default()) + }} + + struct App; + + impl Sandbox for App {{ + type Message = (); + + fn new() -> Self {{ + Self {{}} + }} + + fn title(&self) -> String {{ + "{title}".into() + }} + + fn update(&mut self, message: Message) {{ + + }} + + fn view(&self) -> Element {{ + {view}.into() + }} + }}"# + ); + + Ok(RustFmt::default().format_str(app_code)?) + } } diff --git a/src/main.rs b/src/main.rs index b195c27..5b691bb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,6 @@ use iced::{ }, Alignment, Application, Color, Command, Element, Font, Length, Settings, }; -use rust_format::{Formatter, RustFmt}; use types::{rendered_element::RenderedElement, DesignerPage, DesignerState}; fn main() -> iced::Result { @@ -65,13 +64,13 @@ impl Application for App { focus: None, designer_state: DesignerState { designer_content: vec![], - designer_page: DesignerPage::Designer, + designer_page: DesignerPage::CodeView, }, element_list: vec!["Column", "Row", "PickList", "PaneGrid", "Button", "Text"] .into_iter() .map(|c| c.to_owned()) .collect(), - editor_content: text_editor::Content::new(), + editor_content: text_editor::Content::with_text(&RenderedElement::test()), }, Command::none(), ) @@ -118,27 +117,56 @@ impl Application for App { let pane_grid = PaneGrid::new(&self.pane_state, |id, pane, _is_maximized| { let is_focused = Some(id) == self.focus; match pane { - Panes::Designer => { - let content = column![text("Designer")] - .align_items(Alignment::Center) - .height(Length::Fill) - .width(Length::Fill); - let title = text("Designer").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }); - let title_bar = pane_grid::TitleBar::new(title) - .padding(10) - .style(style::title_bar); - pane_grid::Content::new(content) + Panes::Designer => match self.designer_state.designer_page { + DesignerPage::Designer => { + let content = column![text("Designer"),] + .align_items(Alignment::Center) + .height(Length::Fill) + .width(Length::Fill); + let title = text("Designer").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }); + let title_bar = pane_grid::TitleBar::new(title) + .padding(10) + .style(style::title_bar); + pane_grid::Content::new(content) + .title_bar(title_bar) + .style(if is_focused { + style::pane_focused + } else { + style::pane_active + }) + } + DesignerPage::CodeView => { + let title = text("Generated Code").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }); + let title_bar = pane_grid::TitleBar::new(title) + .padding(10) + .style(style::title_bar); + pane_grid::Content::new( + text_editor(&self.editor_content) + .highlight::( + highlighter::Settings { + theme: highlighter::Theme::Base16Mocha, + extension: "rs".to_string(), + }, + |highlight, _theme| highlight.to_format(), + ) + .padding(20), + ) .title_bar(title_bar) .style(if is_focused { style::pane_focused } else { style::pane_active }) - } + } + }, Panes::ElementList => { let items_list = items_list_view(&self.element_list); let content = column![items_list] @@ -160,36 +188,7 @@ impl Application for App { } else { style::pane_active }) - } //Panes::CodeView => { - // let title = text("Generated Code").style(if is_focused { - // PANE_ID_COLOR_FOCUSED - // } else { - // PANE_ID_COLOR_UNFOCUSED - // }); - // let title_bar = - // pane_grid::TitleBar::new(title) - // .padding(10) - // .style(if is_focused { - // style::title_bar_focused - // } else { - // style::title_bar_active - // }); - // pane_grid::Content::new( - // text_editor(&self.editor_content).highlight::( - // highlighter::Settings { - // theme: highlighter::Theme::Base16Mocha, - // extension: "rs".to_string(), - // }, - // |highlight, _theme| highlight.to_format(), - // ), - // ) - // .title_bar(title_bar) - // .style(if is_focused { - // style::pane_focused - // } else { - // style::pane_active - // }) - //} + } } }) .width(Length::Fill) diff --git a/src/types/mod.rs b/src/types/mod.rs index 60b28f7..1fa23d4 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -11,7 +11,6 @@ pub struct DesignerState { } pub enum ElementName { - App(String, iced::Theme), Text(String), Button(String), TextEditor(text_editor::Content), -- cgit v1.2.3 From 86b0ef24d4cfda3abb091a97ad37f26f3bba2a41 Mon Sep 17 00:00:00 2001 From: pml68 Date: Sun, 22 Sep 2024 21:29:55 +0200 Subject: feat: add "Copy to clipboard" button for code view --- fonts/icons.ttf | Bin 0 -> 6352 bytes src/codegen/mod.rs | 37 +++++++++++++++++++++++-------------- src/main.rs | 37 +++++++++++++++++++++++++++++-------- 3 files changed, 52 insertions(+), 22 deletions(-) create mode 100644 fonts/icons.ttf (limited to 'src/main.rs') diff --git a/fonts/icons.ttf b/fonts/icons.ttf new file mode 100644 index 0000000..393c692 Binary files /dev/null and b/fonts/icons.ttf differ diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index bc3c379..2dd9cff 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use rust_format::{Config, Edition, Formatter, RustFmt}; use crate::types::{rendered_element::RenderedElement, ElementName}; @@ -23,9 +25,8 @@ impl RenderedElement { match self.name { ElementName::Column | ElementName::Row | ElementName::Container => { for element in &self.child_elements { - let mut children = String::new(); - - (imports, children) = element.codegen(); + let (c_imports, children) = element.codegen(); + imports = format!("{imports}{c_imports}"); elements = format!("{elements}{},", children); } } @@ -73,11 +74,11 @@ impl RenderedElement { } ElementName::Image(path) => { imports = format!("{imports}image,"); - view = format!("{view}\nimage({}){props}", path.display().to_string()); + view = format!("{view}\nimage(\"{}\"){props}", path.display().to_string()); } ElementName::SVG(path) => { imports = format!("{imports}svg,"); - view = format!("{view}\nsvg({}){props}", path.display().to_string()); + view = format!("{view}\nsvg(\"{}\"){props}", path.display().to_string()); } } @@ -116,11 +117,11 @@ impl RenderedElement { iced::Theme::{} }} - fn update(&mut self, message: Message) {{ + fn update(&mut self, message: Self::Message) {{ }} - fn view(&self) -> Element {{ + fn view(&self) -> Element {{ {view}.into() }} }}"#, @@ -130,20 +131,28 @@ impl RenderedElement { "default()".to_owned() } ); - - Ok(RustFmt::default().format_str(app_code)?) + let config = 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)?) } pub fn test() -> String { let mut text1 = RenderedElement::new(ElementName::Text("wow")); - text1.set_property("padding", "[10, 20]"); - text1.set_property("spacing", "20.0"); - text1.set_property("max_height", "120.5"); - text1.set_property("max_width", "230"); + text1.set_property("height", "120.5"); + text1.set_property("width", "230"); let element = RenderedElement::new(ElementName::Container).push(RenderedElement::from_vec( ElementName::Row, - vec![text1, RenderedElement::new(ElementName::Text("heh"))], + vec![ + text1, + RenderedElement::new(ElementName::Text("heh")), + RenderedElement::new(ElementName::SVG(PathBuf::from( + "/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg", + ))), + ], )); element.app_code("new app", None).unwrap() diff --git a/src/main.rs b/src/main.rs index 5b691bb..d3fafc6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,20 +2,23 @@ mod codegen; mod types; use iced::{ - executor, + clipboard, executor, highlighter::{self, Highlighter}, theme, widget::{ button, column, container, pane_grid::{self, Pane, PaneGrid}, - row, text, text_editor, Column, + row, text, text_editor, tooltip, Column, Space, }, Alignment, Application, Color, Command, Element, Font, Length, Settings, }; use types::{rendered_element::RenderedElement, DesignerPage, DesignerState}; fn main() -> iced::Result { - App::run(Settings::default()) + App::run(Settings { + fonts: vec![include_bytes!("../fonts/icons.ttf").as_slice().into()], + ..Settings::default() + }) } struct App { @@ -32,6 +35,7 @@ struct App { #[derive(Debug, Clone)] enum Message { ToggleTheme, + CopyCode, Resized(pane_grid::ResizeEvent), Clicked(pane_grid::Pane), } @@ -98,6 +102,7 @@ impl Application for App { fn update(&mut self, message: Message) -> Command { match message { Message::ToggleTheme => self.dark_theme = !self.dark_theme, + Message::CopyCode => return clipboard::write(self.editor_content.text()), Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { self.pane_state.resize(split, ratio); } @@ -140,11 +145,26 @@ impl Application for App { }) } DesignerPage::CodeView => { - let title = text("Generated Code").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }); + let title = row![ + text("Generated Code").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }), + Space::with_width(Length::Fill), + tooltip( + button( + container( + text('\u{0e801}').font(Font::with_name("editor-icons")) + ) + .width(30) + .center_x() + ) + .on_press(Message::CopyCode), + "Copy code to clipboard", + tooltip::Position::Left + ) + ]; let title_bar = pane_grid::TitleBar::new(title) .padding(10) .style(style::title_bar); @@ -157,6 +177,7 @@ impl Application for App { }, |highlight, _theme| highlight.to_format(), ) + .height(Length::Fill) .padding(20), ) .title_bar(title_bar) -- cgit v1.2.3