diff --git a/Core.cs b/Core.cs
index 58b725b9b..1714588a6 100644
--- a/Core.cs
+++ b/Core.cs
@@ -16,6 +16,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
+using System.Linq;
namespace Terminal {
@@ -66,6 +67,12 @@ namespace Terminal {
/// keystroke will be passed using the ProcessColdKey
/// method to other views to process.
///
+ ///
+ /// 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.
+ ///
///
public virtual bool ProcessKey (KeyEvent kb)
{
@@ -439,6 +446,12 @@ namespace Terminal {
Driver.AddCh (ch);
}
+ protected void ClearNeedsDisplay ()
+ {
+ NeedDisplay = Rect.Empty;
+ childNeedsDisplay = false;
+ }
+
///
/// Performs a redraw of this view and its subviews, only redraws the views that have been flagged for a re-display.
///
@@ -462,8 +475,7 @@ namespace Terminal {
}
}
}
- NeedDisplay = Rect.Empty;
- childNeedsDisplay = false;
+ ClearNeedsDisplay ();
}
///
@@ -669,6 +681,12 @@ namespace Terminal {
///
/// Toplevel views can be modally executed.
///
+ ///
+ ///
+ /// Toplevels can be modally executing views, and they return control
+ /// to the caller when the "Running" property is set to false.
+ ///
+ ///
public class Toplevel : View {
public bool Running;
@@ -708,7 +726,7 @@ namespace Terminal {
}
return true;
case Key.BackTab:
- old = Focused;
+ old = Focused;
if (!FocusPrev ())
FocusPrev ();
if (old != Focused) {
@@ -717,26 +735,15 @@ namespace Terminal {
}
return true;
case Key.ControlL:
- SetNeedsDisplay();
+ Application.Refresh ();
return true;
}
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
}
///
- /// 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.
///
public class Window : Toplevel, IEnumerable {
View contentView;
@@ -754,6 +761,11 @@ namespace Terminal {
public ContentView (Rect frame) : base (frame) { }
}
+ ///
+ /// Initializes a new instance of the class with an optioanl title
+ ///
+ /// Frame.
+ /// Title.
public Window (Rect frame, string title = null) : base (frame)
{
this.Title = title;
@@ -762,6 +774,10 @@ namespace Terminal {
base.Add (contentView);
}
+ ///
+ /// Enumerates the various views in the ContentView.
+ ///
+ /// The enumerator.
public new IEnumerator GetEnumerator ()
{
return contentView.GetEnumerator ();
@@ -772,6 +788,10 @@ namespace Terminal {
DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), true);
}
+ ///
+ /// Add the specified view to the ContentView.
+ ///
+ /// View to add to the window.
public override void Add (View view)
{
contentView.Add (view);
@@ -795,9 +815,27 @@ namespace Terminal {
Driver.SetAttribute (Colors.Dialog.Normal);
}
contentView.Redraw (contentView.Bounds);
+ ClearNeedsDisplay ();
}
}
+ ///
+ /// The application driver for gui.cs
+ ///
+ ///
+ ///
+ /// You can hook up to the Iteration event to have your method
+ /// invoked on each iteration of the mainloop.
+ ///
+ ///
+ /// Creates a mainloop to process input events, handle timers and
+ /// other sources of data. It is accessible via the MainLoop property.
+ ///
+ ///
+ /// When invoked sets the SynchronizationContext to one that is tied
+ /// to the mainloop, allowing user code to use async/await.
+ ///
+ ///
public class Application {
public static ConsoleDriver Driver = new CursesDriver ();
public static Toplevel Top { get; private set; }
@@ -805,7 +843,6 @@ namespace Terminal {
public static Mono.Terminal.MainLoop MainLoop { get; private set; }
static Stack toplevels = new Stack ();
- static Responder focus;
///
/// 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);
}
+ //
+ // provides the sync context set while executing code in gui.cs, to let
+ // users use async/await on their code
+ //
class MainLoopSyncContext : SynchronizationContext {
Mono.Terminal.MainLoop mainLoop;
@@ -868,7 +909,6 @@ namespace Terminal {
SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
Top = Toplevel.Create ();
Current = Top;
- focus = Top;
}
public class RunState : IDisposable {
@@ -906,7 +946,6 @@ namespace Terminal {
return;
}
-
static public RunState Begin (Toplevel toplevel)
{
if (toplevel == null)
@@ -930,6 +969,7 @@ namespace Terminal {
{
if (rs == null)
throw new ArgumentNullException (nameof (rs));
+
rs.Dispose ();
}
@@ -950,16 +990,19 @@ namespace Terminal {
Driver.Refresh ();
}
+ ///
+ /// Triggers a refresh of the entire display.
+ ///
public static void Refresh ()
{
Driver.RedrawTop ();
View last = null;
- foreach (var v in toplevels) {
+ foreach (var v in toplevels.Reverse ()) {
+ v.SetNeedsDisplay ();
v.Redraw (v.Bounds);
last = v;
}
- if (last != null)
- last.PositionCursor ();
+ last?.PositionCursor ();
Driver.Refresh ();
}
@@ -1014,9 +1057,15 @@ namespace Terminal {
/// Runs the main loop on the given container.
///
///
- /// This method is used to start processing events
- /// for the main application, but it is also used to
- /// run modal dialog boxes.
+ ///
+ /// This method is used to start processing events
+ /// for the main application, but it is also used to
+ /// run modal dialog boxes.
+ ///
+ ///
+ /// To make a toplevel stop execution, set the "Running"
+ /// property to false.
+ ///
///
public static void Run (Toplevel view)
{
diff --git a/Driver.cs b/Driver.cs
index f8f707f2d..b392c796a 100644
--- a/Driver.cs
+++ b/Driver.cs
@@ -124,6 +124,7 @@ namespace Terminal {
}
}
+ static bool sync;
public override void AddCh (int ch)
{
if (Clip.Contains (ccol, crow)) {
@@ -134,6 +135,8 @@ namespace Terminal {
Curses.addch (ch);
} else
needMove = true;
+ if (sync)
+ Application.Driver.Refresh ();
ccol++;
}
diff --git a/TODO.md b/TODO.md
index 6a24cd323..b9e3303ad 100644
--- a/TODO.md
+++ b/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
colors are set there only.
+## Focus
+
+Use left/right/up/down to switch focus as well when nothing handles the event
+
## Views
Checkbox, ListView, Menu.
diff --git a/Views/Button.cs b/Views/Button.cs
index 3bcf522b0..6a302fc50 100644
--- a/Views/Button.cs
+++ b/Views/Button.cs
@@ -168,7 +168,7 @@ namespace Terminal {
Clicked (this, EventArgs.Empty);
return true;
}
- return false;
+ return base.ProcessKey (kb);
}
#if false
diff --git a/Views/Checkbox.cs b/Views/Checkbox.cs
index da1f7adb7..e95598bac 100644
--- a/Views/Checkbox.cs
+++ b/Views/Checkbox.cs
@@ -110,7 +110,7 @@ namespace Terminal {
SetNeedsDisplay ();
return true;
}
- return false;
+ return base.ProcessKey (kb);
}
#if false
diff --git a/Views/Dialog.cs b/Views/Dialog.cs
index 47b9973e5..3a50325ee 100644
--- a/Views/Dialog.cs
+++ b/Views/Dialog.cs
@@ -47,5 +47,15 @@ namespace Terminal {
start += bf.Width + 1;
}
}
+
+ public override bool ProcessKey (KeyEvent kb)
+ {
+ switch (kb.Key) {
+ case Key.Esc:
+ Running = false;
+ return true;
+ }
+ return base.ProcessKey (kb);
+ }
}
}
diff --git a/Views/Menu.cs b/Views/Menu.cs
index 57f9fe9c3..3c4286046 100644
--- a/Views/Menu.cs
+++ b/Views/Menu.cs
@@ -190,7 +190,7 @@ namespace Terminal {
}
break;
}
- return false;
+ return base.ProcessKey (kb);
}
}
diff --git a/Views/RadioGroup.cs b/Views/RadioGroup.cs
index be138eb89..60c32e50b 100644
--- a/Views/RadioGroup.cs
+++ b/Views/RadioGroup.cs
@@ -134,10 +134,8 @@ namespace Terminal {
case Key.Space:
Selected = cursor;
return true;
- default:
-
- return false;
}
+ return base.ProcessKey (kb);
}
}
}