craft_core 0.1.1

Core library for the Craft GUI framework.
Documentation
use std::sync::Arc;
use crate::components::component::ComponentId;
use crate::elements::text::TextState;
use crate::elements::text_input::TextInputState;
use crate::reactive::element_state_store::ElementStateStore;
use crate::resource_manager::resource::Resource;
use crate::resource_manager::{ResourceIdentifier, ResourceManager};

use taffy::Size;

use crate::style::Style;
use crate::text::text_context::TextContext;

pub struct TaffyTextContext {
    pub id: ComponentId,
}

#[derive(Eq, Hash, PartialEq, Copy, Clone, Debug)]
pub struct TextHashKey {
    pub width_constraint: Option<u32>,
    pub height_constraint: Option<u32>,
    pub available_space_width: AvailableSpaceKey,
    pub available_space_height: AvailableSpaceKey,
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum AvailableSpaceKey {
    /// The amount of space available is the specified number of pixels
    Definite(u32),
    /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
    MinContent,
    /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
    MaxContent,
}

impl TaffyTextContext {
    pub fn new(id: ComponentId) -> Self {
        Self { id }
    }
}

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
pub struct MetricsRaw {
    /// Font size in pixels
    pub font_size: u32,
    /// Line height in pixels
    pub line_height: u32,
    pub scaling_factor: u64,
}

impl MetricsRaw {
    #[allow(dead_code)]
    pub(crate) fn from(style: &Style, scaling_factor: f64) -> Self {
        Self {
            font_size: (style.font_size() * scaling_factor as f32).to_bits(),
            line_height: (style.font_size() * 1.2 * scaling_factor as f32).to_bits(),
            scaling_factor: scaling_factor.to_bits(),
        }
    }
}

pub(crate) struct ImageContext {
    pub(crate) resource_identifier: ResourceIdentifier,
}

impl ImageContext {
    pub fn measure(
        &mut self,
        known_dimensions: Size<Option<f32>>,
        _available_space: Size<taffy::AvailableSpace>,
        resource_manager: Arc<ResourceManager>,
        _style: &taffy::Style,
    ) -> Size<f32> {
        let mut original_image_width: f32 = 0.0;
        let mut original_image_height: f32 = 0.0;
        if let Some(resource) = resource_manager.resources.get(&self.resource_identifier) {
            if let Resource::Image(image_data) = resource.as_ref() {
                original_image_width = image_data.width as f32;
                original_image_height = image_data.height as f32;
            }
        }
        // println!("image size: {} {}", original_image_width, original_image_height);
        // println!("known dims: {:?}", known_dimensions);
        match (known_dimensions.width, known_dimensions.height) {
            (Some(width), Some(height)) => Size { width, height },
            (Some(width), None) => Size {
                width,
                height: (width / original_image_width) * original_image_height,
            },
            (None, Some(height)) => Size {
                width: (height / original_image_height) * original_image_width,
                height,
            },
            (None, None) => Size {
                width: original_image_width,
                height: original_image_height,
            },
        }
    }
}

pub(crate) enum LayoutContext {
    Text(TaffyTextContext),
    TextInput(TaffyTextInputContext),
    Image(ImageContext),
    TinyVg(TinyVgContext)
}

pub fn measure_content(
    element_state: &mut ElementStateStore,
    known_dimensions: Size<Option<f32>>,
    available_space: Size<taffy::AvailableSpace>,
    node_context: Option<&mut LayoutContext>,
    text_context: &mut TextContext,
    resource_manager: Arc<ResourceManager>,
    style: &taffy::Style,
) -> Size<f32> {
    if let Size {
        width: Some(width),
        height: Some(height),
    } = known_dimensions
    {
        return Size { width, height };
    }

    match node_context {
        None => Size::ZERO,
        Some(LayoutContext::Text(taffy_text_context)) => {
            let text_state: &mut TextState =
                element_state.storage.get_mut(&taffy_text_context.id).unwrap().data.downcast_mut().unwrap();
            
            text_state.measure(
                known_dimensions,
                available_space,
                text_context,
            )
        }
        Some(LayoutContext::Image(image_context)) => {
            image_context.measure(known_dimensions, available_space, resource_manager, style)
        }
        Some(LayoutContext::TextInput(taffy_text_input_context)) => {
            let text_input_state: &mut TextInputState =
                element_state.storage.get_mut(&taffy_text_input_context.id).unwrap().data.downcast_mut().unwrap();

            text_input_state.measure(
                known_dimensions,
                available_space,
                text_context,
            )
        }
        Some(LayoutContext::TinyVg(tinyvg_context)) => {
            tinyvg_context.measure(known_dimensions, available_space, resource_manager, style)
        }
    }
}

//////////////////////////////////////////////////////////////////////////////

pub struct TaffyTextInputContext {
    pub id: ComponentId,
}

impl TaffyTextInputContext {
    pub fn new(id: ComponentId) -> Self {
        Self { id }
    }
}

impl TextHashKey {
    pub(crate) fn new(known_dimensions: Size<Option<f32>>, available_space: Size<taffy::AvailableSpace>) -> Self {
        let available_space_width_u32: AvailableSpaceKey = match available_space.width {
            taffy::AvailableSpace::MinContent => AvailableSpaceKey::MinContent,
            taffy::AvailableSpace::MaxContent => AvailableSpaceKey::MaxContent,
            taffy::AvailableSpace::Definite(width) => AvailableSpaceKey::Definite(width.to_bits()),
        };
        let available_space_height_u32: AvailableSpaceKey = match available_space.height {
            taffy::AvailableSpace::MinContent => AvailableSpaceKey::MinContent,
            taffy::AvailableSpace::MaxContent => AvailableSpaceKey::MaxContent,
            taffy::AvailableSpace::Definite(height) => AvailableSpaceKey::Definite(height.to_bits()),
        };

        Self {
            width_constraint: known_dimensions.width.map(|w| w.to_bits()),
            height_constraint: known_dimensions.height.map(|h| h.to_bits()),
            available_space_width: available_space_width_u32,
            available_space_height: available_space_height_u32,
        }
    }

