From 06383a4742363acd1bade56617effb8985adc28f Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 24 May 2020 19:18:28 -0600 Subject: [PATCH] added two demos --- Terminal.Gui/Core.cs | 6 + UICatalog/Scenarios/Progress.cs | 237 ++++++++++++++++++++++---------- 2 files changed, 167 insertions(+), 76 deletions(-) diff --git a/Terminal.Gui/Core.cs b/Terminal.Gui/Core.cs index 3e0032e14..c6568fb3e 100644 --- a/Terminal.Gui/Core.cs +++ b/Terminal.Gui/Core.cs @@ -2421,6 +2421,10 @@ namespace Terminal.Gui { /// public static void Shutdown () { + // Shutdown is the bookend for Init. As such it needs to clean up all resources + // Init created. Apps that do any threading will need to code defensively for this. + // e.g. see Issue #537 + // TODO: Some of this state is actually related to Begin/End (not Init/Shutdown) and should be moved to `RunState` (#520) foreach (var t in toplevels) { t.Running = false; } @@ -2428,6 +2432,8 @@ namespace Terminal.Gui { Current = null; CurrentView = null; Top = null; + + // MainLoop = null; Driver.End (); diff --git a/UICatalog/Scenarios/Progress.cs b/UICatalog/Scenarios/Progress.cs index 223053939..a62456ce0 100644 --- a/UICatalog/Scenarios/Progress.cs +++ b/UICatalog/Scenarios/Progress.cs @@ -1,6 +1,8 @@ -using System; +using NStack; +using System; using System.Threading; using Terminal.Gui; +using System.Linq; namespace UICatalog { // @@ -11,99 +13,182 @@ namespace UICatalog { [ScenarioCategory ("Threading")] class Progress : Scenario { - private ProgressBar _activityProgressBar; - private ProgressBar _pulseProgressBar; - private Timer _timer; - private object _timeoutToken = null; + class ProgressDemo : FrameView, IDisposable { + internal ProgressBar ActivityProgressBar { get; private set; } + internal ProgressBar PulseProgressBar { get; private set; } + bool _disposedValue; + const int _verticalSpace = 1; + internal Action StartBtnClick; + internal Action StopBtnClick; + internal Action PulseBtnClick; + + internal ProgressDemo (ustring title) : base (title) + { + ColorScheme = Colors.Dialog; + + var leftFrame = new FrameView ("Settings") { + X = 0, + Y = 0, + Height = Dim.Percent (100), + Width = Dim.Percent (30) + }; + Add (leftFrame); + + var startButton = new Button ("Start Timer") { + X = Pos.Right (leftFrame) + 1, + Y = 0, + Clicked = () => StartBtnClick?.Invoke () + }; + var pulseButton = new Button ("Pulse") { + X = Pos.Right (startButton) + 2, + Y = Pos.Y (startButton), + Clicked = () => PulseBtnClick.Invoke () + }; + var stopbutton = new Button ("Stop Timer") { + X = Pos.Right (pulseButton) + 2, + Y = Pos.Top (pulseButton), + Clicked = () => StopBtnClick.Invoke () + }; + + Add (startButton); + Add (pulseButton); + Add (stopbutton); + + ActivityProgressBar = new ProgressBar () { + X = Pos.Right (leftFrame) + 1, + Y = Pos.Bottom (startButton) + 1, + Width = Dim.Fill (), + Height = 1, + Fraction = 0.25F, + ColorScheme = Colors.Error + }; + Add (ActivityProgressBar); + + PulseProgressBar = new ProgressBar () { + X = Pos.Right (leftFrame) + 1, + Y = Pos.Bottom (ActivityProgressBar) + 1, + Width = Dim.Fill (), + Height = 1, + ColorScheme = Colors.Error + }; + Add (PulseProgressBar); + + // Set height to height of controls + spacing + frame + Height = 2 + _verticalSpace + Dim.Height (startButton) + _verticalSpace + Dim.Height (ActivityProgressBar) + _verticalSpace + Dim.Height (PulseProgressBar) + _verticalSpace; + + } + + protected virtual void Dispose (bool disposing) + { + if (!_disposedValue) { + if (disposing) { + + } + _disposedValue = true; + } + } + + public void Dispose () + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose (disposing: true); + GC.SuppressFinalize (this); + } + } + + private Timer _systemTimer = null; + private int _systemTimerTick = 100; // ms + private object _mainLoopTimeout = null; + private int _mainLooopTimeoutTick = 1000; // ms public override void Setup () { - var pulseButton = new Button ("Pulse") { - X = Pos.Center (), - Y = Pos.Center () - 3, - Clicked = () => Pulse () + // Demo #1 - Use System.Timer (and threading) + var systemTimerDemo = new ProgressDemo ("System.Timer (threads)") { + X = 0, + Y = 0, + Width = Dim.Percent (100), + }; + systemTimerDemo.StartBtnClick = () => { + _systemTimer?.Dispose (); + _systemTimer = null; + + systemTimerDemo.ActivityProgressBar.Fraction = 0F; + systemTimerDemo.PulseProgressBar.Fraction = 0F; + + _systemTimer = new Timer ((o) => { + // Note the check for Mainloop being valid. System.Timers can run after they are Disposed. + // This code must be defensive for that. + Application.MainLoop?.Invoke (() => systemTimerDemo.PulseBtnClick ()); + }, null, 0, _systemTimerTick); }; - var startButton = new Button ("Start Timer") { - Y = Pos.Y(pulseButton), - Clicked = () => Start () + systemTimerDemo.PulseBtnClick = () => { + if (systemTimerDemo.ActivityProgressBar.Fraction + 0.01F >= 1) { + systemTimerDemo.ActivityProgressBar.Fraction = 0F; + } else { + systemTimerDemo.ActivityProgressBar.Fraction += 0.01F; + } + systemTimerDemo.PulseProgressBar.Pulse (); + }; + systemTimerDemo.StopBtnClick = () => { + _systemTimer?.Dispose (); + _systemTimer = null; + + systemTimerDemo.ActivityProgressBar.Fraction = 1F; + systemTimerDemo.PulseProgressBar.Fraction = 1F; }; - var stopbutton = new Button ("Stop Timer") { - Y = Pos.Y (pulseButton), - Clicked = () => Stop() + + Win.Add (systemTimerDemo); + + // Demo #2 - Use Application.MainLoop.AddTimeout (no threads) + var mainLoopTimeoutDemo = new ProgressDemo ("Application.AddTimer (no threads)") { + X = 0, + Y = Pos.Bottom (systemTimerDemo), + Width = Dim.Percent (100), }; + mainLoopTimeoutDemo.StartBtnClick = () => { + mainLoopTimeoutDemo.StopBtnClick (); - // Center three buttons with 5 spaces between them - // TODO: Use Pos.Width instead of (Right-Left) when implemented (#502) - startButton.X = Pos.Left (pulseButton) - (Pos.Right (startButton) - Pos.Left (startButton)) - 5; - stopbutton.X = Pos.Right (pulseButton) + 5; + mainLoopTimeoutDemo.ActivityProgressBar.Fraction = 0F; + mainLoopTimeoutDemo.PulseProgressBar.Fraction = 0F; - Win.Add (startButton); - Win.Add (pulseButton); - Win.Add (stopbutton); - - _activityProgressBar = new ProgressBar () { - X = Pos.Center (), - // BUGBUG: If you remove the +1 below the control is drawn at top?!?! - Y = Pos.Center ()+1, - Width = 30, - Fraction = 0.25F, + Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (_mainLooopTimeoutTick), (loop) => { + mainLoopTimeoutDemo?.PulseBtnClick (); + return true; + }); }; - Win.Add (_activityProgressBar); - - _pulseProgressBar = new ProgressBar () { - X = Pos.Center (), - // BUGBUG: If you remove the +1 below the control is drawn at top?!?! - Y = Pos.Center () + 3, - Width = 30, + mainLoopTimeoutDemo.PulseBtnClick = () => { + if (mainLoopTimeoutDemo.ActivityProgressBar.Fraction + 0.01F >= 1) { + mainLoopTimeoutDemo.ActivityProgressBar.Fraction = 0F; + } else { + mainLoopTimeoutDemo.ActivityProgressBar.Fraction += 0.01F; + } + mainLoopTimeoutDemo.PulseProgressBar.Pulse (); }; - Win.Add (_pulseProgressBar); + mainLoopTimeoutDemo.StopBtnClick = () => { + if (_mainLoopTimeout != null) { + Application.MainLoop.RemoveTimeout (_mainLoopTimeout); + _mainLoopTimeout = null; + } + + mainLoopTimeoutDemo.ActivityProgressBar.Fraction = 1F; + mainLoopTimeoutDemo.PulseProgressBar.Fraction = 1F; + }; + Win.Add (mainLoopTimeoutDemo); + } protected override void Dispose (bool disposing) { - _timer?.Dispose (); - _timer = null; - if (_timeoutToken != null) { - Application.MainLoop.RemoveTimeout (_timeoutToken); + Win.GetEnumerator ().Reset (); + while (Win.GetEnumerator ().MoveNext ()) { + var cur = (ProgressDemo)Win.GetEnumerator ().Current; + cur?.StopBtnClick (); + cur.Dispose (); } base.Dispose (disposing); } - - private void Pulse () - { - if (_activityProgressBar.Fraction + 0.01F >= 1) { - _activityProgressBar.Fraction = 0F; - } else { - _activityProgressBar.Fraction += 0.01F; - } - _pulseProgressBar.Pulse (); - } - - private void Start () - { - _timer?.Dispose (); - _timer = null; - - _activityProgressBar.Fraction = 0F; - _pulseProgressBar.Fraction = 0F; - - _timer = new Timer ((o) => { - Application.MainLoop.Invoke (() => Pulse ()); - }, null, 0, 20); - } - - private void Stop () - { - _timer?.Dispose (); - _timer = null; - if (_timeoutToken != null) { - Application.MainLoop.RemoveTimeout (_timeoutToken); - } - - _activityProgressBar.Fraction = 1F; - _pulseProgressBar.Fraction = 1F; - } } } \ No newline at end of file