summaryrefslogtreecommitdiff
path: root/iced_builder/src
diff options
context:
space:
mode:
authorPolesznyák Márk László <116908301+pml68@users.noreply.github.com>2024-10-26 22:23:44 +0200
committerGitHub <noreply@github.com>2024-10-26 22:23:44 +0200
commit95fcd7aefa1aaf8f69c719d6d66003311c097e97 (patch)
treeda813fb6d9bab57d9b114280f761a9635a1a8903 /iced_builder/src
parentMerge pull request #2 from pml68/feat/drag-and-drop (diff)
parentfeat: implement `std::error::Error` for custom `Error` enum (diff)
downloadiced-builder-95fcd7aefa1aaf8f69c719d6d66003311c097e97.tar.gz
Merge pull request #3 from pml68/refactor/internal-restructuring
Internal restructuring done
Diffstat (limited to 'iced_builder/src')
-rw-r--r--iced_builder/src/lib.rs38
-rw-r--r--iced_builder/src/main.rs205
-rw-r--r--iced_builder/src/types/element_name.rs12
-rw-r--r--iced_builder/src/types/project.rs21
-rwxr-xr-xiced_builder/src/types/rendered_element.rs94
-rw-r--r--iced_builder/src/views/code_view.rs50
-rw-r--r--iced_builder/src/views/designer_view.rs40
-rw-r--r--iced_builder/src/views/element_list.rs48
-rw-r--r--iced_builder/src/views/mod.rs4
-rw-r--r--iced_builder/src/views/style.rs40
10 files changed, 284 insertions, 268 deletions
diff --git a/iced_builder/src/lib.rs b/iced_builder/src/lib.rs
index 420b14c..14a044e 100644
--- a/iced_builder/src/lib.rs
+++ b/iced_builder/src/lib.rs
@@ -1,4 +1,5 @@
pub mod types;
+pub mod views;
use std::path::PathBuf;
@@ -7,41 +8,27 @@ use types::{
element_name::ElementName, project::Project, rendered_element::RenderedElement, DesignerPage,
};
-#[derive(Debug, Clone)]
+use thiserror::Error;
+
+#[derive(Debug, Clone, Error)]
pub enum Error {
- IOError(std::io::ErrorKind),
+ #[error("an IO error accured: {0}")]
+ IOError(String),
+ #[error("a Serde error accured: {0}")]
SerdeError(String),
+ #[error("an RustFmt error accured: {0}")]
FormatError(String),
+ #[error("the element tree contains no matching element")]
NonExistentElement,
+ #[error("the file dialog has been closed without selecting a valid option")]
DialogClosed,
+ #[error("{0}")]
String(String),
}
-impl std::fmt::Display for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::SerdeError(string) | Self::FormatError(string) | Self::String(string) => {
- write!(f, "{}", string)
- }
- Self::IOError(kind) => {
- write!(f, "{}", kind)
- }
- Self::NonExistentElement => {
- write!(f, "The element tree contains no matching element.")
- }
- Self::DialogClosed => {
- write!(
- f,
- "The file dialog has been closed without selecting a valid option."
- )
- }
- }
- }
-}
-
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
- Self::IOError(value.kind())
+ Self::IOError(value.to_string())
}
}
@@ -87,5 +74,6 @@ pub enum Message {
OpenFile,
FileOpened(Result<(PathBuf, Project), Error>),
SaveFile,
+ SaveFileAs,
FileSaved(Result<PathBuf, Error>),
}
diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs
index fc7f18c..ed3f264 100644
--- a/iced_builder/src/main.rs
+++ b/iced_builder/src/main.rs
@@ -2,19 +2,19 @@ use std::path::PathBuf;
use iced::{
advanced::widget::Id,
- clipboard, highlighter, keyboard,
+ clipboard, keyboard,
widget::{
- button, column, container,
+ button, container,
pane_grid::{self, Pane, PaneGrid},
- row, text, text_editor, themer, tooltip, Column, Space,
+ row, text_editor, Column,
},
- Alignment, Element, Font, Length, Settings, Task, Theme,
+ Alignment, Element, Length, Settings, Task, Theme,
};
-use iced_builder::types::{
- element_name::ElementName, project::Project, rendered_element::ActionKind, DesignerPage,
+use iced_builder::{
+ types::{element_name::ElementName, project::Project, rendered_element::Action, DesignerPage},
+ views::{code_view, designer_view, element_list},
+ Message,
};
-use iced_builder::Message;
-use iced_drop::droppable;
fn main() -> iced::Result {
iced::application(App::title, App::update, App::view)
@@ -129,12 +129,11 @@ impl App {
Message::HandleNew(name, zones) => {
let ids: Vec<Id> = zones.into_iter().map(|z| z.0).collect();
if ids.len() > 0 {
- let action = ActionKind::new(ids, &mut self.project.content.clone(), None);
- let result = name.handle_action(self.project.content.as_mut(), action);
+ let action = Action::new(ids, &mut self.project.element_tree.clone(), None);
+ let result = name.handle_action(self.project.element_tree.as_mut(), action);
if let Ok(Some(ref element)) = result {
- self.project.content = Some(element.clone());
+ self.project.element_tree = Some(element.clone());
}
- println!("{:?}", result);
}
return Task::done(Message::RefreshEditorContent);
@@ -151,14 +150,12 @@ impl App {
Message::HandleMove(element, zones) => {
let ids: Vec<Id> = zones.into_iter().map(|z| z.0).collect();
if ids.len() > 0 {
- let action = ActionKind::new(
+ let action = Action::new(
ids,
- &mut self.project.content.clone(),
+ &mut self.project.element_tree.clone(),
Some(element.get_id()),
);
- let result = element.handle_action(self.project.content.as_mut(), action);
-
- println!("{result:?}");
+ let _ = element.handle_action(self.project.element_tree.as_mut(), action);
}
return Task::done(Message::RefreshEditorContent);
@@ -211,6 +208,16 @@ impl App {
);
}
}
+ 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;
@@ -227,7 +234,13 @@ impl App {
fn subscription(&self) -> iced::Subscription<Message> {
keyboard::on_key_press(|key, modifiers| match key.as_ref() {
keyboard::Key::Character("o") if modifiers.command() => Some(Message::OpenFile),
- keyboard::Key::Character("s") if modifiers.command() => Some(Message::SaveFile),
+ keyboard::Key::Character("s") if modifiers.command() => {
+ if modifiers.shift() {
+ Some(Message::SaveFileAs)
+ } else {
+ Some(Message::SaveFile)
+ }
+ }
keyboard::Key::Character("n") if modifiers.command() => Some(Message::NewFile),
_ => None,
})
@@ -242,96 +255,16 @@ impl App {
let is_focused = Some(id) == self.focus;
match pane {
Panes::Designer => match &self.designer_page {
- DesignerPage::Designer => {
- let el_tree = match self.project.content.clone() {
- Some(tree) => tree.as_element(),
- None => text("Open a project or begin creating one").into(),
- };
- let content = container(themer(self.project.get_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
- })
- }
+ DesignerPage::Designer => designer_view::view(
+ &self.project.element_tree,
+ self.project.get_theme(),
+ is_focused,
+ ),
DesignerPage::CodeView => {
- let title = row![
- text("Generated Code"),
- Space::with_width(Length::Fill),
- tooltip(
- button(
- container(
- text('\u{0e801}').font(Font::with_name("editor-icons"))
- )
- .center_x(30)
- )
- .on_press(Message::CopyCode),
- "Copy code to clipboard",
- tooltip::Position::FollowCursor
- ),
- Space::with_width(20),
- button("Switch to Designer view")
- .on_press(Message::SwitchPage(DesignerPage::Designer))
- ]
- .align_y(Alignment::Center);
- let title_bar = pane_grid::TitleBar::new(title)
- .padding(10)
- .style(style::title_bar);
- pane_grid::Content::new(
- text_editor(&self.editor_content)
- .on_action(Message::EditorAction)
- .highlight(
- "rs",
- if self.dark_theme {
- 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
- })
+ code_view::view(&self.editor_content, self.dark_theme, is_focused)
}
},
- Panes::ElementList => {
- let items_list = items_list_view(self.element_list.clone());
- 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
- })
- }
+ Panes::ElementList => element_list::view(&self.element_list, is_focused),
}
})
.width(Length::Fill)
@@ -351,65 +284,3 @@ impl App {
container(content).height(Length::Fill).into()
}
}
-
-fn items_list_view<'a>(items: Vec<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()
-}
-
-mod style {
- 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/element_name.rs b/iced_builder/src/types/element_name.rs
index 8d00814..93e12a1 100644
--- a/iced_builder/src/types/element_name.rs
+++ b/iced_builder/src/types/element_name.rs
@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use crate::Error;
use super::rendered_element::{
- button, column, container, image, row, svg, text, ActionKind, RenderedElement,
+ button, column, container, image, row, svg, text, Action, RenderedElement,
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -31,7 +31,7 @@ impl ElementName {
pub fn handle_action(
&self,
element_tree: Option<&mut RenderedElement>,
- action: ActionKind,
+ action: Action,
) -> Result<Option<RenderedElement>, Error> {
let element = match self {
Self::Text(_) => text(""),
@@ -43,9 +43,9 @@ impl ElementName {
Self::Column => column(None),
};
match action {
- ActionKind::Stop => Ok(None),
- ActionKind::AddNew => Ok(Some(element)),
- ActionKind::PushFront(id) => {
+ Action::Stop => 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)
@@ -53,7 +53,7 @@ impl ElementName {
.push_front(&element);
Ok(None)
}
- ActionKind::InsertAfter(parent_id, child_id) => {
+ Action::InsertAfter(parent_id, child_id) => {
element_tree
.ok_or(
"The action was of kind `InsertAfter`, but no element tree was provided.",
diff --git a/iced_builder/src/types/project.rs b/iced_builder/src/types/project.rs
index 0e0442a..52da41c 100644
--- a/iced_builder/src/types/project.rs
+++ b/iced_builder/src/types/project.rs
@@ -12,7 +12,7 @@ use super::rendered_element::RenderedElement;
pub struct Project {
pub title: Option<String>,
pub theme: Option<String>,
- pub content: Option<RenderedElement>,
+ pub element_tree: Option<RenderedElement>,
}
impl Project {
@@ -20,7 +20,7 @@ impl Project {
Self {
title: None,
theme: None,
- content: None,
+ element_tree: None,
}
}
@@ -92,10 +92,10 @@ impl Project {
Ok(path)
}
- pub fn app_code(self) -> Result<String, Error> {
- match &self.content {
- Some(el) => {
- let (imports, view) = el.codegen();
+ pub fn app_code(&self) -> Result<String, Error> {
+ 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!(
@@ -103,7 +103,7 @@ impl Project {
{app_code}
fn main() -> iced::Result {{
- iced::run("{}", State::update, State::view)
+ iced::application("{}", State::update, State::view).theme(iced::Theme::{}).run()
}}
#[derive(Default)]
@@ -119,10 +119,11 @@ impl Project {
{view}.into()
}}
}}"#,
- match &self.title {
- Some(t) => t,
+ match self.title {
+ Some(ref t) => t,
None => "New app",
- }
+ },
+ self.get_theme().to_string().replace(" ", "")
);
let config = Config::new_str()
.edition(Edition::Rust2021)
diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs
index e2bebfa..08d7ba3 100755
--- a/iced_builder/src/types/rendered_element.rs
+++ b/iced_builder/src/types/rendered_element.rs
@@ -109,10 +109,7 @@ impl RenderedElement {
pub fn insert_after(&mut self, id: Id, element: &RenderedElement) {
if let Some(child_elements) = self.child_elements.as_mut() {
- if let Some(index) = child_elements
- .iter()
- .position(|x| Id::new(x.id.clone()) == id)
- {
+ if let Some(index) = child_elements.iter().position(|x| x.get_id() == id) {
child_elements.insert(index + 1, element.clone());
} else {
child_elements.push(element.clone());
@@ -123,16 +120,16 @@ impl RenderedElement {
pub fn handle_action(
&self,
element_tree: Option<&mut RenderedElement>,
- action: ActionKind,
+ action: Action,
) -> Result<(), Error> {
let element_tree = element_tree.unwrap();
match action {
- ActionKind::Stop => Ok(()),
- ActionKind::AddNew => Err(
- "The action was of kind `AddNew`, but invoking it on an existing element tree is not possible.".into(),
+ Action::Stop => Ok(()),
+ Action::AddNew => Err(
+ "the action was of kind `AddNew`, but invoking it on an existing element tree is not possible".into(),
),
- ActionKind::PushFront(id) => {
+ Action::PushFront(id) => {
let old_parent = element_tree.find_parent(self).unwrap();
old_parent.remove(self);
@@ -141,7 +138,7 @@ impl RenderedElement {
Ok(())
}
- ActionKind::InsertAfter(parent_id, target_id) => {
+ Action::InsertAfter(parent_id, target_id) => {
let old_parent = element_tree.find_parent(self).unwrap();
old_parent.remove(self);
@@ -178,7 +175,8 @@ impl RenderedElement {
iced_drop::droppable(
widget::container(
widget::column![widget::text(self.name.clone().to_string()), children]
- .width(Length::Fill),
+ .width(Length::Fill)
+ .spacing(10),
)
.padding(10)
.style(widget::container::bordered_box),
@@ -189,23 +187,17 @@ impl RenderedElement {
.into()
}
- fn props_codegen(&self) -> String {
- let mut props_string = String::new();
+ 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 {
- props_string = format!("{props_string}.{k}({v})");
+ options = format!("{options}.{k}({v})");
}
}
- props_string
- }
-
- pub fn codegen(&self) -> (String, String) {
- let mut imports = String::new();
- let mut view = String::new();
- let props = self.props_codegen();
-
let mut elements = String::new();
if let Some(els) = &self.child_elements {
@@ -219,20 +211,20 @@ impl RenderedElement {
match &self.name {
ElementName::Container => {
imports = format!("{imports}container,");
- view = format!("{view}\ncontainer({elements}){props}");
+ view = format!("{view}\ncontainer({elements}){options}");
}
ElementName::Row => {
imports = format!("{imports}row,");
- view = format!("{view}\nrow![{elements}]{props}");
+ view = format!("{view}\nrow![{elements}]{options}");
}
ElementName::Column => {
imports = format!("{imports}column,");
- view = format!("{view}\ncolumn![{elements}]{props}");
+ view = format!("{view}\ncolumn![{elements}]{options}");
}
ElementName::Text(string) => {
imports = format!("{imports}text,");
view = format!(
- "{view}\ntext(\"{}\"){props}",
+ "{view}\ntext(\"{}\"){options}",
if *string == String::new() {
"New Text"
} else {
@@ -243,7 +235,7 @@ impl RenderedElement {
ElementName::Button(string) => {
imports = format!("{imports}button,");
view = format!(
- "{view}\nbutton(\"{}\"){props}",
+ "{view}\nbutton(\"{}\"){options}",
if *string == String::new() {
"New Button"
} else {
@@ -253,40 +245,28 @@ impl RenderedElement {
}
ElementName::Image(path) => {
imports = format!("{imports}image,");
- view = format!("{view}\nimage(\"{path}\"){props}");
+ view = format!("{view}\nimage(\"{path}\"){options}");
}
ElementName::SVG(path) => {
imports = format!("{imports}svg,");
- view = format!("{view}\nsvg(\"{path}\"){props}");
+ view = format!("{view}\nsvg(\"{path}\"){options}");
}
}
(imports, view)
}
-
- pub fn test() -> RenderedElement {
- let text1 = text("wow").option("height", "120.5").option("width", "230");
-
- let element = container(Some(row(Some(vec![
- text1,
- text("heh"),
- svg("/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg"),
- ]))));
-
- element
- }
}
impl std::fmt::Display for RenderedElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- let mut has_props = false;
+ 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_props = true;
+ has_options = true;
f.write_fmt(format_args!(
"\n{:width$.precision$}{}: {}",
"",
@@ -297,7 +277,7 @@ impl std::fmt::Display for RenderedElement {
))?;
}
}
- if has_props {
+ if has_options {
f.write_str("\n")?;
f.pad("")?;
}
@@ -320,14 +300,14 @@ impl std::fmt::Display for RenderedElement {
}
#[derive(Debug, Clone)]
-pub enum ActionKind {
+pub enum Action {
AddNew,
PushFront(Id),
InsertAfter(Id, Id),
Stop,
}
-impl ActionKind {
+impl Action {
pub fn new(
ids: Vec<Id>,
element_tree: &mut Option<RenderedElement>,
@@ -355,14 +335,14 @@ impl ActionKind {
.find_by_id(id.clone())
.unwrap();
- match (
- element.is_parent(),
- element.name == ElementName::Container && !element.is_empty(),
- ) {
- (true, false) => {
+ // 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);
}
- _ if ids.len() > 2 => {
+ false if ids.len() > 2 => {
let parent = element_tree
.as_mut()
.unwrap()
@@ -416,15 +396,9 @@ pub fn container(content: Option<RenderedElement>) -> RenderedElement {
}
pub fn row(child_elements: Option<Vec<RenderedElement>>) -> RenderedElement {
- match child_elements {
- Some(els) => RenderedElement::with(ElementName::Row, els),
- None => RenderedElement::with(ElementName::Row, vec![]),
- }
+ RenderedElement::with(ElementName::Row, child_elements.unwrap_or_default())
}
pub fn column(child_elements: Option<Vec<RenderedElement>>) -> RenderedElement {
- match child_elements {
- Some(els) => RenderedElement::with(ElementName::Column, els),
- None => RenderedElement::with(ElementName::Column, vec![]),
- }
+ RenderedElement::with(ElementName::Column, child_elements.unwrap_or_default())
}
diff --git a/iced_builder/src/views/code_view.rs b/iced_builder/src/views/code_view.rs
new file mode 100644
index 0000000..4515687
--- /dev/null
+++ b/iced_builder/src/views/code_view.rs
@@ -0,0 +1,50 @@
+use super::style;
+use crate::{types::DesignerPage, Message};
+use iced::{
+ highlighter,
+ widget::{button, container, pane_grid, row, text, text_editor, tooltip, Space},
+ Alignment, Font, Length,
+};
+
+pub fn view<'a>(
+ editor_content: &'a text_editor::Content,
+ dark_theme: bool,
+ is_focused: bool,
+) -> pane_grid::Content<'a, Message> {
+ let title = row![
+ text("Generated Code"),
+ Space::with_width(Length::Fill),
+ tooltip(
+ button(container(text('\u{0e801}').font(Font::with_name("editor-icons"))).center_x(30))
+ .on_press(Message::CopyCode),
+ "Copy code to clipboard",
+ tooltip::Position::FollowCursor
+ ),
+ Space::with_width(20),
+ button("Switch to Designer view").on_press(Message::SwitchPage(DesignerPage::Designer))
+ ]
+ .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 dark_theme {
+ 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/views/designer_view.rs b/iced_builder/src/views/designer_view.rs
new file mode 100644
index 0000000..6f31a51
--- /dev/null
+++ b/iced_builder/src/views/designer_view.rs
@@ -0,0 +1,40 @@
+use super::style;
+use crate::{
+ types::{rendered_element::RenderedElement, DesignerPage},
+ Message,
+};
+use iced::{
+ widget::{button, container, pane_grid, row, text, themer, Space},
+ Alignment, Length,
+};
+
+pub fn view<'a>(
+ element_tree: &Option<RenderedElement>,
+ designer_theme: iced::Theme,
+ is_focused: bool,
+) -> pane_grid::Content<'a, Message> {
+ let el_tree = match element_tree {
+ Some(tree) => tree.clone().as_element(),
+ 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/views/element_list.rs b/iced_builder/src/views/element_list.rs
new file mode 100644
index 0000000..f0fdd2f
--- /dev/null
+++ b/iced_builder/src/views/element_list.rs
@@ -0,0 +1,48 @@
+use super::style;
+use crate::{types::element_name::ElementName, Message};
+use iced::{
+ widget::{column, container, pane_grid, text, Column},
+ Alignment, Element, Length,
+};
+use iced_drop::droppable;
+
+fn items_list_view<'a>(items: &'a Vec<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 Vec<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/views/mod.rs b/iced_builder/src/views/mod.rs
new file mode 100644
index 0000000..387662a
--- /dev/null
+++ b/iced_builder/src/views/mod.rs
@@ -0,0 +1,4 @@
+pub mod code_view;
+pub mod designer_view;
+pub mod element_list;
+mod style;
diff --git a/iced_builder/src/views/style.rs b/iced_builder/src/views/style.rs
new file mode 100644
index 0000000..1eefb2d
--- /dev/null
+++ b/iced_builder/src/views/style.rs
@@ -0,0 +1,40 @@
+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()
+ }
+}