/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <controller/SlsAnimator.hxx>
#include <utility>
#include <view/SlideSorterView.hxx>
#include <osl/diagnose.h>
namespace sd::slidesorter::controller {
/** Handle one animation function by using a timer for frequent calls to
the animations operator().
*/
class Animator::Animation
{
public:
Animation (
Animator::AnimationFunctor aAnimation,
const double nStartOffset,
const double nDuration,
const double nGlobalTime,
const Animator::AnimationId nAnimationId,
Animator::FinishFunctor aFinishFunctor);
/** Run next animation step. If animation has reached its end it is
expired.
*/
bool Run (
const double nGlobalTime);
/** Typically called when an animation has finished, but also from
Animator::Disposed(). The finish functor is called and the
animation is marked as expired to prevent another run.
*/
void Expire();
bool IsExpired()
const {
return mbIsExpired;}
Animator::AnimationFunctor maAnimation;
Animator::FinishFunctor maFinishFunctor;
const Animator::AnimationId mnAnimationId;
const double mnDuration;
const double mnEnd;
const double mnGlobalTimeAtStart;
bool mbIsExpired;
};
Animator::Animator (SlideSorter& rSlideSorter)
: mrSlideSorter(rSlideSorter),
maIdle(
"sd slidesorter controller Animator"),
mbIsDisposed(
false),
mnNextAnimationId(0)
{
maIdle.SetPriority(TaskPriority::REPAINT);
maIdle.SetInvokeHandler(LINK(
this,Animator,TimeoutHandler));
}
Animator::~Animator()
{
if ( ! mbIsDisposed)
{
OSL_ASSERT(mbIsDisposed);
Dispose();
}
}
void Animator::Dispose()
{
mbIsDisposed =
true;
AnimationList aCopy (maAnimations);
for (
const auto& rxAnimation : aCopy)
rxAnimation->Expire();
maIdle.Stop();
if (mpDrawLock)
{
mpDrawLock->Dispose();
mpDrawLock.reset();
}
}
Animator::AnimationId Animator::AddAnimation (
const AnimationFunctor& rAnimation,
const FinishFunctor& rFinishFunctor)
{
// When the animator is already disposed then ignore this call
// silently (well, we show an assertion, but do not throw an exception.)
OSL_ASSERT( ! mbIsDisposed);
if (mbIsDisposed)
return -1;
std::shared_ptr<Animation> pAnimation =
std::make_shared<Animation>(
rAnimation,
0,
300 / 1000.0,
maElapsedTime.getElapsedTime(),
++mnNextAnimationId,
rFinishFunctor);
maAnimations.push_back(pAnimation);
RequestNextFrame();
return pAnimation->mnAnimationId;
}
void Animator::RemoveAnimation (
const Animator::AnimationId nId)
{
OSL_ASSERT( ! mbIsDisposed);
const AnimationList::iterator iAnimation (::std::find_if(
maAnimations.begin(),
maAnimations.end(),
[nId] (std::shared_ptr<Animation>
const& pAnim)
{
return nId == pAnim->mnAnimationId; }));
if (iAnimation != maAnimations.end())
{
OSL_ASSERT((*iAnimation)->mnAnimationId == nId);
(*iAnimation)->Expire();
maAnimations.erase(iAnimation);
}
if (maAnimations.empty())
{
// Reset the animation id when we can.
mnNextAnimationId = 0;
// No more animations => we do not have to suppress painting
// anymore.
mpDrawLock.reset();
}
}
void Animator::RemoveAllAnimations()
{
for (
auto const& it : maAnimations)
{
it->Expire();
}
maAnimations.clear();
mnNextAnimationId = 0;
// No more animations => we do not have to suppress painting
// anymore.
mpDrawLock.reset();
}
bool Animator::ProcessAnimations (
const double nTime)
{
bool bExpired (
false);
OSL_ASSERT( ! mbIsDisposed);
if (mbIsDisposed)
return bExpired;
AnimationList aCopy (maAnimations);
for (
const auto& rxAnimation : aCopy)
{
bExpired |= rxAnimation->Run(nTime);
}
return bExpired;
}
void Animator::CleanUpAnimationList()
{
OSL_ASSERT( ! mbIsDisposed);
if (mbIsDisposed)
return;
AnimationList aActiveAnimations;
for (
const auto& rxAnimation : maAnimations)
{
if ( ! rxAnimation->IsExpired())
aActiveAnimations.push_back(rxAnimation);
}
maAnimations.swap(aActiveAnimations);
}
void Animator::RequestNextFrame ()
{
if ( ! maIdle.IsActive())
{
// Prevent redraws except for the ones in TimeoutHandler. While the
// Animator is active it will schedule repaints regularly. Repaints
// in between would only lead to visual artifacts.
mpDrawLock.reset(
new view::SlideSorterView::DrawLock(mrSlideSorter));
maIdle.Start();
}
}
IMPL_LINK_NOARG(Animator, TimeoutHandler, Timer *,
void)
{
if (mbIsDisposed)
return;
if (ProcessAnimations(maElapsedTime.getElapsedTime()))
CleanUpAnimationList();
// Unlock the draw lock. This should lead to a repaint.
mpDrawLock.reset();
if (!maAnimations.empty())
RequestNextFrame();
}
//===== Animator::Animation ===================================================
Animator::Animation::Animation (
Animator::AnimationFunctor aAnimation,
const double nStartOffset,
const double nDuration,
const double nGlobalTime,
const Animator::AnimationId nId,
Animator::FinishFunctor aFinishFunctor)
: maAnimation(std::move(aAnimation)),
maFinishFunctor(std::move(aFinishFunctor)),
mnAnimationId(nId),
mnDuration(nDuration),
mnEnd(nGlobalTime + nDuration + nStartOffset),
mnGlobalTimeAtStart(nGlobalTime + nStartOffset),
mbIsExpired(
false)
{
Run(nGlobalTime);
}
bool Animator::Animation::Run (
const double nGlobalTime)
{
if ( ! mbIsExpired)
{
if (mnDuration > 0)
{
if (nGlobalTime >= mnEnd)
{
maAnimation(1.0);
Expire();
}
else if (nGlobalTime >= mnGlobalTimeAtStart)
{
maAnimation((nGlobalTime - mnGlobalTimeAtStart) / mnDuration);
}
}
else if (mnDuration < 0)
{
// Animations without end have to be expired by their owner.
maAnimation(nGlobalTime);
}
}
return mbIsExpired;
}
void Animator::Animation::Expire()
{
if ( ! mbIsExpired)
{
mbIsExpired =
true;
if (maFinishFunctor)
maFinishFunctor();
}
}
}
// end of namespace ::sd::slidesorter::controller
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */