diff options
| author | pml68 <contact@pml68.dev> | 2025-03-31 00:57:02 +0200 |
|---|---|---|
| committer | pml68 <contact@pml68.dev> | 2025-03-31 00:58:24 +0200 |
| commit | d0e05b960cc626b7406582d61cc9a7cae485b915 (patch) | |
| tree | 3735269efee53ef8104768c1c728bf7b57fa33a5 /src | |
| parent | ci: add dependabot config file [skip ci] (diff) | |
| download | iced-builder-d0e05b960cc626b7406582d61cc9a7cae485b915.tar.gz | |
feat: remove GTK3 dependency
Diffstat (limited to 'src')
| -rw-r--r-- | src/dialogs.rs | 68 | ||||
| -rw-r--r-- | src/main.rs | 188 | ||||
| -rw-r--r-- | src/types.rs | 22 |
3 files changed, 171 insertions, 107 deletions
diff --git a/src/dialogs.rs b/src/dialogs.rs index 5954ff5..08513fd 100644 --- a/src/dialogs.rs +++ b/src/dialogs.rs @@ -1,35 +1,47 @@ -use rfd::{ - AsyncMessageDialog, MessageButtons, MessageDialog, MessageDialogResult, - MessageLevel, -}; +use iced::{Element, Task}; +use iced_dialog::button; -pub async fn error_dialog(description: impl Into<String>) { - let _ = AsyncMessageDialog::new() - .set_level(MessageLevel::Error) - .set_buttons(MessageButtons::Ok) - .set_title("Oops! Something went wrong.") - .set_description(description) - .show() - .await; +use crate::Message; +use crate::types::{DialogAction, DialogButtons}; + +pub const UNSAVED_CHANGES_TITLE: &str = "Unsaved changes"; +pub const WARNING_TITLE: &str = "Heads up!"; +pub const ERROR_TITLE: &str = "Oops! Something went wrong."; + +pub fn ok_button<'a>() -> Element<'a, Message> { + button("Ok").on_press(Message::DialogOk).into() } -pub async fn warning_dialog(description: impl Into<String>) { - let _ = AsyncMessageDialog::new() - .set_level(MessageLevel::Warning) - .set_buttons(MessageButtons::Ok) - .set_title("Heads up!") - .set_description(description) - .show() - .await; +pub fn cancel_button<'a>() -> Element<'a, Message> { + button("Cancel").on_press(Message::DialogCancel).into() } -pub fn unsaved_changes_dialog(description: impl Into<String>) -> bool { - let result = MessageDialog::new() - .set_level(MessageLevel::Warning) - .set_buttons(MessageButtons::OkCancel) - .set_title("Unsaved changes") - .set_description(description) - .show(); +pub fn error_dialog(description: impl Into<String>) -> Task<Message> { + Task::done(Message::OpenDialog( + ERROR_TITLE, + description.into(), + DialogButtons::Ok, + DialogAction::None, + )) +} + +pub fn warning_dialog(description: impl Into<String>) -> Task<Message> { + Task::done(Message::OpenDialog( + WARNING_TITLE, + description.into(), + DialogButtons::Ok, + DialogAction::None, + )) +} - matches!(result, MessageDialogResult::Ok) +pub fn unsaved_changes_dialog( + description: impl Into<String>, + action: DialogAction, +) -> Task<Message> { + Task::done(Message::OpenDialog( + UNSAVED_CHANGES_TITLE, + description.into(), + DialogButtons::OkCancel, + action, + )) } diff --git a/src/main.rs b/src/main.rs index e566ed5..d5715ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,16 +15,25 @@ use std::path::PathBuf; use std::sync::Arc; use config::Config; -use dialogs::{error_dialog, unsaved_changes_dialog, warning_dialog}; +use dialogs::{ + cancel_button, error_dialog, ok_button, unsaved_changes_dialog, + warning_dialog, +}; use error::Error; use iced::advanced::widget::Id; -use iced::widget::{Column, container, pane_grid, pick_list, row, text_editor}; +use iced::widget::{ + Column, container, pane_grid, pick_list, row, text, text_editor, +}; use iced::{Alignment, Element, Length, Task, Theme, clipboard, keyboard}; use iced_anim::transition::Easing; use iced_anim::{Animated, Animation}; +use iced_dialog::dialog::Dialog; use panes::{code_view, designer_view, element_list}; use tokio::runtime; -use types::{Action, DesignerPane, ElementName, Message, Project}; +use types::{ + Action, DesignerPane, DialogAction, DialogButtons, ElementName, Message, + Project, +}; fn main() -> Result<(), Box<dyn std::error::Error>> { let version = std::env::args() @@ -51,7 +60,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { .theme(|state| state.theme.value().clone()) .subscription(App::subscription) .run_with(move || App::new(config_load))?; - Ok(()) } @@ -65,6 +73,11 @@ struct App { pane_state: pane_grid::State<Panes>, focus: Option<pane_grid::Pane>, designer_page: DesignerPane, + dialog_is_open: bool, + dialog_title: &'static str, + dialog_content: String, + dialog_buttons: DialogButtons, + dialog_action: DialogAction, element_list: &'static [ElementName], editor_content: text_editor::Content, } @@ -96,11 +109,10 @@ impl App { Message::FileOpened, ) } else { - Task::future(warning_dialog(format!( + warning_dialog(format!( "The file {} does not exist, or isn't a file.", path.to_string_lossy() - ))) - .discard() + )) } } else { Task::none() @@ -117,6 +129,11 @@ impl App { pane_state: state, focus: None, designer_page: DesignerPane::DesignerView, + dialog_is_open: false, + dialog_title: "", + dialog_content: String::new(), + dialog_buttons: DialogButtons::None, + dialog_action: DialogAction::None, element_list: ElementName::ALL, editor_content: text_editor::Content::new(), }, @@ -146,41 +163,37 @@ impl App { fn update(&mut self, message: Message) -> Task<Message> { match message { - Message::SwitchTheme(event) => { - self.theme.update(event); - - Task::none() - } - Message::CopyCode => clipboard::write(self.editor_content.text()), - Message::SwitchPage(page) => { - self.designer_page = page; - Task::none() + Message::SwitchTheme(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); } - Task::none() } Message::RefreshEditorContent => { match self.project.app_code(&self.config) { Ok(code) => { self.editor_content = text_editor::Content::with_text(&code); - Task::none() } - Err(error) => Task::future(error_dialog(error)).discard(), + Err(error) => return error_dialog(error), } } Message::DropNewElement(name, point, _) => { - iced_drop::zones_on_point( + return iced_drop::zones_on_point( move |zones| Message::HandleNew(name.clone(), zones), point, None, None, - ) + ); } Message::HandleNew(name, zones) => { + let refresh_editor_task = + Task::done(Message::RefreshEditorContent); + let ids: Vec<Id> = zones.into_iter().map(|z| z.0).collect(); if !ids.is_empty() { let eltree_clone = self.project.element_tree.clone(); @@ -195,23 +208,21 @@ impl App { self.project.element_tree = Some(element.clone()); } Err(error) => { - return Task::future(error_dialog(error)) - .map(|_| Message::RefreshEditorContent); + return error_dialog(error) + .chain(refresh_editor_task); } _ => {} } - Task::done(Message::RefreshEditorContent) - } else { - Task::none() + return refresh_editor_task; } } Message::MoveElement(element, point, _) => { - iced_drop::zones_on_point( + return iced_drop::zones_on_point( move |zones| Message::HandleMove(element.clone(), zones), point, None, None, - ) + ); } Message::HandleMove(element, zones) => { let ids: Vec<Id> = zones.into_iter().map(|z| z.0).collect(); @@ -227,111 +238,119 @@ impl App { action, ); if let Err(error) = result { - return Task::future(error_dialog(error)).discard(); + return error_dialog(error); } self.is_dirty = true; - Task::done(Message::RefreshEditorContent) - } else { - Task::none() + return Task::done(Message::RefreshEditorContent); } } Message::PaneResized(pane_grid::ResizeEvent { split, ratio }) => { self.pane_state.resize(split, ratio); - Task::none() - } - Message::PaneClicked(pane) => { - self.focus = Some(pane); - Task::none() } + Message::PaneClicked(pane) => self.focus = Some(pane), Message::PaneDragged(pane_grid::DragEvent::Dropped { pane, target, - }) => { - self.pane_state.drop(pane, target); - Task::none() + }) => self.pane_state.drop(pane, target), + Message::PaneDragged(_) => {} + Message::OpenDialog(title, content, buttons, action) => { + self.dialog_title = title; + self.dialog_content = content; + self.dialog_buttons = buttons; + self.dialog_action = action; + self.dialog_is_open = true; } - Message::PaneDragged(_) => Task::none(), - Message::NewFile => { - if !self.is_loading { - if !self.is_dirty { + Message::CloseDialog => self.dialog_is_open = false, + Message::DialogOk => { + let close_dialog_task = Task::done(Message::CloseDialog); + + match self.dialog_action { + DialogAction::None => {} + DialogAction::NewFile => { + self.is_dirty = false; self.project = Project::new(); self.project_path = None; self.editor_content = text_editor::Content::new(); - } else if unsaved_changes_dialog( - "You have unsaved changes. Do you wish to discard these and create a new project?", - ) { + } + DialogAction::OpenFile => { self.is_dirty = false; + self.is_loading = true; + return Task::perform( + Project::from_file(self.config.clone()), + Message::FileOpened, + ) + .chain(close_dialog_task); + } + } + + return close_dialog_task; + } + Message::DialogCancel => return Task::done(Message::CloseDialog), + 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 { + return unsaved_changes_dialog( + "You have unsaved changes. Do you wish to discard these and create a new project?", + DialogAction::NewFile, + ); } } - - Task::none() } Message::OpenFile => { if !self.is_loading { if !self.is_dirty { self.is_loading = true; - Task::perform( - Project::from_file(self.config.clone()), - Message::FileOpened, - ) - } else if 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; - Task::perform( + return Task::perform( Project::from_file(self.config.clone()), Message::FileOpened, - ) + ); } else { - Task::none() + return unsaved_changes_dialog( + "You have unsaved changes. Do you wish to discard these and open another project?", + DialogAction::OpenFile, + ); } - } else { - Task::none() } } Message::FileOpened(result) => { self.is_loading = false; self.is_dirty = false; - match result { + return match result { Ok((path, project)) => { self.project = project; self.project_path = Some(path); Task::done(Message::RefreshEditorContent) } - Err(error) => Task::future(error_dialog(error)).discard(), - } + Err(error) => return error_dialog(error), + }; } Message::SaveFile => { if !self.is_loading { self.is_loading = true; - Task::perform( + return Task::perform( self.project .clone() .write_to_file(self.project_path.clone()), Message::FileSaved, - ) - } else { - Task::none() + ); } } Message::SaveFileAs => { if !self.is_loading { self.is_loading = true; - Task::perform( + return Task::perform( self.project.clone().write_to_file(None), Message::FileSaved, - ) - } else { - Task::none() + ); } } Message::FileSaved(result) => { @@ -341,12 +360,13 @@ impl App { Ok(path) => { self.project_path = Some(path); self.is_dirty = false; - Task::none() } - Err(error) => Task::future(error_dialog(error)).discard(), + Err(error) => return error_dialog(error), } } } + + Task::none() } fn subscription(&self) -> iced::Subscription<Message> { @@ -405,14 +425,26 @@ impl App { .on_click(Message::PaneClicked) .on_drag(Message::PaneDragged); - let content = Column::new() + let base = 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)) + let content = Dialog::with_buttons( + self.dialog_is_open, + container(base).height(Length::Fill), + text(&self.dialog_content), + match self.dialog_buttons { + DialogButtons::None => vec![], + DialogButtons::Ok => vec![ok_button()], + DialogButtons::OkCancel => vec![ok_button(), cancel_button()], + }, + ) + .title(self.dialog_title); + + Animation::new(&self.theme, content) .on_update(Message::SwitchTheme) .into() } diff --git a/src/types.rs b/src/types.rs index 73728e3..a7fae1c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -27,6 +27,10 @@ pub enum Message { PaneResized(pane_grid::ResizeEvent), PaneClicked(pane_grid::Pane), PaneDragged(pane_grid::DragEvent), + OpenDialog(&'static str, String, DialogButtons, DialogAction), + CloseDialog, + DialogOk, + DialogCancel, NewFile, OpenFile, FileOpened(Result<(PathBuf, Project), Error>), @@ -35,7 +39,23 @@ pub enum Message { FileSaved(Result<PathBuf, Error>), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy, Default)] +pub enum DialogButtons { + #[default] + None, + Ok, + OkCancel, +} + +#[derive(Debug, Clone, Copy, Default)] +pub enum DialogAction { + #[default] + None, + NewFile, + OpenFile, +} + +#[derive(Debug, Clone, Copy)] pub enum DesignerPane { DesignerView, CodeView, |
