Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#574): reimplementation of Image widget layout function #605

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ extend-ignore-re = [
# is treated as always incorrect.

[default.extend-identifiers]
FillStrat = "FillStrat" # short for strategy
wdth = "wdth" # Variable font parameter

# Case insensitive
Expand Down
4 changes: 2 additions & 2 deletions masonry/examples/custom_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use accesskit::Role;
use masonry::app_driver::{AppDriver, DriverCtx};
use masonry::kurbo::{BezPath, Stroke};
use masonry::widget::{FillStrat, RootWidget};
use masonry::widget::{ObjectFit, RootWidget};
use masonry::{
AccessCtx, AccessEvent, Action, Affine, BoxConstraints, Color, EventCtx, LayoutCtx, LifeCycle,
LifeCycleCtx, PaintCtx, Point, PointerEvent, Rect, Size, StatusChange, TextEvent, Widget,
Expand Down Expand Up @@ -124,7 +124,7 @@ impl Widget for CustomWidget {
// Let's burn some CPU to make a (partially transparent) image buffer
let image_data = make_image_data(256, 256);
let image_data = Image::new(image_data.into(), Format::Rgba8, 256, 256);
let transform = FillStrat::Fill.affine_to_fill(ctx.size(), size);
let transform = ObjectFit::Fill.affine_to_fill(ctx.size(), size);
scene.draw_image(&image_data, transform);
}

Expand Down
4 changes: 2 additions & 2 deletions masonry/examples/simple_image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use masonry::app_driver::{AppDriver, DriverCtx};
use masonry::dpi::LogicalSize;
use masonry::widget::{FillStrat, Image, RootWidget};
use masonry::widget::{Image, ObjectFit, RootWidget};
use masonry::{Action, WidgetId};
use vello::peniko::{Format, Image as ImageBuf};
use winit::window::Window;
Expand All @@ -26,7 +26,7 @@ pub fn main() {
let image_data = image::load_from_memory(image_bytes).unwrap().to_rgba8();
let (width, height) = image_data.dimensions();
let png_data = ImageBuf::new(image_data.to_vec().into(), Format::Rgba8, width, height);
let image = Image::new(png_data).fill_mode(FillStrat::Contain);
let image = Image::new(png_data).fill_mode(ObjectFit::Contain);

let window_size = LogicalSize::new(650.0, 450.0);
let window_attributes = Window::default_attributes()
Expand Down
38 changes: 19 additions & 19 deletions masonry/src/widget/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use vello::peniko::{BlendMode, Image as ImageBuf};
use vello::Scene;

use crate::widget::{FillStrat, WidgetMut};
use crate::widget::{ObjectFit, WidgetMut};
use crate::{
AccessCtx, AccessEvent, BoxConstraints, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx,
PointerEvent, Size, StatusChange, TextEvent, Widget, WidgetId,
Expand All @@ -28,25 +28,25 @@
/// than the image size).
pub struct Image {
image_data: ImageBuf,
fill: FillStrat,
fill: ObjectFit,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we rename this field? I'd suggest either fit or object_fit?

}

// --- MARK: BUILDERS ---
impl Image {
/// Create an image drawing widget from an image buffer.
///
/// By default, the Image will scale to fit its box constraints ([`FillStrat::Fill`]).

Check warning on line 38 in masonry/src/widget/image.rs

View workflow job for this annotation

GitHub Actions / typos

"Strat" should be "Start" or "Strata".
#[inline]
pub fn new(image_data: ImageBuf) -> Self {
Image {
image_data,
fill: FillStrat::default(),
fill: ObjectFit::default(),
}
}

/// Builder-style method for specifying the fill strategy.
#[inline]
pub fn fill_mode(mut self, mode: FillStrat) -> Self {
pub fn fill_mode(mut self, mode: ObjectFit) -> Self {
self.fill = mode;
self
}
Expand All @@ -56,7 +56,7 @@
impl<'a> WidgetMut<'a, Image> {
/// Modify the widget's fill strategy.
#[inline]
pub fn set_fill_mode(&mut self, newfil: FillStrat) {
pub fn set_fill_mode(&mut self, newfil: ObjectFit) {
self.widget.fill = newfil;
self.ctx.request_paint();
}
Expand Down Expand Up @@ -93,15 +93,15 @@
}
let image_aspect_ratio = image_size.height / image_size.width;
let size = match self.fill {
FillStrat::Contain => bc.constrain_aspect_ratio(image_aspect_ratio, image_size.width),
FillStrat::Cover => Size::new(bc.max().width, bc.max().width * image_aspect_ratio),
FillStrat::Fill => bc.max(),
FillStrat::FitHeight => {
ObjectFit::Contain => bc.constrain_aspect_ratio(image_aspect_ratio, image_size.width),
ObjectFit::Cover => Size::new(bc.max().width, bc.max().width * image_aspect_ratio),
ObjectFit::Fill => bc.max(),
ObjectFit::FitHeight => {
Size::new(bc.max().height / image_aspect_ratio, bc.max().height)
}
FillStrat::FitWidth => Size::new(bc.max().width, bc.max().width * image_aspect_ratio),
FillStrat::None => image_size,
FillStrat::ScaleDown => {
ObjectFit::FitWidth => Size::new(bc.max().width, bc.max().width * image_aspect_ratio),
ObjectFit::None => image_size,
ObjectFit::ScaleDown => {
let mut size = image_size;

if !bc.contains(size) {
Expand Down Expand Up @@ -221,37 +221,37 @@
let harness_size = Size::new(100.0, 50.0);

// Contain.
let image_widget = Image::new(image_data.clone()).fill_mode(FillStrat::Contain);
let image_widget = Image::new(image_data.clone()).fill_mode(ObjectFit::Contain);
let mut harness = TestHarness::create_with_size(image_widget, harness_size);
assert_render_snapshot!(harness, "layout_contain");

// Cover.
let image_widget = Image::new(image_data.clone()).fill_mode(FillStrat::Cover);
let image_widget = Image::new(image_data.clone()).fill_mode(ObjectFit::Cover);
let mut harness = TestHarness::create_with_size(image_widget, harness_size);
assert_render_snapshot!(harness, "layout_cover");

// Fill.
let image_widget = Image::new(image_data.clone()).fill_mode(FillStrat::Fill);
let image_widget = Image::new(image_data.clone()).fill_mode(ObjectFit::Fill);
let mut harness = TestHarness::create_with_size(image_widget, harness_size);
assert_render_snapshot!(harness, "layout_fill");

// FitHeight.
let image_widget = Image::new(image_data.clone()).fill_mode(FillStrat::FitHeight);
let image_widget = Image::new(image_data.clone()).fill_mode(ObjectFit::FitHeight);
let mut harness = TestHarness::create_with_size(image_widget, harness_size);
assert_render_snapshot!(harness, "layout_fitheight");

// FitWidth.
let image_widget = Image::new(image_data.clone()).fill_mode(FillStrat::FitWidth);
let image_widget = Image::new(image_data.clone()).fill_mode(ObjectFit::FitWidth);
let mut harness = TestHarness::create_with_size(image_widget, harness_size);
assert_render_snapshot!(harness, "layout_fitwidth");

// None.
let image_widget = Image::new(image_data.clone()).fill_mode(FillStrat::None);
let image_widget = Image::new(image_data.clone()).fill_mode(ObjectFit::None);
let mut harness = TestHarness::create_with_size(image_widget, harness_size);
assert_render_snapshot!(harness, "layout_none");

// ScaleDown.
let image_widget = Image::new(image_data.clone()).fill_mode(FillStrat::ScaleDown);
let image_widget = Image::new(image_data.clone()).fill_mode(ObjectFit::ScaleDown);
let mut harness = TestHarness::create_with_size(image_widget, harness_size);
assert_render_snapshot!(harness, "layout_scaledown");
}
Expand Down
28 changes: 9 additions & 19 deletions masonry/src/widget/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
// These are based on https://api.flutter.dev/flutter/painting/BoxFit-class.html
/// Strategies for inscribing a rectangle inside another rectangle.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#[derive(Clone, Copy, Default, PartialEq)]
pub enum FillStrat {
pub enum ObjectFit {
/// As large as possible without changing aspect ratio of image and all of image shown
#[default]
Contain,
Expand All @@ -81,8 +81,8 @@

// TODO - Need to write tests for this, in a way that's relatively easy to visualize.

impl FillStrat {
impl ObjectFit {
/// Calculate an origin and scale for an image with a given `FillStrat`.

Check warning on line 85 in masonry/src/widget/mod.rs

View workflow job for this annotation

GitHub Actions / typos

"Strat" should be "Start" or "Strata".
///
/// This takes some properties of a widget and a fill strategy and returns an affine matrix
/// used to position and scale the image in the widget.
Expand All @@ -91,22 +91,22 @@
let raw_scaley = parent.height / fit_box.height;

let (scalex, scaley) = match self {
FillStrat::Contain => {
ObjectFit::Contain => {
let scale = raw_scalex.min(raw_scaley);
(scale, scale)
}
FillStrat::Cover => {
ObjectFit::Cover => {
let scale = raw_scalex.max(raw_scaley);
(scale, scale)
}
FillStrat::Fill => (raw_scalex, raw_scaley),
FillStrat::FitHeight => (raw_scaley, raw_scaley),
FillStrat::FitWidth => (raw_scalex, raw_scalex),
FillStrat::ScaleDown => {
ObjectFit::Fill => (raw_scalex, raw_scaley),
ObjectFit::FitHeight => (raw_scaley, raw_scaley),
ObjectFit::FitWidth => (raw_scalex, raw_scalex),
ObjectFit::ScaleDown => {
let scale = raw_scalex.min(raw_scaley).min(1.0);
(scale, scale)
}
FillStrat::None => (1.0, 1.0),
ObjectFit::None => (1.0, 1.0),
};

let origin_x = (parent.width - (fit_box.width * scalex)) / 2.0;
Expand All @@ -115,13 +115,3 @@
Affine::new([scalex, 0., 0., scaley, origin_x, origin_y])
}
}

// TODO - remove prelude
#[allow(missing_docs)]
pub mod prelude {
#[doc(hidden)]
pub use crate::{
BoxConstraints, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx, PointerEvent, Size,
StatusChange, TextEvent, Widget, WidgetId,
};
}
8 changes: 4 additions & 4 deletions xilem/src/view/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

//! The bitmap image widget.

use masonry::widget::{self, FillStrat};
use masonry::widget::{self, ObjectFit};
use xilem_core::{Mut, ViewMarker};

use crate::{MessageResult, Pod, View, ViewCtx, ViewId};

/// Displays the bitmap `image`.
///
/// By default, the Image will scale to fit its box constraints ([`FillStrat::Fill`]).

Check warning on line 13 in xilem/src/view/image.rs

View workflow job for this annotation

GitHub Actions / typos

"Strat" should be "Start" or "Strata".
/// To configure this, call [`fill`](Image::fill) on the returned value.
///
/// Corresponds to the [`Image`](widget::Image) widget.
Expand All @@ -24,7 +24,7 @@
// We take by reference as we expect all users of this API will need to clone, and it's
// easier than documenting that cloning is cheap.
image: image.clone(),
fill: FillStrat::default(),
fill: ObjectFit::default(),
}
}

Expand All @@ -33,12 +33,12 @@
/// See `image`'s docs for more details.
pub struct Image {
image: vello::peniko::Image,
fill: FillStrat,
fill: ObjectFit,
}

impl Image {
/// Specify the fill strategy.
pub fn fill(mut self, fill: FillStrat) -> Self {
pub fn fill(mut self, fill: ObjectFit) -> Self {
self.fill = fill;
self
}
Expand Down
Loading