canary-rs/scripts/sao-ui/src/widgets/text.rs

158 lines
3.9 KiB
Rust

// 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<TextLayout>,
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<TextLayout>,
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);
}
}
}