simple-render 0.2.0

a simple immediate mode wayland ui renderer inspired by clay
Documentation
use super::*;

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum TextWrap {
    None,
    Glyph,
    Word,
    #[default]
    WordOrGlyph,
}

impl TextWrap {
    pub(in crate::ui) fn into_cosmic(self) -> Wrap {
        match self {
            Self::None => Wrap::None,
            Self::Glyph => Wrap::Glyph,
            Self::Word => Wrap::Word,
            Self::WordOrGlyph => Wrap::WordOrGlyph,
        }
    }
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum TextOverflow {
    #[default]
    Clip,
    Ellipsis,
}

#[derive(Clone, Debug, PartialEq)]
pub struct TextStyle {
    pub color: Paint,
    pub gradient: GradientDirection,
    pub size: u32,
    pub family: Option<String>,
    pub bold: bool,
    pub italic: bool,
    pub underline: bool,
    pub strikethrough: bool,
    pub overline: bool,
}

impl Default for TextStyle {
    fn default() -> Self {
        Self {
            color: Paint::solid(Color::WHITE),
            gradient: GradientDirection::default(),
            size: 14,
            family: None,
            bold: false,
            italic: false,
            underline: false,
            strikethrough: false,
            overline: false,
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Text {
    pub content: Arc<str>,
    pub style: TextStyle,
    pub align: Align,
    pub vertical_align: Align,
    pub wrap: TextWrap,
    pub overflow: TextOverflow,
    pub max_lines: Option<u32>,
    pub emoji_family: Option<String>,
}

impl Text {
    pub fn new(content: impl Into<Arc<str>>) -> Self {
        Self {
            content: content.into(),
            ..Self::default()
        }
    }
}

impl Default for Text {
    fn default() -> Self {
        Self {
            content: Arc::from(""),
            style: TextStyle::default(),
            align: Align::Start,
            vertical_align: Align::Start,
            wrap: TextWrap::default(),
            overflow: TextOverflow::default(),
            max_lines: None,
            emoji_family: Some("Noto Color Emoji".into()),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct TextRun {
    pub content: Arc<str>,
    pub style: TextStyle,
}

impl TextRun {
    pub fn new(content: impl Into<Arc<str>>, style: TextStyle) -> Self {
        Self {
            content: content.into(),
            style,
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct RichText {
    pub runs: Arc<[TextRun]>,
    pub align: Align,
    pub vertical_align: Align,
    pub wrap: TextWrap,
    pub overflow: TextOverflow,
    pub max_lines: Option<u32>,
    pub emoji_family: Option<String>,
}

impl RichText {
    pub fn new(runs: impl Into<Arc<[TextRun]>>) -> Self {
        Self {
            runs: runs.into(),
            ..Self::default()
        }
    }
}

impl Default for RichText {
    fn default() -> Self {
        Self {
            runs: Arc::from([]),
            align: Align::Start,
            vertical_align: Align::Start,
            wrap: TextWrap::default(),
            overflow: TextOverflow::default(),
            max_lines: None,
            emoji_family: Some("Noto Color Emoji".into()),
        }
    }
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ImageFit {
    #[default]
    None,
    Fill,
    Contain,
    Cover,
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ImageFilter {
    #[default]
    Nearest,
    Linear,
}

pub trait RgbaImageSource: fmt::Debug + Send + Sync {
    fn rgba(&self) -> &[u8];
}

#[derive(Clone)]
pub enum ImagePixels {
    Owned(Arc<[u8]>),
    Source(Arc<dyn RgbaImageSource>),
}

impl ImagePixels {
    pub fn as_rgba(&self) -> &[u8] {
        match self {
            Self::Owned(pixels) => pixels,
            Self::Source(source) => source.rgba(),
        }
    }
}

impl fmt::Debug for ImagePixels {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("ImagePixels")
            .field("len", &self.as_rgba().len())
            .finish_non_exhaustive()
    }
}

impl PartialEq for ImagePixels {
    fn eq(&self, other: &Self) -> bool {
        self.as_rgba() == other.as_rgba()
    }
}

impl Eq for ImagePixels {}

impl From<Arc<[u8]>> for ImagePixels {
    fn from(pixels: Arc<[u8]>) -> Self {
        Self::Owned(pixels)
    }
}

impl From<Vec<u8>> for ImagePixels {
    fn from(pixels: Vec<u8>) -> Self {
        Self::Owned(pixels.into())
    }
}

impl<const N: usize> From<[u8; N]> for ImagePixels {
    fn from(pixels: [u8; N]) -> Self {
        Self::Owned(Arc::from(pixels))
    }
}

impl RgbaImageSource for Vec<u8> {
    fn rgba(&self) -> &[u8] {
        self
    }
}

impl RgbaImageSource for Box<[u8]> {
    fn rgba(&self) -> &[u8] {
        self
    }
}

impl RgbaImageSource for Arc<[u8]> {
    fn rgba(&self) -> &[u8] {
        self
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Image {
    pub width: u32,
    pub height: u32,
    pub rgba: ImagePixels,
    pub stride: u32,
    pub fit: ImageFit,
    pub filter: ImageFilter,
    pub align: Align,
    pub vertical_align: Align,
}

impl Image {
    pub fn new(width: u32, height: u32, rgba: impl Into<ImagePixels>) -> Self {
        Self {
            width,
            height,
            rgba: rgba.into(),
            stride: width.saturating_mul(4),
            fit: ImageFit::default(),
            filter: ImageFilter::default(),
            align: Align::Start,
            vertical_align: Align::Start,
        }
    }

    pub fn with_stride(mut self, stride: u32) -> Self {
        self.stride = stride;
        self
    }

    pub fn from_source(width: u32, height: u32, source: Arc<dyn RgbaImageSource>) -> Self {
        Self::new(width, height, ImagePixels::Source(source))
    }

    pub fn from_source_with_stride(
        width: u32,
        height: u32,
        stride: u32,
        source: Arc<dyn RgbaImageSource>,
    ) -> Self {
        Self::from_source(width, height, source).with_stride(stride)
    }

    pub fn rgba(&self) -> &[u8] {
        self.rgba.as_rgba()
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Content {
    Text(Text),
    RichText(RichText),
    Image(Image),
}

impl From<Text> for Content {
    fn from(text: Text) -> Self {
        Self::Text(text)
    }
}

impl From<RichText> for Content {
    fn from(text: RichText) -> Self {
        Self::RichText(text)
    }
}

impl From<Image> for Content {
    fn from(image: Image) -> Self {
        Self::Image(image)
    }
}