2
0
miguel 7 жил өмнө
parent
commit
12c4c88389

+ 46 - 0
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.
+

+ 161 - 0
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.