makepad_draw/shader/
draw_text.rs

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        //debug: true;
17        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            //rect = vec4(min_pos.x, min_pos.y, max_pos.x, max_pos.y) - draw_scroll.xyxy;
42            
43            self.tex_coord1 = mix(
44                self.font_t1.xy,
45                self.font_t2.xy,
46                normalized.xy
47            )
48            self.pos = normalized;
49            /*
50            self.tex_coord2 = mix(
51                self.font_t1.xy,
52                self.font_t1.xy + (self.font_t2.xy - self.font_t1.xy) * 0.75,
53                normalized.xy
54            )
55            
56            self.tex_coord3 = mix(
57                self.font_t1.xy,
58                self.font_t1.xy + (self.font_t2.xy - self.font_t1.xy) * 0.6,
59                normalized.xy
60            )
61            */
62            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 dx = dFdx(vec2(self.tex_coord1.x * 2048.0, 0.)).x;
79            //let dp = 1.0 / 2048.0;
80            
81            // basic hardcoded mipmapping so it stops 'swimming' in VR
82            // mipmaps are stored in red/green/blue channel
83            //let s = 1.0;
84            
85            /*if dx > 7.0 {
86                s = 0.7;
87            }
88            else if dx > 2.75 {
89                s = (
90                    sample2d_rt(self.tex, self.tex_coord3.xy + vec2(0., 0.)).z
91                        + sample2d_rt(self.tex, self.tex_coord3.xy + vec2(dp, 0.)).z
92                        + sample2d_rt(self.tex, self.tex_coord3.xy + vec2(0., dp)).z
93                        + sample2d_rt(self.tex, self.tex_coord3.xy + vec2(dp, dp)).z
94                ) * 0.25;
95            }
96            else if dx > 1.75 {
97                s = sample2d_rt(self.tex, self.tex_coord3.xy).z;
98            }
99            else if dx > 1.3 {
100                s = sample2d_rt(self.tex, self.tex_coord2.xy).y;
101            }
102            else {*/
103                let s = sample2d_rt(self.tex, self.tex_coord1.xy).x;
104            /*}*/
105            
106            s = pow(s, self.curve);
107            let col = self.get_color(); //color!(white);//get_color();
108            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() { // we only return words where whitespace turns to word
190                    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                // this causes a character-based split if the word doesnt fit at all
199                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}
223/*
224#[derive(Debug, Clone, Copy, Live, LiveHook)]
225pub enum Overflow {
226    #[live] Cut,
227    #[pick] Ellipsis,
228    #[live] None
229}*/
230
231pub 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    // these values are all generated
252    #[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        //self.draw_clip = cx.turtle().draw_clip().into();
333        //let in_many = self.many_instances.is_some();
334        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        //cx.debug.rect_r(Rect{pos:dvec2(1.0,2.0), size:dvec2(200.0,300.0)});
341        let mut walk_x = pos.x;
342        if walk_x.is_infinite() || walk_x.is_nan() {
343            return
344        }
345        //let mut char_offset = char_offset;
346        
347        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        // This relies on the UBA ("Unicode Bidirectional Algorithm")
371        // (see http://www.unicode.org/reports/tr9/#Basic_Display_Algorithm),
372        // as implemented by `unicode_bidi`, to slice the text into substrings
373        // that can be individually shaped, then assembled visually.
374        let bidi_info = unicode_bidi::BidiInfo::new(chunk, None);
375        
376        // NOTE(eddyb) the caller of `draw_inner` has already processed the text,
377        // such that `chunk` won't contain e.g. any `\n`.
378        if bidi_info.paragraphs.len() == 1 {
379            let runs_with_level_and_range = {
380                let para = &bidi_info.paragraphs[0];
381                // Split `chunk` into "runs" (that differ in their LTR/RTL "level").
382                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                // FIXME(eddyb) UBA/`unicode_bidi` only offers a LTR/RTL distinction,
388                // even if `rustybuzz` has vertical `Direction`s as well.
389                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                    // snap width/height to pixel granularity
410                    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                    // this one needs pixel snapping
414                    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                    // compute subpixel shift
418                    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                    // scale and snap it
421                    // only use a subpixel id for small fonts
422                    let subpixel_id = if self.text_style.font_size>32.0 {
423                        0
424                    }
425                    else { // subtle 64 index subpixel id
426                        ((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                        // see if we can fit it
443                        // allocate slot
444                        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                    // give the callback a chance to do things
462                    //et scaled_min_pos_x = walk_x + delta_x;
463                    //let scaled_min_pos_y = pos.y - delta_y;
464                    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_offset as f32;
473                    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        // we include the align factor and the width/height
487        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                        // ok so now what.
517                        if measured_width + adv >= eval_width { // we have to drop back to ellip_pt
518                            // if we don't have an ellip_pt, set it to 0
519                            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            //log!("Draw text without font");
607            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        //let in_many = self.many_instances.is_some();
614        // lets compute the geom
615        if text.len() == 0 {
616            return
617        }
618        //if !in_many {
619        //    self.begin_many_instances_internal(cx, fonts_atlas);
620        //}
621        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                    // otherwise we should check the ellipsis
632                    if let Some((ellip, at_x, dots)) = geom.ellip_pt {
633                        // ok so how do we draw this
634                        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 { // we might have space to h-align
645                        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                    // lets just output it and walk it
691                    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                    // lets do our y alignment
698                    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        // alright now we go and walk from start to end and collect our selection rects
758        
759        let index = start * rect_pos.stride;
760        let start_x = rect_pos.buffer[index + 0] - delta.buffer[index + 0]; // + advance.buffer[index + 0] * pos;
761        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 { // emit rect
777                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            // lets get the last one and advance
819            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        //let font_size = if let Some(font_size) = font_size{font_size}else{self.font_size};
854        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}