1use {
2 crate::{
3 cx_2d::Cx2d,
4 cx_draw::CxDraw,
5 geometry::GeometryQuad2D,
6 makepad_platform::*,
7 text::{
8 color::Color,
9 font::FontId,
10 font_family::FontFamilyId,
11 fonts::Fonts,
12 geom::{Point, Rect, Size, Transform},
13 layouter::{
14 BorrowedLayoutParams, LaidoutGlyph, LaidoutRow, LaidoutText, LayoutOptions, Span,
15 Style,
16 },
17 loader::{FontDefinition, FontFamilyDefinition},
18 rasterizer::{AtlasKind, RasterizedGlyph},
19 selection::{Cursor, Selection},
20 },
21 turtle::*,
22 turtle::{Align, Walk},
23 },
24 std::{cell::RefCell, rc::Rc},
25};
26
27live_design! {
28 use link::shaders::*;
29
30 pub DrawText = {{DrawText}} {
31 color: #ffff,
32
33 uniform radius: float;
34 uniform cutoff: float;
35 uniform grayscale_atlas_size: vec2;
36 uniform color_atlas_size: vec2;
37
38 texture grayscale_texture: texture2d
39 texture color_texture: texture2d
40
41 varying pos: vec2
42 varying t: vec2
43 varying world: vec4
44
45 fn vertex(self) -> vec4 {
46 let p = mix(self.rect_pos, self.rect_pos + self.rect_size, self.geom_pos);
47 let p_clipped = clamp(p, self.draw_clip.xy, self.draw_clip.zw);
48 let p_normalized: vec2 = (p_clipped - self.rect_pos) / self.rect_size;
49
50 self.pos = p_normalized;
51 self.t = mix(self.t_min, self.t_max, p_normalized.xy);
52 self.world = self.view_transform * vec4(
53 p_clipped.x,
54 p_clipped.y,
55 self.glyph_depth + self.draw_zbias,
56 1.
57 );
58 return self.camera_projection * (self.camera_view * (self.world));
59 }
60
61 fn sdf(self, scale: float, p: vec2) -> float {
62 let s = sample2d(self.grayscale_texture, p).x;
63 s = clamp(((s - (1.0 - self.cutoff)) * self.radius / scale + 0.5)*1.1, 0.0, 1.0);
65 return s;
66 }
67
68 fn get_color(self) -> vec4 {
69 return self.color
70 }
71
72 fn fragment(self) -> vec4 {
73 return depth_clip(self.world, self.pixel(), self.depth_clip);
74 }
75
76 fn pixel(self) -> vec4 {
77 let dxt = length(dFdx(self.t));
78 let dyt = length(dFdy(self.t));
79 let color = #0000
80 if self.texture_index == 0 {
81 let scale = (dxt + dyt) * self.grayscale_atlas_size.x * 0.5;
83 let s = self.sdf(scale, self.t.xy);
84 let c = self.get_color();
85 return s * vec4(c.rgb * c.a, c.a);
86 } else {
87 let c = sample2d(self.color_texture, self.t);
88 return vec4(c.rgb * c.a, c.a);
89 }
90 }
91 }
92}
93
94#[derive(Live, LiveRegister)]
95#[repr(C)]
96pub struct DrawText {
97 #[live]
98 pub geometry: GeometryQuad2D,
99 #[live]
100 pub text_style: TextStyle,
101 #[live(1.0)]
102 pub font_scale: f32,
103 #[live(1.0)]
104 pub draw_depth: f32,
105 #[live]
106 pub debug: bool,
107
108 #[live]
109 pub temp_y_shift: f32,
110
111 #[deref]
112 pub draw_vars: DrawVars,
113 #[calc]
114 pub rect_pos: Vec2,
115 #[calc]
116 pub rect_size: Vec2,
117 #[calc]
118 pub draw_clip: Vec4,
119 #[live(1.0)]
120 pub depth_clip: f32,
121 #[calc]
122 pub glyph_depth: f32,
123 #[live]
124 pub color: Vec4,
125 #[calc]
126 pub texture_index: f32,
127 #[calc]
128 pub t_min: Vec2,
129 #[calc]
130 pub t_max: Vec2,
131}
132
133impl LiveHook for DrawText {
134 fn before_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) {
135 self.draw_vars
136 .before_apply_init_shader(cx, apply, index, nodes, &self.geometry);
137 }
138
139 fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) {
140 self.draw_vars
141 .after_apply_update_self(cx, apply, index, nodes, &self.geometry);
142 }
143}
144
145impl DrawText {
146 pub fn draw_abs(&mut self, cx: &mut Cx2d, pos: DVec2, text: &str) {
147 let text = self.layout(cx, 0.0, 0.0, None, false, Align::default(), text);
148 self.draw_text(cx, Point::new(pos.x as f32, pos.y as f32), &text);
149 }
150
151 pub fn draw_walk(
152 &mut self,
153 cx: &mut Cx2d,
154 walk: Walk,
155 align: Align,
156 text: &str,
157 ) -> makepad_platform::Rect {
158 let turtle_rect = cx.turtle().padded_rect();
159 let max_width_in_lpxs = if !turtle_rect.size.x.is_nan() {
160 Some(turtle_rect.size.x as f32)
161 } else {
162 None
163 };
164 let wrap = cx.turtle().layout().flow == Flow::RightWrap;
165
166 let text = self.layout(cx, 0.0, 0.0, max_width_in_lpxs, wrap, align, text);
167 self.draw_walk_laidout(cx, walk, &text)
168 }
169
170 pub fn draw_walk_laidout(
171 &mut self,
172 cx: &mut Cx2d,
173 walk: Walk,
174 laidout_text: &LaidoutText,
175 ) -> makepad_platform::Rect {
176 use crate::text::geom::{Point, Size};
177 use crate::turtle;
178
179 let size_in_lpxs = laidout_text.size_in_lpxs * self.font_scale;
180 let max_size_in_lpxs = Size::new(
181 cx.turtle()
182 .max_width(walk)
183 .map_or(size_in_lpxs.width, |max_width| max_width as f32),
184 cx.turtle()
185 .max_height(walk)
186 .map_or(size_in_lpxs.height, |max_height| max_height as f32),
187 );
188 let turtle_rect = cx.walk_turtle(Walk {
189 abs_pos: walk.abs_pos,
190 margin: walk.margin,
191 width: turtle::Size::Fixed(max_size_in_lpxs.width as f64),
192 height: turtle::Size::Fixed(max_size_in_lpxs.height as f64),
193 });
194
195 if self.debug {
196 let mut area = Area::Empty;
197 cx.add_aligned_rect_area(&mut area, turtle_rect);
198 cx.cx.debug.area(area, vec4(1.0, 1.0, 1.0, 1.0));
199 }
200
201 let origin_in_lpxs = Point::new(
202 turtle_rect.pos.x as f32,
203 turtle_rect.pos.y as f32,
204 );
205 self.draw_text(cx, origin_in_lpxs, &laidout_text);
206
207 rect(
208 origin_in_lpxs.x as f64,
209 origin_in_lpxs.y as f64,
210 size_in_lpxs.width as f64,
211 size_in_lpxs.height as f64,
212 )
213 }
214
215 pub fn draw_walk_resumable_with(
216 &mut self,
217 cx: &mut Cx2d,
218 text_str: &str,
219 mut f: impl FnMut(&mut Cx2d, makepad_platform::Rect),
220 ) {
221 let turtle_pos = cx.turtle().pos();
222 let turtle_rect = cx.turtle().padded_rect();
223 let origin_in_lpxs = Point::new(turtle_rect.pos.x as f32, turtle_pos.y as f32);
224 let first_row_indent_in_lpxs = turtle_pos.x as f32 - origin_in_lpxs.x;
225 let row_height = cx.turtle().row_height();
226
227 let max_width_in_lpxs = if !turtle_rect.size.x.is_nan() {
246 Some(turtle_rect.size.x as f32)
247 } else {
248 None
249 };
250 let wrap = cx.turtle().layout().flow == Flow::RightWrap;
251
252 let text = self.layout(
253 cx,
254 first_row_indent_in_lpxs,
255 row_height as f32,
256 max_width_in_lpxs,
257 wrap,
258 Align::default(),
259 text_str,
260 );
261 self.draw_text(cx, origin_in_lpxs, &text);
262
263 let last_row = text.rows.last().unwrap();
264 let new_turtle_pos = origin_in_lpxs
265 + Size::new(
266 last_row.width_in_lpxs,
267 last_row.origin_in_lpxs.y - last_row.ascender_in_lpxs,
268 ) * self.font_scale;
269 let used_size_in_lpxs = text.size_in_lpxs * self.font_scale;
270 let new_turtle_pos = dvec2(new_turtle_pos.x as f64, new_turtle_pos.y as f64);
271 let turtle = cx.turtle_mut();
272
273 turtle.set_pos(new_turtle_pos);
274 turtle.update_width_max(origin_in_lpxs.x as f64, used_size_in_lpxs.width as f64);
275 turtle.update_height_max(origin_in_lpxs.y as f64, used_size_in_lpxs.height as f64);
276
277 turtle.set_wrap_spacing((
278 last_row.line_spacing_above_in_lpxs - last_row.ascender_in_lpxs
279 )as f64);
280
281 cx.emit_turtle_walk(makepad_platform::Rect {
282 pos: new_turtle_pos,
283 size: dvec2(
284 used_size_in_lpxs.width as f64,
285 used_size_in_lpxs.height as f64,
286 ),
287 });
288
289 let shift = if let Some(row) = text.rows.get(0){
290 if let Some(glyph) = row.glyphs.get(0){
291 glyph.font_size_in_lpxs * self.temp_y_shift
292 }
293 else{0.0}
294 }
295 else{0.0};
296
297 for rect_in_lpxs in text.selection_rects_in_lpxs(Selection {
298 anchor: Cursor {
299 index: 0,
300 prefer_next_row: false,
301 },
302 cursor: Cursor {
303 index: text.text.len(),
304 prefer_next_row: false,
305 },
306 }) {
307 let rect_in_lpxs = Rect::new(
308 origin_in_lpxs + Size::from(rect_in_lpxs.origin) * self.font_scale,
309 rect_in_lpxs.size * self.font_scale,
310 );
311 f(
312 cx,
313 makepad_platform::rect(
314 rect_in_lpxs.origin.x as f64,
315 rect_in_lpxs.origin.y as f64 + shift as f64,
316 rect_in_lpxs.size.width as f64,
317 rect_in_lpxs.size.height as f64,
318 ),
319 )
320 }
321 }
322
323 pub fn layout(
324 &self,
325 cx: &mut Cx,
326 first_row_indent_in_lpxs: f32,
327 first_row_min_line_spacing_below_in_lpxs: f32,
328 max_width_in_lpxs: Option<f32>,
329 wrap: bool,
330 align: Align,
331 text: &str,
332 ) -> Rc<LaidoutText> {
333 CxDraw::lazy_construct_fonts(cx);
334 let fonts = cx.get_global::<Rc<RefCell<Fonts>>>().clone();
335 let mut fonts = fonts.borrow_mut();
336
337 let text_len = text.len();
338 fonts.get_or_layout(BorrowedLayoutParams {
339 text,
340 spans: &[Span {
341 style: Style {
342 font_family_id: self.text_style.font_family.to_font_family_id(),
343 font_size_in_pts: self.text_style.font_size,
344 color: None,
345 },
346 len: text_len,
347 }],
348 options: LayoutOptions {
349 first_row_indent_in_lpxs,
350 first_row_min_line_spacing_below_in_lpxs,
351 max_width_in_lpxs,
352 wrap,
353 align: align.x as f32,
354 line_spacing_scale: self.text_style.line_spacing as f32,
355 },
356 })
357 }
358
359 fn draw_text(&mut self, cx: &mut Cx2d, origin_in_lpxs: Point<f32>, text: &LaidoutText) {
360 self.update_draw_vars(cx);
361 let Some(mut instances) = cx.begin_many_aligned_instances(&self.draw_vars) else {
362 return;
363 };
364 self.glyph_depth = self.draw_depth;
365 for row in &text.rows {
366 self.draw_row(
367 cx,
368 origin_in_lpxs + Size::from(row.origin_in_lpxs) * self.font_scale,
369 row,
370 &mut instances.instances,
371 );
372 }
373 let area = cx.end_many_instances(instances);
374 self.draw_vars.area = cx.update_area_refs(self.draw_vars.area, area);
375 }
376
377 fn update_draw_vars(&mut self, cx: &mut Cx2d) {
378 let fonts = cx.fonts.borrow();
379 let rasterizer = fonts.rasterizer().borrow();
380 let sdfer_settings = rasterizer.sdfer().settings();
381 self.draw_vars.user_uniforms[0] = sdfer_settings.radius;
382 self.draw_vars.user_uniforms[1] = sdfer_settings.cutoff;
383 let grayscale_atlas_size = rasterizer.grayscale_atlas().size();
384 self.draw_vars.user_uniforms[2] = grayscale_atlas_size.width as f32;
385 self.draw_vars.user_uniforms[3] = grayscale_atlas_size.height as f32;
386 let color_atlas_size = rasterizer.color_atlas().size();
387 self.draw_vars.user_uniforms[4] = color_atlas_size.width as f32;
388 self.draw_vars.user_uniforms[5] = color_atlas_size.height as f32;
389 self.draw_vars.texture_slots[0] = Some(fonts.grayscale_texture().clone());
390 self.draw_vars.texture_slots[1] = Some(fonts.color_texture().clone());
391 }
392
393 fn draw_row(
394 &mut self,
395 cx: &mut Cx2d,
396 origin_in_lpxs: Point<f32>,
397 row: &LaidoutRow,
398 out_instances: &mut Vec<f32>,
399 ) {
400 for glyph in &row.glyphs {
401 self.draw_glyph(
402 cx,
403 origin_in_lpxs + Size::from(glyph.origin_in_lpxs) * self.font_scale,
404 glyph,
405 out_instances,
406 );
407 }
408
409 let width_in_lpxs = row.width_in_lpxs * self.font_scale;
410 if self.debug {
411 let mut area = Area::Empty;
412 cx.add_aligned_rect_area(
413 &mut area,
414 makepad_platform::rect(
415 origin_in_lpxs.x as f64,
416 (origin_in_lpxs.y - row.ascender_in_lpxs * self.font_scale) as f64,
417 width_in_lpxs as f64,
418 1.0,
419 ),
420 );
421 cx.cx
422 .debug
423 .area(area, makepad_platform::vec4(1.0, 0.0, 0.0, 1.0));
424 let mut area = Area::Empty;
425 cx.add_aligned_rect_area(
426 &mut area,
427 makepad_platform::rect(
428 origin_in_lpxs.x as f64,
429 origin_in_lpxs.y as f64,
430 width_in_lpxs as f64,
431 1.0,
432 ),
433 );
434 cx.cx
435 .debug
436 .area(area, makepad_platform::vec4(0.0, 1.0, 0.0, 1.0));
437 let mut area = Area::Empty;
438 cx.add_aligned_rect_area(
439 &mut area,
440 makepad_platform::rect(
441 origin_in_lpxs.x as f64,
442 (origin_in_lpxs.y - row.descender_in_lpxs * self.font_scale) as f64,
443 width_in_lpxs as f64,
444 1.0,
445 ),
446 );
447 cx.cx
448 .debug
449 .area(area, makepad_platform::vec4(0.0, 0.0, 1.0, 1.0));
450 }
451 }
452
453 fn draw_glyph(
454 &mut self,
455 cx: &mut Cx2d,
456 origin_in_lpxs: Point<f32>,
457 glyph: &LaidoutGlyph,
458 output: &mut Vec<f32>,
459 ) {
460 use crate::text::geom::Point;
461 let font_size_in_dpxs = glyph.font_size_in_lpxs * cx.current_dpi_factor() as f32;
462 if let Some(rasterized_glyph) = glyph.rasterize(font_size_in_dpxs) {
463 self.draw_rasterized_glyph(
464 Point::new(
465 origin_in_lpxs.x + glyph.offset_in_lpxs() * self.font_scale,
466 origin_in_lpxs.y,
467 ),
468 glyph.font_size_in_lpxs,
469 glyph.color,
470 rasterized_glyph,
471 output,
472 );
473 }
474 }
475
476 fn draw_rasterized_glyph(
477 &mut self,
478 origin_in_lpxs: Point<f32>,
479 font_size_in_lpxs: f32,
480 color: Option<Color>,
481 glyph: RasterizedGlyph,
482 output: &mut Vec<f32>,
483 ) {
484 fn tex_coord(point: Point<usize>, size: Size<usize>) -> Point<f32> {
485 Point::new(
486 point.x as f32 / size.width as f32,
487 point.y as f32 / size.height as f32,
488 )
489 }
490
491 let texture_index = match glyph.atlas_kind {
492 AtlasKind::Grayscale => 0.0,
493 AtlasKind::Color => 1.0,
494 };
495
496 let atlas_image_bounds = glyph.atlas_image_bounds;
497 let atlas_size = glyph.atlas_size;
498 let t_min = tex_coord(glyph.atlas_image_bounds.min(), atlas_size);
499 let t_max = tex_coord(glyph.atlas_image_bounds.max(), atlas_size);
500
501 let atlas_image_padding = glyph.atlas_image_padding;
502 let atlas_image_size = atlas_image_bounds.size;
503 let origin_in_dpxs = glyph.origin_in_dpxs;
504 let bounds_in_dpxs = Rect::new(
505 Point::new(
506 origin_in_dpxs.x - atlas_image_padding as f32,
507 -origin_in_dpxs.y - atlas_image_size.height as f32 + (atlas_image_padding as f32),
508 ),
509 Size::new(atlas_image_size.width as f32, atlas_image_size.height as f32),
510 );
511 let bounds_in_lpxs = bounds_in_dpxs.apply_transform(
512 Transform::from_scale_uniform(font_size_in_lpxs / glyph.dpxs_per_em * self.font_scale)
513 .translate(origin_in_lpxs.x, origin_in_lpxs.y),
514 );
515
516 self.rect_pos = vec2(bounds_in_lpxs.origin.x, bounds_in_lpxs.origin.y) + vec2(0.0,self.temp_y_shift* font_size_in_lpxs);
517 self.rect_size = vec2(bounds_in_lpxs.size.width, bounds_in_lpxs.size.height);
518 if let Some(color) = color {
519 self.color = vec4(
520 color.r as f32,
521 color.g as f32,
522 color.b as f32,
523 color.a as f32,
524 ) / 255.0;
525 }
526 self.texture_index = texture_index;
527 self.t_min = vec2(t_min.x, t_min.y);
528 self.t_max = vec2(t_max.x, t_max.y);
529
530 output.extend_from_slice(self.draw_vars.as_slice());
531 self.glyph_depth += 0.000001;
532 }
533}
534
535#[derive(Debug, Clone, Live, LiveHook, LiveRegister)]
536#[live_ignore]
537pub struct TextStyle {
538 #[live]
539 pub font_family: FontFamily,
540 #[live(10.0)]
541 pub font_size: f32,
542 #[live(1.0)]
543 pub line_spacing: f32,
544}
545
546#[derive(Debug, Clone, Live, LiveRegister, PartialEq)]
547pub struct FontFamily {
548 #[rust]
549 id: LiveId,
550}
551
552impl FontFamily {
553 fn to_font_family_id(&self) -> FontFamilyId {
554 (self.id.0).into()
555 }
556}
557
558impl LiveHook for FontFamily {
559 fn skip_apply(
560 &mut self,
561 cx: &mut Cx,
562 _apply: &mut Apply,
563 index: usize,
564 nodes: &[LiveNode],
565 ) -> Option<usize> {
566 CxDraw::lazy_construct_fonts(cx);
567 let fonts = cx.get_global::<Rc<RefCell<Fonts>>>().clone();
568 let mut fonts = fonts.borrow_mut();
569
570 let mut id = LiveId::seeded();
571 let mut next_child_index = Some(index + 1);
572 while let Some(child_index) = next_child_index {
573 if let LiveValue::Font(font) = &nodes[child_index].value {
574 id = id.id_append(font.to_live_id());
575 }
576 next_child_index = nodes.next_child(child_index);
577 }
578 self.id = id;
579
580 let font_family_id = self.to_font_family_id();
581 if !fonts.is_font_family_known(font_family_id) {
582 let mut font_ids = Vec::new();
583 let mut next_child_index = Some(index + 1);
584 while let Some(child_index) = next_child_index {
585 if let LiveValue::Font(font) = &nodes[child_index].value {
586 let font_id: FontId = (font.to_live_id().0).into();
587 if !fonts.is_font_known(font_id) {
588 let data = if font.paths.len()>1{
590 let mut data = Vec::new();
592 for path in &*font.paths{
593 let dep = cx.get_dependency(path).unwrap();
594 data.extend(&*dep);
595 }
596 Rc::new(data)
597 }
598 else{
599 cx.get_dependency(font.paths[0].as_str()).unwrap().into()
600 };
601 fonts.define_font(
602 font_id,
603 FontDefinition {
604 data,
605 index: 0,
606 ascender_fudge_in_ems: font.ascender_fudge,
607 descender_fudge_in_ems: font.descender_fudge,
608 },
609 );
610 }
611 font_ids.push(font_id);
612 }
613 next_child_index = nodes.next_child(child_index);
614 }
615 fonts.define_font_family(font_family_id, FontFamilyDefinition { font_ids });
616 }
617
618 Some(nodes.skip_node(index))
619 }
620}