Browse Source

Refresh works, Dialogs close

Miguel de Icaza 7 years ago
parent
commit
db534ed1c1
8 changed files with 95 additions and 31 deletions
  1. 74 25
      Core.cs
  2. 3 0
      Driver.cs
  3. 4 0
      TODO.md
  4. 1 1
      Views/Button.cs
  5. 1 1
      Views/Checkbox.cs
  6. 10 0
      Views/Dialog.cs
  7. 1 1
      Views/Menu.cs
  8. 1 3
      Views/RadioGroup.cs

+ 74 - 25
Core.cs

@@ -16,6 +16,7 @@ using System;
 using System.Collections;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading;
+using System.Linq;
 
 
 namespace Terminal {
 namespace Terminal {
 
 
@@ -66,6 +67,12 @@ namespace Terminal {
 		///     keystroke will be passed using the ProcessColdKey
 		///     keystroke will be passed using the ProcessColdKey
 		///     method to other views to process.
 		///     method to other views to process.
 		///   </para>
 		///   </para>
+		///   <para>
+		///     The View implementation does nothing but return false,
+		///     so it is not necessary to call base.ProcessKey if you 
+		///     derive directly from View, but you should if you derive
+		///     other View subclasses.
+		///   </para>
 		/// </remarks>
 		/// </remarks>
 		public virtual bool ProcessKey (KeyEvent kb)
 		public virtual bool ProcessKey (KeyEvent kb)
 		{
 		{
@@ -439,6 +446,12 @@ namespace Terminal {
 			Driver.AddCh (ch);
 			Driver.AddCh (ch);
 		}
 		}
 
 
+		protected void ClearNeedsDisplay ()
+		{
+			NeedDisplay = Rect.Empty;
+			childNeedsDisplay = false;
+		}
+
 		/// <summary>
 		/// <summary>
 		/// Performs a redraw of this view and its subviews, only redraws the views that have been flagged for a re-display.
 		/// Performs a redraw of this view and its subviews, only redraws the views that have been flagged for a re-display.
 		/// </summary>
 		/// </summary>
@@ -462,8 +475,7 @@ namespace Terminal {
 					}
 					}
 				}
 				}
 			}
 			}
-			NeedDisplay = Rect.Empty;
-			childNeedsDisplay = false;
+			ClearNeedsDisplay ();
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
@@ -669,6 +681,12 @@ namespace Terminal {
 	/// <summary>
 	/// <summary>
 	/// Toplevel views can be modally executed.
 	/// Toplevel views can be modally executed.
 	/// </summary>
 	/// </summary>
+	/// <remarks>
+	///   <para>
+	///     Toplevels can be modally executing views, and they return control
+	///     to the caller when the "Running" property is set to false.
+	///   </para>
+	/// </remarks>
 	public class Toplevel : View {
 	public class Toplevel : View {
 		public bool Running;
 		public bool Running;
 
 
@@ -708,7 +726,7 @@ namespace Terminal {
 				}
 				}
 				return true;
 				return true;
 			case Key.BackTab:
 			case Key.BackTab:
-			old = Focused;
+				old = Focused;
 				if (!FocusPrev ())
 				if (!FocusPrev ())
 					FocusPrev ();
 					FocusPrev ();
 				if (old != Focused) {
 				if (old != Focused) {
@@ -717,26 +735,15 @@ namespace Terminal {
 				}
 				}
 				return true;
 				return true;
 			case Key.ControlL:
 			case Key.ControlL:
-				SetNeedsDisplay();
+				Application.Refresh ();
 				return true;
 				return true;
 			}
 			}
 			return false;
 			return false;
 		}
 		}
-
-#if false
-        public override void Redraw ()
-        {
-            base.Redraw ();
-            for (int i = 0; i < Driver.Cols; i++) {
-                Driver.Move (0, i);
-                Driver.AddStr ("Line: " + i);
-            }
-        }
-#endif
 	}
 	}
 
 
 	/// <summary>
 	/// <summary>
