tumo_scope

Crates.iotumo_scope
lib.rstumo_scope
version0.1.0
created_at2026-01-22 20:30:26.406844+00
updated_at2026-01-22 20:30:26.406844+00
descriptionA reactive UI toolkit for building GUI interfaces, inspired by Jetpack Compose.
homepage
repositoryhttps://gitee.com/mindbox/tumo
max_upload_size
id2062567
size63,031
Jeff Feng (monovee)

documentation

README

Scope Framework

A reactive UI toolkit for building GUI interfaces, inspired by Jetpack Compose. Scope provides a declarative syntax and component-based architecture for creating efficient, responsive user interfaces in Rust.

Overview

Scope is a modern UI toolkit that embraces the principles of reactive programming and declarative UI construction. It enables developers to build complex user interfaces with fine-grained state management and efficient re-composition.

Core Concepts

Recomposition Units

Recomposition units are functions that generate UI descriptions. There are three types of composition units, each serving different purposes:

  • Scope: Direct execution units without state reactivity. Type: FnOnce(&mut Cx<'_, E>)
  • Preact: State-reactive units for logic and state management. Type: Fn(&mut Cx<'_, E>) + 'static
  • Proact: State-reactive units that provide post-layout space, similar to Flutter's Silver types. Type: Fn(&mut Cx<'_, E>, f32, f32) + 'static

State Management

Any type can be used as state, internally stored using Rc to persist across recompositions. State comes in four variants:

  • stay: Persists across recompositions, immutable
  • stay_cell: Persists across recompositions, mutable but non-reactive
  • stay_refcell: Persists across recompositions, mutable but non-reactive
  • State: Persists across recompositions, mutable and reactive (triggers recomposition)

Effects

Effects are functions of type FnOnce(&mut E) -> T used for executing side effects. They are triggered based on a rekey value (a u64 type) that identifies the effect's dependencies. This value can be a hash of the dependency content.

Offers

A mechanism for sharing data across multiple levels of recomposition, similar to Flutter's Provider pattern. Offers enable efficient data propagation through the component tree.

Environment

The E type in Cx<'_, E> represents the environment type, with only one instance per Context. Through Cx, you can directly dereference to &E, allowing all scopes read-only access to the environment. In effect functions, you can obtain &mut E and modify the environment. Scope definitions can specify required environment traits using E: Trait bounds.

Architecture

Core Components

  • Context: Runtime context managing component tree lifecycle and state updates
  • Cx<'c, E>: Component execution context providing state management and scope functionality
  • Actor: Component instances with support for Preact and Proact composition

Key Features

  • Fine-grained reactivity: Precise control over when components recompose
  • Efficient state persistence: State persists across recompositions without unnecessary copying
  • Type-safe environment: Compile-time guarantees for environment access
  • Flexible composition: Support for nested scopes and component hierarchies
  • Message passing: Built-in communication between components

Usage Examples

Basic Component

use scope::prelude::*;

struct AppEnv {
    counter: u32,
}

fn my_component(cx: &mut Cx<'_, AppEnv>) {
	println!("print every re-composition: {}", env.counter);
	cx.effect_once(move|env| {
		println!("print once: {}", env.counter);
	});
}

let mut ctx = Context::<AppEnv>::new(my_component);
let mut env = AppEnv { counter: 0 };
ctx.build_root(true, &mut env);

Reactive State

fn counter_component(cx: &mut Cx<'_, AppEnv>) {
    let counter_state = 0.state(cx).bind(cx);
    
    cx.effect(counter_state.peek(), |env| {
        // This effect runs whenever counter_state changes
        env.counter = counter_state.peek();
    });
    
    cx.effect_once(move|env| {
        // Update state
        counter_state.set(counter_state.peek() + 1);
    });
}

Data Sharing with Offers

struct Theme {
    primary_color: Color,
}

impl Offer for Theme {}

fn theme_provider(cx: &mut Cx<'_, AppEnv>) {
    let theme = Theme { primary_color: Color::BLUE };
    theme.offer(cx);
    
    cx.scope(cx.a(), |cx| {
        if let Some(current_theme) = Theme::try_current(cx) {
            // Use the offered theme
        }
    });
}

License

MIT

Commit count: 0

cargo fmt