Event propagation means that, when an event is emitted on a particular widget, it can be passed to its parent widget (and that widget can pass it to its parent, and so on) and, if the parent has an event handler, that handler will be called.
Contrary to other events, keyboard events are first sent to the toplevel window
(Gtk::Window
), where it will be checked
for any keyboard shortcuts that may be set (accelerator keys and mnemonics,
used for selecting menu items from the keyboard). After this (and assuming
the event wasn't handled), it is sent to the widget which has focus,
and the propagation begins from there.
The event will propagate until it reaches the top-level widget, or until
you stop the propagation by returning true
from an
event handler.
Notice, that after canceling an event, no other function will be called (even if it is from the same widget).
In this example there are three event handlers that are called after
Gtk::Window
's default event handler, one in the
Gtk::Entry
, one in the Gtk::Grid
and one in the Gtk::Window
.
In the Gtk::Window
, we have also the default handler
overridden (on_key_release_event()
), and
another handler being called before the default handler
(windowKeyReleaseBefore()
).
The purpose of this example is to show the steps the event takes when it is emitted.
When you write in the entry, a key release event will be emitted,
which will go first to the toplevel window (Gtk::Window
),
since we have one event handler set to be called before, that's what is
called first (windowKeyReleaseBefore()
).
Then the default handler is called (which we have overridden), and after
that the event is sent to the widget that has focus,
the Entry
in our example and, depending on whether we let
it propagate, it can reach the Grid
's and the
Window
's event handlers. If it propagates,
the text you're writing will appear in the Label
above the Entry
.
File: examplewindow.h
(For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EVENT_PROPAGATION_H #define GTKMM_EVENT_PROPAGATION_H #include <gtkmm.h> class ExampleWindow : public Gtk::Window { public: ExampleWindow(); virtual ~ExampleWindow(); private: //Override default signal handler: bool on_key_release_event(GdkEventKey* event) override; bool entryKeyRelease(GdkEventKey* event); bool gridKeyRelease(GdkEventKey* event); bool windowKeyReleaseBefore(GdkEventKey* event); bool windowKeyRelease(GdkEventKey* event); Gtk::Grid m_container; Gtk::Label m_label; Gtk::Entry m_entry; Gtk::CheckButton m_checkbutton_can_propagate; }; #endif //GTKMM_EVENT_PROPAGATION_H
File: main.cc
(For use with gtkmm 3, not gtkmm 2)
#include "examplewindow.h" #include <gtkmm/application.h> int main(int argc, char *argv[]) { auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); ExampleWindow window; //Shows the window and returns when it is closed. return app->run(window); }
File: examplewindow.cc
(For use with gtkmm 3, not gtkmm 2)
#include "examplewindow.h" #include <iostream> ExampleWindow::ExampleWindow() { add(m_container); set_title("Event Propagation"); set_border_width(10); m_label.set_label("A label"); m_checkbutton_can_propagate.set_label("Can Propagate"); m_checkbutton_can_propagate.set_active(); // Main Container m_container.set_orientation(Gtk::ORIENTATION_VERTICAL); m_container.add(m_label); m_container.add(m_entry); m_container.add(m_checkbutton_can_propagate); // Events add_events(Gdk::KEY_RELEASE_MASK); m_entry.signal_key_release_event().connect( sigc::mem_fun(*this, &ExampleWindow::entryKeyRelease)); m_container.signal_key_release_event().connect( sigc::mem_fun(*this, &ExampleWindow::gridKeyRelease)); // Called before the default event signal handler. signal_key_release_event().connect( sigc::mem_fun(*this, &ExampleWindow::windowKeyReleaseBefore), false); // Called after the default event signal handler. signal_key_release_event().connect( sigc::mem_fun(*this, &ExampleWindow::windowKeyRelease)); show_all_children(); } //By changing the return value we allow, or don't allow, the event to propagate to other elements. bool ExampleWindow::entryKeyRelease(GdkEventKey* /* event */ ) { std::cout << "Entry" << std::endl; if(m_checkbutton_can_propagate.get_active()) { return false; } return true; } bool ExampleWindow::gridKeyRelease(GdkEventKey* /* event */ ) { std::cout << "Grid" << std::endl; //Let it propagate: return false; } bool ExampleWindow::windowKeyReleaseBefore(GdkEventKey* /* event */ ) { std::cout << "Window before" << std::endl; return false; } bool ExampleWindow::on_key_release_event(GdkEventKey* key_event) { std::cout << "Window overridden" << std::endl; // call base class function (to get the normal behaviour) return Gtk::Window::on_key_release_event(key_event); } // This will set the entry's text in the label, every time a key is pressed. bool ExampleWindow::windowKeyRelease(GdkEventKey* /* event */ ) { std::cout << "Window after"; //checking if the entry is on focus, otherwise the label would get changed by pressing keys //on the window (when the entry is not on focus), even if m_checkbutton_can_propagate wasn't active if(m_entry.has_focus()) { m_label.set_text(m_entry.get_text()); std::cout << ", " << m_entry.get_text(); } std::cout << std::endl; return true; } ExampleWindow::~ExampleWindow() { }