Skip to content

Commit

Permalink
fix #31 allow messages to be written above the console (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mpdreamz authored Aug 20, 2019
1 parent 6577f5b commit c332337
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ protected override void Start()
BackgroundColor = ConsoleColor.DarkGray,
BackgroundCharacter = '\u2593'
};
var wait = TimeSpan.FromSeconds(25);
var wait = TimeSpan.FromSeconds(5);
using (var pbar = new FixedDurationBar(wait, "", options))
{
var t = new Thread(()=> LongRunningTask(pbar));
Expand Down
54 changes: 54 additions & 0 deletions src/ShellProgressBar.Example/Examples/PersistMessageExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Threading;

namespace ShellProgressBar.Example.Examples
{
public class PersistMessageExample : ExampleBase
{
protected override void Start()
{
var options = new ProgressBarOptions
{
ForegroundColor = ConsoleColor.Yellow,
ForegroundColorDone = ConsoleColor.DarkGreen,
BackgroundColor = ConsoleColor.DarkGray,
BackgroundCharacter = '\u2593',
WriteQueuedMessage = message => {
if (message.StartsWith("Report 500"))
{
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine("Add an extra message, because why not");

Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(message);
return 2; //signal to the progressbar we wrote two messages
}
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(message);
return 1;
}
};
var wait = TimeSpan.FromSeconds(5);
using (var pbar = new FixedDurationBar(wait, "", options))
{
var t = new Thread(()=> LongRunningTask(pbar));
t.Start();

if (!pbar.CompletedHandle.WaitOne(wait))
Console.Error.WriteLine($"{nameof(FixedDurationBar)} did not signal {nameof(FixedDurationBar.CompletedHandle)} after {wait}");

}
}

private static void LongRunningTask(FixedDurationBar bar)
{
for (var i = 0; i < 1_000_000; i++)
{
bar.Message = $"{i} events";
if (bar.IsCompleted) break;
if (i % 500 == 0) bar.WriteLine($"Report {i} to console above the progressbar");
Thread.Sleep(1);
}
}
}
}
7 changes: 4 additions & 3 deletions src/ShellProgressBar.Example/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Program
{
private static readonly IList<IProgressBarExample> TestCases = new List<IProgressBarExample>
{
new PersistMessageExample(),
new FixedDurationExample(),
new DeeplyNestedProgressBarTreeExample(),
new NestedProgressBarPerStepProgress(),
Expand Down Expand Up @@ -91,14 +92,14 @@ private static async Task RunExample(CancellationToken token, int nth)

private static async Task RunTestCases(CancellationToken token)
{
var i = 0;
foreach (var example in TestCases)
{
Console.Clear(); //not necessary but for demo/recording purposes.
if (i > 0) Console.Clear(); //not necessary but for demo/recording purposes.
await example.Start(token);
i++;
}
Console.Write("Shown all examples!");

Console.ReadLine();
}

public static void BusyWait(int milliseconds)
Expand Down
7 changes: 6 additions & 1 deletion src/ShellProgressBar/ChildProgressBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ namespace ShellProgressBar
public class ChildProgressBar : ProgressBarBase, IProgressBar
{
private readonly Action _scheduleDraw;
private readonly Action<string> _writeLine;
private readonly Action<ProgressBarHeight> _growth;

public DateTime StartDate { get; } = DateTime.Now;

protected override void DisplayProgress() => _scheduleDraw?.Invoke();

internal ChildProgressBar(int maxTicks, string message, Action scheduleDraw, ProgressBarOptions options = null, Action<ProgressBarHeight> growth = null)
internal ChildProgressBar(int maxTicks, string message, Action scheduleDraw, Action<string> writeLine,
ProgressBarOptions options = null, Action<ProgressBarHeight> growth = null)
: base(maxTicks, message, options)
{
_scheduleDraw = scheduleDraw;
_writeLine = writeLine;
_growth = growth;
_growth?.Invoke(ProgressBarHeight.Increment);
}
Expand All @@ -41,6 +44,8 @@ protected override void OnDone()
}
}

public override void WriteLine(string message) => _writeLine(message);

public void Dispose()
{
OnDone();
Expand Down
3 changes: 3 additions & 0 deletions src/ShellProgressBar/IProgressBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public interface IProgressBar : IDisposable

ConsoleColor ForeGroundColor { get; }

/// <summary> This writes a new line above the progress bar on the console where <see cref="Message"/> updates the message inside the progress bar</summary>
void WriteLine(string message);

IProgress<T> AsProgress<T>(Func<T, string> message = null, Func<T, double?> percentage = null);
}
}
50 changes: 41 additions & 9 deletions src/ShellProgressBar/ProgressBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public class ProgressBar : ProgressBarBase, IProgressBar
private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

private readonly ConsoleColor _originalColor;
private readonly int _originalCursorTop;
private readonly Func<string, int> _writeMessageToConsole;
private int _originalCursorTop;
private readonly int _originalWindowTop;
private int _isDisposed;

Expand All @@ -34,6 +35,8 @@ public ProgressBar(int maxTicks, string message, ProgressBarOptions options = nu
_originalWindowTop = Console.WindowTop;
_originalColor = Console.ForegroundColor;

_writeMessageToConsole = this.Options.WriteQueuedMessage ?? DefaultConsoleWrite;

if (!Console.IsOutputRedirected)
Console.CursorVisible = false;

Expand Down Expand Up @@ -68,10 +71,7 @@ public ProgressBar(int maxTicks, string message, ProgressBarOptions options = nu
});
}

