Skip to content
Open
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
126 changes: 126 additions & 0 deletions core/src/border.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,60 @@ pub struct Border {

/// The [`Radius`] of the border.
pub radius: Radius,

/// Overrides for the top edge of the border.
pub top: Side,

/// Overrides for the right edge of the border.
pub right: Side,

/// Overrides for the bottom edge of the border.
pub bottom: Side,

/// Overrides for the left edge of the border.
pub left: Side,
}

/// Optional overrides for one side of a [`Border`].
///
/// A [`Side`] inherits the border's [`Border::color`] and [`Border::width`]
/// when its corresponding value is `None`.
///
/// This is useful for patterns such as a collapsible header, where the open
/// state can keep only a bottom divider:
///
/// ```
/// # use iced_core::{border, Color};
/// let header = border::color(Color::BLACK)
/// .width(1)
/// .bottom(border::Side::default().color(Color::from_rgb(0.2, 0.4, 1.0)));
/// let collapsed = header.bottom(border::Side::default().width(0));
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Side {
/// An optional color override for this side.
pub color: Option<Color>,

/// An optional width override for this side.
pub width: Option<f32>,
}

impl Side {
/// Sets the color override for this side.
pub fn color(self, color: impl Into<Color>) -> Self {
Self {
color: Some(color.into()),
..self
}
}

/// Sets the width override for this side.
pub fn width(self, width: impl Into<Pixels>) -> Self {
Self {
width: Some(width.into().0),
..self
}
}
}

/// Creates a new [`Border`] with the given [`Radius`].
Expand Down Expand Up @@ -73,6 +127,26 @@ impl Border {
..self
}
}

/// Sets the overrides for the top edge of the [`Border`].
pub fn top(self, top: Side) -> Self {
Self { top, ..self }
}

/// Sets the overrides for the right edge of the [`Border`].
pub fn right(self, right: Side) -> Self {
Self { right, ..self }
}

/// Sets the overrides for the bottom edge of the [`Border`].
pub fn bottom(self, bottom: Side) -> Self {
Self { bottom, ..self }
}

/// Sets the overrides for the left edge of the [`Border`].
pub fn left(self, left: Side) -> Self {
Self { left, ..self }
}
}

/// The border radii for the corners of a graphics primitive in the order:
Expand Down Expand Up @@ -276,3 +350,55 @@ impl std::ops::Mul<f32> for Radius {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn sides_fall_back_to_the_uniform_values() {
let border = Border::default().color(Color::BLACK).width(2);

assert_eq!(border.top.color.unwrap_or(border.color), Color::BLACK);
assert_eq!(border.right.width.unwrap_or(border.width), 2.0);
}

#[test]
fn sides_override_color_and_width_independently() {
let border = Border::default()
.color(Color::BLACK)
.width(2)
.left(Side::default().color(Color::WHITE))
.right(Side::default().width(4));

assert_eq!(border.left.color, Some(Color::WHITE));
assert_eq!(border.left.width, None);
assert_eq!(border.right.color, None);
assert_eq!(border.right.width, Some(4.0));
}

#[test]
fn uniform_builders_do_not_replace_side_overrides() {
let side = Side::default().color(Color::WHITE).width(4);
let before = Border::default().left(side).color(Color::BLACK).width(2);
let after = Border::default().color(Color::BLACK).width(2).left(side);

assert_eq!(before, after);
}

#[test]
fn replacing_a_side_with_default_clears_its_overrides() {
let border = Border::default()
.top(Side::default().color(Color::WHITE).width(3))
.top(Side::default());

assert_eq!(border.top, Side::default());
}

#[test]
fn zero_width_is_a_valid_side_override() {
let border = Border::default().width(2).bottom(Side::default().width(0));

assert_eq!(border.bottom.width.unwrap_or(border.width), 0.0);
}
}
28 changes: 22 additions & 6 deletions examples/custom_quad/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ mod quad {
use iced::advanced::widget::{self, Widget};
use iced::border;
use iced::mouse;
use iced::{Border, Color, Element, Length, Rectangle, Shadow, Size};
use iced::{Color, Element, Length, Rectangle, Shadow, Size};

pub struct CustomQuad {
size: f32,
Expand Down Expand Up @@ -192,11 +192,27 @@ mod quad {
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),
},
// Side overrides demonstrate a collapsible-header-like
// divider: zeroing a side width cleanly removes it.
border: border::color(Color::from_rgb(1.0, 0.0, 0.0))
.rounded(self.radius)
.width(self.border_width)
.top(border::Side::default().color(Color::from_rgb(1.0, 0.2, 0.2)))
.right(
border::Side::default()
.color(Color::from_rgb(0.2, 1.0, 0.2))
.width(self.border_width * 0.5),
)
.bottom(
border::Side::default()
.color(Color::from_rgb(0.2, 0.4, 1.0))
.width(0),
)
.left(
border::Side::default()
.color(Color::from_rgb(1.0, 0.8, 0.2))
.width(self.border_width * 1.5),
),
shadow: self.shadow,
snap: self.snap,
},
Expand Down
Loading