Jelajahi Sumber

added two demos

Charlie Kindel 5 tahun lalu
induk
melakukan
06383a4742
2 mengubah file dengan 166 tambahan dan 75 penghapusan
  1. 6 0
      Terminal.Gui/Core.cs
  2. 160 75
      UICatalog/Scenarios/Progress.cs

+ 6 - 0
Terminal.Gui/Core.cs

@@ -2421,6 +2421,10 @@ namespace Terminal.Gui {
 		/// </summary>
 		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 ();

+ 160 - 75
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),
 			};
-
-			var startButton = new Button ("Start Timer") {
-				Y = Pos.Y(pulseButton),
-				Clicked = () => Start ()
+			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 stopbutton = new Button ("Stop Timer") {
-				Y = Pos.Y (pulseButton),
-				Clicked = () => Stop()
+			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;
 
-			// 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;
-
-			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,
+				systemTimerDemo.ActivityProgressBar.Fraction = 1F;
+				systemTimerDemo.PulseProgressBar.Fraction = 1F;
 			};
-			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,
-			};
-			Win.Add (_pulseProgressBar);
-		}
 
-		protected override void Dispose (bool disposing)
-		{
-			_timer?.Dispose ();
-			_timer = null;
-			if (_timeoutToken != null) {
-				Application.MainLoop.RemoveTimeout (_timeoutToken);
-			}
-			base.Dispose (disposing);
-		}
+			Win.Add (systemTimerDemo);
 
-		private void Pulse ()
-		{
-			if (_activityProgressBar.Fraction + 0.01F >= 1) {
-				_activityProgressBar.Fraction = 0F;
-			} else {
-				_activityProgressBar.Fraction += 0.01F;
-			}
-			_pulseProgressBar.Pulse ();
-		}
+			// 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 ();
 
-		private void Start ()
-		{
-			_timer?.Dispose ();
-			_timer = null;
+				mainLoopTimeoutDemo.ActivityProgressBar.Fraction = 0F;
+				mainLoopTimeoutDemo.PulseProgressBar.Fraction = 0F;
 
-			_activityProgressBar.Fraction = 0F;
-			_pulseProgressBar.Fraction = 0F;
+				Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (_mainLooopTimeoutTick), (loop) => {
+					mainLoopTimeoutDemo?.PulseBtnClick ();
+					return true;
+				});
+			};
+			mainLoopTimeoutDemo.PulseBtnClick = () => {
+				if (mainLoopTimeoutDemo.ActivityProgressBar.Fraction + 0.01F >= 1) {
+					mainLoopTimeoutDemo.ActivityProgressBar.Fraction = 0F;
+				} else {
+					mainLoopTimeoutDemo.ActivityProgressBar.Fraction += 0.01F;
+				}
+				mainLoopTimeoutDemo.PulseProgressBar.Pulse ();
+			};
+			mainLoopTimeoutDemo.StopBtnClick = () => {
+				if (_mainLoopTimeout != null) {
+					Application.MainLoop.RemoveTimeout (_mainLoopTimeout);
+					_mainLoopTimeout = null;
+				}
+
+				mainLoopTimeoutDemo.ActivityProgressBar.Fraction = 1F;
+				mainLoopTimeoutDemo.PulseProgressBar.Fraction = 1F;
+			};
+			Win.Add (mainLoopTimeoutDemo);
 
-			_timer = new Timer ((o) => {
-				Application.MainLoop.Invoke (() => Pulse ());
-			}, null, 0, 20);
 		}
 
-		private void Stop ()
+		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 ();
 			}
-
-			_activityProgressBar.Fraction = 1F;
-			_pulseProgressBar.Fraction = 1F;
+			base.Dispose (disposing);
 		}
 	}
 }