Skip to main content

fission_core/
lib.rs

1//! # fission-core
2//!
3//! The runtime, widget system, and action/reducer architecture for the Fission UI
4//! framework.
5//!
6//! `fission-core` provides:
7//!
8//! - A **declarative widget tree** built from composable primitives ([`Node`], [`Widget`]).
9//! - A **unidirectional data-flow** pipeline: [`Action`] -> [`Runtime::dispatch`] -> reducer
10//!   -> mutated [`AppState`].
11//! - An **effect system** for async side-effects ([`Effect`], [`SystemEffect`]).
12//! - Built-in widgets: [`Button`], [`Text`], [`TextInput`], [`Container`], [`Row`],
13//!   [`Column`], [`Scroll`], [`ZStack`], [`Grid`], [`LazyColumn`], and more.
14//!
15//! ## Getting started
16//!
17//! ```rust,ignore
18//! use fission_core::*;
19//! use fission_core::ui::*;
20//!
21//! // Define application state
22//! #[derive(Debug, Default)]
23//! struct MyState { value: String }
24//! impl AppState for MyState {}
25//!
26//! // Build a widget
27//! struct MyWidget;
28//! impl Widget<MyState> for MyWidget {
29//!     fn build(&self, ctx: &mut BuildCtx<MyState>, view: &View<MyState>) -> Node {
30//!         Text::new(&*view.state.value).into_node()
31//!     }
32//! }
33//! ```
34
35use anyhow::{anyhow, Result};
36use fission_diagnostics::prelude as diag;
37use downcast_rs::Downcast;
38use fission_ir::CoreIR;
39use lazy_static::lazy_static;
40use serde_json;
41use std::any::{Any, TypeId};
42use std::collections::{HashMap, HashSet};
43use std::sync::Arc;
44
45extern crate self as fission_core;
46
47pub mod action;
48pub mod context; // New
49pub mod diff;
50pub mod effect; // New
51pub mod env;
52pub mod event;
53pub mod hit_test;
54pub mod input;
55pub mod lowering;
56pub mod media; 
57pub mod registry;
58pub mod runtime;
59pub mod time;
60pub mod ui;
61
62pub mod view;
63
64#[cfg(test)]
65mod tests;
66
67use crate::env::ActiveAnimation;
68pub use action::{Action, ActionEnvelope, ActionId, AppState};
69pub use context::{ReducerContext, Effects}; // New
70pub use effect::{Effect, EffectEnvelope, EffectPayload, ActionInput, SystemEffect}; // New
71pub use env::{Env, InteractionStateMap, RuntimeState, ScrollStateMap, Clipboard, ImeHandler};
72pub use runtime::Runtime;
73
74pub use event::{InputEvent, KeyCode, KeyEvent, LifecycleEvent, PointerButton, PointerEvent};
75pub use fission_ir::op;
76pub use fission_ir::{EmbedKind, NodeId, Op, WidgetNodeId};
77pub use fission_layout::{
78    BoxConstraints, FlexDirection, LayoutEngine, LayoutOp, LayoutPoint, LayoutRect, LayoutSize, LayoutSnapshot, LayoutUnit, TextMeasurer,
79};
80use hit_test::{find_next_focus_node, hit_test, hit_test_with_scroll};
81pub use lowering::{LoweringContext, NodeBuilder};
82pub use registry::{
83    ActionRegistry, AnimationPropertyId, AnimationRequest, AnimationStartValue, BuildCtx, Handler,
84    PortalLayer, VideoRegistration,
85};
86pub use time::{Clock, CurrentTime};
87pub use ui::{Builder, Button, Column, CustomNode, LayoutBuilder, Lower, LowerDyn, Node, Row, Text};
88pub use view::{Selector, View, Widget};
89
90/// A frame-tick action that advances the runtime clock by a delta.
91///
92/// The platform shell dispatches `Tick` once per frame so that animations,
93/// timers, and other time-dependent logic can progress.
94///
95/// # Example
96///
97/// ```rust,ignore
98/// // Advance the runtime by 16 ms (~60 fps)
99/// runtime.tick(16)?;
100/// ```
101#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
102pub struct Tick {
103    /// Delta time in milliseconds since the last tick.
104    pub dt: CurrentTime,
105}
106
107impl Action for Tick {
108    fn static_id() -> ActionId {
109        *TICK_ACTION_ID
110    }
111}
112
113lazy_static! {
114    pub static ref TICK_ACTION_ID: ActionId = ActionId::from_name("fission_core::Tick");
115}
116
117/// An action that sets the runtime clock to an absolute timestamp.
118///
119/// Unlike [`Tick`] which advances by a delta, `AdvanceTo` jumps directly to
120/// the given time. Useful for testing and deterministic replay.
121///
122/// # Example
123///
124/// ```rust,ignore
125/// let envelope: ActionEnvelope = AdvanceTo { time: 5000 }.into();
126/// runtime.dispatch(envelope, NodeId::derived(0, &[0]))?;
127/// ```
128#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
129pub struct AdvanceTo {
130    /// The absolute time (in milliseconds) to set the clock to.
131    pub time: CurrentTime,
132}
133
134impl Action for AdvanceTo {
135    fn static_id() -> ActionId {
136        *ADVANCE_TO_ACTION_ID
137    }
138}
139
140lazy_static! {
141    pub static ref ADVANCE_TO_ACTION_ID: ActionId = ActionId::from_name("fission_core::AdvanceTo");
142}
143
144/// A type-erased reducer function stored in the [`Runtime`].
145///
146/// `BoxedReducer` is the internal representation used by the runtime to invoke
147/// reducers without knowing the concrete `AppState` or `Action` types. You
148/// rarely need to interact with this type directly -- use [`BuildCtx::bind`] or
149/// [`ActionRegistry::register`] instead.
150pub type BoxedReducer = Box<
151    dyn FnMut(
152        &mut HashMap<TypeId, Box<dyn AppState>>, 
153        &ActionEnvelope, 
154        NodeId,
155        &mut Vec<EffectEnvelope>,
156        &ActionInput
157    ) -> Result<()>
158        + Send
159        + Sync,
160>;