use crate::component::Component;
use crate::components::slotted_bar::SlotContent;
use crate::components::text::TextAlign;
use crate::context::{RenderContext, UseTheme};
use crate::event::EventHandler;
use crate::layout::Rect;
use crate::render::Renderer;
use anyhow::Result;
pub struct TextSlot {
text: String,
align: TextAlign,
style: String,
fixed_width: Option<u16>,
dirty: bool,
}
impl TextSlot {
pub fn new(text: impl Into<String>) -> Self {
TextSlot {
text: text.into(),
align: TextAlign::Start,
style: String::new(),
fixed_width: None,
dirty: true,
}
}
pub fn with_align(mut self, align: TextAlign) -> Self {
self.align = align;
self
}
pub fn with_style(mut self, style: impl Into<String>) -> Self {
self.style = style.into();
self
}
pub fn with_fixed_width(mut self, width: u16) -> Self {
self.fixed_width = Some(width);
self
}
pub fn set_text(&mut self, text: impl Into<String>) {
self.text = text.into();
self.dirty = true;
}
pub fn text(&self) -> &str {
&self.text
}
}
impl EventHandler for TextSlot {}
impl Component for TextSlot {
fn render(&mut self, renderer: &mut Renderer, bounds: Rect, ctx: &RenderContext) -> Result<()> {
let text_len = self.text.len() as u16;
if bounds.width == 0 {
return Ok(());
}
let display_text = if text_len > bounds.width {
&self.text[..bounds.width as usize]
} else {
&self.text
};
let text_direction = self.use_text_direction(ctx);
let physical_align = self.align.resolve(text_direction);
let x = match physical_align {
crate::components::text::PhysicalAlign::Left => bounds.x,
crate::components::text::PhysicalAlign::Center => {
let offset = (bounds.width.saturating_sub(display_text.len() as u16)) / 2;
bounds.x.saturating_add(offset)
}
crate::components::text::PhysicalAlign::Right => {
let offset = bounds.width.saturating_sub(display_text.len() as u16);
bounds.x.saturating_add(offset)
}
};
renderer.move_cursor(x, bounds.y)?;
if self.style.is_empty() {
renderer.write_text(display_text)?;
} else {
renderer.write_styled(display_text, &self.style)?;
}
self.dirty = false;
Ok(())
}
fn min_size(&self) -> (u16, u16) {
(self.text.len() as u16, 1)
}
fn mark_dirty(&mut self) {
self.dirty = true;
}
fn is_dirty(&self) -> bool {
self.dirty
}
fn name(&self) -> &str {
"TextSlot"
}
}
impl SlotContent for TextSlot {
fn responsive_sizes(&self) -> Vec<crate::components::slotted_bar::SlotSize> {
use crate::components::slotted_bar::SlotSize;
if let Some(fixed) = self.fixed_width {
vec![SlotSize::Blocks(fixed)]
} else {
let text_len = self.text.len() as u16;
vec![SlotSize::Fill, SlotSize::Blocks(text_len)]
}
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
pub struct Badge {
text: String,
style: String,
padding: u16,
dirty: bool,
}
impl Badge {
pub fn new(text: impl Into<String>) -> Self {
Badge {
text: text.into(),
style: "\x1b[7m".to_string(), padding: 1,
dirty: true,
}
}
pub fn with_style(mut self, style: impl Into<String>) -> Self {
self.style = style.into();
self
}
pub fn with_padding(mut self, padding: u16) -> Self {
self.padding = padding;
self
}
fn total_width(&self) -> u16 {
self.text.len() as u16 + (self.padding * 2)
}
}
impl EventHandler for Badge {}
impl Component for Badge {
fn render(
&mut self,
renderer: &mut Renderer,
bounds: Rect,
_ctx: &RenderContext,
) -> Result<()> {
let padding_str = " ".repeat(self.padding as usize);
let full_text = format!("{}{}{}", padding_str, self.text, padding_str);
renderer.move_cursor(bounds.x, bounds.y)?;
renderer.write_styled(&full_text, &self.style)?;
self.dirty = false;
Ok(())
}
fn min_size(&self) -> (u16, u16) {
(self.total_width(), 1)
}
fn mark_dirty(&mut self) {
self.dirty = true;
}
fn is_dirty(&self) -> bool {
self.dirty
}
fn name(&self) -> &str {
"Badge"
}
}
impl SlotContent for Badge {
fn responsive_sizes(&self) -> Vec<crate::components::slotted_bar::SlotSize> {
use crate::components::slotted_bar::SlotSize;
vec![SlotSize::Blocks(self.total_width())]
}
fn can_hide(&self) -> bool {
false }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
pub struct Spacer;
impl Spacer {
pub fn new() -> Self {
Spacer
}
}
impl Default for Spacer {
fn default() -> Self {
Self::new()
}
}
impl EventHandler for Spacer {}
impl Component for Spacer {
fn render(
&mut self,
_renderer: &mut Renderer,
_bounds: Rect,
_ctx: &RenderContext,
) -> Result<()> {
Ok(())
}
fn min_size(&self) -> (u16, u16) {
(0, 1)
}
fn mark_dirty(&mut self) {
}
fn is_dirty(&self) -> bool {
false
}
fn name(&self) -> &str {
"Spacer"
}
}
impl SlotContent for Spacer {
fn responsive_sizes(&self) -> Vec<crate::components::slotted_bar::SlotSize> {
use crate::components::slotted_bar::SlotSize;
vec![SlotSize::Fill]
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}