From 12c4c883891ccdb897df47666ac2d06f2f177726 Mon Sep 17 00:00:00 2001 From: miguel Date: Tue, 17 Jul 2018 22:20:02 -0400 Subject: [PATCH] Add more docs --- docfx/articles/keyboard.md | 46 +++++++++++ docfx/articles/mainloop.md | 161 +++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 docfx/articles/keyboard.md create mode 100644 docfx/articles/mainloop.md diff --git a/docfx/articles/keyboard.md b/docfx/articles/keyboard.md new file mode 100644 index 000000000..7faa4b9e5 --- /dev/null +++ b/docfx/articles/keyboard.md @@ -0,0 +1,46 @@ +Keyboard Event Processing +========================= + +Keyboard events are sent by the [Main Loop](mainloop.html) to the +Application class for processing. The keyboard events are sent +exclusively to the current `Toplevel`, this being either the default +that is created when you call `Application.Init`, or one that you +created an passed to `Application.Run(Toplevel)`. + +Flow +---- + +Keystrokes are first processes as hotkeys, then as regular keys, and +there is a final cold post-processing event that is invoked if no view +processed the key. + +HotKey Processing +----------------- + +Events are first send to all views as a "HotKey", this means that the +`View.ProcessHotKey` method is invoked on the current toplevel, which +in turns propagates this to all the views in the hierarchy. If any +view decides to process the event, no further processing takes place. + +This is how hotkeys for buttons are implemented. For example, the +keystroke "Alt-A" is handled by Buttons that have a hot-letter "A" to +activate the button. + +Regular Processing +------------------ + +Unlike the hotkey processing, the regular processing is only sent to +the currently focused view in the focus chain. + +The regular key processing is only invoked if no hotkey was caught. + +Cold-key Processing +------------------- + +This stage only is executed if the focused view did not process the +event, and is broadcast to all the views in the Toplevel. + +This method can be overwritten by views that want to provide +accelerator functionality (Alt-key for example), but without +interefering with normal ProcessKey behavior. + diff --git a/docfx/articles/mainloop.md b/docfx/articles/mainloop.md new file mode 100644 index 000000000..cafd3c23e --- /dev/null +++ b/docfx/articles/mainloop.md @@ -0,0 +1,161 @@ +Event Processing and the Application Main Loop +============================================== + +The method `Application.Run` that we covered before will wait for +events from either the keyboard or mouse and route those events to the +proper view. + +The job of waiting for events and dispatching them in the +`Application` is implemented by an instance of the +[`MainLoop`](docs/api/Mono.Terminal/Mono.Terminal.MainLoop.html) +class. + +Mainloops are a common idiom in many user interface toolkits so many +of the concepts will be familiar to you if you have used other +toolkits before. + +This class provides the following capabilities: + +* Keyboard and mouse processing +* .NET Async support +* Timers processing +* Invoking of UI code from a background thread +* Idle processing handlers +* Possibility of integration with other mainloops. +* On Unix systems, it can monitor file descriptors for readability or writability. + +The `MainLoop` property in the the +[`Application`](../api/Terminal.Gui/Terminal.Gui.Application.html) +provides access to these functions. + +When your code invokes `Application.Run (Toplevel)`, the application +will prepare the current +`Toplevel`[../api/Terminal.Gui/Terminal.Gui.Toplevel.html] instance by +redrawing the screen appropriately and then calling the mainloop to +run. + +You can configure the Mainloop before calling Application.Run, or you +can configure the MainLoop in response to events during the execution. + +The keyboard inputs is dispatched by the application class to the +current TopLevel window this is covered in more detail in the +[Keyboard Event Processing](keyboard.md) document. + + +Async Execution +--------------- + +On startup, the `Application` class configured the .NET Asynchronous +machinery to allow you to use the `await` keyword to run tasks in the +background and have the execution of those tasks resume on the context +of the main thread running the main loop. + +Once you invoke `Application.Main` the async machinery will be ready +to use, and you can merely call methods using `await` from your main +thread, and the awaited code will resume execution on the main +thread. + +Timers Processing +----------------- + +You can register timers to be executed at specified intervals by +calling the [`AddTimeout`](https://migueldeicaza.github.io/gui.cs/api/Mono.Terminal/Mono.Terminal.MainLoop.html#Mono_Terminal_MainLoop_AddTimeout_System_TimeSpan_System_Func_Mono_Terminal_MainLoop_System_Boolean__) method, like this: + +```csharp +void UpdateTimer () +{ + time.Text = DateTime.Now.ToString (); +} + +var token = Application.MainLoop.AddTimeout (TimeSpan.FromSeconds (20), UpdateTimer); +``` + +The return value from AddTimeout is a token value that you can use if +you desire to cancel the timer before it runs: + +```csharup +Application.MainLoop.RemoveTimeout (token); +``` + +Idle Handlers +------------- + +You can register code to be executed when the application is idling +and there are no events to process by calling the +[`AddIdle`](https://migueldeicaza.github.io/gui.cs/api/Mono.Terminal/Mono.Terminal.MainLoop.html#Mono_Terminal_MainLoop_AddIdle_System_Func_System_Boolean__) +method. This method takes as a parameter a function that will be +invoked when the application is idling. + +Idle functions should return `true` if they should be invoked again, +and `false` if the idle invocations should stop. + +Like the timer APIs, the return value is a token that can be used to +cancel the scheduled idle function from being executed. + +Threading +--------- + +Like other UI toolkits, Terminal.Gui is generally not thread safe. +You should avoid calling methods in the UI classes from a background +thread as there is no guarantee that they will not corrupt the state +of the UI application. + +Generally, as there is not much state, you will get lucky, but the +application will not behave properly. + +You will be served better off by using C# async machinery and the +various APIs in the `System.Threading.Tasks.Task` APIs. But if you +absolutely must work with threads on your own you should only invoke +APIs in Terminal.Gui from the main thread. + +To make this simple, you can use the `Application.MainLoop.Invoke` +method and pass an `Action`. This action will be queued for execution +on the main thread at an appropriate time and will run your code +there. + +For example, the following shows how to properly update a label from a +background thread: + +``` +void BackgroundThreadUpdateProgress () +{ + Application.MainLoop.Invoke (() => { + progress.Text = $"Progress: {bytesDownloaded/totalBytes}"; + }); +} +``` + +Integration With Other Main Loop Drivers +---------------------------------------- + +It is possible to run the main loop in a way that it does not take +over control of your application, but rather in a cooperative way. + +To do this, you must use the lower-level APIs in `Application`: the +`Begin` method to prepare a toplevel for execution, followed by calls +to `MainLoop.EventsPending` to determine whether the events must be +processed, and in that case, calling `RunLoop` method and finally +completing the process by calling `End`. + +The method `Run` is implemented like this: + +``` +void Run (Toplevel top) +{ + var runToken = Begin (view); + RunLoop (runToken); + End (runToken); +} +``` + +Unix File Descriptor Monitoring +------------------------------- + +On Unix, it is possible to monitor file descriptors for input being +available, or for the file descriptor being available for data to be +written without blocking the application. + +To do this, you on Unix, you can cast the `MainLoop` instance to a +[`UnixMainLoop`](docs/api/Mono.Terminal/Mono.Terminal.UnixMainLoop.html) +and use the `AddWatch` method to register an interest on a particular +condition. \ No newline at end of file