Skip to content

Commit

Permalink
Merge pull request #2662 from iced-rs/reactive-rendering
Browse files Browse the repository at this point in the history
Reactive Rendering
  • Loading branch information
hecrj authored Nov 13, 2024
2 parents 42a2cb6 + 28ec6df commit a11fcf8
Show file tree
Hide file tree
Showing 60 changed files with 2,180 additions and 1,771 deletions.
24 changes: 12 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ maintenance = { status = "actively-developed" }

[features]
default = ["wgpu", "tiny-skia", "fira-sans", "auto-detect-theme"]
# Enable the `wgpu` GPU-accelerated renderer backend
# Enables the `wgpu` GPU-accelerated renderer backend
wgpu = ["iced_renderer/wgpu", "iced_widget/wgpu"]
# Enable the `tiny-skia` software renderer backend
# Enables the `tiny-skia` software renderer backend
tiny-skia = ["iced_renderer/tiny-skia"]
# Enables the `Image` widget
# Enables the `image` widget
image = ["image-without-codecs", "image/default"]
# Enables the `Image` widget, without any built-in codecs of the `image` crate
# Enables the `image` widget, without any built-in codecs of the `image` crate
image-without-codecs = ["iced_widget/image", "dep:image"]
# Enables the `Svg` widget
# Enables the `svg` widget
svg = ["iced_widget/svg"]
# Enables the `Canvas` widget
# Enables the `canvas` widget
canvas = ["iced_widget/canvas"]
# Enables the `QRCode` widget
# Enables the `qr_code` widget
qr_code = ["iced_widget/qr_code"]
# Enables the `markdown` widget
markdown = ["iced_widget/markdown"]
Expand All @@ -55,18 +55,18 @@ system = ["iced_winit/system"]
web-colors = ["iced_renderer/web-colors"]
# Enables the WebGL backend, replacing WebGPU
webgl = ["iced_renderer/webgl"]
# Enables the syntax `highlighter` module
# Enables syntax highligthing
highlighter = ["iced_highlighter", "iced_widget/highlighter"]
# Enables experimental multi-window support.
multi-window = ["iced_winit/multi-window"]
# Enables the advanced module
advanced = ["iced_core/advanced", "iced_widget/advanced"]
# Enables embedding Fira Sans as the default font on Wasm builds
# Embeds Fira Sans as the default font on Wasm builds
fira-sans = ["iced_renderer/fira-sans"]
# Enables auto-detecting light/dark mode for the built-in theme
# Auto-detects light/dark mode for the built-in theme
auto-detect-theme = ["iced_core/auto-detect-theme"]
# Enables strict assertions for debugging purposes at the expense of performance
strict-assertions = ["iced_renderer/strict-assertions"]
# Redraws on every runtime event, and not only when a widget requests it
unconditional-rendering = ["iced_winit/unconditional-rendering"]

[dependencies]
iced_core.workspace = true
Expand Down
21 changes: 9 additions & 12 deletions core/src/element.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use crate::event::{self, Event};
use crate::layout;
use crate::mouse;
use crate::overlay;
use crate::renderer;
use crate::widget;
use crate::widget::tree::{self, Tree};
use crate::{
Border, Clipboard, Color, Layout, Length, Rectangle, Shell, Size, Vector,
Widget,
Border, Clipboard, Color, Event, Layout, Length, Rectangle, Shell, Size,
Vector, Widget,
};

use std::borrow::Borrow;
Expand Down Expand Up @@ -309,7 +308,7 @@ where
self.widget.operate(tree, layout, renderer, operation);
}

