Skip to content
Merged
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
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"
}
}
)
}