// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using osu.Framework.Graphics; namespace osu.Game.Screens.Edit { /// /// A component that tracks a batch change, only applying after all active changes are completed. /// public abstract class TransactionalCommitComponent : Component { /// /// Fires whenever a transaction begins. Will not fire on nested transactions. /// public event Action TransactionBegan; /// /// Fires when the last transaction completes. /// public event Action TransactionEnded; /// /// Fires when is called and results in a non-transactional state save. /// public event Action SaveStateTriggered; public bool TransactionActive => bulkChangesStarted > 0; private int bulkChangesStarted; /// /// Signal the beginning of a change. /// public void BeginChange() { if (bulkChangesStarted++ == 0) TransactionBegan?.Invoke(); } /// /// Signal the end of a change. /// /// Throws if was not first called. public void EndChange() { if (bulkChangesStarted == 0) throw new InvalidOperationException($"Cannot call {nameof(EndChange)} without a previous call to {nameof(BeginChange)}."); if (--bulkChangesStarted == 0) { UpdateState(); TransactionEnded?.Invoke(); } } /// /// Force an update of the state with no attached transaction. /// This is a no-op if a transaction is already active. Should generally be used as a safety measure to ensure granular changes are not left outside a transaction. /// public void SaveState() { if (bulkChangesStarted > 0) return; SaveStateTriggered?.Invoke(); UpdateState(); } protected abstract void UpdateState(); } }