flux-tui 0.5.0

Fast and lightweight Terminal UI drawing library
Documentation
use std::num::NonZero;
use unicode_width::UnicodeWidthStr;

use crate::canvas::*;

use super::*;

/// Represents a single text label
///
/// Design (optional Border):
/// ```text
/// ┌─────┐
/// │Label│
/// └─────┘
/// ```
pub struct Label {
	has_focus: bool,
	text: String,
	text_alignment: HorizontalAlignment,
	base: WidgetBase,
	focus_color: Option<Color>,
}

impl Default for Label {
	fn default() -> Self {
		let mut s = Self {
			has_focus: false,
			text: String::default(),
			text_alignment: HorizontalAlignment::Center,
			base: WidgetBase::default(),
			focus_color: None,
		};
		s.base.constraints.y.min = NonZero::<TSize>::MIN;
		s.base.constraints.y.max = Size::Fixed(NonZero::<TSize>::MIN);
		s
	}
}

impl Label {
	/// Sets the alignment of the inner text
	pub fn set_text_alignment(&mut self, alignment: HorizontalAlignment) {
		self.text_alignment = alignment;
	}

	/// Replaces the inner text
	pub fn set_text(&mut self, text: &str) {
		self.text.clear();
		self.text.push_str(text);
		let old = self.base.constraints.x.max;
		self.base.constraints.x.max =
			Size::Fixed(NonZero::new(self.text.width() as TSize).unwrap_or(NonZero::<TSize>::MIN));
		if old != self.base.constraints.x.max {
			self.provoke_changed_property(WindowProperty::Size);
		}
	}

	/// Gets the inner text
	pub fn get_text(&self) -> &str {
		&self.text
	}

	pub fn set_focus_color(&mut self, color: Option<Color>) {
		self.focus_color = color;
	}
}

impl Window for Label {
	fn render(&self, canvas: &mut Canvas) {
		let fg = match self.base.enabled {
			true => match self.has_focus {
				true => self.focus_color,
				false => None,
			},
			false => self.base.colors.disabled,
		}
		.or(self.base.colors.base_fg);

		let mut row = canvas.get_row_variable_width(TSize::MIN).unwrap();
		let text_w = self.text.width() as TSize;
		match self.text_alignment {
			HorizontalAlignment::Left => {}
			HorizontalAlignment::Center => {
				row.skip(row.width().checked_sub(text_w).unwrap_or_default() / 2);
			}
			HorizontalAlignment::Right => {
				row.skip(row.width().checked_sub(text_w).unwrap_or_default());
			}
		}

		row.add_string(
			&self.text,
			fg,
			self.base.colors.base_bg,
			Style::default(),
			Some(VariableWidthGlyphRow::DOT3_REPLACEMENT),
		)
		.unwrap()
	}

	fn handle_event(&mut self, event: &mut WindowEvent) {
		match event.raw() {
			Event::FocusGained => self.has_focus = true,
			Event::FocusLost => self.has_focus = false,
			_ => {}
		}
	}

	fn is_enabled(&self) -> bool {
		self.base.enabled
	}
}

impl WindowLayout for Label {
	fn desired_size(&self, available_size: TPoint) -> TPoint {
		self.base.desired_size(available_size)
	}

	fn alignment(&self) -> (super::HorizontalAlignment, super::VerticalAlignment) {
		self.base.alignment
	}

	fn margin(&self) -> Thickness {
		self.base.margin
	}

	fn border(&self) -> BorderStyle {
		self.base.border
	}

	fn is_visible(&self) -> bool {
		self.base.visibility
	}
}

impl HasWindowUID for Label {
	fn uid(&self) -> WindowUID {
		self.base.uid
	}
}

impl Widget for Label {
	fn set_alignment(&mut self, horizontal: HorizontalAlignment, vertical: VerticalAlignment) {
		self.base.alignment = (horizontal, vertical);
		self.provoke_changed_property(WindowProperty::Alignment);
	}

	fn set_visibility(&mut self, visibility: bool) {
		self.base.visibility = visibility;
		self.provoke_changed_property(WindowProperty::IsVisible);
	}

	fn set_width(&mut self, width: super::Size) {
		self.base.size.x = width;
		self.provoke_changed_property(WindowProperty::Size);
	}

	fn set_height(&mut self, _: super::Size) {}

	fn set_margin(&mut self, margin: Thickness) {
		self.base.margin = margin;
		self.provoke_changed_property(WindowProperty::Margin);
	}

	fn set_border(&mut self, border: BorderStyle) {
		self.base.border = border;
		self.provoke_changed_property(WindowProperty::Border);
	}

	fn set_enabled_state(&mut self, is_enabled: bool) {
		self.base.enabled = is_enabled;
	}

	fn set_width_constraint(&mut self, width: SizeConstraint) {
		self.base.constraints.x = width;
		self.provoke_changed_property(WindowProperty::Size);
	}

	fn set_height_constraint(&mut self, _: SizeConstraint) {}
}

impl WidgetColors for Label {
	fn set_disabled_color(&mut self, color: Option<Color>) {
		self.base.colors.disabled = color;
	}

	fn set_base_fg_color(&mut self, color: Option<Color>) {
		self.base.colors.base_fg = color;
	}

	fn set_base_bg_color(&mut self, color: Option<Color>) {
		self.base.colors.base_bg = color;
	}
}