    pub(crate) fn available_space(&self) -> Size<taffy::AvailableSpace> {
        Size {
            width: match self.available_space_width {
                AvailableSpaceKey::Definite(width) => taffy::AvailableSpace::Definite(width as f32),
                AvailableSpaceKey::MinContent => taffy::AvailableSpace::MinContent,
                AvailableSpaceKey::MaxContent => taffy::AvailableSpace::MaxContent,
            },
            height: match self.available_space_height {
                AvailableSpaceKey::Definite(height) => taffy::AvailableSpace::Definite(height as f32),
                AvailableSpaceKey::MinContent => taffy::AvailableSpace::MinContent,
                AvailableSpaceKey::MaxContent => taffy::AvailableSpace::MaxContent,
            },
        }
    }

    pub(crate) fn known_dimensions(&self) -> Size<Option<f32>> {
        Size {
            width: self.width_constraint.map(f32::from_bits),
            height: self.height_constraint.map(f32::from_bits),
        }
    }
}

pub(crate) struct TinyVgContext {
    pub(crate) resource_identifier: ResourceIdentifier,
}

impl TinyVgContext {
    pub fn measure(
        &mut self,
        known_dimensions: Size<Option<f32>>,
        _available_space: Size<taffy::AvailableSpace>,
        resource_manager: Arc<ResourceManager>,
        _style: &taffy::Style,
    ) -> Size<f32> {
        let mut original_image_width: f32 = 0.0;
        let mut original_image_height: f32 = 0.0;

        if let Some(resource) = resource_manager.resources.get(&self.resource_identifier) {
            if let Resource::TinyVg(resource) = resource.as_ref() {
                if let Some(tinyvg) = &resource.tinyvg {
                    original_image_width = tinyvg.header.width as f32;
                    original_image_height = tinyvg.header.height as f32;
                }
            }
        }

        match (known_dimensions.width, known_dimensions.height) {
            (Some(width), Some(height)) => Size { width, height },
            (Some(width), None) => Size {
                width,
                height: (width / original_image_width) * original_image_height,
            },
            (None, Some(height)) => Size {
                width: (height / original_image_height) * original_image_width,
                height,
            },
            (None, None) => Size {
                width: original_image_width,
                height: original_image_height,
            },
        }
    }
}