/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2021. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
* between Ultraleap and you, your company or other organization. *
******************************************************************************/
#if UNITY_EDITOR
using UnityEditor;
#endif
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Leap.Unity
{
///
/// This interface describes a generic way to update the progress of an action.
///
public interface IProgressView
{
///
/// Clears the progress view. Is called if the action has been completed
/// or canceled.
///
void Clear();
///
/// Updates the progress view with some title text, information, and a
/// progress percentage that ranges from 0 to 1.
///
void DisplayProgress(string title, string info, float progress);
}
#if UNITY_EDITOR
///
/// An example progress view that uses the simple EditorUtility methods to
/// provide a developer with progress of an editor action.
///
public class EditorProgressView : IProgressView
{
///
/// Gets a reference to a singleton instance of the EditorProgressView.
/// This is safe because EditorProgressView is stateless.
///
public static readonly EditorProgressView Single = new EditorProgressView();
public void Clear()
{
EditorUtility.ClearProgressBar();
}
public void DisplayProgress(string title, string info, float progress)
{
EditorUtility.DisplayProgressBar(title, info, progress);
}
}
#endif
///
/// This class allows you to easily give feedback of an action as
/// it completes.
///
/// The progress bar is represented as a single 'Chunk' that is made
/// of a certain number of sections. The progress bar is hierarchical,
/// and so each section can itself be another chunk.
///
public class ProgressBar
{
private List chunks = new List();
private List progress = new List();
private List titleStrings = new List();
private List infoStrings = new List();
private Stopwatch stopwatch = new Stopwatch();
private bool _forceUpdate;
private IProgressView _view;
#if UNITY_EDITOR
///
/// Constructs a new progress bar given a default EditorProgressView.
/// You can use this constructor whenever you want to give progress
/// feedback to a Unity developer about an editor action that might
/// take some time to complete.
///
public ProgressBar() : this(EditorProgressView.Single) { }
#endif
///
/// Constructs a new progress bar given a progress view object
/// that will display the progress information for this progress
/// bar.
///
public ProgressBar(IProgressView view)
{
_view = view;
}
///
/// Begins a new chunk. If this call is made from within a chunk it
/// will generate a sub-chunk that represents a single step in the
/// parent chunk.
///
/// You must specify the number of sections this chunk contains.
/// All title and info strings will be concatenated together when
/// the progress bar is displayed.
///
/// You must specify a delegate that represents the action performed
/// by this chunk. This delegate is allowed to call both Begin and
/// StepProgress to progress through its work.
///
public void Begin(int sections, string title, string info, Action action)
{
if (!stopwatch.IsRunning)
{
stopwatch.Reset();
stopwatch.Start();
}
chunks.Add(sections);
progress.Add(0);
titleStrings.Add(title);
infoStrings.Add(info);
try
{
_forceUpdate = true;
action();
}
finally
{
int lastIndex = chunks.Count - 1;
chunks.RemoveAt(lastIndex);
progress.RemoveAt(lastIndex);
titleStrings.RemoveAt(lastIndex);
infoStrings.RemoveAt(lastIndex);
lastIndex--;
if (lastIndex >= 0)
{
progress[lastIndex]++;
}
if (chunks.Count == 0)
{
_view.Clear();
stopwatch.Stop();
}
}
}
///
/// Steps through one section of the current chunk. You can provide
/// an optional info string that will be concatenated to the current
/// info string before progress is displayed.
///
public void Step(string infoString = "")
{
progress[progress.Count - 1]++;
if (stopwatch.ElapsedMilliseconds > 17 || _forceUpdate)
{
displayBar(infoString);
stopwatch.Reset();
stopwatch.Start();
}
}
private void displayBar(string info = "")
{
_forceUpdate = false;
float percent = 0.0f;
float fraction = 1.0f;
string titleString = "";
string infoString = "";
for (int i = 0; i < chunks.Count; i++)
{
float chunkSize = chunks[i];
float chunkProgress = progress[i];
percent += fraction * (chunkProgress / chunkSize);
fraction /= chunkSize;
titleString += titleStrings[i];
infoString += infoStrings[i];
}
infoString += info;
_view.DisplayProgress(titleString, infoString, percent);
}
}
}