Skip to content

Commit

Permalink
Merge pull request #843 from software-mansion/add-border-radius-borde…
Browse files Browse the repository at this point in the history
…r-box-shadow

Add `border_radius`/`border_width`/`box_shadow` to `View` and `Rescaler`
  • Loading branch information
wkozyra95 authored Nov 4, 2024
2 parents 57d96dc + a805900 commit 107cb80
Show file tree
Hide file tree
Showing 70 changed files with 3,115 additions and 389 deletions.
80 changes: 59 additions & 21 deletions compositor_api/src/types/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ pub struct View {
/// List of component's children.
pub children: Option<Vec<Component>>,

/// Width of a component in pixels. Exact behavior might be different based on the parent
/// component:
/// Width of a component in pixels (without a border). Exact behavior might be different
/// based on the parent component:
/// - If the parent component is a layout, check sections "Absolute positioning" and "Static
/// positioning" of that component.
/// - If the parent component is not a layout, then this field is required.
pub width: Option<f32>,
/// Height of a component in pixels. Exact behavior might be different based on the parent
/// component:
/// Height of a component in pixels (without a border). Exact behavior might be different
/// based on the parent component:
/// - If the parent component is a layout, check sections "Absolute positioning" and "Static
/// positioning" of that component.
/// - If the parent component is not a layout, then this field is required.
Expand All @@ -52,16 +52,16 @@ pub struct View {
/// Direction defines how static children are positioned inside a View component.
pub direction: Option<ViewDirection>,

/// Distance in pixels between this component's top edge and its parent's top edge.
/// Distance in pixels between this component's top edge and its parent's top edge (including a border).
/// If this field is defined, then the component will ignore a layout defined by its parent.
pub top: Option<f32>,
/// Distance in pixels between this component's left edge and its parent's left edge.
/// Distance in pixels between this component's left edge and its parent's left edge (including a border).
/// If this field is defined, this element will be absolutely positioned, instead of being
/// laid out by its parent.
pub left: Option<f32>,
/// Distance in pixels between the bottom edge of this component and the bottom edge of its parent.
/// If this field is defined, this element will be absolutely positioned, instead of being
/// laid out by its parent.
/// Distance in pixels between the bottom edge of this component and the bottom edge of its
/// parent (including a border). If this field is defined, this element will be absolutely
/// positioned, instead of being laid out by its parent.
pub bottom: Option<f32>,
/// Distance in pixels between this component's right edge and its parent's right edge.
/// If this field is defined, this element will be absolutely positioned, instead of being
Expand All @@ -80,6 +80,27 @@ pub struct View {

/// (**default=`"#00000000"`**) Background color in a `"#RRGGBBAA"` format.
pub background_color_rgba: Option<RGBAColor>,

/// (**default=`0.0`**) Radius of a rounded corner.
pub border_radius: Option<f32>,

/// (**default=`0.0`**) Border width.
pub border_width: Option<f32>,

/// (**default=`"#00000000"`**) Border color in a `"#RRGGBBAA"` format.
pub border_color_rgba: Option<RGBAColor>,

/// List of box shadows.
pub box_shadow: Option<Vec<BoxShadow>>,
}

#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct BoxShadow {
pub offset_x: Option<f32>,
pub offset_y: Option<f32>,
pub color_rgba: Option<RGBAColor>,
pub blur_radius: Option<f32>,
}

#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
Expand Down Expand Up @@ -126,29 +147,29 @@ pub struct Rescaler {
/// (**default=`"center"`**) Vertical alignment.
pub vertical_align: Option<VerticalAlign>,

/// Width of a component in pixels. Exact behavior might be different based on the parent
/// component:
/// Width of a component in pixels (without a border). Exact behavior might be different
/// based on the parent component:
/// - If the parent component is a layout, check sections "Absolute positioning" and "Static
/// positioning" of that component.
/// - If the parent component is not a layout, then this field is required.
pub width: Option<f32>,
/// Height of a component in pixels. Exact behavior might be different based on the parent
/// component:
/// Height of a component in pixels (without a border). Exact behavior might be different
/// based on the parent component:
/// - If the parent component is a layout, check sections "Absolute positioning" and "Static
/// positioning" of that component.
/// - If the parent component is not a layout, then this field is required.
pub height: Option<f32>,

/// Distance in pixels between this component's top edge and its parent's top edge.
/// Distance in pixels between this component's top edge and its parent's top edge (including a border).
/// If this field is defined, then the component will ignore a layout defined by its parent.
pub top: Option<f32>,
/// Distance in pixels between this component's left edge and its parent's left edge.
/// Distance in pixels between this component's left edge and its parent's left edge (including a border).
/// If this field is defined, this element will be absolutely positioned, instead of being
/// laid out by its parent.
pub left: Option<f32>,
/// Distance in pixels between this component's bottom edge and its parent's bottom edge.
/// If this field is defined, this element will be absolutely positioned, instead of being
/// laid out by its parent.
/// Distance in pixels between the bottom edge of this component and the bottom edge of its
/// parent (including a border). If this field is defined, this element will be absolutely
/// positioned, instead of being laid out by its parent.
pub bottom: Option<f32>,
/// Distance in pixels between this component's right edge and its parent's right edge.
/// If this field is defined, this element will be absolutely positioned, instead of being
Expand All @@ -161,6 +182,18 @@ pub struct Rescaler {
/// Defines how this component will behave during a scene update. This will only have an
/// effect if the previous scene already contained a `Rescaler` component with the same id.
pub transition: Option<Transition>,

/// (**default=`0.0`**) Radius of a rounded corner.
pub border_radius: Option<f32>,

/// (**default=`0.0`**) Border width.
pub border_width: Option<f32>,

/// (**default=`"#00000000"`**) Border color in a `"#RRGGBBAA"` format.
pub border_color_rgba: Option<RGBAColor>,

/// List of box shadows.
pub box_shadow: Option<Vec<BoxShadow>>,
}

#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
Expand All @@ -183,7 +216,8 @@ pub struct WebView {
/// List of component's children.
pub children: Option<Vec<Component>>,

/// Id of a web renderer instance. It identifies an instance registered using a [`register web renderer`](../routes.md#register-web-renderer-instance) request.
/// Id of a web renderer instance. It identifies an instance registered using a
/// [`register web renderer`](../routes.md#register-web-renderer-instance) request.
///
/// :::warning
/// You can only refer to specific instances in one Component at a time.
Expand Down Expand Up @@ -217,8 +251,10 @@ pub struct Shader {
/// @group(1) @binding(0) var<uniform>
/// ```
/// :::note
/// This object's structure must match the structure defined in a shader source code. Currently, we do not handle memory layout automatically.
/// To achieve the correct memory alignment, you might need to pad your data with additional fields. See [WGSL documentation](https://www.w3.org/TR/WGSL/#alignment-and-size) for more details.
/// This object's structure must match the structure defined in a shader source code.
/// Currently, we do not handle memory layout automatically. To achieve the correct memory
/// alignment, you might need to pad your data with additional fields. See
/// [WGSL documentation](https://www.w3.org/TR/WGSL/#alignment-and-size) for more details.
/// :::
pub shader_param: Option<ShaderParam>,
/// Resolution of a texture where shader will be executed.
Expand Down Expand Up @@ -378,4 +414,6 @@ pub struct Tiles {
/// Defines how this component will behave during a scene update. This will only have an
/// effect if the previous scene already contained a `Tiles` component with the same id.
pub transition: Option<Transition>,

pub border_radius: Option<f32>,
}
41 changes: 41 additions & 0 deletions compositor_api/src/types/from_component.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::sync::Arc;

use compositor_render::scene;
use compositor_render::scene::BorderRadius;
use compositor_render::scene::Position;
use compositor_render::MAX_NODE_RESOLUTION;

Expand Down Expand Up @@ -101,6 +102,18 @@ impl TryFrom<View> for scene::ViewComponent {
.map(TryInto::try_into)
.unwrap_or(Ok(scene::RGBAColor(0, 0, 0, 0)))?,
transition: view.transition.map(TryInto::try_into).transpose()?,
border_radius: BorderRadius::new_with_radius(view.border_radius.unwrap_or(0.0)),
border_width: view.border_width.unwrap_or(0.0),
border_color: view
.border_color_rgba
.map(TryInto::try_into)
.unwrap_or(Ok(scene::RGBAColor(0, 0, 0, 0)))?,
box_shadow: view
.box_shadow
.unwrap_or_default()
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, _>>()?,
})
}
}
Expand Down Expand Up @@ -165,6 +178,18 @@ impl TryFrom<Rescaler> for scene::RescalerComponent {
.unwrap_or(VerticalAlign::Center)
.into(),
transition: rescaler.transition.map(TryInto::try_into).transpose()?,
border_radius: BorderRadius::new_with_radius(rescaler.border_radius.unwrap_or(0.0)),
border_width: rescaler.border_width.unwrap_or(0.0),
border_color: rescaler
.border_color_rgba
.map(TryInto::try_into)
.unwrap_or(Ok(scene::RGBAColor(0, 0, 0, 0)))?,
box_shadow: rescaler
.box_shadow
.unwrap_or_default()
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, _>>()?,
})
}
}
Expand Down Expand Up @@ -339,3 +364,19 @@ impl TryFrom<Tiles> for scene::TilesComponent {
Ok(result)
}
}

