ribir_core 0.4.0-alpha.55

A non-intrusive declarative GUI framework, to build modern native/wasm cross-platform applications.
Documentation
//! # The `Declare` Macro System
//!
//! The `#[declare]` macro is the backbone of Ribir's widget system. It
//! generates the builder pattern implementation required to construct widgets
//! in a declarative style.
//!
//! This module contains the helper types used by the generated code.
//!
//! ## Core Concepts
//!
//! When you annotate a struct with `#[derive(Declare)]` or `#[declare]`, the
//! macro generates:
//! 1. A **Builder Struct** (e.g., `ButtonDeclarer` for `Button`).
//! 2. Implementations of [`Declare`] and [`ObjDeclarer`].
//! 3. **Setter Methods** for each field, enabling the chaining syntax.
//! 4. Integration with `FatObj` for built-in widget features, unless disabled.
//!
//! ## `#[declare]` vs `#[derive(Declare)]`
//!
//! There are two ways to use the macro:
//!
//! 1. **`#[declare]` (Attribute Macro)**: This is the modern and preferred
//!    style. It supports all features, including `simple` and `stateless`
//!    modes.
//! 2. **`#[derive(Declare)]` (Derive Macro)**: Use this only when you need
//!    compatibility with other macros that might conflict with attribute
//!    macros, or when you specifically want to use the derive syntax. Field
//!    attributes like `#[declare(...)]` work the same way in both cases.
//!
//! ## Field Attributes
//!
//! The macro supports several field-level attributes to customize the builder
//! behavior:
//!
//! ### `#[declare(default = ...)]`
//! Specifies a custom default value expression for the field.
//!
//! ### `#[declare(skip)]`
//! Skips generating a setter for this field. The field relies on `Default` or
//! `#[declare(default = ...)]`.
//!
//! ### `#[declare(strict)]`
//! By default, setters are generic and accept `impl Into<FieldType>` or `Pipe`.
//! Adding `strict` restricts the setter to accept only the exact field type.
//!
//! ### `#[declare(event = ...)]`
//! Enables two-way data binding. This tells the builder how to sync values back
//! from events.
//! - `event = EventType`: Uses `Into` to convert event data to the field type.
//! - `event = EventType.field`: Binds to a specific field.
//! - `event = EventType.method()`: Binds via a method call.
//!
//! ### `#[declare(setter = ...)]`
//! Specifies the method to use for setting the field value on the widget,
//! instead of direct field assignment. This is useful when the field is private
//! or requires special logic during setting.
//! - `setter = method_name`: Calls `widget.method_name(value)`.
//! - `setter = method_name(Type)`: The `method_name` accepts a value of `Type`
//!   and converts it to the field's type.
//!
//! ### `#[declare(custom)]`
//! Tags a field to indicate you will implement a custom setter manually.
//!
//! ## Struct Attributes
//!
//! ### `#[declare(validate)]` or `#[declare(validate = method_name)]`
//!
//! When present, the builder calls the specified validation method before
//! building.
//! - `#[declare(validate)]`: Calls `self.declare_validate()`. you must
//!   implement this method.
//! - `#[declare(validate = method_name)]`: Calls `self.method_name()`.
//!
//! ### `#[declare(simple)]`
//! Generates a lightweight builder without the `FatObj` wrapper.
//!
//! - **Use case**: For helper widgets, internal structures, or when you don't
//!   need universal widget attributes (like `margin`, `on_click`, etc.).
//! - **Outcome**: The builder returns `Stateful<T>` by default, or `T` if
//!   combined with `stateless` or if the struct is empty.
//! - **Fields**: Setters accept static values (no pipes).
//!
//! ### `#[declare(stateless)]`
//! Optimizes the widget by removing the internal `Stateful` wrapper, treating
//! the widget as immutable after creation.
//!
//! - **Use case**: For widgets that perform a one-time build and don't need to
//!   update their internal fields later (e.g., purely specific layout
//!   structures).
//! - **Outcome**: Returns `FatObj<T>` (supporting standard attributes) instead
//!   of `FatObj<Stateful<T>>`.
//! - **Fields**: Setters accept static values (no pipes).
//!
//! ## Example
//!
//! ```ignore
//! #[declare]
//! pub struct MyWidget {
//!     #[declare(default = 1.0)]
//!     opacity: f32,
//!     #[declare(event = ValueChanged)]
//!     value: TwoWay<f32>,
//! }
//! ```

use std::convert::Infallible;

use rxrust::observable::boxed::LocalBoxedObservable;

use crate::prelude::*;

/// Trait used to create a widget declarer that can interact with the `BuildCtx`
/// to create a widget.
///
/// This is the entry point for the builder pattern generated by `#[declare]`.
pub trait Declare {
  type Builder: ObjDeclarer;
  fn declarer() -> Self::Builder;
}

/// An object declarer is a type that can be used to create a object with the
/// given context.
pub trait ObjDeclarer {
  type Target;
  /// Finish the object creation with the given context.
  fn finish(self) -> Self::Target;
}

