// https://w3c.github.io/web-animations/#set-the-current-time void Animation::SetCurrentTime(const TimeDuration& aSeekTime) { // Return early if the current time has not changed. However, if we // are pause-pending, then setting the current time to any value // including the current value has the effect of aborting the // pause so we should not return early in that case. if (mPendingState != PendingState::PausePending && Nullable<TimeDuration>(aSeekTime) == GetCurrentTime()) { return; } AutoMutationBatchForAnimation mb(*this); SilentlySetCurrentTime(aSeekTime); if (mPendingState == PendingState::PausePending) { // Finish the pause operation mHoldTime.SetValue(aSeekTime); mStartTime.SetNull(); if (mReady) { mReady->MaybeResolve(this); } CancelPendingTasks(); } UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Async); if (IsRelevant()) { nsNodeUtils::AnimationChanged(this); } PostUpdate(); }
// https://w3c.github.io/web-animations/#set-the-animation-playback-rate void Animation::SetPlaybackRate(double aPlaybackRate) { if (aPlaybackRate == mPlaybackRate) { return; } AutoMutationBatchForAnimation mb(*this); Nullable<TimeDuration> previousTime = GetCurrentTime(); mPlaybackRate = aPlaybackRate; if (!previousTime.IsNull()) { SetCurrentTime(previousTime.Value()); } // In the case where GetCurrentTime() returns the same result before and // after updating mPlaybackRate, SetCurrentTime will return early since, // as far as it can tell, nothing has changed. // As a result, we need to perform the following updates here: // - update timing (since, if the sign of the playback rate has changed, our // finished state may have changed), // - dispatch a change notification for the changed playback rate, and // - update the playback rate on animations on layers. UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Async); if (IsRelevant()) { nsNodeUtils::AnimationChanged(this); } PostUpdate(); }
// https://w3c.github.io/web-animations/#finish-an-animation void Animation::Finish(ErrorResult& aRv) { if (mPlaybackRate == 0 || (mPlaybackRate > 0 && EffectEnd() == TimeDuration::Forever())) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } AutoMutationBatchForAnimation mb(*this); // Seek to the end TimeDuration limit = mPlaybackRate > 0 ? TimeDuration(EffectEnd()) : TimeDuration(0); bool didChange = GetCurrentTime() != Nullable<TimeDuration>(limit); SilentlySetCurrentTime(limit); // If we are paused or play-pending we need to fill in the start time in // order to transition to the finished state. // // We only do this, however, if we have an active timeline. If we have an // inactive timeline we can't transition into the finished state just like // we can't transition to the running state (this finished state is really // a substate of the running state). if (mStartTime.IsNull() && mTimeline && !mTimeline->GetCurrentTime().IsNull()) { mStartTime.SetValue(mTimeline->GetCurrentTime().Value() - limit.MultDouble(1.0 / mPlaybackRate)); didChange = true; } // If we just resolved the start time for a pause or play-pending // animation, we need to clear the task. We don't do this as a branch of // the above however since we can have a play-pending animation with a // resolved start time if we aborted a pause operation. if (!mStartTime.IsNull() && (mPendingState == PendingState::PlayPending || mPendingState == PendingState::PausePending)) { if (mPendingState == PendingState::PausePending) { mHoldTime.SetNull(); } CancelPendingTasks(); didChange = true; if (mReady) { mReady->MaybeResolve(this); } } UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Sync); if (didChange && IsRelevant()) { nsNodeUtils::AnimationChanged(this); } PostUpdate(); }
void Animation::UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag) { // Update the sequence number each time we transition in or out of the // idle state if (!IsUsingCustomCompositeOrder()) { if (PlayState() == AnimationPlayState::Idle) { mSequenceNum = kUnsequenced; } else if (mSequenceNum == kUnsequenced) { mSequenceNum = sNextSequenceNum++; } } // We call UpdateFinishedState before UpdateEffect because the former // can change the current time, which is used by the latter. UpdateFinishedState(aSeekFlag, aSyncNotifyFlag); UpdateEffect(); // Unconditionally Add/Remove from the timeline. This is ok because if the // animation has already been added/removed (which will be true more often // than not) the work done by AnimationTimeline/DocumentTimeline is still // negligible and its easier than trying to detect whenever we are switching // to/from being relevant. // // We need to do this after calling UpdateEffect since it updates some // cached state used by IsRelevant. // // Note that we only store relevant animations on the timeline since they // are the only ones that need ticks and are the only ones returned from // AnimationTimeline::GetAnimations. Storing any more than that would mean // that we fail to garbage collect irrelevant animations since the timeline // keeps a strong reference to each animation. // // Once we tick animations from the their timeline, and once we expect // timelines to go in and out of being inactive, we will also need to store // non-idle animations that are waiting for their timeline to become active // on their timeline (as otherwise once the timeline becomes active it will // have no way of notifying its animations). For now, however, we can // simply store just the relevant animations. if (mTimeline) { // FIXME: Once we expect animations to go back and forth betweeen being // inactive and active, we will need to store more than just relevant // animations on the timeline. This is because an animation might be // deemed irrelevant because its timeline is inactive. If it is removed // from the timeline at that point the timeline will have no way of // getting the animation to add itself again once it becomes active. if (IsRelevant()) { mTimeline->AddAnimation(*this); } else { mTimeline->RemoveAnimation(*this); } } }
// https://w3c.github.io/web-animations/#pause-an-animation void Animation::DoPause(ErrorResult& aRv) { if (IsPausedOrPausing()) { return; } AutoMutationBatchForAnimation mb(*this); // If we are transitioning from idle, fill in the current time if (GetCurrentTime().IsNull()) { if (mPlaybackRate >= 0.0) { mHoldTime.SetValue(TimeDuration(0)); } else { if (EffectEnd() == TimeDuration::Forever()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } mHoldTime.SetValue(TimeDuration(EffectEnd())); } } bool reuseReadyPromise = false; if (mPendingState == PendingState::PlayPending) { CancelPendingTasks(); reuseReadyPromise = true; } if (!reuseReadyPromise) { // Clear ready promise. We'll create a new one lazily. mReady = nullptr; } mPendingState = PendingState::PausePending; nsIDocument* doc = GetRenderedDocument(); if (doc) { PendingAnimationTracker* tracker = doc->GetOrCreatePendingAnimationTracker(); tracker->AddPausePending(*this); } else { TriggerOnNextTick(Nullable<TimeDuration>()); } UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async); if (IsRelevant()) { nsNodeUtils::AnimationChanged(this); } }
void CLogChangedPathArray::RemoveIrrelevantPaths() { struct IsRelevant { bool operator()(const CLogChangedPath& path) const { return !path.IsRelevantForStartPath(); } }; iterator first = begin(); iterator last = end(); erase (std::remove_copy_if (first, last, first, IsRelevant()), last); actions = 0; }
// https://w3c.github.io/web-animations/#set-the-animation-start-time void Animation::SetStartTime(const Nullable<TimeDuration>& aNewStartTime) { if (aNewStartTime == mStartTime) { return; } AutoMutationBatchForAnimation mb(*this); Nullable<TimeDuration> timelineTime; if (mTimeline) { // The spec says to check if the timeline is active (has a resolved time) // before using it here, but we don't need to since it's harmless to set // the already null time to null. timelineTime = mTimeline->GetCurrentTime(); } if (timelineTime.IsNull() && !aNewStartTime.IsNull()) { mHoldTime.SetNull(); } Nullable<TimeDuration> previousCurrentTime = GetCurrentTime(); mStartTime = aNewStartTime; if (!aNewStartTime.IsNull()) { if (mPlaybackRate != 0.0) { mHoldTime.SetNull(); } } else { mHoldTime = previousCurrentTime; } CancelPendingTasks(); if (mReady) { // We may have already resolved mReady, but in that case calling // MaybeResolve is a no-op, so that's okay. mReady->MaybeResolve(this); } UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async); if (IsRelevant()) { nsNodeUtils::AnimationChanged(this); } PostUpdate(); }
// https://w3c.github.io/web-animations/#reverse-an-animation void Animation::Reverse(ErrorResult& aRv) { if (!mTimeline || mTimeline->GetCurrentTime().IsNull()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } if (mPlaybackRate == 0.0) { return; } AutoMutationBatchForAnimation mb(*this); SilentlySetPlaybackRate(-mPlaybackRate); Play(aRv, LimitBehavior::AutoRewind); if (IsRelevant()) { nsNodeUtils::AnimationChanged(this); } }
// https://w3c.github.io/web-animations/#play-an-animation void Animation::DoPlay(ErrorResult& aRv, LimitBehavior aLimitBehavior) { AutoMutationBatchForAnimation mb(*this); bool abortedPause = mPendingState == PendingState::PausePending; Nullable<TimeDuration> currentTime = GetCurrentTime(); if (mPlaybackRate > 0.0 && (currentTime.IsNull() || (aLimitBehavior == LimitBehavior::AutoRewind && (currentTime.Value().ToMilliseconds() < 0.0 || currentTime.Value() >= EffectEnd())))) { mHoldTime.SetValue(TimeDuration(0)); } else if (mPlaybackRate < 0.0 && (currentTime.IsNull() || (aLimitBehavior == LimitBehavior::AutoRewind && (currentTime.Value().ToMilliseconds() <= 0.0 || currentTime.Value() > EffectEnd())))) { if (EffectEnd() == TimeDuration::Forever()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } mHoldTime.SetValue(TimeDuration(EffectEnd())); } else if (mPlaybackRate == 0.0 && currentTime.IsNull()) { mHoldTime.SetValue(TimeDuration(0)); } bool reuseReadyPromise = false; if (mPendingState != PendingState::NotPending) { CancelPendingTasks(); reuseReadyPromise = true; } // If the hold time is null then we're either already playing normally (and // we can ignore this call) or we aborted a pending pause operation (in which // case, for consistency, we need to go through the motions of doing an // asynchronous start even though we already have a resolved start time). if (mHoldTime.IsNull() && !abortedPause) { return; } // Clear the start time until we resolve a new one. We do this except // for the case where we are aborting a pause and don't have a hold time. // // If we're aborting a pause and *do* have a hold time (e.g. because // the animation is finished or we just applied the auto-rewind behavior // above) we should respect it by clearing the start time. If we *don't* // have a hold time we should keep the current start time so that the // the animation continues moving uninterrupted by the aborted pause. // // (If we're not aborting a pause, mHoldTime must be resolved by now // or else we would have returned above.) if (!mHoldTime.IsNull()) { mStartTime.SetNull(); } if (!reuseReadyPromise) { // Clear ready promise. We'll create a new one lazily. mReady = nullptr; } mPendingState = PendingState::PlayPending; nsIDocument* doc = GetRenderedDocument(); if (doc) { PendingAnimationTracker* tracker = doc->GetOrCreatePendingAnimationTracker(); tracker->AddPlayPending(*this); } else { TriggerOnNextTick(Nullable<TimeDuration>()); } UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async); if (IsRelevant()) { nsNodeUtils::AnimationChanged(this); } }
void ColumnManager::ReadSettings ( DWORD defaultColumns , DWORD hideColumns , const CString& containerName , int maxsize , int * widthlist) { // defaults DWORD selectedStandardColumns = defaultColumns & ~hideColumns; m_dwDefaultColumns = defaultColumns & ~hideColumns; columns.resize (maxsize); int power = 1; for (size_t i = 0; i < maxsize; ++i) { columns[i].index = static_cast<int>(i); if(widthlist==NULL) columns[i].width = 0; else columns[i].width = widthlist[i]; columns[i].visible = true; columns[i].relevant = !(hideColumns & power); power *= 2; } // userProps.clear(); // where the settings are stored within the registry registryPrefix = _T("Software\\TortoiseGit\\StatusColumns\\") + containerName; // we accept settings of current version only bool valid = (DWORD)CRegDWORD (registryPrefix + _T("Version"), 0xff) == BLAME_COLUMN_VERSION; if (valid) { // read (possibly different) column selection selectedStandardColumns = CRegDWORD (registryPrefix, selectedStandardColumns) & ~hideColumns; // read user-prop lists CString userPropList = CRegString (registryPrefix + _T("UserProps")); CString shownUserProps = CRegString (registryPrefix + _T("ShownUserProps")); ParseUserPropSettings (userPropList, shownUserProps); // read column widths CString colWidths = CRegString (registryPrefix + _T("_Width")); ParseWidths (colWidths); } // process old-style visibility setting SetStandardColumnVisibility (selectedStandardColumns); // clear all previously set header columns int c = ((CHeaderCtrl*)(control->GetDlgItem(0)))->GetItemCount()-1; while (c>=0) control->DeleteColumn(c--); // create columns for (int i = 0, count = GetColumnCount(); i < count; ++i) control->InsertColumn (i, GetName(i), LVCFMT_LEFT, IsVisible(i)&&IsRelevant(i) ? -1 : GetVisibleWidth(i, false)); // restore column ordering if (valid) ParseColumnOrder (CRegString (registryPrefix + _T("_Order"))); else ParseColumnOrder (CString()); ApplyColumnOrder(); // auto-size the columns so we can see them while fetching status // (seems the same values will not take affect in InsertColumn) for (int i = 0, count = GetColumnCount(); i < count; ++i) if (IsVisible(i)) control->SetColumnWidth (i, GetVisibleWidth (i, true)); }
void ColumnManager::OnContextMenuHeader(CWnd* pWnd, CPoint point, bool isGroundEnable /* = false*/) { CHeaderCtrl* pHeaderCtrl = (CHeaderCtrl*)pWnd; if ((point.x == -1) && (point.y == -1)) { CRect rect; pHeaderCtrl->GetItemRect(0, &rect); pHeaderCtrl->ClientToScreen(&rect); point = rect.CenterPoint(); } CMenu popup; if (popup.CreatePopupMenu()) { int columnCount = GetColumnCount(); CString temp; UINT uCheckedFlags = MF_STRING | MF_ENABLED | MF_CHECKED; UINT uUnCheckedFlags = MF_STRING | MF_ENABLED; // build control menu //temp.LoadString(IDS_STATUSLIST_SHOWGROUPS); //popup.AppendMenu(isGroundEnable? uCheckedFlags : uUnCheckedFlags, columnCount, temp); temp.LoadString(IDS_STATUSLIST_RESETCOLUMNORDER); popup.AppendMenu(uUnCheckedFlags, columnCount + 2, temp); popup.AppendMenu(MF_SEPARATOR); // standard columns AddMenuItem(&popup); // user-prop columns: // find relevant ones and sort 'em std::map<CString, int> sortedProps; for (int i = (int)itemName.size(); i < columnCount; ++i) if (IsRelevant(i)) sortedProps[GetName(i)] = i; if (!sortedProps.empty()) { // add 'em to the menu popup.AppendMenu(MF_SEPARATOR); for (auto iter = sortedProps.cbegin(), end = sortedProps.cend(); iter != end; ++iter) { popup.AppendMenu(IsVisible(iter->second) ? uCheckedFlags : uUnCheckedFlags , iter->second , iter->first); } } // show menu & let user pick an entry int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, pWnd); if ((cmd >= 1) && (cmd < columnCount)) SetVisible(cmd, !IsVisible(cmd)); else if (cmd == columnCount) { pWnd->GetParent()->SendMessage(LVM_ENABLEGROUPVIEW, !isGroundEnable, NULL); //EnableGroupView(!isGroundEnable); } else if (cmd == columnCount + 1) RemoveUnusedProps(); else if (cmd == columnCount + 2) { temp.LoadString(IDS_CONFIRMRESETCOLUMNORDER); if (MessageBox(pWnd->m_hWnd, temp, L"TortoiseGit", MB_YESNO | MB_ICONQUESTION) == IDYES) ResetColumns(m_dwDefaultColumns); } } }