using Spectre.Console; using SPT_AKI_Installer.Aki.Core.Interfaces; using System; using System.Text; using System.Threading; using System.Threading.Tasks; namespace SPT_AKI_Installer.Aki.Core.Model { public abstract class LiveTableTask : ILiveTaskTableEntry, IProgressableTask, IDisposable { /// /// The name that will be displayed in th first column of the table /// public string TaskName { get; set; } /// /// Wheather the task reports progress or not /// public bool IsIndeterminate; /// /// The progress (percent completed) of the task /// public int Progress { get; set; } /// /// The row index in the table of the task /// public int RowIndex { get; set; } private bool _continueRendering = false; private int _indeterminateState = 0; private string _currentStatus = "running"; private Table _table { get; set; } private LiveDisplayContext _context { get; set; } public LiveTableTask(string name, bool isIndeterminate = true) { TaskName = name; IsIndeterminate = isIndeterminate; } private string GetIndetermminateStatus() { string status = $"[blue]{_currentStatus.EscapeMarkup()} "; if (_indeterminateState > 3) _indeterminateState = 0; status += new string('.', _indeterminateState); status += "[/]"; _indeterminateState++; return status; } /// /// Start indeterminate progress spinner /// /// this doesn't need to be called if you set isIndeterminate in the constructor. You need to set IsIndeterminate to false to stop this background task public void StartDrawingIndeterminateProgress() { _continueRendering = true; new Task(new Action(() => { RenderIndeterminateProgress(ref _continueRendering); })).Start(); } public void StartDrawingProgress() { _continueRendering = true; new Task(new Action(() => { RenderProgress(ref _continueRendering); })).Start(); } private void ReRenderEntry(string message) { _table.RemoveRow(RowIndex); _table.InsertRow(RowIndex, TaskName, message); _context.Refresh(); } private void RenderIndeterminateProgress(ref bool continueRendering) { while (continueRendering) { ReRenderEntry(GetIndetermminateStatus()); Thread.Sleep(300); } } private void RenderProgress(ref bool continueRendering) { while (continueRendering) { string progressBar = new string(' ', 10); int progressFill = (int)Math.Floor((double)Progress / 10); progressBar = progressBar.Remove(0, progressFill).Insert(0, new string('=', progressFill)); progressBar = $"[blue][[{progressBar}]][/] {Progress}% {_currentStatus}"; ReRenderEntry(progressBar); Thread.Sleep(300); } } /// /// Set the context and table for this task /// /// /// /// This is called by when it is ran. No need to call it yourself public void SetContext(LiveDisplayContext context, Table table) { _context = context; _table = table; } /// /// Set the status text for the task /// /// The message to show /// Stop rendering progress updates (progress and indeterminate progress tasks) /// If you are running an indeterminate task, set render to false. It will render at the next indeterminate update interval public void SetStatus(string message, bool stopRendering = true) { _currentStatus = message; if (stopRendering) { _continueRendering = false; ReRenderEntry(message); } } /// /// Run the task async /// /// Returns the result of the task public abstract Task RunAsync(); public void Dispose() { IsIndeterminate = false; } } }