diff options
| author | Polesznyák Márk <contact@pml68.dev> | 2025-11-15 12:33:22 +0100 |
|---|---|---|
| committer | Polesznyák Márk <contact@pml68.dev> | 2025-11-15 12:33:22 +0100 |
| commit | e67680b0030a890d75ed35bc1144d1e8880dd3be (patch) | |
| tree | c465299e27a7af8a6c53d435b8301100702ef3be /src | |
| parent | chore: update deps (diff) | |
| download | iced-builder-e67680b0030a890d75ed35bc1144d1e8880dd3be.tar.gz | |
feat: add `Grid` as a usable widget
Diffstat (limited to '')
| -rw-r--r-- | src/options.rs | 36 | ||||
| -rw-r--r-- | src/types/element_name.rs | 9 | ||||
| -rwxr-xr-x | src/types/rendered_element.rs | 36 | ||||
| -rw-r--r-- | src/values.rs | 1 | ||||
| -rw-r--r-- | src/values/sizing.rs | 109 |
5 files changed, 186 insertions, 5 deletions
diff --git a/src/options.rs b/src/options.rs index 2dc25d7..2ebdd5f 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,9 +1,10 @@ use std::collections::BTreeMap; use std::str::FromStr; +use iced::widget::grid::Sizing; use iced::widget::text::LineHeight; #[allow(unused_imports)] -use iced::widget::{Button, Column, Container, Image, Row, Svg, Text}; +use iced::widget::{Button, Column, Container, Grid, Image, Row, Svg, Text}; use iced::{Alignment, ContentFit, Length, Padding, Pixels, Rotation}; use crate::values::Value; @@ -258,6 +259,39 @@ impl<Message> ApplyOptions for Row<'_, Message> { } } +impl<'a, Message> ApplyOptions for Grid<'a, Message> { + fn apply_options(self, options: BTreeMap<String, Option<String>>) -> Self { + let mut grid = self; + + if let Some(spacing) = options.get("spacing").expect("spacing key") { + let spacing = f32::from_str(spacing).unwrap(); + grid = grid.spacing(spacing); + } + + if let Some(width) = options.get("width").expect("width key") { + let width = Pixels::from_str(width).unwrap(); + grid = grid.width(width); + } + + if let Some(height) = options.get("height").expect("height key") { + let height = Sizing::from_str(height).unwrap(); + grid = grid.height(height); + } + + if let Some(columns) = options.get("columns").expect("columns key") { + let columns = usize::from_str(columns).unwrap(); + grid = grid.columns(columns); + } + + if let Some(fluid) = options.get("fluid").expect("fluid key") { + let fluid = Pixels::from_str(fluid).unwrap(); + grid = grid.fluid(fluid); + } + + grid + } +} + impl<Handle> ApplyOptions for Image<Handle> { fn apply_options(self, options: BTreeMap<String, Option<String>>) -> Self { let mut image = self; diff --git a/src/types/element_name.rs b/src/types/element_name.rs index c824fc5..8f8ab7e 100644 --- a/src/types/element_name.rs +++ b/src/types/element_name.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize}; use super::rendered_element::{ - Action, RenderedElement, button, column, container, image, row, svg, text, + Action, RenderedElement, button, column, container, grid, image, row, svg, + text, }; use crate::Error; @@ -14,10 +15,11 @@ pub enum ElementName { Container, Row, Column, + Grid, } impl ElementName { - pub const ALL: &'static [Self; 7] = &[ + pub const ALL: &'static [Self; 8] = &[ Self::Text(String::new()), Self::Button(String::new()), Self::Svg(String::new()), @@ -25,6 +27,7 @@ impl ElementName { Self::Container, Self::Row, Self::Column, + Self::Grid, ]; pub fn handle_action( @@ -40,6 +43,7 @@ impl ElementName { Self::Container => container(None), Self::Row => row(vec![]), Self::Column => column(vec![]), + Self::Grid => grid(vec![]), }; match action { Action::Stop | Action::Drop => Ok(None), @@ -79,6 +83,7 @@ impl std::fmt::Display for ElementName { Self::Container => "Container", Self::Row => "Row", Self::Column => "Column", + Self::Grid => "Grid", } ) } diff --git a/src/types/rendered_element.rs b/src/types/rendered_element.rs index 1ad7cdf..15e851c 100755 --- a/src/types/rendered_element.rs +++ b/src/types/rendered_element.rs @@ -1,9 +1,8 @@ use std::collections::BTreeMap; -use iced::Element; use iced::advanced::widget::Id; use iced::widget::text::IntoFragment; -use iced::widget::{self}; +use iced::{Element, widget}; use serde::{Deserialize, Serialize}; use crate::Error; @@ -215,6 +214,12 @@ impl RenderedElement { imports = format!("{imports}column,"); view = format!("{view}\ncolumn![{elements}]{options}"); } + ElementName::Grid => { + imports = format!("{imports}grid,"); + view = format!( + "{view}\ngrid([{elements}].map(Into::into)){options}" + ); + } ElementName::Text(string) => { imports = format!("{imports}text,"); view = format!( @@ -389,6 +394,28 @@ impl<'a> From<RenderedElement> for Element<'a, Message> { ..Default::default() }) .into(), + ElementName::Grid => widget::container( + if !child_elements.is_empty() { + widget::grid(child_elements.into_iter().map(Into::into)) + } else { + widget::grid([text("New Column").into()]) + } + .apply_options(copy.options), + ) + .padding(20) + .style(|theme: &iced::Theme| widget::container::Style { + border: iced::Border { + color: theme.palette().warning.scale_alpha(0.6), + + width: 2.0, + radius: 4.into(), + }, + background: Some( + theme.palette().warning.scale_alpha(0.25).into(), + ), + ..Default::default() + }) + .into(), }; iced_drop::droppable(content) @@ -546,3 +573,8 @@ pub fn column(child_elements: Vec<RenderedElement>) -> RenderedElement { ], ) } + +pub fn grid(child_elements: Vec<RenderedElement>) -> RenderedElement { + RenderedElement::with(ElementName::Grid, child_elements) + .preset_options(&["spacing", "width", "height", "columns", "fluid"]) +} diff --git a/src/values.rs b/src/values.rs index d2dae74..62997ad 100644 --- a/src/values.rs +++ b/src/values.rs @@ -5,6 +5,7 @@ mod line_height; mod padding; mod pixels; mod rotation; +mod sizing; pub trait Value: Sized { type Err; diff --git a/src/values/sizing.rs b/src/values/sizing.rs new file mode 100644 index 0000000..e6a5086 --- /dev/null +++ b/src/values/sizing.rs @@ -0,0 +1,109 @@ +use std::num::ParseFloatError; +use std::str::FromStr; + +use iced::Length; +use iced::widget::grid::Sizing; + +use super::Value; +use super::length::ParseLengthError; + +#[derive(Debug, thiserror::Error, Clone, PartialEq)] +pub enum ParseSizingError { + #[error("float parsing error: {0}")] + ParseFloatError(ParseFloatError), + #[error("length parsing error: {0}")] + ParseLengthError(#[from] ParseLengthError), + #[error("invalid prefix")] + InvalidPrefix, + #[error("missing prefix")] + MissingPrefix, + #[error("cannot parse sizing from empty string")] + Empty, +} + +impl From<ParseFloatError> for ParseSizingError { + fn from(value: ParseFloatError) -> Self { + Self::ParseFloatError(value) + } +} + +impl Value for Sizing { + type Err = ParseSizingError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let s = s.trim(); + + if s.is_empty() { + return Err(ParseSizingError::Empty); + } + + if s.starts_with(|c: char| c.is_ascii_digit()) { + return Err(ParseSizingError::MissingPrefix); + } + + let (prefix, value) = s.split_at(2); + match prefix.to_lowercase().as_str() { + "ar" => Ok(Self::AspectRatio(f32::from_str(value)?)), + "ed" => Ok(Self::EvenlyDistribute(Length::from_str(value)?)), + _ => Err(ParseSizingError::InvalidPrefix), + } + } + + fn to_string(&self) -> String { + match self { + Self::AspectRatio(ratio) => format!("ar{ratio}"), + Self::EvenlyDistribute(length) => { + format!("ed{}", length.to_string()) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_parse_ratio() { + assert_eq!(Sizing::from_str("ar5.1"), Ok(Sizing::AspectRatio(5.1))) + } + + #[test] + fn can_parse_distribute_with_space() { + assert_eq!( + Sizing::from_str(" edfp2 "), + Ok(Sizing::EvenlyDistribute(Length::FillPortion(2))) + ) + } + + #[test] + fn cant_parse_invalid_prefix() { + assert_eq!( + Sizing::from_str("bc4.1 "), + Err(ParseSizingError::InvalidPrefix) + ) + } + + #[test] + fn cant_parse_invalid_float() { + assert_eq!( + Sizing::from_str(" ar2.a"), + Err(ParseSizingError::ParseFloatError( + f32::from_str("2.a").expect_err("float parse should fail") + )) + ) + } + + #[test] + fn cant_parse_with_missing_prefix() { + assert_eq!( + Sizing::from_str("2.4"), + Err(ParseSizingError::MissingPrefix) + ) + } + + #[test] + fn cant_parse_empty_string() { + assert_eq!(Sizing::from_str(" "), Err(ParseSizingError::Empty)) + } +} |
