Editor/Window/ProgressBarStepComponent.cs (119 lines of code) (raw):
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine.UIElements;
using static AmazonGameLift.Editor.StatusBox;
using UnityEngine;
namespace AmazonGameLift.Editor
{
public abstract class ProgressBarStepComponent : StatefulInput
{
private VisualElement _progressBarElement;
private VerticalProgressBar _progressBar;
private StatusBox _statusBox;
protected readonly VisualElement _container;
protected readonly StateManager _stateManager;
protected ProgressBarStepComponent _nextStep = null;
protected ProgressBarStepComponent _prevStep = null;
protected bool HasNextStep => _nextStep != null;
protected bool HasPrevStep => _prevStep != null;
protected VerticalProgressState ProgressState => _progressBar.State;
public FlowProgress Progress => ProgressFlowContainer.ConvertVerticalProgresToFlowProgress(_progressBar.State);
/**
* Starts or resumes running the step's processing to reach Completed status.
* If the step was already in progress, it should resume processing.
* If the step was already completed, it should call 'CompleteStep' as if it just completed.
* If the step had hit an exception, it should call 'EncounteredException' as if it just encountered the exception.
*/
protected abstract Task StartOrResumeStep();
/**
* Clear all state associated with the step such that on reload the step would be considered NotStarted.
*/
protected abstract void ResetStep();
protected override void UpdateGUI() { }
public ProgressBarStepComponent(VisualElement container, StateManager stateManager, string uxmlPath)
{
_container = container;
_stateManager = stateManager;
InitializeUxml(uxmlPath);
}
public void SetNextStep(ProgressBarStepComponent nextStep)
{
_nextStep = nextStep;
_progressBar.Set(_progressBar.State, HasNextStep);
}
public void SetPrevStep(ProgressBarStepComponent prevStep)
{
_prevStep = prevStep;
}
public void TryStart()
{
_statusBox.Close();
if (Progress == FlowProgress.Completed)
{
throw new InvalidOperationException($"Unexpected progress state {ProgressState} for starting a step.");
}
_progressBar.Set(VerticalProgressState.InProgress);
StartOrResumeStep();
}
/**
* Recursively resets state of this step and it's following steps
*/
public void Reset()
{
_statusBox.Close();
ResetStep();
if (HasNextStep)
{
_nextStep.Reset();
}
_progressBar.Set(VerticalProgressState.NotStarted);
}
/**
* Calls Reset() and then TryStart()
*/
public void ResetAndTryStart()
{
Reset();
TryStart();
}
/**
* Modifies a Completed step to InProgress and resets its next steps
*/
protected void EditCompleted()
{
if (Progress != FlowProgress.Completed)
{
throw new InvalidOperationException($"Unexpected progress state {ProgressState} for editing completed step.");
}
_statusBox.Close();
_nextStep?.Reset();
_progressBar.Set(VerticalProgressState.InProgress);
}
/**
* Updates the progress to completed and starts the next step.
* Should only be called by the step when it has finished processing or on reloading state.
* It's expected the state is either InProgress or NotStarted (when resuming).
*/
protected void CompleteStep()
{
if (Progress == FlowProgress.Completed || ProgressState == VerticalProgressState.InProgressError)
{
throw new InvalidOperationException($"Unexpected progress state {ProgressState} for completing a step.");
}
_progressBar.Set(_progressBar.State == VerticalProgressState.InProgress
? VerticalProgressState.Completed
: VerticalProgressState.CompletedWarning);
if (HasNextStep)
{
_nextStep.TryStart();
}
}
/**
* Displays the encountered exception in the step's StatusBox and marks the step as in progress with a warning or error.
* Choosing Warning and Error for the status box type determines which in progress state is used.
* Can't be called for any status type other than warning or error.
* It's expected the state is either InProgress or NotStarted (when resuming).
*/
protected void EncounteredException(StatusBoxType statusBoxType, string text, string additionalText = null,
string externalButtonLink = null, string externalButtonText = null,
StatusBoxExternalTargetType externalTargetType = StatusBoxExternalTargetType.Link)
{
if (statusBoxType != StatusBoxType.Error && statusBoxType != StatusBoxType.Warning)
{
throw new InvalidOperationException($"{statusBoxType} is not a supported status box type to encounter as an exception.");
}
_statusBox.Show(statusBoxType, text, additionalText, externalButtonLink, externalButtonText, externalTargetType);
// Throwing exception after displaying the status so that the status box can support investigating what went wrong.
if (Progress == FlowProgress.Completed)
{
throw new InvalidOperationException($"Unexpected progress state {ProgressState} for encountering an exception.");
}
_progressBar.Set(statusBoxType == StatusBoxType.Error
? VerticalProgressState.InProgressError
: VerticalProgressState.InProgressWarning);
}
private void InitializeUxml(string uxmlPath)
{
var mVisualTreeAsset = UnityEngine.Resources.Load<VisualTreeAsset>(uxmlPath);
var uxml = mVisualTreeAsset.Instantiate();
_container.Add(uxml);
_progressBar = _container.Q<VerticalProgressBar>();
_statusBox = _container.Q<StatusBox>();
// Initialize state of UXML
_progressBar?.Set(VerticalProgressState.NotStarted, false);
_statusBox.Close();
}
}
}