use azul_core::{
geom::{LogicalPosition, LogicalRect, LogicalSize},
ui_solver::ResolvedOffsets,
};
use azul_css::props::{
basic::{pixel::PixelValue, PhysicalSize, PropertyContext, ResolutionContext, SizeMetric},
layout::LayoutWritingMode,
style::{StyleDirection, StyleTextOrientation},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BoxSizing {
#[default]
ContentBox,
BorderBox,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct PositionedRectangle {
pub bounds: LogicalRect,
pub margin: ResolvedOffsets,
pub border: ResolvedOffsets,
pub padding: ResolvedOffsets,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct EdgeSizes {
pub top: f32,
pub right: f32,
pub bottom: f32,
pub left: f32,
}
impl EdgeSizes {
pub fn horizontal_sum(&self) -> f32 {
self.left + self.right
}
pub fn vertical_sum(&self) -> f32 {
self.top + self.bottom
}
pub fn main_start(&self, wm: LayoutWritingMode) -> f32 {
match wm {
LayoutWritingMode::HorizontalTb => self.top,
LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => self.left,
}
}
pub fn main_end(&self, wm: LayoutWritingMode) -> f32 {
match wm {
LayoutWritingMode::HorizontalTb => self.bottom,
LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => self.right,
}
}
pub fn main_sum(&self, wm: LayoutWritingMode) -> f32 {
self.main_start(wm) + self.main_end(wm)
}
pub fn cross_start(&self, wm: LayoutWritingMode) -> f32 {
match wm {
LayoutWritingMode::HorizontalTb => self.left,
LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => self.top,
}
}
pub fn cross_end(&self, wm: LayoutWritingMode) -> f32 {
match wm {
LayoutWritingMode::HorizontalTb => self.right,
LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => self.bottom,
}
}
pub fn cross_sum(&self, wm: LayoutWritingMode) -> f32 {
self.cross_start(wm) + self.cross_end(wm)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub enum UnresolvedMargin {
#[default]
Zero,
Auto,
Length(PixelValue),
}
impl UnresolvedMargin {
pub fn is_auto(&self) -> bool {
matches!(self, UnresolvedMargin::Auto)
}
pub fn resolve(&self, ctx: &ResolutionContext) -> f32 {
match self {
UnresolvedMargin::Zero => 0.0,
UnresolvedMargin::Auto => 0.0, UnresolvedMargin::Length(pv) => pv.resolve_with_context(ctx, PropertyContext::Margin),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct UnresolvedEdge<T> {
pub top: T,
pub right: T,
pub bottom: T,
pub left: T,
}
impl<T> UnresolvedEdge<T> {
pub fn new(top: T, right: T, bottom: T, left: T) -> Self {
Self { top, right, bottom, left }
}
}
impl UnresolvedEdge<UnresolvedMargin> {
pub fn resolve(&self, ctx: &ResolutionContext) -> EdgeSizes {
EdgeSizes {
top: self.top.resolve(ctx),
right: self.right.resolve(ctx),
bottom: self.bottom.resolve(ctx),
left: self.left.resolve(ctx),
}
}
pub fn get_margin_auto(&self) -> MarginAuto {
MarginAuto {
top: self.top.is_auto(),
right: self.right.is_auto(),
bottom: self.bottom.is_auto(),
left: self.left.is_auto(),
}
}
}
impl UnresolvedEdge<PixelValue> {
pub fn resolve(&self, ctx: &ResolutionContext, prop_ctx: PropertyContext) -> EdgeSizes {
EdgeSizes {
top: self.top.resolve_with_context(ctx, prop_ctx),
right: self.right.resolve_with_context(ctx, prop_ctx),
bottom: self.bottom.resolve_with_context(ctx, prop_ctx),
left: self.left.resolve_with_context(ctx, prop_ctx),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ResolutionParams {
pub containing_block: LogicalSize,
pub viewport_size: LogicalSize,
pub element_font_size: f32,
pub root_font_size: f32,
}
impl ResolutionParams {
pub fn to_resolution_context(&self) -> ResolutionContext {
ResolutionContext {
element_font_size: self.element_font_size,
parent_font_size: self.element_font_size,
root_font_size: self.root_font_size,
element_size: None,
containing_block_size: PhysicalSize::new(
self.containing_block.width,
self.containing_block.height,
),
viewport_size: PhysicalSize::new(
self.viewport_size.width,
self.viewport_size.height,
),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct UnresolvedBoxProps {
pub margin: UnresolvedEdge<UnresolvedMargin>,
pub padding: UnresolvedEdge<PixelValue>,
pub border: UnresolvedEdge<PixelValue>,
}
impl UnresolvedBoxProps {
pub fn resolve(&self, params: &ResolutionParams) -> ResolvedBoxProps {
let ctx = params.to_resolution_context();
ResolvedBoxProps {
margin: self.margin.resolve(&ctx),
padding: self.padding.resolve(&ctx, PropertyContext::Padding),
border: self.border.resolve(&ctx, PropertyContext::BorderWidth),
margin_auto: self.margin.get_margin_auto(),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct MarginAuto {
pub left: bool,
pub right: bool,
pub top: bool,
pub bottom: bool,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ResolvedBoxProps {
pub margin: EdgeSizes,
pub padding: EdgeSizes,
pub border: EdgeSizes,
pub margin_auto: MarginAuto,
}
impl ResolvedBoxProps {
pub fn inner_size(&self, outer_size: LogicalSize, wm: LayoutWritingMode) -> LogicalSize {
let outer_main = outer_size.main(wm);
let outer_cross = outer_size.cross(wm);
let cross_axis_spacing = self.padding.cross_sum(wm) + self.border.cross_sum(wm);
let main_axis_spacing = self.padding.main_sum(wm) + self.border.main_sum(wm);
let inner_main = (outer_main - main_axis_spacing).max(0.0);
let inner_cross = (outer_cross - cross_axis_spacing).max(0.0);
LogicalSize::from_main_cross(inner_main, inner_cross, wm)
}
pub fn content_box(&self, border_box: LogicalRect) -> LogicalRect {
let x = border_box.origin.x + self.border.left + self.padding.left;
let y = border_box.origin.y + self.border.top + self.padding.top;
let w = (border_box.size.width - self.border.horizontal_sum() - self.padding.horizontal_sum()).max(0.0);
let h = (border_box.size.height - self.border.vertical_sum() - self.padding.vertical_sum()).max(0.0);
LogicalRect { origin: LogicalPosition { x, y }, size: LogicalSize { width: w, height: h } }
}
pub fn padding_box(&self, border_box: LogicalRect) -> LogicalRect {
let x = border_box.origin.x + self.border.left;
let y = border_box.origin.y + self.border.top;
let w = (border_box.size.width - self.border.horizontal_sum()).max(0.0);
let h = (border_box.size.height - self.border.vertical_sum()).max(0.0);
LogicalRect { origin: LogicalPosition { x, y }, size: LogicalSize { width: w, height: h } }
}
pub fn margin_box(&self, border_box: LogicalRect) -> LogicalRect {
let x = border_box.origin.x - self.margin.left;
let y = border_box.origin.y - self.margin.top;
let w = border_box.size.width + self.margin.horizontal_sum();
let h = border_box.size.height + self.margin.vertical_sum();
LogicalRect { origin: LogicalPosition { x, y }, size: LogicalSize { width: w, height: h } }
}
pub fn horizontal_mbp(&self) -> f32 {
self.margin.horizontal_sum() + self.border.horizontal_sum() + self.padding.horizontal_sum()
}
pub fn vertical_mbp(&self) -> f32 {
self.margin.vertical_sum() + self.border.vertical_sum() + self.padding.vertical_sum()
}
pub fn horizontal_bp(&self) -> f32 {
self.border.horizontal_sum() + self.padding.horizontal_sum()
}
pub fn vertical_bp(&self) -> f32 {
self.border.vertical_sum() + self.padding.vertical_sum()
}
}
pub type BoxProps = ResolvedBoxProps;
#[derive(Debug, Clone, Copy, Default)]
#[repr(C)]
pub struct PackedBoxProps {
pub margin: [i16; 4], pub padding: [i16; 4], pub border: [i16; 4], pub margin_auto: MarginAuto,
}
impl PackedBoxProps {
#[inline]
pub fn pack(bp: &ResolvedBoxProps) -> Self {
Self {
margin: Self::pack_edge(&bp.margin),
padding: Self::pack_edge(&bp.padding),
border: Self::pack_edge(&bp.border),
margin_auto: bp.margin_auto,
}
}
#[inline]
pub fn unpack(&self) -> ResolvedBoxProps {
ResolvedBoxProps {
margin: Self::unpack_edge(&self.margin),
padding: Self::unpack_edge(&self.padding),
border: Self::unpack_edge(&self.border),
margin_auto: self.margin_auto,
}
}
#[inline]
pub fn inner_size(&self, outer_size: LogicalSize, wm: LayoutWritingMode) -> LogicalSize {
self.unpack().inner_size(outer_size, wm)
}
#[inline]
pub fn content_box(&self, border_box: LogicalRect) -> LogicalRect {
self.unpack().content_box(border_box)
}
#[inline]
pub fn padding_box(&self, border_box: LogicalRect) -> LogicalRect {
self.unpack().padding_box(border_box)
}
#[inline]
pub fn margin_box(&self, border_box: LogicalRect) -> LogicalRect {
self.unpack().margin_box(border_box)
}
#[inline]
pub fn horizontal_mbp(&self) -> f32 {
self.unpack().horizontal_mbp()
}
#[inline]
pub fn vertical_mbp(&self) -> f32 {
self.unpack().vertical_mbp()
}
#[inline]
pub fn horizontal_bp(&self) -> f32 {
self.unpack().horizontal_bp()
}
#[inline]
pub fn vertical_bp(&self) -> f32 {
self.unpack().vertical_bp()
}
#[inline(always)]
fn pack_edge(e: &EdgeSizes) -> [i16; 4] {
[
(e.top * 10.0).round() as i16,
(e.right * 10.0).round() as i16,
(e.bottom * 10.0).round() as i16,
(e.left * 10.0).round() as i16,
]
}
#[inline(always)]
fn unpack_edge(e: &[i16; 4]) -> EdgeSizes {
EdgeSizes {
top: e[0] as f32 * 0.1,
right: e[1] as f32 * 0.1,
bottom: e[2] as f32 * 0.1,
left: e[3] as f32 * 0.1,
}
}
}
pub use azul_css::props::layout::{LayoutClear, LayoutFloat};
#[derive(Debug, Clone, Copy, Default)]
pub struct IntrinsicSizes {
pub min_content_width: f32,
pub max_content_width: f32,
pub preferred_width: Option<f32>,
pub min_content_height: f32,
pub max_content_height: f32,
pub preferred_height: Option<f32>,
}
pub fn is_horizontal(writing_mode: LayoutWritingMode) -> bool {
matches!(writing_mode, LayoutWritingMode::HorizontalTb)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WritingModeContext {
pub writing_mode: LayoutWritingMode,
pub direction: StyleDirection,
pub text_orientation: StyleTextOrientation,
}
impl Default for WritingModeContext {
fn default() -> Self {
Self {
writing_mode: LayoutWritingMode::HorizontalTb,
direction: StyleDirection::Ltr,
text_orientation: StyleTextOrientation::Mixed,
}
}
}
impl WritingModeContext {
pub fn new(
writing_mode: LayoutWritingMode,
direction: StyleDirection,
text_orientation: StyleTextOrientation,
) -> Self {
let used_direction = if text_orientation == StyleTextOrientation::Upright {
StyleDirection::Ltr
} else {
direction
};
Self {
writing_mode,
direction: used_direction,
text_orientation,
}
}
pub fn used_direction(&self) -> StyleDirection {
match self.writing_mode {
LayoutWritingMode::HorizontalTb => self.direction,
LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => {
if self.text_orientation == StyleTextOrientation::Upright {
StyleDirection::Ltr
} else {
self.direction
}
}
}
}
pub fn is_horizontal(&self) -> bool {
is_horizontal(self.writing_mode)
}
pub fn inline_size_is_width(&self) -> bool {
self.is_horizontal()
}
pub fn block_size_is_height(&self) -> bool {
self.is_horizontal()
}
pub fn is_inline_reversed(&self) -> bool {
self.used_direction() == StyleDirection::Rtl
}
}