From f5c35e48c480355036778d26aacde498e5c15e68 Mon Sep 17 00:00:00 2001 From: pml68 Date: Sun, 22 Sep 2024 23:53:02 +0200 Subject: feat: restructure project, start drag and drop --- iced_builder/src/codegen/mod.rs | 158 ++++++++++++++ iced_builder/src/lib.rs | 0 iced_builder/src/main.rs | 326 +++++++++++++++++++++++++++++ iced_builder/src/types/mod.rs | 54 +++++ iced_builder/src/types/rendered_element.rs | 47 +++++ 5 files changed, 585 insertions(+) create mode 100644 iced_builder/src/codegen/mod.rs create mode 100644 iced_builder/src/lib.rs create mode 100644 iced_builder/src/main.rs create mode 100644 iced_builder/src/types/mod.rs create mode 100644 iced_builder/src/types/rendered_element.rs (limited to 'iced_builder/src') diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs new file mode 100644 index 0000000..88c0c61 --- /dev/null +++ b/iced_builder/src/codegen/mod.rs @@ -0,0 +1,158 @@ +use rust_format::{Config, Edition, Formatter, RustFmt}; + +use crate::types::{rendered_element::RenderedElement, ElementName}; + +impl RenderedElement { + fn props_codegen(&self) -> String { + let mut props_string = String::new(); + + for (k, v) in self.props.clone() { + props_string = format!("{props_string}.{k}({v})"); + } + + props_string + } + + 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(); + + match self.name { + ElementName::Column | ElementName::Row | ElementName::Container => { + for element in &self.child_elements { + let (c_imports, children) = element.codegen(); + imports = format!("{imports}{c_imports}"); + elements = format!("{elements}{},", children); + } + } + _ => {} + } + + match &self.name { + ElementName::Container => { + imports = format!("{imports}container,"); + view = if self.child_elements.len() < 2 { + format!("{view}\ncontainer({elements}){props}") + } else { + format!("{view}\ncontainer(){props}") + }; + } + ElementName::Row => { + imports = format!("{imports}row,"); + view = format!("{view}\nrow![{elements}]{props}"); + } + ElementName::Column => { + imports = format!("{imports}column,"); + view = format!("{view}\ncolumn![{elements}]{props}"); + } + ElementName::Text(string) => { + imports = format!("{imports}text,"); + view = format!( + "{view}\ntext(\"{}\"){props}", + if *string == String::new() { + "New Text" + } else { + string + } + ); + } + ElementName::Button(string) => { + imports = format!("{imports}button,"); + view = format!( + "{view}\nbutton(\"{}\"){props}", + if *string == String::new() { + "New Button" + } else { + string + } + ); + } + ElementName::Image(path) => { + imports = format!("{imports}image,"); + view = format!("{view}\nimage(\"{path}\"){props}"); + } + ElementName::SVG(path) => { + imports = format!("{imports}svg,"); + view = format!("{view}\nsvg(\"{path}\"){props}"); + } + } + + (imports, view) + } + + pub fn app_code( + &self, + title: &str, + theme: Option, + ) -> Result> { + let (imports, view) = self.codegen(); + let mut app_code = format!("use iced::{{widget::{{{imports}}},Sandbox,Settings,Element}};"); + + app_code = format!( + r#"{app_code} + + fn main() -> iced::Result {{ + App::run(Settings::default()) + }} + + struct App; + + impl Sandbox for App {{ + type Message = (); + + fn new() -> Self {{ + Self {{}} + }} + + fn title(&self) -> String {{ + "{title}".into() + }} + + fn theme(&self) -> iced::Theme {{ + iced::Theme::{} + }} + + fn update(&mut self, message: Self::Message) {{ + + }} + + fn view(&self) -> Element {{ + {view}.into() + }} + }}"#, + if let Some(c) = theme { + c.to_string().replace(' ', "") + } else { + "default()".to_owned() + } + ); + let config = Config::new_str() + .edition(Edition::Rust2021) + .option("trailing_comma", "Never") + .option("imports_granularity", "Crate"); + let rustfmt = RustFmt::from_config(config); + Ok(rustfmt.format_str(app_code)?) + } + + pub fn test() -> String { + let mut text1 = RenderedElement::new(ElementName::Text("wow")); + text1.set_property("height", "120.5"); + text1.set_property("width", "230"); + + let element = RenderedElement::new(ElementName::Container).push(RenderedElement::from_vec( + ElementName::Row, + vec![ + text1, + RenderedElement::new(ElementName::Text("heh")), + RenderedElement::new(ElementName::SVG( + "/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg", + )), + ], + )); + + element.app_code("new app", None).unwrap() + } +} diff --git a/iced_builder/src/lib.rs b/iced_builder/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs new file mode 100644 index 0000000..e966614 --- /dev/null +++ b/iced_builder/src/main.rs @@ -0,0 +1,326 @@ +mod codegen; +mod types; + +use iced::{ + clipboard, executor, + highlighter::{self, Highlighter}, + theme, + widget::{ + button, column, container, + pane_grid::{self, Pane, PaneGrid}, + row, text, text_editor, tooltip, Column, Space, + }, + Alignment, Application, Color, Command, Element, Font, Length, Settings, +}; +use iced_drop::droppable; +use types::{rendered_element::RenderedElement, DesignerPage, DesignerState}; + +fn main() -> iced::Result { + App::run(Settings { + fonts: vec![include_bytes!("../fonts/icons.ttf").as_slice().into()], + ..Settings::default() + }) +} + +struct App { + is_saved: bool, + current_project: Option, + dark_theme: bool, + pane_state: pane_grid::State, + focus: Option, + designer_state: DesignerState, + element_list: Vec, + editor_content: text_editor::Content, +} + +#[derive(Debug, Clone)] +enum Message { + ToggleTheme, + CopyCode, + Drop(types::ElementName, iced::Point, iced::Rectangle), + HandleZones( + types::ElementName, + Vec<(iced::advanced::widget::Id, iced::Rectangle)>, + ), + Resized(pane_grid::ResizeEvent), + Clicked(pane_grid::Pane), + PaneDragged(pane_grid::DragEvent), +} + +#[derive(Clone, Debug)] +enum Panes { + Designer, + ElementList, +} + +impl Application for App { + type Message = Message; + type Theme = theme::Theme; + type Executor = executor::Default; + type Flags = (); + + fn new(_flags: ()) -> (Self, Command) { + 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_saved: true, + current_project: None, + dark_theme: true, + pane_state: state, + focus: None, + designer_state: DesignerState { + designer_content: vec![], + designer_page: DesignerPage::Designer, + }, + element_list: types::ElementName::ALL.to_vec(), + editor_content: text_editor::Content::with_text(&RenderedElement::test()), + }, + Command::none(), + ) + } + + fn title(&self) -> String { + let saved_state = if self.is_saved { "" } else { " *" }; + + let project_name = match &self.current_project { + Some(n) => format!(" - {n}"), + None => "".to_owned(), + }; + + format!("iced Builder{project_name}{saved_state}") + } + + fn theme(&self) -> iced::Theme { + if self.dark_theme { + theme::Theme::CatppuccinMocha + } else { + theme::Theme::CatppuccinLatte + } + } + + fn update(&mut self, message: Message) -> Command { + match message { + Message::ToggleTheme => self.dark_theme = !self.dark_theme, + Message::CopyCode => return clipboard::write(self.editor_content.text()), + Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { + self.pane_state.resize(split, ratio); + } + Message::Clicked(pane) => { + self.focus = Some(pane); + } + Message::Drop(name, point, _) => { + return iced_drop::zones_on_point( + move |zones| Message::HandleZones(name, zones), + point, + None, + None, + ) + .into() + } + Message::HandleZones(name, zones) => { + println!("{:?}\n{name}", zones); + } + Message::PaneDragged(pane_grid::DragEvent::Dropped { pane, target }) => { + self.pane_state.drop(pane, target); + } + Message::PaneDragged(_) => {} + } + + Command::none() + } + + fn view(&self) -> Element { + let header = row![button("Toggle Theme") + .on_press(Message::ToggleTheme) + .padding(5)] + .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_state.designer_page { + DesignerPage::Designer => { + let content = container("") + .id(iced::widget::container::Id::new("drop_zone")) + .height(Length::Fill) + .width(Length::Fill); + let title = text("Designer").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }); + 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::CodeView => { + let title = row![ + text("Generated Code").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }), + Space::with_width(Length::Fill), + tooltip( + button( + container( + text('\u{0e801}').font(Font::with_name("editor-icons")) + ) + .width(30) + .center_x() + ) + .on_press(Message::CopyCode), + "Copy code to clipboard", + tooltip::Position::Left + ) + ]; + let title_bar = pane_grid::TitleBar::new(title) + .padding(10) + .style(style::title_bar); + pane_grid::Content::new( + text_editor(&self.editor_content) + .highlight::( + highlighter::Settings { + theme: highlighter::Theme::Base16Mocha, + extension: "rs".to_string(), + }, + |highlight, _theme| highlight.to_format(), + ) + .height(Length::Fill) + .padding(20), + ) + .title_bar(title_bar) + .style(if is_focused { + style::pane_focused + } else { + style::pane_active + }) + } + }, + Panes::ElementList => { + let items_list = items_list_view(self.element_list.clone()); + let content = column![items_list] + .align_items(Alignment::Center) + .height(Length::Fill) + .width(Length::Fill); + let title = text("Element List").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }); + 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 + }) + } + } + }) + .width(Length::Fill) + .height(Length::Fill) + .spacing(10) + .on_resize(10, Message::Resized) + .on_click(Message::Clicked) + .on_drag(Message::PaneDragged); + + let content = Column::new() + .push(header) + .push(pane_grid) + .spacing(5) + .align_items(Alignment::Center) + .width(Length::Fill); + + container(content).height(Length::Fill).into() + } +} + +const fn from_grayscale(grayscale: f32) -> Color { + Color { + r: grayscale, + g: grayscale, + b: grayscale, + a: 1.0, + } +} + +// #ffffff +const PANE_ID_COLOR_FOCUSED: Color = from_grayscale(1.0); + +// #e8e8e8 +const PANE_ID_COLOR_UNFOCUSED: Color = from_grayscale(0xE8 as f32 / 255.0); + +fn items_list_view(items: Vec) -> Element<'static, Message> { + let mut column = Column::new() + .spacing(20) + .align_items(Alignment::Center) + .width(Length::Fill); + + for item in items { + let value = item.clone(); + column = column.push( + droppable(text(value.to_string())) + .on_drop(move |point, rect| Message::Drop(value, point, rect)), + ); + } + + container(column).height(250.0).width(300).into() +} + +mod style { + use iced::widget::container; + use iced::{Border, Theme}; + + pub fn title_bar(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); + + container::Appearance { + text_color: Some(palette.background.strong.text), + background: Some(palette.background.strong.color.into()), + ..Default::default() + } + } + + pub fn pane_active(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); + + container::Appearance { + 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) -> container::Appearance { + let palette = theme.extended_palette(); + + container::Appearance { + 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/mod.rs b/iced_builder/src/types/mod.rs new file mode 100644 index 0000000..db06ffa --- /dev/null +++ b/iced_builder/src/types/mod.rs @@ -0,0 +1,54 @@ +pub mod rendered_element; + +use rendered_element::RenderedElement; + +pub struct DesignerState { + pub designer_content: Vec, + pub designer_page: DesignerPage, +} + +#[derive(Debug, Clone, Copy)] +pub enum ElementName { + Text(&'static str), + Button(&'static str), + SVG(&'static str), + Image(&'static str), + Container, + Row, + Column, +} + +impl ElementName { + pub const ALL: [Self; 7] = [ + Self::Text(""), + Self::Button(""), + Self::SVG(""), + Self::Image(""), + Self::Container, + Self::Row, + Self::Column, + ]; +} + +impl std::fmt::Display for ElementName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Text(_) => "Text", + Self::Button(_) => "Button", + Self::SVG(_) => "SVG", + Self::Image(_) => "Image", + Self::Container => "Container", + Self::Row => "Row", + Self::Column => "Column", + } + ) + } +} + +pub enum DesignerPage { + Designer, + CodeView, +} diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs new file mode 100644 index 0000000..f05594d --- /dev/null +++ b/iced_builder/src/types/rendered_element.rs @@ -0,0 +1,47 @@ +use std::collections::HashMap; + +use unique_id::{string::StringGenerator, Generator}; + +use iced::advanced::widget::Id; + +use super::ElementName; + +#[derive(Debug)] +pub struct RenderedElement { + pub id: Id, + pub child_elements: Vec, + pub name: ElementName, + pub props: HashMap<&'static str, &'static str>, +} + +impl RenderedElement { + pub fn new(name: ElementName) -> Self { + let gen = StringGenerator::default(); + Self { + id: Id::new(gen.next_id()), + child_elements: vec![], + name, + props: HashMap::new(), + } + } + + pub fn from_vec(name: ElementName, child_elements: Vec) -> Self { + let gen = StringGenerator::default(); + Self { + id: Id::new(gen.next_id()), + child_elements, + name, + props: HashMap::new(), + } + } + + pub fn push(mut self, element: RenderedElement) -> Self { + self.child_elements.push(element); + self + } + + pub fn set_property(&mut self, prop: &'static str, value: &'static str) { + let prop_ref = self.props.entry(prop).or_insert(value); + *prop_ref = value; + } +} -- cgit v1.2.3 From bc2066220f656765cc820baeb687d8594fbc8826 Mon Sep 17 00:00:00 2001 From: pml68 Date: Mon, 23 Sep 2024 23:57:45 +0200 Subject: feat: make code view scrollable, add autogen comment to code --- iced_builder/src/codegen/mod.rs | 3 ++- iced_builder/src/main.rs | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'iced_builder/src') diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs index 88c0c61..20a2e65 100644 --- a/iced_builder/src/codegen/mod.rs +++ b/iced_builder/src/codegen/mod.rs @@ -92,7 +92,8 @@ impl RenderedElement { let mut app_code = format!("use iced::{{widget::{{{imports}}},Sandbox,Settings,Element}};"); app_code = format!( - r#"{app_code} + r#"// Automatically generated by iced Builder + {app_code} fn main() -> iced::Result {{ App::run(Settings::default()) diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index e966614..2633e6c 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -37,6 +37,7 @@ struct App { enum Message { ToggleTheme, CopyCode, + EditorAction(text_editor::Action), Drop(types::ElementName, iced::Point, iced::Rectangle), HandleZones( types::ElementName, @@ -107,6 +108,11 @@ impl Application for App { match message { Message::ToggleTheme => self.dark_theme = !self.dark_theme, Message::CopyCode => return clipboard::write(self.editor_content.text()), + Message::EditorAction(action) => { + if let text_editor::Action::Scroll { lines: _ } = action { + self.editor_content.perform(action); + } + } Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { self.pane_state.resize(split, ratio); } @@ -190,6 +196,7 @@ impl Application for App { .style(style::title_bar); pane_grid::Content::new( text_editor(&self.editor_content) + .on_action(Message::EditorAction) .highlight::( highlighter::Settings { theme: highlighter::Theme::Base16Mocha, -- cgit v1.2.3 From b7e1527ab07acc45038f71c0a2bbbaf4180e5db3 Mon Sep 17 00:00:00 2001 From: pml68 Date: Sat, 28 Sep 2024 12:17:53 +0200 Subject: feat: add button to switch between designer and code view f --- iced_builder/src/codegen/mod.rs | 4 ++-- iced_builder/src/main.rs | 50 +++++++++++++++++++++++++++++++++-------- iced_builder/src/types/mod.rs | 3 ++- 3 files changed, 45 insertions(+), 12 deletions(-) (limited to 'iced_builder/src') diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs index 20a2e65..923ef99 100644 --- a/iced_builder/src/codegen/mod.rs +++ b/iced_builder/src/codegen/mod.rs @@ -138,7 +138,7 @@ impl RenderedElement { Ok(rustfmt.format_str(app_code)?) } - pub fn test() -> String { + pub fn test() -> RenderedElement { let mut text1 = RenderedElement::new(ElementName::Text("wow")); text1.set_property("height", "120.5"); text1.set_property("width", "230"); @@ -154,6 +154,6 @@ impl RenderedElement { ], )); - element.app_code("new app", None).unwrap() + element } } diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 2633e6c..42ea627 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -37,6 +37,7 @@ struct App { enum Message { ToggleTheme, CopyCode, + SwitchPage(DesignerPage), EditorAction(text_editor::Action), Drop(types::ElementName, iced::Point, iced::Rectangle), HandleZones( @@ -75,11 +76,11 @@ impl Application for App { pane_state: state, focus: None, designer_state: DesignerState { - designer_content: vec![], + designer_content: Some(RenderedElement::test()), designer_page: DesignerPage::Designer, }, element_list: types::ElementName::ALL.to_vec(), - editor_content: text_editor::Content::with_text(&RenderedElement::test()), + editor_content: text_editor::Content::new(), }, Command::none(), ) @@ -89,7 +90,16 @@ impl Application for App { let saved_state = if self.is_saved { "" } else { " *" }; let project_name = match &self.current_project { - Some(n) => format!(" - {n}"), + Some(n) => { + format!( + " - {}", + if n.len() > 60 { + format!("...{}", &n[n.len() - 40..]) + } else { + n.to_owned() + } + ) + } None => "".to_owned(), }; @@ -108,6 +118,7 @@ impl Application for App { match message { Message::ToggleTheme => self.dark_theme = !self.dark_theme, Message::CopyCode => return clipboard::write(self.editor_content.text()), + Message::SwitchPage(page) => self.designer_state.designer_page = page, Message::EditorAction(action) => { if let text_editor::Action::Scroll { lines: _ } = action { self.editor_content.perform(action); @@ -130,6 +141,19 @@ impl Application for App { } Message::HandleZones(name, zones) => { println!("{:?}\n{name}", zones); + println!("{:?}\n{name}\n{:?}", zones, self.title()); + if let Some(el) = &self.designer_state.designer_content { + self.editor_content = text_editor::Content::with_text( + &el.app_code( + match &self.current_project { + Some(title) => &title, + None => "New App", + }, + None, + ) + .unwrap(), + ); + } } Message::PaneDragged(pane_grid::DragEvent::Dropped { pane, target }) => { self.pane_state.drop(pane, target); @@ -154,11 +178,16 @@ impl Application for App { .id(iced::widget::container::Id::new("drop_zone")) .height(Length::Fill) .width(Length::Fill); - let title = text("Designer").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }); + let title = row![ + text("Designer").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }), + Space::with_width(Length::Fill), + button("Switch to Code view") + .on_press(Message::SwitchPage(DesignerPage::CodeView)), + ]; let title_bar = pane_grid::TitleBar::new(title) .padding(10) .style(style::title_bar); @@ -189,7 +218,10 @@ impl Application for App { .on_press(Message::CopyCode), "Copy code to clipboard", tooltip::Position::Left - ) + ), + Space::with_width(20), + button("Switch to Designer view") + .on_press(Message::SwitchPage(DesignerPage::Designer)) ]; let title_bar = pane_grid::TitleBar::new(title) .padding(10) diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs index db06ffa..eaf1686 100644 --- a/iced_builder/src/types/mod.rs +++ b/iced_builder/src/types/mod.rs @@ -3,7 +3,7 @@ pub mod rendered_element; use rendered_element::RenderedElement; pub struct DesignerState { - pub designer_content: Vec, + pub designer_content: Option, pub designer_page: DesignerPage, } @@ -48,6 +48,7 @@ impl std::fmt::Display for ElementName { } } +#[derive(Debug, Clone)] pub enum DesignerPage { Designer, CodeView, -- cgit v1.2.3 From 99e141d9fe9c8f494bb15a9ca31c07d27722f9dd Mon Sep 17 00:00:00 2001 From: pml68 Date: Sat, 28 Sep 2024 23:10:17 +0200 Subject: feat: rework RenderedElement, prepare for JSON project format --- Cargo.lock | 2 + iced_builder/Cargo.toml | 2 + iced_builder/src/codegen/mod.rs | 49 ++++++++++------------- iced_builder/src/main.rs | 10 +++-- iced_builder/src/types/mod.rs | 19 ++++----- iced_builder/src/types/rendered_element.rs | 63 ++++++++++++++++++++++-------- 6 files changed, 88 insertions(+), 57 deletions(-) (limited to 'iced_builder/src') diff --git a/Cargo.lock b/Cargo.lock index d12997d..baeab67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1246,6 +1246,8 @@ dependencies = [ "iced_aw", "iced_drop", "rust-format", + "serde", + "serde_json", "tokio", "unique_id", ] diff --git a/iced_builder/Cargo.toml b/iced_builder/Cargo.toml index febff28..8106b09 100644 --- a/iced_builder/Cargo.toml +++ b/iced_builder/Cargo.toml @@ -12,6 +12,8 @@ keywords = ["gui", "iced"] iced = { version = "0.12.1", features = [ "image","svg","canvas","qr_code","advanced","tokio","highlighter"] } iced_aw = { version = "0.9.3", default-features = false, features = ["menu","color_picker"] } iced_drop = { path = "../iced_drop" } +serde = { version = "1.0.210", features = ["derive"] } +serde_json = "1.0.128" tokio = { version = "1.40.0", features = ["fs"] } rust-format = "0.3.4" unique_id = "0.1.5" diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs index 923ef99..927b6e4 100644 --- a/iced_builder/src/codegen/mod.rs +++ b/iced_builder/src/codegen/mod.rs @@ -1,13 +1,18 @@ use rust_format::{Config, Edition, Formatter, RustFmt}; -use crate::types::{rendered_element::RenderedElement, ElementName}; +use crate::types::{ + rendered_element::{container, row, svg, text, RenderedElement}, + ElementName, +}; impl RenderedElement { fn props_codegen(&self) -> String { let mut props_string = String::new(); for (k, v) in self.props.clone() { - props_string = format!("{props_string}.{k}({v})"); + if let Some(value) = v { + props_string = format!("{props_string}.{k}({value})"); + } } props_string @@ -20,25 +25,18 @@ impl RenderedElement { let mut elements = String::new(); - match self.name { - ElementName::Column | ElementName::Row | ElementName::Container => { - for element in &self.child_elements { - let (c_imports, children) = element.codegen(); - imports = format!("{imports}{c_imports}"); - elements = format!("{elements}{},", children); - } + if let Some(els) = &self.child_elements { + for element in els { + let (c_imports, children) = element.codegen(); + imports = format!("{imports}{c_imports}"); + elements = format!("{elements}{},", children); } - _ => {} } match &self.name { ElementName::Container => { imports = format!("{imports}container,"); - view = if self.child_elements.len() < 2 { - format!("{view}\ncontainer({elements}){props}") - } else { - format!("{view}\ncontainer(){props}") - }; + view = format!("{view}\ncontainer({elements}){props}"); } ElementName::Row => { imports = format!("{imports}row,"); @@ -139,20 +137,13 @@ impl RenderedElement { } pub fn test() -> RenderedElement { - let mut text1 = RenderedElement::new(ElementName::Text("wow")); - text1.set_property("height", "120.5"); - text1.set_property("width", "230"); - - let element = RenderedElement::new(ElementName::Container).push(RenderedElement::from_vec( - ElementName::Row, - vec![ - text1, - RenderedElement::new(ElementName::Text("heh")), - RenderedElement::new(ElementName::SVG( - "/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg", - )), - ], - )); + let text1 = text("wow").option("height", "120.5").option("width", "230"); + + let element = container(row(vec![ + text1, + text("heh"), + svg("/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg"), + ])); element } diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 42ea627..971adce 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -132,7 +132,7 @@ impl Application for App { } Message::Drop(name, point, _) => { return iced_drop::zones_on_point( - move |zones| Message::HandleZones(name, zones), + move |zones| Message::HandleZones(name.clone(), zones), point, None, None, @@ -231,7 +231,11 @@ impl Application for App { .on_action(Message::EditorAction) .highlight::( highlighter::Settings { - theme: highlighter::Theme::Base16Mocha, + theme: if self.dark_theme { + highlighter::Theme::Base16Mocha + } else { + highlighter::Theme::InspiredGitHub + }, extension: "rs".to_string(), }, |highlight, _theme| highlight.to_format(), @@ -314,7 +318,7 @@ fn items_list_view(items: Vec) -> Element<'static, Message> let value = item.clone(); column = column.push( droppable(text(value.to_string())) - .on_drop(move |point, rect| Message::Drop(value, point, rect)), + .on_drop(move |point, rect| Message::Drop(value.clone(), point, rect)), ); } diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs index eaf1686..def04cf 100644 --- a/iced_builder/src/types/mod.rs +++ b/iced_builder/src/types/mod.rs @@ -1,18 +1,19 @@ pub mod rendered_element; use rendered_element::RenderedElement; +use serde::{Deserialize, Serialize}; pub struct DesignerState { pub designer_content: Option, pub designer_page: DesignerPage, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum ElementName { - Text(&'static str), - Button(&'static str), - SVG(&'static str), - Image(&'static str), + Text(String), + Button(String), + SVG(String), + Image(String), Container, Row, Column, @@ -20,10 +21,10 @@ pub enum ElementName { impl ElementName { pub const ALL: [Self; 7] = [ - Self::Text(""), - Self::Button(""), - Self::SVG(""), - Self::Image(""), + Self::Text(String::new()), + Self::Button(String::new()), + Self::SVG(String::new()), + Self::Image(String::new()), Self::Container, Self::Row, Self::Column, diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index f05594d..1938aa0 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -1,47 +1,78 @@ use std::collections::HashMap; +use serde::{Deserialize, Serialize}; use unique_id::{string::StringGenerator, Generator}; -use iced::advanced::widget::Id; - use super::ElementName; -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct RenderedElement { - pub id: Id, - pub child_elements: Vec, + pub id: String, + pub child_elements: Option>, pub name: ElementName, - pub props: HashMap<&'static str, &'static str>, + pub props: HashMap>, } impl RenderedElement { - pub fn new(name: ElementName) -> Self { + fn new(name: ElementName) -> Self { let gen = StringGenerator::default(); Self { - id: Id::new(gen.next_id()), - child_elements: vec![], + id: gen.next_id(), + child_elements: None, name, props: HashMap::new(), } } - pub fn from_vec(name: ElementName, child_elements: Vec) -> Self { + fn from_vec(name: ElementName, child_elements: Vec) -> Self { let gen = StringGenerator::default(); Self { - id: Id::new(gen.next_id()), - child_elements, + id: gen.next_id(), + child_elements: Some(child_elements), name, props: HashMap::new(), } } pub fn push(mut self, element: RenderedElement) -> Self { - self.child_elements.push(element); + if let Some(els) = self.child_elements.as_mut() { + els.push(element); + } else { + self.child_elements = Some(vec![element]); + } self } - pub fn set_property(&mut self, prop: &'static str, value: &'static str) { - let prop_ref = self.props.entry(prop).or_insert(value); - *prop_ref = value; + pub fn option(mut self, prop: &'static str, value: &'static str) -> Self { + let prop_ref = self + .props + .entry(prop.to_owned()) + .or_insert(Some(value.to_owned())); + *prop_ref = Some(value.to_owned()); + self } } + +pub fn text(text: &str) -> RenderedElement { + RenderedElement::new(ElementName::Text(text.to_owned())) +} + +pub fn button(text: &str) -> RenderedElement { + RenderedElement::new(ElementName::Button(text.to_owned())) +} + +pub fn svg(path: &str) -> RenderedElement { + RenderedElement::new(ElementName::SVG(path.to_owned())) +} + +pub fn image(path: &str) -> RenderedElement { + RenderedElement::new(ElementName::Image(path.to_owned())) +} + +pub fn container(content: RenderedElement) -> RenderedElement { + RenderedElement::from_vec(ElementName::Container, vec![content]) +} + +pub fn row(child_elements: Vec) -> RenderedElement { + RenderedElement::from_vec(ElementName::Row, child_elements) +} -- cgit v1.2.3 From f950f6357aea66962ea3a778501430205bf4d6c6 Mon Sep 17 00:00:00 2001 From: pml68 Date: Mon, 30 Sep 2024 01:02:46 +0200 Subject: feat: implement fmt::Display for RenderedElement, work on props --- iced_builder/src/main.rs | 16 ++++--- iced_builder/src/types/mod.rs | 1 + iced_builder/src/types/rendered_element.rs | 68 ++++++++++++++++++++++++++---- 3 files changed, 72 insertions(+), 13 deletions(-) (limited to 'iced_builder/src') diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 971adce..f0f58cc 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -172,12 +172,18 @@ impl Application for App { 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_state.designer_page { + Panes::Designer => match &self.designer_state.designer_page { DesignerPage::Designer => { - let content = container("") - .id(iced::widget::container::Id::new("drop_zone")) - .height(Length::Fill) - .width(Length::Fill); + let content = container(text(format!( + "{:0.4}", + self.designer_state + .designer_content + .clone() + .expect("Designer content hasn't been set yet."), + ))) + .id(iced::widget::container::Id::new("drop_zone")) + .height(Length::Fill) + .width(Length::Fill); let title = row![ text("Designer").style(if is_focused { PANE_ID_COLOR_FOCUSED diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs index def04cf..344f543 100644 --- a/iced_builder/src/types/mod.rs +++ b/iced_builder/src/types/mod.rs @@ -3,6 +3,7 @@ pub mod rendered_element; use rendered_element::RenderedElement; use serde::{Deserialize, Serialize}; +#[derive(Clone)] pub struct DesignerState { pub designer_content: Option, pub designer_page: DesignerPage, diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 1938aa0..2d5c81a 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -5,7 +5,7 @@ use unique_id::{string::StringGenerator, Generator}; use super::ElementName; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct RenderedElement { pub id: String, pub child_elements: Option>, @@ -34,6 +34,13 @@ impl RenderedElement { } } + fn preset_options(mut self, options: Vec<&str>) -> Self { + for opt in options { + self.props.insert(opt.to_owned(), None); + } + self + } + pub fn push(mut self, element: RenderedElement) -> Self { if let Some(els) = self.child_elements.as_mut() { els.push(element); @@ -43,18 +50,63 @@ impl RenderedElement { self } - pub fn option(mut self, prop: &'static str, value: &'static str) -> Self { - let prop_ref = self - .props - .entry(prop.to_owned()) - .or_insert(Some(value.to_owned())); - *prop_ref = Some(value.to_owned()); + pub fn option(mut self, option: &'static str, value: &'static str) -> Self { + self.props + .entry(option.to_owned()) + .and_modify(|opt| *opt = Some(value.to_owned())); self } } +impl std::fmt::Display for RenderedElement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut has_props = false; + f.pad("")?; + f.write_fmt(format_args!("{:?}\n", self.name))?; + f.pad("")?; + f.write_str("Options: (")?; + for (k, v) in &self.props { + if let Some(value) = v { + has_props = true; + f.write_fmt(format_args!( + "\n{:width$.precision$}{}: {}", + "", + k, + value, + width = f.width().unwrap_or(0) + f.precision().unwrap_or(0), + precision = f.precision().unwrap_or(0) + ))?; + } + } + if has_props { + f.write_str("\n")?; + f.pad("")?; + } + f.write_str(")")?; + if let Some(els) = &self.child_elements { + f.write_str(" {\n")?; + for el in els { + f.write_fmt(format_args!( + "\n{:width$.precision$}\n", + el, + width = f.width().unwrap_or(0) + f.precision().unwrap_or(0), + precision = f.precision().unwrap_or(0) + ))?; + } + f.pad("")?; + f.write_str("}")?; + } + Ok(()) + } +} + pub fn text(text: &str) -> RenderedElement { - RenderedElement::new(ElementName::Text(text.to_owned())) + RenderedElement::new(ElementName::Text(text.to_owned())).preset_options(vec![ + "size", + "line_height", + "width", + "height", + ]) } pub fn button(text: &str) -> RenderedElement { -- cgit v1.2.3 From 510d68b92972b99868e187dd5340f04780b4c354 Mon Sep 17 00:00:00 2001 From: pml68 Date: Fri, 4 Oct 2024 00:44:02 +0200 Subject: feat: update to iced 0.13.1, basic project state file, prepare for drag&drop --- .gitignore | 1 + Cargo.lock | 1564 ++++++++++++++++++++-------- iced_builder/Cargo.toml | 5 +- iced_builder/src/codegen/mod.rs | 122 ++- iced_builder/src/lib.rs | 85 ++ iced_builder/src/main.rs | 292 +++--- iced_builder/src/types/mod.rs | 10 +- iced_builder/src/types/project/mod.rs | 59 ++ iced_builder/src/types/rendered_element.rs | 124 ++- iced_drop/Cargo.toml | 2 +- iced_drop/LICENSE | 21 + iced_drop/README.md | 2 + iced_drop/src/lib.rs | 24 +- iced_drop/src/widget/droppable.rs | 8 +- iced_drop/src/widget/operation/drop.rs | 5 +- 15 files changed, 1652 insertions(+), 672 deletions(-) create mode 100644 iced_builder/src/types/project/mod.rs create mode 100644 iced_drop/LICENSE (limited to 'iced_builder/src') diff --git a/.gitignore b/.gitignore index f4ceea7..9545d1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ **/target/ +**/*.json diff --git a/Cargo.lock b/Cargo.lock index baeab67..a341d35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.11" @@ -60,9 +71,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-activity" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", "bitflags 2.6.0", @@ -74,7 +85,7 @@ dependencies = [ "log", "ndk", "ndk-context", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", "thiserror", ] @@ -130,6 +141,182 @@ dependencies = [ "libloading 0.7.4", ] +[[package]] +name = "ashpd" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe7e0dd0ac5a401dc116ed9f9119cf9decc625600474cb41f0fc0a0050abc9a" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand", + "raw-window-handle", + "serde", + "serde_repr", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -138,9 +325,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" @@ -163,12 +350,6 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -234,31 +415,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] -name = "block-sys" -version = "0.2.1" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "objc-sys", + "generic-array", ] [[package]] name = "block2" -version = "0.3.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "block-sys", - "objc2 0.4.1", + "objc2", ] [[package]] -name = "block2" -version = "0.5.1" +name = "blocking" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "objc2 0.5.2", + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", ] [[package]] @@ -290,7 +474,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -305,20 +489,6 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" -[[package]] -name = "calloop" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" -dependencies = [ - "bitflags 2.6.0", - "log", - "polling", - "rustix", - "slab", - "thiserror", -] - [[package]] name = "calloop" version = "0.13.0" @@ -333,25 +503,13 @@ dependencies = [ "thiserror", ] -[[package]] -name = "calloop-wayland-source" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" -dependencies = [ - "calloop 0.12.4", - "rustix", - "wayland-backend", - "wayland-client", -] - [[package]] name = "calloop-wayland-source" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "calloop 0.13.0", + "calloop", "rustix", "wayland-backend", "wayland-client", @@ -359,9 +517,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.21" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" dependencies = [ "jobserver", "libc", @@ -407,7 +565,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f" dependencies = [ - "objc2 0.5.2", + "objc2", "objc2-app-kit", "objc2-foundation", ] @@ -573,25 +731,36 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.10.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75acbfb314aeb4f5210d379af45ed1ec2c98c7f1790bf57b8a4c562ac0c51b71" +checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2" dependencies = [ - "fontdb", - "libm", + "bitflags 2.6.0", + "fontdb 0.16.2", "log", "rangemap", - "rustc-hash", - "rustybuzz 0.11.0", + "rayon", + "rustc-hash 1.1.0", + "rustybuzz", "self_cell", "swash", "sys-locale", + "ttf-parser 0.21.1", "unicode-bidi", "unicode-linebreak", "unicode-script", "unicode-segmentation", ] +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -632,6 +801,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "ctor-lite" version = "0.1.0" @@ -655,12 +834,34 @@ dependencies = [ "winapi", ] +[[package]] +name = "dark-light" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a76fa97167fa740dcdbfe18e8895601e1bc36525f09b044e00916e717c03a3c" +dependencies = [ + "dconf_rs", + "detect-desktop-environment", + "dirs", + "objc", + "rust-ini", + "web-sys", + "winreg", + "zbus", +] + [[package]] name = "data-url" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" +[[package]] +name = "dconf_rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7046468a81e6a002061c01e6a7c83139daf91b11c30e66795b13217c2d885c8b" + [[package]] name = "deranged" version = "0.3.11" @@ -670,6 +871,42 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "detect-desktop-environment" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d8ad60dd5b13a4ee6bd8fa2d5d88965c597c67bce32b5fc49c94f55cb50810" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -685,12 +922,24 @@ dependencies = [ "libloading 0.8.5", ] +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + [[package]] name = "downcast-rs" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" + [[package]] name = "drm" version = "0.12.0" @@ -736,6 +985,33 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -777,6 +1053,27 @@ dependencies = [ "num-traits", ] +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "exr" version = "1.72.0" @@ -807,18 +1104,18 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fdeflate" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab" dependencies = [ "simd-adler32", ] [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -866,21 +1163,35 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7" dependencies = [ - "roxmltree 0.20.0", + "roxmltree", ] [[package]] name = "fontdb" -version = "0.15.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020e203f177c0fb250fb19455a252e838d2bbbce1f80f25ecc42402aafa8cd38" +checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" dependencies = [ "fontconfig-parser", "log", - "memmap2 0.8.0", + "memmap2", "slotmap", "tinyvec", - "ttf-parser 0.19.2", + "ttf-parser 0.20.0", +] + +[[package]] +name = "fontdb" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser 0.21.1", ] [[package]] @@ -901,7 +1212,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -910,6 +1221,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.30" @@ -959,6 +1279,19 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.30" @@ -967,7 +1300,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1000,6 +1333,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "gethostname" version = "0.4.3" @@ -1021,16 +1364,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "gif" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gif" version = "0.13.1" @@ -1085,18 +1418,6 @@ dependencies = [ "gl_generator", ] -[[package]] -name = "glyphon" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a62d0338e4056db6a73221c2fb2e30619452f6ea9651bac4110f51b0f7a7581" -dependencies = [ - "cosmic-text", - "etagere", - "lru", - "wgpu", -] - [[package]] name = "gpu-alloc" version = "0.6.0" @@ -1137,7 +1458,7 @@ checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ "bitflags 2.6.0", "gpu-descriptor-types", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -1169,16 +1490,31 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash", + "ahash 0.8.11", "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hassle-rs" version = "0.11.0" @@ -1206,6 +1542,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hexf-parse" version = "0.2.1" @@ -1214,9 +1556,9 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "iced" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d4eb0fbbefb8c428b70680e77ed9013887b17c1d6be366b40f264f956d1a096" +checksum = "88acfabc84ec077eaf9ede3457ffa3a104626d79022a9bf7f296093b1d60c73f" dependencies = [ "iced_core", "iced_futures", @@ -1230,12 +1572,13 @@ dependencies = [ [[package]] name = "iced_aw" -version = "0.9.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e68c330918a95bd73176206d65b84efe9aee6581da0e6dea0390cd146d7214c" +checksum = "42e05df3019f20c6decea93d035b32a2afc7b329d89cc5a68cca097d0e0a1889" dependencies = [ "cfg-if", "iced", + "iced_fonts", ] [[package]] @@ -1245,6 +1588,7 @@ dependencies = [ "iced", "iced_aw", "iced_drop", + "rfd", "rust-format", "serde", "serde_json", @@ -1254,20 +1598,22 @@ dependencies = [ [[package]] name = "iced_core" -version = "0.12.3" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d7e6bbd197f311ed3d8b71651876b0ce01318fde52cda862a9a7a4373c9b930" +checksum = "0013a238275494641bf8f1732a23a808196540dc67b22ff97099c044ae4c8a1c" dependencies = [ "bitflags 2.6.0", + "bytes", + "dark-light", "glam", "log", "num-traits", + "once_cell", "palette", - "raw-window-handle", + "rustc-hash 2.0.0", "smol_str", "thiserror", "web-time", - "xxhash-rust", ] [[package]] @@ -1277,25 +1623,48 @@ dependencies = [ "iced", ] +[[package]] +name = "iced_fonts" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7deb0800a850ee25c8a42559f72c0f249e577feb3aad37b9b65dc1e517e52a" +dependencies = [ + "iced_core", +] + [[package]] name = "iced_futures" -version = "0.12.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370bad88fb3832cbeeb3fa6c486b4701fb7e8da32a753b3101d4ce81fc1d9497" +checksum = "0c04a6745ba2e80f32cf01e034fd00d853aa4f4cd8b91888099cb7aaee0d5d7c" dependencies = [ "futures", "iced_core", "log", + "rustc-hash 2.0.0", "tokio", "wasm-bindgen-futures", "wasm-timer", ] +[[package]] +name = "iced_glyphon" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c3bb56f1820ca252bc1d0994ece33d233a55657c0c263ea7cb16895adbde82" +dependencies = [ + "cosmic-text", + "etagere", + "lru", + "rustc-hash 2.0.0", + "wgpu", +] + [[package]] name = "iced_graphics" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a044c193ef0840eacabfa05424717331d1fc5b3ecb9a89316200c75da2ba9a4" +checksum = "ba25a18cfa6d5cc160aca7e1b34f73ccdff21680fa8702168c09739767b6c66f" dependencies = [ "bitflags 2.6.0", "bytemuck", @@ -1309,17 +1678,16 @@ dependencies = [ "lyon_path", "once_cell", "raw-window-handle", - "rustc-hash", + "rustc-hash 2.0.0", "thiserror", "unicode-segmentation", - "xxhash-rust", ] [[package]] name = "iced_highlighter" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d27d1e5f526255ad4902907509f9ff9e98a40653dd31df36aba38d7c3905ad" +checksum = "bad88b25a1328cd4bb0b72d8e20f8207c0433649dc788f67e911423b9406f45c" dependencies = [ "iced_core", "once_cell", @@ -1328,9 +1696,9 @@ dependencies = [ [[package]] name = "iced_renderer" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c281e03001d566058f53dec9325bbe61c62da715341206d2627f57a3ecc7f69" +checksum = "73558208059f9e622df2bf434e044ee2f838ce75201a023cf0ca3e1244f46c2a" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -1341,32 +1709,22 @@ dependencies = [ [[package]] name = "iced_runtime" -version = "0.12.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a79f852c01cc6d61663c94379cb3974ac3ad315a28c504e847d573e094f46822" +checksum = "348b5b2c61c934d88ca3b0ed1ed913291e923d086a66fa288ce9669da9ef62b5" dependencies = [ + "bytes", "iced_core", "iced_futures", "raw-window-handle", "thiserror", ] -[[package]] -name = "iced_style" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea42a740915d2a5a9ff9c3aa0bca28b16e9fb660bc8f675eed71d186cadb579" -dependencies = [ - "iced_core", - "once_cell", - "palette", -] - [[package]] name = "iced_tiny_skia" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2228781f4d381a1cbbd7905a9f077351aa8d37269094021d5d9e779f130aff" +checksum = "c625d368284fcc43b0b36b176f76eff1abebe7959dd58bd8ce6897d641962a50" dependencies = [ "bytemuck", "cosmic-text", @@ -1374,59 +1732,64 @@ dependencies = [ "kurbo 0.10.4", "log", "resvg", - "rustc-hash", + "rustc-hash 2.0.0", "softbuffer", "tiny-skia", - "xxhash-rust", ] [[package]] name = "iced_wgpu" -version = "0.12.1" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c243b6700452886aac1ee1987e84d9fb43b56b53fea9a1eb67713fd0fde244" +checksum = "15708887133671d2bcc6c1d01d1f176f43a64d6cdc3b2bf893396c3ee498295f" dependencies = [ "bitflags 2.6.0", "bytemuck", "futures", "glam", - "glyphon", "guillotiere", + "iced_glyphon", "iced_graphics", "log", "lyon", "once_cell", "resvg", + "rustc-hash 2.0.0", + "thiserror", "wgpu", ] [[package]] name = "iced_widget" -version = "0.12.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e01b2212adecf1cb80e2267f302c0e0c263e55f97812056949199ccf9f0b908" +checksum = "81429e1b950b0e4bca65be4c4278fea6678ea782030a411778f26fa9f8983e1d" dependencies = [ + "iced_highlighter", "iced_renderer", "iced_runtime", - "iced_style", "num-traits", + "once_cell", "qrcode", + "rustc-hash 2.0.0", "thiserror", "unicode-segmentation", ] [[package]] name = "iced_winit" -version = "0.12.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63f66831d0e399b93f631739121a6171780d344b275d56808b9504d8ca75c7d2" +checksum = "f44cd4e1c594b6334f409282937bf972ba14d31fedf03c23aa595d982a2fda28" dependencies = [ + "iced_futures", "iced_graphics", "iced_runtime", - "iced_style", "log", + "rustc-hash 2.0.0", "thiserror", "tracing", + "wasm-bindgen-futures", "web-sys", "winapi", "window_clipboard", @@ -1434,14 +1797,13 @@ dependencies = [ ] [[package]] -name = "icrate" -version = "0.0.4" +name = "idna" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "block2 0.3.0", - "dispatch", - "objc2 0.4.1", + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -1454,7 +1816,7 @@ dependencies = [ "byteorder", "color_quant", "exr", - "gif 0.13.1", + "gif", "jpeg-decoder", "num-traits", "png", @@ -1470,12 +1832,12 @@ checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] [[package]] @@ -1570,18 +1932,19 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kurbo" -version = "0.9.5" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" +checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440" dependencies = [ "arrayvec", + "smallvec", ] [[package]] name = "kurbo" -version = "0.10.4" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440" +checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f" dependencies = [ "arrayvec", "smallvec", @@ -1601,9 +1964,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libloading" @@ -1642,6 +2005,16 @@ dependencies = [ "redox_syscall 0.4.1", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1681,9 +2054,6 @@ name = "lru" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" -dependencies = [ - "hashbrown", -] [[package]] name = "lyon" @@ -1754,20 +2124,20 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" -version = "0.8.0" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] [[package]] -name = "memmap2" -version = "0.9.5" +name = "memoffset" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ - "libc", + "autocfg", ] [[package]] @@ -1792,7 +2162,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", - "simd-adler32", ] [[package]] @@ -1802,6 +2171,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1823,7 +2193,7 @@ dependencies = [ "indexmap", "log", "num-traits", - "rustc-hash", + "rustc-hash 1.1.0", "spirv", "termcolor", "thiserror", @@ -1832,14 +2202,14 @@ dependencies = [ [[package]] name = "ndk" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ "bitflags 2.6.0", "jni-sys", "log", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", "raw-window-handle", "thiserror", @@ -1860,6 +2230,28 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases 0.2.1", + "libc", + "memoffset", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1904,7 +2296,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1923,16 +2315,6 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" -[[package]] -name = "objc2" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" -dependencies = [ - "objc-sys", - "objc2-encode 3.0.0", -] - [[package]] name = "objc2" version = "0.5.2" @@ -1940,7 +2322,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ "objc-sys", - "objc2-encode 4.0.3", + "objc2-encode", ] [[package]] @@ -1950,15 +2332,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", + "block2", "libc", - "objc2 0.5.2", + "objc2", "objc2-core-data", "objc2-core-image", "objc2-foundation", "objc2-quartz-core", ] +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + [[package]] name = "objc2-core-data" version = "0.2.2" @@ -1966,8 +2372,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", ] @@ -1977,17 +2383,23 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", "objc2-metal", ] [[package]] -name = "objc2-encode" -version = "3.0.0" +name = "objc2-core-location" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] [[package]] name = "objc2-encode" @@ -2002,9 +2414,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", + "block2", + "dispatch", "libc", - "objc2 0.5.2", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", ] [[package]] @@ -2014,8 +2439,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", ] @@ -2026,12 +2451,67 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", "objc2-metal", ] +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -2052,9 +2532,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "onig" @@ -2084,7 +2567,27 @@ version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" dependencies = [ - "libredox", + "libredox 0.0.2", +] + +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list", + "hashbrown 0.12.3", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", ] [[package]] @@ -2117,9 +2620,15 @@ dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.11.2" @@ -2163,7 +2672,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.4", + "redox_syscall 0.5.7", "smallvec", "windows-targets 0.52.6", ] @@ -2210,7 +2719,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2219,7 +2728,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] @@ -2228,6 +2737,26 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -2240,11 +2769,22 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "plist" @@ -2261,15 +2801,15 @@ dependencies = [ [[package]] name = "png" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" dependencies = [ "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.4", + "miniz_oxide 0.8.0", ] [[package]] @@ -2287,12 +2827,33 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "presser" version = "0.3.1" @@ -2371,6 +2932,18 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", "rand_core", ] @@ -2379,6 +2952,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] [[package]] name = "range-alloc" @@ -2418,12 +2994,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "rctree" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" - [[package]] name = "read-fonts" version = "0.20.0" @@ -2445,36 +3015,38 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] -name = "redox_syscall" -version = "0.5.4" +name = "redox_users" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "bitflags 2.6.0", + "getrandom", + "libredox 0.1.3", + "thiserror", ] [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "renderdoc-sys" @@ -2484,21 +3056,42 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "resvg" -version = "0.36.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7980f653f9a7db31acff916a262c3b78c562919263edea29bf41a056e20497" +checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051" dependencies = [ - "gif 0.12.0", + "gif", "jpeg-decoder", "log", "pico-args", - "png", "rgb", "svgtypes", "tiny-skia", "usvg", ] +[[package]] +name = "rfd" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af382a047821a08aa6bfc09ab0d80ff48d45d8726f7cd8e44891f7cb4a4278e" +dependencies = [ + "ashpd", + "block2", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + [[package]] name = "rgb" version = "0.8.50" @@ -2508,15 +3101,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "roxmltree" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302" -dependencies = [ - "xmlparser", -] - [[package]] name = "roxmltree" version = "0.20.0" @@ -2529,6 +3113,16 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60e7c00b6c3bf5e38a880eec01d7e829d12ca682079f8238a464def3c4b31627" +[[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2541,6 +3135,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustix" version = "0.38.37" @@ -2556,31 +3156,15 @@ dependencies = [ [[package]] name = "rustybuzz" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71cd15fef9112a1f94ac64b58d1e4628192631ad6af4dc69997f995459c874e7" -dependencies = [ - "bitflags 1.3.2", - "bytemuck", - "smallvec", - "ttf-parser 0.19.2", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - -[[package]] -name = "rustybuzz" -version = "0.11.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee8fe2a8461a0854a37101fe7a1b13998d0cfa987e43248e81d2a5f4570f6fa" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "bytemuck", "libm", "smallvec", - "ttf-parser 0.20.0", + "ttf-parser 0.21.1", "unicode-bidi-mirroring", "unicode-ccc", "unicode-properties", @@ -2616,14 +3200,14 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sctk-adwaita" -version = "0.8.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" dependencies = [ "ab_glyph", "log", - "memmap2 0.9.5", - "smithay-client-toolkit 0.18.1", + "memmap2", + "smithay-client-toolkit", "tiny-skia", ] @@ -2650,7 +3234,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2665,12 +3249,43 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -2692,6 +3307,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "skrifa" version = "0.20.0" @@ -2726,31 +3347,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "smithay-client-toolkit" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" -dependencies = [ - "bitflags 2.6.0", - "calloop 0.12.4", - "calloop-wayland-source 0.2.0", - "cursor-icon", - "libc", - "log", - "memmap2 0.9.5", - "rustix", - "thiserror", - "wayland-backend", - "wayland-client", - "wayland-csd-frame", - "wayland-cursor", - "wayland-protocols 0.31.2", - "wayland-protocols-wlr 0.2.0", - "wayland-scanner", - "xkeysym", -] - [[package]] name = "smithay-client-toolkit" version = "0.19.2" @@ -2758,20 +3354,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ "bitflags 2.6.0", - "calloop 0.13.0", - "calloop-wayland-source 0.3.0", + "calloop", + "calloop-wayland-source", "cursor-icon", "libc", "log", - "memmap2 0.9.5", + "memmap2", "rustix", "thiserror", "wayland-backend", "wayland-client", "wayland-csd-frame", "wayland-cursor", - "wayland-protocols 0.32.4", - "wayland-protocols-wlr 0.3.4", + "wayland-protocols", + "wayland-protocols-wlr", "wayland-scanner", "xkeysym", ] @@ -2783,7 +3379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" dependencies = [ "libc", - "smithay-client-toolkit 0.19.2", + "smithay-client-toolkit", "wayland-backend", ] @@ -2811,12 +3407,12 @@ dependencies = [ "foreign-types", "js-sys", "log", - "memmap2 0.9.5", - "objc2 0.5.2", + "memmap2", + "objc2", "objc2-foundation", "objc2-quartz-core", "raw-window-handle", - "redox_syscall 0.5.4", + "redox_syscall 0.5.7", "rustix", "tiny-xlib", "wasm-bindgen", @@ -2869,12 +3465,12 @@ checksum = "20e16a0f46cf5fd675563ef54f26e83e20f2366bcf027bcb3cc3ed2b98aaf2ca" [[package]] name = "svgtypes" -version = "0.12.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71499ff2d42f59d26edb21369a308ede691421f79ebc0f001e2b1fd3a7c9e52" +checksum = "794de53cc48eaabeed0ab6a3404a65f40b3e38c067e4435883a65d2aa4ca000e" dependencies = [ - "kurbo 0.9.5", - "siphasher", + "kurbo 0.11.1", + "siphasher 1.0.1", ] [[package]] @@ -2901,9 +3497,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -2941,6 +3537,19 @@ dependencies = [ "libc", ] +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -2967,7 +3576,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3084,9 +3693,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", @@ -3112,7 +3721,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3126,15 +3735,15 @@ dependencies = [ [[package]] name = "ttf-parser" -version = "0.19.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49d64318d8311fc2668e48b63969f4343e0a85c4a109aa8460d6672e364b8bd1" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" [[package]] name = "ttf-parser" -version = "0.20.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" [[package]] name = "ttf-parser" @@ -3142,23 +3751,40 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-bidi-mirroring" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" [[package]] name = "unicode-ccc" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" [[package]] name = "unicode-ident" @@ -3172,11 +3798,20 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-properties" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unicode-script" @@ -3220,64 +3855,48 @@ dependencies = [ ] [[package]] -name = "usvg" -version = "0.36.0" +name = "url" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51daa774fe9ee5efcf7b4fec13019b8119cda764d9a8b5b06df02bb1445c656" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ - "base64 0.21.7", - "log", - "pico-args", - "usvg-parser", - "usvg-text-layout", - "usvg-tree", - "xmlwriter", + "form_urlencoded", + "idna", + "percent-encoding", + "serde", ] [[package]] -name = "usvg-parser" -version = "0.36.0" +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" -checksum = "45c88a5ffaa338f0e978ecf3d4e00d8f9f493e29bed0752e1a808a1db16afc40" +checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" dependencies = [ + "base64 0.22.1", "data-url", "flate2", + "fontdb 0.18.0", "imagesize", - "kurbo 0.9.5", + "kurbo 0.11.1", "log", - "roxmltree 0.18.1", + "pico-args", + "roxmltree", + "rustybuzz", "simplecss", - "siphasher", + "siphasher 1.0.1", + "strict-num", "svgtypes", - "usvg-tree", -] - -[[package]] -name = "usvg-text-layout" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2374378cb7a3fb8f33894e0fdb8625e1bbc4f25312db8d91f862130b541593" -dependencies = [ - "fontdb", - "kurbo 0.9.5", - "log", - "rustybuzz 0.10.0", + "tiny-skia-path", "unicode-bidi", "unicode-script", "unicode-vo", - "usvg-tree", -] - -[[package]] -name = "usvg-tree" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cacb0c5edeaf3e80e5afcf5b0d4004cc1d36318befc9a7c6606507e5d0f4062" -dependencies = [ - "rctree", - "strict-num", - "svgtypes", - "tiny-skia-path", + "xmlwriter", ] [[package]] @@ -3333,7 +3952,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -3367,7 +3986,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3441,18 +4060,6 @@ dependencies = [ "xcursor", ] -[[package]] -name = "wayland-protocols" -version = "0.31.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" -dependencies = [ - "bitflags 2.6.0", - "wayland-backend", - "wayland-client", - "wayland-scanner", -] - [[package]] name = "wayland-protocols" version = "0.32.4" @@ -3467,27 +4074,14 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" -dependencies = [ - "bitflags 2.6.0", - "wayland-backend", - "wayland-client", - "wayland-protocols 0.31.2", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols-wlr" -version = "0.2.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +checksum = "8a0a41a6875e585172495f7a96dfa42ca7e0213868f4f15c313f7c33221a7eff" dependencies = [ "bitflags 2.6.0", "wayland-backend", "wayland-client", - "wayland-protocols 0.31.2", + "wayland-protocols", "wayland-scanner", ] @@ -3500,7 +4094,7 @@ dependencies = [ "bitflags 2.6.0", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.4", + "wayland-protocols", "wayland-scanner", ] @@ -3529,9 +4123,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -3539,9 +4133,9 @@ dependencies = [ [[package]] name = "web-time" -version = "0.2.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -3596,7 +4190,7 @@ dependencies = [ "parking_lot 0.12.3", "profiling", "raw-window-handle", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", "thiserror", "web-sys", @@ -3632,7 +4226,7 @@ dependencies = [ "log", "metal", "naga", - "ndk-sys", + "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", "parking_lot 0.12.3", @@ -3640,7 +4234,7 @@ dependencies = [ "range-alloc", "raw-window-handle", "renderdoc-sys", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", "thiserror", "wasm-bindgen", @@ -3946,47 +4540,51 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.29.15" +version = "0.30.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" +checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67" dependencies = [ - "ahash", + "ahash 0.8.11", "android-activity", "atomic-waker", "bitflags 2.6.0", + "block2", "bytemuck", - "calloop 0.12.4", - "cfg_aliases 0.1.1", + "calloop", + "cfg_aliases 0.2.1", + "concurrent-queue", "core-foundation 0.9.4", "core-graphics 0.23.2", "cursor-icon", - "icrate", + "dpi", "js-sys", "libc", - "log", - "memmap2 0.9.5", + "memmap2", "ndk", - "ndk-sys", - "objc2 0.4.1", - "once_cell", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", "orbclient", "percent-encoding", + "pin-project", "raw-window-handle", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "rustix", "sctk-adwaita", - "smithay-client-toolkit 0.18.1", + "smithay-client-toolkit", "smol_str", + "tracing", "unicode-segmentation", "wasm-bindgen", "wasm-bindgen-futures", "wayland-backend", "wayland-client", - "wayland-protocols 0.31.2", + "wayland-protocols", "wayland-protocols-plasma", "web-sys", "web-time", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "x11-dl", "x11rb", "xkbcommon-dl", @@ -3994,13 +4592,22 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -4039,6 +4646,16 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "xkbcommon-dl" version = "0.4.2" @@ -4064,24 +4681,12 @@ version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" -[[package]] -name = "xmlparser" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" - [[package]] name = "xmlwriter" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" -[[package]] -name = "xxhash-rust" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" - [[package]] name = "yaml-rust" version = "0.4.5" @@ -4097,6 +4702,68 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.79", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zeno" version = "0.2.3" @@ -4109,6 +4776,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] @@ -4120,7 +4788,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4131,3 +4799,41 @@ checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" dependencies = [ "simd-adler32", ] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.79", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] diff --git a/iced_builder/Cargo.toml b/iced_builder/Cargo.toml index 8106b09..a7b1e7b 100644 --- a/iced_builder/Cargo.toml +++ b/iced_builder/Cargo.toml @@ -9,12 +9,13 @@ license = "GPL-3.0-or-later" keywords = ["gui", "iced"] [dependencies] -iced = { version = "0.12.1", features = [ "image","svg","canvas","qr_code","advanced","tokio","highlighter"] } -iced_aw = { version = "0.9.3", default-features = false, features = ["menu","color_picker"] } +iced = { version = "0.13.1", features = [ "image","svg","canvas","qr_code","advanced","tokio","highlighter"] } +iced_aw = { version = "0.11.0", default-features = false, features = ["menu","color_picker"] } iced_drop = { path = "../iced_drop" } serde = { version = "1.0.210", features = ["derive"] } serde_json = "1.0.128" tokio = { version = "1.40.0", features = ["fs"] } +rfd = "0.15.0" rust-format = "0.3.4" unique_id = "0.1.5" diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs index 927b6e4..38e8ec3 100644 --- a/iced_builder/src/codegen/mod.rs +++ b/iced_builder/src/codegen/mod.rs @@ -1,8 +1,12 @@ use rust_format::{Config, Edition, Formatter, RustFmt}; -use crate::types::{ - rendered_element::{container, row, svg, text, RenderedElement}, - ElementName, +use crate::{ + types::{ + project::Project, + rendered_element::{container, row, svg, text, RenderedElement}, + ElementName, + }, + Error, }; impl RenderedElement { @@ -10,8 +14,8 @@ impl RenderedElement { let mut props_string = String::new(); for (k, v) in self.props.clone() { - if let Some(value) = v { - props_string = format!("{props_string}.{k}({value})"); + if let Some(v) = v { + props_string = format!("{props_string}.{k}({v})"); } } @@ -81,70 +85,62 @@ impl RenderedElement { (imports, view) } - pub fn app_code( - &self, - title: &str, - theme: Option, - ) -> Result> { - let (imports, view) = self.codegen(); - let mut app_code = format!("use iced::{{widget::{{{imports}}},Sandbox,Settings,Element}};"); - - app_code = format!( - r#"// Automatically generated by iced Builder - {app_code} - - fn main() -> iced::Result {{ - App::run(Settings::default()) - }} - - struct App; - - impl Sandbox for App {{ - type Message = (); - - fn new() -> Self {{ - Self {{}} - }} - - fn title(&self) -> String {{ - "{title}".into() - }} - - fn theme(&self) -> iced::Theme {{ - iced::Theme::{} - }} - - fn update(&mut self, message: Self::Message) {{ - - }} - - fn view(&self) -> Element {{ - {view}.into() - }} - }}"#, - if let Some(c) = theme { - c.to_string().replace(' ', "") - } else { - "default()".to_owned() - } - ); - let config = Config::new_str() - .edition(Edition::Rust2021) - .option("trailing_comma", "Never") - .option("imports_granularity", "Crate"); - let rustfmt = RustFmt::from_config(config); - Ok(rustfmt.format_str(app_code)?) - } - pub fn test() -> RenderedElement { - let text1 = text("wow").option("height", "120.5").option("width", "230"); + let mut text1 = text("wow"); + text1.option("height", "120.5"); + text1.option("width", "230"); - let element = container(row(vec![ + 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 Project { + pub fn app_code(self) -> Result { + match &self.content { + Some(el) => { + let (imports, view) = el.codegen(); + let mut app_code = format!("use iced::{{widget::{{{imports}}},Element}};"); + + app_code = format!( + r#"// Automatically generated by iced Builder + {app_code} + + fn main() -> iced::Result {{ + iced::run("{}", State::update, State::view) + }} + + #[derive(Default)] + struct State; + + #[derive(Debug, Clone)] + enum Message {{}} + + impl State {{ + fn update(&mut self, _message: Message) {{}} + + fn view(&self) -> Element {{ + {view}.into() + }} + }}"#, + match &self.title { + Some(t) => t, + None => "New app", + } + ); + let config = Config::new_str() + .edition(Edition::Rust2021) + .option("trailing_comma", "Never") + .option("imports_granularity", "Crate"); + let rustfmt = RustFmt::from_config(config); + Ok(rustfmt.format_str(app_code)?) + } + None => Err("No element tree present".into()), + } + } +} diff --git a/iced_builder/src/lib.rs b/iced_builder/src/lib.rs index e69de29..971e0e3 100644 --- a/iced_builder/src/lib.rs +++ b/iced_builder/src/lib.rs @@ -0,0 +1,85 @@ +pub mod codegen; +pub mod types; + +use std::path::PathBuf; + +use iced::widget::{pane_grid, text_editor}; +use types::{project::Project, rendered_element::RenderedElement, DesignerPage}; + +#[derive(Debug, Clone)] +pub enum Error { + IOError(std::io::ErrorKind), + SerdeError(String), + FormatError(String), + DialogClosed, + 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::DialogClosed => { + write!( + f, + "The file dialog has been closed without selecting a valid option." + ) + } + } + } +} + +impl From for Error { + fn from(value: std::io::Error) -> Self { + Self::IOError(value.kind()) + } +} + +impl From for Error { + fn from(value: serde_json::Error) -> Self { + Self::SerdeError(value.to_string()) + } +} + +impl From for Error { + fn from(value: rust_format::Error) -> Self { + Self::FormatError(value.to_string()) + } +} + +impl From<&'static str> for Error { + fn from(value: &'static str) -> Self { + Self::String(value.to_owned()) + } +} + +#[derive(Debug, Clone)] +pub enum Message { + ToggleTheme, + CopyCode, + SwitchPage(DesignerPage), + EditorAction(text_editor::Action), + DropNewElement(types::ElementName, iced::Point, iced::Rectangle), + HandleNew( + types::ElementName, + Vec<(iced::advanced::widget::Id, iced::Rectangle)>, + ), + MoveElement(RenderedElement, iced::Point, iced::Rectangle), + HandleMove( + RenderedElement, + Vec<(iced::advanced::widget::Id, iced::Rectangle)>, + ), + Resized(pane_grid::ResizeEvent), + Clicked(pane_grid::Pane), + PaneDragged(pane_grid::DragEvent), + NewFile, + OpenFile, + FileOpened(Result<(PathBuf, Project), Error>), + SaveFile, + FileSaved(Result), +} diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index f0f58cc..aeb5ea6 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -1,67 +1,50 @@ -mod codegen; -mod types; +use std::path::PathBuf; use iced::{ - clipboard, executor, - highlighter::{self, Highlighter}, - theme, + clipboard, highlighter, keyboard, widget::{ button, column, container, pane_grid::{self, Pane, PaneGrid}, row, text, text_editor, tooltip, Column, Space, }, - Alignment, Application, Color, Command, Element, Font, Length, Settings, + Alignment, Element, Font, Length, Settings, Task, Theme, }; +use iced_builder::types::{project::Project, DesignerPage, ElementName}; +use iced_builder::Message; use iced_drop::droppable; -use types::{rendered_element::RenderedElement, DesignerPage, DesignerState}; fn main() -> iced::Result { - App::run(Settings { - fonts: vec![include_bytes!("../fonts/icons.ttf").as_slice().into()], - ..Settings::default() - }) + 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) + .subscription(App::subscription) + .run_with(App::new) } struct App { - is_saved: bool, - current_project: Option, + is_dirty: bool, + is_loading: bool, + project_path: Option, + project: Project, dark_theme: bool, pane_state: pane_grid::State, focus: Option, - designer_state: DesignerState, - element_list: Vec, + designer_page: DesignerPage, + element_list: Vec, editor_content: text_editor::Content, } -#[derive(Debug, Clone)] -enum Message { - ToggleTheme, - CopyCode, - SwitchPage(DesignerPage), - EditorAction(text_editor::Action), - Drop(types::ElementName, iced::Point, iced::Rectangle), - HandleZones( - types::ElementName, - Vec<(iced::advanced::widget::Id, iced::Rectangle)>, - ), - Resized(pane_grid::ResizeEvent), - Clicked(pane_grid::Pane), - PaneDragged(pane_grid::DragEvent), -} - #[derive(Clone, Debug)] enum Panes { Designer, ElementList, } -impl Application for App { - type Message = Message; - type Theme = theme::Theme; - type Executor = executor::Default; - type Flags = (); - - fn new(_flags: ()) -> (Self, Command) { +impl App { + fn new() -> (Self, Task) { let state = pane_grid::State::with_configuration(pane_grid::Configuration::Split { axis: pane_grid::Axis::Vertical, ratio: 0.8, @@ -70,26 +53,25 @@ impl Application for App { }); ( Self { - is_saved: true, - current_project: None, + is_dirty: false, + is_loading: false, + project_path: None, + project: Project::new(), dark_theme: true, pane_state: state, focus: None, - designer_state: DesignerState { - designer_content: Some(RenderedElement::test()), - designer_page: DesignerPage::Designer, - }, - element_list: types::ElementName::ALL.to_vec(), + designer_page: DesignerPage::Designer, + element_list: ElementName::ALL.to_vec(), editor_content: text_editor::Content::new(), }, - Command::none(), + Task::none(), ) } fn title(&self) -> String { - let saved_state = if self.is_saved { "" } else { " *" }; + let saved_state = if !self.is_dirty { "" } else { " *" }; - let project_name = match &self.current_project { + let project_name = match &self.project.title { Some(n) => { format!( " - {}", @@ -108,17 +90,17 @@ impl Application for App { fn theme(&self) -> iced::Theme { if self.dark_theme { - theme::Theme::CatppuccinMocha + Theme::SolarizedDark } else { - theme::Theme::CatppuccinLatte + Theme::SolarizedLight } } - fn update(&mut self, message: Message) -> Command { + fn update(&mut self, message: Message) -> Task { match message { Message::ToggleTheme => self.dark_theme = !self.dark_theme, Message::CopyCode => return clipboard::write(self.editor_content.text()), - Message::SwitchPage(page) => self.designer_state.designer_page = page, + Message::SwitchPage(page) => self.designer_page = page, Message::EditorAction(action) => { if let text_editor::Action::Scroll { lines: _ } = action { self.editor_content.perform(action); @@ -130,38 +112,103 @@ impl Application for App { Message::Clicked(pane) => { self.focus = Some(pane); } - Message::Drop(name, point, _) => { + Message::DropNewElement(name, point, _) => { return iced_drop::zones_on_point( - move |zones| Message::HandleZones(name.clone(), zones), + move |zones| Message::HandleNew(name.clone(), zones), point, None, None, ) .into() } - Message::HandleZones(name, zones) => { - println!("{:?}\n{name}", zones); - println!("{:?}\n{name}\n{:?}", zones, self.title()); - if let Some(el) = &self.designer_state.designer_content { - self.editor_content = text_editor::Content::with_text( - &el.app_code( - match &self.current_project { - Some(title) => &title, - None => "New App", - }, - None, - ) - .unwrap(), - ); - } + Message::HandleNew(name, zones) => { + println!("\n\n{:?}\n{name}\n{:?}", zones, self.title()); + let code = self + .project + .clone() + .app_code() + .unwrap_or_else(|err| err.to_string()); + self.editor_content = text_editor::Content::with_text(&code); + } + Message::MoveElement(element, point, _) => { + return iced_drop::zones_on_point( + move |zones| Message::HandleMove(element.clone(), zones), + point, + None, + None, + ) + .into() + } + Message::HandleMove(element, zones) => { + println!( + "\n\n{:?}\n{element:0.4}", + zones + .into_iter() + .map(|c| c.0) + .collect::>() + ); } 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(); + } + } + Message::OpenFile => { + if !self.is_loading { + self.is_loading = true; + + return Task::perform(Project::from_file(), 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()), + ); + } + } + Message::SaveFile => { + if !self.is_loading { + self.is_loading = true; + + return Task::perform( + self.project + .clone() + .write_to_file(self.project_path.clone()), + Message::FileSaved, + ); + } + } + Message::FileSaved(result) => { + self.is_loading = false; + + if let Ok(path) = result { + self.project_path = Some(path); + self.is_dirty = false; + } + } } - Command::none() + Task::none() + } + + fn subscription(&self) -> iced::Subscription { + 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), + _ => None, + }) } fn view(&self) -> Element { @@ -172,28 +219,23 @@ impl Application for App { 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_state.designer_page { + Panes::Designer => match &self.designer_page { DesignerPage::Designer => { - let content = container(text(format!( - "{:0.4}", - self.designer_state - .designer_content - .clone() - .expect("Designer content hasn't been set yet."), - ))) - .id(iced::widget::container::Id::new("drop_zone")) - .height(Length::Fill) - .width(Length::Fill); + 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(el_tree) + .id(iced::widget::container::Id::new("drop_zone")) + .height(Length::Fill) + .width(Length::Fill); let title = row![ - text("Designer").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }), + 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); @@ -207,44 +249,37 @@ impl Application for App { } DesignerPage::CodeView => { let title = row![ - text("Generated Code").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }), + text("Generated Code"), Space::with_width(Length::Fill), tooltip( button( container( text('\u{0e801}').font(Font::with_name("editor-icons")) ) - .width(30) - .center_x() + .center_x(30) ) .on_press(Message::CopyCode), "Copy code to clipboard", - tooltip::Position::Left + 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::( - highlighter::Settings { - theme: if self.dark_theme { - highlighter::Theme::Base16Mocha - } else { - highlighter::Theme::InspiredGitHub - }, - extension: "rs".to_string(), + .highlight( + "rs", + if self.dark_theme { + highlighter::Theme::SolarizedDark + } else { + highlighter::Theme::InspiredGitHub }, - |highlight, _theme| highlight.to_format(), ) .height(Length::Fill) .padding(20), @@ -260,14 +295,10 @@ impl Application for App { Panes::ElementList => { let items_list = items_list_view(self.element_list.clone()); let content = column![items_list] - .align_items(Alignment::Center) + .align_x(Alignment::Center) .height(Length::Fill) .width(Length::Fill); - let title = text("Element List").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }); + let title = text("Element List"); let title_bar = pane_grid::TitleBar::new(title) .padding(10) .style(style::title_bar); @@ -292,39 +323,24 @@ impl Application for App { .push(header) .push(pane_grid) .spacing(5) - .align_items(Alignment::Center) + .align_x(Alignment::Center) .width(Length::Fill); container(content).height(Length::Fill).into() } } -const fn from_grayscale(grayscale: f32) -> Color { - Color { - r: grayscale, - g: grayscale, - b: grayscale, - a: 1.0, - } -} - -// #ffffff -const PANE_ID_COLOR_FOCUSED: Color = from_grayscale(1.0); - -// #e8e8e8 -const PANE_ID_COLOR_UNFOCUSED: Color = from_grayscale(0xE8 as f32 / 255.0); - -fn items_list_view(items: Vec) -> Element<'static, Message> { +fn items_list_view<'a>(items: Vec) -> Element<'a, Message> { let mut column = Column::new() .spacing(20) - .align_items(Alignment::Center) + .align_x(Alignment::Center) .width(Length::Fill); for item in items { let value = item.clone(); column = column.push( droppable(text(value.to_string())) - .on_drop(move |point, rect| Message::Drop(value.clone(), point, rect)), + .on_drop(move |point, rect| Message::DropNewElement(value.clone(), point, rect)), ); } @@ -332,23 +348,23 @@ fn items_list_view(items: Vec) -> Element<'static, Message> } mod style { - use iced::widget::container; - use iced::{Border, Theme}; + use iced::widget::{container::Style as CStyle, text::Style as TStyle}; + use iced::{color, Border, Theme}; - pub fn title_bar(theme: &Theme) -> container::Appearance { + pub fn title_bar(theme: &Theme) -> CStyle { let palette = theme.extended_palette(); - container::Appearance { + CStyle { text_color: Some(palette.background.strong.text), background: Some(palette.background.strong.color.into()), ..Default::default() } } - pub fn pane_active(theme: &Theme) -> container::Appearance { + pub fn pane_active(theme: &Theme) -> CStyle { let palette = theme.extended_palette(); - container::Appearance { + CStyle { background: Some(palette.background.weak.color.into()), border: Border { width: 1.0, @@ -359,10 +375,10 @@ mod style { } } - pub fn pane_focused(theme: &Theme) -> container::Appearance { + pub fn pane_focused(theme: &Theme) -> CStyle { let palette = theme.extended_palette(); - container::Appearance { + CStyle { background: Some(palette.background.weak.color.into()), border: Border { width: 4.0, diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs index 344f543..2d6cd8f 100644 --- a/iced_builder/src/types/mod.rs +++ b/iced_builder/src/types/mod.rs @@ -1,15 +1,9 @@ +pub mod project; pub mod rendered_element; -use rendered_element::RenderedElement; use serde::{Deserialize, Serialize}; -#[derive(Clone)] -pub struct DesignerState { - pub designer_content: Option, - pub designer_page: DesignerPage, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ElementName { Text(String), Button(String), diff --git a/iced_builder/src/types/project/mod.rs b/iced_builder/src/types/project/mod.rs new file mode 100644 index 0000000..557fa92 --- /dev/null +++ b/iced_builder/src/types/project/mod.rs @@ -0,0 +1,59 @@ +use std::path::{Path, PathBuf}; + +use serde::{Deserialize, Serialize}; + +use crate::Error; + +use super::rendered_element::RenderedElement; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Project { + pub title: Option, + pub content: Option, +} + +impl Project { + pub fn new() -> Self { + Self { + title: None, + content: None, + } + } + + pub async fn from_file() -> Result<(PathBuf, Self), Error> { + let picked_file = rfd::AsyncFileDialog::new() + .set_title("Open a JSON file...") + .add_filter("*.JSON, *.json", &["JSON", "json"]) + .pick_file() + .await + .ok_or(Error::DialogClosed)?; + + let path = picked_file.path().to_owned(); + + let contents = tokio::fs::read_to_string(&path).await?; + let element: Self = serde_json::from_str(&contents)?; + + Ok((path, element)) + } + + pub async fn write_to_file(self, path: Option) -> Result { + let path = if let Some(p) = path { + p + } else { + rfd::AsyncFileDialog::new() + .set_title("Save to JSON file...") + .add_filter("*.JSON, *.json", &["JSON", "json"]) + .save_file() + .await + .as_ref() + .map(rfd::FileHandle::path) + .map(Path::to_owned) + .ok_or(Error::DialogClosed)? + }; + + let contents = serde_json::to_string(&self.clone())?; + tokio::fs::write(&path, contents).await?; + + Ok(path) + } +} diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 2d5c81a..967352b 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -1,11 +1,15 @@ use std::collections::HashMap; +use iced::advanced::widget::Id; +use iced::{widget, Element, Length}; use serde::{Deserialize, Serialize}; use unique_id::{string::StringGenerator, Generator}; +use crate::Message; + use super::ElementName; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RenderedElement { pub id: String, pub child_elements: Option>, @@ -34,27 +38,104 @@ impl RenderedElement { } } - fn preset_options(mut self, options: Vec<&str>) -> Self { - for opt in options { - self.props.insert(opt.to_owned(), None); + pub fn find_by_id(&mut self, id: Id) -> Option<&mut Self> { + if Id::new(self.id.clone()) == id.clone() { + println!(""); + return Some(self); + } else if let Some(child_elements) = self.child_elements.as_mut() { + for element in child_elements { + let element = element.find_by_id(id.clone()); + if element.is_some() { + return element; + } + } + return None; + } else { + return None; } - self } - pub fn push(mut self, element: RenderedElement) -> Self { - if let Some(els) = self.child_elements.as_mut() { - els.push(element); + pub fn find_parent(&mut self, child_element: &RenderedElement) -> Option<&mut Self> { + if child_element == self { + return Some(self); + } else if self.child_elements.is_some() { + if self.child_elements.clone()?.contains(child_element) { + return Some(self); + } else { + if let Some(child_elements) = self.child_elements.as_mut() { + for element in child_elements { + let element: Option<&mut Self> = element.find_parent(child_element); + if element.is_some() { + return element; + } + } + } + return None; + } } else { - self.child_elements = Some(vec![element]); + return None; + } + } + + pub fn remove(&mut self, element: &RenderedElement) { + let parent = self.find_parent(element); + if let Some(child_elements) = parent.unwrap().child_elements.as_mut() { + if let Some(index) = child_elements.iter().position(|x| x == element) { + child_elements.remove(index); + } + } + } + + pub fn push(&mut self, element: RenderedElement) { + if let Some(child_elements) = self.child_elements.as_mut() { + child_elements.push(element); + } + } + + 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) + { + child_elements.insert(index, element); + } else { + child_elements.push(element); + } + } + } + + fn preset_options(mut self, options: Vec<&str>) -> Self { + for opt in options { + self.props.insert(opt.to_owned(), None); } self } - pub fn option(mut self, option: &'static str, value: &'static str) -> Self { + pub fn option(&mut self, option: &'static str, value: &'static str) { self.props .entry(option.to_owned()) .and_modify(|opt| *opt = Some(value.to_owned())); - self + } + + pub fn as_element(self) -> Element<'static, Message> { + let mut children = widget::column![]; + + if let Some(els) = self.child_elements.clone() { + for el in els { + children = children.push(el.clone().as_element()); + } + } + iced_drop::droppable( + widget::container( + widget::column![widget::text(self.name.clone().to_string()), children] + .width(Length::Fill), + ) + .style(widget::container::bordered_box), + ) + .id(Id::new(self.id.clone())) + .on_drop(move |point, rect| Message::MoveElement(self.clone(), point, rect)) + .into() } } @@ -121,10 +202,23 @@ pub fn image(path: &str) -> RenderedElement { RenderedElement::new(ElementName::Image(path.to_owned())) } -pub fn container(content: RenderedElement) -> RenderedElement { - RenderedElement::from_vec(ElementName::Container, vec![content]) +pub fn container(content: Option) -> RenderedElement { + match content { + Some(el) => RenderedElement::from_vec(ElementName::Container, vec![el]), + None => RenderedElement::from_vec(ElementName::Container, vec![]), + } } -pub fn row(child_elements: Vec) -> RenderedElement { - RenderedElement::from_vec(ElementName::Row, child_elements) +pub fn row(child_elements: Option>) -> RenderedElement { + match child_elements { + Some(els) => RenderedElement::from_vec(ElementName::Row, els), + None => RenderedElement::from_vec(ElementName::Row, vec![]), + } +} + +pub fn column(child_elements: Option>) -> RenderedElement { + match child_elements { + Some(els) => RenderedElement::from_vec(ElementName::Column, els), + None => RenderedElement::from_vec(ElementName::Column, vec![]), + } } diff --git a/iced_drop/Cargo.toml b/iced_drop/Cargo.toml index 8b9d88c..40beeb0 100644 --- a/iced_drop/Cargo.toml +++ b/iced_drop/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies.iced] -version = "0.12.1" +version = "0.13.1" features = ["advanced"] diff --git a/iced_drop/LICENSE b/iced_drop/LICENSE new file mode 100644 index 0000000..89d9fee --- /dev/null +++ b/iced_drop/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 jhannyj + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/iced_drop/README.md b/iced_drop/README.md index 41b637b..3768d54 100644 --- a/iced_drop/README.md +++ b/iced_drop/README.md @@ -1,5 +1,7 @@ # iced_drop +# Modified to use iced 0.13.1 for compatibility + A small library which provides a custom widget and operation to make drag and drop easier to implement in [iced](https://github.com/iced-rs/iced/tree/master) ## Usage diff --git a/iced_drop/src/lib.rs b/iced_drop/src/lib.rs index fc559dc..9906cbe 100644 --- a/iced_drop/src/lib.rs +++ b/iced_drop/src/lib.rs @@ -1,8 +1,10 @@ pub mod widget; use iced::{ - advanced::{graphics::futures::MaybeSend, renderer, widget::Id}, - Command, Element, Point, Rectangle, + advanced::widget::{operate, Id}, + advanced::{graphics::futures::MaybeSend, renderer}, + task::Task, + Element, Point, Rectangle, }; use widget::droppable::*; @@ -18,17 +20,17 @@ where Droppable::new(content) } -pub fn zones_on_point( +pub fn zones_on_point( msg: MF, point: Point, options: Option>, depth: Option, -) -> Command +) -> Task where - Message: 'static, - MF: Fn(Vec<(Id, Rectangle)>) -> Message + MaybeSend + Sync + Clone + 'static, + T: Send + 'static, + MF: Fn(Vec<(Id, Rectangle)>) -> T + MaybeSend + Sync + Clone + 'static, { - Command::widget(drop::find_zones( + operate(drop::find_zones( move |bounds| bounds.contains(point), options, depth, @@ -41,11 +43,11 @@ pub fn find_zones( filter: F, options: Option>, depth: Option, -) -> Command +) -> Task where - Message: 'static, + Message: Send + 'static, MF: Fn(Vec<(Id, Rectangle)>) -> Message + MaybeSend + Sync + Clone + 'static, - F: Fn(&Rectangle) -> bool + 'static, + F: Fn(&Rectangle) -> bool + Send + 'static, { - Command::widget(drop::find_zones(filter, options, depth)).map(move |id| msg(id)) + operate(drop::find_zones(filter, options, depth)).map(move |id| msg(id)) } diff --git a/iced_drop/src/widget/droppable.rs b/iced_drop/src/widget/droppable.rs index ed7dcbd..80d8600 100644 --- a/iced_drop/src/widget/droppable.rs +++ b/iced_drop/src/widget/droppable.rs @@ -228,8 +228,10 @@ where state.action = Action::Drag(start, position); // update the position of the overlay since the cursor was moved if self.drag_center { - state.overlay_bounds.x = position.x - state.overlay_bounds.width / 2.0; - state.overlay_bounds.y = position.y - state.overlay_bounds.height / 2.0; + state.overlay_bounds.x = + position.x - state.overlay_bounds.width / 2.0; + state.overlay_bounds.y = + position.y - state.overlay_bounds.height / 2.0; } else { state.overlay_bounds.x = state.widget_pos.x + position.x - start.x; state.overlay_bounds.y = state.widget_pos.y + position.y - start.y; @@ -315,7 +317,7 @@ where tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, - operation: &mut dyn Operation, + operation: &mut dyn Operation, ) { let state = tree.state.downcast_mut::(); operation.custom(state, self.id.as_ref()); diff --git a/iced_drop/src/widget/operation/drop.rs b/iced_drop/src/widget/operation/drop.rs index 12a2e30..a76181c 100644 --- a/iced_drop/src/widget/operation/drop.rs +++ b/iced_drop/src/widget/operation/drop.rs @@ -17,7 +17,7 @@ pub fn find_zones( depth: Option, ) -> impl Operation> where - F: Fn(&Rectangle) -> bool + 'static, + F: Fn(&Rectangle) -> bool + Send + 'static, { struct FindDropZone { filter: F, @@ -30,7 +30,7 @@ where impl Operation> for FindDropZone where - F: Fn(&Rectangle) -> bool + 'static, + F: Fn(&Rectangle) -> bool + Send + 'static, { fn container( &mut self, @@ -70,6 +70,7 @@ where _state: &mut dyn Scrollable, _id: Option<&Id>, bounds: Rectangle, + _content_bounds: Rectangle, translation: Vector, ) { if (self.filter)(&bounds) { -- cgit v1.2.3 From 7d241eb768a3c56d98f5d41183866fffb9ff167a Mon Sep 17 00:00:00 2001 From: pml68 Date: Sun, 20 Oct 2024 11:51:54 +0200 Subject: feat: implement d&d for new elements `ActionKind` enum to help generalize d&d actions removed `codegen` module small refactors --- Cargo.lock | 2 + iced_builder/Cargo.toml | 1 + iced_builder/src/codegen/mod.rs | 146 -------------------- iced_builder/src/lib.rs | 17 ++- iced_builder/src/main.rs | 68 +++++++--- iced_builder/src/types/element_name.rs | 90 +++++++++++++ iced_builder/src/types/mod.rs | 44 +----- iced_builder/src/types/project.rs | 137 +++++++++++++++++++ iced_builder/src/types/project/mod.rs | 59 --------- iced_builder/src/types/rendered_element.rs | 206 +++++++++++++++++++++++++---- 10 files changed, 467 insertions(+), 303 deletions(-) delete mode 100644 iced_builder/src/codegen/mod.rs create mode 100644 iced_builder/src/types/element_name.rs create mode 100644 iced_builder/src/types/project.rs delete mode 100644 iced_builder/src/types/project/mod.rs (limited to 'iced_builder/src') diff --git a/Cargo.lock b/Cargo.lock index a341d35..d65a742 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1588,6 +1588,7 @@ dependencies = [ "iced", "iced_aw", "iced_drop", + "indexmap", "rfd", "rust-format", "serde", @@ -1838,6 +1839,7 @@ checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown 0.15.0", + "serde", ] [[package]] diff --git a/iced_builder/Cargo.toml b/iced_builder/Cargo.toml index a7b1e7b..d788bc2 100644 --- a/iced_builder/Cargo.toml +++ b/iced_builder/Cargo.toml @@ -18,6 +18,7 @@ tokio = { version = "1.40.0", features = ["fs"] } rfd = "0.15.0" rust-format = "0.3.4" unique_id = "0.1.5" +indexmap = { version = "2.6.0", features = ["serde"] } [[bin]] name = "iced-builder" diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs deleted file mode 100644 index 38e8ec3..0000000 --- a/iced_builder/src/codegen/mod.rs +++ /dev/null @@ -1,146 +0,0 @@ -use rust_format::{Config, Edition, Formatter, RustFmt}; - -use crate::{ - types::{ - project::Project, - rendered_element::{container, row, svg, text, RenderedElement}, - ElementName, - }, - Error, -}; - -impl RenderedElement { - fn props_codegen(&self) -> String { - let mut props_string = String::new(); - - for (k, v) in self.props.clone() { - if let Some(v) = v { - props_string = format!("{props_string}.{k}({v})"); - } - } - - props_string - } - - 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 { - for element in els { - let (c_imports, children) = element.codegen(); - imports = format!("{imports}{c_imports}"); - elements = format!("{elements}{},", children); - } - } - - match &self.name { - ElementName::Container => { - imports = format!("{imports}container,"); - view = format!("{view}\ncontainer({elements}){props}"); - } - ElementName::Row => { - imports = format!("{imports}row,"); - view = format!("{view}\nrow![{elements}]{props}"); - } - ElementName::Column => { - imports = format!("{imports}column,"); - view = format!("{view}\ncolumn![{elements}]{props}"); - } - ElementName::Text(string) => { - imports = format!("{imports}text,"); - view = format!( - "{view}\ntext(\"{}\"){props}", - if *string == String::new() { - "New Text" - } else { - string - } - ); - } - ElementName::Button(string) => { - imports = format!("{imports}button,"); - view = format!( - "{view}\nbutton(\"{}\"){props}", - if *string == String::new() { - "New Button" - } else { - string - } - ); - } - ElementName::Image(path) => { - imports = format!("{imports}image,"); - view = format!("{view}\nimage(\"{path}\"){props}"); - } - ElementName::SVG(path) => { - imports = format!("{imports}svg,"); - view = format!("{view}\nsvg(\"{path}\"){props}"); - } - } - - (imports, view) - } - - pub fn test() -> RenderedElement { - let mut text1 = text("wow"); - text1.option("height", "120.5"); - text1.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 Project { - pub fn app_code(self) -> Result { - match &self.content { - Some(el) => { - let (imports, view) = el.codegen(); - let mut app_code = format!("use iced::{{widget::{{{imports}}},Element}};"); - - app_code = format!( - r#"// Automatically generated by iced Builder - {app_code} - - fn main() -> iced::Result {{ - iced::run("{}", State::update, State::view) - }} - - #[derive(Default)] - struct State; - - #[derive(Debug, Clone)] - enum Message {{}} - - impl State {{ - fn update(&mut self, _message: Message) {{}} - - fn view(&self) -> Element {{ - {view}.into() - }} - }}"#, - match &self.title { - Some(t) => t, - None => "New app", - } - ); - let config = Config::new_str() - .edition(Edition::Rust2021) - .option("trailing_comma", "Never") - .option("imports_granularity", "Crate"); - let rustfmt = RustFmt::from_config(config); - Ok(rustfmt.format_str(app_code)?) - } - None => Err("No element tree present".into()), - } - } -} diff --git a/iced_builder/src/lib.rs b/iced_builder/src/lib.rs index 971e0e3..6de9ba8 100644 --- a/iced_builder/src/lib.rs +++ b/iced_builder/src/lib.rs @@ -1,16 +1,18 @@ -pub mod codegen; pub mod types; use std::path::PathBuf; use iced::widget::{pane_grid, text_editor}; -use types::{project::Project, rendered_element::RenderedElement, DesignerPage}; +use types::{ + element_name::ElementName, project::Project, rendered_element::RenderedElement, DesignerPage, +}; #[derive(Debug, Clone)] pub enum Error { IOError(std::io::ErrorKind), SerdeError(String), FormatError(String), + NonExistentElement, DialogClosed, String(String), } @@ -24,6 +26,9 @@ impl std::fmt::Display for Error { Self::IOError(kind) => { write!(f, "{}", kind) } + Self::NonExistentElement => { + write!(f, "The element tree contains no matching element.") + } Self::DialogClosed => { write!( f, @@ -64,9 +69,9 @@ pub enum Message { CopyCode, SwitchPage(DesignerPage), EditorAction(text_editor::Action), - DropNewElement(types::ElementName, iced::Point, iced::Rectangle), + DropNewElement(ElementName, iced::Point, iced::Rectangle), HandleNew( - types::ElementName, + ElementName, Vec<(iced::advanced::widget::Id, iced::Rectangle)>, ), MoveElement(RenderedElement, iced::Point, iced::Rectangle), @@ -74,8 +79,8 @@ pub enum Message { RenderedElement, Vec<(iced::advanced::widget::Id, iced::Rectangle)>, ), - Resized(pane_grid::ResizeEvent), - Clicked(pane_grid::Pane), + PaneResized(pane_grid::ResizeEvent), + PaneClicked(pane_grid::Pane), PaneDragged(pane_grid::DragEvent), NewFile, OpenFile, diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index aeb5ea6..8efad3e 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -1,15 +1,18 @@ use std::path::PathBuf; use iced::{ + advanced::widget::Id, clipboard, highlighter, keyboard, widget::{ button, column, container, pane_grid::{self, Pane, PaneGrid}, - row, text, text_editor, tooltip, Column, Space, + row, text, text_editor, themer, tooltip, Column, Space, }, Alignment, Element, Font, Length, Settings, Task, Theme, }; -use iced_builder::types::{project::Project, DesignerPage, ElementName}; +use iced_builder::types::{ + element_name::ElementName, project::Project, rendered_element::ActionKind, DesignerPage, +}; use iced_builder::Message; use iced_drop::droppable; @@ -37,7 +40,7 @@ struct App { editor_content: text_editor::Content, } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] enum Panes { Designer, ElementList, @@ -106,12 +109,6 @@ impl App { self.editor_content.perform(action); } } - Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { - self.pane_state.resize(split, ratio); - } - Message::Clicked(pane) => { - self.focus = Some(pane); - } Message::DropNewElement(name, point, _) => { return iced_drop::zones_on_point( move |zones| Message::HandleNew(name.clone(), zones), @@ -122,7 +119,16 @@ impl App { .into() } Message::HandleNew(name, zones) => { - println!("\n\n{:?}\n{name}\n{:?}", zones, self.title()); + //println!("\n\n{:?}\n{name}\n{:?}", zones, self.title()); + let ids: Vec = 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); + if let Ok(Some(ref element)) = result { + self.project.content = Some(element.clone()); + } + println!("{:?}", result); + } let code = self .project .clone() @@ -140,13 +146,30 @@ impl App { .into() } Message::HandleMove(element, zones) => { - println!( - "\n\n{:?}\n{element:0.4}", - zones - .into_iter() - .map(|c| c.0) - .collect::>() - ); + let ids: Vec = zones.into_iter().map(|z| z.0).collect(); + if ids.len() > 0 { + println!( + "{:?}", + ActionKind::new( + ids, + &mut self.project.content.clone(), + Some(element.get_id()) + ) + ); + } + //println!( + // "\n\n{:?}\n{element:0.4}", + // zones + // .into_iter() + // .map(|c| c.0) + // .collect::>() + //); + } + Message::PaneResized(pane_grid::ResizeEvent { split, ratio }) => { + self.pane_state.resize(split, ratio); + } + Message::PaneClicked(pane) => { + self.focus = Some(pane); } Message::PaneDragged(pane_grid::DragEvent::Dropped { pane, target }) => { self.pane_state.drop(pane, target); @@ -207,6 +230,7 @@ impl App { 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("n") if modifiers.command() => Some(Message::NewFile), _ => None, }) } @@ -225,7 +249,7 @@ impl App { Some(tree) => tree.as_element(), None => text("Open a project or begin creating one").into(), }; - let content = container(el_tree) + let content = container(themer(self.project.get_theme(), el_tree)) .id(iced::widget::container::Id::new("drop_zone")) .height(Length::Fill) .width(Length::Fill); @@ -315,8 +339,8 @@ impl App { .width(Length::Fill) .height(Length::Fill) .spacing(10) - .on_resize(10, Message::Resized) - .on_click(Message::Clicked) + .on_resize(10, Message::PaneResized) + .on_click(Message::PaneClicked) .on_drag(Message::PaneDragged); let content = Column::new() @@ -348,8 +372,8 @@ fn items_list_view<'a>(items: Vec) -> Element<'a, Message> { } mod style { - use iced::widget::{container::Style as CStyle, text::Style as TStyle}; - use iced::{color, Border, Theme}; + use iced::widget::container::Style as CStyle; + use iced::{Border, Theme}; pub fn title_bar(theme: &Theme) -> CStyle { let palette = theme.extended_palette(); diff --git a/iced_builder/src/types/element_name.rs b/iced_builder/src/types/element_name.rs new file mode 100644 index 0000000..ca0668c --- /dev/null +++ b/iced_builder/src/types/element_name.rs @@ -0,0 +1,90 @@ +use serde::{Deserialize, Serialize}; + +use crate::Error; + +use super::rendered_element::{ + self, button, column, container, image, row, svg, text, ActionKind, RenderedElement, +}; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum ElementName { + Text(String), + Button(String), + SVG(String), + Image(String), + Container, + Row, + Column, +} + +impl ElementName { + pub const ALL: [Self; 7] = [ + Self::Text(String::new()), + Self::Button(String::new()), + Self::SVG(String::new()), + Self::Image(String::new()), + Self::Container, + Self::Row, + Self::Column, + ]; + + pub fn handle_action( + &self, + element_tree: Option<&mut RenderedElement>, + action: ActionKind, + ) -> Result, Error> { + let element = match self { + Self::Text(_) => text(""), + Self::Button(_) => button(""), + Self::SVG(_) => svg(""), + Self::Image(_) => image(""), + Self::Container => container(None), + Self::Row => row(None), + Self::Column => column(None), + }; + match action { + ActionKind::Stop => Ok(None), + ActionKind::AddNew => Ok(Some(element)), + ActionKind::PushFront(id) => { + element_tree + .ok_or(Error::String( + "The action was of kind `PushFront`, but no element tree was provided." + .to_owned(), + ))? + .find_by_id(id) + .ok_or(Error::NonExistentElement)? + .push_front(&element); + Ok(None) + } + ActionKind::InsertAfter(parent_id, child_id) => { + element_tree + .ok_or(Error::String( + "The action was of kind `InsertAfter`, but no element tree was provided." + .to_owned(), + ))? + .find_by_id(parent_id) + .ok_or(Error::NonExistentElement)? + .insert_after(child_id, &element); + Ok(None) + } + } + } +} + +impl std::fmt::Display for ElementName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Text(_) => "Text", + Self::Button(_) => "Button", + Self::SVG(_) => "SVG", + Self::Image(_) => "Image", + Self::Container => "Container", + Self::Row => "Row", + Self::Column => "Column", + } + ) + } +} diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs index 2d6cd8f..a48a2d8 100644 --- a/iced_builder/src/types/mod.rs +++ b/iced_builder/src/types/mod.rs @@ -1,49 +1,7 @@ +pub mod element_name; pub mod project; pub mod rendered_element; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub enum ElementName { - Text(String), - Button(String), - SVG(String), - Image(String), - Container, - Row, - Column, -} - -impl ElementName { - pub const ALL: [Self; 7] = [ - Self::Text(String::new()), - Self::Button(String::new()), - Self::SVG(String::new()), - Self::Image(String::new()), - Self::Container, - Self::Row, - Self::Column, - ]; -} - -impl std::fmt::Display for ElementName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::Text(_) => "Text", - Self::Button(_) => "Button", - Self::SVG(_) => "SVG", - Self::Image(_) => "Image", - Self::Container => "Container", - Self::Row => "Row", - Self::Column => "Column", - } - ) - } -} - #[derive(Debug, Clone)] pub enum DesignerPage { Designer, diff --git a/iced_builder/src/types/project.rs b/iced_builder/src/types/project.rs new file mode 100644 index 0000000..0e0442a --- /dev/null +++ b/iced_builder/src/types/project.rs @@ -0,0 +1,137 @@ +use rust_format::{Config, Edition, Formatter, RustFmt}; +use std::path::{Path, PathBuf}; + +use iced::Theme; +use serde::{Deserialize, Serialize}; + +use crate::Error; + +use super::rendered_element::RenderedElement; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Project { + pub title: Option, + pub theme: Option, + pub content: Option, +} + +impl Project { + pub fn new() -> Self { + Self { + title: None, + theme: None, + content: None, + } + } + + pub fn get_theme(&self) -> Theme { + match &self.theme { + Some(theme) => match theme.as_str() { + "Light" => Theme::Light, + "Dark" => Theme::Dark, + "Dracula" => Theme::Dracula, + "Nord" => Theme::Nord, + "Solarized Light" => Theme::SolarizedLight, + "Solarized Dark" => Theme::SolarizedDark, + "Gruvbox Light" => Theme::GruvboxLight, + "Gruvbox Dark" => Theme::GruvboxDark, + "Catppuccin Latte" => Theme::CatppuccinLatte, + "Catppuccin Frappé" => Theme::CatppuccinFrappe, + "Catppuccin Macchiato" => Theme::CatppuccinMacchiato, + "Catppuccin Mocha" => Theme::CatppuccinMocha, + "Tokyo Night" => Theme::TokyoNight, + "Tokyo Night Storm" => Theme::TokyoNightStorm, + "Tokyo Night Light" => Theme::TokyoNightLight, + "Kanagawa Wave" => Theme::KanagawaWave, + "Kanagawa Dragon" => Theme::KanagawaDragon, + "Kanagawa Lotus" => Theme::KanagawaLotus, + "Moonfly" => Theme::Moonfly, + "Nightfly" => Theme::Nightfly, + "Oxocarbon" => Theme::Oxocarbon, + "Ferra" => Theme::Ferra, + _ => Theme::Dark, + }, + None => Theme::Dark, + } + } + + pub async fn from_file() -> Result<(PathBuf, Self), Error> { + let picked_file = rfd::AsyncFileDialog::new() + .set_title("Open a JSON file...") + .add_filter("*.JSON, *.json", &["JSON", "json"]) + .pick_file() + .await + .ok_or(Error::DialogClosed)?; + + let path = picked_file.path().to_owned(); + + let contents = tokio::fs::read_to_string(&path).await?; + let element: Self = serde_json::from_str(&contents)?; + + Ok((path, element)) + } + + pub async fn write_to_file(self, path: Option) -> Result { + let path = if let Some(p) = path { + p + } else { + rfd::AsyncFileDialog::new() + .set_title("Save to JSON file...") + .add_filter("*.JSON, *.json", &["JSON", "json"]) + .save_file() + .await + .as_ref() + .map(rfd::FileHandle::path) + .map(Path::to_owned) + .ok_or(Error::DialogClosed)? + }; + + let contents = serde_json::to_string(&self.clone())?; + tokio::fs::write(&path, contents).await?; + + Ok(path) + } + + pub fn app_code(self) -> Result { + match &self.content { + Some(el) => { + let (imports, view) = el.codegen(); + let mut app_code = format!("use iced::{{widget::{{{imports}}},Element}};"); + + app_code = format!( + r#"// Automatically generated by iced Builder + {app_code} + + fn main() -> iced::Result {{ + iced::run("{}", State::update, State::view) + }} + + #[derive(Default)] + struct State; + + #[derive(Debug, Clone)] + enum Message {{}} + + impl State {{ + fn update(&mut self, _message: Message) {{}} + + fn view(&self) -> Element {{ + {view}.into() + }} + }}"#, + match &self.title { + Some(t) => t, + None => "New app", + } + ); + let config = Config::new_str() + .edition(Edition::Rust2021) + .option("trailing_comma", "Never") + .option("imports_granularity", "Crate"); + let rustfmt = RustFmt::from_config(config); + Ok(rustfmt.format_str(app_code)?) + } + None => Err("No element tree present".into()), + } + } +} diff --git a/iced_builder/src/types/project/mod.rs b/iced_builder/src/types/project/mod.rs deleted file mode 100644 index 557fa92..0000000 --- a/iced_builder/src/types/project/mod.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::path::{Path, PathBuf}; - -use serde::{Deserialize, Serialize}; - -use crate::Error; - -use super::rendered_element::RenderedElement; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Project { - pub title: Option, - pub content: Option, -} - -impl Project { - pub fn new() -> Self { - Self { - title: None, - content: None, - } - } - - pub async fn from_file() -> Result<(PathBuf, Self), Error> { - let picked_file = rfd::AsyncFileDialog::new() - .set_title("Open a JSON file...") - .add_filter("*.JSON, *.json", &["JSON", "json"]) - .pick_file() - .await - .ok_or(Error::DialogClosed)?; - - let path = picked_file.path().to_owned(); - - let contents = tokio::fs::read_to_string(&path).await?; - let element: Self = serde_json::from_str(&contents)?; - - Ok((path, element)) - } - - pub async fn write_to_file(self, path: Option) -> Result { - let path = if let Some(p) = path { - p - } else { - rfd::AsyncFileDialog::new() - .set_title("Save to JSON file...") - .add_filter("*.JSON, *.json", &["JSON", "json"]) - .save_file() - .await - .as_ref() - .map(rfd::FileHandle::path) - .map(Path::to_owned) - .ok_or(Error::DialogClosed)? - }; - - let contents = serde_json::to_string(&self.clone())?; - tokio::fs::write(&path, contents).await?; - - Ok(path) - } -} diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 967352b..827e8c2 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use indexmap::IndexMap; use iced::advanced::widget::Id; use iced::{widget, Element, Length}; @@ -7,14 +7,14 @@ use unique_id::{string::StringGenerator, Generator}; use crate::Message; -use super::ElementName; +use super::element_name::ElementName; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RenderedElement { - pub id: String, + id: String, pub child_elements: Option>, pub name: ElementName, - pub props: HashMap>, + pub options: IndexMap>, } impl RenderedElement { @@ -24,22 +24,26 @@ impl RenderedElement { id: gen.next_id(), child_elements: None, name, - props: HashMap::new(), + options: IndexMap::new(), } } - fn from_vec(name: ElementName, child_elements: Vec) -> Self { + fn with(name: ElementName, child_elements: Vec) -> Self { let gen = StringGenerator::default(); Self { id: gen.next_id(), child_elements: Some(child_elements), name, - props: HashMap::new(), + options: IndexMap::new(), } } + pub fn get_id(&self) -> Id { + Id::new(self.id.clone()) + } + pub fn find_by_id(&mut self, id: Id) -> Option<&mut Self> { - if Id::new(self.id.clone()) == id.clone() { + if self.get_id() == id.clone() { println!(""); return Some(self); } else if let Some(child_elements) = self.child_elements.as_mut() { @@ -59,12 +63,17 @@ impl RenderedElement { if child_element == self { return Some(self); } else if self.child_elements.is_some() { - if self.child_elements.clone()?.contains(child_element) { + if self + .child_elements + .clone() + .unwrap_or(vec![]) + .contains(child_element) + { return Some(self); } else { if let Some(child_elements) = self.child_elements.as_mut() { for element in child_elements { - let element: Option<&mut Self> = element.find_parent(child_element); + let element = element.find_parent(child_element); if element.is_some() { return element; } @@ -77,6 +86,10 @@ impl RenderedElement { } } + pub fn is_parent(&self) -> bool { + self.child_elements.is_some() + } + pub fn remove(&mut self, element: &RenderedElement) { let parent = self.find_parent(element); if let Some(child_elements) = parent.unwrap().child_elements.as_mut() { @@ -86,39 +99,39 @@ impl RenderedElement { } } - pub fn push(&mut self, element: RenderedElement) { + pub fn push_front(&mut self, element: &RenderedElement) { if let Some(child_elements) = self.child_elements.as_mut() { - child_elements.push(element); + child_elements.insert(0, element.clone()); } } - pub fn insert_after(&mut self, id: Id, element: 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) { - child_elements.insert(index, element); + child_elements.insert(index + 1, element.clone()); } else { - child_elements.push(element); + child_elements.push(element.clone()); } } } fn preset_options(mut self, options: Vec<&str>) -> Self { for opt in options { - self.props.insert(opt.to_owned(), None); + self.options.insert(opt.to_owned(), None); } self } - pub fn option(&mut self, option: &'static str, value: &'static str) { - self.props + pub fn option<'a>(&mut self, option: &'a str, value: &'a str) { + self.options .entry(option.to_owned()) .and_modify(|opt| *opt = Some(value.to_owned())); } - pub fn as_element(self) -> Element<'static, Message> { + pub fn as_element<'a>(self) -> Element<'a, Message> { let mut children = widget::column![]; if let Some(els) = self.child_elements.clone() { @@ -131,12 +144,103 @@ impl RenderedElement { widget::column![widget::text(self.name.clone().to_string()), children] .width(Length::Fill), ) + .padding(10) .style(widget::container::bordered_box), ) - .id(Id::new(self.id.clone())) + .id(self.get_id()) + .drag_hide(true) .on_drop(move |point, rect| Message::MoveElement(self.clone(), point, rect)) .into() } + + fn props_codegen(&self) -> String { + let mut props_string = String::new(); + + for (k, v) in self.options.clone() { + if let Some(v) = v { + props_string = format!("{props_string}.{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 { + for element in els { + let (c_imports, children) = element.codegen(); + imports = format!("{imports}{c_imports}"); + elements = format!("{elements}{},", children); + } + } + + match &self.name { + ElementName::Container => { + imports = format!("{imports}container,"); + view = format!("{view}\ncontainer({elements}){props}"); + } + ElementName::Row => { + imports = format!("{imports}row,"); + view = format!("{view}\nrow![{elements}]{props}"); + } + ElementName::Column => { + imports = format!("{imports}column,"); + view = format!("{view}\ncolumn![{elements}]{props}"); + } + ElementName::Text(string) => { + imports = format!("{imports}text,"); + view = format!( + "{view}\ntext(\"{}\"){props}", + if *string == String::new() { + "New Text" + } else { + string + } + ); + } + ElementName::Button(string) => { + imports = format!("{imports}button,"); + view = format!( + "{view}\nbutton(\"{}\"){props}", + if *string == String::new() { + "New Button" + } else { + string + } + ); + } + ElementName::Image(path) => { + imports = format!("{imports}image,"); + view = format!("{view}\nimage(\"{path}\"){props}"); + } + ElementName::SVG(path) => { + imports = format!("{imports}svg,"); + view = format!("{view}\nsvg(\"{path}\"){props}"); + } + } + + (imports, view) + } + + pub fn test() -> RenderedElement { + let mut text1 = text("wow"); + text1.option("height", "120.5"); + text1.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 { @@ -146,7 +250,7 @@ impl std::fmt::Display for RenderedElement { f.write_fmt(format_args!("{:?}\n", self.name))?; f.pad("")?; f.write_str("Options: (")?; - for (k, v) in &self.props { + for (k, v) in &self.options { if let Some(value) = v { has_props = true; f.write_fmt(format_args!( @@ -181,6 +285,54 @@ impl std::fmt::Display for RenderedElement { } } +#[derive(Debug, Clone)] +pub enum ActionKind { + AddNew, + PushFront(Id), + InsertAfter(Id, Id), + Stop, +} + +impl ActionKind { + pub fn new( + ids: Vec, + element_tree: &mut Option, + source_id: Option, + ) -> Self { + let mut action = Self::Stop; + if ids.len() == 1 { + if element_tree.is_none() { + action = Self::AddNew; + } + } else { + let id: Id = match source_id { + Some(id) if ids.contains(&id) => { + let element_id = ids[ids.iter().position(|x| *x == id).unwrap()].clone(); + if ids.len() > 2 && ids[ids.clone().len() - 1] == element_id { + return Self::Stop; + } + element_id + } + _ => ids.last().cloned().unwrap(), + }; + let element = element_tree.as_mut().unwrap().find_by_id(id.clone()); + + match element.unwrap().is_parent() { + true => action = Self::PushFront(id), + false => { + if ids.len() > 2 { + action = Self::InsertAfter( + ids[ids.clone().len() - 2].clone(), + ids[ids.clone().len() - 1].clone(), + ); + } + } + } + } + action + } +} + pub fn text(text: &str) -> RenderedElement { RenderedElement::new(ElementName::Text(text.to_owned())).preset_options(vec![ "size", @@ -204,21 +356,21 @@ pub fn image(path: &str) -> RenderedElement { pub fn container(content: Option) -> RenderedElement { match content { - Some(el) => RenderedElement::from_vec(ElementName::Container, vec![el]), - None => RenderedElement::from_vec(ElementName::Container, vec![]), + Some(el) => RenderedElement::with(ElementName::Container, vec![el]), + None => RenderedElement::with(ElementName::Container, vec![]), } } pub fn row(child_elements: Option>) -> RenderedElement { match child_elements { - Some(els) => RenderedElement::from_vec(ElementName::Row, els), - None => RenderedElement::from_vec(ElementName::Row, vec![]), + Some(els) => RenderedElement::with(ElementName::Row, els), + None => RenderedElement::with(ElementName::Row, vec![]), } } pub fn column(child_elements: Option>) -> RenderedElement { match child_elements { - Some(els) => RenderedElement::from_vec(ElementName::Column, els), - None => RenderedElement::from_vec(ElementName::Column, vec![]), + Some(els) => RenderedElement::with(ElementName::Column, els), + None => RenderedElement::with(ElementName::Column, vec![]), } } -- cgit v1.2.3 From b66f5d57013243211c37fa52dbe26fe0b241bd07 Mon Sep 17 00:00:00 2001 From: pml68 Date: Tue, 22 Oct 2024 12:57:35 +0200 Subject: feat: disable appending to container if it already has children fix: Column element now showing in element list --- iced_builder/src/main.rs | 24 +++++++++++++----------- iced_builder/src/types/element_name.rs | 2 +- iced_builder/src/types/rendered_element.rs | 13 +++++++++++-- 3 files changed, 25 insertions(+), 14 deletions(-) (limited to 'iced_builder/src') diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 8efad3e..0c76374 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -361,34 +361,36 @@ fn items_list_view<'a>(items: Vec) -> Element<'a, Message> { .width(Length::Fill); for item in items { - let value = item.clone(); column = column.push( - droppable(text(value.to_string())) - .on_drop(move |point, rect| Message::DropNewElement(value.clone(), point, rect)), + droppable(text(item.clone().to_string())) + .on_drop(move |point, rect| Message::DropNewElement(item.clone(), point, rect)), ); } - container(column).height(250.0).width(300).into() + container(column) + .width(Length::Fill) + .height(Length::Fill) + .into() } mod style { - use iced::widget::container::Style as CStyle; + use iced::widget::container::Style; use iced::{Border, Theme}; - pub fn title_bar(theme: &Theme) -> CStyle { + pub fn title_bar(theme: &Theme) -> Style { let palette = theme.extended_palette(); - CStyle { + Style { text_color: Some(palette.background.strong.text), background: Some(palette.background.strong.color.into()), ..Default::default() } } - pub fn pane_active(theme: &Theme) -> CStyle { + pub fn pane_active(theme: &Theme) -> Style { let palette = theme.extended_palette(); - CStyle { + Style { background: Some(palette.background.weak.color.into()), border: Border { width: 1.0, @@ -399,10 +401,10 @@ mod style { } } - pub fn pane_focused(theme: &Theme) -> CStyle { + pub fn pane_focused(theme: &Theme) -> Style { let palette = theme.extended_palette(); - CStyle { + Style { background: Some(palette.background.weak.color.into()), border: Border { width: 4.0, diff --git a/iced_builder/src/types/element_name.rs b/iced_builder/src/types/element_name.rs index ca0668c..c30d6e3 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::{ - self, button, column, container, image, row, svg, text, ActionKind, RenderedElement, + button, column, container, image, row, svg, text, ActionKind, RenderedElement, }; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 827e8c2..6b01bc3 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -317,8 +317,17 @@ impl ActionKind { }; let element = element_tree.as_mut().unwrap().find_by_id(id.clone()); - match element.unwrap().is_parent() { - true => action = Self::PushFront(id), + match element.as_ref().unwrap().is_parent() { + true => { + let element = &element.unwrap(); + if element.name == ElementName::Container + && element.child_elements != Some(vec![]) + { + action = Self::Stop; + } else { + action = Self::PushFront(id); + } + } false => { if ids.len() > 2 { action = Self::InsertAfter( -- cgit v1.2.3 From 75b8ef8d9801aec40654c955ff941a3205859603 Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 24 Oct 2024 20:45:26 +0200 Subject: test: remove some unneeded printlns --- iced_builder/src/main.rs | 8 -------- iced_builder/src/types/rendered_element.rs | 1 - 2 files changed, 9 deletions(-) (limited to 'iced_builder/src') diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 0c76374..424e0e0 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -119,7 +119,6 @@ impl App { .into() } Message::HandleNew(name, zones) => { - //println!("\n\n{:?}\n{name}\n{:?}", zones, self.title()); let ids: Vec = zones.into_iter().map(|z| z.0).collect(); if ids.len() > 0 { let action = ActionKind::new(ids, &mut self.project.content.clone(), None); @@ -157,13 +156,6 @@ impl App { ) ); } - //println!( - // "\n\n{:?}\n{element:0.4}", - // zones - // .into_iter() - // .map(|c| c.0) - // .collect::>() - //); } Message::PaneResized(pane_grid::ResizeEvent { split, ratio }) => { self.pane_state.resize(split, ratio); diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 6b01bc3..336f78f 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -44,7 +44,6 @@ impl RenderedElement { pub fn find_by_id(&mut self, id: Id) -> Option<&mut Self> { if self.get_id() == id.clone() { - println!(""); return Some(self); } else if let Some(child_elements) = self.child_elements.as_mut() { for element in child_elements { -- cgit v1.2.3 From bddbb502c2fed62c6d05f7ef2c9d0b76da493ab1 Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 24 Oct 2024 20:48:21 +0200 Subject: feat(actions): refactor match statement, add special case for containers When a new element is dragged on top of a container that isn't the root element (i.e. has a parent) and said parent isn't a container either, the element is inserted into the container's parent element. s --- iced_builder/src/types/rendered_element.rs | 46 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 16 deletions(-) mode change 100644 => 100755 iced_builder/src/types/rendered_element.rs (limited to 'iced_builder/src') diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs old mode 100644 new mode 100755 index 336f78f..8b9b222 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -89,9 +89,12 @@ impl RenderedElement { self.child_elements.is_some() } + pub fn is_empty(&self) -> bool { + self.child_elements == Some(vec![]) + } + pub fn remove(&mut self, element: &RenderedElement) { - let parent = self.find_parent(element); - if let Some(child_elements) = parent.unwrap().child_elements.as_mut() { + if let Some(child_elements) = self.child_elements.as_mut() { if let Some(index) = child_elements.iter().position(|x| x == element) { child_elements.remove(index); } @@ -314,27 +317,38 @@ impl ActionKind { } _ => ids.last().cloned().unwrap(), }; - let element = element_tree.as_mut().unwrap().find_by_id(id.clone()); - - match element.as_ref().unwrap().is_parent() { - true => { - let element = &element.unwrap(); - if element.name == ElementName::Container - && element.child_elements != Some(vec![]) + let element = element_tree + .as_mut() + .unwrap() + .find_by_id(id.clone()) + .unwrap(); + + match ( + element.is_parent(), + element.name == ElementName::Container && !element.is_empty(), + ) { + (true, false) => { + action = Self::PushFront(id); + } + _ if ids.len() > 2 => { + let parent = element_tree + .as_mut() + .unwrap() + .find_by_id(ids[&ids.len() - 2].clone()) + .unwrap(); + + if parent.name == ElementName::Container + && parent.child_elements != Some(vec![]) { action = Self::Stop; } else { - action = Self::PushFront(id); - } - } - false => { - if ids.len() > 2 { action = Self::InsertAfter( - ids[ids.clone().len() - 2].clone(), - ids[ids.clone().len() - 1].clone(), + ids[&ids.len() - 2].clone(), + ids[&ids.len() - 1].clone(), ); } } + _ => {} } } action -- cgit v1.2.3 From f04dc01bf5400faa6876b402100037501e374807 Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 24 Oct 2024 23:17:42 +0200 Subject: feat: implement d&d for existing elements --- iced_builder/src/lib.rs | 1 + iced_builder/src/main.rs | 32 ++++++++++++++--------- iced_builder/src/types/element_name.rs | 12 +++------ iced_builder/src/types/rendered_element.rs | 42 ++++++++++++++++++++++++++---- 4 files changed, 61 insertions(+), 26 deletions(-) (limited to 'iced_builder/src') diff --git a/iced_builder/src/lib.rs b/iced_builder/src/lib.rs index 6de9ba8..420b14c 100644 --- a/iced_builder/src/lib.rs +++ b/iced_builder/src/lib.rs @@ -69,6 +69,7 @@ pub enum Message { CopyCode, SwitchPage(DesignerPage), EditorAction(text_editor::Action), + RefreshEditorContent, DropNewElement(ElementName, iced::Point, iced::Rectangle), HandleNew( ElementName, diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 424e0e0..fc7f18c 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -109,6 +109,14 @@ impl App { self.editor_content.perform(action); } } + 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); + } Message::DropNewElement(name, point, _) => { return iced_drop::zones_on_point( move |zones| Message::HandleNew(name.clone(), zones), @@ -128,12 +136,8 @@ impl App { } println!("{:?}", result); } - let code = self - .project - .clone() - .app_code() - .unwrap_or_else(|err| err.to_string()); - self.editor_content = text_editor::Content::with_text(&code); + + return Task::done(Message::RefreshEditorContent); } Message::MoveElement(element, point, _) => { return iced_drop::zones_on_point( @@ -147,15 +151,17 @@ impl App { Message::HandleMove(element, zones) => { let ids: Vec = zones.into_iter().map(|z| z.0).collect(); if ids.len() > 0 { - println!( - "{:?}", - ActionKind::new( - ids, - &mut self.project.content.clone(), - Some(element.get_id()) - ) + let action = ActionKind::new( + ids, + &mut self.project.content.clone(), + Some(element.get_id()), ); + let result = element.handle_action(self.project.content.as_mut(), action); + + println!("{result:?}"); } + + return Task::done(Message::RefreshEditorContent); } Message::PaneResized(pane_grid::ResizeEvent { split, ratio }) => { self.pane_state.resize(split, ratio); diff --git a/iced_builder/src/types/element_name.rs b/iced_builder/src/types/element_name.rs index c30d6e3..8d00814 100644 --- a/iced_builder/src/types/element_name.rs +++ b/iced_builder/src/types/element_name.rs @@ -47,10 +47,7 @@ impl ElementName { ActionKind::AddNew => Ok(Some(element)), ActionKind::PushFront(id) => { element_tree - .ok_or(Error::String( - "The action was of kind `PushFront`, but no element tree was provided." - .to_owned(), - ))? + .ok_or("The action was of kind `PushFront`, but no element tree was provided.")? .find_by_id(id) .ok_or(Error::NonExistentElement)? .push_front(&element); @@ -58,10 +55,9 @@ impl ElementName { } ActionKind::InsertAfter(parent_id, child_id) => { element_tree - .ok_or(Error::String( - "The action was of kind `InsertAfter`, but no element tree was provided." - .to_owned(), - ))? + .ok_or( + "The action was of kind `InsertAfter`, but no element tree was provided.", + )? .find_by_id(parent_id) .ok_or(Error::NonExistentElement)? .insert_after(child_id, &element); diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 8b9b222..e2bebfa 100755 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -5,7 +5,7 @@ use iced::{widget, Element, Length}; use serde::{Deserialize, Serialize}; use unique_id::{string::StringGenerator, Generator}; -use crate::Message; +use crate::{Error, Message}; use super::element_name::ElementName; @@ -120,6 +120,39 @@ impl RenderedElement { } } + pub fn handle_action( + &self, + element_tree: Option<&mut RenderedElement>, + action: ActionKind, + ) -> 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(), + ), + ActionKind::PushFront(id) => { + let old_parent = element_tree.find_parent(self).unwrap(); + old_parent.remove(self); + + let new_parent = element_tree.find_by_id(id).unwrap(); + new_parent.push_front(self); + + Ok(()) + } + ActionKind::InsertAfter(parent_id, target_id) => { + let old_parent = element_tree.find_parent(self).unwrap(); + old_parent.remove(self); + + let new_parent = element_tree.find_by_id(parent_id).unwrap(); + new_parent.insert_after(target_id, self); + + Ok(()) + } + } + } + fn preset_options(mut self, options: Vec<&str>) -> Self { for opt in options { self.options.insert(opt.to_owned(), None); @@ -127,10 +160,11 @@ impl RenderedElement { self } - pub fn option<'a>(&mut self, option: &'a str, value: &'a str) { + pub fn option<'a>(&mut self, option: &'a str, value: &'a str) -> Self { self.options .entry(option.to_owned()) .and_modify(|opt| *opt = Some(value.to_owned())); + self.clone() } pub fn as_element<'a>(self) -> Element<'a, Message> { @@ -231,9 +265,7 @@ impl RenderedElement { } pub fn test() -> RenderedElement { - let mut text1 = text("wow"); - text1.option("height", "120.5"); - text1.option("width", "230"); + let text1 = text("wow").option("height", "120.5").option("width", "230"); let element = container(Some(row(Some(vec![ text1, -- cgit v1.2.3