-	/// A toplevel view that draws a frame around its region
+	/// A toplevel view that draws a frame around its region and has a "ContentView" subview where the contents are added.
 	/// </summary>
 	/// </summary>
 	public class Window : Toplevel, IEnumerable {
 	public class Window : Toplevel, IEnumerable {
 		View contentView;
 		View contentView;
@@ -754,6 +761,11 @@ namespace Terminal {
 			public ContentView (Rect frame) : base (frame) { }
 			public ContentView (Rect frame) : base (frame) { }
 		}
 		}
 
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Window"/> class with an optioanl title
+		/// </summary>
+		/// <param name="frame">Frame.</param>
+		/// <param name="title">Title.</param>
 		public Window (Rect frame, string title = null) : base (frame)
 		public Window (Rect frame, string title = null) : base (frame)
 		{
 		{
 			this.Title = title;
 			this.Title = title;
@@ -762,6 +774,10 @@ namespace Terminal {
 			base.Add (contentView);
 			base.Add (contentView);
 		}
 		}
 
 
+		/// <summary>
+		/// Enumerates the various views in the ContentView.
+		/// </summary>
+		/// <returns>The enumerator.</returns>
 		public new IEnumerator GetEnumerator ()
 		public new IEnumerator GetEnumerator ()
 		{
 		{
 			return contentView.GetEnumerator ();
 			return contentView.GetEnumerator ();
@@ -772,6 +788,10 @@ namespace Terminal {
 			DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), true);
 			DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), true);
 		}
 		}
 
 
+		/// <summary>
+		/// Add the specified view to the ContentView.
+		/// </summary>
+		/// <param name="view">View to add to the window.</param>
 		public override void Add (View view)
 		public override void Add (View view)
 		{
 		{
 			contentView.Add (view);
 			contentView.Add (view);
@@ -795,9 +815,27 @@ namespace Terminal {
 				Driver.SetAttribute (Colors.Dialog.Normal);
 				Driver.SetAttribute (Colors.Dialog.Normal);
 			}
 			}
 			contentView.Redraw (contentView.Bounds);
 			contentView.Redraw (contentView.Bounds);
+			ClearNeedsDisplay ();
 		}
 		}
 	}
 	}
 
 
