Skip to content
53 changes: 53 additions & 0 deletions druid/src/widget/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct BorderStyle {
/// A widget that provides simple visual styling options to a child.
pub struct Container<T> {
background: Option<BackgroundBrush<T>>,
foreground: Option<BackgroundBrush<T>>,
border: Option<BorderStyle>,
corner_radius: KeyOrValue<RoundedRectRadii>,

Expand All @@ -41,6 +42,7 @@ impl<T: Data> Container<T> {
pub fn new(child: impl Widget<T> + 'static) -> Self {
Self {
background: None,
foreground: None,
border: None,
corner_radius: 0.0.into(),
child: WidgetPod::new(child).boxed(),
Expand Down Expand Up @@ -77,6 +79,36 @@ impl<T: Data> Container<T> {
self.background = None;
}

/// Builder-style method for setting the foreground for this widget.
///
/// This can be passed anything which can be represented by a [`BackgroundBrush`];
Copy link
Member

Choose a reason for hiding this comment

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

I guess BackgroundBrush is no longer a great name. Not a change we need to worry about in this PR though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I was thinking the same since I was preparing the PR. Since this didn't make it in time should I prepare another PR for the BackgroundBrush change?

Copy link
Member

Choose a reason for hiding this comment

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

If you wish, sure. After we merge this one here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I just rename it to Brush? I am also thinking PrimitivesBrush

Copy link
Member

Choose a reason for hiding this comment

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

Brush sort-of clashes with Piet's Brush so perhaps not. Given that it is defined in painter.rs I'm also thinking maybe PaintBrush. Although we could maybe go with something non-brush even, given that it supports a full Painter widget as a variant. 🤔

/// notably, it can be any [`Color`], a [`Key<Color>`] resolvable in the [`Env`],
/// any gradient, or a fully custom [`Painter`] widget.
///
/// [`Key<Color>`]: crate::Key
/// [`Painter`]: super::Painter
pub fn foreground(mut self, brush: impl Into<BackgroundBrush<T>>) -> Self {
self.set_foreground(brush);
self
}

/// Set the foreground for this widget.
///
/// This can be passed anything which can be represented by a [`BackgroundBrush`];
/// notably, it can be any [`Color`], a [`Key<Color>`] resolvable in the [`Env`],
/// any gradient, or a fully custom [`Painter`] widget.
///
/// [`Key<Color>`]: crate::Key
/// [`Painter`]: super::Painter
pub fn set_foreground(&mut self, brush: impl Into<BackgroundBrush<T>>) {
self.foreground = Some(brush.into());
}

/// Clears foreground.
pub fn clear_foreground(&mut self) {
self.foreground = None;
}

/// Builder-style method for painting a border around the widget with a color and width.
///
/// Arguments can be either concrete values, or a [`Key`] of the respective
Expand Down Expand Up @@ -130,6 +162,11 @@ impl<T: Data> Container<T> {
self.background.is_some()
}

#[cfg(test)]
pub(crate) fn foreground_is_some(&self) -> bool {
self.foreground.is_some()
}

#[cfg(test)]
pub(crate) fn border_is_some(&self) -> bool {
self.border.is_some()
Expand Down Expand Up @@ -158,6 +195,11 @@ impl<T: Data> Widget<T> for Container<T> {
brush.update(ctx, old_data, data, env);
});
}
if let Some(brush) = self.foreground.as_mut() {
trace_span!("update foreground").in_scope(|| {
brush.update(ctx, old_data, data, env);
});
}
if let Some(border) = &self.border {
if ctx.env_key_changed(&border.width) {
ctx.request_layout();
Expand Down Expand Up @@ -228,6 +270,17 @@ impl<T: Data> Widget<T> for Container<T> {
};

self.child.paint(ctx, data, env);

if let Some(foreground) = self.foreground.as_mut() {
let panel = ctx.size().to_rounded_rect(corner_radius);

trace_span!("paint foreground").in_scope(|| {
ctx.with_save(|ctx| {
ctx.clip(panel);
foreground.paint(ctx, data, env);
});
});
}
}

fn debug_state(&self, data: &T) -> DebugState {
Expand Down
13 changes: 12 additions & 1 deletion druid/src/widget/widget_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ pub trait WidgetExt<T: Data>: Widget<T> + Sized + 'static {
Container::new(self).background(brush)
}

/// Wrap this widget in a [`Container`] with the provided foreground `brush`.
///
/// See [`Container::foreground`] for more information.
fn foreground(self, brush: impl Into<BackgroundBrush<T>>) -> Container<T> {
Container::new(self).foreground(brush)
}

/// Wrap this widget in a [`Container`] with the given border.
///
/// Arguments can be either concrete values, or a [`Key`] of the respective
Expand Down Expand Up @@ -302,17 +309,21 @@ mod tests {
// this should be Container<Align<Container<Slider>>>
let widget = Slider::new()
.background(Color::BLACK)
.foreground(Color::WHITE)
.align_left()
.border(Color::BLACK, 1.0);
assert!(widget.border_is_some());
assert!(!widget.background_is_some());
assert!(!widget.foreground_is_some());

// this should be Container<Slider>
let widget = Slider::new()
.background(Color::BLACK)
.border(Color::BLACK, 1.0);
.border(Color::BLACK, 1.0)
.foreground(Color::WHITE);
assert!(widget.background_is_some());
assert!(widget.border_is_some());
assert!(widget.foreground_is_some());
}

#[test]
Expand Down