mirui 0.42.0

A lightweight, no_std ECS-driven UI framework for embedded, desktop, and WebAssembly
Documentation
#![allow(clippy::needless_update)]

extern crate alloc;

use alloc::vec;

use crate::prelude::draw::*;
use crate::prelude::*;
use crate::render::raster::FillRule;
use crate::render::scene::{
    GradientStop, GradientUnits, LinearGradient, Paint, RadialGradient, SpreadMode,
};
use crate::types::Transform;

fn unit_point(x: f32, y: f32) -> mirx::Point {
    Point {
        x: Fixed::from_f32(x),
        y: Fixed::from_f32(y),
    }
    .into()
}

fn unit(v: f32) -> mirx::Fixed {
    Fixed::from_f32(v).into()
}

#[derive(Default)]
pub struct Gradient;

fn circle_path(cx: Fixed, cy: Fixed, r: Fixed) -> Path {
    let k = r * Fixed::from_f32(0.552_284_8);
    let mut path = Path::new();
    path.move_to(Point { x: cx + r, y: cy });
    path.cubic_to(
        Point {
            x: cx + r,
            y: cy + k,
        },
        Point {
            x: cx + k,
            y: cy + r,
        },
        Point { x: cx, y: cy + r },
    );
    path.cubic_to(
        Point {
            x: cx - k,
            y: cy + r,
        },
        Point {
            x: cx - r,
            y: cy + k,
        },
        Point { x: cx - r, y: cy },
    );
    path.cubic_to(
        Point {
            x: cx - r,
            y: cy - k,
        },
        Point {
            x: cx - k,
            y: cy - r,
        },
        Point { x: cx, y: cy - r },
    );
    path.cubic_to(
        Point {
            x: cx + k,
            y: cy - r,
        },
        Point {
            x: cx + r,
            y: cy - k,
        },
        Point { x: cx + r, y: cy },
    );
    path.close();
    path
}

fn gradient_render(
    renderer: &mut dyn Renderer,
    _world: &World,
    _entity: Entity,
    rect: &Rect,
    ctx: &mut ViewCtx,
) {
    let left = Path::rounded_rect(
        rect.x + Fixed::from_int(48),
        rect.y + Fixed::from_int(70),
        Fixed::from_int(160),
        Fixed::from_int(180),
        Fixed::from_int(28),
    );
    let linear = Paint::LinearGradient(LinearGradient {
        start: unit_point(0.0, 0.0),
        end: unit_point(1.0, 1.0),
        stops: alloc::borrow::Cow::Owned(vec![
            GradientStop {
                offset: unit(0.0),
                color: Color::rgb(50, 120, 255).into(),
            },
            GradientStop {
                offset: unit(0.55),
                color: Color::rgb(125, 90, 255).into(),
            },
            GradientStop {
                offset: unit(1.0),
                color: Color::rgb(255, 70, 90).into(),
            },
        ]),
        spread: SpreadMode::Pad,
        units: GradientUnits::ObjectBoundingBox,
        transform: Transform::IDENTITY.into(),
    });
    renderer.draw(
        &DrawCommand::FillPath {
            path: &left,
            transform: ctx.transform,
            paint: &linear,
            opa: 255,
            fill_rule: FillRule::EvenOdd,
        },
        ctx.clip,
    );

    let circle = circle_path(
        rect.x + Fixed::from_int(340),
        rect.y + Fixed::from_int(160),
        Fixed::from_int(86),
    );
    let radial = Paint::RadialGradient(RadialGradient {
        center: unit_point(0.42, 0.38),
        radius: unit(0.7),
        focal: unit_point(0.35, 0.32),
        focal_radius: unit(0.0),
        stops: alloc::borrow::Cow::Owned(vec![
            GradientStop {
                offset: unit(0.0),
                color: Color::rgb(255, 255, 255).into(),
            },
            GradientStop {
                offset: unit(0.42),
                color: Color::rgb(90, 190, 255).into(),
            },
            GradientStop {
                offset: unit(1.0),
                color: Color::rgb(20, 70, 190).into(),
            },
        ]),
        spread: SpreadMode::Pad,
        units: GradientUnits::ObjectBoundingBox,
        transform: Transform::IDENTITY.into(),
    });
    renderer.draw(
        &DrawCommand::FillPath {
            path: &circle,
            transform: ctx.transform,
            paint: &radial,
            opa: 255,
            fill_rule: FillRule::EvenOdd,
        },
        ctx.clip,
    );
}

pub fn gradient_view() -> View {
    View::new("Gradient", 60, gradient_render).with_filter::<Gradient>()
}

#[compose]
pub fn build_widgets() {
    ui! {
        Gradient (grow: 1.0)
    };
}

#[cfg(feature = "std")]
pub fn setup_app<B, F>(app: &mut App<B, F>, parent: Entity)
where
    B: Surface,
    F: RendererFactory<B>,
{
    app.with_widget(gradient_view());
    app.compose(parent, build_widgets);
}