use super::timeline::{Timeline, TimelineEx};
use crate::{
core::timeline::Timeline as CoreTimeline,
timeline::{Status, TimelineId},
Animation,
};
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
use std::{collections::HashMap, rc::Rc};
thread_local! {
static MANAGER: Manager = Manager::new();
}
pub fn timeline<F>(animation: F) -> Timeline<F::Item>
where
F: Animation + 'static,
{
let timeline: CoreTimeline<_> = CoreTimeline::new(animation);
let shared = MANAGER.with(|m| m.shared.clone());
let wrapper = TimelineWrapper::new(timeline, shared);
Timeline::new(wrapper)
}
#[inline]
pub fn update() {
MANAGER.with(|m| m.update());
}
pub(crate) struct TimelineWrapper<T> {
id: TimelineId,
pub(crate) inner: Rc<Mutex<Inner<T>>>,
shared: Shared,
}
pub(crate) struct Inner<T> {
pub(crate) timeline: CoreTimeline<T>,
scheduled: bool,
}
impl<T> TimelineWrapper<T> {
#[inline]
fn new(timeline: CoreTimeline<T>, shared: Shared) -> Self {
Self {
id: timeline.id(),
inner: Rc::new(Mutex::new(Inner {
timeline,
scheduled: false,
})),
shared,
}
}
#[inline]
fn scheduled(&self) -> bool {
let state = self.inner.lock();
state.scheduled
}
}
impl<T: 'static> TimelineEx<T> for TimelineWrapper<T> {
#[inline]
fn status(&self) -> Status {
let state = &*self.inner.lock();
state.timeline.status()
}
#[inline]
fn value(&self) -> T {
let state = &*self.inner.lock();
state.timeline.value()
}
#[inline]
fn begin(&self) {
{
let state = &mut *self.inner.lock();
state.timeline.begin();
}
self.shared.schedule(Rc::clone(&self.inner));
}
#[inline]
fn stop(&self) {
{
let state = &mut *self.inner.lock();
state.timeline.stop();
}
let id = self.id;
self.shared.cancel(id);
}
#[inline]
fn pause(&self) {
{
let state = &mut *self.inner.lock();
state.timeline.pause();
}
let id = self.id;
self.shared.cancel(id);
}
#[inline]
fn resume(&self) {
{
let state = &mut *self.inner.lock();
state.timeline.resume();
}
self.shared.schedule(Rc::clone(&self.inner));
}
#[inline]
fn reset(&self) {
let state = &mut *self.inner.lock();
state.timeline.reset();
}
}
impl<T> Drop for TimelineWrapper<T> {
fn drop(&mut self) {
let id = self.id;
let scheduled = self.scheduled();
if scheduled && Rc::strong_count(&self.inner) == 2 {
self.shared.cancel(id);
}
}
}
impl<T: 'static> From<TimelineWrapper<T>> for Timeline<T> {
#[inline]
fn from(src: TimelineWrapper<T>) -> Self {
Timeline::new(src)
}
}
trait TimelineControl {
fn id(&self) -> TimelineId;
fn update(&self) -> Status;
fn on_schedule(&self);
fn on_remove(&self);
}
impl<T> TimelineControl for Rc<Mutex<Inner<T>>> {
#[inline]
fn id(&self) -> TimelineId {
let state = &*self.lock();
state.timeline.id()
}
#[inline]
fn update(&self) -> Status {
let state = &mut *self.lock();
state.timeline.update()
}
#[inline]
fn on_schedule(&self) {
let state = &mut *self.lock();
state.scheduled = true;
}
#[inline]
fn on_remove(&self) {
let state = &mut *self.lock();
state.scheduled = false;
}
}
#[derive(Clone)]
struct Shared(Rc<RwLock<HashMap<TimelineId, Box<dyn TimelineControl + 'static>>>>);
impl Shared {
#[inline]
fn update(&self) {
let mut holder = Vec::new();
let state = self.0.upgradable_read();
for (id, item) in state.iter() {
let status = item.update();
if status == Status::Completed || status == Status::Paused {
holder.push(*id);
}
}
if !holder.is_empty() {
let mut state = RwLockUpgradableReadGuard::upgrade(state);
for id in holder {
state.remove(&id);
}
}
}
#[inline]
fn schedule(&self, timeline: impl TimelineControl + 'static) {
let id = timeline.id();
let state = self.0.upgradable_read();
if !state.contains_key(&id) {
timeline.on_schedule();
let mut state = RwLockUpgradableReadGuard::upgrade(state);
state.insert(id, Box::new(timeline));
}
}
#[inline]
fn cancel(&self, id: TimelineId) -> bool {
let res = {
let state = self.0.upgradable_read();
if state.contains_key(&id) {
let mut state = RwLockUpgradableReadGuard::upgrade(state);
state.remove(&id)
} else {
None
}
};
if let Some(ref item) = res {
item.on_remove();
}
res.is_some()
}
}
struct Manager {
shared: Shared,
}
impl Manager {
fn new() -> Self {
Self {
shared: Shared(Rc::new(RwLock::new(Default::default()))),
}
}
#[inline]
fn update(&self) {
self.shared.update();
}
}