Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ smol = ["iced_futures/smol"]
system = ["iced_winit/system"]
# Enables broken "sRGB linear" blending to reproduce color management of the Web
web-colors = ["iced_renderer/web-colors"]
# Enables pixel snapping for crisp edges by default (can cause jitter!)
crisp = ["iced_core/crisp", "iced_widget/crisp"]
# Enables the WebGL backend
webgl = ["iced_renderer/webgl"]
# Enables syntax highligthing
Expand Down
1 change: 1 addition & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ workspace = true
[features]
auto-detect-theme = ["dep:dark-light"]
advanced = []
crisp = []

[dependencies]
bitflags.workspace = true
Expand Down
4 changes: 4 additions & 0 deletions core/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ pub struct Quad {

/// The [`Shadow`] of the [`Quad`].
pub shadow: Shadow,

/// Whether the [`Quad`] should be snapped to the pixel grid.
pub snap: bool,
}

impl Default for Quad {
Expand All @@ -83,6 +86,7 @@ impl Default for Quad {
bounds: Rectangle::with_size(Size::ZERO),
border: Border::default(),
shadow: Shadow::default(),
snap: cfg!(feature = "crisp"),
}
}
}
Expand Down
182 changes: 97 additions & 85 deletions examples/custom_quad/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,89 +1,6 @@
//! This example showcases a drawing a quad.
mod quad {
use iced::advanced::layout::{self, Layout};
use iced::advanced::renderer;
use iced::advanced::widget::{self, Widget};
use iced::border;
use iced::mouse;
use iced::{Border, Color, Element, Length, Rectangle, Shadow, Size};

pub struct CustomQuad {
size: f32,
radius: border::Radius,
border_width: f32,
shadow: Shadow,
}

impl CustomQuad {
pub fn new(
size: f32,
radius: border::Radius,
border_width: f32,
shadow: Shadow,
) -> Self {
Self {
size,
radius,
border_width,
shadow,
}
}
}

impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer> for CustomQuad
where
Renderer: renderer::Renderer,
{
fn size(&self) -> Size<Length> {
Size {
width: Length::Shrink,
height: Length::Shrink,
}
}

fn layout(
&self,
_tree: &mut widget::Tree,
_renderer: &Renderer,
_limits: &layout::Limits,
) -> layout::Node {
layout::Node::new(Size::new(self.size, self.size))
}

fn draw(
&self,
_state: &widget::Tree,
renderer: &mut Renderer,
_theme: &Theme,
_style: &renderer::Style,
layout: Layout<'_>,
_cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
renderer.fill_quad(
renderer::Quad {
bounds: layout.bounds(),
border: Border {
radius: self.radius,
width: self.border_width,
color: Color::from_rgb(1.0, 0.0, 0.0),
},
shadow: self.shadow,
},
Color::BLACK,
);
}
}

impl<Message> From<CustomQuad> for Element<'_, Message> {
fn from(circle: CustomQuad) -> Self {
Self::new(circle)
}
}
}

use iced::border;
use iced::widget::{center, column, slider, text};
use iced::widget::{center, column, slider, text, toggler};
use iced::{Center, Color, Element, Shadow, Vector};

pub fn main() -> iced::Result {
Expand All @@ -94,6 +11,7 @@ struct Example {
radius: border::Radius,
border_width: f32,
shadow: Shadow,
snap: bool,
}

#[derive(Debug, Clone, Copy)]
Expand All @@ -107,6 +25,7 @@ enum Message {
ShadowXOffsetChanged(f32),
ShadowYOffsetChanged(f32),
ShadowBlurRadiusChanged(f32),
SnapToggled(bool),
}

impl Example {
Expand All @@ -119,6 +38,7 @@ impl Example {
offset: Vector::new(0.0, 8.0),
blur_radius: 16.0,
},
snap: false,
}
}

Expand Down Expand Up @@ -148,6 +68,9 @@ impl Example {
Message::ShadowBlurRadiusChanged(s) => {
self.shadow.blur_radius = s;
}
Message::SnapToggled(snap) => {
self.snap = snap;
}
}
}

Expand All @@ -170,7 +93,8 @@ impl Example {
200.0,
self.radius,
self.border_width,
self.shadow
self.shadow,
self.snap,
),
text!("Radius: {top_left:.2}/{top_right:.2}/{bottom_right:.2}/{bottom_left:.2}"),
slider(1.0..=100.0, top_left, Message::RadiusTopLeftChanged).step(0.01),
Expand All @@ -188,6 +112,7 @@ impl Example {
.step(0.01),
slider(0.0..=100.0, sr, Message::ShadowBlurRadiusChanged)
.step(0.01),
toggler(self.snap).label("Snap to pixel grid").on_toggle(Message::SnapToggled),
]
.padding(20)
.spacing(20)
Expand All @@ -203,3 +128,90 @@ impl Default for Example {
Self::new()
}
}

