### Using `@` Syntactic Sugar for Concise `rdl!` Widget Composition Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md Introduces the `@` syntactic sugar as a more concise alternative to the verbose `rdl!` macro. This example demonstrates how `@Button { ... }` for struct literals and `@ { ... }` for expressions simplify widget creation and composition, making the code cleaner and easier to read. ```Rust use ribir::prelude::*; fn main() { App::run(fn_widget! { @Button { @ { "0" } } }); } ``` -------------------------------- ### Best practices for Ribir pipe! expressions Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This example illustrates the recommended practice of keeping `pipe!` expressions as small as possible and using `.map()` for subsequent transformations. This approach enhances code clarity, makes the source of changes more apparent, and helps avoid unnecessary dependencies in complex reactive expressions. ```Rust pipe!(*$counter).map(move |counter| { move || { (0..counter).map(move |_| { @Container { margin: EdgeInsets::all(2.), size: Size::new(10., 10.), background: Color::RED } }) } }) ``` ```Rust pipe!{ move || { (0..*$counter).map(move |_| { @Container { margin: EdgeInsets::all(2.), size: Size::new(10., 10.), background: Color::RED } }) } } ``` -------------------------------- ### Rust: Demonstrating State Transitions and Separations with `part_writer` Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust example illustrates how to use `State::part_writer` to create both 'mapped' and 'split' sub-states. It demonstrates the differing notification behaviors for data changes and framework updates when modifications originate from the parent state, a mapped sub-state, or a split sub-state. The `AppCtx::run_until_stalled()` calls are used to force immediate asynchronous pushes for demonstration purposes. ```rust use ribir::prelude::*; struct AppData { count: usize, } let state = State::value(AppData { count: 0 }); let map_count = state.part_writer(PartialId::any(), |d| PartMut::new(&mut d.count)); let split_count = state.part_writer(PartialId::any(), |d| PartMut::new(&mut d.count)); watch!($read(state).count).subscribe(|_| println!("Parent data")); watch!(*$read(map_count)).subscribe(|_| println!("Child(map) data")); watch!(*$read(split_count)).subscribe(|_| println!("Child(split) data")); state .raw_modifies() .filter(|s| s.contains(ModifyEffect::FRAMEWORK)) .subscribe(|_| println!("Parent framework")); map_count .raw_modifies() .filter(|s| s.contains(ModifyEffect::FRAMEWORK)) .subscribe(|_| println!("Child(map) framework")); split_count .raw_modifies() .filter(|s| s.contains(ModifyEffect::FRAMEWORK)) .subscribe(|_| println!("Child(split) framework")); // Modify data through the split sub-state, the data modification push to both the parent and child state subscribers. // But only the split sub-state subscribers are pushed framework notifications. *split_count.write() = 1; AppCtx::run_until_stalled(); // Print: // Parent data // Child(map) data // Child(split) data // Child(split) framework // When data is modified through the parent state, both the data modification and framework notifications are pushed to the subscribers of the parent and child states. However, the split sub-state becomes invalidated. state.write().count = 3; // The push is asynchronous, forcing the push to be sent immediately AppCtx::run_until_stalled(); // Print: // Parent data // Child(map) data // Parent framework // Child(map) framework // Modify data through the map sub-state, the data modification push to both the parent and child state subscribers. *map_count.write() = 2; AppCtx::run_until_stalled(); // Print: // Parent data // Child(map) data // Parent framework // Child(map) framework ``` -------------------------------- ### Run specific Ribir built-in examples Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/try_it.md Commands to execute various pre-built Ribir examples (e.g., counter, storybook, todos) directly from the cloned repository using `cargo run -p`. ```sh cargo run -p counter cargo run -p storybook cargo run -p messages cargo run -p todos cargo run -p wordle_game ``` -------------------------------- ### Run Ribir Storybook Example Source: https://github.com/ribirx/ribir/blob/master/examples/README.md This command executes the `storybook` example project within the Ribir repository. It uses `cargo run` to build and run the specified package, allowing users to view and interact with the UI components. ```sh cargo run -p storybook ``` -------------------------------- ### Define Ribir `Pipe` Stream for Data Reactivity Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust example demonstrates how to create a `Pipe` stream using the `pipe!` macro in Ribir. The `pipe!` macro monitors all `$`-marked states within its expression, automatically recalculating and emitting new values whenever those states change, enabling continuous data updates. ```Rust use ribir::prelude::*; let a = State::value(0); let b = State::value(0); let sum = pipe!(*$read(a) + *$read(b)); ``` -------------------------------- ### Nesting Widgets Declaratively with `rdl!` in Ribir Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md Shows how to compose widgets by nesting them directly within a parent widget's `rdl!` declaration. This example demonstrates creating a `Button` and embedding a string literal as its child, adhering to Ribir's composition format requirements. ```Rust use ribir::prelude::*; fn main() { let counter = fn_widget! { rdl!{ Button { rdl!{ "0" } } } }; App::run(counter); } ``` -------------------------------- ### Clone Ribir repository for examples Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/try_it.md Commands to clone the official Ribir Git repository from GitHub and change the current directory to the `Ribir/Ribir` folder, where built-in examples are located. ```sh git clone git@github.com:RibirX/Ribir.git cd Ribir/Ribir ``` -------------------------------- ### Using rxRust operators with Ribir Pipe chains for efficient updates Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust example demonstrates how to apply `rxRust` operators, such as `distinct_until_changed`, to `Pipe` streams in Ribir. It shows how to filter out redundant UI updates, ensuring that a `Text` widget only re-renders when the computed sum of two `State` variables actually changes, thereby optimizing performance. ```Rust use ribir::prelude::*; fn main() { App::run(fn_widget! { let a = State::value(0); let b = State::value(0); @Column { @Text { text: pipe!($read(a).to_string()) } @Text { text: pipe!($read(b).to_string()) } @Text { text: pipe!((*$read(a) + *$read(b)).to_string()) .transform(|s| s.distinct_until_changed().box_it()), on_tap: move |_| { *$write(a) += 1; *$write(b) -= 1; } } } }); } ``` -------------------------------- ### Implement Counter with Ribir `Compose` Widget Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust example shows how to use the `Compose` trait to create a custom widget from a data structure. It defines a `Counter` struct and implements `Compose` for it, allowing the `Counter` instance itself to be used as a widget. The `compose` method defines the UI (a button) that interacts with the `Counter`'s internal state. ```Rust use ribir::prelude::*; struct Counter(usize); impl Counter { fn increment(&mut self) { self.0 += 1; } } impl Compose for Counter { fn compose(this: impl StateWriter) -> Widget<'static> { button! { on_tap: move |_| $write(this).increment(), @pipe!($read(this).0.to_string()) } .into_widget() } } fn main() { App::run(fn_widget!(Counter(0))); } ``` -------------------------------- ### Define Ribir Widget with Function Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust code defines a `hello_world` function that creates a `Text` widget using its declarative API. It then runs the application using `App::run` with this function. This demonstrates the basic structure for creating and running a Ribir application by defining a widget as a standalone function. ```Rust use ribir::prelude::*; fn hello_world() -> Widget<'static> { let mut text = Text::declarer(); text.with_text("Hello World!"); text.finish().into_widget() } fn main() { App::run(hello_world); } ``` -------------------------------- ### Run Ribir Counter Application Source: https://github.com/ribirx/ribir/blob/master/examples/counter/README.md Commands to compile and run the Ribir counter example application, supporting both desktop and WebAssembly environments. These commands utilize `cargo` for execution. ```sh cargo run -p counter ``` ```sh cargo run-wasm -p counter ``` -------------------------------- ### Ribir Application Main Entry Point Setup in Rust Source: https://github.com/ribirx/ribir/blob/master/docs/en/practice_todos_app/develop_a_todos_app.md This Rust code sets up the main entry point for a Ribir application. It initializes the `Todos` state, configures an auto-save mechanism to persist changes to disk every 5 seconds, and runs the application with `Todos` as the root widget. It demonstrates state management and background task scheduling. ```Rust // main.rs mod todos; mod ui; use ribir::prelude::*; use std::time::Duration; fn main() { let todos = State::value(todos::Todos::load()); // save changes to disk every 5 seconds . let save_todos = todos.clone_reader(); watch!($todos;) .debounce(Duration::from_secs(5), AppCtx::scheduler()) .subscribe(move |_| { if let Err(err) = save_todos.read().save() { log::error!("Save tasks failed: {}", err); } }); App::run(fn_widget! { todos }) } ``` -------------------------------- ### Ribir `$` Syntactic Sugar for State Access Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This section explains the expansion rules for Ribir's `$` syntactic sugar when accessing state. It clarifies how `$counter` expands to `counter.read()`, and how `$counter.write()` and `$counter.silent()` map to their respective `State` methods for mutable access. ```Rust $counter.write() expands to counter.write() $counter.silent() expands to counter.silent() $counter expands to counter.read() ``` -------------------------------- ### Implement Counter with Ribir `watch!` Macro Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust example demonstrates using the `watch!` macro in Ribir to create a reactive counter. It initializes a `State` variable for the count, displays it in an `H1` element, and updates the display whenever the count changes via a `subscribe` callback. An "Increment" button modifies the `count` state. ```Rust use ribir::prelude::*; fn main() { App::run(fn_widget! { let count = State::value(0); let display = @H1 { text: "0" }; watch!(*$read(count)).subscribe(move |v| { $write(display).text = v.to_string().into(); }); @Row { @Button { on_tap: move |_| *$write(count) += 1, @ { "Increment" } } @{ display } } }); } ``` -------------------------------- ### Define Ribir Widget with Closure Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust code shows how to define a Ribir widget using a closure instead of a named function. The `hello_world` closure creates a `Text` widget using `rdl!` and is then passed to `App::run`, illustrating an alternative, more inline approach to widget definition when the widget is not reused. ```Rust use ribir::prelude::*; fn main() { let hello_world = || { rdl!{ Text { text: "Hello World!" } } .into_widget() }; App::run(hello_world); } ``` -------------------------------- ### Run Ribir Messages UI locally Source: https://github.com/ribirx/ribir/blob/master/examples/messages/README.md Command to run the Ribir messages UI example application locally using `cargo run`. ```sh cargo run -p messages ``` -------------------------------- ### Expression-Based Object Creation with `rdl!` Macro Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md Illustrates how `rdl!` can encapsulate any Rust expression, allowing for dynamic object creation based on conditional logic. This feature is particularly useful when dealing with complex or nested compositions. ```Rust use ribir::prelude::*; let _parent = rdl!{ // You can write any expression here, and the result of the expression will be the child if { // ... } else { // ... } }; ``` -------------------------------- ### Simplify Ribir Widget Definition with fn_widget! Macro Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust code demonstrates the use of the `fn_widget!` macro to further simplify the definition of a Ribir widget. It wraps the `rdl!` macro call, providing a more idiomatic and concise way to create function widgets directly within the `App::run` call, reducing boilerplate. ```Rust use ribir::prelude::*; fn main() { App::run(fn_widget! { rdl!{ Text { text: "Hello World!" } } }); } ``` -------------------------------- ### Declarative Object Creation with `rdl!` Macro Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md Shows the basic syntax for declaratively creating objects using `rdl!` with a struct literal. This approach requires the object type to implement the `Declare` trait for proper instantiation. ```Rust rdl! { ObjectType { // Field declarations } } ``` -------------------------------- ### Render dynamic UI with a counter using Ribir Pipe Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust code demonstrates how to dynamically render a variable number of red square widgets based on a counter's value. It uses Ribir's `App::run_with_data` and the `pipe!` macro to reactively update the UI as the counter increments, showcasing a continuously changing widget structure. ```Rust use ribir::prelude::*; fn main() { App::run_with_data( || Stateful::new(0), move |cnt: &'static Stateful| { row! { @Button { on_tap: move |_| *$write(cnt) += 1, @ { "Increment" } } @ { pipe!(*$read(cnt)).map(move |cnt| { (0..cnt).map(move |_| { @Container { margin: EdgeInsets::all(2.), size: Size::new(10., 10.), background: Color::RED } }) }) } } }, ); } ``` -------------------------------- ### Implement a Counter in Ribir Source: https://github.com/ribirx/ribir/blob/master/README.md This example demonstrates how to create a simple counter application using the Ribir GUI framework in Rust, showcasing both the declarative macro approach and the direct API usage. ```Rust use ribir::prelude::*; fn main() { App::run_with_data( || Stateful::new(0), move |cnt: &'static Stateful| { button! { h_align: HAlign::Center, v_align: VAlign::Center, on_tap: move |_| *$write(cnt) += 1, @pipe!($read(cnt).to_string()) } } ); } ``` ```Rust use ribir::prelude::*; fn main() { App::run_with_data( || Stateful::new(0), move |cnt: &'static Stateful| { let c_cnt = cnt.clone_writer(); let mut btn = Button::declarer(); btn .on_tap(move |_| *c_cnt.write() += 1) .with_h_align(HAlign::Center) .with_v_align(VAlign::Center); btn.finish().with_child(pipe!($read(cnt).to_string())) }); } ``` -------------------------------- ### Ribir State Automatic Sharing in `move` Closures Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This example illustrates how Ribir's `$` syntactic sugar automatically handles state sharing within `move` closures. It demonstrates that using `$` inside a `move` closure implicitly clones the state's writer, simplifying direct state manipulation without manual cloning. ```Rust move |_| *$count.write() += 1 ``` ```Rust { let count = count.clone_writer(); move |_| *count.write() += 1 } ``` -------------------------------- ### Further Simplify Ribir Widget with Declarative Macro Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust code illustrates the ultimate simplification for defining a Ribir `Text` widget using a dedicated declarative macro (`text!`). This approach combines the `fn_widget!` and `rdl!` functionalities into a single, highly concise macro call, making widget creation very streamlined and readable. ```Rust use ribir::prelude::*; fn main() { App::run(text! { text: "Hello World!"}); } ``` -------------------------------- ### Simplify Ribir Widget Definition with rdl! Macro Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust code demonstrates how to simplify the creation of a `Text` widget using the `rdl!` macro. It shows a more concise way to declare widgets in Ribir, converting the `rdl!` output into a `Widget` type. This macro streamlines the declarative object creation process. ```Rust use ribir::prelude::*; fn hello_world() -> Widget<'static> { rdl!{ Text { text: "Hello World!" } } .into_widget() } ``` -------------------------------- ### Declare Ribir Margin Widget Explicitly Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This snippet demonstrates the explicit declaration of a `Margin` widget as a parent to a `Text` widget in Ribir. It shows how to apply a 10-pixel margin around the "Hello World!" text using `EdgeInsets::all(10.)`. This approach clearly defines the widget hierarchy. ```Rust use ribir::prelude::*; fn main() { App::run(fn_widget! { // Declare `Margin` as the parent of `Text` @Margin { margin: EdgeInsets::all(10.), @Text { text: "Hello World!" } } }); } ``` -------------------------------- ### Ribir Todo App Main Entry Point and Persistence Source: https://github.com/ribirx/ribir/blob/master/docs/zh/practice_todos_app/develop_a_todos_app.md Initializes the Ribir application, loads existing todos, and sets up a debounced mechanism to automatically save todo changes to disk every 5 seconds. It uses `State::value` for reactive state management and `App::run` to start the UI. ```Rust // main.rs mod todos; mod ui; use ribir::prelude::*; use std::time::Duration; fn main() { let todos = State::value(todos::Todos::load()); // save changes to disk every 5 seconds . let save_todos = todos.clone_reader(); watch!($todos;) .debounce(Duration::from_secs(5), AppCtx::scheduler()) .subscribe(move |_| { if let Err(err) = save_todos.read().save() { log::error!("Save tasks failed: {}", err); } }); App::run(fn_widget! { todos }) } ``` -------------------------------- ### Dynamically Access and Modify Built-in Widget Properties in Ribir Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This example demonstrates how to access and modify built-in widget properties, like `margin`, even when they are not explicitly declared during widget creation. Ribir automatically initializes these properties with default values, allowing dynamic modification after the widget is instantiated. This showcases Ribir's flexible property access. ```Rust use ribir::prelude::*; fn main() { App::run(fn_widget! { // `margin` is not declared let mut hello_world = @Text { text: "Hello World!" }; // But you can still access the `margin` field, // It's created with default value when you use it. *$write(hello_world.margin()) = EdgeInsets::all(10.); hello_world }); } ``` -------------------------------- ### Implement Ribir Counter with Mixed API and Macros Source: https://github.com/ribirx/ribir/blob/master/docs/en/understanding_ribir/without_dsl.md Illustrates a counter example in Ribir, combining direct API usage for button and label creation with the `@` syntax for layout. It also shows how to use `$` for state management and property initialization to avoid cloning. ```rust use ribir::prelude::*; let counter = fn_widget! { let cnt = Stateful::new(0); let mut btn = Button::declarer(); btn.on_tap(move |_| *$write(cnt) += 1); let btn = btn.finish().with_child("Inc"); let mut label = H1::declarer(); label.with_text(pipe!($read(cnt).to_string())); let label = label.finish(); @Row { align_items: Align::Center, @ { btn } @ { label } } }; ``` -------------------------------- ### WebAssembly Module Initialization and Startup Source: https://github.com/ribirx/ribir/blob/master/cli/template/index.html This JavaScript code handles the asynchronous initialization and execution of a WebAssembly module. It imports the 'init' and 'run' functions from 'web_wasm.js', awaits the initialization, and then calls the 'run' function to start the WASM application. ```javascript import init, { run } from "./web_wasm.js"; async function startup() { await init(); run(); } await startup() ``` -------------------------------- ### Implement Reactive Counter with Ribir State Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust code demonstrates how to build a reactive counter in Ribir. It shows the three essential steps: creating a `State` variable, modifying its value on a button tap using `on_tap` and `$write`, and dynamically displaying the updated state using a `pipe!` macro. ```Rust use ribir::prelude::*; fn main() { App::run(fn_widget! { // Change 1: Create a state using `State::value` let count = State::value(0); @Button { // Change 2: Modify the state on tap on_tap: move |_| *$write(count) += 1, // Change 3: Display data using state and keep the view updated. // For macros or function calls, you can omit the curly braces after `@` @ pipe!($read(count).to_string()) } }); } ``` -------------------------------- ### Demonstrating Ribir State Transformation and Separation Source: https://github.com/ribirx/ribir/blob/master/docs/zh/get_started/quick_start.md This Rust code example illustrates the concepts of state transformation (`part_writer`) and separation in Ribir. It shows how a parent state (`state`) can be transformed into a child state (`map_count`) and separated into another child state (`split_count`). The example demonstrates how modifications through different states trigger distinct update notifications (data vs. framework updates) for parent and child state subscribers, highlighting the specific behaviors of transformation and separation. It also notes the use of `AppCtx::run_until_stalled()` for immediate asynchronous update flushing, which is for demonstration purposes only and not recommended for production code. ```rust use ribir::prelude::*; struct AppData { count: usize, } let state = State::value(AppData { count: 0 }); let map_count = state.part_writer(PartialId::any(), |d| PartMut::new(&mut d.count)); let split_count = state.part_writer(PartialId::any(), |d| PartMut::new(&mut d.count)); watch!($read(state).count).subscribe(|_| println!("父状态数据")); watch!(*$read(map_count)).subscribe(|_| println!("子状态(转换)数据")); watch!(*$read(split_count)).subscribe(|_| println!("子状态(分离)数据")); state .raw_modifies() .filter(|s| s.contains(ModifyEffect::FRAMEWORK)) .subscribe(|_| println!("父状态 框架")); map_count .raw_modifies() .filter(|s| s.contains(ModifyEffect::FRAMEWORK)) .subscribe(|_| println!("子状态(转换)框架")); split_count .raw_modifies() .filter(|s| s.contains(ModifyEffect::FRAMEWORK)) .subscribe(|_| println!("子状态(分离)框架")); // 通过分离子状态修改数据,父子状态的订阅者都会被推送数据通知, // 只有分离子状态的订阅者被推送框架通知 *split_count.write() = 1; // 推送是是异步的,强制推送立即发出 AppCtx::run_until_stalled(); // 打印内容: // 父状态数据 // 子状态(转换)数据 // 子状态(分离)数据 // 子状态(分离)框架 // 通过父状态修改数据, 分离状态会失效,父子状态的依赖都会被推送 state.write().count = 3; AppCtx::run_until_stalled(); // 打印内容: // 父状态数据 // 子状态(转换)数据 // 父状态 框架 // 子状态(转换)框架 // 通过转换子状态修改数据,父子状态的依赖都会被推送 *map_count.write() = 2; AppCtx::run_until_stalled(); // 打印内容: // 父状态数据 // 子状态(转换)数据 // 父状态 框架 // 子状态(转换)框架 ``` -------------------------------- ### Run Ribir Messages UI in web Source: https://github.com/ribirx/ribir/blob/master/examples/messages/README.md Command to run the Ribir messages UI example application in a web browser using `cargo run-wasm`. ```sh cargo run-wasm -p messages ``` -------------------------------- ### Ribir: Using @ Syntax Sugar for Basic Widget Definition Source: https://github.com/ribirx/ribir/blob/master/docs/zh/get_started/quick_start.md This example demonstrates Ribir's @ syntax sugar, which provides a concise way to define widgets, replacing the more verbose rdl!{} macro. It shows a simple Button widget displaying '0'. ```Rust use ribir::prelude::*; fn main() { App::run(fn_widget! { @Button { @ { "0" } } }); } ``` -------------------------------- ### Composing Existing Widgets with `rdl!` Macro in Ribir Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md Explains how to compose a child widget with a parent widget that has already been created and assigned to a variable. It utilizes the `rdl!{ ($variable) { ... } }` syntax, indicating that the parent is an existing instance rather than a new type. ```Rust use ribir::prelude::*; fn main() { let counter = fn_widget! { let btn = rdl! { Button {} }; rdl!{ (btn) { rdl!{ "0" } } } }; App::run(counter); } ``` -------------------------------- ### Mixed API and Macro Usage for Ribir Counter Widget Source: https://github.com/ribirx/ribir/blob/master/docs/zh/understanding_ribir/without_dsl.md Presents an example of a counter widget that combines direct API calls for button and text creation with Ribir's macros (`$`, `@`) for state management and layout. ```rust use ribir::prelude::*; let counter = fn_widget! { let cnt = Stateful::new(0); let mut btn = Button::declarer(); btn.on_tap(move |_| *$write(cnt) += 1); let btn = btn.finish().with_child("Inc"); let mut label = H1::declarer(); label.with_text(pipe!($read(cnt).to_string())); let label = label.finish(); @Row { align_items: Align::Center, @ { btn } @ { label } } }; ``` -------------------------------- ### Ribir Stateful to Stateless Conversion Example (Rust) Source: https://github.com/ribirx/ribir/blob/master/docs/en/introduction.md This Rust example illustrates Ribir's non-intrusive state management. A `Stateful` variable `show_hi` is used to control the `visible` property of a `Text` widget via the `pipe!` macro. If the state has no write source, Ribir optimizes it by converting it to stateless, resulting in a simple static view. ```Rust use ribir::prelude::*; fn_widget!{ let show_hi = Stateful::new(true); @Text { visible: pipe!(*$read(show_hi)), text: "Hello world!" } }; ``` -------------------------------- ### Initialize Ribir Widget Property with `Pipe` Stream Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Ribir snippet shows how to initialize a widget property, such as `text`, with a `Pipe` stream. By assigning a `pipe!` macro output to a property, the property will automatically update whenever the underlying state changes, ensuring the view remains synchronized with the data. ```Rust @Text { text: pipe!($count.to_string()) } ``` -------------------------------- ### Apply Margin Directly to Ribir Text Widget Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This snippet illustrates Ribir's simplified syntax for applying built-in widget properties. Instead of explicitly declaring a `Margin` widget, the `margin` property is directly applied to the `Text` widget, achieving the same visual effect with more concise code. This highlights Ribir's implicit widget creation. ```Rust use ribir::prelude::*; fn main() { App::run(fn_widget! { // Use the `Margin::margin` field directly in `Text` @Text { margin: EdgeInsets::all(10.), text: "Hello World!" } }); } ``` -------------------------------- ### Ribir Todo App Data Model and Persistence (todos.rs) Source: https://github.com/ribirx/ribir/blob/master/docs/en/practice_todos_app/develop_a_todos_app.md Defines the data structures for `Todos`, `Task`, and `TaskId` using `serde` for serialization. Implements methods for managing tasks (add, remove, get), loading todos from a JSON file, and saving them to disk. ```Rust // todos.rs use serde::{Deserialize, Serialize}; use std::{ collections::BTreeMap, fs::File, io::{self, BufWriter, Write}, }; #[derive(Debug, Serialize, Deserialize)] pub struct Todos { tasks: BTreeMap, next_id: TaskId, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Task { id: TaskId, pub complete: bool, pub label: String, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct TaskId(usize); impl Todos { pub fn new_task(&mut self, label: String) -> TaskId { let id = self.next_id; self.next_id = self.next_id.next(); self.tasks.insert(id, Task { id, label, complete: false }); id } pub fn remove(&mut self, id: TaskId) { self.tasks.remove(&id); } pub fn get_task(&self, id: TaskId) -> Option<&Task> { self.tasks.get(&id) } pub fn get_task_mut(&mut self, id: TaskId) -> Option<&mut Task> { self.tasks.get_mut(&id) } pub fn all_tasks(&self) -> impl Iterator + '_ { self.tasks.keys().copied() } } impl Task { pub fn id(&self) -> TaskId { self.id } } impl Todos { pub fn load() -> Self { std::fs::read(Self::store_path()) .map(|v| serde_json::from_slice(v.as_slice()).unwrap()) .unwrap_or_else(|_| Todos { tasks: BTreeMap::new(), next_id: TaskId(0), }) } pub fn save(&self) -> Result<(), io::Error> { let file = File::create(Self::store_path())?; let mut writer = BufWriter::new(file); serde_json::to_writer(&mut writer, self)?; writer.flush()?; Ok(()) } fn store_path() -> std::path::PathBuf { std::env::temp_dir().join("ribir_todos.json") } } impl TaskId { pub fn next(&self) -> Self { Self(self.0 + 1) } } ``` -------------------------------- ### Rust: Creating Read-Only Sub-States with `part_reader` Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust snippet shows how to create a read-only sub-state using `State::part_reader`. It highlights that while a `part_reader` allows creating a read-only view, Ribir does not provide a `split_reader` because separating a read-only sub-state offers no distinct advantage over simply converting it. ```rust let count_reader = state.part_reader(|d| &d.count); ``` -------------------------------- ### Create Ribir Text and Icon Buttons with Child Widgets Source: https://github.com/ribirx/ribir/blob/master/docs/en/understanding_ribir/without_dsl.md Demonstrates how to create a text button and an icon button in Ribir using the `Button::declarer()` and `with_child` methods. This example highlights how child widgets enable flexible composition, avoiding unnecessary property allocations. ```rust use ribir::prelude::*; let text_btn = Button::declarer() .finish() .with_child("Text Button"); let icon_btn = Button::declarer() .finish() .with_child(Icon.with_child(named_svgs::get_or_default("search"))); ``` -------------------------------- ### Ribir Syntactic Sugar Priority with External Macros Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This snippet clarifies the priority of Ribir's `@` and `$` syntactic sugars. It shows that when these symbols are used inside external macros, their semantics are determined by the macro's implementation, overriding Ribir's default syntactic sugar behavior. ```Rust use ribir::prelude::*; fn_widget!{ user_macro! { // `@` is not a syntactic sugar here, its semantics // depend on the implementation of `user_macro!` @Button { ... } } } ``` -------------------------------- ### Implementing `Declare` Trait for `rdl!` Objects in Ribir Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md Demonstrates how to implement the `Declare` trait for a custom struct (`Counter`) to enable declarative object creation with `rdl!`. It illustrates the use of `#[derive(Declare)]` and `#[declare(default = ...)]` for setting default field values. ```Rust use ribir::prelude::*; #[derive(Declare)] pub struct Counter { #[declare(default = 1usize)] count: usize, } fn use_rdl() { let _ = rdl!{ Counter { } }; } ``` -------------------------------- ### Manage Ribir `watch!` Subscription Lifecycle for External State Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust function `show_name` illustrates how to manually unsubscribe from a `watch!` subscription when the widget's lifecycle is shorter than the observed state. It updates a `Text` widget based on an external `State` and ensures the subscription is disposed when the widget is destroyed to prevent memory leaks. ```Rust use ribir::prelude::*; fn show_name(name: State) -> Widget<'static> { fn_widget!{ let mut text = @Text { text: "Hi, Guest!" }; let u = watch!($read(name).to_string()).subscribe(move |name| { $write(text).text = format!("Hi, {}!", name).into(); }); // `name` is a shareable state that can be held by other people, // making its lifecycle longer than that of the widget // so we need to unsubscribe when the widget is destroyed. @(text) { on_disposed: move |_| u.unsubscribe() } } .into_widget() } ``` -------------------------------- ### Ribir Todo App Data Models and Persistence Logic Source: https://github.com/ribirx/ribir/blob/master/docs/zh/practice_todos_app/develop_a_todos_app.md Defines the data structures for `Todos`, `Task`, and `TaskId` using `serde` for serialization/deserialization. It includes methods for managing tasks (add, remove, get), loading todos from a file, and saving them to a temporary file. ```Rust // todos.rs use serde::{Deserialize, Serialize}; use std::{ collections::BTreeMap, fs::File, io::{self, BufWriter, Write}, }; #[derive(Debug, Serialize, Deserialize)] pub struct Todos { tasks: BTreeMap, next_id: TaskId, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Task { id: TaskId, pub complete: bool, pub label: String, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct TaskId(usize); impl Todos { pub fn new_task(&mut self, label: String) -> TaskId { let id = self.next_id; self.next_id = self.next_id.next(); self.tasks.insert(id, Task { id, label, complete: false }); id } pub fn remove(&mut self, id: TaskId) { self.tasks.remove(&id); } pub fn get_task(&self, id: TaskId) -> Option<&Task> { self.tasks.get(&id) } pub fn get_task_mut(&mut self, id: TaskId) -> Option<&mut Task> { self.tasks.get_mut(&id) } pub fn all_tasks(&self) -> impl Iterator + '_ { self.tasks.keys().copied() } } impl Task { pub fn id(&self) -> TaskId { self.id } } impl Todos { pub fn load() -> Self { std::fs::read(Self::store_path()) .map(|v| serde_json::from_slice(v.as_slice()).unwrap()) .unwrap_or_else(|_| Todos { tasks: BTreeMap::new(), next_id: TaskId(0), }) } pub fn save(&self) -> Result<(), io::Error> { let file = File::create(Self::store_path())?; let mut writer = BufWriter::new(file); serde_json::to_writer(&mut writer, self)?; writer.flush()?; Ok(()) } fn store_path() -> std::path::PathBuf { std::env::temp_dir().join("ribir_todos.json") } } impl TaskId { pub fn next(&self) -> Self { Self(self.0 + 1) } } ``` -------------------------------- ### Create Radio Widget using Declare Trait Source: https://github.com/ribirx/ribir/blob/master/docs/en/understanding_ribir/without_dsl.md This example illustrates the recommended way to create Ribir widgets using the `Declare` trait, which offers a more comprehensive initialization API. It shows how to configure properties like `selected` and attach event handlers (`on_tap`) before finalizing the widget into a `FatObj>`. ```Rust use ribir::prelude::*; let mut btn = Radio::declarer(); btn.with_selected(true).on_tap(|_| println!("Radio clicked")); let btn: FatObj> = btn.finish(); ``` -------------------------------- ### Ribir: Dynamically Rendering Widgets with Pipe and map Source: https://github.com/ribirx/ribir/blob/master/docs/zh/get_started/quick_start.md This example illustrates how to dynamically render a collection of widgets based on a reactive state. It uses pipe!(*$read(cnt)).map(...) to transform the counter's value into an iterable of Container widgets, effectively displaying a number of red boxes corresponding to the counter's value, updating reactively. ```Rust use ribir::prelude::*; fn main() { App::run_with_data( || Stateful::new(0), move |cnt: &'static Stateful| { row! { @Button { on_tap: move |_| *$write(cnt) += 1, @ { "Increment" } } @ { pipe!(*$read(cnt)).map(move |cnt| { (0..cnt).map(move |_| { @Container { margin: EdgeInsets::all(2.), size: Size::new(10., 10.), background: Color::RED } }) }) } } } ); } ``` -------------------------------- ### Create new Ribir Rust project Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/try_it.md Commands to initialize a new Rust project named `ribir-hello-world` and navigate into its directory, preparing for Ribir integration. ```sh cargo new ribir-hello-world cd ribir-hello-world ``` -------------------------------- ### Run the Ribir Hello World application Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/try_it.md Command to compile and execute the Ribir application from the project root, displaying the 'Hello World!' output. ```sh cargo run ``` -------------------------------- ### Prevent Circular References with Ribir `watch!` Manual Unsubscribe Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/quick_start.md This Rust example demonstrates a scenario where a `watch!` subscription's callback modifies the very state it's observing, creating a circular reference. To avoid a memory leak, manual unsubscription is required. The code ensures an `even_num` state remains even by incrementing it if it becomes odd. ```Rust use ribir::prelude::*; let even_num = State::value(0); // Respond to changes in even_num, ensure it is even. // If even_num is odd, add 1 to make it even let u = watch!(*$read(even_num)).subscribe(move |v| { if v % 2 == 1 { *even_num.write() = v + 1; } }); // The following code needs to be called at the right time, otherwise it will result in circular references u.unsubscribe() ``` -------------------------------- ### Implement Hello World in Ribir (Rust) Source: https://github.com/ribirx/ribir/blob/master/docs/en/get_started/try_it.md Rust code for `src/main.rs` demonstrating a minimal Ribir application. It imports Ribir's prelude and uses `App::run` with a `text!` macro to display 'Hello World!'. ```rust // main.rs use ribir::prelude::*; fn main() { App::run(text! { text: "Hello World!" }); } ``` -------------------------------- ### Ribir Todo App Main Entry Point (main.rs) Source: https://github.com/ribirx/ribir/blob/master/docs/en/practice_todos_app/develop_a_todos_app.md Initializes the Ribir application, loads existing todos, and sets up a 5-second debounced auto-save mechanism for the todo list. This file defines the `main` function and integrates the `todos` and `ui` modules. ```Rust // main.rs mod todos; mod ui; use ribir::prelude::*; use std::time::Duration; fn main() { let todos = State::value(todos::Todos::load()); // save changes to disk every 5 seconds . let save_todos = todos.clone_reader(); watch!($todos;) .debounce(Duration::from_secs(5), AppCtx::scheduler()) .subscribe(move |_| { if let Err(err) = save_todos.read().save() { log::error!("Save tasks failed: {}", err); } }); App::run(fn_widget! { todos }) } ``` -------------------------------- ### Ribir Widget Initialization with Declare, FatObj, and State Source: https://github.com/ribirx/ribir/blob/master/docs/zh/understanding_ribir/without_dsl.md Demonstrates the full initialization of a Ribir widget using `Declare`, resulting in a `FatObj>`. It also shows how to use `watch!` to subscribe to state changes. ```rust use ribir::prelude::*; let mut radio = Radio::declarer(); // 我们可以使用内建能力 radio.on_tap(|_| println!("taped!")); let radio: FatObj> = radio.finish(); watch!($read(radio).selected) .subscribe(|selected| println!("The radio state change to {selected}")); ``` -------------------------------- ### Direct Instantiation of Ribir Radio Widget Source: https://github.com/ribirx/ribir/blob/master/docs/zh/understanding_ribir/without_dsl.md Demonstrates how to create an instance of the `Radio` widget directly using its struct constructor, setting its `selected` state and `value`. ```rust use ribir::prelude::*; let radio = Radio { selected: true, value: Box::new(1.) }; ``` -------------------------------- ### Run Wordle Game Locally with Cargo Source: https://github.com/ribirx/ribir/blob/master/examples/wordle_game/README.md This snippet demonstrates how to compile and run the Wordle game application directly on your local machine using the `cargo run` command, targeting the `wordle_game` package. ```sh cargo run -p wordle_game ``` -------------------------------- ### Instantiate Radio Widget Directly Source: https://github.com/ribirx/ribir/blob/master/docs/en/understanding_ribir/without_dsl.md This snippet shows how to create an instance of the `Radio` struct directly, just like any other Rust struct. It initializes a `Radio` with `selected` set to `true` and a `value` of type `f64`, demonstrating basic object creation. ```Rust use ribir::prelude::*; let radio = Radio { selected: true, value: Box::new(1.) }; ``` -------------------------------- ### Extending Ribir Widget with FatObj for Event Handling Source: https://github.com/ribirx/ribir/blob/master/docs/zh/understanding_ribir/without_dsl.md Shows how to wrap a `Radio` widget with `FatObj` to add built-in capabilities like event listeners, specifically `on_tap`. ```rust use ribir::prelude::*; let radio = Radio { selected: true, value: Box::new(1.) }; let radio = FatObj::new(radio) .on_tap(|_| println!("Radio tapped")); ```