use std::{fmt, collections::BTreeMap};
use azul_css::{
LayoutRect, LayoutPoint, LayoutSize, PixelValue, StyleFontSize,
StyleTextColor, ColorU as StyleColorU, Overflow,
StyleTextAlignmentHorz, StyleTextAlignmentVert,
};
use crate::{
app_resources::{Words, ScaledWords, FontInstanceKey, WordPositions, LayoutedGlyphs},
id_tree::{NodeId, NodeDataContainer},
dom::{DomHash, ScrollTagId},
callbacks::PipelineId,
};
pub const DEFAULT_FONT_SIZE_PX: isize = 16;
pub const DEFAULT_FONT_SIZE: StyleFontSize = StyleFontSize(PixelValue::const_px(DEFAULT_FONT_SIZE_PX));
pub const DEFAULT_FONT_ID: &str = "serif";
pub const DEFAULT_FONT_COLOR: StyleTextColor = StyleTextColor(StyleColorU { r: 0, b: 0, g: 0, a: 255 });
pub const DEFAULT_LINE_HEIGHT: f32 = 1.0;
pub const DEFAULT_WORD_SPACING: f32 = 1.0;
pub const DEFAULT_LETTER_SPACING: f32 = 0.0;
pub const DEFAULT_TAB_WIDTH: f32 = 4.0;
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct InlineTextLayout {
pub lines: Vec<InlineTextLine>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct InlineTextLine {
pub bounds: LayoutRect,
pub word_start: usize,
pub word_end: usize,
}
impl InlineTextLine {
pub const fn new(bounds: LayoutRect, word_start: usize, word_end: usize) -> Self {
Self { bounds, word_start, word_end }
}
}
impl InlineTextLayout {
pub fn get_leading(&self) -> f32 {
match self.lines.first() {
None => 0.0,
Some(s) => s.bounds.origin.x,
}
}
pub fn get_trailing(&self) -> f32 {
match self.lines.first() {
None => 0.0,
Some(s) => s.bounds.origin.x + s.bounds.size.width,
}
}
pub const fn new(lines: Vec<InlineTextLine>) -> Self {
Self { lines }
}
#[inline]
#[must_use]
pub fn get_bounds(&self) -> LayoutRect {
LayoutRect::union(self.lines.iter().map(|c| c.bounds)).unwrap_or(LayoutRect::zero())
}
#[must_use]
pub fn get_children_horizontal_diff_to_right_edge(&self, parent: &LayoutRect) -> Vec<f32> {
let parent_right_edge = parent.origin.x + parent.size.width;
let parent_left_edge = parent.origin.x;
self.lines.iter().map(|line| {
let child_right_edge = line.bounds.origin.x + line.bounds.size.width;
let child_left_edge = line.bounds.origin.x;
(child_left_edge - parent_left_edge) + (parent_right_edge - child_right_edge)
}).collect()
}
pub fn align_children_horizontal(&mut self, horizontal_alignment: StyleTextAlignmentHorz) {
let shift_multiplier = match calculate_horizontal_shift_multiplier(horizontal_alignment) {
None => return,
Some(s) => s,
};
let self_bounds = self.get_bounds();
let horz_diff = self.get_children_horizontal_diff_to_right_edge(&self_bounds);
for (line, shift) in self.lines.iter_mut().zip(horz_diff.into_iter()) {
line.bounds.origin.x += shift * shift_multiplier;
}
}
pub fn align_children_vertical_in_parent_bounds(&mut self, parent_size: &LayoutSize, vertical_alignment: StyleTextAlignmentVert) {
let shift_multiplier = match calculate_vertical_shift_multiplier(vertical_alignment) {
None => return,
Some(s) => s,
};
let self_bounds = self.get_bounds();
let child_bottom_edge = self_bounds.origin.y + self_bounds.size.height;
let child_top_edge = self_bounds.origin.y;
let shift = child_top_edge + (parent_size.height - child_bottom_edge);
for line in self.lines.iter_mut() {
line.bounds.origin.y += shift * shift_multiplier;
}
}
}
#[inline]
pub fn calculate_horizontal_shift_multiplier(horizontal_alignment: StyleTextAlignmentHorz) -> Option<f32> {
use azul_css::StyleTextAlignmentHorz::*;
match horizontal_alignment {
Left => None,
Center => Some(0.5),
Right => Some(1.0),
}
}
#[inline]
pub fn calculate_vertical_shift_multiplier(vertical_alignment: StyleTextAlignmentVert) -> Option<f32> {
use azul_css::StyleTextAlignmentVert::*;
match vertical_alignment {
Top => None,
Center => Some(0.5),
Bottom => Some(1.0),
}
}
#[derive(Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
#[repr(C)]
pub struct ExternalScrollId(pub u64, pub PipelineId);
impl ::std::fmt::Display for ExternalScrollId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ExternalScrollId({:0x}, {})", self.0, self.1)
}
}
impl ::std::fmt::Debug for ExternalScrollId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
#[derive(Default, Debug, Clone)]
pub struct ScrolledNodes {
pub overflowing_nodes: BTreeMap<NodeId, OverflowingScrollNode>,
pub tags_to_node_ids: BTreeMap<ScrollTagId, NodeId>,
}
#[derive(Debug, Clone)]
pub struct OverflowingScrollNode {
pub child_rect: LayoutRect,
pub parent_external_scroll_id: ExternalScrollId,
pub parent_dom_hash: DomHash,
pub scroll_tag_id: ScrollTagId,
}
#[derive(Debug, Default, Clone)]
pub struct LayoutResult {
pub rects: NodeDataContainer<PositionedRectangle>,
pub word_cache: BTreeMap<NodeId, Words>,
pub scaled_words: BTreeMap<NodeId, (ScaledWords, FontInstanceKey)>,
pub positioned_word_cache: BTreeMap<NodeId, (WordPositions, FontInstanceKey)>,
pub layouted_glyph_cache: BTreeMap<NodeId, LayoutedGlyphs>,
pub node_depths: Vec<(usize, NodeId)>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
pub struct TextLayoutOptions {
pub font_size_px: PixelValue,
pub line_height: Option<f32>,
pub letter_spacing: Option<PixelValue>,
pub word_spacing: Option<PixelValue>,
pub tab_width: Option<f32>,
pub max_horizontal_width: Option<f32>,
pub leading: Option<f32>,
pub holes: Vec<LayoutRect>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
pub struct ResolvedTextLayoutOptions {
pub font_size_px: f32,
pub line_height: Option<f32>,
pub letter_spacing: Option<f32>,
pub word_spacing: Option<f32>,
pub tab_width: Option<f32>,
pub max_horizontal_width: Option<f32>,
pub leading: Option<f32>,
pub holes: Vec<LayoutRect>,
}
#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
pub struct ResolvedOffsets {
pub top: f32,
pub left: f32,
pub right: f32,
pub bottom: f32,
}
impl ResolvedOffsets {
pub const fn zero() -> Self { Self { top: 0.0, left: 0.0, right: 0.0, bottom: 0.0 } }
pub fn total_vertical(&self) -> f32 { self.top + self.bottom }
pub fn total_horizontal(&self) -> f32 { self.left + self.right }
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct PositionedRectangle {
pub size: LayoutSize,
pub position: PositionInfo,
pub padding: ResolvedOffsets,
pub margin: ResolvedOffsets,
pub border_widths: ResolvedOffsets,
pub resolved_text_layout_options: Option<(ResolvedTextLayoutOptions, InlineTextLayout, LayoutRect)>,
pub overflow: Overflow,
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub enum PositionInfo {
Static { x_offset: f32, y_offset: f32 },
Fixed { x_offset: f32, y_offset: f32 },
Absolute { x_offset: f32, y_offset: f32 },
Relative { x_offset: f32, y_offset: f32 },
}
impl PositionInfo {
pub fn is_positioned(&self) -> bool {
match self {
PositionInfo::Static { .. } => false,
PositionInfo::Fixed { .. } => true,
PositionInfo::Absolute { .. } => true,
PositionInfo::Relative { .. } => true,
}
}
}
impl PositionedRectangle {
pub fn get_static_bounds(&self) -> Option<LayoutRect> {
match self.position {
PositionInfo::Static { x_offset, y_offset } => Some(LayoutRect::new(
LayoutPoint::new(x_offset, y_offset), self.size
)),
PositionInfo::Fixed { .. } => None,
PositionInfo::Absolute { .. } => None,
PositionInfo::Relative { x_offset, y_offset } => Some(LayoutRect::new(
LayoutPoint::new(x_offset, y_offset), self.size
)),
}
}
pub fn to_layouted_rectangle(&self) -> LayoutedRectangle {
LayoutedRectangle {
size: self.size,
position: self.position,
padding: self.padding,
margin: self.margin,
border_widths: self.border_widths,
overflow: self.overflow,
}
}
pub fn get_content_size(&self) -> LayoutSize {
self.size
}
pub fn get_background_bounds(&self) -> (LayoutSize, PositionInfo) {
use crate::ui_solver::PositionInfo::*;
let b_size = LayoutSize {
width: self.size.width + self.padding.total_horizontal() + self.border_widths.total_horizontal(),
height: self.size.height + self.padding.total_vertical() + self.border_widths.total_vertical(),
};
let x_offset_add = 0.0 - self.padding.left - self.border_widths.left;
let y_offset_add = 0.0 - self.padding.top - self.border_widths.top;
let b_position = match self.position {
Static { x_offset, y_offset } => Static { x_offset: x_offset + x_offset_add, y_offset: y_offset + y_offset_add },
Fixed { x_offset, y_offset } => Fixed { x_offset: x_offset + x_offset_add, y_offset: y_offset + y_offset_add },
Relative { x_offset, y_offset } => Relative { x_offset: x_offset + x_offset_add, y_offset: y_offset + y_offset_add },
Absolute { x_offset, y_offset } => Absolute { x_offset: x_offset + x_offset_add, y_offset: y_offset + y_offset_add },
};
(b_size, b_position)
}
pub fn get_margin_box_width(&self) -> f32 {
self.size.width +
self.padding.total_horizontal() +
self.border_widths.total_horizontal() +
self.margin.total_horizontal()
}
pub fn get_margin_box_height(&self) -> f32 {
self.size.height +
self.padding.total_vertical() +
self.border_widths.total_vertical() +
self.margin.total_vertical()
}
pub fn get_left_leading(&self) -> f32 {
self.margin.left +
self.padding.left +
self.border_widths.left
}
pub fn get_top_leading(&self) -> f32 {
self.margin.top +
self.padding.top +
self.border_widths.top
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct LayoutedRectangle {
pub size: LayoutSize,
pub position: PositionInfo,
pub padding: ResolvedOffsets,
pub margin: ResolvedOffsets,
pub border_widths: ResolvedOffsets,
pub overflow: Overflow,
}