diff options
Diffstat (limited to 'iced_builder')
| -rw-r--r-- | iced_builder/Cargo.toml | 57 | ||||
| -rw-r--r-- | iced_builder/assets/windows/iced_builder.manifest | 8 | ||||
| -rw-r--r-- | iced_builder/assets/windows/iced_builder.rc | 3 | ||||
| -rw-r--r-- | iced_builder/build.rs | 12 | ||||
| -rw-r--r-- | iced_builder/fonts/icons.toml | 6 | ||||
| -rw-r--r-- | iced_builder/fonts/icons.ttf | bin | 6348 -> 0 bytes | |||
| -rw-r--r-- | iced_builder/rustfmt.toml | 4 | ||||
| -rw-r--r-- | iced_builder/src/dialogs.rs | 21 | ||||
| -rw-r--r-- | iced_builder/src/error.rs | 44 | ||||
| -rw-r--r-- | iced_builder/src/icon.rs | 23 | ||||
| -rw-r--r-- | iced_builder/src/lib.rs | 9 | ||||
| -rw-r--r-- | iced_builder/src/main.rs | 346 | ||||
| -rw-r--r-- | iced_builder/src/panes.rs | 4 | ||||
| -rw-r--r-- | iced_builder/src/panes/code_view.rs | 49 | ||||
| -rw-r--r-- | iced_builder/src/panes/designer_view.rs | 37 | ||||
| -rw-r--r-- | iced_builder/src/panes/element_list.rs | 49 | ||||
| -rw-r--r-- | iced_builder/src/panes/style.rs | 40 | ||||
| -rw-r--r-- | iced_builder/src/types.rs | 48 | ||||
| -rw-r--r-- | iced_builder/src/types/element_name.rs | 86 | ||||
| -rw-r--r-- | iced_builder/src/types/project.rs | 142 | ||||
| -rwxr-xr-x | iced_builder/src/types/rendered_element.rs | 482 | ||||
| -rw-r--r-- | iced_builder/src/widget.rs | 21 |
22 files changed, 0 insertions, 1491 deletions
diff --git a/iced_builder/Cargo.toml b/iced_builder/Cargo.toml deleted file mode 100644 index a1b41cc..0000000 --- a/iced_builder/Cargo.toml +++ /dev/null @@ -1,57 +0,0 @@ -[package] -name = "iced_builder" -description = "GUI builder for iced, built with iced." -version = "0.1.0" -edition = "2021" -authors = ["pml68 <contact@pml68.dev>"] -repository = "https://github.com/pml68/iced-builder" -license = "GPL-3.0-or-later" -keywords = ["gui", "iced"] - -[dependencies] -iced = { version = "0.13.1", features = [ "image","svg","canvas","qr_code","advanced","tokio","highlighter"] } -# iced_aw = { version = "0.11.0", default-features = false, features = ["menu","color_picker"] } -iced_anim = { version = "0.1.4", features = ["derive", "serde"] } -iced_drop = { path = "../iced_drop" } -serde = { version = "1.0.216", features = ["derive"] } -serde_json = "1.0.133" -tokio = { version = "1.42.0", features = ["fs"] } -rfd = { version = "0.15.1", default-features = false, features = ["async-std", "gtk3"] } -rust-format = "0.3.4" -blob-uuid = "0.5.0" -thiserror = "2.0.6" - -[build-dependencies] -iced_fontello = "0.13.1" - -[target.'cfg(windows)'.build-dependencies] -embed-resource = "3.0.1" -windows_exe_info = "0.4" - -[[bin]] -name = "iced-builder" -path = "src/main.rs" - -[lints.rust] -missing_debug_implementations = "deny" -# missing_docs = "deny" -unsafe_code = "deny" -unused_results = "deny" - -[lints.clippy] -type-complexity = "allow" -semicolon_if_nothing_returned = "deny" -trivially-copy-pass-by-ref = "deny" -default_trait_access = "deny" -match-wildcard-for-single-variants = "deny" -redundant-closure-for-method-calls = "deny" -filter_map_next = "deny" -manual_let_else = "deny" -unused_async = "deny" -from_over_into = "deny" -needless_borrow = "deny" -new_without_default = "deny" -useless_conversion = "deny" - -[lints.rustdoc] -broken_intra_doc_links = "forbid" diff --git a/iced_builder/assets/windows/iced_builder.manifest b/iced_builder/assets/windows/iced_builder.manifest deleted file mode 100644 index 82039bf..0000000 --- a/iced_builder/assets/windows/iced_builder.manifest +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version='1.0' encoding='UTF-8' standalone='yes'?> -<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" > - <asmv3:application> - <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> - <dpiAwareness>PerMonitorV2, unaware</dpiAwareness> - </asmv3:windowsSettings> - </asmv3:application> -</assembly> diff --git a/iced_builder/assets/windows/iced_builder.rc b/iced_builder/assets/windows/iced_builder.rc deleted file mode 100644 index 7255b65..0000000 --- a/iced_builder/assets/windows/iced_builder.rc +++ /dev/null @@ -1,3 +0,0 @@ -#define RT_MANIFEST 24 - -1 RT_MANIFEST "iced_builder.manifest" diff --git a/iced_builder/build.rs b/iced_builder/build.rs deleted file mode 100644 index 438ce37..0000000 --- a/iced_builder/build.rs +++ /dev/null @@ -1,12 +0,0 @@ -fn main() { - println!("cargo::rerun-if-changed=fonts/icons.toml"); - iced_fontello::build("fonts/icons.toml").expect("Build icons font"); - #[cfg(windows)] - { - embed_resource::compile( - "assets/windows/iced_builder.rc", - embed_resource::NONE, - ); - windows_exe_info::versioninfo::link_cargo_env(); - } -} diff --git a/iced_builder/fonts/icons.toml b/iced_builder/fonts/icons.toml deleted file mode 100644 index a70c0e7..0000000 --- a/iced_builder/fonts/icons.toml +++ /dev/null @@ -1,6 +0,0 @@ -module = "icon" - -[glyphs] -save = "entypo-floppy" -open = "fontawesome-folder-open-empty" -copy = "fontawesome-file-code" diff --git a/iced_builder/fonts/icons.ttf b/iced_builder/fonts/icons.ttf Binary files differdeleted file mode 100644 index 7af6b0e..0000000 --- a/iced_builder/fonts/icons.ttf +++ /dev/null diff --git a/iced_builder/rustfmt.toml b/iced_builder/rustfmt.toml deleted file mode 100644 index 197262a..0000000 --- a/iced_builder/rustfmt.toml +++ /dev/null @@ -1,4 +0,0 @@ -edition = "2021" -imports_granularity = "Module" -group_imports = "StdExternalCrate" -max_width = 80 diff --git a/iced_builder/src/dialogs.rs b/iced_builder/src/dialogs.rs deleted file mode 100644 index 047ffd2..0000000 --- a/iced_builder/src/dialogs.rs +++ /dev/null @@ -1,21 +0,0 @@ -use rfd::{MessageButtons, MessageDialog, MessageDialogResult, MessageLevel}; - -pub fn error_dialog(description: impl Into<String>) { - let _ = MessageDialog::new() - .set_level(MessageLevel::Error) - .set_buttons(MessageButtons::Ok) - .set_title("Oops! Something went wrong.") - .set_description(description) - .show(); -} - -pub fn unsaved_changes_dialog( - description: impl Into<String>, -) -> MessageDialogResult { - MessageDialog::new() - .set_level(MessageLevel::Warning) - .set_buttons(MessageButtons::OkCancel) - .set_title("Unsaved changes") - .set_description(description) - .show() -} diff --git a/iced_builder/src/error.rs b/iced_builder/src/error.rs deleted file mode 100644 index 8876016..0000000 --- a/iced_builder/src/error.rs +++ /dev/null @@ -1,44 +0,0 @@ -use std::io; -use std::sync::Arc; - -use thiserror::Error; - -#[derive(Debug, Clone, Error)] -#[error(transparent)] -pub enum Error { - IOError(Arc<io::Error>), - SerdeError(Arc<serde_json::Error>), - FormatError(Arc<rust_format::Error>), - #[error("The element tree contains no matching element")] - NonExistentElement, - #[error( - "The file dialog has been closed without selecting a valid option" - )] - DialogClosed, - #[error("{0}")] - Other(String), -} - -impl From<io::Error> for Error { - fn from(value: io::Error) -> Self { - Self::IOError(Arc::new(value)) - } -} - -impl From<serde_json::Error> for Error { - fn from(value: serde_json::Error) -> Self { - Self::SerdeError(Arc::new(value)) - } -} - -impl From<rust_format::Error> for Error { - fn from(value: rust_format::Error) -> Self { - Self::FormatError(Arc::new(value)) - } -} - -impl From<&str> for Error { - fn from(value: &str) -> Self { - Self::Other(value.to_owned()) - } -} diff --git a/iced_builder/src/icon.rs b/iced_builder/src/icon.rs deleted file mode 100644 index f6760d5..0000000 --- a/iced_builder/src/icon.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Generated automatically by iced_fontello at build time. -// Do not edit manually. Source: ../fonts/icons.toml -// 02c7558d187cdc056fdd0e6a638ef805fa10f5955f834575e51d75acd35bc70e -use iced::widget::{text, Text}; -use iced::Font; - -pub const FONT: &[u8] = include_bytes!("../fonts/icons.ttf"); - -pub fn copy<'a>() -> Text<'a> { - icon("\u{F1C9}") -} - -pub fn open<'a>() -> Text<'a> { - icon("\u{F115}") -} - -pub fn save<'a>() -> Text<'a> { - icon("\u{1F4BE}") -} - -fn icon<'a>(codepoint: &'a str) -> Text<'a> { - text(codepoint).font(Font::with_name("icons")) -} diff --git a/iced_builder/src/lib.rs b/iced_builder/src/lib.rs deleted file mode 100644 index f3165f5..0000000 --- a/iced_builder/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod dialogs; -pub mod error; -pub mod icon; -pub mod panes; -pub mod types; -pub mod widget; - -pub use error::Error; -pub type Result<T> = core::result::Result<T, Error>; diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs deleted file mode 100644 index a041c6f..0000000 --- a/iced_builder/src/main.rs +++ /dev/null @@ -1,346 +0,0 @@ -use std::path::PathBuf; - -use iced::advanced::widget::Id; -use iced::widget::pane_grid::{self, Pane, PaneGrid}; -use iced::widget::{container, pick_list, row, text_editor, Column}; -use iced::{clipboard, keyboard, Alignment, Element, Length, Task, Theme}; -use iced_anim::{Animation, Spring}; -use iced_builder::dialogs::{error_dialog, unsaved_changes_dialog}; -use iced_builder::icon; -use iced_builder::panes::{code_view, designer_view, element_list}; -use iced_builder::types::{ - Action, DesignerPage, ElementName, Message, Project, -}; -use rfd::MessageDialogResult; - -const THEMES: &'static [Theme] = &[Theme::SolarizedDark, Theme::SolarizedLight]; - -fn main() -> iced::Result { - iced::application(App::title, App::update, App::view) - .font(icon::FONT) - .theme(|state| state.theme.value().clone()) - .subscription(App::subscription) - .run_with(App::new) -} - -struct App { - is_dirty: bool, - is_loading: bool, - project_path: Option<PathBuf>, - project: Project, - theme: Spring<Theme>, - pane_state: pane_grid::State<Panes>, - focus: Option<Pane>, - designer_page: DesignerPage, - element_list: &'static [ElementName], - editor_content: text_editor::Content, -} - -#[derive(Clone, Copy, Debug)] -enum Panes { - Designer, - ElementList, -} - -impl App { - fn new() -> (Self, Task<Message>) { - let state = pane_grid::State::with_configuration( - pane_grid::Configuration::Split { - axis: pane_grid::Axis::Vertical, - ratio: 0.8, - a: Box::new(pane_grid::Configuration::Pane(Panes::Designer)), - b: Box::new(pane_grid::Configuration::Pane(Panes::ElementList)), - }, - ); - ( - Self { - is_dirty: false, - is_loading: false, - project_path: None, - project: Project::new(), - theme: Spring::new(Theme::SolarizedDark), - pane_state: state, - focus: None, - designer_page: DesignerPage::DesignerView, - element_list: ElementName::ALL, - editor_content: text_editor::Content::new(), - }, - Task::none(), - ) - } - - fn title(&self) -> String { - let saved_state = if !self.is_dirty { "" } else { " *" }; - - let project_name = match &self.project.title { - Some(n) => { - format!( - " - {}", - if n.len() > 60 { - format!("...{}", &n[n.len() - 40..]) - } else { - n.to_owned() - } - ) - } - None => "".to_owned(), - }; - - format!("iced Builder{project_name}{saved_state}") - } - - fn update(&mut self, message: Message) -> Task<Message> { - match message { - Message::ToggleTheme(event) => { - self.theme.update(event); - } - Message::CopyCode => { - return clipboard::write(self.editor_content.text()) - } - Message::SwitchPage(page) => self.designer_page = page, - Message::EditorAction(action) => { - if let text_editor::Action::Scroll { lines: _ } = action { - self.editor_content.perform(action); - } - } - Message::RefreshEditorContent => { - match self.project.clone().app_code() { - Ok(code) => { - self.editor_content = - text_editor::Content::with_text(&code); - } - Err(error) => error_dialog(error.to_string()), - } - } - Message::DropNewElement(name, point, _) => { - return iced_drop::zones_on_point( - move |zones| Message::HandleNew(name.clone(), zones), - point, - None, - None, - ) - .into() - } - Message::HandleNew(name, zones) => { - let ids: Vec<Id> = zones.into_iter().map(|z| z.0).collect(); - if ids.len() > 0 { - let action = Action::new( - ids, - &mut self.project.element_tree.clone(), - None, - ); - let result = name.handle_action( - self.project.element_tree.as_mut(), - action, - ); - match result { - Ok(Some(ref element)) => { - self.project.element_tree = Some(element.clone()) - } - Err(error) => error_dialog(error.to_string()), - _ => {} - } - - self.is_dirty = true; - return Task::done(Message::RefreshEditorContent); - } - } - Message::MoveElement(element, point, _) => { - return iced_drop::zones_on_point( - move |zones| Message::HandleMove(element.clone(), zones), - point, - None, - None, - ) - .into() - } - Message::HandleMove(element, zones) => { - let ids: Vec<Id> = zones.into_iter().map(|z| z.0).collect(); - if ids.len() > 0 { - let action = Action::new( - ids, - &mut self.project.element_tree.clone(), - Some(element.get_id()), - ); - let result = element.handle_action( - self.project.element_tree.as_mut(), - action, - ); - if let Err(error) = result { - error_dialog(error.to_string()); - } - - self.is_dirty = true; - return Task::done(Message::RefreshEditorContent); - } - } - Message::PaneResized(pane_grid::ResizeEvent { split, ratio }) => { - self.pane_state.resize(split, ratio); - } - Message::PaneClicked(pane) => { - self.focus = Some(pane); - } - Message::PaneDragged(pane_grid::DragEvent::Dropped { - pane, - target, - }) => { - self.pane_state.drop(pane, target); - } - Message::PaneDragged(_) => {} - Message::NewFile => { - if !self.is_loading { - if !self.is_dirty { - self.project = Project::new(); - self.project_path = None; - self.editor_content = text_editor::Content::new(); - } else { - if let MessageDialogResult::Ok = unsaved_changes_dialog("You have unsaved changes. Do you wish to discard these and create a new project?") { - self.is_dirty = false; - self.project = Project::new(); - self.project_path = None; - self.editor_content = text_editor::Content::new(); - } - } - } - } - Message::OpenFile => { - if !self.is_loading { - if !self.is_dirty { - self.is_loading = true; - - return Task::perform( - Project::from_path(), - Message::FileOpened, - ); - } else { - if let MessageDialogResult::Ok = unsaved_changes_dialog("You have unsaved changes. Do you wish to discard these and open another project?") { - self.is_dirty = false; - self.is_loading = true; - return Task::perform(Project::from_path(), Message::FileOpened); - } - } - } - } - Message::FileOpened(result) => { - self.is_loading = false; - self.is_dirty = false; - - match result { - Ok((path, project)) => { - self.project = project.clone(); - self.project_path = Some(path); - self.editor_content = text_editor::Content::with_text( - &project - .app_code() - .unwrap_or_else(|err| err.to_string()), - ); - } - Err(error) => error_dialog(error.to_string()), - } - } - Message::SaveFile => { - if !self.is_loading { - self.is_loading = true; - - return Task::perform( - self.project - .clone() - .write_to_file(self.project_path.clone()), - Message::FileSaved, - ); - } - } - Message::SaveFileAs => { - if !self.is_loading { - self.is_loading = true; - - return Task::perform( - self.project.clone().write_to_file(None), - Message::FileSaved, - ); - } - } - Message::FileSaved(result) => { - self.is_loading = false; - - match result { - Ok(path) => { - self.project_path = Some(path); - self.is_dirty = false; - } - Err(error) => error_dialog(error.to_string()), - } - } - } - - Task::none() - } - - fn subscription(&self) -> iced::Subscription<Message> { - keyboard::on_key_press(|key, modifiers| { - if modifiers.command() { - match key.as_ref() { - keyboard::Key::Character("o") => Some(Message::OpenFile), - keyboard::Key::Character("s") => { - Some(if modifiers.shift() { - Message::SaveFileAs - } else { - Message::SaveFile - }) - } - keyboard::Key::Character("n") => Some(Message::NewFile), - _ => None, - } - } else { - None - } - }) - } - - fn view(&self) -> Element<'_, Message> { - let header = row![pick_list( - THEMES, - Some(self.theme.target().clone()), - |theme| { Message::ToggleTheme(theme.into()) } - )] - .width(200); - let pane_grid = - PaneGrid::new(&self.pane_state, |id, pane, _is_maximized| { - let is_focused = Some(id) == self.focus; - match pane { - Panes::Designer => match &self.designer_page { - DesignerPage::DesignerView => designer_view::view( - &self.project.element_tree, - self.project.get_theme(), - is_focused, - ), - DesignerPage::CodeView => code_view::view( - &self.editor_content, - self.theme.value().clone(), - is_focused, - ), - }, - Panes::ElementList => { - element_list::view(self.element_list, is_focused) - } - } - }) - .width(Length::Fill) - .height(Length::Fill) - .spacing(10) - .on_resize(10, Message::PaneResized) - .on_click(Message::PaneClicked) - .on_drag(Message::PaneDragged); - - let content = Column::new() - .push(header) - .push(pane_grid) - .spacing(5) - .align_x(Alignment::Center) - .width(Length::Fill); - - Animation::new(&self.theme, container(content).height(Length::Fill)) - .on_update(Message::ToggleTheme) - .into() - } -} diff --git a/iced_builder/src/panes.rs b/iced_builder/src/panes.rs deleted file mode 100644 index 387662a..0000000 --- a/iced_builder/src/panes.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod code_view; -pub mod designer_view; -pub mod element_list; -mod style; diff --git a/iced_builder/src/panes/code_view.rs b/iced_builder/src/panes/code_view.rs deleted file mode 100644 index fe7801c..0000000 --- a/iced_builder/src/panes/code_view.rs +++ /dev/null @@ -1,49 +0,0 @@ -use iced::widget::{button, pane_grid, row, text, text_editor, Space}; -use iced::{Alignment, Length, Theme}; -use super::style; -use crate::icon::copy; -use crate::types::{DesignerPage, Message}; -use crate::widget::tip; - -pub fn view<'a>( - editor_content: &'a text_editor::Content, - theme: Theme, - is_focused: bool, -) -> pane_grid::Content<'a, Message> { - let title = row![ - text("Generated Code"), - Space::with_width(Length::Fill), - tip( - button(copy()).on_press(Message::CopyCode), - "Copy code to clipboard", - tip::Position::FollowCursor - ), - Space::with_width(20), - button("Switch to Designer view") - .on_press(Message::SwitchPage(DesignerPage::DesignerView)) - ] - .align_y(Alignment::Center); - let title_bar = pane_grid::TitleBar::new(title) - .padding(10) - .style(style::title_bar); - pane_grid::Content::new( - text_editor(editor_content) - .on_action(Message::EditorAction) - .highlight( - "rs", - if theme.to_string().contains("Dark") { - highlighter::Theme::SolarizedDark - } else { - highlighter::Theme::InspiredGitHub - }, - ) - .height(Length::Fill) - .padding(20), - ) - .title_bar(title_bar) - .style(if is_focused { - style::pane_focused - } else { - style::pane_active - }) -} diff --git a/iced_builder/src/panes/designer_view.rs b/iced_builder/src/panes/designer_view.rs deleted file mode 100644 index 76456db..0000000 --- a/iced_builder/src/panes/designer_view.rs +++ /dev/null @@ -1,37 +0,0 @@ -use iced::widget::{button, container, pane_grid, row, text, themer, Space}; -use iced::{Alignment, Element, Length}; - -use super::style; -use crate::types::{DesignerPage, Message, RenderedElement}; - -pub fn view<'a>( - element_tree: &Option<RenderedElement>, - designer_theme: iced::Theme, - is_focused: bool, -) -> pane_grid::Content<'a, Message> { - let el_tree: Element<'a, Message> = match element_tree { - Some(tree) => tree.clone().into(), - None => text("Open a project or begin creating one").into(), - }; - let content = container(themer(designer_theme, el_tree)) - .id(iced::widget::container::Id::new("drop_zone")) - .height(Length::Fill) - .width(Length::Fill); - let title = row![ - text("Designer"), - Space::with_width(Length::Fill), - button("Switch to Code view") - .on_press(Message::SwitchPage(DesignerPage::CodeView)), - ] - .align_y(Alignment::Center); - 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 - }) -} diff --git a/iced_builder/src/panes/element_list.rs b/iced_builder/src/panes/element_list.rs deleted file mode 100644 index 74188af..0000000 --- a/iced_builder/src/panes/element_list.rs +++ /dev/null @@ -1,49 +0,0 @@ -use iced::widget::{column, container, pane_grid, text, Column}; -use iced::{Alignment, Element, Length}; -use iced_drop::droppable; - -use super::style; -use crate::types::{ElementName, Message}; - -fn items_list_view<'a>(items: &'a [ElementName]) -> Element<'a, Message> { - let mut column = Column::new() - .spacing(20) - .align_x(Alignment::Center) - .width(Length::Fill); - - for item in items { - column = - column.push(droppable(text(item.clone().to_string())).on_drop( - move |point, rect| { - Message::DropNewElement(item.clone(), point, rect) - }, - )); - } - - container(column) - .width(Length::Fill) - .height(Length::Fill) - .into() -} - -pub fn view<'a>( - element_list: &'a [ElementName], - is_focused: bool, -) -> pane_grid::Content<'a, Message> { - let items_list = items_list_view(element_list); - let content = column![items_list] - .align_x(Alignment::Center) - .height(Length::Fill) - .width(Length::Fill); - let title = text("Element List"); - 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 - }) -} diff --git a/iced_builder/src/panes/style.rs b/iced_builder/src/panes/style.rs deleted file mode 100644 index 1eefb2d..0000000 --- a/iced_builder/src/panes/style.rs +++ /dev/null @@ -1,40 +0,0 @@ -use iced::widget::container::Style; -use iced::{Border, Theme}; - -pub fn title_bar(theme: &Theme) -> Style { - let palette = theme.extended_palette(); - - Style { - text_color: Some(palette.background.strong.text), - background: Some(palette.background.strong.color.into()), - ..Default::default() - } -} - -pub fn pane_active(theme: &Theme) -> Style { - let palette = theme.extended_palette(); - - Style { - background: Some(palette.background.weak.color.into()), - border: Border { - width: 1.0, - color: palette.background.strong.color, - ..Border::default() - }, - ..Default::default() - } -} - -pub fn pane_focused(theme: &Theme) -> Style { - let palette = theme.extended_palette(); - - Style { - background: Some(palette.background.weak.color.into()), - border: Border { - width: 4.0, - color: palette.background.strong.color, - ..Border::default() - }, - ..Default::default() - } -} diff --git a/iced_builder/src/types.rs b/iced_builder/src/types.rs deleted file mode 100644 index 161b5e1..0000000 --- a/iced_builder/src/types.rs +++ /dev/null @@ -1,48 +0,0 @@ -pub mod element_name; -pub mod project; -pub mod rendered_element; - -use std::path::PathBuf; - -pub use element_name::ElementName; -use iced::widget::{pane_grid, text_editor}; -use iced::Theme; -use iced_anim::SpringEvent; -pub use project::Project; -pub use rendered_element::*; - -use crate::Result; - -#[derive(Debug, Clone)] -pub enum Message { - ToggleTheme(SpringEvent<Theme>), - CopyCode, - SwitchPage(DesignerPage), - EditorAction(text_editor::Action), - RefreshEditorContent, - DropNewElement(ElementName, iced::Point, iced::Rectangle), - HandleNew( - ElementName, - Vec<(iced::advanced::widget::Id, iced::Rectangle)>, - ), - MoveElement(RenderedElement, iced::Point, iced::Rectangle), - HandleMove( - RenderedElement, - Vec<(iced::advanced::widget::Id, iced::Rectangle)>, - ), - PaneResized(pane_grid::ResizeEvent), - PaneClicked(pane_grid::Pane), - PaneDragged(pane_grid::DragEvent), - NewFile, - OpenFile, - FileOpened(Result<(PathBuf, Project)>), - SaveFile, - SaveFileAs, - FileSaved(Result<PathBuf>), -} - -#[derive(Debug, Clone)] -pub enum DesignerPage { - DesignerView, - CodeView, -} diff --git a/iced_builder/src/types/element_name.rs b/iced_builder/src/types/element_name.rs deleted file mode 100644 index e172227..0000000 --- a/iced_builder/src/types/element_name.rs +++ /dev/null @@ -1,86 +0,0 @@ -use serde::{Deserialize, Serialize}; - -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 { - 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>> { - 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 => Ok(None), - 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 f4dbcc4..0000000 --- a/iced_builder/src/types/project.rs +++ /dev/null @@ -1,142 +0,0 @@ -use std::path::{Path, PathBuf}; - -use iced::Theme; -use rust_format::{Config, Edition, Formatter, RustFmt}; -use serde::{Deserialize, Serialize}; - -use super::rendered_element::RenderedElement; -use crate::{Error, Result}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Project { - pub title: Option<String>, - pub theme: Option<String>, - pub element_tree: Option<RenderedElement>, -} - -impl Project { - pub fn new() -> Self { - Self { - title: None, - theme: None, - element_tree: None, - } - } - - pub fn get_theme(&self) -> Theme { - match &self.theme { - Some(theme) => match theme.as_str() { - "Light" => Theme::Light, - "Dark" => Theme::Dark, - "Dracula" => Theme::Dracula, - "Nord" => Theme::Nord, - "Solarized Light" => Theme::SolarizedLight, - "Solarized Dark" => Theme::SolarizedDark, - "Gruvbox Light" => Theme::GruvboxLight, - "Gruvbox Dark" => Theme::GruvboxDark, - "Catppuccin Latte" => Theme::CatppuccinLatte, - "Catppuccin Frappé" => Theme::CatppuccinFrappe, - "Catppuccin Macchiato" => Theme::CatppuccinMacchiato, - "Catppuccin Mocha" => Theme::CatppuccinMocha, - "Tokyo Night" => Theme::TokyoNight, - "Tokyo Night Storm" => Theme::TokyoNightStorm, - "Tokyo Night Light" => Theme::TokyoNightLight, - "Kanagawa Wave" => Theme::KanagawaWave, - "Kanagawa Dragon" => Theme::KanagawaDragon, - "Kanagawa Lotus" => Theme::KanagawaLotus, - "Moonfly" => Theme::Moonfly, - "Nightfly" => Theme::Nightfly, - "Oxocarbon" => Theme::Oxocarbon, - "Ferra" => Theme::Ferra, - _ => Theme::Dark, - }, - None => Theme::Dark, - } - } - - 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"]) - .pick_file() - .await - .ok_or(Error::DialogClosed)?; - - let path = picked_file.path().to_owned(); - - let contents = tokio::fs::read_to_string(&path).await?; - let element: Self = serde_json::from_str(&contents)?; - - Ok((path, element)) - } - - 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"]) - .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(&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}};"); - - app_code = format!( - r#"// Automatically generated by iced Builder - {app_code} - - 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", - }, - self.get_theme().to_string().replace(" ", "") - ); - 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)?) - } - 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 d4d1a6c..0000000 --- a/iced_builder/src/types/rendered_element.rs +++ /dev/null @@ -1,482 +0,0 @@ -use std::collections::BTreeMap; - -use blob_uuid::random_blob; -use iced::advanced::widget::Id; -use iced::{widget, Element, Length}; -use serde::{Deserialize, Serialize}; - -use super::ElementName; -use crate::types::Message; -use crate::Result; - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct RenderedElement { - id: String, - child_elements: Option<Vec<RenderedElement>>, - name: ElementName, - options: BTreeMap<String, Option<String>>, -} - -impl RenderedElement { - fn new(name: ElementName) -> Self { - Self { - id: random_blob(), - child_elements: None, - name, - options: BTreeMap::new(), - } - } - - fn with(name: ElementName, child_elements: Vec<RenderedElement>) -> Self { - Self { - id: random_blob(), - child_elements: Some(child_elements), - name, - options: BTreeMap::new(), - } - } - - pub fn get_id(&self) -> Id { - Id::new(self.id.clone()) - } - - pub fn find_by_id(&mut self, id: Id) -> Option<&mut Self> { - if self.get_id() == id.clone() { - return 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.clone()); - if element.is_some() { - return element; - } - } - return None; - } else { - return 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(vec![]) - .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; - } - } - } - return None; - } - } else { - return 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<()> { - 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<'a>(mut self, options: &[&'a 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 as_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()); - } - } - 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_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, -} - -impl Action { - pub fn new( - ids: Vec<Id>, - element_tree: &mut 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()].clone(); - if ids.len() > 2 && ids[ids.clone().len() - 1] == element_id - { - return Self::Stop; - } - element_id - } - _ => ids.last().cloned().unwrap(), - }; - let element = element_tree - .as_mut() - .unwrap() - .find_by_id(id.clone()) - .unwrap(); - - // Element IS a parent but ISN'T a non-empty container - match element.is_parent() - && !(element.name == ElementName::Container - && !element.is_empty()) - { - true => { - action = Self::PushFront(id); - } - false if ids.len() > 2 => { - let parent = element_tree - .as_mut() - .unwrap() - .find_by_id(ids[&ids.len() - 2].clone()) - .unwrap(); - - if parent.name == ElementName::Container - && parent.child_elements != Some(vec![]) - { - action = Self::Stop; - } else { - action = Self::InsertAfter( - ids[&ids.len() - 2].clone(), - ids[&ids.len() - 1].clone(), - ); - } - } - _ => {} - } - } - 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(), - ) -} diff --git a/iced_builder/src/widget.rs b/iced_builder/src/widget.rs deleted file mode 100644 index ed2073a..0000000 --- a/iced_builder/src/widget.rs +++ /dev/null @@ -1,21 +0,0 @@ -use iced::widget::{container, text, tooltip}; -use iced::Element; - -pub mod tip { - pub use super::tooltip::Position; -} - -pub fn tip<'a, Message: 'a>( - target: impl Into<Element<'a, Message>>, - tip: &'a str, - position: tip::Position, -) -> Element<'a, Message> { - tooltip( - target, - container(text(tip).size(14)) - .padding(5) - .style(container::rounded_box), - position, - ) - .into() -} |