/// A enum that represents a value that can be either a constant value or a
/// pipe.
///
/// This is the underlying storage for most fields in a `#[declare]` builder.
/// It allows a field to be initialized with receiving a static value,
/// or a dynamic stream (Pipe) that updates the field automatically.
pub enum PipeValue<V> {
  Value(V),
  Pipe { init_value: V, pipe: Pipe<V> },
}

pub type ValueStream<V> = LocalBoxedObservable<'static, V, Infallible>;

impl<V: 'static> PipeValue<V> {
  pub fn unzip(self) -> (V, Option<ValueStream<V>>) {
    match self {
      Self::Value(v) => (v, None),
      Self::Pipe { init_value, pipe } => {
        let pipe = pipe.with_effect(ModifyEffect::DATA);
        (init_value, Some(pipe.into_observable()))
      }
    }
  }

  pub fn map<F, U: 'static>(self, f: F) -> PipeValue<U>
  where
    F: Fn(V) -> U + 'static,
  {
    match self {
      Self::Value(v) => PipeValue::Value(f(v)),
      Self::Pipe { init_value, pipe } => {
        PipeValue::Pipe { init_value: f(init_value), pipe: pipe.map(f) }
      }
    }
  }
}

impl<T: Default> Default for PipeValue<T> {
  fn default() -> Self { Self::Value(T::default()) }
}

/// Helper type for implementing `RInto` traits to support flexible builder
/// arguments.
///
/// This allows setters to accept both `V` and `Pipe<V>` by utilizing the logic
/// in `RFrom`.
pub struct ValueKind<K: ?Sized>(PhantomData<fn() -> K>);
impl<T: RInto<V, K>, V, K: ?Sized> RFrom<T, ValueKind<K>> for PipeValue<V> {
  fn r_from(value: T) -> Self { Self::Value(value.r_into()) }
}

impl<P, V, K: ?Sized + 'static> RFrom<Pipe<P>, Pipe<fn() -> K>> for PipeValue<V>
where
  V: Default + 'static,
  P: RInto<V, K> + 'static,
{
  fn r_from(value: Pipe<P>) -> Self {
    let pipe = value.transform(|stream| stream.map(RInto::r_into));
    Self::Pipe { init_value: V::default(), pipe }
  }
}

/// Two-way binding wrapper for automatic read/write synchronization.
///
/// When passed to a field marked with `#[declare(event = ...)]` in the
/// widget macro, the builder auto-generates logic to synchronize changes.
///
/// The `event` attribute supports several syntaxes to define how values flow
/// back:
/// - `event = EventType`: Uses `Into` to convert event data to the field type.
/// - `event = EventType.field`: Extracts a field from the event data.
/// - `event = EventType.method()`: Calls a method on the event data.
///
/// # Example
/// ```ignore
/// #[declare(event = SliderChanged)]
/// pub struct Slider {
///   #[declare(convert = output.value)]
///   pub value: f32,
/// }
///
/// // Usage in `fn_widget!`:
/// @Slider {
///   // Binds `state` to `value`.
///   // When `state` changes, `value` updates.
///   // When `Slider` emits `SliderChanged`, `state` is updated with the new value.
///   value: TwoWay::new(state)
/// }
/// ```
pub struct TwoWay<W>(pub W);

impl<W> TwoWay<W> {
  pub fn new(writer: W) -> Self { Self(writer) }
}

/// Value type for fields with two-way binding support.
///
/// Used for fields with `#[declare(event = ...)]` attribute.
/// It can hold either a standard `PipeValue` (one-way or static)
/// or a `TwoWay` writer that enables automatic write-back on events.
pub enum TwoWayValue<V> {
  /// Normal value or pipe (no write-back)
  Pipe(PipeValue<V>),
  /// Two-way binding with boxed writer for event binding
  TwoWay(Box<dyn StateWriter<Value = V>>),
}

impl<V: Default> Default for TwoWayValue<V> {
  fn default() -> Self { Self::Pipe(PipeValue::default()) }
}

/// Kind marker for TwoWayValue value conversion.
pub struct TwoWayValueKind<K: ?Sized>(PhantomData<fn() -> K>);

impl<T: RInto<V, K>, V, K: ?Sized> RFrom<T, TwoWayValueKind<K>> for TwoWayValue<V> {
  fn r_from(value: T) -> Self { Self::Pipe(PipeValue::Value(value.r_into())) }
}

impl<P, V, K: ?Sized + 'static> RFrom<Pipe<P>, Pipe<fn() -> K>> for TwoWayValue<V>
where
  V: Default + 'static,
  P: RInto<V, K> + 'static,
{
  fn r_from(value: Pipe<P>) -> Self { Self::Pipe(value.r_into()) }
}

/// Kind marker for TwoWay conversion from `TwoWay<W>` to `TwoWayValue<V>`.
pub struct TwoWayKind;

impl<W, V> RFrom<TwoWay<W>, TwoWayKind> for TwoWayValue<V>
where
  W: StateWriter<Value = V> + 'static,
  V: Clone + 'static,
{
  fn r_from(tw: TwoWay<W>) -> Self { Self::TwoWay(tw.0.clone_boxed_writer()) }
}