mod quad {
use iced::advanced::layout::{self, Layout};
use iced::advanced::renderer;
use iced::advanced::widget::{self, Widget};
use iced::border;
use iced::mouse;
use iced::{Border, Color, Element, Length, Rectangle, Shadow, Size};

pub struct CustomQuad {
size: f32,
radius: border::Radius,
border_width: f32,
shadow: Shadow,
snap: bool,
}

impl CustomQuad {
pub fn new(
size: f32,
radius: border::Radius,
border_width: f32,
shadow: Shadow,
snap: bool,
) -> Self {
Self {
size,
radius,
border_width,
shadow,
snap,
}
}
}

impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer> for CustomQuad
where
Renderer: renderer::Renderer,
{
fn size(&self) -> Size<Length> {
Size {
width: Length::Shrink,
height: Length::Shrink,
}
}

fn layout(
&self,
_tree: &mut widget::Tree,
_renderer: &Renderer,
_limits: &layout::Limits,
) -> layout::Node {
layout::Node::new(Size::new(self.size, self.size))
}

fn draw(
&self,
_state: &widget::Tree,
renderer: &mut Renderer,
_theme: &Theme,
_style: &renderer::Style,
layout: Layout<'_>,
_cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
renderer.fill_quad(
renderer::Quad {
bounds: layout.bounds(),
border: Border {
radius: self.radius,
width: self.border_width,
color: Color::from_rgb(1.0, 0.0, 0.0),
},
shadow: self.shadow,
snap: self.snap,
},
Color::BLACK,
);
}
}

impl<Message> From<CustomQuad> for Element<'_, Message> {
fn from(circle: CustomQuad) -> Self {
Self::new(circle)
}
}
}
1 change: 1 addition & 0 deletions wgpu/src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ impl Layer {
shadow_color: color::pack(quad.shadow.color),
shadow_offset: quad.shadow.offset.into(),
shadow_blur_radius: quad.shadow.blur_radius,
snap: quad.snap as u32,
};

self.quads.add(quad, &background);
Expand Down
3 changes: 3 additions & 0 deletions wgpu/src/quad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ pub struct Quad {

/// The shadow blur radius of the [`Quad`].
pub shadow_blur_radius: f32,

/// Whether the [`Quad`] should be snapped to the pixel grid.
pub snap: u32,
}

#[derive(Debug, Clone)]
Expand Down
4 changes: 3 additions & 1 deletion wgpu/src/quad/gradient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ impl Pipeline {
// Border radius
8 => Float32x4,
// Border width
9 => Float32
9 => Float32,
// Snap
10 => Uint32,
),
}],
compilation_options:
Expand Down
2 changes: 2 additions & 0 deletions wgpu/src/quad/solid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ impl Pipeline {
7 => Float32x2,
// Shadow blur radius
8 => Float32,
// Snap
9 => Uint32,
),
}],
compilation_options:
Expand Down
17 changes: 13 additions & 4 deletions wgpu/src/shader/quad/gradient.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ struct GradientVertexInput {
@location(7) border_color: vec4<f32>,
@location(8) border_radius: vec4<f32>,
@location(9) border_width: f32,
@location(10) snap: u32,
}

struct GradientVertexOutput {
Expand All @@ -33,6 +34,14 @@ fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
var pos: vec2<f32> = input.position_and_scale.xy * globals.scale;
var scale: vec2<f32> = input.position_and_scale.zw * globals.scale;

var pos_snap = vec2<f32>(0.0, 0.0);
var scale_snap = vec2<f32>(0.0, 0.0);

if bool(input.snap) {
pos_snap = round(pos + vec2(0.001, 0.001)) - pos;
scale_snap = round(pos + scale + vec2(0.001, 0.001)) - pos - pos_snap - scale;
}

var min_border_radius = min(input.position_and_scale.z, input.position_and_scale.w) * 0.5;
var border_radius: vec4<f32> = vec4<f32>(
min(input.border_radius.x, min_border_radius),
Expand All @@ -42,10 +51,10 @@ fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
);

var transform: mat4x4<f32> = mat4x4<f32>(
vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
vec4<f32>(scale.x + scale_snap.x + 1.0, 0.0, 0.0, 0.0),
vec4<f32>(0.0, scale.y + scale_snap.y + 1.0, 0.0, 0.0),
vec4<f32>(0.0, 0.0, 1.0, 0.0),
vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0)
vec4<f32>(pos + pos_snap - vec2<f32>(0.5, 0.5), 0.0, 1.0)
);

out.position = globals.transform * transform * vec4<f32>(vertex_position(input.vertex_index), 0.0, 1.0);
Expand All @@ -55,7 +64,7 @@ fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
out.colors_4 = input.colors_4;
out.offsets = input.offsets;
out.direction = input.direction * globals.scale;
out.position_and_scale = vec4<f32>(pos, scale);
out.position_and_scale = vec4<f32>(pos + pos_snap, scale + scale_snap);
out.border_color = premultiply(input.border_color);
out.border_radius = border_radius * globals.scale;
out.border_width = input.border_width * globals.scale;
Expand Down
Loading