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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use iced::{layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget};
use std::hash::Hash;

const DEFAULT_FILL_FRACTION: f32 = 1.0;
const DEFAULT_VERTICAL_ADJUSTMENT: f32 = 0.05;

/// Wraps the existing Text widget giving it more advanced layouting
/// capabilities
/// Centers child text widget and adjust the font size depending on the height
/// of the limits Assumes single line text is being used
pub struct FillText<R>
where
    R: iced::text::Renderer,
{
    //max_font_size: u16, uncomment if there is a use case for this
    /// Portion of the height of the limits which the font size should be
    fill_fraction: f32,
    /// Adjustment factor to center the text vertically
    /// Multiplied by font size and used to move the text up if positive
    // TODO: use the produced glyph geometry directly to do this and/or add support to
    // layouting library
    vertical_adjustment: f32,
    text: iced::Text<R>,
}

impl<R> FillText<R>
where
    R: iced::text::Renderer,
{
    pub fn new(label: impl Into<String>) -> Self {
        Self {
            //max_font_size: u16::MAX,
            fill_fraction: DEFAULT_FILL_FRACTION,
            vertical_adjustment: DEFAULT_VERTICAL_ADJUSTMENT,
            text: iced::Text::new(label),
        }
    }

    #[must_use]
    pub fn fill_fraction(mut self, fraction: f32) -> Self {
        self.fill_fraction = fraction;
        self
    }

    #[must_use]
    pub fn vertical_adjustment(mut self, adjustment: f32) -> Self {
        self.vertical_adjustment = adjustment;
        self
    }

    #[must_use]
    pub fn color(mut self, color: impl Into<iced::Color>) -> Self {
        self.text = self.text.color(color);
        self
    }

    #[must_use]
    pub fn font(mut self, font: impl Into<R::Font>) -> Self {
        self.text = self.text.font(font);
        self
    }
}

impl<M, R> Widget<M, R> for FillText<R>
where
    R: iced::text::Renderer,
{
    fn width(&self) -> Length { Length::Fill }

    fn height(&self) -> Length { Length::Fill }

    fn layout(&self, renderer: &R, limits: &layout::Limits) -> layout::Node {
        let limits = limits.width(Length::Fill).height(Length::Fill);

        let size = limits.max();

        let font_size = (size.height * self.fill_fraction) as u16;

        let mut text =
            Widget::<M, _>::layout(&self.text.clone().size(font_size), renderer, &limits);

        // Size adjusted for centering
        text.align(
            iced::Align::Center,
            iced::Align::Center,
            Size::new(
                size.width,
                size.height - 2.0 * font_size as f32 * self.vertical_adjustment,
            ),
        );

        layout::Node::with_children(size, vec![text])
    }

    fn draw(
        &self,
        renderer: &mut R,
        defaults: &R::Defaults,
        layout: Layout<'_>,
        cursor_position: Point,
        viewport: &Rectangle,
    ) -> R::Output {
        // Note: this breaks if the parent widget adjusts the bounds height
        let font_size = (layout.bounds().height * self.fill_fraction) as u16;
        Widget::<M, _>::draw(
            &self.text.clone().size(font_size),
            renderer,
            defaults,
            layout.children().next().unwrap(),
            cursor_position,
            viewport,
        )
    }

    fn hash_layout(&self, state: &mut Hasher) {
        struct Marker;
        std::any::TypeId::of::<Marker>().hash(state);

        self.fill_fraction.to_bits().hash(state);
        self.vertical_adjustment.to_bits().hash(state);
        Widget::<M, R>::hash_layout(&self.text, state);
    }
}

impl<'a, M, R> From<FillText<R>> for Element<'a, M, R>
where
    R: 'a + iced::text::Renderer,
{
    fn from(fill_text: FillText<R>) -> Element<'a, M, R> { Element::new(fill_text) }
}