diff options
author | 2022-02-23 16:56:01 +0700 | |
---|---|---|
committer | 2022-02-23 16:56:01 +0700 | |
commit | 9fe50801537e8fbfd2914f5139b5a609929e25db (patch) | |
tree | b649ccb07f01214e8d9ea4e4b413290e10ee724a /native/src | |
parent | e0185b8cda82a0092386a2b5a7e2ca0a3a2f0a25 (diff) | |
parent | 33b9b50883dfcc9838f7792d884324aaffef0a41 (diff) | |
download | iced-9fe50801537e8fbfd2914f5139b5a609929e25db.tar.gz iced-9fe50801537e8fbfd2914f5139b5a609929e25db.tar.bz2 iced-9fe50801537e8fbfd2914f5139b5a609929e25db.zip |
Merge pull request #1219 from Alch-Emi/image-modes
ContentFit support for images
Diffstat (limited to 'native/src')
-rw-r--r-- | native/src/lib.rs | 4 | ||||
-rw-r--r-- | native/src/widget/image.rs | 83 | ||||
-rw-r--r-- | native/src/widget/svg.rs | 83 |
3 files changed, 136 insertions, 34 deletions
diff --git a/native/src/lib.rs b/native/src/lib.rs index 6d98f7d1..5c9c24c9 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -71,8 +71,8 @@ mod debug; pub use iced_core::alignment; pub use iced_core::time; pub use iced_core::{ - Alignment, Background, Color, Font, Length, Padding, Point, Rectangle, - Size, Vector, + Alignment, Background, Color, ContentFit, Font, Length, Padding, Point, + Rectangle, Size, Vector, }; pub use iced_futures::{executor, futures}; diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index b8fb662e..83c24ee5 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -5,7 +5,10 @@ pub use viewer::Viewer; use crate::image; use crate::layout; use crate::renderer; -use crate::{Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget}; +use crate::{ + ContentFit, Element, Hasher, Layout, Length, Point, Rectangle, Size, + Vector, Widget, +}; use std::hash::Hash; @@ -26,6 +29,7 @@ pub struct Image<Handle> { handle: Handle, width: Length, height: Length, + content_fit: ContentFit, } impl<Handle> Image<Handle> { @@ -35,6 +39,7 @@ impl<Handle> Image<Handle> { handle: handle.into(), width: Length::Shrink, height: Length::Shrink, + content_fit: ContentFit::Contain, } } @@ -49,6 +54,16 @@ impl<Handle> Image<Handle> { self.height = height; self } + + /// Sets the [`ContentFit`] of the [`Image`]. + /// + /// Defaults to [`ContentFit::Contain`] + pub fn content_fit(self, content_fit: ContentFit) -> Self { + Self { + content_fit, + ..self + } + } } impl<Message, Renderer, Handle> Widget<Message, Renderer> for Image<Handle> @@ -69,24 +84,32 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { + // The raw w/h of the underlying image let (width, height) = renderer.dimensions(&self.handle); + let image_size = Size::new(width as f32, height as f32); - let aspect_ratio = width as f32 / height as f32; - - let mut size = limits + // The size to be available to the widget prior to `Shrink`ing + let raw_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) + .resolve(image_size); + + // The uncropped size of the image when fit to the bounds above + let full_size = self.content_fit.fit(image_size, raw_size); + + // Shrink the widget to fit the resized image, if requested + let final_size = Size { + width: match self.width { + Length::Shrink => f32::min(raw_size.width, full_size.width), + _ => raw_size.width, + }, + height: match self.height { + Length::Shrink => f32::min(raw_size.height, full_size.height), + _ => raw_size.height, + }, + }; + + layout::Node::new(final_size) } fn draw( @@ -97,7 +120,34 @@ where _cursor_position: Point, _viewport: &Rectangle, ) { - renderer.draw(self.handle.clone(), layout.bounds()); + let (width, height) = renderer.dimensions(&self.handle); + let image_size = Size::new(width as f32, height as f32); + + let bounds = layout.bounds(); + let adjusted_fit = self.content_fit.fit(image_size, bounds.size()); + + let render = |renderer: &mut Renderer| { + let offset = Vector::new( + (bounds.width - adjusted_fit.width).max(0.0) / 2.0, + (bounds.height - adjusted_fit.height).max(0.0) / 2.0, + ); + + let drawing_bounds = Rectangle { + width: adjusted_fit.width, + height: adjusted_fit.height, + ..bounds + }; + + renderer.draw(self.handle.clone(), drawing_bounds + offset) + }; + + if adjusted_fit.width > bounds.width + || adjusted_fit.height > bounds.height + { + renderer.with_layer(bounds, render); + } else { + render(renderer) + } } fn hash_layout(&self, state: &mut Hasher) { @@ -107,6 +157,7 @@ where self.handle.hash(state); self.width.hash(state); self.height.hash(state); + self.content_fit.hash(state); } } diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs index f212dfcb..22aac331 100644 --- a/native/src/widget/svg.rs +++ b/native/src/widget/svg.rs @@ -2,7 +2,10 @@ use crate::layout; use crate::renderer; use crate::svg::{self, Handle}; -use crate::{Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget}; +use crate::{ + ContentFit, Element, Hasher, Layout, Length, Point, Rectangle, Size, + Vector, Widget, +}; use std::hash::Hash; use std::path::PathBuf; @@ -18,6 +21,7 @@ pub struct Svg { handle: Handle, width: Length, height: Length, + content_fit: ContentFit, } impl Svg { @@ -27,6 +31,7 @@ impl Svg { handle: handle.into(), width: Length::Fill, height: Length::Shrink, + content_fit: ContentFit::Contain, } } @@ -47,6 +52,16 @@ impl Svg { self.height = height; self } + + /// Sets the [`ContentFit`] of the [`Svg`]. + /// + /// Defaults to [`ContentFit::Contain`] + pub fn content_fit(self, content_fit: ContentFit) -> Self { + Self { + content_fit, + ..self + } + } } impl<Message, Renderer> Widget<Message, Renderer> for Svg @@ -66,24 +81,32 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { + // The raw w/h of the underlying image let (width, height) = renderer.dimensions(&self.handle); + let image_size = Size::new(width as f32, height as f32); - let aspect_ratio = width as f32 / height as f32; - - let mut size = limits + // The size to be available to the widget prior to `Shrink`ing + let raw_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) + .resolve(image_size); + + // The uncropped size of the image when fit to the bounds above + let full_size = self.content_fit.fit(image_size, raw_size); + + // Shrink the widget to fit the resized image, if requested + let final_size = Size { + width: match self.width { + Length::Shrink => f32::min(raw_size.width, full_size.width), + _ => raw_size.width, + }, + height: match self.height { + Length::Shrink => f32::min(raw_size.height, full_size.height), + _ => raw_size.height, + }, + }; + + layout::Node::new(final_size) } fn draw( @@ -94,7 +117,34 @@ where _cursor_position: Point, _viewport: &Rectangle, ) { - renderer.draw(self.handle.clone(), layout.bounds()) + let (width, height) = renderer.dimensions(&self.handle); + let image_size = Size::new(width as f32, height as f32); + + let bounds = layout.bounds(); + let adjusted_fit = self.content_fit.fit(image_size, bounds.size()); + + let render = |renderer: &mut Renderer| { + let offset = Vector::new( + (bounds.width - adjusted_fit.width).max(0.0) / 2.0, + (bounds.height - adjusted_fit.height).max(0.0) / 2.0, + ); + + let drawing_bounds = Rectangle { + width: adjusted_fit.width, + height: adjusted_fit.height, + ..bounds + }; + + renderer.draw(self.handle.clone(), drawing_bounds + offset) + }; + + if adjusted_fit.width > bounds.width + || adjusted_fit.height > bounds.height + { + renderer.with_layer(bounds, render); + } else { + render(renderer) + } } fn hash_layout(&self, state: &mut Hasher) { @@ -103,6 +153,7 @@ where self.handle.hash(state); self.width.hash(state); self.height.hash(state); + self.content_fit.hash(state); } } |