diff options
Diffstat (limited to 'native')
-rw-r--r-- | native/CHANGELOG.md | 1 | ||||
-rw-r--r-- | native/src/widget.rs | 3 | ||||
-rw-r--r-- | native/src/widget/button.rs | 25 | ||||
-rw-r--r-- | native/src/widget/svg.rs | 187 |
4 files changed, 214 insertions, 2 deletions
diff --git a/native/CHANGELOG.md b/native/CHANGELOG.md index cdf02c4b..df8852b7 100644 --- a/native/CHANGELOG.md +++ b/native/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Ctrl+Left` and `Ctrl+Right` cursor word jump for `TextInput`. [#108] - `keyboard::ModifiersState` struct which contains the state of the keyboard modifiers. [#108] - `TextInput::password` method to enable secure password input mode. [#113] +- `Button::height` and `Button::min_height` methods to control the height of a button. ### Changed - `Image::new` takes an `Into<image::Handle>` now instead of an `Into<String>`. [#90] diff --git a/native/src/widget.rs b/native/src/widget.rs index 71dcdc0d..ee7232cb 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -24,6 +24,7 @@ pub mod button; pub mod checkbox; pub mod column; pub mod container; +pub mod svg; pub mod image; pub mod radio; pub mod row; @@ -51,6 +52,8 @@ pub use scrollable::Scrollable; #[doc(no_inline)] pub use slider::Slider; #[doc(no_inline)] +pub use svg::Svg; +#[doc(no_inline)] pub use text::Text; #[doc(no_inline)] pub use text_input::TextInput; diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 3348c58c..67b49dc6 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -33,7 +33,9 @@ pub struct Button<'a, Message, Renderer> { content: Element<'a, Message, Renderer>, on_press: Option<Message>, width: Length, + height: Length, min_width: u32, + min_height: u32, padding: u16, background: Option<Background>, border_radius: u16, @@ -54,7 +56,9 @@ impl<'a, Message, Renderer> Button<'a, Message, Renderer> { content: content.into(), on_press: None, width: Length::Shrink, + height: Length::Shrink, min_width: 0, + min_height: 0, padding: 0, background: None, border_radius: 0, @@ -69,6 +73,14 @@ impl<'a, Message, Renderer> Button<'a, Message, Renderer> { self } + /// Sets the height of the [`Button`]. + /// + /// [`Button`]: struct.Button.html + pub fn height(mut self, height: Length) -> Self { + self.height = height; + self + } + /// Sets the minimum width of the [`Button`]. /// /// [`Button`]: struct.Button.html @@ -77,6 +89,14 @@ impl<'a, Message, Renderer> Button<'a, Message, Renderer> { self } + /// Sets the minimum height of the [`Button`]. + /// + /// [`Button`]: struct.Button.html + pub fn min_height(mut self, min_height: u32) -> Self { + self.min_height = min_height; + self + } + /// Sets the padding of the [`Button`]. /// /// [`Button`]: struct.Button.html @@ -139,7 +159,7 @@ where } fn height(&self) -> Length { - Length::Shrink + self.height } fn layout( @@ -150,8 +170,9 @@ where let padding = f32::from(self.padding); let limits = limits .min_width(self.min_width) + .min_height(self.min_height) .width(self.width) - .height(Length::Shrink) + .height(self.height) .pad(padding); let mut content = self.content.layout(renderer, &limits); diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs new file mode 100644 index 00000000..9580f195 --- /dev/null +++ b/native/src/widget/svg.rs @@ -0,0 +1,187 @@ +//! Display vector graphics in your application. +use crate::{layout, Element, Hasher, Layout, Length, Point, Size, Widget}; + +use std::{ + hash::Hash, + path::{Path, PathBuf}, +}; + +/// A vector graphics image. +/// +/// An [`Svg`] image resizes smoothly without losing any quality. +/// +/// [`Svg`] images can have a considerable rendering cost when resized, +/// specially when they are complex. +/// +/// [`Svg`]: struct.Svg.html +#[derive(Debug, Clone)] +pub struct Svg { + handle: Handle, + width: Length, + height: Length, +} + +impl Svg { + /// Creates a new [`Svg`] from the given [`Handle`]. + /// + /// [`Svg`]: struct.Svg.html + /// [`Handle`]: struct.Handle.html + pub fn new(handle: impl Into<Handle>) -> Self { + Svg { + handle: handle.into(), + width: Length::Fill, + height: Length::Fill, + } + } + + /// Sets the width of the [`Svg`]. + /// + /// [`Svg`]: struct.Svg.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the height of the [`Svg`]. + /// + /// [`Svg`]: struct.Svg.html + pub fn height(mut self, height: Length) -> Self { + self.height = height; + self + } +} + +impl<Message, Renderer> Widget<Message, Renderer> for Svg +where + Renderer: self::Renderer, +{ + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + self.height + } + + fn layout( + &self, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let (width, height) = renderer.dimensions(&self.handle); + + let aspect_ratio = width as f32 / height as f32; + + let mut size = limits + .width(self.width) + .height(self.height) + .resolve(Size::new(width as f32, height as f32)); + + let viewport_aspect_ratio = size.width / size.height; + + if viewport_aspect_ratio > aspect_ratio { + size.width = width as f32 * size.height / height as f32; + } else { + size.height = height as f32 * size.width / width as f32; + } + + layout::Node::new(size) + } + + fn draw( + &self, + renderer: &mut Renderer, + layout: Layout<'_>, + _cursor_position: Point, + ) -> Renderer::Output { + renderer.draw(self.handle.clone(), layout) + } + + fn hash_layout(&self, state: &mut Hasher) { + self.width.hash(state); + self.height.hash(state); + } +} + +/// An [`Svg`] handle. +/// +/// [`Svg`]: struct.Svg.html +#[derive(Debug, Clone)] +pub struct Handle { + id: u64, + path: PathBuf, +} + +impl Handle { + /// Creates an SVG [`Handle`] pointing to the vector image of the given + /// path. + /// + /// [`Handle`]: struct.Handle.html + pub fn from_path<T: Into<PathBuf>>(path: T) -> Handle { + use std::hash::Hasher as _; + + let path = path.into(); + + let mut hasher = Hasher::default(); + path.hash(&mut hasher); + + Handle { + id: hasher.finish(), + path, + } + } + + /// Returns the unique identifier of the [`Handle`]. + /// + /// [`Handle`]: struct.Handle.html + pub fn id(&self) -> u64 { + self.id + } + + /// Returns a reference to the path of the [`Handle`]. + /// + /// [`Handle`]: enum.Handle.html + pub fn path(&self) -> &Path { + &self.path + } +} + +impl From<String> for Handle { + fn from(path: String) -> Handle { + Handle::from_path(path) + } +} + +impl From<&str> for Handle { + fn from(path: &str) -> Handle { + Handle::from_path(path) + } +} + +/// The renderer of an [`Svg`]. +/// +/// Your [renderer] will need to implement this trait before being able to use +/// an [`Svg`] in your user interface. +/// +/// [`Svg`]: struct.Svg.html +/// [renderer]: ../../renderer/index.html +pub trait Renderer: crate::Renderer { + /// Returns the default dimensions of an [`Svg`] located on the given path. + /// + /// [`Svg`]: struct.Svg.html + fn dimensions(&self, handle: &Handle) -> (u32, u32); + + /// Draws an [`Svg`]. + /// + /// [`Svg`]: struct.Svg.html + fn draw(&mut self, handle: Handle, layout: Layout<'_>) -> Self::Output; +} + +impl<'a, Message, Renderer> From<Svg> for Element<'a, Message, Renderer> +where + Renderer: self::Renderer, +{ + fn from(icon: Svg) -> Element<'a, Message, Renderer> { + Element::new(icon) + } +} |