1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Text drawing API for `kas_wgpu`

use std::f32;
use wgpu_glyph::{GlyphCruncher, HorizontalAlign, Layout, Scale, Section, VerticalAlign};

use super::{CustomPipe, CustomWindow, DrawPipe, DrawWindow};
use kas::draw::{DrawText, DrawTextShared, Font, FontId, Pass, TextProperties};
use kas::geom::{Coord, Rect, Vec2};
use kas::Align;

impl<C: CustomPipe + 'static> DrawTextShared for DrawPipe<C> {
    fn load_font(&mut self, font: Font<'static>) -> FontId {
        let id = FontId(self.fonts.len());
        self.fonts.push(font);
        id
    }
}

impl<CW: CustomWindow + 'static> DrawText for DrawWindow<CW> {
    fn text(&mut self, pass: Pass, rect: Rect, text: &str, props: TextProperties) {
        let bounds = Coord::from(rect.size);

        // TODO: support justified alignment
        let (h_align, h_offset) = match props.align.0 {
            Align::Begin | Align::Stretch => (HorizontalAlign::Left, 0),
            Align::Centre => (HorizontalAlign::Center, bounds.0 / 2),
            Align::End => (HorizontalAlign::Right, bounds.0),
        };
        let (v_align, v_offset) = match props.align.1 {
            Align::Begin | Align::Stretch => (VerticalAlign::Top, 0),
            Align::Centre => (VerticalAlign::Center, bounds.1 / 2),
            Align::End => (VerticalAlign::Bottom, bounds.1),
        };

        let text_pos = rect.pos + Coord(h_offset, v_offset);

        let layout = match props.line_wrap {
            true => Layout::default_wrap(),
            false => Layout::default_single_line(),
        };
        let layout = layout.h_align(h_align).v_align(v_align);

        self.glyph_brush.queue(Section {
            text,
            screen_position: Vec2::from(text_pos).into(),
            bounds: Vec2::from(bounds).into(),
            scale: Scale::uniform(props.scale),
            color: props.col.into(),
            z: pass.depth(),
            layout,
            font_id: wgpu_glyph::FontId(props.font.0),
        });
    }

    #[inline]
    fn text_bound(
        &mut self,
        text: &str,
        font_id: FontId,
        font_scale: f32,
        bounds: (f32, f32),
        line_wrap: bool,
    ) -> (f32, f32) {
        let layout = match line_wrap {
            true => Layout::default_wrap(),
            false => Layout::default_single_line(),
        };

        self.glyph_brush
            .glyph_bounds(Section {
                text,
                screen_position: (0.0, 0.0),
                bounds,
                scale: Scale::uniform(font_scale),
                color: Default::default(),
                z: 0.0,
                layout,
                font_id: wgpu_glyph::FontId(font_id.0),
            })
            .map(|rect| (Vec2(rect.min.x, rect.min.y), Vec2(rect.max.x, rect.max.y)))
            .map(|(min, max)| max - min)
            .unwrap_or(Vec2::splat(0.0))
            .into()
    }
}