diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
index 67b1a5825..b9d1fba24 100644
--- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
@@ -30,7 +30,7 @@ namespace Terminal.Gui {
IClipboard clipboard;
int [,,] contents;
- internal override int [,,] Contents => contents;
+ public override int [,,] Contents => contents;
// Current row, and current col, tracked by Move/AddRune only
int ccol, crow;
@@ -488,7 +488,7 @@ namespace Terminal.Gui {
Flags = mouseFlag
};
- var view = Application.wantContinuousButtonPressedView;
+ var view = Application.WantContinuousButtonPressedView;
if (view == null)
break;
if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
index ffe45bc78..c865c24d6 100644
--- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
@@ -35,7 +35,7 @@ namespace Terminal.Gui {
///
/// Assists with testing, the format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
///
- internal override int [,,] Contents => contents;
+ public override int [,,] Contents => contents;
//void UpdateOffscreen ()
//{
diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
index 0a75db93a..9ef1b41c1 100644
--- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
@@ -947,7 +947,7 @@ namespace Terminal.Gui {
await Task.Delay (200);
while (isButtonPressed) {
await Task.Delay (100);
- var view = Application.wantContinuousButtonPressedView;
+ var view = Application.WantContinuousButtonPressedView;
if (view == null) {
break;
}
@@ -1187,7 +1187,7 @@ namespace Terminal.Gui {
public NetWinVTConsole NetWinConsole { get; }
public bool IsWinPlatform { get; }
public override IClipboard Clipboard { get; }
- internal override int [,,] Contents => contents;
+ public override int [,,] Contents => contents;
int largestWindowHeight;
diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
index a185ec027..0a233c8a3 100644
--- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
@@ -732,7 +732,7 @@ namespace Terminal.Gui {
public override int Top => top;
public override bool HeightAsBuffer { get; set; }
public override IClipboard Clipboard => clipboard;
- internal override int [,,] Contents => contents;
+ public override int [,,] Contents => contents;
public WindowsConsole WinConsole { get; private set; }
@@ -1181,7 +1181,7 @@ namespace Terminal.Gui {
Flags = mouseFlag
};
- var view = Application.wantContinuousButtonPressedView;
+ var view = Application.WantContinuousButtonPressedView;
if (view == null) {
break;
}
diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs
index e87f615ac..d79704308 100644
--- a/Terminal.Gui/Core/Application.cs
+++ b/Terminal.Gui/Core/Application.cs
@@ -105,6 +105,11 @@ namespace Terminal.Gui {
/// The current.
public static Toplevel Current { get; private set; }
+ ///
+ /// The current object that wants continuous mouse button pressed events.
+ ///
+ public static View WantContinuousButtonPressedView { get; private set; }
+
///
/// The current used in the terminal.
///
@@ -210,6 +215,24 @@ namespace Terminal.Gui {
///
public static bool IsMouseDisabled { get; set; }
+ ///
+ /// Set to true to cause the RunLoop method to exit after the first iterations.
+ /// Set to false (the default) to cause the RunLoop to continue running until Application.RequestStop() is called.
+ ///
+ public static bool ExitRunLoopAfterFirstIteration { get; set; } = false;
+
+ ///
+ /// Notify that a new token was created,
+ /// used if is true.
+ ///
+ public static event Action NotifyNewRunState;
+
+ ///
+ /// Notify that a existent token is stopping,
+ /// used if is true.
+ ///
+ public static event Action NotifyStopRunState;
+
///
/// This event is raised on each iteration of the
///
@@ -297,12 +320,12 @@ namespace Terminal.Gui {
}
// Used only for start debugging on Unix.
-//#if DEBUG
-// while (!System.Diagnostics.Debugger.IsAttached) {
-// System.Threading.Thread.Sleep (100);
-// }
-// System.Diagnostics.Debugger.Break ();
-//#endif
+ //#if DEBUG
+ // while (!System.Diagnostics.Debugger.IsAttached) {
+ // System.Threading.Thread.Sleep (100);
+ // }
+ // System.Diagnostics.Debugger.Break ();
+ //#endif
// Reset all class variables (Application is a singleton).
ResetState ();
@@ -352,7 +375,10 @@ namespace Terminal.Gui {
{
Toplevel = view;
}
- internal Toplevel Toplevel;
+ ///
+ /// The belong to this .
+ ///
+ public Toplevel Toplevel { get; internal set; }
///
/// Releases alTop = l resource used by the object.
@@ -385,7 +411,7 @@ namespace Terminal.Gui {
static void ProcessKeyEvent (KeyEvent ke)
{
- if(RootKeyEvent?.Invoke(ke) ?? false) {
+ if (RootKeyEvent?.Invoke (ke) ?? false) {
return;
}
@@ -580,9 +606,8 @@ namespace Terminal.Gui {
///
/// Return true to suppress the KeyPress event
///
- public static Func RootKeyEvent;
+ public static Func RootKeyEvent;
- internal static View wantContinuousButtonPressedView;
static View lastMouseOwnerView;
static void ProcessMouseEvent (MouseEvent me)
@@ -594,9 +619,9 @@ namespace Terminal.Gui {
var view = FindDeepestView (Current, me.X, me.Y, out int rx, out int ry);
if (view != null && view.WantContinuousButtonPressed)
- wantContinuousButtonPressedView = view;
+ WantContinuousButtonPressedView = view;
else
- wantContinuousButtonPressedView = null;
+ WantContinuousButtonPressedView = null;
if (view != null) {
me.View = view;
}
@@ -655,9 +680,9 @@ namespace Terminal.Gui {
return;
if (view.WantContinuousButtonPressed)
- wantContinuousButtonPressedView = view;
+ WantContinuousButtonPressedView = view;
else
- wantContinuousButtonPressedView = null;
+ WantContinuousButtonPressedView = null;
// Should we bubbled up the event, if it is not handled?
view.OnMouseEvent (nme);
@@ -952,51 +977,65 @@ namespace Terminal.Gui {
bool firstIteration = true;
for (state.Toplevel.Running = true; state.Toplevel.Running;) {
- if (MainLoop.EventsPending (wait)) {
- // Notify Toplevel it's ready
- if (firstIteration) {
- state.Toplevel.OnReady ();
- }
- firstIteration = false;
-
- MainLoop.MainIteration ();
- Iteration?.Invoke ();
-
- EnsureModalOrVisibleAlwaysOnTop (state.Toplevel);
- if ((state.Toplevel != Current && Current?.Modal == true)
- || (state.Toplevel != Current && Current?.Modal == false)) {
- MdiTop?.OnDeactivate (state.Toplevel);
- state.Toplevel = Current;
- MdiTop?.OnActivate (state.Toplevel);
- Top.SetChildNeedsDisplay ();
- Refresh ();
- }
- if (Driver.EnsureCursorVisibility ()) {
- state.Toplevel.SetNeedsDisplay ();
- }
- } else if (!wait) {
+ if (ExitRunLoopAfterFirstIteration && !firstIteration)
return;
+ RunMainLoopIteration (ref state, wait, ref firstIteration);
+ }
+ }
+
+ ///
+ /// Run one iteration of the MainLoop.
+ ///
+ /// The state returned by the Begin method.
+ /// If will execute the runloop waiting for events.
+ /// If it's the first run loop iteration.
+ public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool firstIteration)
+ {
+ if (MainLoop.EventsPending (wait)) {
+ // Notify Toplevel it's ready
+ if (firstIteration) {
+ state.Toplevel.OnReady ();
}
- if (state.Toplevel != Top
- && (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
- Top.Redraw (Top.Bounds);
- state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
+
+ MainLoop.MainIteration ();
+ Iteration?.Invoke ();
+
+ EnsureModalOrVisibleAlwaysOnTop (state.Toplevel);
+ if ((state.Toplevel != Current && Current?.Modal == true)
+ || (state.Toplevel != Current && Current?.Modal == false)) {
+ MdiTop?.OnDeactivate (state.Toplevel);
+ state.Toplevel = Current;
+ MdiTop?.OnActivate (state.Toplevel);
+ Top.SetChildNeedsDisplay ();
+ Refresh ();
}
- if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.ChildNeedsDisplay || state.Toplevel.LayoutNeeded
- || MdiChildNeedsDisplay ()) {
- state.Toplevel.Redraw (state.Toplevel.Bounds);
- if (DebugDrawBounds) {
- DrawBounds (state.Toplevel);
- }
- state.Toplevel.PositionCursor ();
- Driver.Refresh ();
- } else {
- Driver.UpdateCursor ();
+ if (Driver.EnsureCursorVisibility ()) {
+ state.Toplevel.SetNeedsDisplay ();
}
- if (state.Toplevel != Top && !state.Toplevel.Modal
- && (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
- Top.Redraw (Top.Bounds);
+ } else if (!wait) {
+ return;
+ }
+ firstIteration = false;
+
+ if (state.Toplevel != Top
+ && (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
+ Top.Redraw (Top.Bounds);
+ state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
+ }
+ if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.ChildNeedsDisplay || state.Toplevel.LayoutNeeded
+ || MdiChildNeedsDisplay ()) {
+ state.Toplevel.Redraw (state.Toplevel.Bounds);
+ if (DebugDrawBounds) {
+ DrawBounds (state.Toplevel);
}
+ state.Toplevel.PositionCursor ();
+ Driver.Refresh ();
+ } else {
+ Driver.UpdateCursor ();
+ }
+ if (state.Toplevel != Top && !state.Toplevel.Modal
+ && (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
+ Top.Redraw (Top.Bounds);
}
}
@@ -1107,7 +1146,12 @@ namespace Terminal.Gui {
resume = false;
var runToken = Begin (view);
RunLoop (runToken);
- End (runToken);
+ if (!ExitRunLoopAfterFirstIteration)
+ End (runToken);
+ else
+ // If ExitRunLoopAfterFirstIteration is true then the user must deal his disposing when it ends
+ // by using NotifyStopRunState event.
+ NotifyNewRunState?.Invoke (runToken);
#if !DEBUG
}
catch (Exception error)
@@ -1190,6 +1234,8 @@ namespace Terminal.Gui {
return;
}
currentTop.Running = false;
+ if (ExitRunLoopAfterFirstIteration)
+ NotifyStopRunState?.Invoke (currentTop);
}
}
diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs
index 9a29ab064..616a85475 100644
--- a/Terminal.Gui/Core/ConsoleDriver.cs
+++ b/Terminal.Gui/Core/ConsoleDriver.cs
@@ -665,8 +665,10 @@ namespace Terminal.Gui {
///
public abstract bool HeightAsBuffer { get; set; }
- // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
- internal abstract int [,,] Contents { get; }
+ ///
+ /// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
+ ///
+ public virtual int [,,] Contents { get; }
///
/// Initializes the driver
diff --git a/Terminal.Gui/Core/MainLoop.cs b/Terminal.Gui/Core/MainLoop.cs
index 493e3be48..4988a5a8e 100644
--- a/Terminal.Gui/Core/MainLoop.cs
+++ b/Terminal.Gui/Core/MainLoop.cs
@@ -45,8 +45,17 @@ namespace Terminal.Gui {
/// does not seem to be a way of supporting this on Windows.
///
public class MainLoop {
- internal class Timeout {
+ ///
+ /// Provides data for timers running manipulation.
+ ///
+ public sealed class Timeout {
+ ///
+ /// Time to wait before invoke the callback.
+ ///
public TimeSpan Span;
+ ///
+ /// The function that will be invoked.
+ ///
public Func Callback;
}
@@ -54,12 +63,30 @@ namespace Terminal.Gui {
object timeoutsLockToken = new object ();
internal List> idleHandlers = new List> ();
+ ///
+ /// Gets the list of all timeouts sorted by the time ticks./>.
+ /// A shorter limit time can be added at the end, but it will be called before an
+ /// earlier addition that has a longer limit time.
+ ///
+ public SortedList Timeouts => timeouts;
+
+ ///
+ /// Gets the list of all idle handlers.
+ ///
+ public List> IdleHandlers => idleHandlers;
+
///
/// The current IMainLoopDriver in use.
///
/// The driver.
public IMainLoopDriver Driver { get; }
+ ///
+ /// Invoked when a new timeout is added to be used on the case
+ /// if is true,
+ ///
+ public event Action TimeoutAdded;
+
///
/// Creates a new Mainloop.
///
@@ -120,7 +147,8 @@ namespace Terminal.Gui {
{
lock (timeoutsLockToken) {
var k = (DateTime.UtcNow + time).Ticks;
- timeouts.Add (NudgeToUniqueKey(k), timeout);
+ timeouts.Add (NudgeToUniqueKey (k), timeout);
+ TimeoutAdded?.Invoke (k);
}
}
@@ -188,11 +216,10 @@ namespace Terminal.Gui {
AddTimeout (timeout.Span, timeout);
} else {
lock (timeoutsLockToken) {
- timeouts.Add (NudgeToUniqueKey(k), timeout);
+ timeouts.Add (NudgeToUniqueKey (k), timeout);
}
}
}
-
}
///
@@ -203,7 +230,7 @@ namespace Terminal.Gui {
///
private long NudgeToUniqueKey (long k)
{
- lock(timeoutsLockToken) {
+ lock (timeoutsLockToken) {
while (timeouts.ContainsKey (k)) {
k++;
}
diff --git a/UnitTests/ApplicationTests.cs b/UnitTests/ApplicationTests.cs
index e48b4aec8..9f70b66aa 100644
--- a/UnitTests/ApplicationTests.cs
+++ b/UnitTests/ApplicationTests.cs
@@ -1144,7 +1144,7 @@ namespace Terminal.Gui.Core {
var rs = Application.Begin (Application.Top);
Assert.Equal (Application.Top, rs.Toplevel);
Assert.Null (Application.mouseGrabView);
- Assert.Null (Application.wantContinuousButtonPressedView);
+ Assert.Null (Application.WantContinuousButtonPressedView);
Assert.False (Application.DebugDrawBounds);
Assert.False (Application.ShowChild (Application.Top));
Application.End (Application.Top);