1use std::{fmt, collections::BTreeMap};
2use azul_css::{
3 LayoutRect, LayoutPoint, LayoutSize, PixelValue, StyleFontSize,
4 StyleTextColor, ColorU as StyleColorU, Overflow,
5 StyleTextAlignmentHorz, StyleTextAlignmentVert,
6};
7use crate::{
8 app_resources::{Words, ScaledWords, FontInstanceKey, WordPositions, LayoutedGlyphs},
9 id_tree::{NodeId, NodeDataContainer},
10 dom::{DomHash, ScrollTagId},
11 callbacks::PipelineId,
12};
13
14pub const DEFAULT_FONT_SIZE_PX: isize = 16;
15pub const DEFAULT_FONT_SIZE: StyleFontSize = StyleFontSize(PixelValue::const_px(DEFAULT_FONT_SIZE_PX));
16pub const DEFAULT_FONT_ID: &str = "serif";
17pub const DEFAULT_FONT_COLOR: StyleTextColor = StyleTextColor(StyleColorU { r: 0, b: 0, g: 0, a: 255 });
18pub const DEFAULT_LINE_HEIGHT: f32 = 1.0;
19pub const DEFAULT_WORD_SPACING: f32 = 1.0;
20pub const DEFAULT_LETTER_SPACING: f32 = 0.0;
21pub const DEFAULT_TAB_WIDTH: f32 = 4.0;
22
23#[derive(Debug, Clone, PartialEq, PartialOrd)]
24pub struct InlineTextLayout {
25 pub lines: Vec<InlineTextLine>,
26}
27
28#[derive(Debug, Clone, PartialEq, PartialOrd)]
29pub struct InlineTextLine {
30 pub bounds: LayoutRect,
31 pub word_start: usize,
33 pub word_end: usize,
35}
36
37impl InlineTextLine {
38 pub const fn new(bounds: LayoutRect, word_start: usize, word_end: usize) -> Self {
39 Self { bounds, word_start, word_end }
40 }
41}
42
43impl InlineTextLayout {
44
45 pub fn get_leading(&self) -> f32 {
46 match self.lines.first() {
47 None => 0.0,
48 Some(s) => s.bounds.origin.x,
49 }
50 }
51
52 pub fn get_trailing(&self) -> f32 {
53 match self.lines.first() {
54 None => 0.0,
55 Some(s) => s.bounds.origin.x + s.bounds.size.width,
56 }
57 }
58
59 pub const fn new(lines: Vec<InlineTextLine>) -> Self {
60 Self { lines }
61 }
62
63 #[inline]
64 #[must_use]
65 pub fn get_bounds(&self) -> LayoutRect {
66 LayoutRect::union(self.lines.iter().map(|c| c.bounds)).unwrap_or(LayoutRect::zero())
67 }
68
69 #[must_use]
70 pub fn get_children_horizontal_diff_to_right_edge(&self, parent: &LayoutRect) -> Vec<f32> {
71 let parent_right_edge = parent.origin.x + parent.size.width;
72 let parent_left_edge = parent.origin.x;
73 self.lines.iter().map(|line| {
74 let child_right_edge = line.bounds.origin.x + line.bounds.size.width;
75 let child_left_edge = line.bounds.origin.x;
76 (child_left_edge - parent_left_edge) + (parent_right_edge - child_right_edge)
77 }).collect()
78 }
79
80 pub fn align_children_horizontal(&mut self, horizontal_alignment: StyleTextAlignmentHorz) {
82 let shift_multiplier = match calculate_horizontal_shift_multiplier(horizontal_alignment) {
83 None => return,
84 Some(s) => s,
85 };
86 let self_bounds = self.get_bounds();
87 let horz_diff = self.get_children_horizontal_diff_to_right_edge(&self_bounds);
88
89 for (line, shift) in self.lines.iter_mut().zip(horz_diff.into_iter()) {
90 line.bounds.origin.x += shift * shift_multiplier;
91 }
92 }
93
94 pub fn align_children_vertical_in_parent_bounds(&mut self, parent_size: &LayoutSize, vertical_alignment: StyleTextAlignmentVert) {
96
97 let shift_multiplier = match calculate_vertical_shift_multiplier(vertical_alignment) {
98 None => return,
99 Some(s) => s,
100 };
101
102 let self_bounds = self.get_bounds();
103 let child_bottom_edge = self_bounds.origin.y + self_bounds.size.height;
104 let child_top_edge = self_bounds.origin.y;
105 let shift = child_top_edge + (parent_size.height - child_bottom_edge);
106
107 for line in self.lines.iter_mut() {
108 line.bounds.origin.y += shift * shift_multiplier;
109 }
110 }
111}
112
113#[inline]
114pub fn calculate_horizontal_shift_multiplier(horizontal_alignment: StyleTextAlignmentHorz) -> Option<f32> {
115 use azul_css::StyleTextAlignmentHorz::*;
116 match horizontal_alignment {
117 Left => None,
118 Center => Some(0.5), Right => Some(1.0), }
121}
122
123#[inline]
124pub fn calculate_vertical_shift_multiplier(vertical_alignment: StyleTextAlignmentVert) -> Option<f32> {
125 use azul_css::StyleTextAlignmentVert::*;
126 match vertical_alignment {
127 Top => None,
128 Center => Some(0.5), Bottom => Some(1.0), }
131}
132
133#[derive(Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
134#[repr(C)]
135pub struct ExternalScrollId(pub u64, pub PipelineId);
136
137impl ::std::fmt::Display for ExternalScrollId {
138 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139 write!(f, "ExternalScrollId({:0x}, {})", self.0, self.1)
140 }
141}
142
143impl ::std::fmt::Debug for ExternalScrollId {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 write!(f, "{}", self)
146 }
147}
148
149#[derive(Default, Debug, Clone)]
150pub struct ScrolledNodes {
151 pub overflowing_nodes: BTreeMap<NodeId, OverflowingScrollNode>,
152 pub tags_to_node_ids: BTreeMap<ScrollTagId, NodeId>,
153}
154
155#[derive(Debug, Clone)]
156pub struct OverflowingScrollNode {
157 pub child_rect: LayoutRect,
158 pub parent_external_scroll_id: ExternalScrollId,
159 pub parent_dom_hash: DomHash,
160 pub scroll_tag_id: ScrollTagId,
161}
162
163#[derive(Debug, Default, Clone)]
164pub struct LayoutResult {
165 pub rects: NodeDataContainer<PositionedRectangle>,
166 pub word_cache: BTreeMap<NodeId, Words>,
167 pub scaled_words: BTreeMap<NodeId, (ScaledWords, FontInstanceKey)>,
168 pub positioned_word_cache: BTreeMap<NodeId, (WordPositions, FontInstanceKey)>,
169 pub layouted_glyph_cache: BTreeMap<NodeId, LayoutedGlyphs>,
170 pub node_depths: Vec<(usize, NodeId)>,
171}
172
173#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
175pub struct TextLayoutOptions {
176 pub font_size_px: PixelValue,
178 pub line_height: Option<f32>,
180 pub letter_spacing: Option<PixelValue>,
182 pub word_spacing: Option<PixelValue>,
184 pub tab_width: Option<f32>,
187 pub max_horizontal_width: Option<f32>,
189 pub leading: Option<f32>,
192 pub holes: Vec<LayoutRect>,
197}
198
199#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
202pub struct ResolvedTextLayoutOptions {
203 pub font_size_px: f32,
205 pub line_height: Option<f32>,
207 pub letter_spacing: Option<f32>,
209 pub word_spacing: Option<f32>,
211 pub tab_width: Option<f32>,
214 pub max_horizontal_width: Option<f32>,
216 pub leading: Option<f32>,
219 pub holes: Vec<LayoutRect>,
224}
225
226#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
227pub struct ResolvedOffsets {
228 pub top: f32,
229 pub left: f32,
230 pub right: f32,
231 pub bottom: f32,
232}
233
234impl ResolvedOffsets {
235 pub const fn zero() -> Self { Self { top: 0.0, left: 0.0, right: 0.0, bottom: 0.0 } }
236 pub fn total_vertical(&self) -> f32 { self.top + self.bottom }
237 pub fn total_horizontal(&self) -> f32 { self.left + self.right }
238}
239
240#[derive(Debug, Clone, PartialEq, PartialOrd)]
241pub struct PositionedRectangle {
242 pub size: LayoutSize,
244 pub position: PositionInfo,
246 pub padding: ResolvedOffsets,
248 pub margin: ResolvedOffsets,
250 pub border_widths: ResolvedOffsets,
252 pub resolved_text_layout_options: Option<(ResolvedTextLayoutOptions, InlineTextLayout, LayoutRect)>,
255 pub overflow: Overflow,
257}
258
259#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
260pub enum PositionInfo {
261 Static { x_offset: f32, y_offset: f32 },
262 Fixed { x_offset: f32, y_offset: f32 },
263 Absolute { x_offset: f32, y_offset: f32 },
264 Relative { x_offset: f32, y_offset: f32 },
265}
266
267impl PositionInfo {
268 pub fn is_positioned(&self) -> bool {
269 match self {
270 PositionInfo::Static { .. } => false,
271 PositionInfo::Fixed { .. } => true,
272 PositionInfo::Absolute { .. } => true,
273 PositionInfo::Relative { .. } => true,
274 }
275 }
276}
277impl PositionedRectangle {
278
279 pub fn get_static_bounds(&self) -> Option<LayoutRect> {
280 match self.position {
281 PositionInfo::Static { x_offset, y_offset } => Some(LayoutRect::new(
282 LayoutPoint::new(x_offset, y_offset), self.size
283 )),
284 PositionInfo::Fixed { .. } => None,
285 PositionInfo::Absolute { .. } => None, PositionInfo::Relative { x_offset, y_offset } => Some(LayoutRect::new(
287 LayoutPoint::new(x_offset, y_offset), self.size
288 )), }
290 }
291
292 pub fn to_layouted_rectangle(&self) -> LayoutedRectangle {
293 LayoutedRectangle {
294 size: self.size,
295 position: self.position,
296 padding: self.padding,
297 margin: self.margin,
298 border_widths: self.border_widths,
299 overflow: self.overflow,
300 }
301 }
302
303 pub fn get_content_size(&self) -> LayoutSize {
305 self.size
306 }
307
308 pub fn get_background_bounds(&self) -> (LayoutSize, PositionInfo) {
310
311 use crate::ui_solver::PositionInfo::*;
312
313 let b_size = LayoutSize {
314 width: self.size.width + self.padding.total_horizontal() + self.border_widths.total_horizontal(),
315 height: self.size.height + self.padding.total_vertical() + self.border_widths.total_vertical(),
316 };
317
318 let x_offset_add = 0.0 - self.padding.left - self.border_widths.left;
319 let y_offset_add = 0.0 - self.padding.top - self.border_widths.top;
320
321 let b_position = match self.position {
322 Static { x_offset, y_offset } => Static { x_offset: x_offset + x_offset_add, y_offset: y_offset + y_offset_add },
323 Fixed { x_offset, y_offset } => Fixed { x_offset: x_offset + x_offset_add, y_offset: y_offset + y_offset_add },
324 Relative { x_offset, y_offset } => Relative { x_offset: x_offset + x_offset_add, y_offset: y_offset + y_offset_add },
325 Absolute { x_offset, y_offset } => Absolute { x_offset: x_offset + x_offset_add, y_offset: y_offset + y_offset_add },
326 };
327
328 (b_size, b_position)
329 }
330
331 pub fn get_margin_box_width(&self) -> f32 {
332 self.size.width +
333 self.padding.total_horizontal() +
334 self.border_widths.total_horizontal() +
335 self.margin.total_horizontal()
336 }
337
338 pub fn get_margin_box_height(&self) -> f32 {
339 self.size.height +
340 self.padding.total_vertical() +
341 self.border_widths.total_vertical() +
342 self.margin.total_vertical()
343 }
344
345 pub fn get_left_leading(&self) -> f32 {
346 self.margin.left +
347 self.padding.left +
348 self.border_widths.left
349 }
350
351 pub fn get_top_leading(&self) -> f32 {
352 self.margin.top +
353 self.padding.top +
354 self.border_widths.top
355 }
356}
357
358#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
361pub struct LayoutedRectangle {
362 pub size: LayoutSize,
364 pub position: PositionInfo,
366 pub padding: ResolvedOffsets,
368 pub margin: ResolvedOffsets,
370 pub border_widths: ResolvedOffsets,
372 pub overflow: Overflow,
374}