summaryrefslogtreecommitdiff
path: root/iced_builder/src/main.rs
diff options
context:
space:
mode:
authorPolesznyák Márk László <116908301+pml68@users.noreply.github.com>2024-12-26 00:12:06 +0100
committerGitHub <noreply@github.com>2024-12-26 00:12:06 +0100
commit0ae3ec6cc9babcab39c76f023606229a151916ab (patch)
treee92d0109599622984b2c485cc020951da288cec3 /iced_builder/src/main.rs
parentMerge pull request #3 from pml68/refactor/internal-restructuring (diff)
parentfeat: add `tip` widget helper from `hecrj/icebreaker` (diff)
downloadiced-builder-0ae3ec6cc9babcab39c76f023606229a151916ab.tar.gz
Merge pull request #4 from pml68/feat/playground
Playground done **for now**
Diffstat (limited to '')
-rw-r--r--iced_builder/src/main.rs270
1 files changed, 165 insertions, 105 deletions
diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs
index ed3f264..a041c6f 100644
--- a/iced_builder/src/main.rs
+++ b/iced_builder/src/main.rs
@@ -1,28 +1,24 @@
use std::path::PathBuf;
-use iced::{
- advanced::widget::Id,
- clipboard, keyboard,
- widget::{
- button, container,
- pane_grid::{self, Pane, PaneGrid},
- row, text_editor, Column,
- },
- Alignment, Element, Length, Settings, Task, Theme,
-};
-use iced_builder::{
- types::{element_name::ElementName, project::Project, rendered_element::Action, DesignerPage},
- views::{code_view, designer_view, element_list},
- Message,
+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)
- .settings(Settings {
- fonts: vec![include_bytes!("../fonts/icons.ttf").as_slice().into()],
- ..Settings::default()
- })
- .theme(App::theme)
+ .font(icon::FONT)
+ .theme(|state| state.theme.value().clone())
.subscription(App::subscription)
.run_with(App::new)
}
@@ -32,11 +28,11 @@ struct App {
is_loading: bool,
project_path: Option<PathBuf>,
project: Project,
- dark_theme: bool,
+ theme: Spring<Theme>,
pane_state: pane_grid::State<Panes>,
focus: Option<Pane>,
designer_page: DesignerPage,
- element_list: Vec<ElementName>,
+ element_list: &'static [ElementName],
editor_content: text_editor::Content,
}
@@ -48,23 +44,25 @@ enum Panes {
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)),
- });
+ 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(),
- dark_theme: true,
+ theme: Spring::new(Theme::SolarizedDark),
pane_state: state,
focus: None,
- designer_page: DesignerPage::Designer,
- element_list: ElementName::ALL.to_vec(),
+ designer_page: DesignerPage::DesignerView,
+ element_list: ElementName::ALL,
editor_content: text_editor::Content::new(),
},
Task::none(),
@@ -91,18 +89,14 @@ impl App {
format!("iced Builder{project_name}{saved_state}")
}
- fn theme(&self) -> iced::Theme {
- if self.dark_theme {
- Theme::SolarizedDark
- } else {
- Theme::SolarizedLight
- }
- }
-
fn update(&mut self, message: Message) -> Task<Message> {
match message {
- Message::ToggleTheme => self.dark_theme = !self.dark_theme,
- Message::CopyCode => return clipboard::write(self.editor_content.text()),
+ 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 {
@@ -110,12 +104,13 @@ impl App {
}
}
Message::RefreshEditorContent => {
- let code = self
- .project
- .clone()
- .app_code()
- .unwrap_or_else(|err| err.to_string());
- self.editor_content = text_editor::Content::with_text(&code);
+ 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(
@@ -129,14 +124,26 @@ impl App {
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);
- if let Ok(Some(ref element)) = result {
- self.project.element_tree = Some(element.clone());
+ 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()),
+ _ => {}
}
- }
- return Task::done(Message::RefreshEditorContent);
+ self.is_dirty = true;
+ return Task::done(Message::RefreshEditorContent);
+ }
}
Message::MoveElement(element, point, _) => {
return iced_drop::zones_on_point(
@@ -155,10 +162,17 @@ impl App {
&mut self.project.element_tree.clone(),
Some(element.get_id()),
);
- let _ = element.handle_action(self.project.element_tree.as_mut(), action);
- }
+ let result = element.handle_action(
+ self.project.element_tree.as_mut(),
+ action,
+ );
+ if let Err(error) = result {
+ error_dialog(error.to_string());
+ }
- return Task::done(Message::RefreshEditorContent);
+ self.is_dirty = true;
+ return Task::done(Message::RefreshEditorContent);
+ }
}
Message::PaneResized(pane_grid::ResizeEvent { split, ratio }) => {
self.pane_state.resize(split, ratio);
@@ -166,34 +180,62 @@ impl App {
Message::PaneClicked(pane) => {
self.focus = Some(pane);
}
- Message::PaneDragged(pane_grid::DragEvent::Dropped { pane, target }) => {
+ Message::PaneDragged(pane_grid::DragEvent::Dropped {
+ pane,
+ target,
+ }) => {
self.pane_state.drop(pane, target);
}
Message::PaneDragged(_) => {}
Message::NewFile => {
if !self.is_loading {
- self.project = Project::new();
- self.project_path = None;
- self.editor_content = text_editor::Content::new();
+ 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 {
- self.is_loading = true;
+ if !self.is_dirty {
+ self.is_loading = true;
- return Task::perform(Project::from_file(), Message::FileOpened);
+ 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;
- if let Ok((path, project)) = result {
- 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()),
- );
+ 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 => {
@@ -221,9 +263,12 @@ impl App {
Message::FileSaved(result) => {
self.is_loading = false;
- if let Ok(path) = result {
- self.project_path = Some(path);
- self.is_dirty = false;
+ match result {
+ Ok(path) => {
+ self.project_path = Some(path);
+ self.is_dirty = false;
+ }
+ Err(error) => error_dialog(error.to_string()),
}
}
}
@@ -232,47 +277,60 @@ 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() => {
- if modifiers.shift() {
- Some(Message::SaveFileAs)
- } else {
- Some(Message::SaveFile)
+ 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
}
- keyboard::Key::Character("n") if modifiers.command() => Some(Message::NewFile),
- _ => None,
})
}
- fn view(&self) -> Element<Message> {
- let header = row![button("Toggle Theme")
- .on_press(Message::ToggleTheme)
- .padding(5)]
+ 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::Designer => designer_view::view(
- &self.project.element_tree,
- self.project.get_theme(),
- is_focused,
- ),
- DesignerPage::CodeView => {
- code_view::view(&self.editor_content, self.dark_theme, is_focused)
+ 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)
}
- },
- 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);
+ }
+ })
+ .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)
@@ -281,6 +339,8 @@ impl App {
.align_x(Alignment::Center)
.width(Length::Fill);
- container(content).height(Length::Fill).into()
+ Animation::new(&self.theme, container(content).height(Length::Fill))
+ .on_update(Message::ToggleTheme)
+ .into()
}
}