1use crate::renderer::{CursorShape, CursorStyle};
6use carnelian::Size;
7use carnelian::drawing::TextGrid;
8use carnelian::render::{Context as RenderContext, Path};
9use euclid::{point2, vec2};
10const LINE_THICKNESS_FACTOR: f32 = 1.0 / 16.0;
14
15fn path_for_block(size: &Size, render_context: &mut RenderContext) -> Path {
16 let mut path_builder = render_context.path_builder().expect("path_builder");
17 path_builder
18 .move_to(point2(0.0, 0.0))
19 .line_to(point2(size.width, 0.0))
20 .line_to(point2(size.width, size.height))
21 .line_to(point2(0.0, size.height))
22 .line_to(point2(0.0, 0.0));
23 path_builder.build()
24}
25
26pub struct Line {
27 position_y: f32,
28 thickness: f32,
29}
30
31impl Line {
32 pub fn new(line_metrics: ttf_parser::LineMetrics, textgrid: &TextGrid) -> Self {
33 let scale_y = textgrid.scale.y;
34 let offset_y = textgrid.offset.y;
35 let position = line_metrics.position as f32 * scale_y;
36 let thickness = line_metrics.thickness as f32 * scale_y;
37 let position_y = offset_y - position;
38 Self { thickness, position_y }
39 }
40}
41
42fn line_bounds(line: Option<Line>, default_bounds: (f32, f32), cell_height: f32) -> (f32, f32) {
43 line.map_or(default_bounds, |Line { thickness, position_y: line_y }| {
44 let top = line_y - thickness / 2.0;
45 let bottom = line_y + thickness / 2.0;
46 let (_, default_bottom) = default_bounds;
47
48 if bottom > cell_height {
49 return (default_bottom - thickness, default_bottom);
50 }
51 (top, bottom)
52 })
53}
54
55pub fn path_for_underline(
56 size: &Size,
57 render_context: &mut RenderContext,
58 line: Option<Line>,
59) -> Path {
60 let mut path_builder = render_context.path_builder().expect("path_builder");
61 let default_top = size.height - size.height * LINE_THICKNESS_FACTOR;
62 let default_bottom = size.height;
63 let (top, bottom) = line_bounds(line, (default_top, default_bottom), size.height);
64
65 path_builder
66 .move_to(point2(0.0, top))
67 .line_to(point2(size.width, top))
68 .line_to(point2(size.width, bottom))
69 .line_to(point2(0.0, bottom))
70 .line_to(point2(0.0, top));
71 path_builder.build()
72}
73
74fn path_for_beam(size: &Size, render_context: &mut RenderContext) -> Path {
75 let mut path_builder = render_context.path_builder().expect("path_builder");
76 let right = size.height * LINE_THICKNESS_FACTOR;
77 path_builder
78 .move_to(point2(0.0, 0.0))
79 .line_to(point2(right, 0.0))
80 .line_to(point2(right, size.height))
81 .line_to(point2(0.0, size.height))
82 .line_to(point2(0.0, 0.0));
83 path_builder.build()
84}
85
86fn path_for_hollow_block(size: &Size, render_context: &mut RenderContext) -> Path {
87 let mut path_builder = render_context.path_builder().expect("path_builder");
88 let inset = size.height * LINE_THICKNESS_FACTOR;
89 let bottom_start = size.height - inset;
90 let right_start = size.width - inset;
91 path_builder
92 .move_to(point2(0.0, 0.0))
94 .line_to(point2(size.width, 0.0))
95 .line_to(point2(size.width, inset))
96 .line_to(point2(0.0, inset))
97 .line_to(point2(0.0, 0.0))
98 .move_to(point2(0.0, bottom_start))
100 .line_to(point2(size.width, bottom_start))
101 .line_to(point2(size.width, size.height))
102 .line_to(point2(0.0, size.height))
103 .line_to(point2(0.0, bottom_start))
104 .move_to(point2(0.0, inset))
106 .line_to(point2(inset, inset))
107 .line_to(point2(inset, bottom_start))
108 .line_to(point2(0.0, bottom_start))
109 .line_to(point2(0.0, inset))
110 .move_to(point2(right_start, inset))
112 .line_to(point2(size.width, inset))
113 .line_to(point2(size.width, bottom_start))
114 .line_to(point2(right_start, bottom_start))
115 .line_to(point2(right_start, inset));
116 path_builder.build()
117}
118
119pub fn path_for_strikeout(
120 size: &Size,
121 render_context: &mut RenderContext,
122 line: Option<Line>,
123) -> Path {
124 let mut path_builder = render_context.path_builder().expect("path_builder");
125 let default_top = size.height / 2.0;
126 let default_bottom = default_top + size.height * LINE_THICKNESS_FACTOR;
127 let (top, bottom) = line_bounds(line, (default_top, default_bottom), size.height);
128
129 path_builder
130 .move_to(point2(0.0, top))
131 .line_to(point2(size.width, top))
132 .line_to(point2(size.width, bottom))
133 .line_to(point2(0.0, bottom))
134 .line_to(point2(0.0, top));
135 path_builder.build()
136}
137
138fn path_for_unicode_2500(size: &Size, render_context: &mut RenderContext) -> Path {
140 let mut path_builder = render_context.path_builder().expect("path_builder");
141 let thickness = size.height * LINE_THICKNESS_FACTOR;
142 let top = size.height / 2.0 - thickness / 2.0;
143 let bottom = top + thickness;
144 path_builder
145 .move_to(point2(0.0, top))
146 .line_to(point2(size.width, top))
147 .line_to(point2(size.width, bottom))
148 .line_to(point2(0.0, bottom))
149 .line_to(point2(0.0, top));
150 path_builder.build()
151}
152
153fn path_for_unicode_2502(size: &Size, render_context: &mut RenderContext) -> Path {
155 let mut path_builder = render_context.path_builder().expect("path_builder");
156 let thickness = size.height * LINE_THICKNESS_FACTOR;
157 let left = size.width / 2.0 - thickness / 2.0;
158 let right = left + thickness;
159 path_builder
160 .move_to(point2(left, 0.0))
161 .line_to(point2(right, 0.0))
162 .line_to(point2(right, size.height))
163 .line_to(point2(left, size.height))
164 .line_to(point2(left, 0.0));
165 path_builder.build()
166}
167
168fn path_for_unicode_256d(size: &Size, render_context: &mut RenderContext) -> Path {
170 let mut path_builder = render_context.path_builder().expect("path_builder");
171 let thickness = size.height * LINE_THICKNESS_FACTOR;
172 let bottom_left = size.width / 2.0 - thickness / 2.0;
173 let bottom_right = bottom_left + thickness;
174 let right_top = size.height / 2.0 - thickness / 2.0;
175 let right_bottom = right_top + thickness;
176 let radius = (size.height * 0.25).min(size.width * 0.25);
177 let inner_radius = radius - thickness / 2.0;
178 let outer_radius = inner_radius + thickness;
179 let kappa = 4.0 / 3.0 * (std::f32::consts::PI / 8.0).tan();
180 let outer_control_dist = kappa * outer_radius;
181 let inner_control_dist = kappa * inner_radius;
182 let center = point2(size.width / 2.0, size.height / 2.0) + vec2(radius, radius);
183 let inner_p1 = center + vec2(-inner_control_dist, -inner_radius);
184 let inner_p2 = center + vec2(-inner_radius, -inner_control_dist);
185 let outer_p1 = center + vec2(-outer_radius, -outer_control_dist);
186 let outer_p2 = center + vec2(-outer_control_dist, -outer_radius);
187 path_builder
188 .move_to(point2(size.width, right_top))
189 .line_to(point2(size.width, right_bottom))
190 .line_to(point2(center.x, right_bottom))
191 .cubic_to(inner_p1, inner_p2, point2(bottom_right, center.y))
192 .line_to(point2(bottom_right, size.height))
193 .line_to(point2(bottom_left, size.height))
194 .line_to(point2(bottom_left, center.y))
195 .cubic_to(outer_p1, outer_p2, point2(center.x, right_top))
196 .line_to(point2(size.width, right_top));
197 path_builder.build()
198}
199
200fn path_for_unicode_256e(size: &Size, render_context: &mut RenderContext) -> Path {
202 let mut path_builder = render_context.path_builder().expect("path_builder");
203 let thickness = size.height * LINE_THICKNESS_FACTOR;
204 let bottom_left = size.width / 2.0 - thickness / 2.0;
205 let bottom_right = bottom_left + thickness;
206 let left_top = size.height / 2.0 - thickness / 2.0;
207 let left_bottom = left_top + thickness;
208 let radius = (size.height * 0.25).min(size.width * 0.25);
209 let inner_radius = radius - thickness / 2.0;
210 let outer_radius = inner_radius + thickness;
211 let kappa = 4.0 / 3.0 * (std::f32::consts::PI / 8.0).tan();
212 let outer_control_dist = kappa * outer_radius;
213 let inner_control_dist = kappa * inner_radius;
214 let center = point2(size.width / 2.0, size.height / 2.0) + vec2(-radius, radius);
215 let inner_p1 = center + vec2(inner_radius, -inner_control_dist);
216 let inner_p2 = center + vec2(inner_control_dist, -inner_radius);
217 let outer_p1 = center + vec2(outer_control_dist, -outer_radius);
218 let outer_p2 = center + vec2(outer_radius, -outer_control_dist);
219 path_builder
220 .move_to(point2(0.0, left_top))
221 .line_to(point2(center.x, left_top))
222 .cubic_to(outer_p1, outer_p2, point2(bottom_right, center.y))
223 .line_to(point2(bottom_right, size.height))
224 .line_to(point2(bottom_left, size.height))
225 .line_to(point2(bottom_left, center.y))
226 .cubic_to(inner_p1, inner_p2, point2(center.x, left_bottom))
227 .line_to(point2(0.0, left_bottom))
228 .line_to(point2(0.0, left_top));
229 path_builder.build()
230}
231
232fn path_for_unicode_256f(size: &Size, render_context: &mut RenderContext) -> Path {
234 let mut path_builder = render_context.path_builder().expect("path_builder");
235 let thickness = size.height * LINE_THICKNESS_FACTOR;
236 let top_left = size.width / 2.0 - thickness / 2.0;
237 let top_right = top_left + thickness;
238 let left_top = size.height / 2.0 - thickness / 2.0;
239 let left_bottom = left_top + thickness;
240 let radius = (size.height * 0.25).min(size.width * 0.25);
241 let inner_radius = radius - thickness / 2.0;
242 let outer_radius = inner_radius + thickness;
243 let kappa = 4.0 / 3.0 * (std::f32::consts::PI / 8.0).tan();
244 let outer_control_dist = kappa * outer_radius;
245 let inner_control_dist = kappa * inner_radius;
246 let center = point2(size.width / 2.0, size.height / 2.0) + vec2(-radius, -radius);
247 let inner_p1 = center + vec2(inner_control_dist, inner_radius);
248 let inner_p2 = center + vec2(inner_radius, inner_control_dist);
249 let outer_p1 = center + vec2(outer_radius, outer_control_dist);
250 let outer_p2 = center + vec2(outer_control_dist, outer_radius);
251 path_builder
252 .move_to(point2(top_left, 0.0))
253 .line_to(point2(top_right, 0.0))
254 .line_to(point2(top_right, center.y))
255 .cubic_to(outer_p1, outer_p2, point2(center.x, left_bottom))
256 .line_to(point2(0.0, left_bottom))
257 .line_to(point2(0.0, left_top))
258 .line_to(point2(center.x, left_top))
259 .cubic_to(inner_p1, inner_p2, point2(top_left, center.y))
260 .line_to(point2(top_left, 0.0));
261 path_builder.build()
262}
263
264fn path_for_unicode_2570(size: &Size, render_context: &mut RenderContext) -> Path {
266 let mut path_builder = render_context.path_builder().expect("path_builder");
267 let thickness = size.height * LINE_THICKNESS_FACTOR;
268 let top_left = size.width / 2.0 - thickness / 2.0;
269 let top_right = top_left + thickness;
270 let right_top = size.height / 2.0 - thickness / 2.0;
271 let right_bottom = right_top + thickness;
272 let radius = (size.height * 0.25).min(size.width * 0.25);
273 let inner_radius = radius - thickness / 2.0;
274 let outer_radius = inner_radius + thickness;
275 let kappa = 4.0 / 3.0 * (std::f32::consts::PI / 8.0).tan();
276 let outer_control_dist = kappa * outer_radius;
277 let inner_control_dist = kappa * inner_radius;
278 let center = point2(size.width / 2.0, size.height / 2.0) + vec2(radius, -radius);
279 let inner_p1 = center + vec2(-inner_radius, inner_control_dist);
280 let inner_p2 = center + vec2(-inner_control_dist, inner_radius);
281 let outer_p1 = center + vec2(-outer_control_dist, outer_radius);
282 let outer_p2 = center + vec2(-outer_radius, outer_control_dist);
283 path_builder
284 .move_to(point2(top_left, 0.0))
285 .line_to(point2(top_right, 0.0))
286 .line_to(point2(top_right, center.y))
287 .cubic_to(inner_p1, inner_p2, point2(center.x, right_top))
288 .line_to(point2(size.width, right_top))
289 .line_to(point2(size.width, right_bottom))
290 .line_to(point2(center.x, right_bottom))
291 .cubic_to(outer_p1, outer_p2, point2(top_left, center.y))
292 .line_to(point2(top_left, 0.0));
293 path_builder.build()
294}
295
296fn path_for_unicode_2591(size: &Size, render_context: &mut RenderContext) -> Path {
298 let mut path_builder = render_context.path_builder().expect("path_builder");
299 const GRID_SIZE: usize = 8;
300 let scale_x = size.width / GRID_SIZE as f32;
301 let scale_y = size.height / GRID_SIZE as f32;
302 for y in 0..GRID_SIZE {
303 for x in 0..GRID_SIZE {
304 let offset = if y % 2 == 0 { x } else { x + 2 };
305 if offset % 4 == 0 {
307 let x0 = x as f32 * scale_x;
308 let y0 = y as f32 * scale_y;
309 let x1 = (x + 1) as f32 * scale_x;
310 let y1 = (y + 1) as f32 * scale_y;
311 path_builder
312 .move_to(point2(x0, y0))
313 .line_to(point2(x1, y0))
314 .line_to(point2(x1, y1))
315 .line_to(point2(x0, y1))
316 .line_to(point2(x0, y0));
317 }
318 }
319 }
320 path_builder.build()
321}
322
323fn path_for_unicode_2592(size: &Size, render_context: &mut RenderContext) -> Path {
325 let mut path_builder = render_context.path_builder().expect("path_builder");
326 const GRID_SIZE: usize = 9;
327 let scale_x = size.width / GRID_SIZE as f32;
328 let scale_y = size.height / GRID_SIZE as f32;
329 for y in 0..GRID_SIZE {
330 for x in 0..GRID_SIZE {
331 let offset = if y % 2 == 0 { x } else { x + 1 };
332 if offset % 2 == 0 {
334 let x0 = x as f32 * scale_x;
335 let y0 = y as f32 * scale_y;
336 let x1 = (x + 1) as f32 * scale_x;
337 let y1 = (y + 1) as f32 * scale_y;
338 path_builder
339 .move_to(point2(x0, y0))
340 .line_to(point2(x1, y0))
341 .line_to(point2(x1, y1))
342 .line_to(point2(x0, y1))
343 .line_to(point2(x0, y0));
344 }
345 }
346 }
347 path_builder.build()
348}
349
350fn path_for_unicode_2593(size: &Size, render_context: &mut RenderContext) -> Path {
352 let mut path_builder = render_context.path_builder().expect("path_builder");
353 const GRID_SIZE: usize = 8;
354 let scale_x = size.width / GRID_SIZE as f32;
355 let scale_y = size.height / GRID_SIZE as f32;
356 for y in 0..GRID_SIZE {
357 for x in 0..GRID_SIZE {
358 let offset = if y % 2 == 0 { x } else { x + 2 };
359 if offset % 4 != 0 {
361 let x0 = x as f32 * scale_x;
362 let y0 = y as f32 * scale_y;
363 let x1 = (x + 1) as f32 * scale_x;
364 let y1 = (y + 1) as f32 * scale_y;
365 path_builder
366 .move_to(point2(x0, y0))
367 .line_to(point2(x1, y0))
368 .line_to(point2(x1, y1))
369 .line_to(point2(x0, y1))
370 .line_to(point2(x0, y0));
371 }
372 }
373 }
374 path_builder.build()
375}
376
377fn path_for_unicode_2714(size: &Size, render_context: &mut RenderContext) -> Path {
379 let mut path_builder = render_context.path_builder().expect("path_builder");
380 const OFFSET_FACTOR: f32 = 1.0 / 12.0;
381 let offset = size.height * OFFSET_FACTOR;
382 let center_offset = offset * 3.0;
383 let left_top = 0.4 * size.height;
384 let right_top = 0.25 * size.height;
385 let center = 0.3 * size.width;
386 let bottom = 0.75 * size.height;
387 let left = 0.05;
388 let right = size.width - 0.05;
389 path_builder
390 .move_to(point2(left, left_top + offset))
391 .line_to(point2(left + offset, left_top))
392 .line_to(point2(center, bottom - center_offset))
393 .line_to(point2(right - offset, right_top))
394 .line_to(point2(right, right_top + offset))
395 .line_to(point2(center, bottom))
396 .line_to(point2(left, left_top + offset));
397 path_builder.build()
398}
399
400fn path_for_unicode_2718(size: &Size, render_context: &mut RenderContext) -> Path {
402 let mut path_builder = render_context.path_builder().expect("path_builder");
403 const OFFSET_FACTOR: f32 = 1.0 / 12.0;
404 let offset = size.height * OFFSET_FACTOR;
405 let center_offset = offset;
406 let top = 0.25 * size.height;
407 let center_x = size.width / 2.0;
408 let center_y = size.height / 2.0;
409 let bottom = 0.75 * size.height;
410 let left = 0.05;
411 let right = size.width - 0.05;
412 path_builder
413 .move_to(point2(left, top + offset))
414 .line_to(point2(left + offset, top))
415 .line_to(point2(center_x, center_y - center_offset))
416 .line_to(point2(right - offset, top))
417 .line_to(point2(right, top + offset))
418 .line_to(point2(center_x + center_offset, center_y))
419 .line_to(point2(right, bottom - offset))
420 .line_to(point2(right - offset, bottom))
421 .line_to(point2(center_x, center_y + center_offset))
422 .line_to(point2(left + offset, bottom))
423 .line_to(point2(left, bottom - offset))
424 .line_to(point2(center_x - center_offset, center_y))
425 .line_to(point2(left, top + offset));
426 path_builder.build()
427}
428
429fn path_for_unicode_276e(size: &Size, render_context: &mut RenderContext) -> Path {
431 let mut path_builder = render_context.path_builder().expect("path_builder");
432 const THICKNESS_FACTOR: f32 = 1.0 / 8.0;
433 let thickness = size.height * THICKNESS_FACTOR;
434 let top = 0.25 * size.height;
435 let bottom = 0.85 * size.height;
436 let left = 0.2 * size.width;
437 let right = 0.8 * size.width;
438 let center_y = top + (bottom - top) / 2.0;
439 path_builder
440 .move_to(point2(right - thickness, top))
441 .line_to(point2(right, top))
442 .line_to(point2(left + thickness, center_y))
443 .line_to(point2(right, bottom))
444 .line_to(point2(right - thickness, bottom))
445 .line_to(point2(left, center_y))
446 .line_to(point2(right - thickness, top));
447 path_builder.build()
448}
449
450fn path_for_unicode_276f(size: &Size, render_context: &mut RenderContext) -> Path {
452 let mut path_builder = render_context.path_builder().expect("path_builder");
453 const THICKNESS_FACTOR: f32 = 1.0 / 8.0;
454 let thickness = size.height * THICKNESS_FACTOR;
455 let top = 0.25 * size.height;
456 let bottom = 0.85 * size.height;
457 let left = 0.2 * size.width;
458 let right = 0.8 * size.width;
459 let center_y = top + (bottom - top) / 2.0;
460 path_builder
461 .move_to(point2(left, top))
462 .line_to(point2(left + thickness, top))
463 .line_to(point2(right, center_y))
464 .line_to(point2(left + thickness, bottom))
465 .line_to(point2(left, bottom))
466 .line_to(point2(right - thickness, center_y))
467 .line_to(point2(left, top));
468 path_builder.build()
469}
470
471fn path_for_unicode_e0b0(size: &Size, render_context: &mut RenderContext) -> Path {
473 let mut path_builder = render_context.path_builder().expect("path_builder");
474 path_builder
475 .move_to(point2(0.0, 0.0))
476 .line_to(point2(size.width, size.height / 2.0))
477 .line_to(point2(0.0, size.height))
478 .line_to(point2(0.0, 0.0));
479 path_builder.build()
480}
481
482fn path_for_unicode_e0b1(size: &Size, render_context: &mut RenderContext) -> Path {
484 let mut path_builder = render_context.path_builder().expect("path_builder");
485 let thickness = size.height * LINE_THICKNESS_FACTOR;
487 let offset = (2.0 * thickness * thickness).sqrt() / 2.0;
488 path_builder
489 .move_to(point2(offset, 0.0))
490 .line_to(point2(size.width + offset, size.height / 2.0))
491 .line_to(point2(offset, size.height))
492 .line_to(point2(-offset, size.height))
493 .line_to(point2(size.width - offset, size.height / 2.0))
494 .line_to(point2(-offset, 0.0))
495 .line_to(point2(offset, 0.0));
496 path_builder.build()
497}
498
499fn path_for_unicode_e0b2(size: &Size, render_context: &mut RenderContext) -> Path {
501 let mut path_builder = render_context.path_builder().expect("path_builder");
502 path_builder
503 .move_to(point2(size.width, 0.0))
504 .line_to(point2(size.width, size.height))
505 .line_to(point2(0.0, size.height / 2.0))
506 .line_to(point2(size.width, 0.0));
507 path_builder.build()
508}
509
510fn path_for_unicode_e0b3(size: &Size, render_context: &mut RenderContext) -> Path {
512 let mut path_builder = render_context.path_builder().expect("path_builder");
513 let thickness = size.height * LINE_THICKNESS_FACTOR;
515 let offset = (2.0 * thickness * thickness).sqrt() / 2.0;
516 path_builder
517 .move_to(point2(size.width + offset, 0.0))
518 .line_to(point2(offset, size.height / 2.0))
519 .line_to(point2(size.width + offset, size.height))
520 .line_to(point2(size.width - offset, size.height))
521 .line_to(point2(-offset, size.height / 2.0))
522 .line_to(point2(size.width - offset, 0.0))
523 .line_to(point2(size.width + offset, 0.0));
524 path_builder.build()
525}
526
527fn path_for_unicode_e0ba(size: &Size, render_context: &mut RenderContext) -> Path {
529 let mut path_builder = render_context.path_builder().expect("path_builder");
530 path_builder
531 .move_to(point2(size.width, 0.0))
532 .line_to(point2(size.width, size.height))
533 .line_to(point2(0.0, size.height))
534 .line_to(point2(size.width, 0.0));
535 path_builder.build()
536}
537
538fn path_for_unicode_e0bc(size: &Size, render_context: &mut RenderContext) -> Path {
540 let mut path_builder = render_context.path_builder().expect("path_builder");
541 path_builder
542 .move_to(point2(0.0, 0.0))
543 .line_to(point2(size.width, 0.0))
544 .line_to(point2(0.0, size.height))
545 .line_to(point2(0.0, 0.0));
546 path_builder.build()
547}
548
549pub fn maybe_path_for_cursor_style(
550 render_context: &mut RenderContext,
551 cursor_style: CursorStyle,
552 cell_size: &Size,
553) -> Option<Path> {
554 match cursor_style.shape {
555 CursorShape::Block => Some(path_for_block(cell_size, render_context)),
556 CursorShape::Underline => Some(path_for_underline(cell_size, render_context, None)),
557 CursorShape::Beam => Some(path_for_beam(cell_size, render_context)),
558 CursorShape::HollowBlock => Some(path_for_hollow_block(cell_size, render_context)),
559 CursorShape::Hidden => None,
560 }
561}
562
563pub fn maybe_path_for_char(
564 render_context: &mut RenderContext,
565 c: char,
566 cell_size: &Size,
567) -> Option<Path> {
568 match c {
569 '\u{2500}' => Some(path_for_unicode_2500(cell_size, render_context)),
570 '\u{2502}' => Some(path_for_unicode_2502(cell_size, render_context)),
571 '\u{256d}' => Some(path_for_unicode_256d(cell_size, render_context)),
572 '\u{256e}' => Some(path_for_unicode_256e(cell_size, render_context)),
573 '\u{256f}' => Some(path_for_unicode_256f(cell_size, render_context)),
574 '\u{2570}' => Some(path_for_unicode_2570(cell_size, render_context)),
575 '\u{2591}' => Some(path_for_unicode_2591(cell_size, render_context)),
576 '\u{2592}' => Some(path_for_unicode_2592(cell_size, render_context)),
577 '\u{2593}' => Some(path_for_unicode_2593(cell_size, render_context)),
578 '\u{2714}' => Some(path_for_unicode_2714(cell_size, render_context)),
579 '\u{2718}' => Some(path_for_unicode_2718(cell_size, render_context)),
580 '\u{276e}' => Some(path_for_unicode_276e(cell_size, render_context)),
581 '\u{276f}' => Some(path_for_unicode_276f(cell_size, render_context)),
582 '\u{e0b0}' => Some(path_for_unicode_e0b0(cell_size, render_context)),
583 '\u{e0b1}' => Some(path_for_unicode_e0b1(cell_size, render_context)),
584 '\u{e0b2}' => Some(path_for_unicode_e0b2(cell_size, render_context)),
585 '\u{e0b3}' => Some(path_for_unicode_e0b3(cell_size, render_context)),
586 '\u{e0ba}' => Some(path_for_unicode_e0ba(cell_size, render_context)),
587 '\u{e0bc}' => Some(path_for_unicode_e0bc(cell_size, render_context)),
588 _ => None,
589 }
590}
591
592#[cfg(test)]
593mod tests {
594 use super::*;
595 use anyhow::Error;
596 use carnelian::drawing::{DisplayRotation, FontFace};
597 use carnelian::render::{ContextInner, generic};
598 use euclid::size2;
599
600 static FONT_DATA: &'static [u8] = include_bytes!(
603 "../../../../../prebuilt/third_party/fonts/robotomono/RobotoMono-Regular.ttf"
604 );
605 static FONT_FACE: std::sync::LazyLock<FontFace> =
606 std::sync::LazyLock::new(|| FontFace::new(&FONT_DATA).expect("Failed to create font"));
607
608 #[test]
609 fn check_cursor_paths() -> Result<(), Error> {
610 const SUPPORTED_CURSOR_STYLES: &[CursorStyle] = &[
611 CursorStyle { shape: CursorShape::Block, blinking: false },
612 CursorStyle { shape: CursorShape::Underline, blinking: false },
613 CursorStyle { shape: CursorShape::Beam, blinking: false },
614 CursorStyle { shape: CursorShape::HollowBlock, blinking: false },
615 ];
616 let size = size2(64, 64);
617 let forma_context = generic::Forma::new_context_without_token(size, DisplayRotation::Deg0);
618 let mut render_context = RenderContext { inner: ContextInner::Forma(forma_context) };
619 let cell_size = Size::new(8.0, 16.0);
620 for cursor_style in SUPPORTED_CURSOR_STYLES {
621 let result =
622 maybe_path_for_cursor_style(&mut render_context, *cursor_style, &cell_size);
623 assert_eq!(result.is_some(), true);
624 }
625 Ok(())
626 }
627
628 #[test]
629 fn check_strikeout_path() -> Result<(), Error> {
630 let size = size2(64, 64);
631 let forma_context = generic::Forma::new_context_without_token(size, DisplayRotation::Deg0);
632 let mut render_context = RenderContext { inner: ContextInner::Forma(forma_context) };
633 let cell_size = Size::new(8.0, 16.0);
634 let textgrid = TextGrid::new(&FONT_FACE, &cell_size);
635 let _ = path_for_strikeout(
636 &cell_size,
637 &mut render_context,
638 FONT_FACE
639 .face
640 .strikeout_metrics()
641 .map(|line_metrics| Line::new(line_metrics, &textgrid)),
642 );
643 Ok(())
644 }
645
646 #[test]
647 fn check_underline_path() -> Result<(), Error> {
648 let size = size2(64, 64);
649 let forma_context = generic::Forma::new_context_without_token(size, DisplayRotation::Deg0);
650 let mut render_context = RenderContext { inner: ContextInner::Forma(forma_context) };
651 let cell_size = Size::new(8.0, 16.0);
652 let textgrid = TextGrid::new(&FONT_FACE, &cell_size);
653 let _ = path_for_underline(
654 &cell_size,
655 &mut render_context,
656 FONT_FACE
657 .face
658 .underline_metrics()
659 .map(|line_metrics| Line::new(line_metrics, &textgrid)),
660 );
661 Ok(())
662 }
663
664 #[test]
665 fn check_unicode_paths() -> Result<(), Error> {
666 const SUPPORTED_UNICODE_CHARS: &[char] = &[
667 '\u{2500}', '\u{2502}', '\u{256d}', '\u{256e}', '\u{256f}', '\u{2570}', '\u{2591}',
668 '\u{2592}', '\u{2593}', '\u{2714}', '\u{2718}', '\u{276e}', '\u{276f}', '\u{e0b0}',
669 '\u{e0b1}', '\u{e0b2}', '\u{e0b3}', '\u{e0ba}', '\u{e0bc}',
670 ];
671 let size = size2(64, 64);
672 let forma_context = generic::Forma::new_context_without_token(size, DisplayRotation::Deg0);
673 let mut render_context = RenderContext { inner: ContextInner::Forma(forma_context) };
674 let cell_size = Size::new(8.0, 16.0);
675 for c in SUPPORTED_UNICODE_CHARS {
676 let result = maybe_path_for_char(&mut render_context, *c, &cell_size);
677 assert_eq!(result.is_some(), true);
678 }
679 Ok(())
680 }
681}