h1. Event handling overview GUI programming is sometimes described as being "event-driven". Typically, a wxRuby application will set up an initial state within the App's "on_init":app.html#App_oninit method, showing a starting "Frame":frame.html or "Dialog":dialog.html. After this, the application's behaviour will be determined by its responses to the user's interaction with the GUI elements. These interactions with an application are modelled as "Events":event.html. Events include pressing a key, clicking a "button":button.html, typing text in a "text control":textctrl.html, moving the mouse, selecting a drop-down "menu item":menu.html, resizing a window and so on. In wxRuby, all these user actions are dealt with using *event handlers* h2. Setting up event handlers *Event handlers* are pieces of code that are run when an event occurs. There are two parts to setting up an event handler: # Writing the code that should be run # Connecting that code to the event The basic way to set up an event handler in WxRuby combines these two steps in a single line of code. This is to call a method named @evt_xxx@, where xxx is the type of event that should be handled, and pass it a block that should be run when the event occurs. For example: evt_size { puts "I was resized" } Some types of event handlers, those that deal with events arising from a user operating UI controls like buttons, checkboxes and choices, require that the relevant control be identified in the handler: my_button = Wx::Button.new(self, :label => 'Press me') evt_button(my_button) { puts "Button was pressed" } This is discussed in more detail "below":#events_with_ids The evt_xxx methods are instance methods of the wxRuby class "EvtHandler":evthandler.html. All GUI classes that are displayed on the screen (that is, subclasses of "Window":window.html) inherit from this class, as does "App":app.html. Therefore, event handling can be done by "Frames":frame.html, "Dialogs":dialog.html or within individual controls. As there are very many different types of evt_xxx method, they are described alongside the type of control or event that they belong to. Therefore, the events generated by a "TextCtrl":textctrl.html are given in the documentation for that class. Similarly, available event handlers relating to the activation and disactivation of a Window are listed in the documentation for "ActivateEvent":activateevent.html. h3. Working with event objects Whenever an event occurs, an "Event":event.html object is created containing additional information about the event. For example, for a "mouse event":mouseevent.html, this might be where on the screen it took place; for a "key event":keyevent.html, what key was pressed; for a "activation event":activateevent.html event, whether the Frame became active or inactive. To access this additional information within an event handler, simply set up the block to accept a single parameter, the Event object. evt_size { | event | puts "Width is now #{event.size.width}" } The event object will be of the appropriate class for the type of event being handled - for example, a "SizeEvent":sizeevent.html for a evt_size handler, or a "TreeEvent":treeevent.html from a TreeCtrl. All the event classes are listed "on the home page":/#events. Note that Events are short-lived objects and are destroyed by wxWidgets once all the relevant handlers have been called. Therefore you shouldn't attempt to store an event object passed into a handler in a variable referenced outside the handler block. h3. Using methods to handle events As an application develops, more event handlers are used, and each contains more code. It then becomes easier to organise this code into methods, for example: def initialize .. evt_size { | event | on_size(event) } evt_button(my_button) { | event | on_button_pressed(event) } end def on_size(event) ... def on_button_pressed(event) ... For convenience, wxRuby permits the block here to be replaced simply with the name of the method that should handle the event. evt_size :on_size evt_button my_button, :on_button_pressed Obviously, this can give considerably clearer and shorter code. The method may still choose whether or not to receive the Event object. h2(#events_with_ids). CommandEvents and others As mentioned above, some event handlers, for example, @evt_button@ require that the control which generates the event be identified. This is one aspect of a distinction between two broad groups of events in wxRuby. The first is "CommandEvents":commandevent.html, which are generated by user interaction with controls. The second includes all other events. h3. Command events "CommandEvents":commandevent.html are generated by user interface controls such as buttons, text boxes, radio buttons, lists, menus and so on. These controls enable particular types of interaction, such as typing in a text box, selecting a radio item, or choosing an item from a drop-down list or menu. Such actions can be thought of as "commands", hence the name. It's often desirable to manage the event handlers for a related group of such controls within a container window, such as a "Frame":frame.html or "Panel":panel.html. To facilitate this, "CommandEvents":commandevents.html 'bubble' up to parent windows, so parent windows can set up handlers to listen to events generated by child controls. This also means that you have to explicitly tell WxRuby the source of events you want to handle events from. In C++ this is done by referring to the integer id of the control whose events are being listened to, and this is permitted in wxRuby too: MY_BUTTON_ID = 1001 my_button = Wx::Button.new(panel, MY_BUTTON_ID, 'Press me') evt_button(MY_BUTTON_ID) { 'my_button was pressed' } However, this use of explicit constants is rather cumbersome and unnecessary in a dynamic language like Ruby. Therefore, it is typically easier to allow wxRuby to assign ids to new windows, and then simply pass the control itself as the parameter to the event handler: my_button = Wx::Button.new(panel, :label => 'Press me') evt_button(my_button) { 'my_button was pressed' } If there are many similar controls within a Frame or Dialog, it can be easier to set up a global event handler, then work out what specific action should be taken within the event handling code itself, perhaps using the "event_object":event.html#Event_geteventobject method in Event to identify the control. To set up a catch-all event handler for CommandEvent types, use the special @Wx::ID_ANY@ identifier: evt_button(Wx::ID_ANY) { 'some button was pressed' } h3. Other events Other events in WxRuby include sizing, activating, closing and moving windows, changing layouts with splitters and sashes, and moving through multi-pane organisers like "Wizards":wizard.html and "Notebooks":notebook.html. Importantly, this also includes generic "mouse movement":mouseevent.html and "keyboard presses":keyevent.html that are not associated with any particular command action. All these kind of events are *only visible to the window itself*. They are not visible to parent windows. The practical effect of this is that the evt_xxx method must be set up the window that generates the event, and no @id@ parameter is needed. evt_size { "I was resized" } The fact that non-CommandEvents are only sent to the window that generated them is not, in practice, a serious restriction, given wxRuby's dynamic nature. It is entirely permitted to call the evt_xxx method on the widget whose events should be listened for, but keep the handling code in another instance class MyFrame < Wx::Frame def initialize ... my_panel = Wx::Panel.new(self) my_panel.evt_size { on_panel_sized } end def on_panel_sized end Just note that in this case it is not possible to use the shortcut method-name notation shown above; the following will not work my_panel.evt_size :on_panel_sized h3. Non-gui events As well events that are triggered by UI actions, wxRuby is also able to use event handling to carry out timed or regular actions. The "Timer":timer.html class can be used to trigger one-off or repeating "TimerEvents":timerevent.html events which can be handled as with other events. Note that use of this approach is preferred to ruby's own 'timeout' library as a way to handle timed events in wxRuby. This is because Ruby's interpreter threads do not co-operate well with C and C++ based toolkits like WxWidgets. There is also an @evt_idle@ handler which specifies actions to be done when the application is otherwise doing nothing. See "IdleEvent":idleevent.html for more information. h2. Vetoing and skipping events Sometimes a given event should not be permitted. For example, a user has tried to close a dialog, but there is an invalid value in a text box, so the application should prevent the dialog being closed until the value is corrected. The action can be blocked by calling the "veto":event.html#Event_veto method: if not ready event.veto end Sometimes you want the opposite - to ensure that the event's original aim is completed, or passed on upwards to a parent window for handling. In this case, the "skip":event.html#Event_skip method should be called. event.skip This is important, for example, in @evt_close@ handlers. If @skip@ is not called, the Frame or Dialog will not be closed at all. h2. Disabling event handlers Although it is not a frequent need, it is possible to disable any event handler dynamically within program code. This is achieved by calling the "disconnect":evthandler#EvtHandler_disconnect method of EvtHandler. An example of this can be found in the "events" sample included with wxRuby. h2. Custom event types wxRuby permits completely custom event types and handlers to be defined. This is useful when writing custom control types. The central method for doing this is the EvtHandler class method "register_class":evthandler.html#EvtHandler_registerclass. The documentation there describes how to integrate user-defined Event classes with the wxRuby event system; example code can be found in the "events" sample.