+	/// <summary>
+	/// The application driver for gui.cs
+	/// </summary>
+	/// <remarks>
+	///   <para>
+	///     You can hook up to the Iteration event to have your method 
+	///     invoked on each iteration of the mainloop.
+	///   </para>
+	///   <para>
+	///     Creates a mainloop to process input events, handle timers and
+	///     other sources of data.   It is accessible via the MainLoop property.
+	///   </para>
+	///   <para>
+	///     When invoked sets the SynchronizationContext to one that is tied
+	///     to the mainloop, allowing user code to use async/await.
+	///   </para>
+	/// </remarks>
 	public class Application {
 	public class Application {
 		public static ConsoleDriver Driver = new CursesDriver ();
 		public static ConsoleDriver Driver = new CursesDriver ();
 		public static Toplevel Top { get; private set; }
 		public static Toplevel Top { get; private set; }
@@ -805,7 +843,6 @@ namespace Terminal {
 		public static Mono.Terminal.MainLoop MainLoop { get; private set; }
 		public static Mono.Terminal.MainLoop MainLoop { get; private set; }
 
 
 		static Stack<View> toplevels = new Stack<View> ();
 		static Stack<View> toplevels = new Stack<View> ();
-		static Responder focus;
 
 
 		/// <summary>
 		/// <summary>
 		///   This event is raised on each iteration of the
 		///   This event is raised on each iteration of the
@@ -826,6 +863,10 @@ namespace Terminal {
 			return new Rect (new Point ((Driver.Cols - size.Width) / 2, (Driver.Rows - size.Height) / 2), size);
 			return new Rect (new Point ((Driver.Cols - size.Width) / 2, (Driver.Rows - size.Height) / 2), size);
 		}
 		}
 
 
+		//
+		// provides the sync context set while executing code in gui.cs, to let
+		// users use async/await on their code
+		//
 		class MainLoopSyncContext : SynchronizationContext {
 		class MainLoopSyncContext : SynchronizationContext {
 			Mono.Terminal.MainLoop mainLoop;
 			Mono.Terminal.MainLoop mainLoop;
 
 
@@ -868,7 +909,6 @@ namespace Terminal {
 			SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
 			SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
 			Top = Toplevel.Create ();
 			Top = Toplevel.Create ();
 			Current = Top;
 			Current = Top;
-			focus = Top;
 		}
 		}
 
 
 		public class RunState : IDisposable {
 		public class RunState : IDisposable {
@@ -906,7 +946,6 @@ namespace Terminal {
 				return;
 				return;
 		}
 		}
 
 
-
 		static public RunState Begin (Toplevel toplevel)
 		static public RunState Begin (Toplevel toplevel)
 		{
 		{
 			if (toplevel == null)
 			if (toplevel == null)
@@ -930,6 +969,7 @@ namespace Terminal {
 		{
 		{
 			if (rs == null)
 			if (rs == null)
 				throw new ArgumentNullException (nameof (rs));
 				throw new ArgumentNullException (nameof (rs));
+
 			rs.Dispose ();
 			rs.Dispose ();
 		}
 		}
 
 
@@ -950,16 +990,19 @@ namespace Terminal {
 			Driver.Refresh ();
 			Driver.Refresh ();
 		}
 		}
 
 
+		/// <summary>
+		/// Triggers a refresh of the entire display.
+		/// </summary>
 		public static void Refresh ()
 		public static void Refresh ()
 		{
 		{
 			Driver.RedrawTop ();
 			Driver.RedrawTop ();
 			View last = null;
 			View last = null;
-			foreach (var v in toplevels) {
+			foreach (var v in toplevels.Reverse ()) {
+				v.SetNeedsDisplay ();
 				v.Redraw (v.Bounds);
 				v.Redraw (v.Bounds);
 				last = v;
 				last = v;
 			}
 			}
-			if (last != null)
-				last.PositionCursor ();
+			last?.PositionCursor ();
 			Driver.Refresh ();
 			Driver.Refresh ();
 		}
 		}
 
 
@@ -1014,9 +1057,15 @@ namespace Terminal {
 		///   Runs the main loop on the given container.
 		///   Runs the main loop on the given container.
 		/// </summary>
 		/// </summary>
 		/// <remarks>
 		/// <remarks>
-		///   This method is used to start processing events
-		///   for the main application, but it is also used to
-		///   run modal dialog boxes.
+		///   <para>
+		///     This method is used to start processing events
+		///     for the main application, but it is also used to
+		///     run modal dialog boxes.
+		///   </para>
+		///   <para>
+		///     To make a toplevel stop execution, set the "Running"
+		///     property to false.
+		///   </para>
 		/// </remarks>
 		/// </remarks>
 		public static void Run (Toplevel view)
 		public static void Run (Toplevel view)
 		{
 		{

+ 3 - 0
Driver.cs

@@ -124,6 +124,7 @@ namespace Terminal {
 			}
 			}
 		}
 		}
 
 
+		static bool sync;
 		public override void AddCh (int ch)
 		public override void AddCh (int ch)
 		{
 		{
 			if (Clip.Contains (ccol, crow)) {
 			if (Clip.Contains (ccol, crow)) {
@@ -134,6 +135,8 @@ namespace Terminal {
 				Curses.addch (ch);
 				Curses.addch (ch);
 			} else
 			} else
 				needMove = true;
 				needMove = true;
+			if (sync)
+				Application.Driver.Refresh ();
 			ccol++;
 			ccol++;
 		}
 		}
 
 

+ 4 - 0
TODO.md

@@ -33,6 +33,10 @@ Widgets should not use Colors.Base or Colors.Dialog, they should likely use
 the colors defined in the toplevel container, so that the Dialog vs Toplevel
 the colors defined in the toplevel container, so that the Dialog vs Toplevel
 colors are set there only.
 colors are set there only.
 
 
+## Focus
+
+Use left/right/up/down to switch focus as well when nothing handles the event
+
 ## Views
 ## Views
 
 
 Checkbox, ListView, Menu.
 Checkbox, ListView, Menu.

+ 1 - 1
Views/Button.cs

@@ -168,7 +168,7 @@ namespace Terminal {
 					Clicked (this, EventArgs.Empty);
 					Clicked (this, EventArgs.Empty);
 				return true;
 				return true;
 			}
 			}
-			return false;
+			return base.ProcessKey (kb);
 		}
 		}
 
 
 #if false
 #if false

+ 1 - 1
Views/Checkbox.cs

@@ -110,7 +110,7 @@ namespace Terminal {
 				SetNeedsDisplay ();
 				SetNeedsDisplay ();
 				return true;
 				return true;
 			}
 			}
-			return false;
+			return base.ProcessKey (kb);
 		}
 		}
 
 
 #if false
 #if false

+ 10 - 0
Views/Dialog.cs

@@ -47,5 +47,15 @@ namespace Terminal {
 				start += bf.Width + 1;
 				start += bf.Width + 1;
 			}
 			}
 		}
 		}
+
+		public override bool ProcessKey (KeyEvent kb)
+		{
+			switch (kb.Key) {
+			case Key.Esc:
+				Running = false;
+				return true;
+			}
+			return base.ProcessKey (kb);
+		}
 	}
 	}
 }
 }

+ 1 - 1
Views/Menu.cs

@@ -190,7 +190,7 @@ namespace Terminal {
 				}
 				}
 				break;
 				break;
 			}
 			}
-			return false;
+			return base.ProcessKey (kb);
 		}
 		}
 	}
 	}
 
 

+ 1 - 3
Views/RadioGroup.cs

@@ -134,10 +134,8 @@ namespace Terminal {
 			case Key.Space:
 			case Key.Space:
 				Selected = cursor;
 				Selected = cursor;
 				return true;
 				return true;
-			default:
-				
-				return false;
 			}
 			}
+			return base.ProcessKey (kb);
 		}
 		}
 	}
 	}
 }
 }