veloren_voxygen/ui/ice/widget/
fill_text.rs

1use iced::{Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, layout};
2use std::hash::Hash;
3
4const DEFAULT_FILL_FRACTION: f32 = 1.0;
5const DEFAULT_VERTICAL_ADJUSTMENT: f32 = 0.05;
6
7/// Wraps the existing Text widget giving it more advanced layouting
8/// capabilities
9/// Centers child text widget and adjust the font size depending on the height
10/// of the limits Assumes single line text is being used
11pub struct FillText<R>
12where
13    R: iced::text::Renderer,
14{
15    //max_font_size: u16, uncomment if there is a use case for this
16    /// Portion of the height of the limits which the font size should be
17    fill_fraction: f32,
18    /// Adjustment factor to center the text vertically
19    /// Multiplied by font size and used to move the text up if positive
20    // TODO: use the produced glyph geometry directly to do this and/or add support to
21    // layouting library
22    vertical_adjustment: f32,
23    text: iced::Text<R>,
24}
25
26impl<R> FillText<R>
27where
28    R: iced::text::Renderer,
29{
30    pub fn new(label: impl Into<String>) -> Self {
31        Self {
32            //max_font_size: u16::MAX,
33            fill_fraction: DEFAULT_FILL_FRACTION,
34            vertical_adjustment: DEFAULT_VERTICAL_ADJUSTMENT,
35            text: iced::Text::new(label),
36        }
37    }
38
39    #[must_use]
40    pub fn fill_fraction(mut self, fraction: f32) -> Self {
41        self.fill_fraction = fraction;
42        self
43    }
44
45    #[must_use]
46    pub fn vertical_adjustment(mut self, adjustment: f32) -> Self {
47        self.vertical_adjustment = adjustment;
48        self
49    }
50
51    #[must_use]
52    pub fn color(mut self, color: impl Into<iced::Color>) -> Self {
53        self.text = self.text.color(color);
54        self
55    }
56
57    #[must_use]
58    pub fn font(mut self, font: impl Into<R::Font>) -> Self {
59        self.text = self.text.font(font);
60        self
61    }
62}
63
64impl<M, R> Widget<M, R> for FillText<R>
65where
66    R: iced::text::Renderer,
67{
68    fn width(&self) -> Length { Length::Fill }
69
70    fn height(&self) -> Length { Length::Fill }
71
72    fn layout(&self, renderer: &R, limits: &layout::Limits) -> layout::Node {
73        let limits = limits.width(Length::Fill).height(Length::Fill);
74
75        let size = limits.max();
76
77        let font_size = (size.height * self.fill_fraction) as u16;
78
79        let mut text =
80            Widget::<M, _>::layout(&self.text.clone().size(font_size), renderer, &limits);
81
82        // Size adjusted for centering
83        text.align(
84            iced::Align::Center,
85            iced::Align::Center,
86            Size::new(
87                size.width,
88                size.height - 2.0 * font_size as f32 * self.vertical_adjustment,
89            ),
90        );
91
92        layout::Node::with_children(size, vec![text])
93    }
94
95    fn draw(
96        &self,
97        renderer: &mut R,
98        defaults: &R::Defaults,
99        layout: Layout<'_>,
100        cursor_position: Point,
101        viewport: &Rectangle,
102    ) -> R::Output {
103        // Note: this breaks if the parent widget adjusts the bounds height
104        let font_size = (layout.bounds().height * self.fill_fraction) as u16;
105        Widget::<M, _>::draw(
106            &self.text.clone().size(font_size),
107            renderer,
108            defaults,
109            layout.children().next().unwrap(),
110            cursor_position,
111            viewport,
112        )
113    }
114
115    fn hash_layout(&self, state: &mut Hasher) {
116        struct Marker;
117        std::any::TypeId::of::<Marker>().hash(state);
118
119        self.fill_fraction.to_bits().hash(state);
120        self.vertical_adjustment.to_bits().hash(state);
121        Widget::<M, R>::hash_layout(&self.text, state);
122    }
123}
124
125impl<'a, M, R> From<FillText<R>> for Element<'a, M, R>
126where
127    R: 'a + iced::text::Renderer,
128{
129    fn from(fill_text: FillText<R>) -> Element<'a, M, R> { Element::new(fill_text) }
130}