impl TryFrom<BoxShadow> for scene::BoxShadow {
type Error = TypeError;

fn try_from(value: BoxShadow) -> Result<Self, Self::Error> {
Ok(Self {
offset_x: value.offset_x.unwrap_or(0.0),
offset_y: value.offset_y.unwrap_or(0.0),
blur_radius: value.blur_radius.unwrap_or(0.0),
color: value
.color_rgba
.map(TryInto::try_into)
.unwrap_or(Ok(scene::RGBAColor(255, 255, 255, 255)))?,
})
}
}
16 changes: 15 additions & 1 deletion compositor_render/src/scene/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use std::{fmt::Display, sync::Arc, time::Duration};
use crate::{InputId, RendererId};

use super::{
AbsolutePosition, Component, HorizontalAlign, InterpolationKind, RGBAColor, Size, VerticalAlign,
AbsolutePosition, BorderRadius, BoxShadow, Component, HorizontalAlign, InterpolationKind,
RGBAColor, Size, VerticalAlign,
};

mod interpolation;
mod position;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ComponentId(pub Arc<str>);
Expand Down Expand Up @@ -140,6 +142,12 @@ pub struct ViewComponent {
pub overflow: Overflow,

pub background_color: RGBAColor,

pub border_radius: BorderRadius,
pub border_width: f32,
pub border_color: RGBAColor,

pub box_shadow: Vec<BoxShadow>,
}

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -181,6 +189,12 @@ pub struct RescalerComponent {
pub mode: RescaleMode,
pub horizontal_align: HorizontalAlign,
pub vertical_align: VerticalAlign,

pub border_radius: BorderRadius,
pub border_width: f32,
pub border_color: RGBAColor,

pub box_shadow: Vec<BoxShadow>,
}