protected virtual void OnTimerTick()
{
DisplayProgress();
}
protected virtual void OnTimerTick() => DisplayProgress();

protected override void Grow(ProgressBarHeight direction)
{
Expand Down Expand Up @@ -182,9 +182,14 @@ private static void DrawTopHalfPrefix(Indentation[] indentation, int depth)
Console.ForegroundColor = indentation[depth - 1].ConsoleColor;
}

protected override void DisplayProgress()
protected override void DisplayProgress() => _displayProgressEvent.Set();

private readonly ConcurrentQueue<string> _stickyMessages = new ConcurrentQueue<string>();

public override void WriteLine(string message)
{
_displayProgressEvent.Set();
_stickyMessages.Enqueue(message);
DisplayProgress();
}

private void UpdateProgress()
Expand All @@ -194,8 +199,12 @@ private void UpdateProgress()
if (this.Options.EnableTaskBarProgress)
TaskbarProgress.SetValue(mainPercentage, 100);

if (Console.IsOutputRedirected)
return;
// write queued console messages, displayprogress is signaled straight after but
// just in case make sure we never write more then 5 in a display progress tick
for (var i = 0; i < 5 && _stickyMessages.TryDequeue(out var m); i++)
WriteConsoleLine(m);

if (Console.IsOutputRedirected) return;

Console.CursorVisible = false;

Expand Down Expand Up @@ -241,6 +250,25 @@ void TopHalf()
_timer = null;
}

private void WriteConsoleLine(string m)
{
var resetString = new string(' ', Console.WindowWidth);
Console.Write(resetString);
Console.Write("\r");
var foreground = Console.ForegroundColor;
var background = Console.BackgroundColor;
var written = _writeMessageToConsole(m);
Console.ForegroundColor = foreground;
Console.BackgroundColor = background;
_originalCursorTop += written;
}

private static int DefaultConsoleWrite(string message)
{
Console.WriteLine(message);
return 1;
}

private static void ResetToBottom(ref int cursorTop)
{
var resetString = new string(' ', Console.WindowWidth);
Expand Down Expand Up @@ -325,6 +353,10 @@ public void Dispose()
// of System.Console
UpdateProgress();

//make sure we pop all pending messages
while (_stickyMessages.TryDequeue(out var m))
WriteConsoleLine(m);

if (this.EndTime == null) this.EndTime = DateTime.Now;
var openDescendantsPadding = (_visibleDescendants * 2);

Expand Down
5 changes: 4 additions & 1 deletion src/ShellProgressBar/ProgressBarBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,15 @@ public double Percentage

public ChildProgressBar Spawn(int maxTicks, string message, ProgressBarOptions options = null)
{
var pbar = new ChildProgressBar(maxTicks, message, DisplayProgress, options, this.Grow);
var pbar = new ChildProgressBar(maxTicks, message, DisplayProgress, WriteLine, options, this.Grow);
this.Children.Add(pbar);
DisplayProgress();
return pbar;
}

public abstract void WriteLine(string message);


public void Tick(string message = null)
{
Interlocked.Increment(ref _currentTick);
Expand Down
7 changes: 7 additions & 0 deletions src/ShellProgressBar/ProgressBarOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,12 @@ public bool EnableTaskBarProgress
_enableTaskBarProgress = value;
}
}

/// <summary>
/// Take ownership of writing a message that is intended to be displayed above the progressbar.
/// The delegate is expected to return the number of messages written to the console as a result of the string argument.
/// <para>Usescase: pretty print or change the console colors, the progressbar will reset back</para>
/// </summary>
public Func<string, int> WriteQueuedMessage { get; set; }
}
}

0 comments on commit c332337

Please sign in to comment.