Skip to content

Add per-side border widths and colors - #3391

Open
Cretezy wants to merge 4 commits into
iced-rs:masterfrom
Cretezy:agent/per-side-border-widths-and-colors
Open

Add per-side border widths and colors#3391
Cretezy wants to merge 4 commits into
iced-rs:masterfrom
Cretezy:agent/per-side-border-widths-and-colors

Conversation

@Cretezy

@Cretezy Cretezy commented Jul 17, 2026

Copy link
Copy Markdown

Summary

Implements per-side border widths and colors for #703.

  • Adds border::Side plus Border::{top,right,bottom,left} overrides while retaining the existing uniform color and width API as fallbacks.
  • Resolves edge values in top/right/bottom/left order, clamps negative widths, permits disabled (0) edges, and scales only opposing pairs that would exceed the quad bounds.
  • Extends the wgpu quad payload and both solid and gradient shaders for four colors and widths.
  • Adds tiny-skia asymmetric ring rendering with rounded outer/inner contours and diagonal corner joins, while preserving its uniform-border fast path.
  • Updates internal Border literals, rustdocs, the custom-quad example, and core/software/wgpu smoke coverage.

Motivation

Today a Border can vary its corner radii but all four edges must share one color and width. This makes common UI patterns—such as dividers on collapsible headers, active-edge indicators, and asymmetric outlines—require custom drawing despite all widgets already flowing through renderer::Quad.

The change exposes that capability once in the shared border API, so every quad-backed widget can use it without introducing widget-specific styling APIs or changing layout/padding behavior.

Design and trade-offs

Side uses optional overrides. That keeps border::color(...) and border::width(...) source-compatible as uniform defaults, makes builder order irrelevant, and allows replacing a side with Side::default() to clear overrides. Side widths remain solid-only; dashed/dotted styles and gradient border colors are intentionally out of scope.

For rendering, borders stay inside the quad. Opposing widths are proportionally constrained only when their sum exceeds the corresponding dimension. The GPU path computes an outer rounded contour with an asymmetrically inset inner contour and chooses corner colors along CSS-style diagonal joins. The tiny-skia path keeps the existing efficient stroke route for uniform borders; non-uniform borders use a clipped ring and elliptical inner arcs where adjacent widths differ. Shadows still follow the outer radius.

I found this approach to be the least breaking, only requiring adding ..Border::default() to previous Border { } definitions.

Validation

  • cargo fmt --all -- --check
  • cargo test -p iced_core -p iced_tiny_skia -p iced_wgpu --lib
  • cargo test -p iced_core --doc
  • cargo check --workspace
  • git diff --check

cargo clippy -p iced_core -p iced_tiny_skia -p iced_wgpu --lib -- -D warnings was also run; it stops on an existing unrelated image::Batch::default() lint in wgpu/src/layer.rs:410.

@Cretezy
Cretezy marked this pull request as ready for review July 17, 2026 15:17
@Cretezy
Cretezy force-pushed the agent/per-side-border-widths-and-colors branch from a084bba to f67b665 Compare July 17, 2026 20:05
@Cretezy
Cretezy force-pushed the agent/per-side-border-widths-and-colors branch from f0c72c1 to 393382b Compare July 17, 2026 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant