summaryrefslogtreecommitdiff
path: root/iced_builder
diff options
context:
space:
mode:
authorPolesznyák Márk László <116908301+pml68@users.noreply.github.com>2025-01-11 23:13:07 +0100
committerGitHub <noreply@github.com>2025-01-11 23:13:07 +0100
commit103699beeb8bdce38bc5803cbe038e74cbc20e40 (patch)
treeb79e13b3decc778cc7c66af7187c647ae0a21a52 /iced_builder
parentMerge pull request #4 from pml68/feat/playground (diff)
parentrefactor: remove iced_drop & workspace (diff)
downloadiced-builder-103699beeb8bdce38bc5803cbe038e74cbc20e40.tar.gz
Merge pull request #5 from pml68/feat/config
Config done
Diffstat (limited to 'iced_builder')
-rw-r--r--iced_builder/Cargo.toml57
-rw-r--r--iced_builder/assets/windows/iced_builder.manifest8
-rw-r--r--iced_builder/assets/windows/iced_builder.rc3
-rw-r--r--iced_builder/build.rs12
-rw-r--r--iced_builder/fonts/icons.toml6
-rw-r--r--iced_builder/fonts/icons.ttfbin6348 -> 0 bytes
-rw-r--r--iced_builder/rustfmt.toml4
-rw-r--r--iced_builder/src/dialogs.rs21
-rw-r--r--iced_builder/src/error.rs44
-rw-r--r--iced_builder/src/icon.rs23
-rw-r--r--iced_builder/src/lib.rs9
-rw-r--r--iced_builder/src/main.rs346
-rw-r--r--iced_builder/src/panes.rs4
-rw-r--r--iced_builder/src/panes/code_view.rs49
-rw-r--r--iced_builder/src/panes/designer_view.rs37
-rw-r--r--iced_builder/src/panes/element_list.rs49
-rw-r--r--iced_builder/src/panes/style.rs40
-rw-r--r--iced_builder/src/types.rs48
-rw-r--r--iced_builder/src/types/element_name.rs86
-rw-r--r--iced_builder/src/types/project.rs142
-rwxr-xr-xiced_builder/src/types/rendered_element.rs482
-rw-r--r--iced_builder/src/widget.rs21
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
deleted file mode 100644
index 7af6b0e..0000000
--- a/iced_builder/fonts/icons.ttf
+++ /dev/null
Binary files differ
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()
-}