// Copyright (c) 2022 Marceline Cramer // SPDX-License-Identifier: AGPL-3.0-or-later use super::prelude::*; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum HorizontalAlignment { Left, Center, Right, } pub struct LabelText { pub font: Font, pub text: String, } pub struct Label { text: LabelText, alignment: HorizontalAlignment, scale: f32, color: Color, left: f32, right: f32, baseline: f32, dirty: bool, layout: Option, bounds: Rect, offset: Vec2, } impl Label { pub fn new( text: LabelText, alignment: HorizontalAlignment, scale: f32, color: Color, left: f32, right: f32, baseline: f32, ) -> Self { Self { text, alignment, scale, color, left, right, baseline, dirty: true, layout: None, bounds: Rect::from_xy_size(Vec2::ZERO, Vec2::ZERO), offset: Vec2::ZERO, } } pub fn new_centered(text: LabelText, scale: f32, color: Color) -> Self { Self::new( text, HorizontalAlignment::Center, scale, color, 0.0, 0.0, 0.0, ) } pub fn set_text(&mut self, text: &str) { if self.text.text != text { self.text.text = text.to_string(); self.layout = None; self.dirty = true; } } } impl Widget for Label { fn draw(&mut self, ctx: &DrawContext) { if self.dirty { let layout = TextLayout::new(&self.text.font, &self.text.text); let bounds = Rect::from(layout.get_bounds()).scale(self.scale); self.bounds = bounds; let xoff = match self.alignment { HorizontalAlignment::Left => self.left - bounds.tl.x, HorizontalAlignment::Right => self.right - bounds.br.x, HorizontalAlignment::Center => { let available = self.right - self.left; let halfway = available / 2.0 + self.left; let width = bounds.br.x - bounds.tl.x; let left = halfway - width / 2.0; left - bounds.tl.x } }; self.offset = Vec2::new(xoff, self.baseline); self.dirty = false; self.layout = Some(layout); } if let Some(layout) = self.layout.as_ref() { ctx.draw_text_layout(layout, self.offset, self.scale, self.color); } } } pub struct Icon { text: LabelText, scale: f32, color: Color, center: Vec2, dirty: bool, layout: Option, bounds: Rect, offset: Vec2, } impl Icon { pub fn new(text: LabelText, scale: f32, color: Color, center: Vec2) -> Self { Self { text, scale, color, center, dirty: true, layout: None, bounds: Rect::from_xy_size(Vec2::ZERO, Vec2::ZERO), offset: Vec2::ZERO, } } pub fn set_text(&mut self, text: &str) { if self.text.text != text { self.text.text = text.to_string(); self.layout = None; self.dirty = true; } } } impl Widget for Icon { fn draw(&mut self, ctx: &DrawContext) { if self.dirty { let layout = TextLayout::new(&self.text.font, &self.text.text); let bounds = Rect::from(layout.get_bounds()).scale(self.scale); self.bounds = bounds; self.offset = self.center - bounds.size() / 2.0; self.offset.y -= bounds.tl.y; self.dirty = false; self.layout = Some(layout); } if let Some(layout) = self.layout.as_ref() { ctx.draw_text_layout(layout, self.offset, self.scale, self.color); } } }