#[derive(Debug, Clone, Copy)]
Expand Down
44 changes: 43 additions & 1 deletion compositor_render/src/scene/components/interpolation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::scene::types::interpolation::{ContinuousValue, InterpolationState};
use crate::scene::{
types::interpolation::{ContinuousValue, InterpolationState},
BorderRadius, BoxShadow,
};

use super::{AbsolutePosition, Position};

Expand Down Expand Up @@ -46,3 +49,42 @@ impl ContinuousValue for AbsolutePosition {
}
}
}

impl ContinuousValue for BorderRadius {
fn interpolate(start: &Self, end: &Self, state: InterpolationState) -> Self {
Self {
top_left: ContinuousValue::interpolate(&start.top_left, &end.top_left, state),
top_right: ContinuousValue::interpolate(&start.top_right, &end.top_right, state),
bottom_right: ContinuousValue::interpolate(
&start.bottom_right,
&end.bottom_right,
state,
),
bottom_left: ContinuousValue::interpolate(&start.bottom_left, &end.bottom_left, state),
}
}
}

impl ContinuousValue for Vec<BoxShadow> {
fn interpolate(start: &Self, end: &Self, state: InterpolationState) -> Self {
start
.iter()
.zip(end.iter())
// interpolate as long both lists have entries
.map(|(start, end)| ContinuousValue::interpolate(start, end, state))
// add remaining elements if end is longer
.chain(end.iter().skip(usize::min(start.len(), end.len())).copied())
.collect()
}
}

impl ContinuousValue for BoxShadow {
fn interpolate(start: &Self, end: &Self, state: InterpolationState) -> Self {
Self {
offset_x: ContinuousValue::interpolate(&start.offset_x, &end.offset_x, state),
offset_y: ContinuousValue::interpolate(&start.offset_y, &end.offset_y, state),
blur_radius: ContinuousValue::interpolate(&start.blur_radius, &end.blur_radius, state),
color: end.color,
}
}
}
27 changes: 27 additions & 0 deletions compositor_render/src/scene/components/position.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use crate::scene::AbsolutePosition;

use super::Position;

impl Position {
pub(crate) fn with_border(self, border_width: f32) -> Self {
match self {
Position::Static { width, height } => Self::Static {
width: width.map(|w| w + 2.0 * border_width),
height: height.map(|h| h + 2.0 * border_width),
},
Position::Absolute(AbsolutePosition {
width,
height,
position_horizontal,
position_vertical,
rotation_degrees,
}) => Self::Absolute(AbsolutePosition {
width: width.map(|w| w + 2.0 * border_width),
height: height.map(|h| h + 2.0 * border_width),
position_horizontal,
position_vertical,
rotation_degrees,
}),
}
}
}
Loading

0 comments on commit 107cb80

Please sign in to comment.