anathema-widgets 0.2.11

Anathema widget base
Documentation
use std::fmt::Write;
use std::ops::ControlFlow;

use anathema_geometry::Size;
use anathema_state::States;
use anathema_store::tree::root_node;
use anathema_templates::Document;
use anathema_value_resolver::{AttributeStorage, FunctionTable, Scope};

use crate::components::ComponentRegistry;
use crate::layout::{LayoutCtx, Viewport};
use crate::{
    Components, Factory, FloatingWidgets, GlyphMap, LayoutForEach, Widget, WidgetTree, WidgetTreeView, eval_blueprint,
};

pub fn with_template<F>(tpl: &'static str, mut f: F)
where
    F: for<'bp> FnMut(WidgetTreeView<'_, 'bp>, &mut AttributeStorage<'bp>),
{
    let mut tree = WidgetTree::empty();
    let mut doc = Document::new(tpl);
    let (blueprint, globals) = doc.compile().unwrap();
    let globals = Box::leak(Box::new(globals));
    let blueprint = Box::leak(Box::new(blueprint));
    let function_table = Box::leak(Box::new(FunctionTable::new()));

    let mut factory = Factory::new();
    factory.register_default::<Many>("many");
    factory.register_default::<Float>("float");
    factory.register_default::<Text>("text");

    let mut states = States::new();
    let mut attribute_storage = AttributeStorage::empty();

    let mut components = Components::new();
    let mut component_registry = ComponentRegistry::new();

    let mut viewport = Viewport::new((80, 25));

    let mut floating = FloatingWidgets::empty();
    let mut glyph_map = GlyphMap::empty();

    let mut layout_ctx = LayoutCtx::new(
        globals,
        &factory,
        &mut states,
        &mut attribute_storage,
        &mut components,
        &mut component_registry,
        &mut floating,
        &mut glyph_map,
        &mut viewport,
        function_table,
    );

    let mut ctx = layout_ctx.eval_ctx(None);
    let scope = Scope::root();

    eval_blueprint(blueprint, &mut ctx, &scope, root_node(), &mut tree.view()).unwrap();

    let filter = crate::layout::LayoutFilter;
    let mut for_each = LayoutForEach::new(tree.view(), &scope, filter, None);
    _ = for_each
        .each(&mut layout_ctx, |ctx, widget, children| {
            _ = widget.layout(children, ctx.viewport.constraints(), ctx)?;
            Ok(ControlFlow::Break(()))
        })
        .unwrap();

    f(tree.view(), layout_ctx.attribute_storage);
}

// -----------------------------------------------------------------------------
//   - Test widgets -
// -----------------------------------------------------------------------------

#[derive(Debug, Default)]
pub(crate) struct Many;

impl Widget for Many {
    fn layout<'bp>(
        &mut self,
        mut children: crate::LayoutForEach<'_, 'bp>,
        constraints: crate::layout::Constraints,
        _: crate::WidgetId,
        ctx: &mut LayoutCtx<'_, 'bp>,
    ) -> crate::error::Result<anathema_geometry::Size> {
        let mut size = Size::ZERO;
        _ = children.each(ctx, |ctx, child, children| {
            let child_size: Size = child.layout(children, constraints, ctx)?.into();
            size.width = size.width.max(child_size.width);
            size.height += child_size.height;
            Ok(ControlFlow::Continue(()))
        });

        Ok(size)
    }

    fn position<'bp>(
        &mut self,
        mut children: crate::ForEach<'_, 'bp, crate::layout::PositionFilter>,
        _: crate::WidgetId,
        attribute_storage: &AttributeStorage<'bp>,
        ctx: crate::layout::PositionCtx,
    ) {
        _ = children.each(|child, children| {
            child.position(children, ctx.pos, attribute_storage, ctx.viewport);
            ControlFlow::Continue(())
        });
    }
}

#[derive(Debug, Default)]
pub(crate) struct Text(String);

impl Widget for Text {
    fn layout<'bp>(
        &mut self,
        _: crate::LayoutForEach<'_, 'bp>,
        _: crate::layout::Constraints,
        id: crate::WidgetId,
        ctx: &mut LayoutCtx<'_, 'bp>,
    ) -> crate::error::Result<anathema_geometry::Size> {
        let attributes = ctx.attributes(id);
        let Some(value) = attributes.value() else { return Ok(Size::ZERO) };

        let mut buffer = String::new();
        value.strings(|s| write!(&mut buffer, "{s}").is_ok());
        self.0 = buffer;

        Ok(Size::new(self.0.chars().count() as u16, 1))
    }

    fn position<'bp>(
        &mut self,
        _: crate::ForEach<'_, 'bp, crate::layout::PositionFilter>,
        _: crate::WidgetId,
        _: &AttributeStorage<'bp>,
        _: crate::layout::PositionCtx,
    ) {
    }
}

#[derive(Debug, Default)]
pub(crate) struct Float;

impl Widget for Float {
    fn layout<'bp>(
        &mut self,
        mut children: crate::LayoutForEach<'_, 'bp>,
        constraints: crate::layout::Constraints,
        _: crate::WidgetId,
        ctx: &mut LayoutCtx<'_, 'bp>,
    ) -> crate::error::Result<anathema_geometry::Size> {
        let mut size = Size::ZERO;
        _ = children.each(ctx, |ctx, child, children| {
            let child_size: Size = child.layout(children, constraints, ctx)?.into();
            size.width = size.width.max(child_size.width);
            size.height += child_size.height;
            Ok(ControlFlow::Continue(()))
        });

        Ok(size)
    }

    fn position<'bp>(
        &mut self,
        mut children: crate::ForEach<'_, 'bp, crate::layout::PositionFilter>,
        _: crate::WidgetId,
        attribute_storage: &AttributeStorage<'bp>,
        ctx: crate::layout::PositionCtx,
    ) {
        _ = children.each(|child, children| {
            child.position(children, ctx.pos, attribute_storage, ctx.viewport);
            ControlFlow::Continue(())
        });
    }

    fn floats(&self) -> bool {
        true
    }
}