fn on_event(
fn update(
&mut self,
tree: &mut Tree,
event: Event,
Expand All @@ -319,11 +318,11 @@ where
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, B>,
viewport: &Rectangle,
) -> event::Status {
) {
let mut local_messages = Vec::new();
let mut local_shell = Shell::new(&mut local_messages);

let status = self.widget.on_event(
self.widget.update(
tree,
event,
layout,
Expand All @@ -335,8 +334,6 @@ where
);

shell.merge(local_shell, &self.mapper);

status
}

fn draw(
Expand Down Expand Up @@ -447,7 +444,7 @@ where
.operate(state, layout, renderer, operation);
}

fn on_event(
fn update(
&mut self,
state: &mut Tree,
event: Event,
Expand All @@ -457,10 +454,10 @@ where
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) -> event::Status {
self.element.widget.on_event(
) {
self.element.widget.update(
state, event, layout, cursor, renderer, clipboard, shell, viewport,
)
);
}

fn draw(
Expand Down
53 changes: 52 additions & 1 deletion core/src/layout/flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ where
let max_cross = axis.cross(limits.max());

let mut fill_main_sum = 0;
let mut some_fill_cross = false;
let (mut cross, cross_compress) = match axis {
Axis::Vertical if width == Length::Shrink => (0.0, true),
Axis::Horizontal if height == Length::Shrink => (0.0, true),
Expand All @@ -90,6 +91,10 @@ where
let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
nodes.resize(items.len(), Node::default());

// FIRST PASS
// We lay out non-fluid elements in the main axis.
// If we need to compress the cross axis, then we skip any of these elements
// that are also fluid in the cross axis.
for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() {
let (fill_main_factor, fill_cross_factor) = {
let size = child.as_widget().size();
Expand Down Expand Up @@ -121,6 +126,41 @@ where
nodes[i] = layout;
} else {
fill_main_sum += fill_main_factor;
some_fill_cross = some_fill_cross || fill_cross_factor != 0;
}
}

// SECOND PASS (conditional)
// If we must compress the cross axis and there are fluid elements in the
// cross axis, we lay out any of these elements that are also non-fluid in
// the main axis (i.e. the ones we deliberately skipped in the first pass).
//
// We use the maximum cross length obtained in the first pass as the maximum
// cross limit.
if cross_compress && some_fill_cross {
for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate()
{
let (fill_main_factor, fill_cross_factor) = {
let size = child.as_widget().size();

axis.pack(size.width.fill_factor(), size.height.fill_factor())
};

if fill_main_factor == 0 && fill_cross_factor != 0 {
let (max_width, max_height) = axis.pack(available, cross);

let child_limits =
Limits::new(Size::ZERO, Size::new(max_width, max_height));

let layout =
child.as_widget().layout(tree, renderer, &child_limits);
let size = layout.size();

available -= axis.main(size);
cross = cross.max(axis.cross(size));

nodes[i] = layout;
}
}
}

Expand All @@ -135,17 +175,26 @@ where
},
};

// THIRD PASS
// We only have the elements that are fluid in the main axis left.
// We use the remaining space to evenly allocate space based on fill factors.
for (i, (child, tree)) in items.iter().zip(trees).enumerate() {
let (fill_main_factor, fill_cross_factor) = {
let size = child.as_widget().size();

axis.pack(size.width.fill_factor(), size.height.fill_factor())
};

if fill_main_factor != 0 || (cross_compress && fill_cross_factor != 0) {
if fill_main_factor != 0 {
let max_main =
remaining * fill_main_factor as f32 / fill_main_sum as f32;

let max_main = if max_main.is_nan() {
f32::INFINITY
} else {
max_main
};

let min_main = if max_main.is_infinite() {
0.0
} else {
Expand Down Expand Up @@ -178,6 +227,8 @@ where
let pad = axis.pack(padding.left, padding.top);
let mut main = pad.0;

// FOURTH PASS
// We align all the laid out nodes in the cross axis, if needed.
for (i, node) in nodes.iter_mut().enumerate() {
if i > 0 {
main += spacing;
Expand Down
8 changes: 3 additions & 5 deletions core/src/overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ mod group;
pub use element::Element;
pub use group::Group;

use crate::event::{self, Event};
use crate::layout;
use crate::mouse;
use crate::renderer;
use crate::widget;
use crate::widget::Tree;
use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size, Vector};
use crate::{Clipboard, Event, Layout, Point, Rectangle, Shell, Size, Vector};

/// An interactive component that can be displayed on top of other widgets.
pub trait Overlay<Message, Theme, Renderer>
Expand Down Expand Up @@ -57,16 +56,15 @@ where
/// * a [`Clipboard`], if available
///
/// By default, it does nothing.
fn on_event(
fn update(
&mut self,
_event: Event,
_layout: Layout<'_>,
_cursor: mouse::Cursor,
_renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
_shell: &mut Shell<'_, Message>,
) -> event::Status {
event::Status::Ignored
) {
}

/// Returns the current [`mouse::Interaction`] of the [`Overlay`].
Expand Down
17 changes: 7 additions & 10 deletions core/src/overlay/element.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
pub use crate::Overlay;

use crate::event::{self, Event};
use crate::layout;
use crate::mouse;
use crate::renderer;
use crate::widget;
use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size};
use crate::{Clipboard, Event, Layout, Point, Rectangle, Shell, Size};

/// A generic [`Overlay`].
#[allow(missing_debug_implementations)]
Expand Down Expand Up @@ -50,17 +49,17 @@ where
}

/// Processes a runtime [`Event`].
pub fn on_event(
pub fn update(
&mut self,
event: Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
) {
self.overlay
.on_event(event, layout, cursor, renderer, clipboard, shell)
.update(event, layout, cursor, renderer, clipboard, shell);
}

/// Returns the current [`mouse::Interaction`] of the [`Element`].
Expand Down Expand Up @@ -149,19 +148,19 @@ where
self.content.operate(layout, renderer, operation);
}

fn on_event(
fn update(
&mut self,
event: Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, B>,
) -> event::Status {
) {
let mut local_messages = Vec::new();
let mut local_shell = Shell::new(&mut local_messages);

let event_status = self.content.on_event(
self.content.update(
event,
layout,
cursor,
Expand All @@ -171,8 +170,6 @@ where
);

shell.merge(local_shell, self.mapper);

event_status
}

fn mouse_interaction(
Expand Down
29 changes: 12 additions & 17 deletions core/src/overlay/group.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::event;
use crate::layout;
use crate::mouse;
use crate::overlay;
Expand Down Expand Up @@ -73,29 +72,25 @@ where
)
}

fn on_event(
fn update(
&mut self,
event: Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
self.children
.iter_mut()
.zip(layout.children())
.map(|(child, layout)| {
child.on_event(
event.clone(),
layout,
cursor,
renderer,
clipboard,
shell,
)
})
.fold(event::Status::Ignored, event::Status::merge)
) {
for (child, layout) in self.children.iter_mut().zip(layout.children()) {
child.update(
event.clone(),
layout,
cursor,
renderer,
clipboard,
shell,
);
}
}

fn draw(
Expand Down
Loading

0 comments on commit a11fcf8

Please sign in to comment.