diff options
Diffstat (limited to '')
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.lock | 149 | ||||
| -rw-r--r-- | Cargo.toml | 5 | ||||
| -rw-r--r-- | TODO.md | 2 | ||||
| -rw-r--r-- | src/dialogs.rs | 68 | ||||
| -rw-r--r-- | src/main.rs | 188 | ||||
| -rw-r--r-- | src/types.rs | 22 |
7 files changed, 203 insertions, 232 deletions
@@ -1,2 +1,3 @@ **/target/ **/*.json +rustc-ice-*.txt @@ -185,6 +185,8 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" dependencies = [ + "async-fs", + "async-net", "enumflags2", "futures-channel", "futures-util", @@ -192,7 +194,6 @@ dependencies = [ "raw-window-handle", "serde", "serde_repr", - "tokio", "url", "zbus", ] @@ -404,18 +405,6 @@ dependencies = [ ] [[package]] -name = "atk-sys" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -644,16 +633,6 @@ dependencies = [ ] [[package]] -name = "cairo-sys-rs" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] name = "calloop" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1590,36 +1569,6 @@ dependencies = [ ] [[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - -[[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1681,19 +1630,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] -name = "gio-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] name = "gl_generator" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1711,16 +1647,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" [[package]] -name = "glib-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] name = "gloo-timers" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1754,17 +1680,6 @@ dependencies = [ ] [[package]] -name = "gobject-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] name = "gpu-alloc" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1816,24 +1731,6 @@ dependencies = [ ] [[package]] -name = "gtk-sys" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] name = "guillotiere" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2074,6 +1971,7 @@ dependencies = [ "iced", "iced_anim", "iced_custom_highlighter", + "iced_dialog", "iced_drop", "iced_fontello", "rfd", @@ -2118,6 +2016,15 @@ dependencies = [ ] [[package]] +name = "iced_dialog" +version = "0.14.0-dev" +source = "git+https://github.com/pml68/iced_dialog?branch=iced%2Fpersonal#6bd6c48b55201116791c59e73779613d617615c7" +dependencies = [ + "iced_core", + "iced_widget", +] + +[[package]] name = "iced_drop" version = "0.1.0" dependencies = [ @@ -3454,18 +3361,6 @@ dependencies = [ ] [[package]] -name = "pango-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] name = "parking" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3642,6 +3537,12 @@ dependencies = [ ] [[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + +[[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4008,16 +3909,15 @@ dependencies = [ "ashpd 0.11.0", "block2 0.6.0", "dispatch2", - "glib-sys", - "gobject-sys", - "gtk-sys", "js-sys", "log", "objc2 0.6.0", "objc2-app-kit 0.3.0", "objc2-core-foundation", "objc2-foundation 0.3.0", + "pollster", "raw-window-handle", + "urlencoding", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -4872,9 +4772,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", - "signal-hook-registry", "socket2", - "tracing", "windows-sys 0.52.0", ] @@ -5157,6 +5055,12 @@ dependencies = [ ] [[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] name = "usvg" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6285,7 +6189,6 @@ dependencies = [ "serde", "serde_repr", "static_assertions", - "tokio", "tracing", "uds_windows", "windows-sys 0.59.0", @@ -19,12 +19,15 @@ iced.workspace = true iced_anim = { git = "https://github.com/pml68/iced_anim", features = ["derive"] } iced_custom_highlighter = { git = "https://github.com/pml68/iced_custom_highlighter", branch = "master" } iced_drop = { path = "iced_drop" } +iced_dialog = { git = "https://github.com/pml68/iced_dialog", branch = "iced/personal" } serde = { version = "1.0.217", features = ["derive"] } serde_json = "1.0.138" toml = "0.8.20" tokio = { version = "1.43", features = ["fs"] } tokio-stream = { version = "0.1", features = ["fs"] } -rfd = { version = "0.15.2", default-features = false, features = ["tokio", "gtk3"] } +# TODO: enable tokio when it actually compiles +# rfd = { version = "0.15.2", default-features = false, features = ["tokio", "xdg-portal"] } +rfd = "0.15.3" rust-format = "0.3.4" fxhash = "0.2.1" thiserror = "2.0.11" @@ -1,3 +1,3 @@ - [ ] Make Designer view more usable - [ ] Rework options backend to work on-project-load -- [ ] Remove GTK3 dependency +- [x] Remove GTK3 dependency 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, |
