mod brush;
mod font;
mod styleset;
use alloc::borrow::Cow;
#[cfg(feature = "libm")]
#[allow(unused_imports)]
use core_maths::CoreFloat;
use parley_engine::FontMetrics;
pub use brush::*;
pub use font::{
FontFamily, FontFamilyName, FontFeature, FontFeatures, FontStyle, FontVariation,
FontVariations, FontWeight, FontWidth, GenericFamily,
};
pub use fontique::Language;
pub use parlance::{LineBreak, OverflowWrap, TextWrapMode, WordBreak};
pub use styleset::StyleSet;
use crate::util::nearly_eq;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum WhiteSpaceCollapse {
Collapse,
#[default]
Preserve,
PreserveBreaks,
BreakSpaces,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LineHeight {
MetricsRelative(f32),
FontSizeRelative(f32),
Absolute(f32),
}
impl Default for LineHeight {
fn default() -> Self {
Self::NORMAL
}
}
impl LineHeight {
pub const NORMAL: Self = Self::MetricsRelative(1.0);
pub(crate) fn nearly_eq(self, other: Self) -> bool {
match (self, other) {
(Self::MetricsRelative(a), Self::MetricsRelative(b))
| (Self::FontSizeRelative(a), Self::FontSizeRelative(b))
| (Self::Absolute(a), Self::Absolute(b)) => nearly_eq(a, b),
_ => false,
}
}
pub(crate) fn scale(self, scale: f32) -> Self {
match self {
Self::Absolute(value) => Self::Absolute(value * scale),
value => value,
}
}
pub(crate) fn resolve(self, font_size: f32, metrics: &FontMetrics, quantize: bool) -> f32 {
match self {
Self::Absolute(value) => value,
Self::FontSizeRelative(value) => value * font_size,
Self::MetricsRelative(value) if quantize => {
(metrics.ascent.round() + metrics.descent.round() + metrics.leading.round()) * value
}
Self::MetricsRelative(value) => {
(metrics.ascent + metrics.descent + metrics.leading) * value
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AlignmentBaseline {
#[default]
Baseline,
TextTop,
TextBottom,
Middle,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BaselineShift {
Length(f32),
Sub,
Super,
Top,
Bottom,
}
impl Default for BaselineShift {
fn default() -> Self {
Self::Length(0.)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct VerticalAlign {
pub alignment: AlignmentBaseline,
pub shift: BaselineShift,
}
impl VerticalAlign {
pub const BASELINE: Self = Self::new(AlignmentBaseline::Baseline, BaselineShift::Length(0.));
pub const SUB: Self = Self::new(AlignmentBaseline::Baseline, BaselineShift::Sub);
pub const SUPER: Self = Self::new(AlignmentBaseline::Baseline, BaselineShift::Super);
pub const TEXT_TOP: Self = Self::new(AlignmentBaseline::TextTop, BaselineShift::Length(0.));
pub const TEXT_BOTTOM: Self =
Self::new(AlignmentBaseline::TextBottom, BaselineShift::Length(0.));
pub const MIDDLE: Self = Self::new(AlignmentBaseline::Middle, BaselineShift::Length(0.));
pub const TOP: Self = Self::new(AlignmentBaseline::Baseline, BaselineShift::Top);
pub const BOTTOM: Self = Self::new(AlignmentBaseline::Baseline, BaselineShift::Bottom);
pub const fn new(alignment: AlignmentBaseline, shift: BaselineShift) -> Self {
Self { alignment, shift }
}
pub const fn length(length: f32) -> Self {
Self::new(AlignmentBaseline::Baseline, BaselineShift::Length(length))
}
pub fn is_line_relative(self) -> bool {
matches!(self.shift, BaselineShift::Top | BaselineShift::Bottom)
}
pub(crate) fn nearly_eq(self, other: Self) -> bool {
self.alignment == other.alignment
&& match (self.shift, other.shift) {
(BaselineShift::Length(a), BaselineShift::Length(b)) => nearly_eq(a, b),
(a, b) => a == b,
}
}
pub(crate) fn scale(self, scale: f32) -> Self {
match self.shift {
BaselineShift::Length(value) => Self {
shift: BaselineShift::Length(value * scale),
..self
},
_ => self,
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub enum StyleProperty<'a, B: Brush> {
FontFamily(FontFamily<'a>),
FontSize(f32),
FontWidth(FontWidth),
FontStyle(FontStyle),
FontWeight(FontWeight),
FontVariations(FontVariations<'a>),
FontFeatures(FontFeatures<'a>),
Locale(Option<Language>),
Brush(B),
Underline(bool),
UnderlineOffset(Option<f32>),
UnderlineSize(Option<f32>),
UnderlineBrush(Option<B>),
Strikethrough(bool),
StrikethroughOffset(Option<f32>),
StrikethroughSize(Option<f32>),
StrikethroughBrush(Option<B>),
LineHeight(LineHeight),
VerticalAlign(VerticalAlign),
WordSpacing(f32),
LetterSpacing(f32),
WordBreak(WordBreak),
LineBreak(LineBreak),
OverflowWrap(OverflowWrap),
TextWrapMode(TextWrapMode),
WhiteSpaceCollapse(WhiteSpaceCollapse),
}
#[derive(Clone, PartialEq, Debug)]
pub struct TextStyle<'family, 'settings, B: Brush> {
pub font_family: FontFamily<'family>,
pub font_size: f32,
pub font_width: FontWidth,
pub font_style: FontStyle,
pub font_weight: FontWeight,
pub font_variations: FontVariations<'settings>,
pub font_features: FontFeatures<'settings>,
pub locale: Option<Language>,
pub brush: B,
pub has_underline: bool,
pub underline_offset: Option<f32>,
pub underline_size: Option<f32>,
pub underline_brush: Option<B>,
pub has_strikethrough: bool,
pub strikethrough_offset: Option<f32>,
pub strikethrough_size: Option<f32>,
pub strikethrough_brush: Option<B>,
pub line_height: LineHeight,
pub vertical_align: VerticalAlign,
pub word_spacing: f32,
pub letter_spacing: f32,
pub word_break: WordBreak,
pub line_break: LineBreak,
pub overflow_wrap: OverflowWrap,
pub text_wrap_mode: TextWrapMode,
pub white_space_collapse: WhiteSpaceCollapse,
}
impl<B: Brush> Default for TextStyle<'static, 'static, B> {
fn default() -> Self {
TextStyle {
font_family: FontFamily::Source(Cow::Borrowed("sans-serif")),
font_size: 16.0,
font_width: FontWidth::default(),
font_style: FontStyle::default(),
font_weight: FontWeight::default(),
font_variations: FontVariations::empty(),
font_features: FontFeatures::empty(),
locale: None,
brush: B::default(),
has_underline: false,
underline_offset: None,
underline_size: None,
underline_brush: None,
has_strikethrough: false,
strikethrough_offset: None,
strikethrough_size: None,
strikethrough_brush: None,
line_height: LineHeight::default(),
vertical_align: VerticalAlign::default(),
word_spacing: 0.0,
letter_spacing: 0.0,
word_break: WordBreak::default(),
line_break: LineBreak::default(),
overflow_wrap: OverflowWrap::default(),
text_wrap_mode: TextWrapMode::default(),
white_space_collapse: WhiteSpaceCollapse::default(),
}
}
}
impl<'a, B: Brush> From<FontFamily<'a>> for StyleProperty<'a, B> {
fn from(value: FontFamily<'a>) -> Self {
StyleProperty::FontFamily(value)
}
}
impl<'a, B: Brush> From<&'a [FontFamilyName<'a>]> for StyleProperty<'a, B> {
fn from(value: &'a [FontFamilyName<'a>]) -> Self {
StyleProperty::FontFamily(value.into())
}
}
impl<'a, B: Brush> From<FontFamilyName<'a>> for StyleProperty<'a, B> {
fn from(value: FontFamilyName<'a>) -> Self {
StyleProperty::FontFamily(value.into())
}
}
impl<'a, B: Brush> From<FontVariations<'a>> for StyleProperty<'a, B> {
fn from(value: FontVariations<'a>) -> Self {
StyleProperty::FontVariations(value)
}
}
impl<'a, B: Brush> From<FontFeatures<'a>> for StyleProperty<'a, B> {
fn from(value: FontFeatures<'a>) -> Self {
StyleProperty::FontFeatures(value)
}
}
impl<B: Brush> From<GenericFamily> for StyleProperty<'_, B> {
fn from(f: GenericFamily) -> Self {
StyleProperty::FontFamily(f.into())
}
}
impl<B: Brush> From<LineHeight> for StyleProperty<'_, B> {
fn from(value: LineHeight) -> Self {
StyleProperty::LineHeight(value)
}
}
impl<B: Brush> From<VerticalAlign> for StyleProperty<'_, B> {
fn from(value: VerticalAlign) -> Self {
StyleProperty::VerticalAlign(value)
}
}
impl<B: Brush> From<FontWidth> for StyleProperty<'_, B> {
fn from(value: FontWidth) -> Self {
StyleProperty::FontWidth(value)
}
}
impl<B: Brush> From<FontStyle> for StyleProperty<'_, B> {
fn from(value: FontStyle) -> Self {
StyleProperty::FontStyle(value)
}
}
impl<B: Brush> From<FontWeight> for StyleProperty<'_, B> {
fn from(value: FontWeight) -> Self {
StyleProperty::FontWeight(value)
}
}
impl<B: Brush> From<WordBreak> for StyleProperty<'_, B> {
fn from(value: WordBreak) -> Self {
StyleProperty::WordBreak(value)
}
}
impl<B: Brush> From<LineBreak> for StyleProperty<'_, B> {
fn from(value: LineBreak) -> Self {
StyleProperty::LineBreak(value)
}
}
impl<B: Brush> From<OverflowWrap> for StyleProperty<'_, B> {
fn from(value: OverflowWrap) -> Self {
StyleProperty::OverflowWrap(value)
}
}
impl<B: Brush> From<TextWrapMode> for StyleProperty<'_, B> {
fn from(value: TextWrapMode) -> Self {
StyleProperty::TextWrapMode(value)
}
}