fui-rs 0.2.3

Retained-mode Rust UI SDK for EffinDOM
Documentation
use crate::controls::DropdownSizing;
use crate::ffi::{AlignItems, JustifyContent, Unit};
use crate::node::{flex_box, svg, FlexBox, SvgNode};
use crate::theme::Theme;
use std::rc::Rc;

const DROPDOWN_CHEVRON_COLLAPSED_SVG: &str = "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M3 4.5 6 7.5 9 4.5' fill='none' stroke='%23000000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>";
const DROPDOWN_CHEVRON_EXPANDED_SVG: &str = "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M3 7.5 6 4.5 9 7.5' fill='none' stroke='%23000000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>";
const DEFAULT_DROPDOWN_CHEVRON_ICON_SIZE: f32 = 12.0;

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct DropdownChevronMetrics {
    pub icon_size: f32,
}

impl DropdownChevronMetrics {
    pub const fn new(icon_size: f32) -> Self {
        Self { icon_size }
    }
}

pub const DEFAULT_DROPDOWN_CHEVRON_METRICS: DropdownChevronMetrics =
    DropdownChevronMetrics::new(DEFAULT_DROPDOWN_CHEVRON_ICON_SIZE);

fn resolve_chevron_metrics(sizing: Option<DropdownSizing>) -> DropdownChevronMetrics {
    let Some(sizing) = sizing else {
        return DEFAULT_DROPDOWN_CHEVRON_METRICS;
    };
    if !sizing.has_chevron_icon_size() {
        return DEFAULT_DROPDOWN_CHEVRON_METRICS;
    }
    DropdownChevronMetrics::new(sizing.chevron_icon_size_px())
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct DropdownChevronVisualState {
    pub open: bool,
    pub hovered: bool,
    pub enabled: bool,
}

impl DropdownChevronVisualState {
    pub const fn new(open: bool, hovered: bool, enabled: bool) -> Self {
        Self {
            open,
            hovered,
            enabled,
        }
    }
}

pub trait DropdownChevronPresenter {
    fn root(&self) -> FlexBox;
    fn apply(&self, theme: Theme, state: DropdownChevronVisualState);
}

pub trait DropdownChevronTemplate {
    fn create(&self, sizing: Option<DropdownSizing>) -> Rc<dyn DropdownChevronPresenter>;
}

#[derive(Clone)]
pub struct DefaultDropdownChevronPresenter {
    root: FlexBox,
    metrics: DropdownChevronMetrics,
    icon_node: SvgNode,
}

impl DefaultDropdownChevronPresenter {
    pub fn new(metrics: DropdownChevronMetrics) -> Self {
        let root = flex_box();
        root.fill_size()
            .align_items(AlignItems::Center)
            .justify_content(JustifyContent::Center);
        let icon_node = svg(0);
        icon_node
            .width(metrics.icon_size, Unit::Pixel)
            .height(metrics.icon_size, Unit::Pixel);
        root.child(&icon_node);
        Self {
            root,
            metrics,
            icon_node: icon_node.clone(),
        }
    }
}

impl DropdownChevronPresenter for DefaultDropdownChevronPresenter {
    fn root(&self) -> FlexBox {
        self.root.clone()
    }

    fn apply(&self, theme: Theme, state: DropdownChevronVisualState) {
        let metrics = self.metrics;
        self.root
            .fill_size()
            .align_items(AlignItems::Center)
            .justify_content(JustifyContent::Center);
        self.icon_node
            .width(metrics.icon_size, Unit::Pixel)
            .height(metrics.icon_size, Unit::Pixel)
            .source(if state.open {
                DROPDOWN_CHEVRON_EXPANDED_SVG
            } else {
                DROPDOWN_CHEVRON_COLLAPSED_SVG
            })
            .tint(if !state.enabled {
                theme.colors.text_muted
            } else if state.hovered {
                theme.colors.text_primary
            } else {
                theme.colors.text_muted
            });
    }
}

#[derive(Clone, Copy, Debug, Default)]
pub struct DefaultDropdownChevronTemplate;

impl DropdownChevronTemplate for DefaultDropdownChevronTemplate {
    fn create(&self, sizing: Option<DropdownSizing>) -> Rc<dyn DropdownChevronPresenter> {
        create_default_dropdown_chevron_presenter(sizing)
    }
}

pub const DEFAULT_DROPDOWN_CHEVRON_TEMPLATE: DefaultDropdownChevronTemplate =
    DefaultDropdownChevronTemplate;

pub fn create_default_dropdown_chevron_presenter(
    sizing: Option<DropdownSizing>,
) -> Rc<dyn DropdownChevronPresenter> {
    Rc::new(DefaultDropdownChevronPresenter::new(
        resolve_chevron_metrics(sizing),
    ))
}