use super::types::{Animation, TransitionPhase};
use crate::render::{Cell, Modifier};
use crate::style::Color;
use crate::widget::traits::{RenderContext, View, WidgetProps};
use crate::{impl_props_builders, impl_styled_view};
pub struct Transition {
child_content: String,
enter_animation: Option<Animation>,
leave_animation: Option<Animation>,
phase: TransitionPhase,
tween: Option<crate::style::Tween>,
visible: bool,
props: WidgetProps,
}
impl Transition {
pub fn new(content: impl Into<String>) -> Self {
Self {
child_content: content.into(),
enter_animation: None,
leave_animation: None,
phase: TransitionPhase::Visible,
tween: None,
visible: true,
props: WidgetProps::default(),
}
}
pub fn enter(mut self, animation: Animation) -> Self {
self.enter_animation = Some(animation);
self
}
pub fn leave(mut self, animation: Animation) -> Self {
self.leave_animation = Some(animation);
self
}
pub fn animations(mut self, enter: Animation, leave: Animation) -> Self {
self.enter_animation = Some(enter);
self.leave_animation = Some(leave);
self
}
pub fn show(&mut self) {
if !self.visible {
self.visible = true;
self.phase = TransitionPhase::Entering;
if let Some(anim) = &self.enter_animation {
let mut tween = crate::style::Tween::new(0.0, 1.0, anim.get_duration())
.easing(anim.get_easing())
.delay(anim.get_delay());
tween.start();
self.tween = Some(tween);
}
}
}
pub fn hide(&mut self) {
if self.visible {
self.phase = TransitionPhase::Leaving;
if let Some(anim) = &self.leave_animation {
let mut tween = crate::style::Tween::new(1.0, 0.0, anim.get_duration())
.easing(anim.get_easing())
.delay(anim.get_delay());
tween.start();
self.tween = Some(tween);
} else {
self.visible = false;
}
}
}
pub fn toggle(&mut self) {
if self.visible {
self.hide();
} else {
self.show();
}
}
pub fn is_visible(&self) -> bool {
self.visible
}
pub fn phase(&self) -> TransitionPhase {
self.phase
}
pub fn update(&mut self) {
if let Some(ref mut tween) = self.tween {
let _ = tween.value();
if tween.is_completed() {
match self.phase {
TransitionPhase::Entering => {
self.phase = TransitionPhase::Visible;
self.tween = None;
}
TransitionPhase::Leaving => {
self.phase = TransitionPhase::Hidden;
self.visible = false;
self.tween = None;
}
_ => {}
}
}
}
}
}
impl Default for Transition {
fn default() -> Self {
Self::new("")
}
}
impl View for Transition {
crate::impl_view_meta!("Transition");
fn render(&self, ctx: &mut RenderContext) {
if self.phase == TransitionPhase::Hidden {
return;
}
if !self.visible && self.phase == TransitionPhase::Visible {
return;
}
let progress = if let Some(ref tween) = self.tween {
tween.progress()
} else {
match self.phase {
TransitionPhase::Visible => 1.0,
TransitionPhase::Hidden => 0.0,
_ => 1.0,
}
};
let effective_progress = match self.phase {
TransitionPhase::Entering => progress,
TransitionPhase::Leaving => 1.0 - progress,
TransitionPhase::Visible => 1.0,
TransitionPhase::Hidden => 0.0,
};
let area = ctx.area;
let default_bg = Color::BLACK;
let default_fg = Color::WHITE;
let content_len = self.child_content.chars().count();
let chars_to_show = (effective_progress * content_len as f32).round() as usize;
let should_dim = effective_progress > 0.0 && effective_progress < 1.0;
let mut x: u16 = 0;
for (j, ch) in self.child_content.chars().enumerate() {
let cw = crate::utils::char_width(ch) as u16;
if x + cw <= area.width && area.height > 0 {
let cell = if j < chars_to_show {
let mut c = Cell::new(ch);
c.fg = Some(default_fg);
c.bg = Some(default_bg);
if should_dim {
c.modifier |= Modifier::DIM;
}
c
} else {
let mut c = Cell::new(' ');
c.bg = Some(default_bg);
c
};
ctx.set(x, 0, cell);
}
x += cw;
}
}
}
impl_styled_view!(Transition);
impl_props_builders!(Transition);