Floem
repository·main·Indexed 26 days ago
https://github.com/lapce/floemA native Rust UI library featuring fine-grained reactivity and a declarative API. It supports cross-platform rendering (Windows, macOS, Linux) via wgpu or Skia, Flexbox and Grid layouts via Taffy, and high-performance components like VirtualList. Floem implements a two-phase event dispatch model (capturing and bubbling) and provides reactive primitives such as RwSignal for state management.
What's inside floem
- The Timer example demonstrates how Floem handles concurrency between background processes (like a timer updating elapsed time) and user interactions with the GUI. It specifically tests the responsiveness of the application when handling competing signals, such as simultaneous timer ticks and slider adjustments, ensuring that UI updates like slider movements are reflected immediately.
Overview of Floem features
mainFloem is a high-performance, declarative UI library for Rust with the following core capabilities:
- Cross-platform rendering: Supports Windows, macOS, and Linux. It uses
wgpuviavgerorvello, orSkiaviaAnyRender. It includes atiny-skiaCPU fallback if a GPU is unavailable. - Fine-grained reactivity: Built around reactive primitives (inspired by
leptos_reactive) to keep the UI updated with minimal effort. - Layout Engines: Supports both Flexbox and Grid layout systems via
Taffy. - Styling & Animation: Highly customizable widgets with a styling API supporting classes, themes, transitions (CSS-like), and keyframe animations with spring easing.
- Developer Tools: Includes an Element Inspector for debugging layouts.
- Localization: Supports the
Fluentcrate for runtime language switching and fallbacks. - Performance: The view tree is constructed only once, preventing bottlenecks during view generation. Supports virtual lists for efficient large-scale data rendering.
- Cross-platform rendering: Supports Windows, macOS, and Linux. It uses
Overview of the Flight Booker example
mainThe
flight_bookerexample emulates a flight booking application based on the 7gui tasks. It is designed to demonstrate how to model two types of constraints in Floem:- Constraints between widgets: Managing interactions and dependencies between different UI components.
- Constraints within a widget: Managing internal logic and state validation within a single component.
The goal of this example is to show how to make these constraints clear, succinct, and explicit in the source code without excessive scaffolding.
Use the Pan Zoom View for visual editors
mainThepan-zoomexample provides a simple pan-zoom area that supports translation and scaling. It is designed to serve as a foundational component for building visual editors that require a central pan-zoom interface, or for testing and debugging rendering and event handling when using transformations like scale and translation.Bridge Winit events to the ui-events model
mainThe
ui-events-winitcrate provides a bridge betweenwinit's native input events (such as mouse, touch, and keyboard) and theui-eventsmodel. This allows you to process native windowing events as high-level UI events.The primary entry point for this integration is the
WindowEventReducerstruct.Quickstart with Floem
mainFloem is a native Rust UI library featuring fine-grained reactivity. To get started, use
floem::launchto run your application andRwSignalto manage reactive state. Views are constructed using a declarative API where components likeStack,Button, andLabelcan be composed.Key reactive primitives include:
RwSignal: A reactive signal that allows for reading and writing state.Label::derived: Creates a label that automatically updates when its dependencies (signals) change..action(): Attaches a closure to a widget (like aButton) to handle user interactions.
use floem::prelude::*; fn main() { floem::launch(counter_view); } fn counter_view() -> impl IntoView { let mut counter = RwSignal::new(0); Stack::horizontal(( Button::new("Increment").action(move || counter += 1), Label::derived(move || format!("Value: {counter}")), Button::new("Decrement").action(move || counter -= 1), )) .style(|s| s.size_full().items_center().justify_center().gap(10)) }Understand the two-phase event dispatch model
mainFloem implements a two-phase event dispatch system similar to the W3C DOM standard:
- Capturing Phase (Root → Target): Events are dispatched from the root view down toward the target. For each view in the path,
event_before_children()is called. This phase applies coordinate transformations (absolute to local) and can be halted if a view returnsEventPropagation::Stop. - Bubbling Phase (Target → Root): Events are dispatched from the target view back up toward the root. For each view in the path,
event_after_children()is called. This phase processes built-in behaviors and registered event listeners. If a handler returnsEventPropagation::Stop, bubbling ceases.
- Capturing Phase (Root → Target): Events are dispatched from the root view down toward the target. For each view in the path,
Verify Minimum Supported Rust Version (MSRV)
mainThis crate requires Rust 1.81 or later to compile. If you encounter compilation errors related to dependencies, you may need to upgrade your toolchain or usecargo updateto pin a dependency to a specific version compatible with your current Rust version.Reproduce event dispatch benchmarks
mainTo run the existing event dispatch benchmarks to verify performance or establish a baseline, use the following command:
cargo bench --bench event_dispatchHandle keyboard navigation and focus
mainKeyboard events are routed based on the current focus state:
- Dispatch: Events are sent to the focused view first via
dispatch_to_view(focused_id, event, directed: true). If the focused view does not process the event, it falls back to the main view. - Built-in Navigation: Floem supports standard navigation patterns:
Tab/Shift+Tab: Forward and backward navigation.Alt+Arrow keys: Arrow navigation.Enter/Space: Triggers click events on the currently focused element.
- Focus Requirements: Only views where
computed_style.get(Focusable)is true can receive focus. Focus changes triggerFocusGainedandFocusLostevents.
- Dispatch: Events are sent to the focused view first via
Implement drag and drop functionality
mainDrag and drop operations in Floem are managed through specific state transitions:
- Initiation: A drag is tracked via
drag_start: Option<(ViewId, Point)>. An actual drag operation begins when the pointer moves more than 1px from the start point. This triggers theDragStartevent and sets thewindow_state.activeflag. - Dragging: During movement, the
draggingstate (containing position and timing) is updated, anddragging_overis used to highlight potential drop targets. - Dropping: When the pointer is released over a valid target, a
Dropevent is fired at the target, and aDragEndevent is fired for the originating view.
- Initiation: A drag is tracked via
Run Floem on WebGPU using Trunk
mainTo run the WebGPU example, you must have Trunk installed and use a browser with WebGPU support. Run the following command from the example directory:
trunk serve --open