use crate::base::Property;
use crate::drawing::{Drawable, Plan};
use crate::interacting::{Event, Interactive};
use crate::layouts::{HasPlanner, Layout};
use crate::story_board::{StoryBoard, easing};
use skia_safe::{Canvas, Paint, Point, Rect};
use std::any::Any;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
use std::time::Duration;
pub struct SlideAnimation {
status: Rc<Property<bool>>,
direction: Point,
pub state: RefCell<StoryBoard>,
pub child: Rc<dyn Drawable>,
pub planner: RefCell<Option<Rc<dyn Layout>>>,
enabled: RefCell<bool>,
}
impl SlideAnimation {
pub fn new(
status: Rc<Property<bool>>,
direction: Point,
duration: Duration,
child: Rc<dyn Drawable>,
) -> Rc<Self> {
let mut state = StoryBoard::init(
0.0,
1.0,
duration.as_millis() as f64,
false,
easing::cubic_in_out,
);
if status.get() {
state.seek(1.0);
}
let s = Rc::new(Self {
status: status.clone(),
direction,
child: child.clone(),
planner: RefCell::new(None),
enabled: RefCell::new(true),
state: RefCell::new(state),
});
child.set_planner(Some(s.clone()));
let listener_id = format!("slide_anim_{:p}", s.as_ref());
let weak_s = Rc::downgrade(&s);
status.listen(
&listener_id,
Box::new(|v, u| {
if let Some(weak_u) = u.downcast_ref::<Weak<SlideAnimation>>()
&& let Some(u) = weak_u.upgrade()
{
if v {
u.state.borrow_mut().set_route(0.0, 1.0);
} else {
u.state.borrow_mut().set_route(1.0, 0.0);
}
u.state.borrow_mut().restart();
}
}),
Box::new(weak_s),
);
s
}
}
impl Drop for SlideAnimation {
fn drop(&mut self) {
let listener_id = format!("slide_anim_{:p}", self);
self.status.hang_up(&listener_id);
}
}
impl Layout for SlideAnimation {
fn measure_child(&self, _c: &dyn Drawable, canvas: &Canvas) -> Rect {
self.get_planned_drawing_area(canvas)
}
fn as_layout(&self) -> &dyn Layout {
self
}
fn foreach_child(&self, f: &mut dyn FnMut(&dyn Drawable)) {
f(self.child.as_ref())
}
}
impl Drawable for SlideAnimation {
fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
let progress = f64::from(&*self.state.borrow()) as f32;
if progress <= 0.001 {
return Ok(());
}
let inv_progress = 1.0 - progress;
let offset_x = self.direction.x * inv_progress;
let offset_y = self.direction.y * inv_progress;
canvas.save();
if offset_x != 0.0 || offset_y != 0.0 {
canvas.translate(Point::new(offset_x, offset_y));
let mut layer_paint = Paint::default();
layer_paint.set_alpha_f(progress);
canvas.save_layer(&skia_safe::canvas::SaveLayerRec::default().paint(&layer_paint));
}
self.child.draw(canvas).ok();
if offset_x != 0.0 || offset_y != 0.0 {
canvas.restore();
}
canvas.restore();
Ok(())
}
fn get_horizontal_plan(&self) -> Plan {
self.child.get_horizontal_plan()
}
fn get_vertical_plan(&self) -> Plan {
self.child.get_vertical_plan()
}
fn replan(&self) {
self.child.replan()
}
fn try_to_interactive(&self) -> Option<&dyn Interactive> {
Some(self)
}
fn is_enabled(&self) -> bool {
*self.enabled.borrow()
}
fn set_enabled(&self, e: bool) {
*self.enabled.borrow_mut() = e
}
fn as_drawable(&self) -> &dyn Drawable {
self
}
}
impl HasPlanner for SlideAnimation {
fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
&self.planner
}
fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
*self.planner.borrow_mut() = p;
}
}
impl Interactive for SlideAnimation {
fn handle_and_route(&self, e: &Event, u: &dyn Any) {
self.foreach_child(&mut |x| {
if x.is_enabled()
&& let Some(i) = x.try_to_interactive()
{
i.handle_and_route(e, u);
}
});
}
}
pub struct SlideShowAnimation {
pub state: RefCell<StoryBoard>,
direction: Point,
pub child: Rc<dyn Drawable>,
pub planner: RefCell<Option<Rc<dyn Layout>>>,
enabled: RefCell<bool>,
}
impl SlideShowAnimation {
pub fn new(direction: Point, duration: Duration, child: Rc<dyn Drawable>) -> Rc<Self> {
let mut state = StoryBoard::init(
0.0,
1.0,
duration.as_millis() as f64,
false,
easing::cubic_fly_hover_out,
);
state.seek(1.0);
let s = Rc::new(Self {
direction,
child: child.clone(),
planner: RefCell::new(None),
enabled: RefCell::new(true),
state: RefCell::new(state),
});
child.set_planner(Some(s.clone()));
s
}
pub fn show(&self) {
let mut state = self.state.borrow_mut();
let p = state.get_progress();
let t_in = 500.0 / 4500.0;
let t_out = 4000.0 / 4500.0;
if p > t_in && p < t_out {
state.seek(t_in + 0.001);
} else if p >= t_out {
state.restart();
}
}
}
impl Layout for SlideShowAnimation {
fn measure_child(&self, _c: &dyn Drawable, canvas: &Canvas) -> Rect {
self.get_planned_drawing_area(canvas)
}
fn as_layout(&self) -> &dyn Layout {
self
}
fn foreach_child(&self, f: &mut dyn FnMut(&dyn Drawable)) {
f(self.child.as_ref())
}
}
impl Drawable for SlideShowAnimation {
fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
let progress = f64::from(&*self.state.borrow()) as f32;
if progress <= 0.001 {
return Ok(());
}
let inv_progress = 1.0 - progress;
let offset_x = self.direction.x * inv_progress;
let offset_y = self.direction.y * inv_progress;
canvas.save();
if offset_x != 0.0 || offset_y != 0.0 {
canvas.translate(Point::new(offset_x, offset_y));
let mut layer_paint = Paint::default();
layer_paint.set_alpha_f(progress);
canvas.save_layer(&skia_safe::canvas::SaveLayerRec::default().paint(&layer_paint));
}
self.child.draw(canvas).ok();
if offset_x != 0.0 || offset_y != 0.0 {
canvas.restore();
}
canvas.restore();
Ok(())
}
fn get_horizontal_plan(&self) -> Plan {
self.child.get_horizontal_plan()
}
fn get_vertical_plan(&self) -> Plan {
self.child.get_vertical_plan()
}
fn replan(&self) {
self.child.replan()
}
fn try_to_interactive(&self) -> Option<&dyn Interactive> {
Some(self)
}
fn is_enabled(&self) -> bool {
*self.enabled.borrow()
}
fn set_enabled(&self, e: bool) {
*self.enabled.borrow_mut() = e
}
fn as_drawable(&self) -> &dyn Drawable {
self
}
}
impl HasPlanner for SlideShowAnimation {
fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
&self.planner
}
fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
*self.planner.borrow_mut() = p;
}
}
impl Interactive for SlideShowAnimation {
fn handle_and_route(&self, e: &Event, u: &dyn Any) {
self.foreach_child(&mut |x| {
if x.is_enabled()
&& let Some(i) = x.try_to_interactive()
{
i.handle_and_route(e, u);
}
});
}
}