ui/src/elements/multi_line_label.rs

314 lines
8.4 KiB
Rust
Raw Normal View History

2023-01-16 09:53:52 +00:00
use crate::{builder::validator::multi_line_labelinfo::MultiLineLabelInfo, prelude::*};
use std::sync::{
Arc, Mutex,
2025-03-04 10:37:45 +00:00
atomic::{AtomicU32, Ordering::SeqCst},
2023-01-16 09:53:52 +00:00
};
2024-04-07 13:39:32 +00:00
use anyhow::{Context, Result};
2023-01-16 11:58:59 +00:00
use utilities::prelude::*;
2023-01-16 09:53:52 +00:00
pub struct MultiLineLabelBuilder {
text_alignment: TextAlignment,
text_ratio: f32,
text_color: Color,
text: Option<String>,
line_count: u32,
background: Option<FillTypeInfo>,
}
impl MultiLineLabelBuilder {
pub fn set_text(mut self, text: impl Into<String>) -> Self {
self.text = Some(text.into());
self
}
pub fn set_ratio(mut self, ratio: f32) -> Self {
self.text_ratio = ratio;
self
}
pub fn set_text_color(mut self, text_color: impl Into<Color>) -> Self {
self.text_color = text_color.into();
self
}
pub fn set_text_alignment(mut self, text_alignment: TextAlignment) -> Self {
self.text_alignment = text_alignment;
self
}
pub fn set_line_count(mut self, line_count: u32) -> Self {
self.line_count = line_count;
self
}
pub fn set_background(mut self, background: impl Into<FillTypeInfo>) -> Self {
self.background = Some(background.into());
self
}
2025-03-04 11:25:02 +00:00
pub fn build(self, gui_handler: &mut GuiHandler) -> Result<Arc<MultiLineLabel>> {
2025-03-04 10:37:45 +00:00
let base_grid = Grid::new(gui_handler, 1, self.line_count as usize, false)?;
2023-01-16 09:53:52 +00:00
base_grid.set_margin(0);
base_grid.set_padding(0);
if let Some(background) = self.background {
2025-03-04 10:37:45 +00:00
base_grid.set_background(gui_handler, background)?;
2023-01-16 09:53:52 +00:00
}
for i in 0..self.line_count {
let label = Label::builder()
.set_ratio(self.text_ratio)
.set_text_color(self.text_color)
.set_text_alignment(self.text_alignment)
2025-03-04 10:37:45 +00:00
.build(gui_handler)?;
2023-01-16 09:53:52 +00:00
2025-03-04 10:37:45 +00:00
base_grid.attach(gui_handler, label, 0, i as usize, 1, 1)?;
2023-01-16 09:53:52 +00:00
}
Ok(Arc::new(MultiLineLabel {
grid: base_grid,
characters_per_line: AtomicU32::new(0),
text: Mutex::new(self.text.unwrap_or_default()),
splits: Mutex::new(Vec::new()),
}))
}
}
2025-03-04 11:25:02 +00:00
pub struct MultiLineLabel {
grid: Arc<Grid>,
2023-01-16 09:53:52 +00:00
characters_per_line: AtomicU32,
text: Mutex<String>,
splits: Mutex<Vec<String>>,
}
2025-03-04 11:25:02 +00:00
impl MultiLineLabel {
2023-01-16 09:53:52 +00:00
pub fn builder() -> MultiLineLabelBuilder {
MultiLineLabelBuilder {
text_alignment: TextAlignment::Center,
text_ratio: 0.7,
text_color: Color::Black,
text: None,
line_count: 1,
background: None,
}
}
2025-03-04 11:25:02 +00:00
pub fn set_text(&self, gui_handler: &mut GuiHandler, text: impl ToString) -> Result<()> {
2023-01-16 09:53:52 +00:00
*self.text.lock().unwrap() = text.to_string();
2025-03-04 10:37:45 +00:00
self.update_text(gui_handler)
2023-01-16 09:53:52 +00:00
}
2025-03-04 11:25:02 +00:00
fn update_text(&self, gui_handler: &mut GuiHandler) -> Result<()> {
2023-01-16 09:53:52 +00:00
let text = self.text.lock().unwrap();
let splits = text.split(' ');
let character_count = self.characters_per_line.load(SeqCst) as usize;
let mut current_line = String::new();
let mut lines = Vec::new();
for split in splits {
let current_len = current_line.len();
let next_len = split.len() + 1;
if (current_len + next_len) <= character_count {
if current_line.is_empty() {
current_line = split.to_string();
} else {
current_line += &format!(" {}", split);
}
} else {
lines.push(current_line);
current_line = split.to_string();
}
}
if !current_line.is_empty() {
lines.push(current_line);
}
*self.splits.lock().unwrap() = lines.clone();
self.iter_label(|label, y| {
if y < lines.len() {
2025-03-04 10:37:45 +00:00
label.set_text(gui_handler, &lines[y])?;
2023-01-16 09:53:52 +00:00
} else {
2025-03-04 10:37:45 +00:00
label.set_text(gui_handler, "")?;
2023-01-16 09:53:52 +00:00
}
Ok(())
})
}
pub fn text(&self) -> String {
self.text.lock().unwrap().clone()
}
2025-03-04 11:25:02 +00:00
pub fn set_text_color(&self, gui_handler: &mut GuiHandler, text_color: Color) -> Result<()> {
2025-03-04 10:37:45 +00:00
self.iter_label(|label, _| label.set_text_color(gui_handler, text_color))
2023-01-16 09:53:52 +00:00
}
2025-03-04 10:37:45 +00:00
pub fn set_alignment(
&self,
2025-03-04 11:25:02 +00:00
gui_handler: &mut GuiHandler,
2025-03-04 10:37:45 +00:00
alignment: TextAlignment,
) -> Result<()> {
self.iter_label(|label, _| label.set_alignment(gui_handler, alignment))
2023-01-16 09:53:52 +00:00
}
2025-03-04 11:25:02 +00:00
pub fn set_text_ratio(&self, gui_handler: &mut GuiHandler, ratio: f32) -> Result<()> {
2025-03-04 10:37:45 +00:00
self.iter_label(|label, _| label.set_text_ratio(gui_handler, ratio))
2023-01-16 09:53:52 +00:00
}
2025-03-04 10:37:45 +00:00
pub fn set_background(
&self,
2025-03-04 11:25:02 +00:00
gui_handler: &mut GuiHandler,
2025-03-04 10:37:45 +00:00
background: impl Into<FillTypeInfo>,
) -> Result<()> {
self.grid.set_background(gui_handler, background)
2023-01-16 09:53:52 +00:00
}
pub fn try_from(
multi_line_label_info: &MultiLineLabelInfo,
2025-03-04 11:25:02 +00:00
gui_handler: &mut GuiHandler,
2023-01-16 09:53:52 +00:00
) -> Result<Arc<Self>> {
let text = multi_line_label_info.text.read().unwrap().clone();
let color = multi_line_label_info.text_color;
let mut multi_line_label_builder = MultiLineLabel::builder()
.set_text_color(color)
.set_text_alignment(multi_line_label_info.text_alignment)
2024-04-07 13:39:32 +00:00
.set_line_count(
multi_line_label_info
.line_count
.get()
.with_context(|| "line count of label")?,
);
2023-01-16 09:53:52 +00:00
if let Some(background_type) = &multi_line_label_info.background_type {
multi_line_label_builder =
multi_line_label_builder.set_background(background_type.clone());
}
if let Some(ratio) = multi_line_label_info.text_ratio {
multi_line_label_builder = multi_line_label_builder.set_ratio(ratio);
}
if !text.is_empty() {
multi_line_label_builder = multi_line_label_builder.set_text(text);
}
2025-03-04 10:37:45 +00:00
multi_line_label_builder.build(gui_handler)
2023-01-16 09:53:52 +00:00
}
2025-03-04 10:37:45 +00:00
fn iter_label<F>(&self, mut f: F) -> Result<()>
2023-01-16 09:53:52 +00:00
where
2025-03-04 10:37:45 +00:00
F: FnMut(&Label, usize) -> Result<()>,
2023-01-16 09:53:52 +00:00
{
for y in 0..self.grid.dimensions().1 {
let child = self.grid.child_at(0, y)?.unwrap();
let gui_element = child.downcast().unwrap();
let label = gui_element.label().unwrap();
f(label, y)?;
}
Ok(())
}
}
2025-03-04 11:25:02 +00:00
impl GuiElementTraits for MultiLineLabel {
fn gridable(&self) -> Option<&dyn Gridable> {
2023-01-16 09:53:52 +00:00
Some(self)
}
fn visibility(&self) -> Option<&dyn Visibility> {
Some(self)
}
2025-03-04 11:25:02 +00:00
fn downcast<'a>(&'a self) -> Option<GuiElement<'a>> {
2023-01-16 09:53:52 +00:00
Some(GuiElement::MultiLineLabel(self))
}
}
2025-03-04 11:25:02 +00:00
impl Visibility for MultiLineLabel {
2023-01-16 09:53:52 +00:00
fn visible(&self) -> bool {
self.grid.visible()
}
2025-03-04 11:25:02 +00:00
fn set_visibility(&self, gui_handler: &mut GuiHandler, visibility: bool) -> Result<()> {
2025-03-04 10:37:45 +00:00
self.grid.set_visibility(gui_handler, visibility)
2023-01-16 09:53:52 +00:00
}
}
2025-03-04 11:25:02 +00:00
impl Gridable for MultiLineLabel {
2023-01-16 09:53:52 +00:00
fn set_frame(
&self,
2025-03-04 11:25:02 +00:00
gui_handler: &mut GuiHandler,
2023-01-16 09:53:52 +00:00
x: i32,
y: i32,
w: u32,
h: u32,
vert_align: VerticalAlign,
hori_align: HorizontalAlign,
) -> Result<()> {
2025-03-04 10:37:45 +00:00
self.grid
.set_frame(gui_handler, x, y, w, h, vert_align, hori_align)?;
2023-01-16 09:53:52 +00:00
if let Some(child) = self.grid.child_at(0, 0)? {
let gui_element = child.downcast().unwrap();
let label = gui_element.label().unwrap();
let left = label.framable.left();
let right = label.framable.right();
let top = label.framable.top();
let bottom = label.framable.bottom();
let width = right - left;
let height = bottom - top;
let text_ratio = label.text_ratio();
let text_size = height as f32 * text_ratio;
let max_letter_count = (width as f32 / (text_size / 2.0)).floor();
self.characters_per_line
.store(max_letter_count as u32, SeqCst);
2025-03-04 10:37:45 +00:00
self.update_text(gui_handler)?;
2023-01-16 09:53:52 +00:00
}
Ok(())
}
2025-03-04 11:25:02 +00:00
fn selectable(&self) -> Option<&Arc<Selectable>> {
2023-01-16 09:53:52 +00:00
None
}
fn type_name(&self) -> &str {
"MultiLineLabel"
}
fn set_layer(&self, layer: i32) -> Result<()> {
self.grid.set_layer(layer)
}
2024-04-21 05:41:08 +00:00
fn position_extent(&self) -> (i32, i32, u32, u32) {
self.grid.position_extent()
}
2023-01-16 09:53:52 +00:00
}