From 726e9023c4629d70410174ddbd1e6d43f7583fe5 Mon Sep 17 00:00:00 2001 From: pml68 Date: Mon, 28 Oct 2024 01:00:07 +0100 Subject: feat: implement very basic playground fix: `is_dirty` was never checked for in the main update fn, allowing unsaved work to easily be overwritten --- iced_builder/src/lib.rs | 14 +++---- iced_builder/src/main.rs | 14 ++++--- iced_builder/src/types/element_name.rs | 1 + iced_builder/src/types/mod.rs | 4 ++ iced_builder/src/types/rendered_element.rs | 67 ++++++++++++++++++++++++++++-- iced_builder/src/views/designer_view.rs | 8 ++-- iced_builder/src/views/element_list.rs | 2 +- 7 files changed, 89 insertions(+), 21 deletions(-) (limited to 'iced_builder/src') diff --git a/iced_builder/src/lib.rs b/iced_builder/src/lib.rs index 14a044e..c6f3616 100644 --- a/iced_builder/src/lib.rs +++ b/iced_builder/src/lib.rs @@ -1,7 +1,7 @@ pub mod types; pub mod views; -use std::path::PathBuf; +use std::{io, path::PathBuf}; use iced::widget::{pane_grid, text_editor}; use types::{ @@ -12,7 +12,7 @@ use thiserror::Error; #[derive(Debug, Clone, Error)] pub enum Error { - #[error("an IO error accured: {0}")] + #[error("an I/O error accured: {0}")] IOError(String), #[error("a Serde error accured: {0}")] SerdeError(String), @@ -23,11 +23,11 @@ pub enum Error { #[error("the file dialog has been closed without selecting a valid option")] DialogClosed, #[error("{0}")] - String(String), + Other(String), } -impl From for Error { - fn from(value: std::io::Error) -> Self { +impl From for Error { + fn from(value: io::Error) -> Self { Self::IOError(value.to_string()) } } @@ -46,13 +46,13 @@ impl From for Error { impl From<&'static str> for Error { fn from(value: &'static str) -> Self { - Self::String(value.to_owned()) + Self::Other(value.to_owned()) } } #[derive(Debug, Clone)] pub enum Message { - ToggleTheme, + ToggleDarkMode, CopyCode, SwitchPage(DesignerPage), EditorAction(text_editor::Action), diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index ed3f264..d022f29 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -11,7 +11,7 @@ use iced::{ Alignment, Element, Length, Settings, Task, Theme, }; use iced_builder::{ - types::{element_name::ElementName, project::Project, rendered_element::Action, DesignerPage}, + types::{Action, DesignerPage, ElementName, Project}, views::{code_view, designer_view, element_list}, Message, }; @@ -101,7 +101,7 @@ impl App { fn update(&mut self, message: Message) -> Task { match message { - Message::ToggleTheme => self.dark_theme = !self.dark_theme, + Message::ToggleDarkMode => self.dark_theme = !self.dark_theme, Message::CopyCode => return clipboard::write(self.editor_content.text()), Message::SwitchPage(page) => self.designer_page = page, Message::EditorAction(action) => { @@ -134,6 +134,8 @@ impl App { if let Ok(Some(ref element)) = result { self.project.element_tree = Some(element.clone()); } + + self.is_dirty = true; } return Task::done(Message::RefreshEditorContent); @@ -156,6 +158,8 @@ impl App { Some(element.get_id()), ); let _ = element.handle_action(self.project.element_tree.as_mut(), action); + + self.is_dirty = true; } return Task::done(Message::RefreshEditorContent); @@ -178,7 +182,7 @@ impl App { } } Message::OpenFile => { - if !self.is_loading { + if !self.is_loading && !self.is_dirty { self.is_loading = true; return Task::perform(Project::from_file(), Message::FileOpened); @@ -247,8 +251,8 @@ impl App { } fn view(&self) -> Element { - let header = row![button("Toggle Theme") - .on_press(Message::ToggleTheme) + let header = row![button("Toggle Dark Mode") + .on_press(Message::ToggleDarkMode) .padding(5)] .width(200); let pane_grid = PaneGrid::new(&self.pane_state, |id, pane, _is_maximized| { diff --git a/iced_builder/src/types/element_name.rs b/iced_builder/src/types/element_name.rs index 93e12a1..bf38120 100644 --- a/iced_builder/src/types/element_name.rs +++ b/iced_builder/src/types/element_name.rs @@ -44,6 +44,7 @@ impl ElementName { }; match action { Action::Stop => Ok(None), + Action::Drop => Ok(None), Action::AddNew => Ok(Some(element)), Action::PushFront(id) => { element_tree diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs index a48a2d8..0012905 100644 --- a/iced_builder/src/types/mod.rs +++ b/iced_builder/src/types/mod.rs @@ -2,6 +2,10 @@ pub mod element_name; pub mod project; pub mod rendered_element; +pub use element_name::ElementName; +pub use project::Project; +pub use rendered_element::*; + #[derive(Debug, Clone)] pub enum DesignerPage { Designer, diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 08d7ba3..3dad8e7 100755 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -7,14 +7,14 @@ use unique_id::{string::StringGenerator, Generator}; use crate::{Error, Message}; -use super::element_name::ElementName; +use super::ElementName; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RenderedElement { id: String, - pub child_elements: Option>, - pub name: ElementName, - pub options: IndexMap>, + child_elements: Option>, + name: ElementName, + options: IndexMap>, } impl RenderedElement { @@ -126,6 +126,12 @@ impl RenderedElement { match action { Action::Stop => Ok(()), + Action::Drop => { + let parent = element_tree.find_parent(self).unwrap(); + parent.remove(self); + + Ok(()) + } Action::AddNew => Err( "the action was of kind `AddNew`, but invoking it on an existing element tree is not possible".into(), ), @@ -299,11 +305,62 @@ impl std::fmt::Display for RenderedElement { } } +impl<'a> From for Element<'a, Message> { + fn from(value: RenderedElement) -> Self { + let child_elements = match value.child_elements { + Some(ref elements) => elements.clone(), + None => vec![], + }; + + let content: Element<'a, Message> = match value.name.clone() { + ElementName::Text(s) => { + if s == String::new() { + widget::text("New Text").into() + } else { + widget::text(s).into() + } + } + ElementName::Button(s) => { + if s == String::new() { + widget::button(widget::text("New Button")).into() + } else { + widget::button(widget::text(s)).into() + } + } + ElementName::SVG(p) => widget::svg(p).into(), + ElementName::Image(p) => widget::image(p).into(), + ElementName::Container => widget::container(if child_elements.len() == 1 { + child_elements[0].clone().into() + } else { + Element::from("") + }) + .padding(20) + .into(), + ElementName::Row => { + widget::Row::from_iter(child_elements.into_iter().map(|el| el.into())) + .padding(20) + .into() + } + ElementName::Column => { + widget::Column::from_iter(child_elements.into_iter().map(|el| el.into())) + .padding(20) + .into() + } + }; + iced_drop::droppable(content) + .id(value.get_id()) + .drag_hide(true) + .on_drop(move |point, rect| Message::MoveElement(value.clone(), point, rect)) + .into() + } +} + #[derive(Debug, Clone)] pub enum Action { AddNew, PushFront(Id), InsertAfter(Id, Id), + Drop, Stop, } @@ -317,6 +374,8 @@ impl Action { if ids.len() == 1 { if element_tree.is_none() { action = Self::AddNew; + } else { + action = Self::Drop; } } else { let id: Id = match source_id { diff --git a/iced_builder/src/views/designer_view.rs b/iced_builder/src/views/designer_view.rs index 6f31a51..2d88493 100644 --- a/iced_builder/src/views/designer_view.rs +++ b/iced_builder/src/views/designer_view.rs @@ -1,11 +1,11 @@ use super::style; use crate::{ - types::{rendered_element::RenderedElement, DesignerPage}, + types::{DesignerPage, RenderedElement}, Message, }; use iced::{ widget::{button, container, pane_grid, row, text, themer, Space}, - Alignment, Length, + Alignment, Element, Length, }; pub fn view<'a>( @@ -13,8 +13,8 @@ pub fn view<'a>( designer_theme: iced::Theme, is_focused: bool, ) -> pane_grid::Content<'a, Message> { - let el_tree = match element_tree { - Some(tree) => tree.clone().as_element(), + 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)) diff --git a/iced_builder/src/views/element_list.rs b/iced_builder/src/views/element_list.rs index f0fdd2f..a9aeb1f 100644 --- a/iced_builder/src/views/element_list.rs +++ b/iced_builder/src/views/element_list.rs @@ -1,5 +1,5 @@ use super::style; -use crate::{types::element_name::ElementName, Message}; +use crate::{types::ElementName, Message}; use iced::{ widget::{column, container, pane_grid, text, Column}, Alignment, Element, Length, -- cgit v1.2.3