The GUI application consumes all OS events such as keyboard, mouse, system, etc. In general, the application code tends to grow. Typically, the MVC (Model-View-Controller) pattern or its variants are useful for splitting the logic of a GUI application into modules.
Model modules (letter M in the pattern name) must handle application data. Typically, the model code is the simplest part of the GUI application. For example, in a mind map editor, this code keeps the current contents of the mind map (nodes of tree, a selected node, etc.) and implements methods to change the content (insert a new node, add a new letter to node, etc).
View modules (V letter) should display the current state of the model to the user. For example, the View module in the mind map editor draws node text, borders, lines between nodes, cursor, etc. View modules can be complex because the graphic design style is a valuable part of a GUI application. But the core logic of View modules is simple - to display the current state of the model to the user.
Controller modules (C letter) must receive user events, call Model to change its state, and call View to display the model's new state to the user. For example, the control module in the mind map editor draws a mind map, waits for a new event, and in a switch statement for each possible type of event changes the contents of the mind map. And again.
If your application contains two or more controller modules, you must use additional rules to separate events between different application windows.
Some of an options:
Recommended option:
For example, a mouse click can be handled by itself or by a child controller. The controller can get the child window rectangle from the child controller and compare it to the click point. If a mouse click occurs in a child window, the controller can pass the event to the child controller and ignore its own event handler.
The core of the module is the event channel between parent and child controller. The parent controller must create an eventlink.EventLink
variable:
eventLink := eventlink.New()
and link the child controller to run its Action
method as a goroutine:
eventLink.Link(ctx, appFramer, childController)
The child controller must implement Actor
interface to work with eventlink.EventLink
:
type Actor interface {
Action(ctx context.Context, app App)
Wait()
}
Then at any time the parent controller can put an event to the child channel:
eventLink.Chan() <- receivedEvent
On the childs side, the eternal Action
method of the child controller receives the event from the channel, handles or ignores it and redraws the child state in its or any other window. The eventlink.App
is a interface to get events, create child window, etc.
The eventlink.AppFramer
is a wrapper from impress.Application
at the beginning:
var appFramer eventlink.AppFramer = eventlink.MainApp(application)
eventlink.App
may be used as eventlink.AppFramer
parameter of eventlink.Link
to create a new link inside the child controller.
See the module documentation and example for details.
In a typical usecases, child controllers creates on start and run until parent Controller are still alive.
More complex scenarios of parent and child controller coexistence are possible:
To implement complex shutdown scenarios:
ctx.Done
in parallel with event waiting. If the context is canceled, the Action
method must exit immediately. If some background child goroutines are still alive, the place to wait is the Wait
method.Action
method in case of any event. A parent can check the status of a child using the childActor, isAlive := eventLink.Actor()
method.ctx
parameter of Link
must be combined from the grandfathers context and can be canceled at any time.Link
method with a new controller. Then the context of the replaced controller will be canceled.func (c *EventLink) Cancel()
.