orinium_browser/engine/renderer_model/
draw_command.rs1use crate::engine::layouter::types::{Color, InfoNode, NodeKind, TextDecoration, TextStyle};
4use ui_layout::LayoutNode;
5
6#[derive(Debug, Clone)]
7pub enum DrawCommand {
8 DrawText {
9 x: f32,
10 y: f32,
11 text: String,
12 style: TextStyle,
13 },
14 DrawRect {
15 x: f32,
16 y: f32,
17 width: f32,
18 height: f32,
19 color: Color,
20 },
21 DrawPolygon {
22 points: Vec<(f32, f32)>,
23 color: Color,
24 },
25 DrawEllipse {
26 center: (f32, f32),
27 radius_x: f32,
28 radius_y: f32,
29 color: Color,
30 },
31 PushClip {
32 x: f32,
33 y: f32,
34 width: f32,
35 height: f32,
36 },
37 PopClip,
38 PushTransform {
39 dx: f32,
40 dy: f32,
41 },
42 PopTransform,
43}
44
45pub fn generate_draw_commands(layout: &LayoutNode, info: &InfoNode) -> Vec<DrawCommand> {
47 let mut commands = Vec::new();
48
49 match &info.kind {
50 NodeKind::Text { texts, style, .. } => {
51 debug_assert_eq!(
52 texts.len(),
53 layout.self_fragments.len(),
54 "`generate_draw_commands` may be called before layout is complete."
55 );
56 debug_assert_eq!(
57 layout.self_fragments.len(),
58 layout.placements.len(),
59 "Layout should have placements for all self fragments."
60 );
61 for ((text, placement), fragment) in texts
62 .iter()
63 .zip(&layout.placements)
64 .zip(&layout.self_fragments)
65 {
66 let (abs_x, abs_y) = placement.offset;
67
68 commands.push(DrawCommand::DrawText {
70 x: abs_x,
71 y: abs_y,
72 text: text.clone(),
73 style: *style,
74 });
75
76 let font_size = style.font_size;
78 let line_thickness = (font_size * 0.08).max(1.0);
79
80 let (line_y, draw) = match style.text_decoration {
81 TextDecoration::None => (0.0, false),
82 TextDecoration::Underline => (abs_y + font_size, true),
83 TextDecoration::LineThrough => (abs_y + font_size * 0.5, true),
84 TextDecoration::Overline => (abs_y, true),
85 };
86
87 if draw {
88 commands.push(DrawCommand::DrawRect {
89 x: abs_x,
90 y: line_y,
91 width: fragment.width(),
92 height: line_thickness,
93 color: style.color,
94 });
95 }
96 }
97 }
98
99 NodeKind::Container {
100 scroll_offset_x,
101 scroll_offset_y,
102 style,
103 ..
104 } => {
105 for box_model in &layout.layout_boxes {
106 let border_box = box_model.border_box;
107 let padding_box = box_model.padding_box;
108 let content_box = box_model.content_box;
109
110 commands.push(DrawCommand::PushTransform {
112 dx: border_box.x,
113 dy: border_box.y,
114 });
115
116 let bc = &style.border_color;
117
118 let border_width = (padding_box.y - border_box.y).max(0.0);
120 commands.push(DrawCommand::DrawRect {
121 x: 0.0,
122 y: 0.0,
123 width: border_box.width,
124 height: border_width,
125 color: bc.top,
126 });
127
128 let border_width = (border_box.y + border_box.height
130 - (padding_box.y + padding_box.height))
131 .max(0.0);
132 commands.push(DrawCommand::DrawRect {
133 x: 0.0,
134 y: border_box.height - border_width,
135 width: border_box.width,
136 height: border_width,
137 color: bc.bottom,
138 });
139
140 let border_width = (padding_box.x - border_box.x).max(0.0);
142 commands.push(DrawCommand::DrawRect {
143 x: 0.0,
144 y: 0.0,
145 width: border_width,
146 height: border_box.height,
147 color: bc.left,
148 });
149
150 let border_width = (border_box.x + border_box.width
152 - (padding_box.x + padding_box.width))
153 .max(0.0);
154 commands.push(DrawCommand::DrawRect {
155 x: border_box.width - border_width,
156 y: 0.0,
157 width: border_width,
158 height: border_box.height,
159 color: bc.right,
160 });
161
162 commands.push(DrawCommand::PushClip {
164 x: padding_box.x - border_box.x,
165 y: padding_box.y - border_box.y,
166 width: padding_box.width,
167 height: padding_box.height,
168 });
169
170 commands.push(DrawCommand::DrawRect {
172 x: padding_box.x - border_box.x,
173 y: padding_box.y - border_box.y,
174 width: padding_box.width,
175 height: padding_box.height,
176 color: style.background_color,
177 });
178
179 commands.push(DrawCommand::PushTransform {
181 dx: content_box.x - border_box.x,
182 dy: content_box.y - border_box.y,
183 });
184 commands.push(DrawCommand::PushTransform {
185 dx: *scroll_offset_x,
186 dy: -*scroll_offset_y,
187 });
188 }
189 }
190 }
191
192 for (child_layout, child_info) in layout.children.iter().zip(&info.children) {
193 commands.extend(generate_draw_commands(child_layout, child_info));
194 }
195
196 if matches!(info.kind, NodeKind::Container { .. }) {
198 for _ in &layout.layout_boxes {
199 commands.push(DrawCommand::PopTransform);
200 commands.push(DrawCommand::PopTransform);
201 commands.push(DrawCommand::PopClip);
202 commands.push(DrawCommand::PopTransform);
203 }
204 }
205
206 commands
207}