fjvallarino/monomer

[Question] How to handle external events "globally"

Closed this issue · 5 comments

My usecase: I want to be able to hit Esc or Ctrl + Q and quit the app, closing its window.
What is the best way to go about this?

I saw that when creating custom widgets one can use handleEvent, but what about handling events globally, on the app level?
I checked few tutorial samples and all handleEvent functions which are passed to startApp have a fixed user defined data type.

I can see that startApp has WidgetEvent e constraint which is actually a Typeable e, but Typeable is something I haven't studied yet, so maybe I don't see something obvious here.

To handle a global keyboard event you can use the keystroke widget, and have it be your top level widget. This widget provides a way of handling events at the Application/Composite level. The To-Do example uses this widget. Since this widget has a single widget as its child, you can just put it before the content you currently have:

buildUI wenv model = widgetTree where
  bindings = [ ... ]
  widgetTree = keystroke bindings $
    -- Your current widgets

One important thing is that keystroke will only receive events if any of its children can receive focus (textField, checkbox, etc). If you will not have any of those widgets in your UI (maybe you are testing and printing messages to the console), you will want to mark this widget as focusable.

keystroke bindings childNode
  `nodeFocusable` True

Great! Thank you!

I have almost managed to make this work, need a little help if possible.

  • I have added an AppQuit event to AppEvent type
  • I have added appExitEvent AppQuit to AppConfig passed to startApp
  • I have added a keystroke widget as you've described above
  • Now to avoid "partial" case in handleEvent I need to handle AppQuit there too, so I've added AppQuit -> [] branch there

But the thing is that this way it consumes AppQuit event (I guess) and it does not reach "internal" event handler which would quit the app.

If I remove AppQuit -> [] case branch, then everything works as expected, but compiler nags at me for having a partial case, so I'd like to make it happier by not having it.

I would appreciate any hints on how to properly fix this.
Maybe I shouldn't use appExitEvent and instead somehow post the command to quit the app from handleEvent?

Sorry, I missed this. The appExitEvent configuration gives you the chance to cancel exiting the application when the user closes the window (you can return [Request (ExitApplication False)] from the handler).

I'm not sure I understand the use case. Handling Esc and requesting to close the Application should work without issues. I don't think you can handle Ctrl-Q with keystroke, since the OS will most likely capture this combination earlier. If you have some sort of action you want to run on exit, you can use appExitEvent as you mentioned, but it will be a different event than the one you use with keystroke.

handleEvent wenv node model evt = case evt of
  CloseButton -> [ exitApplication ]  -- Same as: [ Request (ExitApplication True) ]
  OnExit -> [ ... ] -- Maybe call cancelExitApplication

Oh! The thing I have missed was that there's a special exitApplication request which can be used in an event handler!

I'm so excited to try things that I start asking questions before I read the whole tutorial :) This event is described in Composite tutorial chapter which is the next in my to-read list.

Thanks!