1use {
2 crate::{
3 makepad_platform::*,
4 turtle::{Walk, Size, Align},
5 font_atlas::{CxFontsAtlasTodo, CxFont, CxFontsAtlas, Font},
6 draw_list_2d::ManyInstances,
7 geometry::GeometryQuad2D,
8 cx_2d::Cx2d
9 },
10};
11
12
13live_design!{
14
15 DrawText = {{DrawText}} {
16 color: #fff
18
19 uniform brightness: float
20 uniform curve: float
21
22 texture tex: texture2d
23
24 varying tex_coord1: vec2
25 varying tex_coord2: vec2
26 varying tex_coord3: vec2
27 varying clipped: vec2
28 varying pos: vec2
29
30 fn vertex(self) -> vec4 {
31 let min_pos = vec2(self.rect_pos.x, self.rect_pos.y)
32 let max_pos = vec2(self.rect_pos.x + self.rect_size.x, self.rect_pos.y - self.rect_size.y)
33
34 self.clipped = clamp(
35 mix(min_pos, max_pos, self.geom_pos),
36 self.draw_clip.xy,
37 self.draw_clip.zw
38 )
39
40 let normalized: vec2 = (self.clipped - min_pos) / vec2(self.rect_size.x, -self.rect_size.y)
41 self.tex_coord1 = mix(
44 self.font_t1.xy,
45 self.font_t2.xy,
46 normalized.xy
47 )
48 self.pos = normalized;
49 return self.camera_projection * (self.camera_view * (self.view_transform * vec4(
63 self.clipped.x,
64 self.clipped.y,
65 self.char_depth + self.draw_zbias,
66 1.
67 )))
68 }
69
70 fn get_color(self) -> vec4 {
71 return self.color;
72 }
73 fn blend_color(self, incol:vec4)->vec4{
74 return incol
75 }
76 fn pixel(self) -> vec4 {
77
78 let s = sample2d_rt(self.tex, self.tex_coord1.xy).x;
104 s = pow(s, self.curve);
107 let col = self.get_color(); return self.blend_color(vec4(s * col.rgb * self.brightness * col.a, s * col.a));
109 }
110 }
111}
112
113#[derive(Clone, Live, LiveHook)]
114#[live_ignore]
115pub struct TextStyle {
116 #[live()] pub font: Font,
117 #[live(9.0)] pub font_size: f64,
118 #[live(1.0)] pub brightness: f32,
119 #[live(0.6)] pub curve: f32,
120 #[live(1.4)] pub line_spacing: f64,
121 #[live(1.1)] pub top_drop: f64,
122 #[live(1.3)] pub height_factor: f64,
123}
124
125#[derive(Clone, Live, LiveHook)]
126#[live_ignore]
127pub enum TextWrap {
128 #[pick] Ellipsis,
129 Word,
130 Line
131}
132
133struct WordIterator<'a> {
134 char_iter: Option<std::str::CharIndices<'a >>,
135 eval_width: f64,
136 word_width: f64,
137 word_start: usize,
138 last_is_whitespace: bool,
139 last_char: char,
140 last_index: usize,
141 font_size_total: f64,
142}
143
144struct WordIteratorItem {
145 start: usize,
146 end: usize,
147 width: f64,
148 with_newline: bool
149}
150
151impl<'a> WordIterator<'a> {
152 fn new(char_iter: std::str::CharIndices<'a>, eval_width: f64, font_size_total: f64) -> Self {
153 Self {
154 eval_width,
155 char_iter: Some(char_iter),
156 last_is_whitespace: false,
157 word_width: 0.0,
158 word_start: 0,
159 last_char: '\0',
160 last_index: 0,
161 font_size_total
162 }
163 }
164 fn next_word(&mut self, font: &mut CxFont) -> Option<WordIteratorItem> {
165 if let Some(char_iter) = &mut self.char_iter {
166 while let Some((i, c)) = char_iter.next() {
167 self.last_index = i;
168 self.last_char = c;
169 let ret = WordIteratorItem {
170 start: self.word_start,
171 end: i,
172 width: self.word_width,
173 with_newline: false
174 };
175
176 let adv = if let Some(glyph) = font.get_glyph(c) {
177 glyph.horizontal_metrics.advance_width * self.font_size_total
178 }else {0.0};
179
180 if c == '\r' {
181 continue;
182 }
183 if c == '\n' {
184 self.last_is_whitespace = false;
185 self.word_start = i;
186 self.word_width = 0.0;
187 return Some(WordIteratorItem {with_newline: true, end: i, ..ret})
188 }
189 else if c.is_whitespace() { self.last_is_whitespace = true;
191 }
192 else if self.last_is_whitespace {
193 self.last_is_whitespace = false;
194 self.word_start = i;
195 self.word_width = adv;
196 return Some(ret);
197 }
198 if self.word_width + adv >= self.eval_width {
200 self.word_start = i;
201 self.word_width = adv;
202 return Some(ret);
203 }
204 self.word_width += adv;
205 }
206 self.char_iter = None;
207
208 let mut buffer = [0; 4];
209 let char_bytes_len = self.last_char.encode_utf8(&mut buffer).len();
210
211 return Some(WordIteratorItem {
212 start: self.word_start,
213 end: self.last_index + char_bytes_len,
214 width: self.word_width,
215 with_newline: false
216 });
217 }
218 else {
219 None
220 }
221 }
222}
223pub struct TextGeom {
232 pub eval_width: f64,
233 pub eval_height: f64,
234 pub measured_width: f64,
235 pub measured_height: f64,
236 pub ellip_pt: Option<(usize, f64, usize)>
237}
238
239#[derive(Live)]
240#[repr(C)]
241pub struct DrawText {
242 #[rust] pub many_instances: Option<ManyInstances>,
243
244 #[live] pub geometry: GeometryQuad2D,
245 #[live] pub text_style: TextStyle,
246 #[live] pub wrap: TextWrap,
247 #[live(1.0)] pub font_scale: f64,
248 #[live(1.0)] pub draw_depth: f32,
249
250 #[deref] pub draw_vars: DrawVars,
251 #[live] pub color: Vec4,
253 #[calc] pub font_t1: Vec2,
254 #[calc] pub font_t2: Vec2,
255 #[calc] pub rect_pos: Vec2,
256 #[calc] pub rect_size: Vec2,
257 #[calc] pub draw_clip: Vec4,
258 #[calc] pub char_depth: f32,
259 #[calc] pub delta: Vec2,
260 #[calc] pub font_size: f32,
261 #[calc] pub advance: f32,
262}
263
264impl LiveHook for DrawText {
265 fn before_apply(&mut self, cx: &mut Cx, apply_from: ApplyFrom, index: usize, nodes: &[LiveNode]) {
266 self.draw_vars.before_apply_init_shader(cx, apply_from, index, nodes, &self.geometry);
267 }
268 fn after_apply(&mut self, cx: &mut Cx, apply_from: ApplyFrom, index: usize, nodes: &[LiveNode]) {
269 self.draw_vars.after_apply_update_self(cx, apply_from, index, nodes, &self.geometry);
270 }
271}
272
273impl DrawText {
274
275 pub fn draw(&mut self, cx: &mut Cx2d, pos: DVec2, val: &str) {
276 self.draw_inner(cx, pos, val, &mut *cx.fonts_atlas_rc.clone().0.borrow_mut());
277 if self.many_instances.is_some() {
278 self.end_many_instances(cx)
279 }
280 }
281
282 pub fn draw_rel(&mut self, cx: &mut Cx2d, pos: DVec2, val: &str) {
283 self.draw_inner(cx, pos + cx.turtle().origin(), val, &mut *cx.fonts_atlas_rc.clone().0.borrow_mut());
284 if self.many_instances.is_some() {
285 self.end_many_instances(cx)
286 }
287 }
288
289 pub fn draw_abs(&mut self, cx: &mut Cx2d, pos: DVec2, val: &str) {
290 self.draw_inner(cx, pos, val, &mut *cx.fonts_atlas_rc.clone().0.borrow_mut());
291 if self.many_instances.is_some() {
292 self.end_many_instances(cx)
293 }
294 }
295
296 pub fn begin_many_instances(&mut self, cx: &mut Cx2d) {
297 let fonts_atlas_rc = cx.fonts_atlas_rc.clone();
298 let fonts_atlas = fonts_atlas_rc.0.borrow();
299 self.begin_many_instances_internal(cx, &*fonts_atlas);
300 }
301
302 fn begin_many_instances_internal(&mut self, cx: &mut Cx2d, fonts_atlas: &CxFontsAtlas) {
303 self.update_draw_call_vars(fonts_atlas);
304 let mi = cx.begin_many_aligned_instances(&self.draw_vars);
305 self.many_instances = mi;
306 }
307
308 pub fn end_many_instances(&mut self, cx: &mut Cx2d) {
309 if let Some(mi) = self.many_instances.take() {
310 let new_area = cx.end_many_instances(mi);
311 self.draw_vars.area = cx.update_area_refs(self.draw_vars.area, new_area);
312 }
313 }
314
315 pub fn new_draw_call(&self, cx: &mut Cx2d) {
316 cx.new_draw_call(&self.draw_vars);
317 }
318
319 pub fn update_draw_call_vars(&mut self, font_atlas: &CxFontsAtlas) {
320 self.draw_vars.texture_slots[0] = Some(font_atlas.texture_id);
321 self.draw_vars.user_uniforms[0] = self.text_style.brightness;
322 self.draw_vars.user_uniforms[1] = self.text_style.curve;
323 }
324
325 fn draw_inner(&mut self, cx: &mut Cx2d, pos: DVec2, chunk: &str, fonts_atlas: &mut CxFontsAtlas) {
326 if !self.draw_vars.can_instance()
327 || pos.x.is_nan()
328 || pos.y.is_nan()
329 || self.text_style.font.font_id.is_none() {
330 return
331 }
332 let font_id = self.text_style.font.font_id.unwrap();
335
336 if fonts_atlas.fonts[font_id].is_none() {
337 return
338 }
339
340 let mut walk_x = pos.x;
342 if walk_x.is_infinite() || walk_x.is_nan() {
343 return
344 }
345 if !self.many_instances.is_some() {
348 self.begin_many_instances_internal(cx, fonts_atlas);
349 }
350
351 let cxfont = fonts_atlas.fonts[font_id].as_mut().unwrap();
352 let dpi_factor = cx.current_dpi_factor();
353
354 let atlas_page_id = cxfont.get_atlas_page_id(dpi_factor, self.text_style.font_size);
355
356 let font = &mut cxfont.ttf_font;
357 let owned_font_face = &cxfont.owned_font_face;
358
359 let font_size_logical = self.text_style.font_size * 96.0 / (72.0 * font.units_per_em);
360 let font_size_pixels = font_size_logical * dpi_factor;
361
362 let atlas_page = &mut cxfont.atlas_pages[atlas_page_id];
363
364 let mi = if let Some(mi) = &mut self.many_instances {mi} else {return};
365 let zbias_step = 0.00001;
366 let mut char_depth = self.draw_depth;
367
368 let mut rustybuzz_buffer = rustybuzz::UnicodeBuffer::new();
369
370 let bidi_info = unicode_bidi::BidiInfo::new(chunk, None);
375
376 if bidi_info.paragraphs.len() == 1 {
379 let runs_with_level_and_range = {
380 let para = &bidi_info.paragraphs[0];
381 let (adjusted_levels, runs) = bidi_info.visual_runs(para, para.range.clone());
383 runs.into_iter().map(move | run_range | (adjusted_levels[run_range.start], run_range))
384 };
385
386 for (run_level, run_range) in runs_with_level_and_range {
387 let (glyph_ids, new_rustybuzz_buffer) = cxfont
390 .shape_cache
391 .get_or_compute_glyph_ids(
392 (
393 if run_level.is_rtl() {
394 rustybuzz::Direction::RightToLeft
395 } else {
396 rustybuzz::Direction::LeftToRight
397 },
398 &bidi_info.text[run_range]
399 ),
400 rustybuzz_buffer,
401 owned_font_face
402 );
403 rustybuzz_buffer = new_rustybuzz_buffer;
404 for &glyph_id in glyph_ids {
405 let glyph = owned_font_face.with_ref(|face| font.get_glyph_by_id(face, glyph_id).unwrap());
406
407 let advance = glyph.horizontal_metrics.advance_width * font_size_logical * self.font_scale;
408
409 let w = ((glyph.bounds.p_max.x - glyph.bounds.p_min.x) * font_size_pixels).ceil() + 1.0;
411 let h = ((glyph.bounds.p_max.y - glyph.bounds.p_min.y) * font_size_pixels).ceil() + 1.0;
412
413 let min_pos_x = walk_x + font_size_logical * glyph.bounds.p_min.x;
415 let min_pos_y = pos.y - font_size_logical * glyph.bounds.p_min.y + self.text_style.font_size * self.text_style.top_drop;
416
417 let subpixel_x_fract = min_pos_x - (min_pos_x * dpi_factor).floor() / dpi_factor;
419 let subpixel_y_fract = min_pos_y - (min_pos_y * dpi_factor).floor() / dpi_factor;
420 let subpixel_id = if self.text_style.font_size>32.0 {
423 0
424 }
425 else { ((subpixel_y_fract * dpi_factor * 7.0) as usize) << 3 |
427 (subpixel_x_fract * dpi_factor * 7.0) as usize
428 };
429
430 let subpixel_map = if let Some(tc) = atlas_page.atlas_glyphs.get_mut(&glyph_id){
431 tc
432 }
433 else{
434 atlas_page.atlas_glyphs.insert(glyph_id, [None; crate::font_atlas::ATLAS_SUBPIXEL_SLOTS]);
435 atlas_page.atlas_glyphs.get_mut(&glyph_id).unwrap()
436 };
437
438 let tc = if let Some(tc) = &subpixel_map[subpixel_id]{
439 tc
440 }
441 else {
442 fonts_atlas.alloc.todo.push(CxFontsAtlasTodo {
445 subpixel_x_fract,
446 subpixel_y_fract,
447 font_id,
448 atlas_page_id,
449 glyph_id,
450 subpixel_id
451 });
452
453 subpixel_map[subpixel_id] = Some(
454 fonts_atlas.alloc.alloc_atlas_glyph(w, h)
455 );
456 subpixel_map[subpixel_id].as_ref().unwrap()
457 };
458
459 let delta_x = font_size_logical * self.font_scale * glyph.bounds.p_min.x - subpixel_x_fract;
460 let delta_y = -font_size_logical * self.font_scale * glyph.bounds.p_min.y + self.text_style.font_size * self.font_scale * self.text_style.top_drop - subpixel_y_fract;
461 self.font_t1 = tc.t1;
465 self.font_t2 = tc.t2;
466 self.rect_pos = dvec2(walk_x + delta_x, pos.y + delta_y).into();
467 self.rect_size = dvec2(w * self.font_scale / dpi_factor, h * self.font_scale / dpi_factor).into();
468 self.char_depth = char_depth;
469 self.delta.x = delta_x as f32;
470 self.delta.y = delta_y as f32;
471 self.font_size = self.text_style.font_size as f32;
472 self.advance = advance as f32; char_depth += zbias_step;
474 mi.instances.extend_from_slice(self.draw_vars.as_slice());
475 walk_x += advance;
476 }
477 }
478 }
479
480 }
481 pub fn compute_geom(&self, cx: &Cx2d, walk: Walk, text: &str) -> Option<TextGeom> {
482 self.compute_geom_inner(cx, walk, text, &mut *cx.fonts_atlas_rc.0.borrow_mut())
483 }
484
485 fn compute_geom_inner(&self, cx: &Cx2d, walk: Walk, text: &str, fonts_atlas: &mut CxFontsAtlas) -> Option<TextGeom> {
486 let font_id = self.text_style.font.font_id.unwrap();
488
489 if fonts_atlas.fonts[font_id].is_none() {
490 return None
491 }
492
493 let font_size_logical = self.text_style.font_size * 96.0 / (72.0 * fonts_atlas.fonts[font_id].as_ref().unwrap().ttf_font.units_per_em);
494 let line_height = self.text_style.font_size * self.text_style.height_factor * self.font_scale;
495 let eval_width = cx.turtle().eval_width(walk.width, walk.margin, cx.turtle().layout().flow);
496 let eval_height = cx.turtle().eval_height(walk.height, walk.margin, cx.turtle().layout().flow);
497
498 match if walk.width.is_fit() {&TextWrap::Line}else {&self.wrap} {
499 TextWrap::Ellipsis => {
500 let ellip_width = if let Some(glyph) = fonts_atlas.fonts[font_id].as_mut().unwrap().get_glyph('.') {
501 glyph.horizontal_metrics.advance_width * font_size_logical * self.font_scale
502 }
503 else {
504 0.0
505 };
506
507 let mut measured_width = 0.0;
508 let mut ellip_pt = None;
509 for (i, c) in text.chars().enumerate() {
510
511 if measured_width + ellip_width * 3.0 < eval_width {
512 ellip_pt = Some((i, measured_width, 3));
513 }
514 if let Some(glyph) = fonts_atlas.fonts[font_id].as_mut().unwrap().get_glyph(c) {
515 let adv = glyph.horizontal_metrics.advance_width * font_size_logical * self.font_scale;
516 if measured_width + adv >= eval_width { if ellip_pt.is_none() {
520 let dots = if ellip_width * 3.0 < eval_width {3}
521 else if ellip_width * 2.0 < eval_width {2}
522 else if ellip_width < eval_width {1}
523 else {0};
524 ellip_pt = Some((0, 0.0, dots));
525 }
526 return Some(TextGeom {
527 eval_width,
528 eval_height,
529 measured_width: ellip_pt.unwrap().1 + ellip_width,
530 measured_height: line_height,
531 ellip_pt
532 })
533 }
534 measured_width += adv;
535 }
536 }
537
538 Some(TextGeom {
539 eval_width,
540 eval_height,
541 measured_width,
542 measured_height: line_height,
543 ellip_pt: None
544 })
545 }
546 TextWrap::Word => {
547 let mut max_width = 0.0;
548 let mut measured_width = 0.0;
549 let mut measured_height = line_height;
550
551 let mut iter = WordIterator::new(text.char_indices(), eval_width, font_size_logical * self.font_scale);
552 while let Some(word) = iter.next_word(fonts_atlas.fonts[font_id].as_mut().unwrap()) {
553 if measured_width + word.width >= eval_width {
554 measured_height += line_height * self.text_style.line_spacing;
555 measured_width = word.width;
556 }
557 else {
558 measured_width += word.width;
559 }
560 if measured_width > max_width {max_width = measured_width}
561 if word.with_newline {
562 measured_height += line_height * self.text_style.line_spacing;
563 measured_width = 0.0;
564 }
565 }
566
567 Some(TextGeom {
568 eval_width,
569 eval_height,
570 measured_width: max_width,
571 measured_height,
572 ellip_pt: None
573 })
574 }
575 TextWrap::Line => {
576 let mut max_width = 0.0;
577 let mut measured_width = 0.0;
578 let mut measured_height = line_height;
579
580 for c in text.chars() {
581 if c == '\n' {
582 measured_height += line_height * self.text_style.line_spacing;
583 }
584 if let Some(glyph) = fonts_atlas.fonts[font_id].as_mut().unwrap().get_glyph(c) {
585 let adv = glyph.horizontal_metrics.advance_width * font_size_logical * self.font_scale;
586 measured_width += adv;
587 }
588 if measured_width > max_width {
589 max_width = measured_width;
590 }
591 }
592 Some(TextGeom {
593 eval_width,
594 eval_height,
595 measured_width: max_width,
596 measured_height: measured_height,
597 ellip_pt: None
598 })
599 }
600 }
601 }
602
603
604 pub fn draw_walk(&mut self, cx: &mut Cx2d, walk: Walk, align: Align, text: &str) {
605 let font_id = if let Some(font_id) = self.text_style.font.font_id{font_id}else{
606 return
608 };
609 let fonts_atlas_rc = cx.fonts_atlas_rc.clone();
610 let mut fonts_atlas = fonts_atlas_rc.0.borrow_mut();
611 let fonts_atlas = &mut*fonts_atlas;
612
613 if text.len() == 0 {
616 return
617 }
618 if let Some(geom) = self.compute_geom_inner(cx, walk, text, fonts_atlas) {
622 let height = if walk.height.is_fit() {
623 geom.measured_height
624 } else {
625 geom.eval_height
626 };
627 let y_align = (height - geom.measured_height) * align.y;
628
629 match if walk.width.is_fit() {&TextWrap::Line}else {&self.wrap} {
630 TextWrap::Ellipsis => {
631 if let Some((ellip, at_x, dots)) = geom.ellip_pt {
633 let rect = cx.walk_turtle(Walk {
635 abs_pos: walk.abs_pos,
636 margin: walk.margin,
637 width: Size::Fixed(geom.eval_width),
638 height: Size::Fixed(height)
639 });
640
641 self.draw_inner(cx, rect.pos + dvec2(0.0, y_align), &text[0..ellip], fonts_atlas);
642 self.draw_inner(cx, rect.pos + dvec2(at_x, y_align), &"..."[0..dots], fonts_atlas);
643 }
644 else { let rect = cx.walk_turtle(Walk {
646 abs_pos: walk.abs_pos,
647 margin: walk.margin,
648 width: Size::Fixed(geom.eval_width),
649 height: Size::Fixed(
650 if walk.height.is_fit() {
651 geom.measured_height
652 } else {
653 geom.eval_height
654 }
655 )
656 });
657 let x_align = (geom.eval_width - geom.measured_width) * align.x;
658 self.draw_inner(cx, rect.pos + dvec2(x_align, y_align), text, fonts_atlas);
659 }
660 }
661 TextWrap::Word => {
662 let font_size_logical = self.text_style.font_size * 96.0 / (72.0 * fonts_atlas.fonts[font_id].as_ref().unwrap().ttf_font.units_per_em);
663 let line_height = self.text_style.font_size * self.text_style.height_factor * self.font_scale;
664
665 let rect = cx.walk_turtle(Walk {
666 abs_pos: walk.abs_pos,
667 margin: walk.margin,
668 width: Size::Fixed(geom.eval_width),
669 height: Size::Fixed(geom.measured_height)
670 });
671 let mut pos = dvec2(0.0, 0.0);
672
673 let mut iter = WordIterator::new(text.char_indices(), geom.eval_width, font_size_logical * self.font_scale);
674 while let Some(word) = iter.next_word(fonts_atlas.fonts[font_id].as_mut().unwrap()) {
675 if pos.x + word.width >= geom.eval_width {
676 pos.y += line_height * self.text_style.line_spacing;
677 pos.x = 0.0;
678 }
679 self.draw_inner(cx, rect.pos + pos, &text[word.start..word.end], fonts_atlas);
680 pos.x += word.width;
681
682 if word.with_newline {
683 pos.y += line_height * self.text_style.line_spacing;
684 pos.x = 0.0;
685 }
686 }
687 }
688 TextWrap::Line => {
689 let line_height = self.text_style.font_size * self.text_style.height_factor * self.font_scale;
690 let rect = cx.walk_turtle(Walk {
692 abs_pos: walk.abs_pos,
693 margin: walk.margin,
694 width: Size::Fixed(geom.measured_width),
695 height: Size::Fixed(height)
696 });
697 let mut ypos = 0.0;
699 for line in text.split('\n') {
700 self.draw_inner(cx, rect.pos + dvec2(0.0, y_align + ypos), line, fonts_atlas);
701 ypos += line_height * self.text_style.line_spacing;
702 }
703
704 }
705 }
706 }
707 if self.many_instances.is_some() {
708 self.end_many_instances(cx)
709 }
710 }
711
712 pub fn closest_offset(&self, cx: &Cx, pos: DVec2) -> Option<usize> {
713 let area = &self.draw_vars.area;
714
715 if !area.is_valid(cx) {
716 return None
717 }
718
719 let line_spacing = self.get_line_spacing();
720 let rect_pos = area.get_read_ref(cx, live_id!(rect_pos), ShaderTy::Vec2).unwrap();
721 let delta = area.get_read_ref(cx, live_id!(delta), ShaderTy::Vec2).unwrap();
722 let advance = area.get_read_ref(cx, live_id!(advance), ShaderTy::Float).unwrap();
723
724 let mut last_y = None;
725 for i in 0..rect_pos.repeat {
726 let index = rect_pos.stride * i;
727 let x = rect_pos.buffer[index + 0] as f64 - delta.buffer[index + 0] as f64;
728 let y = rect_pos.buffer[index + 1] - delta.buffer[index + 1];
729 if last_y.is_none() {last_y = Some(y)}
730 let advance = advance.buffer[index + 0] as f64;
731 if i > 0 && y > last_y.unwrap() && pos.y < last_y.unwrap() as f64 + line_spacing as f64 {
732 return Some(i - 1)
733 }
734 if pos.x < x + advance * 0.5 && pos.y < y as f64 + line_spacing as f64 {
735 return Some(i)
736 }
737 last_y = Some(y)
738 }
739 return Some(rect_pos.repeat);
740
741 }
742
743 pub fn get_selection_rects(&self, cx: &Cx, start: usize, end: usize, shift: DVec2, pad: DVec2) -> Vec<Rect> {
744 let area = &self.draw_vars.area;
745
746 if !area.is_valid(cx) {
747 return Vec::new();
748 }
749
750 let rect_pos = area.get_read_ref(cx, live_id!(rect_pos), ShaderTy::Vec2).unwrap();
751 let delta = area.get_read_ref(cx, live_id!(delta), ShaderTy::Vec2).unwrap();
752 let advance = area.get_read_ref(cx, live_id!(advance), ShaderTy::Float).unwrap();
753
754 if rect_pos.repeat == 0 || start >= rect_pos.repeat{
755 return Vec::new();
756 }
757 let index = start * rect_pos.stride;
760 let start_x = rect_pos.buffer[index + 0] - delta.buffer[index + 0]; let start_y = rect_pos.buffer[index + 1] - delta.buffer[index + 1];
762 let line_spacing = self.get_line_spacing();
763 let mut last_y = start_y;
764 let mut min_x = start_x;
765 let mut last_x = start_x;
766 let mut last_advance = advance.buffer[index + 0];
767 let mut out = Vec::new();
768 for index in start..end {
769 if index >= rect_pos.repeat{
770 break;
771 }
772 let index = index * rect_pos.stride;
773 let end_x = rect_pos.buffer[index + 0] - delta.buffer[index + 0];
774 let end_y = rect_pos.buffer[index + 1] - delta.buffer[index + 1];
775 last_advance = advance.buffer[index + 0];
776 if end_y > last_y { out.push(Rect {
778 pos: dvec2(min_x as f64, last_y as f64) + shift,
779 size: dvec2((last_x - min_x + last_advance) as f64, line_spacing) + pad
780 });
781 min_x = end_x;
782 last_y = end_y;
783 }
784 last_x = end_x;
785 }
786 out.push(Rect {
787 pos: dvec2(min_x as f64, last_y as f64) + shift,
788 size: dvec2((last_x - min_x + last_advance) as f64, line_spacing) + pad
789 });
790 out
791 }
792
793
794 pub fn get_char_count(&self, cx: &Cx) -> usize {
795 let area = &self.draw_vars.area;
796 if !area.is_valid(cx) {
797 return 0
798 }
799 let rect_pos = area.get_read_ref(cx, live_id!(rect_pos), ShaderTy::Vec2).unwrap();
800 rect_pos.repeat
801 }
802
803 pub fn get_cursor_pos(&self, cx: &Cx, pos: f32, index: usize) -> Option<DVec2> {
804 let area = &self.draw_vars.area;
805
806 if !area.is_valid(cx) {
807 return None
808 }
809
810 let rect_pos = area.get_read_ref(cx, live_id!(rect_pos), ShaderTy::Vec2).unwrap();
811 let delta = area.get_read_ref(cx, live_id!(delta), ShaderTy::Vec2).unwrap();
812 let advance = area.get_read_ref(cx, live_id!(advance), ShaderTy::Float).unwrap();
813
814 if rect_pos.repeat == 0 {
815 return None
816 }
817 if index >= rect_pos.repeat {
818 let index = (rect_pos.repeat - 1) * rect_pos.stride;
820 let x = rect_pos.buffer[index + 0] - delta.buffer[index + 0] + advance.buffer[index + 0];
821 let y = rect_pos.buffer[index + 1] - delta.buffer[index + 1];
822 Some(dvec2(x as f64, y as f64))
823 }
824 else {
825 let index = index * rect_pos.stride;
826 let x = rect_pos.buffer[index + 0] - delta.buffer[index + 0] + advance.buffer[index + 0] * pos;
827 let y = rect_pos.buffer[index + 1] - delta.buffer[index + 1];
828 Some(dvec2(x as f64, y as f64))
829 }
830 }
831
832 pub fn get_line_spacing(&self) -> f64 {
833 self.text_style.font_size * self.text_style.height_factor * self.font_scale * self.text_style.line_spacing
834 }
835
836 pub fn get_font_size(&self) -> f64 {
837 self.text_style.font_size * self.font_scale
838 }
839
840 pub fn get_monospace_base(&self, cx: &Cx2d) -> DVec2 {
841 let mut fonts_atlas = cx.fonts_atlas_rc.0.borrow_mut();
842 if self.text_style.font.font_id.is_none() {
843 return DVec2::default();
844 }
845 let font_id = self.text_style.font.font_id.unwrap();
846 if fonts_atlas.fonts[font_id].is_none() {
847 return DVec2::default();
848 }
849 let font = fonts_atlas.fonts[font_id].as_mut().unwrap();
850 let slot = font.owned_font_face.with_ref( | face | face.glyph_index('!').map_or(0, | id | id.0 as usize));
851 let glyph = font.get_glyph_by_id(slot).unwrap();
852
853 DVec2 {
855 x: glyph.horizontal_metrics.advance_width * (96.0 / (72.0 * font.ttf_font.units_per_em)),
856 y: self.text_style.line_spacing
857 }
858 }
859}