bevy icon indicating copy to clipboard operation
bevy copied to clipboard

Support more complicated BoxShadows

Open JMS55 opened this issue 1 year ago • 2 comments

What problem does this solve or what need does it fill?

Allow us to do things like https://www.tyleo.com/html-glass.html.

What solution would you like?

  • [ ] Inset box shadows
  • [ ] Per-side box shadows
  • [ ] More than one box shadow per node
  • [ ] Have border radius clip all of the above (not sure if it already does or not)

JMS55 avatar Nov 24 '24 21:11 JMS55

Inset box shadows

I only included drop shadow support as I wanted to keep the initial PR really simple but inset shadows should be really easy to add. If there's anyone looking for an idea for a simple side project, a 3rd party inset shadow crate shouldn't take much effort. I might make one myself if I run out of bevy PR ideas.

Per-side and multiple box shadows

The plugin just draws blurred rects from a list so we have support for per-side and multiple box shadows already, sort of. That requires users to write their own component and extraction function though, which isn't great.

Clipping

UI clipping only supports right angled, axis-aligned rectangles. It doesn't consider border radius at all, I guess we need some of compositing to implement it properly?

Elliptical border radius

Another missing feature for shadows we need is elliptical border radius support. The box shadow plugin only supports uniform scaling of node's shadows because inon-uniform scaling would require elliptical border radius support to draw the distorted rounded corners.

API

Something like this maybe:

#[derive(Component, Default, Clone, Debug, Reflect)]
#[reflect(Component, Default)]
#[cfg_attr(
    feature = "serialize",
    derive(serde::Serialize, serde::Deserialize),
    reflect(Serialize, Deserialize)
)]
pub struct BoxShadow {
    /// Shadows drawn according to their order in the vector, from back to front
    shadows: Vec<ShadowStyle>,
}

// #[derive(Component, Copy, Clone, Debug, Default, PartialEq, Eq, Reflect)]
// #[reflect(Component, Default, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq, Reflect)]
pub enum ShadowStyle {
    /// Shadow drawn beneath a UI node.
    /// Position and size relative to the corresponding UI node.
    Drop {
        /// The shadow's color
        color: Color,
        /// Horizontal offset
        x_offset: Val,
        /// Vertical offset
        y_offset: Val,
        /// How much the shadow should spread outward.
        ///
        /// Negative values will make the shadow shrink inwards.
        /// Percentage values are based on the width of the UI node.
        spread_radius: Val,
        /// Blurriness of the shadow
        blur_radius: Val,
    },
    /// Shadow drawn beneath its corresponding UI node.
    /// Position is relative to its `Node` but with independent size and radius.
    DropFree {
        /// The shadow's color
        color: Color,
        /// Horizontal offset
        x_offset: Val,
        /// Vertical offset
        y_offset: Val,
        /// Shadow width,
        width: Val,
        /// Shadow height
        height: Val,
        /// radius of the corners
        radius: BorderRadius,
        /// Blurriness of the shadow
        blur_radius: Val,
    },
    /// Inner shadow drawn on top of the node and inside the node's padding box, starting at the border-padding edge.
    Inset {
         /// The shadow's color
        color: Color,
        /// Horizontal offset.
        /// Negative values move the shadow to the left, positive to the right.
        x_offset: Val,
        /// Vertical offset.
        /// Negative values move the shadow to the left, positive to the right.
        y_offset: Val,
        /// Controls the size of the shadow. Positive values increase the size of the shadow,
        /// covering more of the padding box.
        spread_radius: Val,
        /// Blurriness of the shadow
        blur_radius: Val,
    },
}

impl Default for ShadowStyle {
    fn default() -> Self {
        Self::Drop {
            color: Color::BLACK,
            x_offset: Val::Percent(20.),
            y_offset: Val::Percent(20.),
            spread_radius: Val::ZERO,
            blur_radius: Val::Percent(10.),
        }
    }
}
  • Don't like the name DropFree very much. Alternatives I can think of like DropAbsolute, Absolute, DropIndependent, CustomDrop, don't seem much better though.
  • Could just have one Drop variant with a ShadowSize enum with SpreadRadius and Independent/Absolute variants instead.
  • Might be cleaner to have seperate DropShadow and InsetShadow components instead of one BoxShadow component, each with their own seperate list of shadows.

ickshonpe avatar Nov 25 '24 11:11 ickshonpe

Multiple shadow support #16502

ickshonpe avatar Nov 25 '24 12:11 ickshonpe