diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2dc59e0df3..3d57c73d30 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -16,9 +16,6 @@
-
-
-
diff --git a/Examples/UICatalog/Scenarios/Threading.cs b/Examples/UICatalog/Scenarios/Threading.cs
index 1adde0320f..dc97292b20 100644
--- a/Examples/UICatalog/Scenarios/Threading.cs
+++ b/Examples/UICatalog/Scenarios/Threading.cs
@@ -19,6 +19,16 @@ public class Threading : Scenario
private ListView _logJob;
private Action _sync;
+ private LogarithmicTimeout _logarithmicTimeout;
+ private NumericUpDown _numberLog;
+ private Button _btnLogarithmic;
+ private object _timeoutObj;
+
+ private SmoothAcceleratingTimeout _smoothTimeout;
+ private NumericUpDown _numberSmooth;
+ private Button _btnSmooth;
+ private object _timeoutObjSmooth;
+
public override void Main ()
{
Application.Init ();
@@ -82,6 +92,35 @@ public override void Main ()
var text = new TextField { X = 1, Y = 3, Width = 100, Text = "Type anything after press the button" };
+ _btnLogarithmic = new Button ()
+ {
+ X = 50,
+ Y = 4,
+ Text = "Start Log Counter"
+ };
+ _btnLogarithmic.Accepting += StartStopLogTimeout;
+
+ _numberLog = new NumericUpDown ()
+ {
+ X = Pos.Right (_btnLogarithmic),
+ Y = 4,
+ };
+
+ _btnSmooth = new Button ()
+ {
+ X = Pos.Right (_numberLog),
+ Y = 4,
+ Text = "Start Smooth Counter"
+ };
+ _btnSmooth.Accepting += StartStopSmoothTimeout;
+
+ _numberSmooth = new NumericUpDown ()
+ {
+ X = Pos.Right (_btnSmooth),
+ Y = 4,
+ };
+
+
var btnAction = new Button { X = 80, Y = 10, Text = "Load Data Action" };
btnAction.Accepting += (s, e) => _action.Invoke ();
var btnLambda = new Button { X = 80, Y = 12, Text = "Load Data Lambda" };
@@ -107,6 +146,10 @@ public override void Main ()
_btnActionCancel,
_logJob,
text,
+ _btnLogarithmic,
+ _numberLog,
+ _btnSmooth,
+ _numberSmooth,
btnAction,
btnLambda,
btnHandler,
@@ -129,6 +172,51 @@ void Win_Loaded (object sender, EventArgs args)
Application.Shutdown ();
}
+ private bool LogTimeout ()
+ {
+ _numberLog.Value++;
+ _logarithmicTimeout.AdvanceStage ();
+ return true;
+ }
+ private bool SmoothTimeout ()
+ {
+ _numberSmooth.Value++;
+ _smoothTimeout.AdvanceStage ();
+ return true;
+ }
+
+ private void StartStopLogTimeout (object sender, CommandEventArgs e)
+ {
+ if (_timeoutObj != null)
+ {
+ _btnLogarithmic.Text = "Start Log Counter";
+ Application.TimedEvents.Remove (_timeoutObj);
+ _timeoutObj = null;
+ }
+ else
+ {
+ _btnLogarithmic.Text = "Stop Log Counter";
+ _logarithmicTimeout = new LogarithmicTimeout (TimeSpan.FromMilliseconds (500), LogTimeout);
+ _timeoutObj = Application.TimedEvents.Add (_logarithmicTimeout);
+ }
+ }
+
+ private void StartStopSmoothTimeout (object sender, CommandEventArgs e)
+ {
+ if (_timeoutObjSmooth != null)
+ {
+ _btnSmooth.Text = "Start Smooth Counter";
+ Application.TimedEvents.Remove (_timeoutObjSmooth);
+ _timeoutObjSmooth = null;
+ }
+ else
+ {
+ _btnSmooth.Text = "Stop Smooth Counter";
+ _smoothTimeout = new SmoothAcceleratingTimeout (TimeSpan.FromMilliseconds (500), TimeSpan.FromMilliseconds (50), 0.5, SmoothTimeout);
+ _timeoutObjSmooth = Application.TimedEvents.Add (_smoothTimeout);
+ }
+ }
+
private async void CallLoadItemsAsync ()
{
_cancellationTokenSource = new CancellationTokenSource ();
diff --git a/Terminal.Gui/App/Application.Mouse.cs b/Terminal.Gui/App/Application.Mouse.cs
index 6e06ab759d..cab426f98a 100644
--- a/Terminal.Gui/App/Application.Mouse.cs
+++ b/Terminal.Gui/App/Application.Mouse.cs
@@ -19,122 +19,16 @@ public static partial class Application // Mouse handling
[ConfigurationProperty (Scope = typeof (SettingsScope))]
public static bool IsMouseDisabled { get; set; }
- /// Gets that has registered to get continuous mouse button pressed events.
- public static View? WantContinuousButtonPressedView { get; internal set; }
-
///
- /// Gets the view that grabbed the mouse (e.g. for dragging). When this is set, all mouse events will be routed to
- /// this view until the view calls or the mouse is released.
+ /// Static reference to the current .
///
- public static View? MouseGrabView { get; private set; }
-
- /// Invoked when a view wants to grab the mouse; can be canceled.
- public static event EventHandler? GrabbingMouse;
-
- /// Invoked when a view wants un-grab the mouse; can be canceled.
- public static event EventHandler? UnGrabbingMouse;
-
- /// Invoked after a view has grabbed the mouse.
- public static event EventHandler? GrabbedMouse;
-
- /// Invoked after a view has un-grabbed the mouse.
- public static event EventHandler? UnGrabbedMouse;
-
- ///
- /// Grabs the mouse, forcing all mouse events to be routed to the specified view until
- /// is called.
- ///
- /// View that will receive all mouse events until is invoked.
- public static void GrabMouse (View? view)
+ public static IMouseGrabHandler MouseGrabHandler
{
- if (view is null || RaiseGrabbingMouseEvent (view))
- {
- return;
- }
-
- RaiseGrabbedMouseEvent (view);
-
- if (Initialized)
- {
- // MouseGrabView is a static; only set if the application is initialized.
- MouseGrabView = view;
- }
+ get => ApplicationImpl.Instance.MouseGrabHandler;
+ set => ApplicationImpl.Instance.MouseGrabHandler = value ??
+ throw new ArgumentNullException(nameof(value));
}
- /// Releases the mouse grab, so mouse events will be routed to the view on which the mouse is.
- public static void UngrabMouse ()
- {
- if (MouseGrabView is null)
- {
- return;
- }
-
-#if DEBUG_IDISPOSABLE
- if (View.EnableDebugIDisposableAsserts)
- {
- ObjectDisposedException.ThrowIf (MouseGrabView.WasDisposed, MouseGrabView);
- }
-#endif
-
- if (!RaiseUnGrabbingMouseEvent (MouseGrabView))
- {
- View view = MouseGrabView;
- MouseGrabView = null;
- RaiseUnGrabbedMouseEvent (view);
- }
- }
-
- /// A delegate callback throws an exception.
- private static bool RaiseGrabbingMouseEvent (View? view)
- {
- if (view is null)
- {
- return false;
- }
-
- var evArgs = new GrabMouseEventArgs (view);
- GrabbingMouse?.Invoke (view, evArgs);
-
- return evArgs.Cancel;
- }
-
- /// A delegate callback throws an exception.
- private static bool RaiseUnGrabbingMouseEvent (View? view)
- {
- if (view is null)
- {
- return false;
- }
-
- var evArgs = new GrabMouseEventArgs (view);
- UnGrabbingMouse?.Invoke (view, evArgs);
-
- return evArgs.Cancel;
- }
-
- /// A delegate callback throws an exception.
- private static void RaiseGrabbedMouseEvent (View? view)
- {
- if (view is null)
- {
- return;
- }
-
- GrabbedMouse?.Invoke (view, new (view));
- }
-
- /// A delegate callback throws an exception.
- private static void RaiseUnGrabbedMouseEvent (View? view)
- {
- if (view is null)
- {
- return;
- }
-
- UnGrabbedMouse?.Invoke (view, new (view));
- }
-
-
///
/// INTERNAL API: Called when a mouse event is raised by the driver. Determines the view under the mouse and
/// calls the appropriate View mouse event handlers.
@@ -198,15 +92,6 @@ internal static void RaiseMouseEvent (MouseEventArgs mouseEvent)
return;
}
- if (Initialized)
- {
- WantContinuousButtonPressedView = deepestViewUnderMouse switch
- {
- { WantContinuousButtonPressed: true } => deepestViewUnderMouse,
- _ => null
- };
- }
-
// May be null before the prior condition or the condition may set it as null.
// So, the checking must be outside the prior condition.
if (deepestViewUnderMouse is null)
@@ -258,12 +143,7 @@ internal static void RaiseMouseEvent (MouseEventArgs mouseEvent)
RaiseMouseEnterLeaveEvents (viewMouseEvent.ScreenPosition, currentViewsUnderMouse);
- if (Initialized)
- {
- WantContinuousButtonPressedView = deepestViewUnderMouse.WantContinuousButtonPressed ? deepestViewUnderMouse : null;
- }
-
- while (deepestViewUnderMouse.NewMouseEvent (viewMouseEvent) is not true && MouseGrabView is not { })
+ while (deepestViewUnderMouse.NewMouseEvent (viewMouseEvent) is not true && MouseGrabHandler.MouseGrabView is not { })
{
if (deepestViewUnderMouse is Adornment adornmentView)
{
@@ -315,35 +195,35 @@ internal static void RaiseMouseEvent (MouseEventArgs mouseEvent)
internal static bool HandleMouseGrab (View? deepestViewUnderMouse, MouseEventArgs mouseEvent)
{
- if (MouseGrabView is { })
+ if (MouseGrabHandler.MouseGrabView is { })
{
#if DEBUG_IDISPOSABLE
- if (View.EnableDebugIDisposableAsserts && MouseGrabView.WasDisposed)
+ if (View.EnableDebugIDisposableAsserts && MouseGrabHandler.MouseGrabView.WasDisposed)
{
- throw new ObjectDisposedException (MouseGrabView.GetType ().FullName);
+ throw new ObjectDisposedException (MouseGrabHandler.MouseGrabView.GetType ().FullName);
}
#endif
// If the mouse is grabbed, send the event to the view that grabbed it.
// The coordinates are relative to the Bounds of the view that grabbed the mouse.
- Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.ScreenPosition);
+ Point frameLoc = MouseGrabHandler.MouseGrabView.ScreenToViewport (mouseEvent.ScreenPosition);
var viewRelativeMouseEvent = new MouseEventArgs
{
Position = frameLoc,
Flags = mouseEvent.Flags,
ScreenPosition = mouseEvent.ScreenPosition,
- View = deepestViewUnderMouse ?? MouseGrabView
+ View = deepestViewUnderMouse ?? MouseGrabHandler.MouseGrabView
};
//System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
- if (MouseGrabView?.NewMouseEvent (viewRelativeMouseEvent) is true)
+ if (MouseGrabHandler.MouseGrabView?.NewMouseEvent (viewRelativeMouseEvent) is true)
{
return true;
}
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
- if (MouseGrabView is null && deepestViewUnderMouse is Adornment)
+ if (MouseGrabHandler.MouseGrabView is null && deepestViewUnderMouse is Adornment)
{
// The view that grabbed the mouse has been disposed
return true;
diff --git a/Terminal.Gui/App/Application.Run.cs b/Terminal.Gui/App/Application.Run.cs
index d20336e5f8..30c1384a3e 100644
--- a/Terminal.Gui/App/Application.Run.cs
+++ b/Terminal.Gui/App/Application.Run.cs
@@ -89,10 +89,9 @@ public static RunState Begin (Toplevel toplevel)
//#endif
// Ensure the mouse is ungrabbed.
- if (MouseGrabView is { })
+ if (MouseGrabHandler.MouseGrabView is { })
{
- UngrabMouse ();
- MouseGrabView = null;
+ MouseGrabHandler.UngrabMouse ();
}
var rs = new RunState (toplevel);
@@ -366,7 +365,7 @@ public static T Run (Func? errorHandler = null, IConsoleDriv
/// Alternatively, to have a program control the main loop and process events manually, call
/// to set things up manually and then repeatedly call
/// with the wait parameter set to false. By doing this the
- /// method will only process any pending events, timers, idle handlers and then
+ /// method will only process any pending events, timers handlers and then
/// return control immediately.
///
///
diff --git a/Terminal.Gui/App/Application.Screen.cs b/Terminal.Gui/App/Application.Screen.cs
index bd57585eab..cc9dcb0b66 100644
--- a/Terminal.Gui/App/Application.Screen.cs
+++ b/Terminal.Gui/App/Application.Screen.cs
@@ -4,6 +4,7 @@ namespace Terminal.Gui.App;
public static partial class Application // Screen related stuff
{
+ private static readonly object _lockScreen = new ();
private static Rectangle? _screen;
///
@@ -18,11 +19,15 @@ public static Rectangle Screen
{
get
{
- if (_screen == null)
+ lock (_lockScreen)
{
- _screen = Driver?.Screen ?? new (new (0, 0), new (2048, 2048));
+ if (_screen == null)
+ {
+ _screen = Driver?.Screen ?? new (new (0, 0), new (2048, 2048));
+ }
+
+ return _screen.Value;
}
- return _screen.Value;
}
set
{
@@ -30,7 +35,11 @@ public static Rectangle Screen
{
throw new NotImplementedException ($"Screen locations other than 0, 0 are not yet supported");
}
- _screen = value;
+
+ lock (_lockScreen)
+ {
+ _screen = value;
+ }
}
}
diff --git a/Terminal.Gui/App/Application.cd b/Terminal.Gui/App/Application.cd
index 9c22dd77ba..294a90411b 100644
--- a/Terminal.Gui/App/Application.cd
+++ b/Terminal.Gui/App/Application.cd
@@ -1,90 +1,116 @@
-
+
- hEI4FAgAqARIspQfBQo0gTGiACNL0AICESJKoggBSg8=
- Application\Application.cs
+ gEK4FIgQOAQIuhQeBwoUgSCgAAJL0AACESIKoAiBWw8=
+ App\Application.cs
-
-
+
+
AABAAAAAAABCAAAAAAAAAAAAAAAAIgIAAAAAAAAAAAA=
- Application\ApplicationNavigation.cs
+ App\ApplicationNavigation.cs
-
-
+
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- Application\IterationEventArgs.cs
+ App\IterationEventArgs.cs
-
-
+
+
- CAAAIAAAASAAAQAQAAAAAIBADQAAEAAYIgIIwAAAAAI=
- Application\MainLoop.cs
+ AAAAAAAAACAAAAAAAAAAAAAACBAAEAAIIAIAgAAAEAI=
+ App\MainLoop.cs
-
-
+
+
AAAAAgAAAAAAAAAAAEAAAAAACAAAAAAAAAAAAAAAAAA=
- Application\MainLoopSyncContext.cs
+ App\MainLoopSyncContext.cs
-
-
+
+
AAAAAAAAACACAgAAAAAAAAAAAAAAAAACQAAAAAAAAAA=
- Application\RunState.cs
+ App\RunState.cs
-
-
+
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=
- Application\RunStateEventArgs.cs
+ App\RunStateEventArgs.cs
-
-
+
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAQAA=
- Application\Timeout.cs
+ App\Timeout.cs
-
-
+
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAACAIAAAAAAAAAAAA=
- Application\TimeoutEventArgs.cs
+ App\TimeoutEventArgs.cs
-
-
+
+
- AAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAQAACAACAAAI=
- Application\ApplicationImpl.cs
+ AABgAAAAIAAIAgQUAAAAAQAAAAAAAAAAQAAKgAAAEAI=
+ App\ApplicationImpl.cs
-
-
+
+
+
+ BAAgAAAAgABAAoAAAAAAABAAACEAAAAAAABAAgAAAAA=
+ App\MouseGrabHandler.cs
+
+
+
+
+
AAAAAAAACAAAAAQAAAAABAAAAAAAEAAAAAAAAAAAAAA=
- Application\MainLoop.cs
+ App\MainLoop.cs
+
+
+
+
+
+ AAAgAAAAAAAIAgQUAAAAAQAAAAAAAAAAAAAKgAAAEAI=
+ App\IApplication.cs
+
+
+
+
+
+
+
+
+
+ BAAgAAAAAAAAAgAAAAAAABAAACEAAAAAAAAAAgAAAAA=
+ App\IMouseGrabHandler.cs
-
-
+
+
- AAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAACAAAAAAI=
- Application\IApplication.cs
+ BAAAIAAAAQAAAAAQACAAAIBAAQAAAAAAAAAIgAAAAAA=
+ App\ITimedEvents.cs
diff --git a/Terminal.Gui/App/Application.cs b/Terminal.Gui/App/Application.cs
index 3b59b2e13b..7741b12b18 100644
--- a/Terminal.Gui/App/Application.cs
+++ b/Terminal.Gui/App/Application.cs
@@ -42,6 +42,15 @@ public static partial class Application
/// Gets all cultures supported by the application without the invariant language.
public static List? SupportedCultures { get; private set; } = GetSupportedCultures ();
+
+ ///
+ ///
+ /// Handles recurring events. These are invoked on the main UI thread - allowing for
+ /// safe updates to instances.
+ ///
+ ///
+ public static ITimedEvents? TimedEvents => ApplicationImpl.Instance?.TimedEvents;
+
///
/// Gets a string representation of the Application as rendered by .
///
@@ -221,7 +230,7 @@ internal static void ResetState (bool ignoreDisposed = false)
// Run State stuff
NotifyNewRunState = null;
NotifyStopRunState = null;
- MouseGrabView = null;
+ MouseGrabHandler = new MouseGrabHandler ();
Initialized = false;
// Mouse
@@ -229,12 +238,7 @@ internal static void ResetState (bool ignoreDisposed = false)
// last mouse pos.
//_lastMousePosition = null;
CachedViewsUnderMouse.Clear ();
- WantContinuousButtonPressedView = null;
MouseEvent = null;
- GrabbedMouse = null;
- UnGrabbingMouse = null;
- GrabbedMouse = null;
- UnGrabbedMouse = null;
// Keyboard
KeyDown = null;
@@ -252,10 +256,4 @@ internal static void ResetState (bool ignoreDisposed = false)
// (https://github.com/gui-cs/Terminal.Gui/issues/1084).
SynchronizationContext.SetSynchronizationContext (null);
}
-
- ///
- /// Adds specified idle handler function to main iteration processing. The handler function will be called
- /// once per iteration of the main loop after other events have been handled.
- ///
- public static void AddIdle (Func func) { ApplicationImpl.Instance.AddIdle (func); }
}
diff --git a/Terminal.Gui/App/ApplicationImpl.cs b/Terminal.Gui/App/ApplicationImpl.cs
index 111b9783dc..4eadc2ad61 100644
--- a/Terminal.Gui/App/ApplicationImpl.cs
+++ b/Terminal.Gui/App/ApplicationImpl.cs
@@ -18,6 +18,15 @@ public class ApplicationImpl : IApplication
///
public static IApplication Instance => _lazyInstance.Value;
+
+ ///
+ public virtual ITimedEvents? TimedEvents => Application.MainLoop?.TimedEvents;
+
+ ///
+ /// Handles which (if any) has captured the mouse
+ ///
+ public IMouseGrabHandler MouseGrabHandler { get; set; } = new MouseGrabHandler ();
+
///
/// Change the singleton implementation, should not be called except before application
/// startup. This method lets you provide alternative implementations of core static gateway
@@ -119,7 +128,7 @@ public virtual T Run (Func? errorHandler = null, IConsoleDri
/// Alternatively, to have a program control the main loop and process events manually, call
/// to set things up manually and then repeatedly call
/// with the wait parameter set to false. By doing this the
- /// method will only process any pending events, timers, idle handlers and then
+ /// method will only process any pending events, timers handlers and then
/// return control immediately.
///
/// When using or
@@ -261,7 +270,22 @@ public virtual void RequestStop (Toplevel? top)
///
public virtual void Invoke (Action action)
{
- Application.MainLoop?.AddIdle (
+
+ // If we are already on the main UI thread
+ if (Application.MainThreadId == Thread.CurrentThread.ManagedThreadId)
+ {
+ action ();
+ return;
+ }
+
+ if (Application.MainLoop == null)
+ {
+ Logging.Warning ("Ignored Invoke because MainLoop is not initialized yet");
+ return;
+ }
+
+
+ Application.AddTimeout (TimeSpan.Zero,
() =>
{
action ();
@@ -274,20 +298,6 @@ public virtual void Invoke (Action action)
///
public bool IsLegacy { get; protected set; } = true;
- ///
- public virtual void AddIdle (Func func)
- {
- if (Application.MainLoop is null)
- {
- throw new NotInitializedException ("Cannot add idle before main loop is initialized");
- }
-
- // Yes in this case we cannot go direct via TimedEvents because legacy main loop
- // has established behaviour to do other stuff too e.g. 'wake up'.
- Application.MainLoop.AddIdle (func);
-
- }
-
///
public virtual object AddTimeout (TimeSpan time, Func callback)
{
@@ -296,13 +306,13 @@ public virtual object AddTimeout (TimeSpan time, Func callback)
throw new NotInitializedException ("Cannot add timeout before main loop is initialized", null);
}
- return Application.MainLoop.TimedEvents.AddTimeout (time, callback);
+ return Application.MainLoop.TimedEvents.Add (time, callback);
}
///
public virtual bool RemoveTimeout (object token)
{
- return Application.MainLoop?.TimedEvents.RemoveTimeout (token) ?? false;
+ return Application.MainLoop?.TimedEvents.Remove (token) ?? false;
}
///
diff --git a/Terminal.Gui/App/IApplication.cs b/Terminal.Gui/App/IApplication.cs
index d8df8d5528..abc169bc94 100644
--- a/Terminal.Gui/App/IApplication.cs
+++ b/Terminal.Gui/App/IApplication.cs
@@ -9,6 +9,17 @@ namespace Terminal.Gui.App;
///
public interface IApplication
{
+ ///
+ /// Handles recurring events. These are invoked on the main UI thread - allowing for
+ /// safe updates to instances.
+ ///
+ ITimedEvents? TimedEvents { get; }
+
+ ///
+ /// Handles grabbing the mouse (only a single can grab the mouse at once).
+ ///
+ IMouseGrabHandler MouseGrabHandler { get; set; }
+
/// Initializes a new instance of Application.
/// Call this method once per instance (or after has been called).
///
@@ -106,7 +117,7 @@ public T Run (Func? errorHandler = null, IConsoleDriver? dri
/// Alternatively, to have a program control the main loop and process events manually, call
/// to set things up manually and then repeatedly call
/// with the wait parameter set to false. By doing this the
- /// method will only process any pending events, timers, idle handlers and then
+ /// method will only process any pending events, timers handlers and then
/// return control immediately.
///
/// When using or
@@ -156,13 +167,7 @@ public T Run (Func? errorHandler = null, IConsoleDriver? dri
/// is cutting edge.
///
bool IsLegacy { get; }
-
- ///
- /// Adds specified idle handler function to main iteration processing. The handler function will be called
- /// once per iteration of the main loop after other events have been handled.
- ///
- void AddIdle (Func func);
-
+
/// Adds a timeout to the application.
///
/// When time specified passes, the callback will be invoked. If the callback returns true, the timeout will be
diff --git a/Terminal.Gui/App/ITimedEvents.cs b/Terminal.Gui/App/ITimedEvents.cs
deleted file mode 100644
index 1fd867a8c8..0000000000
--- a/Terminal.Gui/App/ITimedEvents.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-#nullable enable
-using System.Collections.ObjectModel;
-
-namespace Terminal.Gui.App;
-
-///
-/// Manages timers and idles
-///
-public interface ITimedEvents
-{
- ///
- /// Adds specified idle handler function to main iteration processing. The handler function will be called
- /// once per iteration of the main loop after other events have been handled.
- ///
- ///
- void AddIdle (Func idleHandler);
-
- ///
- /// Runs all idle hooks
- ///
- void LockAndRunIdles ();
-
- ///
- /// Runs all timeouts that are due
- ///
- void LockAndRunTimers ();
-
- ///
- /// Called from to check if there are any outstanding timers or idle
- /// handlers.
- ///
- ///
- /// Returns the number of milliseconds remaining in the current timer (if any). Will be -1 if
- /// there are no active timers.
- ///
- /// if there is a timer or idle handler active.
- bool CheckTimersAndIdleHandlers (out int waitTimeout);
-
- /// Adds a timeout to the application.
- ///
- /// When time specified passes, the callback will be invoked. If the callback returns true, the timeout will be
- /// reset, repeating the invocation. If it returns false, the timeout will stop and be removed. The returned value is a
- /// token that can be used to stop the timeout by calling .
- ///
- object AddTimeout (TimeSpan time, Func callback);
-
- /// Removes a previously scheduled timeout
- /// The token parameter is the value returned by AddTimeout.
- ///
- /// Returns
- ///
- /// if the timeout is successfully removed; otherwise,
- ///
- /// .
- /// This method also returns
- ///
- /// if the timeout is not found.
- ///
- bool RemoveTimeout (object token);
-
- ///
- /// Returns all currently registered idles. May not include
- /// actively executing idles.
- ///
- ReadOnlyCollection> IdleHandlers { get;}
-
- ///
- /// Returns the next planned execution time (key - UTC ticks)
- /// for each timeout that is not actively executing.
- ///
- SortedList Timeouts { get; }
-
-
- /// Removes an idle handler added with from processing.
- ///
- ///
- /// if the idle handler is successfully removed; otherwise,
- ///
- /// .
- /// This method also returns
- ///
- /// if the idle handler is not found.
- bool RemoveIdle (Func fnTrue);
-
- ///
- /// Invoked when a new timeout is added. To be used in the case when
- /// is .
- ///
- event EventHandler? TimeoutAdded;
-}
diff --git a/Terminal.Gui/App/MainLoop.cs b/Terminal.Gui/App/MainLoop.cs
index 65f4ed599f..64aadf73cf 100644
--- a/Terminal.Gui/App/MainLoop.cs
+++ b/Terminal.Gui/App/MainLoop.cs
@@ -32,7 +32,7 @@ internal interface IMainLoopDriver
void Wakeup ();
}
-/// The MainLoop monitors timers and idle handlers.
+/// The main event loop of v1 driver based applications.
///
/// Monitoring of file descriptors is only available on Unix, there does not seem to be a way of supporting this
/// on Windows.
@@ -40,7 +40,7 @@ internal interface IMainLoopDriver
public class MainLoop : IDisposable
{
///
- /// Gets the class responsible for handling idles and timeouts
+ /// Gets the class responsible for handling timeouts
///
public ITimedEvents TimedEvents { get; } = new TimedEvents();
@@ -75,32 +75,6 @@ public void Dispose ()
MainLoopDriver = null;
}
- ///
- /// Adds specified idle handler function to processing. The handler function will be called
- /// once per iteration of the main loop after other events have been handled.
- ///
- ///
- /// Remove an idle handler by calling with the token this method returns.
- ///
- /// If the returns it will be removed and not called
- /// subsequently.
- ///
- ///
- /// Token that can be used to remove the idle handler with .
- // QUESTION: Why are we re-inventing the event wheel here?
- // PERF: This is heavy.
- // CONCURRENCY: Race conditions exist here.
- // CONCURRENCY: null delegates will hose this.
- //
- internal Func AddIdle (Func idleHandler)
- {
- TimedEvents.AddIdle (idleHandler);
-
- MainLoopDriver?.Wakeup ();
-
- return idleHandler;
- }
-
/// Determines whether there are pending events to be processed.
///
@@ -127,7 +101,7 @@ internal void Run ()
/// Runs one iteration of timers and file watches
///
- /// Use this to process all pending events (timers, idle handlers and file watches).
+ /// Use this to process all pending events (timers handlers and file watches).
///
/// while (main.EventsPending ()) RunIteration ();
///
@@ -138,9 +112,7 @@ internal void RunIteration ()
MainLoopDriver?.Iteration ();
- TimedEvents.LockAndRunTimers ();
-
- TimedEvents.LockAndRunIdles ();
+ TimedEvents.RunTimers ();
}
private void RunAnsiScheduler ()
diff --git a/Terminal.Gui/App/MainLoopSyncContext.cs b/Terminal.Gui/App/MainLoopSyncContext.cs
index d2268c5ad9..548722d6e2 100644
--- a/Terminal.Gui/App/MainLoopSyncContext.cs
+++ b/Terminal.Gui/App/MainLoopSyncContext.cs
@@ -10,14 +10,16 @@ internal sealed class MainLoopSyncContext : SynchronizationContext
public override void Post (SendOrPostCallback d, object state)
{
- Application.MainLoop?.AddIdle (
- () =>
- {
- d (state);
+ // Queue the task
+ Application.MainLoop?.TimedEvents.Add (TimeSpan.Zero,
+ () =>
+ {
+ d (state);
- return false;
- }
- );
+ return false;
+ }
+ );
+ Application.MainLoop?.Wakeup ();
}
//_mainLoop.Driver.Wakeup ();
diff --git a/Terminal.Gui/App/Mouse/IMouseGrabHandler.cs b/Terminal.Gui/App/Mouse/IMouseGrabHandler.cs
new file mode 100644
index 0000000000..026811e15a
--- /dev/null
+++ b/Terminal.Gui/App/Mouse/IMouseGrabHandler.cs
@@ -0,0 +1,87 @@
+#nullable enable
+namespace Terminal.Gui.App;
+
+///
+/// Defines a contract for tracking which (if any) has 'grabbed' the mouse,
+/// giving it exclusive priority for mouse events such as movement, button presses, and release.
+///
+/// This is typically used for scenarios like dragging, scrolling, or any interaction where a view
+/// needs to receive all mouse events until the operation completes (e.g., a scrollbar thumb being dragged).
+///
+///
+/// Usage pattern:
+///
+/// -
+/// Call to route all mouse events to a specific view.
+///
+/// -
+/// Call to release the grab and restore normal mouse routing.
+///
+/// -
+///
+/// Listen to , , ,
+/// and for grab lifecycle events.
+///
+///
+///
+///
+///
+public interface IMouseGrabHandler
+{
+ ///
+ /// Occurs after a view has grabbed the mouse.
+ ///
+ /// This event is raised after the mouse grab operation is complete and the specified view will receive all mouse
+ /// events.
+ ///
+ ///
+ public event EventHandler? GrabbedMouse;
+
+ ///
+ /// Occurs when a view requests to grab the mouse; can be canceled.
+ ///
+ /// Handlers can set e.Cancel to to prevent the grab.
+ ///
+ ///
+ public event EventHandler? GrabbingMouse;
+
+ ///
+ /// Grabs the mouse, forcing all mouse events to be routed to the specified view until is
+ /// called.
+ ///
+ ///
+ /// The that will receive all mouse events until is invoked.
+ /// If , the grab is released.
+ ///
+ public void GrabMouse (View? view);
+
+ ///
+ /// Gets the view that currently has grabbed the mouse (e.g., for dragging).
+ ///
+ /// When this property is not , all mouse events are routed to this view until
+ /// is called or the mouse is released.
+ ///
+ ///
+ public View? MouseGrabView { get; }
+
+ ///
+ /// Occurs after a view has released the mouse grab.
+ ///
+ /// This event is raised after the mouse grab has been released and normal mouse routing resumes.
+ ///
+ ///
+ public event EventHandler? UnGrabbedMouse;
+
+ ///
+ /// Occurs when a view requests to release the mouse grab; can be canceled.
+ ///
+ /// Handlers can set e.Cancel to to prevent the ungrab.
+ ///
+ ///
+ public event EventHandler? UnGrabbingMouse;
+
+ ///
+ /// Releases the mouse grab, so mouse events will be routed to the view under the mouse pointer.
+ ///
+ public void UngrabMouse ();
+}
diff --git a/Terminal.Gui/App/Mouse/MouseGrabHandler.cs b/Terminal.Gui/App/Mouse/MouseGrabHandler.cs
new file mode 100644
index 0000000000..65274d8150
--- /dev/null
+++ b/Terminal.Gui/App/Mouse/MouseGrabHandler.cs
@@ -0,0 +1,118 @@
+#nullable enable
+namespace Terminal.Gui.App;
+
+///
+/// INTERNAL: Implements to manage which (if any) has 'grabbed' the mouse,
+/// giving it exclusive priority for mouse events such as movement, button presses, and release.
+///
+/// Used for scenarios like dragging, scrolling, or any interaction where a view needs to receive all mouse events
+/// until the operation completes (e.g., a scrollbar thumb being dragged).
+///
+///
+/// See for usage details.
+///
+///
+internal class MouseGrabHandler : IMouseGrabHandler
+{
+ ///
+ public View? MouseGrabView { get; private set; }
+
+ ///
+ public event EventHandler? GrabbingMouse;
+
+ ///
+ public event EventHandler? UnGrabbingMouse;
+
+ ///
+ public event EventHandler? GrabbedMouse;
+
+ ///
+ public event EventHandler? UnGrabbedMouse;
+
+ ///
+ public void GrabMouse (View? view)
+ {
+ if (view is null || RaiseGrabbingMouseEvent (view))
+ {
+ return;
+ }
+
+ RaiseGrabbedMouseEvent (view);
+
+ // MouseGrabView is a static; only set if the application is initialized.
+ MouseGrabView = view;
+ }
+
+ ///
+ public void UngrabMouse ()
+ {
+ if (MouseGrabView is null)
+ {
+ return;
+ }
+
+#if DEBUG_IDISPOSABLE
+ if (View.EnableDebugIDisposableAsserts)
+ {
+ ObjectDisposedException.ThrowIf (MouseGrabView.WasDisposed, MouseGrabView);
+ }
+#endif
+
+ if (!RaiseUnGrabbingMouseEvent (MouseGrabView))
+ {
+ View view = MouseGrabView;
+ MouseGrabView = null;
+ RaiseUnGrabbedMouseEvent (view);
+ }
+ }
+
+ /// A delegate callback throws an exception.
+ private bool RaiseGrabbingMouseEvent (View? view)
+ {
+ if (view is null)
+ {
+ return false;
+ }
+
+ var evArgs = new GrabMouseEventArgs (view);
+ GrabbingMouse?.Invoke (view, evArgs);
+
+ return evArgs.Cancel;
+ }
+
+ /// A delegate callback throws an exception.
+ private bool RaiseUnGrabbingMouseEvent (View? view)
+ {
+ if (view is null)
+ {
+ return false;
+ }
+
+ var evArgs = new GrabMouseEventArgs (view);
+ UnGrabbingMouse?.Invoke (view, evArgs);
+
+ return evArgs.Cancel;
+ }
+
+ /// A delegate callback throws an exception.
+ private void RaiseGrabbedMouseEvent (View? view)
+ {
+ if (view is null)
+ {
+ return;
+ }
+
+ GrabbedMouse?.Invoke (view, new (view));
+ }
+
+ /// A delegate callback throws an exception.
+ private void RaiseUnGrabbedMouseEvent (View? view)
+ {
+ if (view is null)
+ {
+ return;
+ }
+
+ UnGrabbedMouse?.Invoke (view, new (view));
+ }
+}
diff --git a/Terminal.Gui/App/Timeout.cs b/Terminal.Gui/App/Timeout.cs
deleted file mode 100644
index 615ca2d9f6..0000000000
--- a/Terminal.Gui/App/Timeout.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// MainLoop.cs: IMainLoopDriver and MainLoop for Terminal.Gui
-//
-// Authors:
-// Miguel de Icaza (miguel@gnome.org)
-//
-
-namespace Terminal.Gui.App;
-
-/// Provides data for timers running manipulation.
-public sealed class Timeout
-{
- /// The function that will be invoked.
- public Func Callback;
-
- /// Time to wait before invoke the callback.
- public TimeSpan Span;
-}
diff --git a/Terminal.Gui/App/Timeout/ITimedEvents.cs b/Terminal.Gui/App/Timeout/ITimedEvents.cs
new file mode 100644
index 0000000000..501da77bc2
--- /dev/null
+++ b/Terminal.Gui/App/Timeout/ITimedEvents.cs
@@ -0,0 +1,64 @@
+#nullable enable
+namespace Terminal.Gui.App;
+
+///
+/// Manages timers.
+///
+public interface ITimedEvents
+{
+ ///
+ /// Adds a timeout to the application.
+ ///
+ ///
+ /// When the specified time passes, the callback will be invoked. If the callback returns , the
+ /// timeout will be
+ /// reset, repeating the invocation. If it returns , the timeout will stop and be removed. The
+ /// returned value is a
+ /// token that can be used to stop the timeout by calling .
+ ///
+ object Add (TimeSpan time, Func callback);
+
+ ///
+ object Add (Timeout timeout);
+
+ ///
+ /// Invoked when a new timeout is added. To be used in the case when
+ /// is .
+ ///
+ event EventHandler? Added;
+
+ ///
+ /// Called from to check if there are any outstanding timer handlers.
+ ///
+ ///
+ /// Returns the number of milliseconds remaining in the current timer (if any). Will be -1 if
+ /// there are no active timers.
+ ///
+ ///
+ /// if there is a timer active; otherwise, .
+ ///
+ bool CheckTimers (out int waitTimeout);
+
+ ///
+ /// Removes a previously scheduled timeout.
+ ///
+ ///
+ /// The token parameter is the value returned by or .
+ ///
+ ///
+ /// if the timeout is successfully removed; otherwise, .
+ /// This method also returns if the timeout is not found.
+ ///
+ bool Remove (object token);
+
+ ///
+ /// Runs all timeouts that are due.
+ ///
+ void RunTimers ();
+
+ ///
+ /// Returns the next planned execution time (key - UTC ticks)
+ /// for each timeout that is not actively executing.
+ ///
+ SortedList Timeouts { get; }
+}
diff --git a/Terminal.Gui/App/Timeout/LogarithmicTimeout.cs b/Terminal.Gui/App/Timeout/LogarithmicTimeout.cs
new file mode 100644
index 0000000000..eacf09607e
--- /dev/null
+++ b/Terminal.Gui/App/Timeout/LogarithmicTimeout.cs
@@ -0,0 +1,38 @@
+namespace Terminal.Gui.App;
+
+/// Implements a logarithmic increasing timeout.
+public class LogarithmicTimeout : Timeout
+{
+ ///
+ /// Creates a new instance where stages are the logarithm multiplied by the
+ /// (starts fast then slows).
+ ///
+ /// Multiple for the logarithm
+ /// Method to invoke
+ public LogarithmicTimeout (TimeSpan baseDelay, Func callback)
+ {
+ _baseDelay = baseDelay;
+ Callback = callback;
+ }
+
+ private readonly TimeSpan _baseDelay;
+ private int _stage;
+
+ /// Increments the stage to increase the timeout.
+ public void AdvanceStage () { _stage++; }
+
+ /// Resets the stage back to zero.
+ public void Reset () { _stage = 0; }
+
+ /// Gets the current calculated Span based on the stage.
+ public override TimeSpan Span
+ {
+ get
+ {
+ // Calculate logarithmic increase
+ double multiplier = Math.Log (_stage + 1); // ln(stage + 1)
+
+ return TimeSpan.FromMilliseconds (_baseDelay.TotalMilliseconds * multiplier);
+ }
+ }
+}
diff --git a/Terminal.Gui/App/Timeout/SmoothAcceleratingTimeout.cs b/Terminal.Gui/App/Timeout/SmoothAcceleratingTimeout.cs
new file mode 100644
index 0000000000..962ce4b19f
--- /dev/null
+++ b/Terminal.Gui/App/Timeout/SmoothAcceleratingTimeout.cs
@@ -0,0 +1,53 @@
+namespace Terminal.Gui.App;
+
+///
+/// Timeout which accelerates slowly at first then fast up to a maximum speed.
+/// Use to increment the stage of the timer (e.g. in
+/// your timer callback code).
+///
+public class SmoothAcceleratingTimeout : Timeout
+{
+ ///
+ /// Creates a new instance of the smooth acceleration timeout.
+ ///
+ /// Delay before first tick, the longest it will ever take
+ /// The fastest the timer can get no matter how long it runs
+ /// Controls how fast the timer accelerates
+ /// Method to call when timer ticks
+ public SmoothAcceleratingTimeout (TimeSpan initialDelay, TimeSpan minDelay, double decayFactor, Func callback)
+ {
+ _initialDelay = initialDelay;
+ _minDelay = minDelay;
+ _decayFactor = decayFactor;
+ Callback = callback;
+ }
+
+ private readonly TimeSpan _initialDelay;
+ private readonly TimeSpan _minDelay;
+ private readonly double _decayFactor;
+ private int _stage;
+
+ ///
+ /// Advances the timer stage, this should be called from your timer callback or whenever
+ /// you want to advance the speed.
+ ///
+ public void AdvanceStage () { _stage++; }
+
+ ///
+ /// Resets the timer to original speed.
+ ///
+ public void Reset () { _stage = 0; }
+
+ ///
+ public override TimeSpan Span
+ {
+ get
+ {
+ double initialMs = _initialDelay.TotalMilliseconds;
+ double minMs = _minDelay.TotalMilliseconds;
+ double delayMs = minMs + (initialMs - minMs) * Math.Pow (_decayFactor, _stage);
+
+ return TimeSpan.FromMilliseconds (delayMs);
+ }
+ }
+}
diff --git a/Terminal.Gui/App/TimedEvents.cs b/Terminal.Gui/App/Timeout/TimedEvents.cs
similarity index 53%
rename from Terminal.Gui/App/TimedEvents.cs
rename to Terminal.Gui/App/Timeout/TimedEvents.cs
index 13553a43b2..b77e0520d3 100644
--- a/Terminal.Gui/App/TimedEvents.cs
+++ b/Terminal.Gui/App/Timeout/TimedEvents.cs
@@ -1,137 +1,163 @@
#nullable enable
-using System.Collections.ObjectModel;
-
namespace Terminal.Gui.App;
///
-/// Handles timeouts and idles
+/// Manages scheduled timeouts (timed callbacks) for the application.
+///
+/// Allows scheduling of callbacks to be invoked after a specified delay, with optional repetition.
+/// Timeouts are stored in a sorted list by their scheduled execution time (UTC ticks).
+/// Thread-safe for concurrent access.
+///
+///
+/// Typical usage:
+///
+/// -
+/// Call to schedule a callback.
+///
+/// -
+///
+/// Call periodically (e.g., from the main loop) to execute due
+/// callbacks.
+///
+///
+/// -
+/// Call to cancel a scheduled timeout.
+///
+///
+///
///
public class TimedEvents : ITimedEvents
{
- internal List> _idleHandlers = new ();
internal SortedList _timeouts = new ();
-
- /// The idle handlers and lock that must be held while manipulating them
- private readonly object _idleHandlersLock = new ();
-
private readonly object _timeoutsLockToken = new ();
-
- /// Gets a copy of the list of all idle handlers.
- public ReadOnlyCollection> IdleHandlers
- {
- get
- {
- lock (_idleHandlersLock)
- {
- return new List> (_idleHandlers).AsReadOnly ();
- }
- }
- }
-
///
/// 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;
- ///
- public void AddIdle (Func idleHandler)
- {
- lock (_idleHandlersLock)
- {
- _idleHandlers.Add (idleHandler);
- }
- }
-
///
- public event EventHandler? TimeoutAdded;
-
+ public event EventHandler? Added;
- private void AddTimeout (TimeSpan time, Timeout timeout)
+ ///
+ public void RunTimers ()
{
lock (_timeoutsLockToken)
{
- long k = (DateTime.UtcNow + time).Ticks;
- _timeouts.Add (NudgeToUniqueKey (k), timeout);
- TimeoutAdded?.Invoke (this, new TimeoutEventArgs (timeout, k));
+ if (_timeouts.Count > 0)
+ {
+ RunTimersImpl ();
+ }
}
}
- ///
- /// Finds the closest number to that is not present in
- /// (incrementally).
- ///
- ///
- ///
- private long NudgeToUniqueKey (long k)
+ ///
+ public bool Remove (object token)
{
lock (_timeoutsLockToken)
{
- while (_timeouts.ContainsKey (k))
+ int idx = _timeouts.IndexOfValue ((token as Timeout)!);
+
+ if (idx == -1)
{
- k++;
+ return false;
}
+
+ _timeouts.RemoveAt (idx);
}
- return k;
+ return true;
}
+ ///
+ public object Add (TimeSpan time, Func callback)
+ {
+ ArgumentNullException.ThrowIfNull (callback);
+
+ var timeout = new Timeout { Span = time, Callback = callback };
+ AddTimeout (time, timeout);
- // PERF: This is heavier than it looks.
- // CONCURRENCY: Potential deadlock city here.
- // CONCURRENCY: Multiple concurrency pitfalls on the delegates themselves.
- // INTENT: It looks like the general architecture here is trying to be a form of publisher/consumer pattern.
- private void RunIdle ()
+ return timeout;
+ }
+
+ ///
+ public object Add (Timeout timeout)
{
- Func [] iterate;
- lock (_idleHandlersLock)
- {
- iterate = _idleHandlers.ToArray ();
- _idleHandlers = new List> ();
- }
+ AddTimeout (timeout.Span, timeout);
- foreach (Func idle in iterate)
+ return timeout;
+ }
+
+ ///
+ public bool CheckTimers (out int waitTimeout)
+ {
+ long now = DateTime.UtcNow.Ticks;
+
+ waitTimeout = 0;
+
+ lock (_timeoutsLockToken)
{
- if (idle ())
+ if (_timeouts.Count > 0)
{
- lock (_idleHandlersLock)
+ waitTimeout = (int)((_timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
+
+ if (waitTimeout < 0)
{
- _idleHandlers.Add (idle);
+ // This avoids 'poll' waiting infinitely if 'waitTimeout < 0' until some action is detected
+ // This can occur after IMainLoopDriver.Wakeup is executed where the pollTimeout is less than 0
+ // and no event occurred in elapsed time when the 'poll' is start running again.
+ waitTimeout = 0;
}
+
+ return true;
}
+
+ // ManualResetEventSlim.Wait, which is called by IMainLoopDriver.EventsPending, will wait indefinitely if
+ // the timeout is -1.
+ waitTimeout = -1;
}
+
+ return false;
}
- ///
- public void LockAndRunTimers ()
+ private void AddTimeout (TimeSpan time, Timeout timeout)
{
lock (_timeoutsLockToken)
{
- if (_timeouts.Count > 0)
+ long k = (DateTime.UtcNow + time).Ticks;
+
+ // if user wants to run as soon as possible set timer such that it expires right away (no race conditions)
+ if (time == TimeSpan.Zero)
{
- RunTimers ();
+ k -= 100;
}
- }
+ _timeouts.Add (NudgeToUniqueKey (k), timeout);
+ Added?.Invoke (this, new (timeout, k));
+ }
}
- ///
- public void LockAndRunIdles ()
+ ///
+ /// Finds the closest number to that is not present in
+ /// (incrementally).
+ ///
+ ///
+ ///
+ private long NudgeToUniqueKey (long k)
{
- bool runIdle;
-
- lock (_idleHandlersLock)
+ lock (_timeoutsLockToken)
{
- runIdle = _idleHandlers.Count > 0;
+ while (_timeouts.ContainsKey (k))
+ {
+ k++;
+ }
}
- if (runIdle)
- {
- RunIdle ();
- }
+ return k;
}
- private void RunTimers ()
+
+ private void RunTimersImpl ()
{
long now = DateTime.UtcNow.Ticks;
SortedList copy;
@@ -143,7 +169,7 @@ private void RunTimers ()
lock (_timeoutsLockToken)
{
copy = _timeouts;
- _timeouts = new SortedList ();
+ _timeouts = new ();
}
foreach ((long k, Timeout timeout) in copy)
@@ -164,94 +190,4 @@ private void RunTimers ()
}
}
}
-
- ///
- public bool RemoveIdle (Func token)
- {
- lock (_idleHandlersLock)
- {
- return _idleHandlers.Remove (token);
- }
- }
-
- /// Removes a previously scheduled timeout
- /// The token parameter is the value returned by AddTimeout.
- /// Returns
- ///
- /// if the timeout is successfully removed; otherwise,
- ///
- /// .
- /// This method also returns
- ///
- /// if the timeout is not found.
- public bool RemoveTimeout (object token)
- {
- lock (_timeoutsLockToken)
- {
- int idx = _timeouts.IndexOfValue ((token as Timeout)!);
-
- if (idx == -1)
- {
- return false;
- }
-
- _timeouts.RemoveAt (idx);
- }
-
- return true;
- }
-
-
- /// Adds a timeout to the .
- ///
- /// When time specified passes, the callback will be invoked. If the callback returns true, the timeout will be
- /// reset, repeating the invocation. If it returns false, the timeout will stop and be removed. The returned value is a
- /// token that can be used to stop the timeout by calling .
- ///
- public object AddTimeout (TimeSpan time, Func callback)
- {
- ArgumentNullException.ThrowIfNull (callback);
-
- var timeout = new Timeout { Span = time, Callback = callback };
- AddTimeout (time, timeout);
-
- return timeout;
- }
-
- ///
- public bool CheckTimersAndIdleHandlers (out int waitTimeout)
- {
- long now = DateTime.UtcNow.Ticks;
-
- waitTimeout = 0;
-
- lock (_timeoutsLockToken)
- {
- if (_timeouts.Count > 0)
- {
- waitTimeout = (int)((_timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
-
- if (waitTimeout < 0)
- {
- // This avoids 'poll' waiting infinitely if 'waitTimeout < 0' until some action is detected
- // This can occur after IMainLoopDriver.Wakeup is executed where the pollTimeout is less than 0
- // and no event occurred in elapsed time when the 'poll' is start running again.
- waitTimeout = 0;
- }
-
- return true;
- }
-
- // ManualResetEventSlim.Wait, which is called by IMainLoopDriver.EventsPending, will wait indefinitely if
- // the timeout is -1.
- waitTimeout = -1;
- }
-
- // There are no timers set, check if there are any idle handlers
-
- lock (_idleHandlersLock)
- {
- return _idleHandlers.Count > 0;
- }
- }
-}
\ No newline at end of file
+}
diff --git a/Terminal.Gui/App/Timeout/Timeout.cs b/Terminal.Gui/App/Timeout/Timeout.cs
new file mode 100644
index 0000000000..c3054869f8
--- /dev/null
+++ b/Terminal.Gui/App/Timeout/Timeout.cs
@@ -0,0 +1,33 @@
+namespace Terminal.Gui.App;
+
+///
+/// Represents a scheduled timeout for use with timer management APIs.
+///
+/// Encapsulates a callback function to be invoked after a specified time interval. The callback can optionally
+/// indicate whether the timeout should repeat.
+///
+///
+/// Used by and related timer systems to manage timed operations in the application.
+///
+///
+public class Timeout
+{
+ ///
+ /// Gets or sets the function to invoke when the timeout expires.
+ ///
+ ///
+ /// A delegate. If the callback returns , the timeout will be
+ /// rescheduled and invoked again after the same interval.
+ /// If the callback returns , the timeout will be removed and not invoked again.
+ ///
+ public Func Callback { get; set; }
+
+ ///
+ /// Gets or sets the time interval to wait before invoking the .
+ ///
+ ///
+ /// A representing the delay before the callback is invoked. If the timeout is rescheduled
+ /// (i.e., returns ), this interval is used again.
+ ///
+ public virtual TimeSpan Span { get; set; }
+}
diff --git a/Terminal.Gui/App/TimeoutEventArgs.cs b/Terminal.Gui/App/Timeout/TimeoutEventArgs.cs
similarity index 93%
rename from Terminal.Gui/App/TimeoutEventArgs.cs
rename to Terminal.Gui/App/Timeout/TimeoutEventArgs.cs
index b77741db43..9ad014fa43 100644
--- a/Terminal.Gui/App/TimeoutEventArgs.cs
+++ b/Terminal.Gui/App/Timeout/TimeoutEventArgs.cs
@@ -1,6 +1,6 @@
namespace Terminal.Gui.App;
-/// for timeout events (e.g. )
+/// for timeout events (e.g. )
public class TimeoutEventArgs : EventArgs
{
/// Creates a new instance of the class.
diff --git a/Terminal.Gui/Drivers/CursesDriver/UnixMainLoop.cs b/Terminal.Gui/Drivers/CursesDriver/UnixMainLoop.cs
index c3486e7027..50053f4bb0 100644
--- a/Terminal.Gui/Drivers/CursesDriver/UnixMainLoop.cs
+++ b/Terminal.Gui/Drivers/CursesDriver/UnixMainLoop.cs
@@ -104,7 +104,7 @@ bool IMainLoopDriver.EventsPending ()
UpdatePollMap ();
- bool checkTimersResult = _mainLoop!.TimedEvents.CheckTimersAndIdleHandlers (out int pollTimeout);
+ bool checkTimersResult = _mainLoop!.TimedEvents.CheckTimers (out int pollTimeout);
int n = poll (_pollMap!, (uint)_pollMap!.Length, pollTimeout);
diff --git a/Terminal.Gui/Drivers/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/Drivers/EscSeqUtils/EscSeqUtils.cs
index b035a2335d..0a99fa8b5d 100644
--- a/Terminal.Gui/Drivers/EscSeqUtils/EscSeqUtils.cs
+++ b/Terminal.Gui/Drivers/EscSeqUtils/EscSeqUtils.cs
@@ -900,22 +900,8 @@ Action continuousButtonPressedHandler
_point = pos;
- if ((mouseFlags [0] & MouseFlags.ReportMousePosition) == 0)
- {
- Application.MainLoop?.AddIdle (
- () =>
- {
- // INTENT: What's this trying to do?
- // The task itself is not awaited.
- Task.Run (
- async () => await ProcessContinuousButtonPressedAsync (
- buttonState,
- continuousButtonPressedHandler));
- return false;
- });
- }
- else if (mouseFlags [0].HasFlag (MouseFlags.ReportMousePosition))
+ if (mouseFlags [0].HasFlag (MouseFlags.ReportMousePosition))
{
_point = pos;
@@ -945,7 +931,7 @@ Action continuousButtonPressedHandler
_isButtonClicked = false;
_isButtonDoubleClicked = true;
- Application.MainLoop?.AddIdle (
+ Application.MainLoop?.TimedEvents.Add (TimeSpan.Zero,
() =>
{
Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
@@ -959,7 +945,7 @@ Action continuousButtonPressedHandler
// lastMouseButtonReleased = null;
// isButtonReleased = false;
// isButtonClicked = true;
- // Application.MainLoop.AddIdle (() => {
+ // Application.MainLoop.AddTimeout (() => {
// Task.Run (async () => await ProcessButtonClickedAsync ());
// return false;
// });
@@ -984,7 +970,7 @@ Action continuousButtonPressedHandler
mouseFlags.Add (GetButtonClicked (buttonState));
_isButtonClicked = true;
- Application.MainLoop?.AddIdle (
+ Application.MainLoop?.TimedEvents.Add (TimeSpan.Zero,
() =>
{
Task.Run (async () => await ProcessButtonClickedAsync ());
@@ -1498,29 +1484,6 @@ private static async Task ProcessButtonDoubleClickedAsync ()
_isButtonDoubleClicked = false;
}
- private static async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag, Action continuousButtonPressedHandler)
- {
- // PERF: Pause and poll in a hot loop.
- // This should be replaced with event dispatch and a synchronization primitive such as AutoResetEvent.
- // Will make a massive difference in responsiveness.
- while (_isButtonPressed)
- {
- await Task.Delay (100);
-
- View view = Application.WantContinuousButtonPressedView;
-
- if (view is null)
- {
- break;
- }
-
- if (_isButtonPressed && _lastMouseButtonPressed is { } && (mouseFlag & MouseFlags.ReportMousePosition) == 0)
- {
- Application.Invoke (() => continuousButtonPressedHandler (mouseFlag, _point ?? Point.Empty));
- }
- }
- }
-
private static MouseFlags SetControlKeyStates (MouseFlags buttonState, MouseFlags mouseFlag)
{
if ((buttonState & MouseFlags.ButtonCtrl) != 0 && (mouseFlag & MouseFlags.ButtonCtrl) == 0)
diff --git a/Terminal.Gui/Drivers/NetDriver/NetMainLoop.cs b/Terminal.Gui/Drivers/NetDriver/NetMainLoop.cs
index b33964be42..96aae80390 100644
--- a/Terminal.Gui/Drivers/NetDriver/NetMainLoop.cs
+++ b/Terminal.Gui/Drivers/NetDriver/NetMainLoop.cs
@@ -57,7 +57,7 @@ bool IMainLoopDriver.EventsPending ()
_waitForProbe.Set ();
- if (_resultQueue.Count > 0 || _mainLoop!.TimedEvents.CheckTimersAndIdleHandlers (out int waitTimeout))
+ if (_resultQueue.Count > 0 || _mainLoop!.TimedEvents.CheckTimers (out int waitTimeout))
{
return true;
}
@@ -84,7 +84,7 @@ bool IMainLoopDriver.EventsPending ()
if (!_eventReadyTokenSource.IsCancellationRequested)
{
- return _resultQueue.Count > 0 || _mainLoop.TimedEvents.CheckTimersAndIdleHandlers (out _);
+ return _resultQueue.Count > 0 || _mainLoop.TimedEvents.CheckTimers (out _);
}
// If cancellation was requested then always return true
diff --git a/Terminal.Gui/Drivers/V2/ApplicationV2.cs b/Terminal.Gui/Drivers/V2/ApplicationV2.cs
index e10a621b78..ca94ebe575 100644
--- a/Terminal.Gui/Drivers/V2/ApplicationV2.cs
+++ b/Terminal.Gui/Drivers/V2/ApplicationV2.cs
@@ -21,6 +21,9 @@ public class ApplicationV2 : ApplicationImpl
private readonly ITimedEvents _timedEvents = new TimedEvents ();
+ ///
+ public override ITimedEvents TimedEvents => _timedEvents;
+
///
/// Creates anew instance of the Application backend. The provided
/// factory methods will be used on Init calls to get things booted.
@@ -225,7 +228,14 @@ public override void RequestStop (Toplevel? top)
///
public override void Invoke (Action action)
{
- _timedEvents.AddIdle (
+ // If we are already on the main UI thread
+ if (Application.MainThreadId == Thread.CurrentThread.ManagedThreadId)
+ {
+ action ();
+ return;
+ }
+
+ _timedEvents.Add (TimeSpan.Zero,
() =>
{
action ();
@@ -236,20 +246,10 @@ public override void Invoke (Action action)
}
///
- public override void AddIdle (Func func) { _timedEvents.AddIdle (func); }
-
- ///
- /// Removes an idle function added by
- ///
- /// Function to remove
- /// True if it was found and removed
- public bool RemoveIdle (Func fnTrue) { return _timedEvents.RemoveIdle (fnTrue); }
-
- ///
- public override object AddTimeout (TimeSpan time, Func callback) { return _timedEvents.AddTimeout (time, callback); }
+ public override object AddTimeout (TimeSpan time, Func callback) { return _timedEvents.Add (time, callback); }
///
- public override bool RemoveTimeout (object token) { return _timedEvents.RemoveTimeout (token); }
+ public override bool RemoveTimeout (object token) { return _timedEvents.Remove (token); }
///
public override void LayoutAndDraw (bool forceDraw)
diff --git a/Terminal.Gui/Drivers/V2/IMainLoop.cs b/Terminal.Gui/Drivers/V2/IMainLoop.cs
index 2638e4074b..647776cbe3 100644
--- a/Terminal.Gui/Drivers/V2/IMainLoop.cs
+++ b/Terminal.Gui/Drivers/V2/IMainLoop.cs
@@ -6,11 +6,11 @@ namespace Terminal.Gui.Drivers;
///
/// Interface for main loop that runs the core Terminal.Gui UI loop.
///
-///
+/// Type of raw input events processed by the loop e.g.
public interface IMainLoop : IDisposable
{
///
- /// Gets the class responsible for servicing user timeouts and idles
+ /// Gets the class responsible for servicing user timeouts
///
public ITimedEvents TimedEvents { get; }
diff --git a/Terminal.Gui/Drivers/V2/MainLoop.cs b/Terminal.Gui/Drivers/V2/MainLoop.cs
index e40dfc66b6..5b6d9fdde7 100644
--- a/Terminal.Gui/Drivers/V2/MainLoop.cs
+++ b/Terminal.Gui/Drivers/V2/MainLoop.cs
@@ -143,9 +143,7 @@ internal void IterationImpl ()
var swCallbacks = Stopwatch.StartNew ();
- TimedEvents.LockAndRunTimers ();
-
- TimedEvents.LockAndRunIdles ();
+ TimedEvents.RunTimers ();
Logging.IterationInvokesAndTimeouts.Record (swCallbacks.Elapsed.Milliseconds);
}
diff --git a/Terminal.Gui/Drivers/V2/V2.cd b/Terminal.Gui/Drivers/V2/V2.cd
index f5004db3ce..440e918849 100644
--- a/Terminal.Gui/Drivers/V2/V2.cd
+++ b/Terminal.Gui/Drivers/V2/V2.cd
@@ -6,7 +6,7 @@
-
+
@@ -18,7 +18,7 @@
-
+
@@ -27,44 +27,33 @@
-
+
QIAACAAAACAEAAAAAAAAAAAkAAAAAAAAAwAAAAAAABA=
- ConsoleDrivers\V2\WindowsInput.cs
+ Drivers\V2\WindowsInput.cs
-
+
AAAAAAAAACAEAAAAQAAAAAAgAAAAAAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\NetInput.cs
+ Drivers\V2\NetInput.cs
-
+
AAAAAAAAACAEAQAAAAAAAAAgACAAAAAAAAAAAAAAAAo=
- ConsoleDrivers\V2\ConsoleInput.cs
+ Drivers\V2\ConsoleInput.cs
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -73,7 +62,7 @@
-
+
@@ -82,7 +71,7 @@
-
+
@@ -93,7 +82,7 @@
-
+
@@ -105,7 +94,7 @@
-
+
@@ -119,12 +108,11 @@
- QQQAAAAQACABJQQAABAAAQAAACAAAAACAAEAAACAEgg=
- ConsoleDrivers\V2\MainLoop.cs
+ QQQAAAAQACABJQQAABAAAQAAACAAAAACAIEAAAAAEgg=
+ Drivers\V2\MainLoop.cs
-
@@ -133,25 +121,25 @@
-
+
IAAAIAEiCAIABAAAABQAAAAAABAAAQQAIQIABAAACgg=
- ConsoleDrivers\V2\MainLoopCoordinator.cs
+ Drivers\V2\MainLoopCoordinator.cs
-
+
AAQAAAAAAAAACIAAAAAAAAAAAAAgAABAAAAACBAAAAA=
- ConsoleDrivers\AnsiResponseParser\AnsiResponseParser.cs
+ Drivers\AnsiResponseParser\AnsiResponseParser.cs
-
+
@@ -159,29 +147,29 @@
AwAAAAAAAIAAAECIBgAEQIAAAAEMRgAACAAAKABAgAA=
- ConsoleDrivers\V2\OutputBuffer.cs
+ Drivers\V2\OutputBuffer.cs
-
+
- AEAAAAAAACAAAAAAAAAAAAAAAAAAQAAAMACAAAEAgAk=
- ConsoleDrivers\V2\NetOutput.cs
+ AEAAAAAAACAAAAAAAAAQAAAAAAAAQAAAMACAAAEAgAk=
+ Drivers\V2\NetOutput.cs
-
+
- AEAAABACACAAhAAAAAAAACCAAAgAQAAIMAAAAAEAgAQ=
- ConsoleDrivers\V2\WindowsOutput.cs
+ AEAAABACACAAhAAAAAAQACCAAAgAYAAIMAAAAAEAgAQ=
+ Drivers\V2\WindowsOutput.cs
-
+
-
+
@@ -192,7 +180,7 @@
AQAkEAAAAASAiAAEAgwgAAAABAIAAAAAAAAAAAAEAAA=
- ConsoleDrivers\V2\InputProcessor.cs
+ Drivers\V2\InputProcessor.cs
@@ -201,28 +189,28 @@
-
+
AAAAAAAAAAAACBAAAgAAAEAAAAAAAAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\NetInputProcessor.cs
+ Drivers\V2\NetInputProcessor.cs
-
+
AQAAAAAAAAAACAAAAgAAAAAAAgAEAAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\WindowsInputProcessor.cs
+ Drivers\V2\WindowsInputProcessor.cs
-
+
BAAAAAAAAAgAAAAAAAAAAAAAIAAAAAAAQAAAAAAAAAA=
- ConsoleDrivers\AnsiResponseParser\AnsiMouseParser.cs
+ Drivers\AnsiResponseParser\AnsiMouseParser.cs
-
+
@@ -230,41 +218,41 @@
AQcgAAAAAKBAgFEIBBgAQJEAAjkaQiIAGQADKABDgAQ=
- ConsoleDrivers\V2\ConsoleDriverFacade.cs
+ Drivers\V2\ConsoleDriverFacade.cs
-
+
AAQAACAAIAAAIAACAESQAAQAACGAAAAAAAAAAAAAQQA=
- ConsoleDrivers\AnsiResponseParser\AnsiRequestScheduler.cs
+ Drivers\AnsiResponseParser\AnsiRequestScheduler.cs
-
+
-
+
-
+
- UAiASAAAEICQALAAQAAAKAAAoAIAAABAAQIAJiAQASQ=
- ConsoleDrivers\AnsiResponseParser\AnsiResponseParser.cs
+ UAiASAAAEICQALCAQAAAKAAAoAIAAABAAQIAJiAQASQ=
+ Drivers\AnsiResponseParser\AnsiResponseParser.cs
@@ -273,296 +261,293 @@
-
+
AAAABAAAAAAAAAAAAgAAAAAAACAAAAAAAAUAAAAIAAA=
- ConsoleDrivers\V2\MouseInterpreter.cs
+ Drivers\V2\MouseInterpreter.cs
-
+
AAAAAAAAAMwAIAAAAAAAAAAAABCAAAAAAAAABAAEAAg=
- ConsoleDrivers\V2\MouseButtonStateEx.cs
+ Drivers\V2\MouseButtonStateEx.cs
-
+
AAAAAAAAAAIAACAAAAAAAIBAAAAAAACAAAAAAAgAAAA=
- ConsoleDrivers\AnsiResponseParser\StringHeld.cs
+ Drivers\AnsiResponseParser\StringHeld.cs
-
+
AAAAAAAAgAIAACAAAAAAAIBAAAAAAACAAAAAAAAAAAA=
- ConsoleDrivers\AnsiResponseParser\GenericHeld.cs
+ Drivers\AnsiResponseParser\GenericHeld.cs
-
+
AAAAAAAAAEAAAAAAAEAAAAACAAAAAAAAAAAAAAAAAAA=
- ConsoleDrivers\AnsiEscapeSequenceRequest.cs
+ Drivers\AnsiEscapeSequenceRequest.cs
-
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAgAAEAAAA=
- ConsoleDrivers\AnsiEscapeSequence.cs
+ Drivers\AnsiEscapeSequence.cs
-
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAgACBAAAAACBAAAAA=
- ConsoleDrivers\AnsiResponseParser\AnsiResponseParser.cs
-
-
-
-
-
- hEK4FAgAqARIspQeBwoUgTGgACNL0AIAESLKoggBSw8=
- Application\Application.cs
+ Drivers\AnsiResponseParser\AnsiResponseParser.cs
-
-
-
- AABAAAAAIAAIAgQQAAAAAQAAAAAAAAAAQAAKgAAAAAI=
- Application\ApplicationImpl.cs
-
-
-
-
-
-
-
+
- QAAAAAgABAEIBgAQAAAAAQBAAAAAgAEAAAAKgIAAAgI=
- ConsoleDrivers\V2\ApplicationV2.cs
+ QAAgAAgABAEIBgAQAAAAAQAAAAAAgAEAAAAKAIAAEgI=
+ Drivers\V2\ApplicationV2.cs
-
-
-
- 3/v2dzPLvbb/5+LOHuv1x0dem3Y57v/8c6afz2/e/Y8=
- View\View.Adornments.cs
-
-
-
-
+
AAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\WindowsKeyConverter.cs
+ Drivers\V2\WindowsKeyConverter.cs
-
+
AAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\NetKeyConverter.cs
+ Drivers\V2\NetKeyConverter.cs
-
+
AAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAE=
- ConsoleDrivers\AnsiResponseParser\Keyboard\AnsiKeyboardParser.cs
+ Drivers\AnsiResponseParser\Keyboard\AnsiKeyboardParser.cs
-
+
AIAAAAAAAAAAAAEAAAAAAAAAAEIAAAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\ToplevelTransitionManager.cs
+ Drivers\V2\ToplevelTransitionManager.cs
-
-
-
- AAAAAAAAAAIgAAAAAAEQAAAAAAAAABAAgAAAAAAAEAA=
- ConsoleDrivers\V2\Logging.cs
-
-
-
+
AAAAgAAAAAAAAAAEAAAAABAAAAAACAAAAAAAAAAAACA=
- ConsoleDrivers\V2\WindowSizeMonitor.cs
+ Drivers\V2\WindowSizeMonitor.cs
-
+
AAACIAAAAAAAAAAAAAAAAAQQAAAAAAAAAAAAAAAACAA=
- ConsoleDrivers\AnsiResponseParser\Keyboard\AnsiKeyboardParserPattern.cs
+ Drivers\AnsiResponseParser\Keyboard\AnsiKeyboardParserPattern.cs
-
+
- AAACAAAAAAAAABAAAAAAAAAQAACAAAAAAAAAAAAAAAA=
- ConsoleDrivers\AnsiResponseParser\Keyboard\CsiKeyPattern.cs
+ AAACQAAAAAAAAAAAAAAAAAAQAACAAAAAAAAAAAAAAAA=
+ Drivers\AnsiResponseParser\Keyboard\CsiKeyPattern.cs
-
+
AAACAAAAAAAAAAAAAAAAAAAQAACAAAAAAAAAAAAAAAA=
- ConsoleDrivers\AnsiResponseParser\Keyboard\EscAsAltPattern.cs
+ Drivers\AnsiResponseParser\Keyboard\EscAsAltPattern.cs
-
+
AAACAAAAAAAAAAAAAAAAAAAQAACAAAAAAAAAAAAAAAA=
- ConsoleDrivers\AnsiResponseParser\Keyboard\Ss3Pattern.cs
+ Drivers\AnsiResponseParser\Keyboard\Ss3Pattern.cs
-
+
+
+
+ AABgAAAAIAAIAgQUAAAAAQAAAAAAAAAAQAAKAAAAEAI=
+ App\ApplicationImpl.cs
+
+
+
+
+
+
+ gEK4FIgYOAQIuhQeBwoUgSCgAAJL0AACESIKoAiBWw8=
+ App\Application.cs
+
+
+
+
+
+ 27u2V3Pfvf7/x/LOXur1x0de3zZt7v/8c+bfzX/e/c8=
+ ViewBase\View.Adornments.cs
+
+
+
+
+
+
+ AAAIEAAAAAIgAYAAAAEQABAAAAAAABAAgAAAAAAAEAA=
+ App\Logging.cs
+
+
+
AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAI=
- ConsoleDrivers\V2\IConsoleInput.cs
+ Drivers\V2\IConsoleInput.cs
-
+
QAQAAAAAAAABIQQAAAAAAAAAAAAAAAACAAAAAAAAEAA=
- ConsoleDrivers\V2\IMainLoop.cs
+ Drivers\V2\IMainLoop.cs
-
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAMAAAAAEAAAA=
- ConsoleDrivers\V2\IConsoleOutput.cs
+ Drivers\V2\IConsoleOutput.cs
-
+
AQAAAAAAAIAAAEAIAAAAQIAAAAEMRgAACAAAKABAgAA=
- ConsoleDrivers\V2\IOutputBuffer.cs
+ Drivers\V2\IOutputBuffer.cs
-
+
AAAkAAAAAACAgAAAAAggAAAABAIAAAAAAAAAAAAEAAA=
- ConsoleDrivers\V2\IInputProcessor.cs
+ Drivers\V2\IInputProcessor.cs
-
+
AAAAAAAAAAIAACAAAAAAAIBAAAAAAACAAAAAAAAAAAA=
- ConsoleDrivers\AnsiResponseParser\IHeld.cs
+ Drivers\AnsiResponseParser\IHeld.cs
-
+
AAAAQAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAJAAAAAA=
- ConsoleDrivers\AnsiResponseParser\IAnsiResponseParser.cs
+ Drivers\AnsiResponseParser\IAnsiResponseParser.cs
-
-
-
- AAAAAAAAAAAIAgQQAAAAAQAAAAAAAAAAAAAKgAAAAAI=
- Application\IApplication.cs
-
-
-
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQIAAAAAAAA=
- ConsoleDrivers\V2\IMainLoopCoordinator.cs
+ Drivers\V2\IMainLoopCoordinator.cs
-
+
AAAAAAAAAAAAAAAEAAAAAAAAAAAACAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\IWindowSizeMonitor.cs
-
-
-
-
-
-
-
-
- BAAAIAAAAQAAAAAQACAAAIBAAQAAAAAAAAAIgAAAAAA=
- Application\ITimedEvents.cs
+ Drivers\V2\IWindowSizeMonitor.cs
-
+
AAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\IKeyConverter.cs
+ Drivers\V2\IKeyConverter.cs
-
+
AIAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\IToplevelTransitionManager.cs
+ Drivers\V2\IToplevelTransitionManager.cs
-
+
AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\IConsoleDriverFacade.cs
+ Drivers\V2\IConsoleDriverFacade.cs
-
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\INetInput.cs
+ Drivers\V2\INetInput.cs
-
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- ConsoleDrivers\V2\IWindowsInput.cs
+ Drivers\V2\IWindowsInput.cs
+
+
+
+
+
+
+
+
+ CAIAAAAAAQAAAAAAAAAABEAAAAAABAAAAAAAAAAAAAA=
+ App\ITimedEvents.cs
+
+
+
+
+
+ AAAgAAAAAAAIAgQUAAAAAQAAAAAAAAAAAAAKAAAAEAI=
+ App\IApplication.cs
-
+
AAAAAAAAAAAAAAAAAAAACAAAAAAIAAIAAAAAAAAAAAA=
- ConsoleDrivers\AnsiResponseParser\AnsiResponseParserState.cs
+ Drivers\AnsiResponseParser\AnsiResponseParserState.cs
diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsDriver.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsDriver.cs
index 3683f0231c..59941924fc 100644
--- a/Terminal.Gui/Drivers/WindowsDriver/WindowsDriver.cs
+++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsDriver.cs
@@ -895,51 +895,6 @@ private async Task ProcessButtonDoubleClickedAsync ()
//buttonPressedCount = 0;
}
- private async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag)
- {
- // When a user presses-and-holds, start generating pressed events every `startDelay`
- // After `iterationsUntilFast` iterations, speed them up to `fastDelay` ms
- const int START_DELAY = 500;
- const int ITERATIONS_UNTIL_FAST = 4;
- const int FAST_DELAY = 50;
-
- int iterations = 0;
- int delay = START_DELAY;
- while (_isButtonPressed)
- {
- // TODO: This makes IConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
- View? view = Application.WantContinuousButtonPressedView;
-
- if (view is null)
- {
- break;
- }
-
- if (iterations++ >= ITERATIONS_UNTIL_FAST)
- {
- delay = FAST_DELAY;
- }
- await Task.Delay (delay);
-
- //Debug.WriteLine($"ProcessContinuousButtonPressedAsync: {view}");
- if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0)
- {
- Point pointMove = _pointMove;
- // TODO: This makes IConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
- Application.Invoke (() =>
- {
- var me = new MouseEventArgs
- {
- ScreenPosition = pointMove,
- Position = pointMove,
- Flags = mouseFlag
- };
- OnMouseEvent (me);
- });
- }
- }
- }
-
private void ResizeScreen ()
{
_outputBuffer = new WindowsConsole.ExtendedCharInfo [Rows * Cols];
@@ -990,7 +945,7 @@ private MouseEventArgs ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent
if (_isButtonDoubleClicked || _isOneFingerDoubleClicked)
{
// TODO: This makes IConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
- Application.MainLoop!.AddIdle (
+ Application.MainLoop!.TimedEvents.Add (TimeSpan.Zero,
() =>
{
Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
@@ -1058,18 +1013,6 @@ private MouseEventArgs ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent
_lastMouseButtonPressed = mouseEvent.ButtonState;
_isButtonPressed = true;
-
- if ((mouseFlag & MouseFlags.ReportMousePosition) == 0)
- {
- // TODO: This makes IConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
- Application.MainLoop!.AddIdle (
- () =>
- {
- Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag));
-
- return false;
- });
- }
}
else if (_lastMouseButtonPressed != null
&& mouseEvent.EventFlags == 0
diff --git a/Terminal.Gui/Drivers/WindowsDriver/WindowsMainLoop.cs b/Terminal.Gui/Drivers/WindowsDriver/WindowsMainLoop.cs
index 0e10a99b9a..73308ae85e 100644
--- a/Terminal.Gui/Drivers/WindowsDriver/WindowsMainLoop.cs
+++ b/Terminal.Gui/Drivers/WindowsDriver/WindowsMainLoop.cs
@@ -73,7 +73,7 @@ bool IMainLoopDriver.EventsPending ()
#if HACK_CHECK_WINCHANGED
_winChange.Set ();
#endif
- if (_resultQueue.Count > 0 || _mainLoop!.TimedEvents.CheckTimersAndIdleHandlers (out int waitTimeout))
+ if (_resultQueue.Count > 0 || _mainLoop!.TimedEvents.CheckTimers (out int waitTimeout))
{
return true;
}
@@ -102,9 +102,9 @@ bool IMainLoopDriver.EventsPending ()
if (!_eventReadyTokenSource.IsCancellationRequested)
{
#if HACK_CHECK_WINCHANGED
- return _resultQueue.Count > 0 || _mainLoop.TimedEvents.CheckTimersAndIdleHandlers (out _) || _winChanged;
+ return _resultQueue.Count > 0 || _mainLoop.TimedEvents.CheckTimers (out _) || _winChanged;
#else
- return _resultQueue.Count > 0 || _mainLoop.TimedEvents.CheckTimersAndIdleHandlers (out _);
+ return _resultQueue.Count > 0 || _mainLoop.TimedEvents.CheckTimers (out _);
#endif
}
diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj
index b78fc2e16b..15d5869758 100644
--- a/Terminal.Gui/Terminal.Gui.csproj
+++ b/Terminal.Gui/Terminal.Gui.csproj
@@ -62,9 +62,7 @@
-
+
@@ -205,7 +203,7 @@
-
-
+
+
diff --git a/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs b/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs
index a76005138d..546c871c75 100644
--- a/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs
+++ b/Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs
@@ -431,9 +431,9 @@ private void ApplicationOnMouseEvent (object? sender, MouseEventArgs e)
Application.MouseEvent -= ApplicationOnMouseEvent;
- if (Application.MouseGrabView == this && _dragPosition.HasValue)
+ if (Application.MouseGrabHandler.MouseGrabView == this && _dragPosition.HasValue)
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
// Clean up all arrangement buttons
@@ -498,7 +498,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
// Set the start grab point to the Frame coords
_startGrabPoint = new (mouseEvent.Position.X + Frame.X, mouseEvent.Position.Y + Frame.Y);
_dragPosition = mouseEvent.Position;
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
// Determine the mode based on where the click occurred
ViewArrangement arrangeMode = DetermineArrangeModeFromClick ();
@@ -511,7 +511,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
return true;
}
- if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && Application.MouseGrabView == this)
+ if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && Application.MouseGrabHandler.MouseGrabView == this)
{
if (_dragPosition.HasValue)
{
@@ -523,7 +523,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && _dragPosition.HasValue)
{
_dragPosition = null;
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
EndArrangeMode ();
@@ -763,7 +763,7 @@ internal void HandleDragOperation (MouseEventArgs mouseEvent)
private void Application_GrabbingMouse (object? sender, GrabMouseEventArgs e)
{
- if (Application.MouseGrabView == this && _dragPosition.HasValue)
+ if (Application.MouseGrabHandler.MouseGrabView == this && _dragPosition.HasValue)
{
e.Cancel = true;
}
@@ -771,7 +771,7 @@ private void Application_GrabbingMouse (object? sender, GrabMouseEventArgs e)
private void Application_UnGrabbingMouse (object? sender, GrabMouseEventArgs e)
{
- if (Application.MouseGrabView == this && _dragPosition.HasValue)
+ if (Application.MouseGrabHandler.MouseGrabView == this && _dragPosition.HasValue)
{
e.Cancel = true;
}
@@ -784,8 +784,8 @@ private void Application_UnGrabbingMouse (object? sender, GrabMouseEventArgs e)
///
protected override void Dispose (bool disposing)
{
- Application.GrabbingMouse -= Application_GrabbingMouse;
- Application.UnGrabbingMouse -= Application_UnGrabbingMouse;
+ Application.MouseGrabHandler.GrabbingMouse -= Application_GrabbingMouse;
+ Application.MouseGrabHandler.UnGrabbingMouse -= Application_UnGrabbingMouse;
_dragPosition = null;
base.Dispose (disposing);
diff --git a/Terminal.Gui/ViewBase/Adornment/Border.cs b/Terminal.Gui/ViewBase/Adornment/Border.cs
index 259a459987..b03645bb6f 100644
--- a/Terminal.Gui/ViewBase/Adornment/Border.cs
+++ b/Terminal.Gui/ViewBase/Adornment/Border.cs
@@ -50,8 +50,8 @@ public Border (View parent) : base (parent)
CanFocus = false;
TabStop = TabBehavior.TabGroup;
- Application.GrabbingMouse += Application_GrabbingMouse;
- Application.UnGrabbingMouse += Application_UnGrabbingMouse;
+ Application.MouseGrabHandler.GrabbingMouse += Application_GrabbingMouse;
+ Application.MouseGrabHandler.UnGrabbingMouse += Application_UnGrabbingMouse;
ThicknessChanged += OnThicknessChanged;
}
diff --git a/Terminal.Gui/ViewBase/IMouseHeldDown.cs b/Terminal.Gui/ViewBase/IMouseHeldDown.cs
new file mode 100644
index 0000000000..5f7435793b
--- /dev/null
+++ b/Terminal.Gui/ViewBase/IMouseHeldDown.cs
@@ -0,0 +1,35 @@
+#nullable enable
+using System.ComponentModel;
+
+namespace Terminal.Gui.ViewBase;
+
+///
+///
+/// Handler for raising periodic events while the mouse is held down.
+/// Typically, mouse button only needs to be pressed down in a view
+/// to begin this event after which it can be moved elsewhere.
+///
+///
+/// Common use cases for this includes holding a button down to increase
+/// a counter (e.g. in ).
+///
+///
+public interface IMouseHeldDown : IDisposable
+{
+ ///
+ /// Periodically raised when the mouse is pressed down inside the view .
+ ///
+ public event EventHandler MouseIsHeldDownTick;
+
+ ///
+ /// Call to indicate that the mouse has been pressed down and any relevant actions should
+ /// be undertaken (start timers, etc).
+ ///
+ void Start ();
+
+ ///
+ /// Call to indicate that the mouse has been released and any relevant actions should
+ /// be undertaken (stop timers, etc).
+ ///
+ void Stop ();
+}
diff --git a/Terminal.Gui/ViewBase/MouseHeldDown.cs b/Terminal.Gui/ViewBase/MouseHeldDown.cs
new file mode 100644
index 0000000000..ff0764733a
--- /dev/null
+++ b/Terminal.Gui/ViewBase/MouseHeldDown.cs
@@ -0,0 +1,111 @@
+#nullable enable
+using System.ComponentModel;
+
+namespace Terminal.Gui.ViewBase;
+
+///
+/// INTERNAL: Manages the logic for handling a "mouse held down" state on a View. It is used to
+/// repeatedly trigger an action (via events) while the mouse button is held down, such as for auto-repeat in
+/// scrollbars or buttons.
+///
+internal class MouseHeldDown : IMouseHeldDown
+{
+ public MouseHeldDown (View host, ITimedEvents? timedEvents, IMouseGrabHandler? mouseGrabber)
+ {
+ _mouseGrabView = host;
+ _timedEvents = timedEvents;
+ _mouseGrabber = mouseGrabber;
+ _smoothTimeout = new (TimeSpan.FromMilliseconds (500), TimeSpan.FromMilliseconds (50), 0.5, TickWhileMouseIsHeldDown);
+ }
+
+ private readonly View _mouseGrabView;
+ private readonly ITimedEvents? _timedEvents;
+ private readonly IMouseGrabHandler? _mouseGrabber;
+
+ private readonly SmoothAcceleratingTimeout _smoothTimeout;
+ private bool _isDown;
+ private object? _timeout;
+
+ public event EventHandler? MouseIsHeldDownTick;
+
+ public void Start ()
+ {
+ if (_isDown)
+ {
+ return;
+ }
+
+ _isDown = true;
+ _mouseGrabber?.GrabMouse (_mouseGrabView);
+
+ // Then periodic ticks
+ _timeout = _timedEvents?.Add (_smoothTimeout);
+ }
+
+ public void Stop ()
+ {
+ _smoothTimeout.Reset ();
+
+ if (_mouseGrabber?.MouseGrabView == _mouseGrabView)
+ {
+ _mouseGrabber?.UngrabMouse ();
+ }
+
+ if (_timeout != null)
+ {
+ _timedEvents?.Remove (_timeout);
+ }
+
+ _mouseGrabView.MouseState = MouseState.None;
+ _isDown = false;
+ }
+
+ public void Dispose ()
+ {
+ if (_mouseGrabber?.MouseGrabView == _mouseGrabView)
+ {
+ Stop ();
+ }
+ }
+
+ protected virtual bool OnMouseIsHeldDownTick (CancelEventArgs eventArgs) { return false; }
+
+ private bool RaiseMouseIsHeldDownTick ()
+ {
+ CancelEventArgs args = new ();
+
+ args.Cancel = OnMouseIsHeldDownTick (args) || args.Cancel;
+
+ if (!args.Cancel && MouseIsHeldDownTick is { })
+ {
+ MouseIsHeldDownTick?.Invoke (this, args);
+ }
+
+ // User event cancelled the mouse held down status so
+ // stop the currently running operation.
+ if (args.Cancel)
+ {
+ Stop ();
+ }
+
+ return args.Cancel;
+ }
+
+ private bool TickWhileMouseIsHeldDown ()
+ {
+ Logging.Debug ("Raising TickWhileMouseIsHeldDown...");
+
+ if (_isDown)
+ {
+ _smoothTimeout.AdvanceStage ();
+ RaiseMouseIsHeldDownTick ();
+ }
+ else
+ {
+ _smoothTimeout.Reset ();
+ Stop ();
+ }
+
+ return _isDown;
+ }
+}
diff --git a/Terminal.Gui/ViewBase/View.Mouse.cs b/Terminal.Gui/ViewBase/View.Mouse.cs
index a59513ef01..47de711d59 100644
--- a/Terminal.Gui/ViewBase/View.Mouse.cs
+++ b/Terminal.Gui/ViewBase/View.Mouse.cs
@@ -5,11 +5,18 @@ namespace Terminal.Gui.ViewBase;
public partial class View // Mouse APIs
{
+ ///
+ /// Handles , we have detected a button
+ /// down in the view and have grabbed the mouse.
+ ///
+ public IMouseHeldDown? MouseHeldDown { get; set; }
+
/// Gets the mouse bindings for this view.
public MouseBindings MouseBindings { get; internal set; } = null!;
private void SetupMouse ()
{
+ MouseHeldDown = new MouseHeldDown (this, Application.TimedEvents,Application.MouseGrabHandler);
MouseBindings = new ();
// TODO: Should the default really work with any button or just button1?
@@ -307,6 +314,19 @@ protected virtual void OnMouseLeave () { }
/// , if the event was handled, otherwise.
public bool RaiseMouseEvent (MouseEventArgs mouseEvent)
{
+ // TODO: probably this should be moved elsewhere, please advise
+ if (WantContinuousButtonPressed && MouseHeldDown != null)
+ {
+ if (mouseEvent.IsPressed)
+ {
+ MouseHeldDown.Start ();
+ }
+ else
+ {
+ MouseHeldDown.Stop ();
+ }
+ }
+
if (OnMouseEvent (mouseEvent) || mouseEvent.Handled)
{
return true;
@@ -355,7 +375,7 @@ internal bool WhenGrabbedHandleReleased (MouseEventArgs mouseEvent)
if (mouseEvent.IsReleased)
{
- if (Application.MouseGrabView == this)
+ if (Application.MouseGrabHandler.MouseGrabView == this)
{
//Logging.Debug ($"{Id} - {MouseState}");
MouseState &= ~MouseState.Pressed;
@@ -387,9 +407,9 @@ private bool WhenGrabbedHandlePressed (MouseEventArgs mouseEvent)
if (mouseEvent.IsPressed)
{
// The first time we get pressed event, grab the mouse and set focus
- if (Application.MouseGrabView != this)
+ if (Application.MouseGrabHandler.MouseGrabView != this)
{
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
if (!HasFocus && CanFocus)
{
@@ -425,11 +445,6 @@ private bool WhenGrabbedHandlePressed (MouseEventArgs mouseEvent)
}
}
- if (WantContinuousButtonPressed && Application.MouseGrabView == this)
- {
- return RaiseMouseClickEvent (mouseEvent);
- }
-
return mouseEvent.Handled = true;
}
@@ -526,10 +541,10 @@ internal bool WhenGrabbedHandleClicked (MouseEventArgs mouseEvent)
{
mouseEvent.Handled = false;
- if (Application.MouseGrabView == this && mouseEvent.IsSingleClicked)
+ if (Application.MouseGrabHandler.MouseGrabView == this && mouseEvent.IsSingleClicked)
{
// We're grabbed. Clicked event comes after the last Release. This is our signal to ungrab
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
// TODO: Prove we need to unset MouseState.Pressed and MouseState.PressedOutside here
// TODO: There may be perf gains if we don't unset these flags here
@@ -680,4 +695,4 @@ protected virtual void OnMouseStateChanged (EventArgs args) { }
#endregion MouseState Handling
private void DisposeMouse () { }
-}
+}
\ No newline at end of file
diff --git a/Terminal.Gui/ViewBase/View.cs b/Terminal.Gui/ViewBase/View.cs
index 3dd4c9c994..2061ef4828 100644
--- a/Terminal.Gui/ViewBase/View.cs
+++ b/Terminal.Gui/ViewBase/View.cs
@@ -71,14 +71,9 @@ protected virtual void Dispose (bool disposing)
DisposeAdornments ();
DisposeScrollBars ();
- if (Application.MouseGrabView == this)
+ if (Application.MouseGrabHandler.MouseGrabView == this)
{
- Application.UngrabMouse ();
- }
-
- if (Application.WantContinuousButtonPressedView == this)
- {
- Application.WantContinuousButtonPressedView = null;
+ Application.MouseGrabHandler.UngrabMouse ();
}
for (int i = InternalSubViews.Count - 1; i >= 0; i--)
diff --git a/Terminal.Gui/Views/Autocomplete/Autocomplete.cd b/Terminal.Gui/Views/Autocomplete/Autocomplete.cd
index ca47d6435b..8b0f16b4f0 100644
--- a/Terminal.Gui/Views/Autocomplete/Autocomplete.cd
+++ b/Terminal.Gui/Views/Autocomplete/Autocomplete.cd
@@ -1,13 +1,13 @@
-
+
AAAgAABAAQIAAAAAAAAAAAAABAAAIAQAgAEIAggAIAA=
Core\Autocomplete\AppendAutocomplete.cs
-
+
AAQgAAAAAUAAIAAAAAIAAAAAAAEAIAQIgQAIQAAAMBA=
@@ -15,10 +15,10 @@
-
+
-
+
Core\Autocomplete\PopupAutocomplete.cs
@@ -29,7 +29,7 @@
Core\Autocomplete\PopupAutocomplete.cs
-
+
CEAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAIAA=
@@ -37,28 +37,28 @@
-
+
AAAAAAAAAAAAAEAAAAAABAAAAAAAAAAAAAAAAAAAAAE=
Core\Autocomplete\Suggestion.cs
-
+
AAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAgAAAAAAAAAA=
Views\TextField.cs
-
+
AAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAgAAAAAAAAAA=
Views\TextView.cs
-
+
AAQgAAAAAUAAIAAAAAAAAAAAAAEAIAQIgQAIQAAAMBA=
@@ -68,7 +68,7 @@
-
+
AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA=
diff --git a/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs b/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs
index 1c977715e9..2c07f1abab 100644
--- a/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs
+++ b/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs
@@ -125,7 +125,7 @@ public override bool OnMouseEvent (MouseEventArgs me, bool fromHost = false)
{
Visible = true;
HostControl?.SetNeedsDraw ();
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
return false;
}
diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs
index 894af07d68..942e97b118 100644
--- a/Terminal.Gui/Views/Button.cs
+++ b/Terminal.Gui/Views/Button.cs
@@ -69,6 +69,11 @@ public Button ()
base.ShadowStyle = DefaultShadow;
HighlightStates = DefaultHighlightStates;
+
+ if (MouseHeldDown != null)
+ {
+ MouseHeldDown.MouseIsHeldDownTick += (_,_) => RaiseAccepting (null);
+ }
}
private bool? HandleHotKeyCommand (ICommandContext commandContext)
diff --git a/Terminal.Gui/Views/CharMap/CharMap.cs b/Terminal.Gui/Views/CharMap/CharMap.cs
index d62e9df930..683ee0601a 100644
--- a/Terminal.Gui/Views/CharMap/CharMap.cs
+++ b/Terminal.Gui/Views/CharMap/CharMap.cs
@@ -767,7 +767,7 @@ private void ShowDetails ()
}
// BUGBUG: This is a workaround for some weird ScrollView related mouse grab bug
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
}
#endregion Details Dialog
diff --git a/Terminal.Gui/Views/CollectionNavigation/CollectionNavigation.cd b/Terminal.Gui/Views/CollectionNavigation/CollectionNavigation.cd
index 7236c3adfe..02bb4adb16 100644
--- a/Terminal.Gui/Views/CollectionNavigation/CollectionNavigation.cd
+++ b/Terminal.Gui/Views/CollectionNavigation/CollectionNavigation.cd
@@ -9,7 +9,7 @@
-
+
AAgEAAAAAAAQAAAIAAEAAgAAAAAABAAEAAAAACwAAAA=
@@ -20,7 +20,7 @@
-
+
AAAAAAAAAAAAQAAAAAAAAgAAAAAAAAAEAAAAAAAAAAA=
@@ -28,7 +28,7 @@
-
+
AAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAQA=
@@ -36,14 +36,14 @@
-
+
AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAEAAAAIAAAAAA=
Views\CollectionNavigation\TableCollectionNavigator.cs
-
+
AAE+ASAkEnAAABAAKGAggYAZJAIAABEAcBAaAwAQIAA=
@@ -54,25 +54,7 @@
-
-
-
-
-
-
- iIY4LQFUHDKVIHIESBgigQcFT6GxhBDABGJItBQAwAQ=
- Views\FileDialog.cs
-
-
-
-
-
-
- AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAA=
- Views\FileDialogCollectionNavigator.cs
-
-
-
+
QwUeAxwgICIAcABIABeR0oBAkhoFGGOBDABgAN3oPEI=
@@ -80,7 +62,7 @@
-
+
AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAA=
@@ -88,7 +70,7 @@
-
+
UwAGySBgBSBGMAQgIiCaBDUItJIBSAWwRMQOSgQCwJI=
@@ -99,21 +81,39 @@
-
+
+
+
+
+
+
+ iIY4LQFUHDKVIHIESBoigQcFT6GxhBDABGJItBQAwAQ=
+ Views\FileDialogs\FileDialog.cs
+
+
+
+
+
+
+ AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAA=
+ Views\FileDialogs\FileDialogCollectionNavigator.cs
+
+
+
AAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAA=
Views\CollectionNavigation\ICollectionNavigatorMatcher.cs
-
+
AAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
Views\CollectionNavigation\IListCollectionNavigator.cs
-
+
AAgAAAAAAAAAAAAIAAAAAAAAAAAABAAAAAAAACgAAAA=
diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs
index 158c7cf23c..369de9e8a6 100644
--- a/Terminal.Gui/Views/ComboBox.cs
+++ b/Terminal.Gui/Views/ComboBox.cs
@@ -958,7 +958,7 @@ protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View pr
{
_isFocusing = true;
_highlighted = _container.SelectedItem;
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
}
}
else
@@ -967,7 +967,7 @@ protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View pr
{
_isFocusing = false;
_highlighted = _container.SelectedItem;
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
}
}
diff --git a/Terminal.Gui/Views/DatePicker.cs b/Terminal.Gui/Views/DatePicker.cs
index 2e2ffa4bc9..a3253f1221 100644
--- a/Terminal.Gui/Views/DatePicker.cs
+++ b/Terminal.Gui/Views/DatePicker.cs
@@ -227,13 +227,7 @@ private void SetInitialProperties (DateTime date)
NoDecorations = true,
ShadowStyle = ShadowStyle.None
};
-
- _previousMonthButton.Accepting += (sender, e) =>
- {
- Date = _date.AddMonths (-1);
- CreateCalendar ();
- _dateField.Date = Date;
- };
+ _previousMonthButton.Accepting += (_, _) => AdjustMonth (-1);
_nextMonthButton = new Button
{
@@ -248,12 +242,7 @@ private void SetInitialProperties (DateTime date)
ShadowStyle = ShadowStyle.None
};
- _nextMonthButton.Accepting += (sender, e) =>
- {
- Date = _date.AddMonths (1);
- CreateCalendar ();
- _dateField.Date = Date;
- };
+ _nextMonthButton.Accepting += (_, _) => AdjustMonth (1);
CreateCalendar ();
SelectDayOnCalendar (_date.Day);
@@ -287,6 +276,13 @@ private void SetInitialProperties (DateTime date)
Add (_dateLabel, _dateField, _calendar, _previousMonthButton, _nextMonthButton);
}
+ private void AdjustMonth (int offset)
+ {
+ Date = _date.AddMonths (offset);
+ CreateCalendar ();
+ _dateField.Date = Date;
+ }
+
///
protected override bool OnDrawingText () { return true; }
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialog.cd b/Terminal.Gui/Views/FileDialogs/FileDialog.cd
index a8174bf7e7..22893eb03a 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialog.cd
+++ b/Terminal.Gui/Views/FileDialogs/FileDialog.cd
@@ -1,6 +1,21 @@
-
+
+
+
+ AAACAAAAAAgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+ FileServices\DefaultSearchMatcher.cs
+
+
+
+
+
+
+ ABAIQAIIIAAAAAACQAAAAIQAAAQAAIAAAQAAAAAIAAI=
+ FileServices\FileSystemInfoStats.cs
+
+
+
@@ -8,190 +23,146 @@
-
-
- Windows\FileDialog.cs
-
-
-
+
- Views\FileDialog.cs
+ Views\FileDialogs\FileDialog.cs
- g4YYDAEXEDKZgHMFyFAikQCFSKUQhRDABqJIlBSAwgw=
- Views\FileDialog.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
- AAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAA=
- FileServices\AllowedType.cs
+ iIY4LQFUHDKVIHIESBoigQcFT6GxhBDABGJItBQAwAQ=
+ Views\FileDialogs\FileDialog.cs
-
-
-
- AAAAAAAAAAAgAAAEAAAAAAAAAAAAAAAAAAgAAAAABAA=
- FileServices\AllowedType.cs
-
-
-
-
-
+
+
- AAAAAAAAAAAAACAAAAAAACAAAAEAAAAAAAAAAAAAgAA=
- FileServices\DefaultFileOperations.cs
+ GgBAAAFHAAAAuAAAAAAAEAQQBYAAKREAAAAYQCCAAAA=
+ Views\FileDialogs\FileDialogStyle.cs
-
-
-
+
+
+
+
+
- AAACAAAAAAgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- FileServices\DefaultSearchMatcher.cs
+ AAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA=
+ Views\FileDialogs\FilesSelectedEventArgs.cs
-
-
+
AQABAgEAAAAAAAAAIACAAAAAAAAAAQAAAAAAAAAADAI=
- FileServices\FileDialogHistory.cs
+ Views\FileDialogs\FileDialogHistory.cs
-
-
-
-
-
-
- AAAAEAAAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAA=
- FileServices\FileDialogRootTreeNode.cs
-
-
-
+
AABAABAAAAAAAAIAAAAEQAAAAAAAQAAAAgAAAAAAAAI=
- FileServices\FileDialogState.cs
+ Views\FileDialogs\FileDialogState.cs
-
-
+
+
- GgBIAAFEAAAAuAAAAgAEEASABQACKRkAAAEYACCAAAA=
- FileServices\FileDialogStyle.cs
+ AAAAAAAAAAAgAAAEAAAAAAAAAAAAAAAAAAgAAAAABAA=
+ Views\FileDialogs\AllowedType.cs
+
-
-
+
+
- EAAACAAAAAAAAAAAQAAAAAQAAAAAQAAAAAAAAAQACAA=
- FileServices\FileDialogTreeBuilder.cs
+ AAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAA=
+ Views\FileDialogs\AllowedType.cs
-
-
+
+
-
+
- AAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAA=
- FileServices\FilesSelectedEventArgs.cs
+ AIACAAABQAAAAAAAAAAACAAAIACAAAAAAAIAAAAAAAA=
+ Text\NerdFonts.cs
-
-
+
+
- ABAIQAIIIAAAAAACQAAAAIQAAAQAAIAAAQAAAAAIAAI=
- FileServices\FileSystemInfoStats.cs
+ EAAAAAAAAAAAAAAAAAAABAAwAAAAQAAAAABAAAAACAA=
+ FileServices\FileSystemTreeBuilder.cs
+
-
-
-
-
-
+
+
- AIACAAABQAAAAAAAAAAAAAAAIACAAAAAAAIAAAQAAAA=
- Views\NerdFonts.cs
+ AAAAAAAAAAAAACAAAAAAACAAAAEAAAAAAAAAAAAAgAA=
+ Views\FileDialogs\DefaultFileOperations.cs
+
-
-
+
+
- AAAAAAAAAgAAAAAAAAAQAAAAAAAEAAAAAAAAAAAAAAA=
- FileServices\FileDialogIconGetterArgs.cs
+ AgAAAAAAAEAAAAAAAAAAAAEAAAAAAACAAAAAAAAAAAA=
+ FileServices\FileSystemColorProvider.cs
-
-
+
+
+
+ ABAAAAAAAACAQAAAAAAAAEAgAAAAAQAAAAAAAAAAAiA=
+ FileServices\FileSystemIconProvider.cs
+
+
+
+
AQAAAAAAIAACAEAACAAAAAACAAAEAAAEAAAAgAgBBAA=
- Views\FileDialogTableSource.cs
+ Views\FileDialogs\FileDialogTableSource.cs
-
-
-
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA=
- FileServices\AllowedType.cs
-
-
-
+
AAAAAAAAAAAAACAAAAAAAAAAAAEAAAAAAAAAAAAAgAA=
FileServices\IFileOperations.cs
-
+
AAACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
FileServices\ISearchMatcher.cs
-
-
+
+
- AAAAABAAAAAAACAAAAAAAAAAAAAEAAAAAAAAAAAAAAA=
- Views\OpenDialog.cs
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA=
+ Views\FileDialogs\AllowedType.cs
-
-
-
+
+
+
- AAAAAAAAAACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAA=
- FileServices\FileDialogIconGetterContext.cs
+ AAAAABAAAAAAACAAAAAAAAAAAAAEAAAAAAAAAAAAAAA=
+ Views\FileDialogs\OpenMode.cs
-
-
-
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAA=
- FileServices\FileDialogRootTreeNode.cs
-
-
\ No newline at end of file
diff --git a/Terminal.Gui/Views/Menuv1/Menu.cs b/Terminal.Gui/Views/Menuv1/Menu.cs
index 54bd6ed6d1..8969595acd 100644
--- a/Terminal.Gui/Views/Menuv1/Menu.cs
+++ b/Terminal.Gui/Views/Menuv1/Menu.cs
@@ -19,7 +19,7 @@ public Menu ()
}
Application.MouseEvent += Application_RootMouseEvent;
- Application.UnGrabbedMouse += Application_UnGrabbedMouse;
+ Application.MouseGrabHandler.UnGrabbedMouse += Application_UnGrabbedMouse;
// Things this view knows how to do
AddCommand (Command.Up, () => MoveUp ());
@@ -220,7 +220,7 @@ public void Run (Action? action)
return;
}
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
_host.CloseAllMenus ();
Application.LayoutAndDraw (true);
@@ -238,7 +238,7 @@ protected override void Dispose (bool disposing)
}
Application.MouseEvent -= Application_RootMouseEvent;
- Application.UnGrabbedMouse -= Application_UnGrabbedMouse;
+ Application.MouseGrabHandler.UnGrabbedMouse -= Application_UnGrabbedMouse;
base.Dispose (disposing);
}
@@ -535,7 +535,7 @@ private void Application_UnGrabbedMouse (object? sender, ViewEventArgs a)
private void CloseAllMenus ()
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
_host.CloseAllMenus ();
}
diff --git a/Terminal.Gui/Views/Menuv1/MenuBar.cs b/Terminal.Gui/Views/Menuv1/MenuBar.cs
index b9d233e70e..615241e579 100644
--- a/Terminal.Gui/Views/Menuv1/MenuBar.cs
+++ b/Terminal.Gui/Views/Menuv1/MenuBar.cs
@@ -442,12 +442,12 @@ out OpenCurrentMenu._currentChild
if (_isContextMenuLoading)
{
- Application.GrabMouse (_openMenu);
+ Application.MouseGrabHandler.GrabMouse (_openMenu);
_isContextMenuLoading = false;
}
else
{
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
}
}
@@ -524,16 +524,16 @@ internal void CleanUp ()
SetNeedsDraw ();
- if (Application.MouseGrabView is { } && Application.MouseGrabView is MenuBar && Application.MouseGrabView != this)
+ if (Application.MouseGrabHandler.MouseGrabView is { } && Application.MouseGrabHandler.MouseGrabView is MenuBar && Application.MouseGrabHandler.MouseGrabView != this)
{
- var menuBar = Application.MouseGrabView as MenuBar;
+ var menuBar = Application.MouseGrabHandler.MouseGrabView as MenuBar;
if (menuBar!.IsMenuOpen)
{
menuBar.CleanUp ();
}
}
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
_isCleaning = false;
}
@@ -556,7 +556,7 @@ internal void CloseAllMenus ()
_selected = -1;
}
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
if (OpenCurrentMenu is { })
@@ -622,9 +622,9 @@ internal bool CloseMenu (bool reopen, bool isSubMenu, bool ignoreUseSubMenusSing
_previousFocused.SetFocus ();
}
- if (Application.MouseGrabView == _openMenu)
+ if (Application.MouseGrabHandler.MouseGrabView == _openMenu)
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
_openMenu?.Dispose ();
_openMenu = null;
@@ -652,9 +652,9 @@ internal bool CloseMenu (bool reopen, bool isSubMenu, bool ignoreUseSubMenusSing
if (OpenCurrentMenu is { })
{
SuperView?.Remove (OpenCurrentMenu);
- if (Application.MouseGrabView == OpenCurrentMenu)
+ if (Application.MouseGrabHandler.MouseGrabView == OpenCurrentMenu)
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
OpenCurrentMenu.Dispose ();
OpenCurrentMenu = null;
@@ -845,9 +845,9 @@ internal void OpenMenu (int index, int sIndex = -1, MenuBarItem? subMenu = null!
if (_openMenu is { })
{
SuperView?.Remove (_openMenu);
- if (Application.MouseGrabView == _openMenu)
+ if (Application.MouseGrabHandler.MouseGrabView == _openMenu)
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
_openMenu.Dispose ();
_openMenu = null;
@@ -935,7 +935,7 @@ internal void OpenMenu (int index, int sIndex = -1, MenuBarItem? subMenu = null!
Host = this, X = first!.Frame.Left, Y = first.Frame.Top, BarItems = newSubMenu
};
last!.Visible = false;
- Application.GrabMouse (OpenCurrentMenu);
+ Application.MouseGrabHandler.GrabMouse (OpenCurrentMenu);
}
OpenCurrentMenu._previousSubFocused = last._previousSubFocused;
@@ -1029,9 +1029,9 @@ internal void RemoveAllOpensSubMenus ()
foreach (Menu item in _openSubMenu)
{
SuperView?.Remove (item);
- if (Application.MouseGrabView == item)
+ if (Application.MouseGrabHandler.MouseGrabView == item)
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
item.Dispose ();
}
@@ -1045,7 +1045,7 @@ internal bool Run (Action? action)
return false;
}
- Application.AddIdle (
+ Application.AddTimeout (TimeSpan.Zero,
() =>
{
action ();
@@ -1137,7 +1137,7 @@ internal bool SelectItem (MenuItem? item)
return false;
}
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
CloseAllMenus ();
Application.LayoutAndDraw (true);
_openedByAltKey = true;
@@ -1209,15 +1209,15 @@ private bool ProcessMenu (int i, MenuBarItem mi)
Point screen = ViewportToScreen (new Point (0, i));
var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = mi };
menu.Run (mi.Action);
- if (Application.MouseGrabView == menu)
+ if (Application.MouseGrabHandler.MouseGrabView == menu)
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
menu.Dispose ();
}
else
{
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
_selected = i;
OpenMenu (i);
@@ -1280,9 +1280,9 @@ private void RemoveSubMenu (int index, bool ignoreUseSubMenusSingleFrame = false
SuperView!.Remove (menu);
_openSubMenu.Remove (menu);
- if (Application.MouseGrabView == menu)
+ if (Application.MouseGrabHandler.MouseGrabView == menu)
{
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
}
menu.Dispose ();
@@ -1458,9 +1458,9 @@ protected override bool OnMouseEvent (MouseEventArgs me)
Point screen = ViewportToScreen (new Point (0, i));
var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = Menus [i] };
menu.Run (Menus [i].Action);
- if (Application.MouseGrabView == menu)
+ if (Application.MouseGrabHandler.MouseGrabView == menu)
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
menu.Dispose ();
@@ -1535,7 +1535,7 @@ protected override bool OnMouseEvent (MouseEventArgs me)
internal bool HandleGrabView (MouseEventArgs me, View current)
{
- if (Application.MouseGrabView is { })
+ if (Application.MouseGrabHandler.MouseGrabView is { })
{
if (me.View is MenuBar or Menu)
{
@@ -1546,7 +1546,7 @@ internal bool HandleGrabView (MouseEventArgs me, View current)
if (me.Flags == MouseFlags.Button1Clicked)
{
mbar.CleanUp ();
- Application.GrabMouse (me.View);
+ Application.MouseGrabHandler.GrabMouse (me.View);
}
else
{
@@ -1556,10 +1556,10 @@ internal bool HandleGrabView (MouseEventArgs me, View current)
}
}
- if (Application.MouseGrabView != me.View)
+ if (Application.MouseGrabHandler.MouseGrabView != me.View)
{
View v = me.View;
- Application.GrabMouse (v);
+ Application.MouseGrabHandler.GrabMouse (v);
return true;
}
@@ -1567,7 +1567,7 @@ internal bool HandleGrabView (MouseEventArgs me, View current)
if (me.View != current)
{
View v = me.View;
- Application.GrabMouse (v);
+ Application.MouseGrabHandler.GrabMouse (v);
MouseEventArgs nme;
if (me.Position.Y > -1)
@@ -1599,7 +1599,7 @@ internal bool HandleGrabView (MouseEventArgs me, View current)
&& me.Flags != MouseFlags.ReportMousePosition
&& me.Flags != 0)
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
if (IsMenuOpen)
{
@@ -1625,11 +1625,11 @@ internal bool HandleGrabView (MouseEventArgs me, View current)
MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
)))
{
- Application.GrabMouse (current);
+ Application.MouseGrabHandler.GrabMouse (current);
}
else if (IsMenuOpen && (me.View is MenuBar || me.View is Menu))
{
- Application.GrabMouse (me.View);
+ Application.MouseGrabHandler.GrabMouse (me.View);
}
else
{
@@ -1645,7 +1645,7 @@ internal bool HandleGrabView (MouseEventArgs me, View current)
private MenuBar? GetMouseGrabViewInstance (View? view)
{
- if (view is null || Application.MouseGrabView is null)
+ if (view is null || Application.MouseGrabHandler.MouseGrabView is null)
{
return null;
}
@@ -1661,7 +1661,7 @@ internal bool HandleGrabView (MouseEventArgs me, View current)
hostView = ((Menu)view).Host;
}
- View grabView = Application.MouseGrabView;
+ View grabView = Application.MouseGrabHandler.MouseGrabView;
MenuBar? hostGrabView = null;
if (grabView is MenuBar bar)
diff --git a/Terminal.Gui/Views/ScrollBar/ScrollSlider.cs b/Terminal.Gui/Views/ScrollBar/ScrollSlider.cs
index c0558192ad..6cb7d5433a 100644
--- a/Terminal.Gui/Views/ScrollBar/ScrollSlider.cs
+++ b/Terminal.Gui/Views/ScrollBar/ScrollSlider.cs
@@ -307,9 +307,9 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
{
if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed) && _lastLocation == -1)
{
- if (Application.MouseGrabView != this)
+ if (Application.MouseGrabHandler.MouseGrabView != this)
{
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
_lastLocation = location;
}
}
@@ -333,9 +333,9 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
{
_lastLocation = -1;
- if (Application.MouseGrabView == this)
+ if (Application.MouseGrabHandler.MouseGrabView == this)
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
}
diff --git a/Terminal.Gui/Views/Slider/Slider.cs b/Terminal.Gui/Views/Slider/Slider.cs
index 4dff5965ea..4808a939f6 100644
--- a/Terminal.Gui/Views/Slider/Slider.cs
+++ b/Terminal.Gui/Views/Slider/Slider.cs
@@ -1311,7 +1311,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
{
_dragPosition = mouseEvent.Position;
_moveRenderPosition = ClampMovePosition ((Point)_dragPosition);
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
}
SetNeedsDraw ();
@@ -1357,7 +1357,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked))
{
// End Drag
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
_dragPosition = null;
_moveRenderPosition = null;
diff --git a/Terminal.Gui/Views/TextInput/TextField.cs b/Terminal.Gui/Views/TextInput/TextField.cs
index 559b7d8a22..7d171c0936 100644
--- a/Terminal.Gui/Views/TextInput/TextField.cs
+++ b/Terminal.Gui/Views/TextInput/TextField.cs
@@ -855,16 +855,16 @@ protected override bool OnMouseEvent (MouseEventArgs ev)
_isButtonReleased = false;
PrepareSelection (x);
- if (Application.MouseGrabView is null)
+ if (Application.MouseGrabHandler.MouseGrabView is null)
{
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
}
}
else if (ev.Flags == MouseFlags.Button1Released)
{
_isButtonReleased = true;
_isButtonPressed = false;
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
else if (ev.Flags == MouseFlags.Button1DoubleClicked)
{
@@ -1007,12 +1007,12 @@ protected override bool OnDrawingContent ()
///
protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
{
- if (Application.MouseGrabView is { } && Application.MouseGrabView == this)
+ if (Application.MouseGrabHandler.MouseGrabView is { } && Application.MouseGrabHandler.MouseGrabView == this)
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
- //if (SelectedLength != 0 && !(Application.MouseGrabView is MenuBar))
+ //if (SelectedLength != 0 && !(Application.MouseGrabHandler.MouseGrabView is MenuBar))
// ClearAllSelection ();
}
diff --git a/Terminal.Gui/Views/TextInput/TextView.cs b/Terminal.Gui/Views/TextInput/TextView.cs
index 6d4d7b7c65..5450835d83 100644
--- a/Terminal.Gui/Views/TextInput/TextView.cs
+++ b/Terminal.Gui/Views/TextInput/TextView.cs
@@ -1677,15 +1677,15 @@ protected override bool OnMouseEvent (MouseEventArgs ev)
_lastWasKill = false;
_columnTrack = CurrentColumn;
- if (Application.MouseGrabView is null)
+ if (Application.MouseGrabHandler.MouseGrabView is null)
{
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
}
}
else if (ev.Flags.HasFlag (MouseFlags.Button1Released))
{
_isButtonReleased = true;
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
else if (ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked))
{
@@ -1893,9 +1893,9 @@ protected override bool OnDrawingContent ()
///
protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view)
{
- if (Application.MouseGrabView is { } && Application.MouseGrabView == this)
+ if (Application.MouseGrabHandler.MouseGrabView is { } && Application.MouseGrabHandler.MouseGrabView == this)
{
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
}
}
@@ -2039,7 +2039,7 @@ public void Paste ()
return null;
}
- if (Application.MouseGrabView == this && IsSelecting)
+ if (Application.MouseGrabHandler.MouseGrabView == this && IsSelecting)
{
// BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
//var minRow = Math.Min (Math.Max (Math.Min (selectionStartRow, currentRow) - topRow, 0), Viewport.Height);
diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs
index 6a993ae577..0e929ac960 100644
--- a/Terminal.Gui/Views/TileView.cs
+++ b/Terminal.Gui/Views/TileView.cs
@@ -916,7 +916,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
{
dragPosition = mouseEvent.Position;
dragOrignalPos = Orientation == Orientation.Horizontal ? Y : X;
- Application.GrabMouse (this);
+ Application.MouseGrabHandler.GrabMouse (this);
if (Orientation == Orientation.Horizontal)
{ }
@@ -960,7 +960,7 @@ protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
{
// End Drag
- Application.UngrabMouse ();
+ Application.MouseGrabHandler.UngrabMouse ();
//Driver.UncookMouse ();
FinalisePosition (
diff --git a/Tests/TerminalGuiFluentTesting/GuiTestContext.cs b/Tests/TerminalGuiFluentTesting/GuiTestContext.cs
index 773a4c97dd..6321a86cbc 100644
--- a/Tests/TerminalGuiFluentTesting/GuiTestContext.cs
+++ b/Tests/TerminalGuiFluentTesting/GuiTestContext.cs
@@ -88,7 +88,7 @@ internal GuiTestContext (Func topLevelBuilder, int width, int height,
}
// Wait for booting to complete with a timeout to avoid hangs
- if (!booting.WaitAsync (TimeSpan.FromSeconds (5)).Result)
+ if (!booting.WaitAsync (TimeSpan.FromSeconds (10)).Result)
{
throw new TimeoutException ("Application failed to start within the allotted time.");
}
diff --git a/Tests/UnitTests/Application/ApplicationTests.cs b/Tests/UnitTests/Application/ApplicationTests.cs
index 40d2ca5389..b504b94846 100644
--- a/Tests/UnitTests/Application/ApplicationTests.cs
+++ b/Tests/UnitTests/Application/ApplicationTests.cs
@@ -307,8 +307,7 @@ void CheckReset ()
// Public Properties
Assert.Null (Application.Top);
- Assert.Null (Application.MouseGrabView);
- Assert.Null (Application.WantContinuousButtonPressedView);
+ Assert.Null (Application.MouseGrabHandler.MouseGrabView);
// Don't check Application.ForceDriver
// Assert.Empty (Application.ForceDriver);
@@ -569,8 +568,7 @@ public void Internal_Properties_Correct ()
Assert.Null (Application.Top);
RunState rs = Application.Begin (new ());
Assert.Equal (Application.Top, rs.Toplevel);
- Assert.Null (Application.MouseGrabView); // public
- Assert.Null (Application.WantContinuousButtonPressedView); // public
+ Assert.Null (Application.MouseGrabHandler.MouseGrabView); // public
Application.Top!.Dispose ();
}
@@ -952,7 +950,7 @@ public void Run_A_Modal_Toplevel_Refresh_Background_On_Moving ()
Assert.Equal (new (0, 0), w.Frame.Location);
Application.RaiseMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
- Assert.Equal (w.Border, Application.MouseGrabView);
+ Assert.Equal (w.Border, Application.MouseGrabHandler.MouseGrabView);
Assert.Equal (new (0, 0), w.Frame.Location);
// Move down and to the right.
@@ -1116,6 +1114,8 @@ public void Run_t_Does_Not_Creates_Top_Without_Init ()
private class TestToplevel : Toplevel { }
+ private readonly object _forceDriverLock = new ();
+
[Theory]
[InlineData ("v2win", typeof (ConsoleDriverFacade))]
[InlineData ("v2net", typeof (ConsoleDriverFacade))]
@@ -1129,20 +1129,29 @@ public void Run_T_Call_Init_ForceDriver_Should_Pick_Correct_Driver (string drive
var result = false;
- Task.Run (() =>
- {
- Task.Delay (300).Wait ();
- }).ContinueWith (
- (t, _) =>
+ lock (_forceDriverLock)
+ {
+ Task.Run (() =>
+ {
+ while (!Application.Initialized)
+ {
+ Task.Delay (300).Wait ();
+ }
+ })
+ .ContinueWith (
+ (t, _) =>
+ {
+ // no longer loading
+ Assert.True (Application.Initialized);
+
+ Application.Invoke (() =>
{
- // no longer loading
- Application.Invoke (() =>
- {
- result = true;
- Application.RequestStop ();
- });
- },
- TaskScheduler.FromCurrentSynchronizationContext ());
+ result = true;
+ Application.RequestStop ();
+ });
+ },
+ TaskScheduler.FromCurrentSynchronizationContext ());
+ }
Application.ForceDriver = driverName;
Application.Run ();
diff --git a/Tests/UnitTests/Application/MainLoopTests.cs b/Tests/UnitTests/Application/MainLoopTests.cs
index b40f362b08..5c7866aa55 100644
--- a/Tests/UnitTests/Application/MainLoopTests.cs
+++ b/Tests/UnitTests/Application/MainLoopTests.cs
@@ -29,61 +29,38 @@ public class MainLoopTests
// Idle Handler tests
[Fact]
- public void AddIdle_Adds_And_Removes ()
+ public void AddTimeout_Adds_And_Removes ()
{
var ml = new MainLoop (new FakeMainLoop ());
Func fnTrue = () => true;
Func fnFalse = () => false;
- ml.AddIdle (fnTrue);
- ml.AddIdle (fnFalse);
+ var a = ml.TimedEvents.Add (TimeSpan.Zero, fnTrue);
+ var b = ml.TimedEvents.Add (TimeSpan.Zero, fnFalse);
- Assert.Equal (2, ml.TimedEvents.IdleHandlers.Count);
- Assert.Equal (fnTrue, ml.TimedEvents.IdleHandlers [0]);
- Assert.NotEqual (fnFalse, ml.TimedEvents.IdleHandlers [0]);
+ Assert.Equal (2, ml.TimedEvents.Timeouts.Count);
+ Assert.Equal (fnTrue, ml.TimedEvents.Timeouts.ElementAt (0).Value.Callback);
+ Assert.NotEqual (fnFalse, ml.TimedEvents.Timeouts.ElementAt (0).Value.Callback);
- Assert.True (ml.TimedEvents.RemoveIdle (fnTrue));
- Assert.Single (ml.TimedEvents.IdleHandlers);
+ Assert.True (ml.TimedEvents.Remove (a));
+ Assert.Single (ml.TimedEvents.Timeouts);
// BUGBUG: This doesn't throw or indicate an error. Ideally RemoveIdle would either
// throw an exception in this case, or return an error.
// No. Only need to return a boolean.
- Assert.False (ml.TimedEvents.RemoveIdle (fnTrue));
+ Assert.False (ml.TimedEvents.Remove (a));
- Assert.True (ml.TimedEvents.RemoveIdle (fnFalse));
+ Assert.True (ml.TimedEvents.Remove (b));
// BUGBUG: This doesn't throw an exception or indicate an error. Ideally RemoveIdle would either
// throw an exception in this case, or return an error.
// No. Only need to return a boolean.
- Assert.False (ml.TimedEvents.RemoveIdle (fnFalse));
-
- // Add again, but with dupe
- ml.AddIdle (fnTrue);
- ml.AddIdle (fnTrue);
-
- Assert.Equal (2, ml.TimedEvents.IdleHandlers.Count);
- Assert.Equal (fnTrue, ml.TimedEvents.IdleHandlers [0]);
- Assert.True (ml.TimedEvents.IdleHandlers [0] ());
- Assert.Equal (fnTrue, ml.TimedEvents.IdleHandlers [1]);
- Assert.True (ml.TimedEvents.IdleHandlers [1] ());
-
- Assert.True (ml.TimedEvents.RemoveIdle (fnTrue));
- Assert.Single (ml.TimedEvents.IdleHandlers);
- Assert.Equal (fnTrue, ml.TimedEvents.IdleHandlers [0]);
- Assert.NotEqual (fnFalse, ml.TimedEvents.IdleHandlers [0]);
-
- Assert.True (ml.TimedEvents.RemoveIdle (fnTrue));
- Assert.Empty (ml.TimedEvents.IdleHandlers);
-
- // BUGBUG: This doesn't throw an exception or indicate an error. Ideally RemoveIdle would either
- // throw an exception in this case, or return an error.
- // No. Only need to return a boolean.
- Assert.False (ml.TimedEvents.RemoveIdle (fnTrue));
+ Assert.False (ml.TimedEvents.Remove(b));
}
[Fact]
- public void AddIdle_Function_GetsCalled_OnIteration ()
+ public void AddTimeout_Function_GetsCalled_OnIteration ()
{
var ml = new MainLoop (new FakeMainLoop ());
@@ -96,13 +73,13 @@ public void AddIdle_Function_GetsCalled_OnIteration ()
return true;
};
- ml.AddIdle (fn);
+ ml.TimedEvents.Add (TimeSpan.Zero, fn);
ml.RunIteration ();
Assert.Equal (1, functionCalled);
}
[Fact]
- public void AddIdle_Twice_Returns_False_Called_Twice ()
+ public void AddTimeout_Twice_Returns_False_Called_Twice ()
{
var ml = new MainLoop (new FakeMainLoop ());
@@ -130,19 +107,21 @@ public void AddIdle_Twice_Returns_False_Called_Twice ()
return true;
};
- ml.AddIdle (fnStop);
- ml.AddIdle (fn1);
- ml.AddIdle (fn1);
+ var a = ml.TimedEvents.Add (TimeSpan.Zero, fnStop);
+ var b = ml.TimedEvents.Add (TimeSpan.Zero, fn1);
ml.Run ();
- Assert.True (ml.TimedEvents.RemoveIdle (fnStop));
- Assert.False (ml.TimedEvents.RemoveIdle (fn1));
- Assert.False (ml.TimedEvents.RemoveIdle (fn1));
- Assert.Equal (2, functionCalled);
+ Assert.True (ml.TimedEvents.Remove(a));
+ Assert.False (ml.TimedEvents.Remove (a));
+
+ // Cannot remove b because it returned false i.e. auto removes itself
+ Assert.False (ml.TimedEvents.Remove (b));
+
+ Assert.Equal (1, functionCalled);
}
[Fact]
- public void AddIdleTwice_Function_CalledTwice ()
+ public void AddTimeoutTwice_Function_CalledTwice ()
{
var ml = new MainLoop (new FakeMainLoop ());
@@ -155,24 +134,24 @@ public void AddIdleTwice_Function_CalledTwice ()
return true;
};
- ml.AddIdle (fn);
- ml.AddIdle (fn);
+ var a = ml.TimedEvents.Add (TimeSpan.Zero, fn);
+ var b = ml.TimedEvents.Add (TimeSpan.Zero, fn);
ml.RunIteration ();
Assert.Equal (2, functionCalled);
- Assert.Equal (2, ml.TimedEvents.IdleHandlers.Count);
+ Assert.Equal (2, ml.TimedEvents.Timeouts.Count);
functionCalled = 0;
- Assert.True (ml.TimedEvents.RemoveIdle (fn));
- Assert.Single (ml.TimedEvents.IdleHandlers);
+ Assert.True (ml.TimedEvents.Remove (a));
+ Assert.Single (ml.TimedEvents.Timeouts);
ml.RunIteration ();
Assert.Equal (1, functionCalled);
functionCalled = 0;
- Assert.True (ml.TimedEvents.RemoveIdle (fn));
- Assert.Empty (ml.TimedEvents.IdleHandlers);
+ Assert.True (ml.TimedEvents.Remove (b));
+ Assert.Empty (ml.TimedEvents.Timeouts);
ml.RunIteration ();
Assert.Equal (0, functionCalled);
- Assert.False (ml.TimedEvents.RemoveIdle (fn));
+ Assert.False (ml.TimedEvents.Remove (b));
}
[Fact]
@@ -189,8 +168,8 @@ public void AddThenRemoveIdle_Function_NotCalled ()
return true;
};
- ml.AddIdle (fn);
- Assert.True (ml.TimedEvents.RemoveIdle (fn));
+ var a = ml.TimedEvents.Add (TimeSpan.Zero, fn);
+ Assert.True (ml.TimedEvents.Remove (a));
ml.RunIteration ();
Assert.Equal (0, functionCalled);
}
@@ -211,13 +190,13 @@ public void AddTimer_Adds_Removes_NoFaults ()
return true;
};
- object token = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
+ object token = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback);
- Assert.True (ml.TimedEvents.RemoveTimeout (token));
+ Assert.True (ml.TimedEvents.Remove (token));
// BUGBUG: This should probably fault?
// Must return a boolean.
- Assert.False (ml.TimedEvents.RemoveTimeout (token));
+ Assert.False (ml.TimedEvents.Remove (token));
}
[Fact]
@@ -241,8 +220,8 @@ public async Task AddTimer_Duplicate_Keys_Not_Allowed ()
return true;
};
- var task1 = new Task (() => token1 = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback));
- var task2 = new Task (() => token2 = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback));
+ var task1 = new Task (() => token1 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback));
+ var task2 = new Task (() => token2 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback));
Assert.Null (token1);
Assert.Null (token2);
task1.Start ();
@@ -251,8 +230,8 @@ public async Task AddTimer_Duplicate_Keys_Not_Allowed ()
Assert.NotNull (token1);
Assert.NotNull (token2);
await Task.WhenAll (task1, task2);
- Assert.True (ml.TimedEvents.RemoveTimeout (token1));
- Assert.True (ml.TimedEvents.RemoveTimeout (token2));
+ Assert.True (ml.TimedEvents.Remove (token1));
+ Assert.True (ml.TimedEvents.Remove (token2));
Assert.Equal (2, callbackCount);
}
@@ -278,13 +257,13 @@ public void AddTimer_EventFired ()
object sender = null;
TimeoutEventArgs args = null;
- ml.TimedEvents.TimeoutAdded += (s, e) =>
+ ml.TimedEvents.Added += (s, e) =>
{
sender = s;
args = e;
};
- object token = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
+ object token = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback);
Assert.Same (ml.TimedEvents, sender);
Assert.NotNull (args.Timeout);
@@ -313,14 +292,14 @@ public void AddTimer_In_Parallel_Wont_Throw ()
};
Parallel.Invoke (
- () => token1 = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback),
- () => token2 = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback)
+ () => token1 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback),
+ () => token2 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback)
);
ml.Run ();
Assert.NotNull (token1);
Assert.NotNull (token2);
- Assert.True (ml.TimedEvents.RemoveTimeout (token1));
- Assert.True (ml.TimedEvents.RemoveTimeout (token2));
+ Assert.True (ml.TimedEvents.Remove (token1));
+ Assert.True (ml.TimedEvents.Remove (token2));
Assert.Equal (2, callbackCount);
}
@@ -345,7 +324,7 @@ public void AddTimer_Remove_NotCalled ()
return true;
};
- ml.AddIdle (fnStop);
+ ml.TimedEvents.Add (TimeSpan.Zero, fnStop);
var callbackCount = 0;
@@ -356,8 +335,8 @@ public void AddTimer_Remove_NotCalled ()
return true;
};
- object token = ml.TimedEvents.AddTimeout (ms, callback);
- Assert.True (ml.TimedEvents.RemoveTimeout (token));
+ object token = ml.TimedEvents.Add (ms, callback);
+ Assert.True (ml.TimedEvents.Remove (token));
ml.Run ();
Assert.Equal (0, callbackCount);
}
@@ -383,7 +362,7 @@ public void AddTimer_ReturnFalse_StopsBeingCalled ()
return true;
};
- ml.AddIdle (fnStop);
+ ml.TimedEvents.Add (TimeSpan.Zero, fnStop);
var callbackCount = 0;
@@ -394,11 +373,11 @@ public void AddTimer_ReturnFalse_StopsBeingCalled ()
return false;
};
- object token = ml.TimedEvents.AddTimeout (ms, callback);
+ object token = ml.TimedEvents.Add (ms, callback);
ml.Run ();
Assert.Equal (1, callbackCount);
Assert.Equal (10, stopCount);
- Assert.False (ml.TimedEvents.RemoveTimeout (token));
+ Assert.False (ml.TimedEvents.Remove (token));
}
[Fact]
@@ -417,9 +396,9 @@ public void AddTimer_Run_Called ()
return true;
};
- object token = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
+ object token = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback);
ml.Run ();
- Assert.True (ml.TimedEvents.RemoveTimeout (token));
+ Assert.True (ml.TimedEvents.Remove (token));
Assert.Equal (1, callbackCount);
}
@@ -442,7 +421,7 @@ public void AddTimer_Run_CalledAtApproximatelyRightTime ()
return true;
};
- object token = ml.TimedEvents.AddTimeout (ms, callback);
+ object token = ml.TimedEvents.Add (ms, callback);
watch.Start ();
ml.Run ();
@@ -450,7 +429,7 @@ public void AddTimer_Run_CalledAtApproximatelyRightTime ()
// https://github.com/xunit/assert.xunit/pull/25
Assert.Equal (ms * callbackCount, watch.Elapsed, new MillisecondTolerance (100));
- Assert.True (ml.TimedEvents.RemoveTimeout (token));
+ Assert.True (ml.TimedEvents.Remove (token));
Assert.Equal (1, callbackCount);
}
@@ -476,7 +455,7 @@ public void AddTimer_Run_CalledTwiceApproximatelyRightTime ()
return true;
};
- object token = ml.TimedEvents.AddTimeout (ms, callback);
+ object token = ml.TimedEvents.Add (ms, callback);
watch.Start ();
ml.Run ();
@@ -484,7 +463,7 @@ public void AddTimer_Run_CalledTwiceApproximatelyRightTime ()
// https://github.com/xunit/assert.xunit/pull/25
Assert.Equal (ms * callbackCount, watch.Elapsed, new MillisecondTolerance (100));
- Assert.True (ml.TimedEvents.RemoveTimeout (token));
+ Assert.True (ml.TimedEvents.Remove (token));
Assert.Equal (2, callbackCount);
}
@@ -492,7 +471,7 @@ public void AddTimer_Run_CalledTwiceApproximatelyRightTime ()
public void CheckTimersAndIdleHandlers_NoTimers_Returns_False ()
{
var ml = new MainLoop (new FakeMainLoop ());
- bool retVal = ml.TimedEvents.CheckTimersAndIdleHandlers (out int waitTimeOut);
+ bool retVal = ml.TimedEvents.CheckTimers(out int waitTimeOut);
Assert.False (retVal);
Assert.Equal (-1, waitTimeOut);
}
@@ -503,10 +482,10 @@ public void CheckTimersAndIdleHandlers_NoTimers_WithIdle_Returns_True ()
var ml = new MainLoop (new FakeMainLoop ());
Func fnTrue = () => true;
- ml.AddIdle (fnTrue);
- bool retVal = ml.TimedEvents.CheckTimersAndIdleHandlers (out int waitTimeOut);
+ ml.TimedEvents.Add (TimeSpan.Zero, fnTrue);
+ bool retVal = ml.TimedEvents.CheckTimers(out int waitTimeOut);
Assert.True (retVal);
- Assert.Equal (-1, waitTimeOut);
+ Assert.Equal (0, waitTimeOut);
}
[Fact]
@@ -517,8 +496,8 @@ public void CheckTimersAndIdleHandlers_With1Timer_Returns_Timer ()
static bool Callback () { return false; }
- _ = ml.TimedEvents.AddTimeout (ms, Callback);
- bool retVal = ml.TimedEvents.CheckTimersAndIdleHandlers (out int waitTimeOut);
+ _ = ml.TimedEvents.Add (ms, Callback);
+ bool retVal = ml.TimedEvents.CheckTimers (out int waitTimeOut);
Assert.True (retVal);
@@ -534,9 +513,9 @@ public void CheckTimersAndIdleHandlers_With2Timers_Returns_Timer ()
static bool Callback () { return false; }
- _ = ml.TimedEvents.AddTimeout (ms, Callback);
- _ = ml.TimedEvents.AddTimeout (ms, Callback);
- bool retVal = ml.TimedEvents.CheckTimersAndIdleHandlers (out int waitTimeOut);
+ _ = ml.TimedEvents.Add (ms, Callback);
+ _ = ml.TimedEvents.Add (ms, Callback);
+ bool retVal = ml.TimedEvents.CheckTimers (out int waitTimeOut);
Assert.True (retVal);
@@ -578,11 +557,11 @@ public void False_Idle_Stops_It_Being_Called_Again ()
return true;
};
- ml.AddIdle (fnStop);
- ml.AddIdle (fn1);
+ var a = ml.TimedEvents.Add (TimeSpan.Zero, fnStop);
+ var b = ml.TimedEvents.Add (TimeSpan.Zero, fn1);
ml.Run ();
- Assert.True (ml.TimedEvents.RemoveIdle (fnStop));
- Assert.False (ml.TimedEvents.RemoveIdle (fn1));
+ Assert.True (ml.TimedEvents.Remove (a));
+ Assert.False (ml.TimedEvents.Remove (a));
Assert.Equal (10, functionCalled);
Assert.Equal (20, stopCount);
@@ -594,7 +573,6 @@ public void Internal_Tests ()
var testMainloop = new TestMainloop ();
var mainloop = new MainLoop (testMainloop);
Assert.Empty (mainloop.TimedEvents.Timeouts);
- Assert.Empty (mainloop.TimedEvents.IdleHandlers);
Assert.NotNull (
new App.Timeout { Span = new (), Callback = () => true }
@@ -602,8 +580,8 @@ public void Internal_Tests ()
}
[Theory]
- [MemberData (nameof (TestAddIdle))]
- public void Mainloop_Invoke_Or_AddIdle_Can_Be_Used_For_Events_Or_Actions (
+ [MemberData (nameof (TestAddTimeout))]
+ public void Mainloop_Invoke_Or_AddTimeout_Can_Be_Used_For_Events_Or_Actions (
Action action,
string pclickMe,
string pcancel,
@@ -683,7 +661,7 @@ int pfour
Application.Shutdown ();
}
-
+
[Fact]
public void RemoveIdle_Function_NotCalled ()
{
@@ -698,7 +676,7 @@ public void RemoveIdle_Function_NotCalled ()
return true;
};
- Assert.False (ml.TimedEvents.RemoveIdle (fn));
+ Assert.False (ml.TimedEvents.Remove ("flibble"));
ml.RunIteration ();
Assert.Equal (0, functionCalled);
}
@@ -722,14 +700,14 @@ public void Run_Runs_Idle_Stop_Stops_Idle ()
return true;
};
- ml.AddIdle (fn);
+ var a = ml.TimedEvents.Add (TimeSpan.Zero, fn);
ml.Run ();
- Assert.True (ml.TimedEvents.RemoveIdle (fn));
+ Assert.True (ml.TimedEvents.Remove (a));
Assert.Equal (10, functionCalled);
}
- public static IEnumerable