use unicode_width::UnicodeWidthStr;
use super::*;
use crate::canvas::*;
#[derive(Default)]
pub struct TextBlock {
base: WidgetBase,
text: String,
text_alignment: HorizontalAlignment,
has_focus: bool,
line_wrap: bool,
focus_color: Option<Color>,
}
impl TextBlock {
pub const LINE_BREAK: char = '\n';
pub const WORD_BREAK: char = ' ';
pub fn new(text: String) -> Self {
Self {
text,
..Default::default()
}
}
pub fn get_text(&self) -> &str {
&self.text
}
pub fn set_text(&mut self, text: &str) {
self.text.clear();
self.text.push_str(text);
}
pub fn set_text_alignment(&mut self, alignment: HorizontalAlignment) {
self.text_alignment = alignment;
}
pub fn set_line_wrapping(&mut self, wrapping: bool) {
self.line_wrap = wrapping;
}
pub fn set_focus_color(&mut self, color: Option<Color>) {
self.focus_color = color;
}
}
impl Window for TextBlock {
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,
};
let mut row_index = 0;
let mut row = canvas.get_row_variable_width(row_index).unwrap();
let space = Grapheme::from(&Self::WORD_BREAK.to_string()).unwrap();
let dot3 = VariableWidthGlyphRow::DOT3_REPLACEMENT;
match self.line_wrap {
true => {
for ln in self.text.split(Self::LINE_BREAK) {
let words = ln.split(Self::WORD_BREAK).collect::<Vec<_>>();
let mut counter = 0;
while counter < words.len() {
let mut word_count = 0;
let mut text_w = 0;
for word in words[counter..].iter() {
text_w += (word_count > 0) as TSize;
let width = word.width() as TSize;
if (text_w + width) > row.width() {
text_w = text_w.checked_sub(1).unwrap_or_default();
break;
}
text_w += width;
word_count += 1;
}
if word_count == 0 {
row.add_string(words[counter], fg, None, Style::None, Some(dot3))
.unwrap();
counter += 1;
}
else {
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());
}
}
for n in 0..word_count {
row.add_string(words[counter + n], fg, None, Style::None, None)
.unwrap();
if n != word_count - 1 {
row.add_grapheme(space, fg, None, Style::None).unwrap();
}
}
}
counter += word_count;
row_index += 1;
row = match canvas.get_row_variable_width(row_index) {
Some(l) => l,
None => break,
};
}
row_index += 1;
row = match canvas.get_row_variable_width(row_index) {
Some(l) => l,
None => break,
};
}
}
false => {
for ln in self.text.split(Self::LINE_BREAK) {
let text_w = ln.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(ln, fg, None, Style::None, Some(dot3))
.unwrap();
row_index += 1;
row = match canvas.get_row_variable_width(row_index) {
Some(l) => l,
None => break,
};
}
}
}
}
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 TextBlock {
fn desired_size(&self, available_size: TPoint) -> TPoint {
self.base.desired_size(available_size)
}
fn border(&self) -> BorderStyle {
self.base.border
}
fn alignment(&self) -> (HorizontalAlignment, VerticalAlignment) {
self.base.alignment
}
fn is_visible(&self) -> bool {
self.base.visibility
}
fn margin(&self) -> Thickness {
self.base.margin
}
}
impl HasWindowUID for TextBlock {
fn uid(&self) -> WindowUID {
self.base.uid
}
}
impl Widget for TextBlock {
fn set_visibility(&mut self, visibility: bool) {
self.base.visibility = visibility;
self.provoke_changed_property(WindowProperty::IsVisible);
}
fn set_width(&mut self, width: Size) {
self.base.size.x = width;
self.provoke_changed_property(WindowProperty::Size);
}
fn set_height(&mut self, height: Size) {
self.base.size.y = height;
self.provoke_changed_property(WindowProperty::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_alignment(&mut self, horizontal: HorizontalAlignment, vertical: VerticalAlignment) {
self.base.alignment = (horizontal, vertical);
self.provoke_changed_property(WindowProperty::Alignment);
}
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, height: SizeConstraint) {
self.base.constraints.y = height;
self.provoke_changed_property(WindowProperty::Size);
}
}
impl WidgetColors for TextBlock {
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;
}
}