Skip to content

Commit

Permalink
feat: Opacity attribute (#397)
Browse files Browse the repository at this point in the history
* feat: Opacity attribute

* chore: Finish

* chore: Add `save_layer_alpha_f` to the mocked APIs

* update mocked code
  • Loading branch information
marc2332 authored Dec 6, 2023
1 parent 9aa25b5 commit b2131c9
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 7 deletions.
23 changes: 23 additions & 0 deletions book/src/guides/style.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Learn how the style attributes work.
- [`corner radius & corner smoothing`](#corner_radius--corner_smoothing)
- [`border`](#border)
- [`overflow`](#overflow)
- [`opacity`](#opacity)
- [`Color syntax`](#color-syntax)
- [`Static colors`](#static-colors)
- [`rgb() / hsl(`)](#rgb--hsl)
Expand Down Expand Up @@ -121,6 +122,28 @@ fn app(cx: Scope) -> Element {

Compatible elements: [`rect`](/guides/elements.html#rect)


### opacity

> Only available on the `main` branch.
Specify the opacity of an element and all its desdendants.

Example:

```rust, no_run
fn app(cx: Scope) -> Element {
render!(
rect {
opacity: "0.5", // 50% visible
label {
"I am fading!"
}
}
)
}
```

### Color syntax

The attributes that have colors as values can use the following syntax:
Expand Down
5 changes: 5 additions & 0 deletions crates/elements/src/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ builder_constructors! {
position_right: String,
position_bottom: String,
position_left: String,
opacity: String,
};
label {
color: String,
Expand Down Expand Up @@ -223,6 +224,7 @@ builder_constructors! {
text_overflow: String,
focusable: String,
margin: String,
opacity: String,
};
paragraph {
layer: String,
Expand Down Expand Up @@ -261,6 +263,7 @@ builder_constructors! {
overflow: String,
focusable: String,
margin: String,
opacity: String,
};
text {
color: String,
Expand Down Expand Up @@ -291,6 +294,7 @@ builder_constructors! {
alt: String,
name: String,
focusable: String,
opacity: String,
};
svg {
margin: String,
Expand All @@ -304,6 +308,7 @@ builder_constructors! {
alt: String,
name: String,
focusable: String,
opacity: String,
};
}

Expand Down
4 changes: 4 additions & 0 deletions crates/engine/src/mocked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,10 @@ impl Canvas {
pub fn draw_circle(&self, _center: impl Into<Point>, _radius: f32, _paint: &Paint) -> &Self {
unimplemented!("This is mocked")
}

pub fn save_layer_alpha_f(&self, bounds: impl Into<Option<Rect>>, alpha: f32) -> usize {
unimplemented!("This is mocked")
}
}

#[repr(i32)]
Expand Down
27 changes: 22 additions & 5 deletions crates/renderer/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use dioxus_native_core::NodeId;
use freya_core::prelude::*;
use freya_dom::prelude::DioxusNode;
use freya_engine::prelude::*;
use freya_node_state::Transform;
use freya_node_state::{Style, Transform};
use torin::geometry::Area;

use crate::elements::{render_image, render_label, render_paragraph, render_rect, render_svg};
Expand Down Expand Up @@ -33,13 +33,16 @@ pub fn render_skia(
viewports: &Viewports,
render_wireframe: bool,
matrices: &mut Vec<(Matrix, Vec<NodeId>)>,
opacities: &mut Vec<(f32, Vec<NodeId>)>,
) {
let node_type = &*dioxus_node.node_type();
if let NodeType::Element(ElementNode { tag, .. }) = node_type {
canvas.save();

let node_transform = &*dioxus_node.get::<Transform>().unwrap();
let node_style = &*dioxus_node.get::<Style>().unwrap();

// Pass rotate effect to children
if let Some(rotate_degs) = node_transform.rotate_degs {
let mut matrix = Matrix::new_identity();
matrix.set_rotate(
Expand All @@ -50,11 +53,15 @@ pub fn render_skia(
}),
);

matrices.push((matrix, dioxus_node.child_ids()));
matrices.push((matrix, vec![dioxus_node.id()]));
}

canvas.concat(&matrix);
// Pass opacity effect to children
if let Some(opacity) = node_style.opacity {
opacities.push((opacity, vec![dioxus_node.id()]));
}

// Apply inherited matrices
for (matrix, nodes) in matrices.iter_mut() {
if nodes.contains(&dioxus_node.id()) {
canvas.concat(matrix);
Expand All @@ -63,10 +70,20 @@ pub fn render_skia(
}
}

let node_viewports = viewports.get(&dioxus_node.id());
// Apply inherited opacity effects
for (opacity, nodes) in opacities.iter_mut() {
if nodes.contains(&dioxus_node.id()) {
canvas.save_layer_alpha_f(
Rect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
*opacity,
);

nodes.extend(dioxus_node.child_ids());
}
}

// Clip all elements with their corresponding viewports
if let Some((element_viewport, node_viewports)) = node_viewports {
if let Some((element_viewport, node_viewports)) = viewports.get(&dioxus_node.id()) {
// Only clip the element iself when it's paragraph because
// it will render the inner text spans on it's own, so if these spans overflow the paragraph,
// It is the paragraph job to make sure they are clipped
Expand Down
6 changes: 4 additions & 2 deletions crates/renderer/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,14 +228,15 @@ impl<T: Clone> WindowEnv<T> {
canvas.clear(self.window_config.background);

let mut matrices: Vec<(Matrix, Vec<NodeId>)> = Vec::default();
let mut opacities: Vec<(f32, Vec<NodeId>)> = Vec::default();

process_render(
viewports,
rdom,
font_collection,
layers,
&mut (canvas, (&mut matrices)),
|dom, node_id, area, font_collection, viewports, (canvas, matrices)| {
&mut (canvas, &mut matrices, &mut opacities),
|dom, node_id, area, font_collection, viewports, (canvas, matrices, opacities)| {
let render_wireframe = if let Some(hovered_node) = &hovered_node {
hovered_node
.lock()
Expand All @@ -254,6 +255,7 @@ impl<T: Clone> WindowEnv<T> {
viewports,
render_wireframe,
matrices,
opacities,
);
}
},
Expand Down
9 changes: 9 additions & 0 deletions crates/state/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub struct Style {
pub image_data: Option<Vec<u8>>,
pub svg_data: Option<Vec<u8>>,
pub overflow: OverflowMode,
pub opacity: Option<f32>,
}

#[partial_derive_state]
Expand All @@ -46,6 +47,7 @@ impl State<CustomAttributeValues> for Style {
"svg_data",
"svg_content",
"overflow",
"opacity",
]));

fn update<'a>(
Expand Down Expand Up @@ -149,6 +151,13 @@ impl State<CustomAttributeValues> for Style {
}
}
}
"opacity" => {
if let Some(value) = attr.value.as_text() {
if let Ok(opacity) = value.parse::<f32>() {
style.opacity = Some(opacity);
}
}
}
_ => {
panic!("Unsupported attribute <{}>, this should not be happening, please report it.", attr.attribute.name);
}
Expand Down
49 changes: 49 additions & 0 deletions examples/opacity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]

use freya::prelude::*;

static FERRIS: &[u8] = include_bytes!("./ferris.svg");

fn main() {
launch_with_props(app, "Opacity", (400.0, 350.0));
}

fn app(cx: Scope) -> Element {
let ferris = bytes_to_data(cx, FERRIS);
let opacity = use_state(cx, || 70.0);

render!(
rect {
height: "100%",
width: "100%",
main_align: "center",
cross_align: "center",
rect {
opacity: "{opacity / 100.0}",
svg {
width: "100%",
height: "50%",
svg_data: ferris,
}
label {
text_align: "center",
width: "100%",
"Meet Ferris!"
}
}
Slider {
width: 100.0,
value: *opacity.get(),
onmoved: |p| {
opacity.set(p);
}
}
label {
"Drag the slider"
}
}
)
}

0 comments on commit b2131c9

Please sign in to comment.