Propagace události
Propagace událostí znamená, že když je událost vyslána na konkrétní widget, může být předána do rodičovského widgetu (a ten ji může předat do svého rodičovského widgetu atd.) a pokud má rodič obsluhu události, je tato obsluha zavolána.
Na rozdíl od jiných událostí, události klávesnice jsou nejprve zaslány do okna nejvyšší úrovně (Gtk::Window), kde jsou zkontrolovány vůči případným klávesovým zkratkám (akcelerátory a horké klávesy používané k výběru položek v nabídkách pomocí klávesnice). Po té (za předpokladu, že událost nebyla obsloužena) jsou zaslány do widgetu, který má zaměření a z něj jsou pak propagovány.
Událost se bude propagovat, dokud nedosáhne widgetu v nejvyšší úrovni, nebo dokud propagaci nezastavíte vrácením true z obsluhy události.
Dejte pozor na to, že po zastavení události nebude už zavolána žádná funkce (i kdyby byla z toho stejného widgetu).
- 21.2.1. Příklad
21.2.1. Příklad
V tomto příkladu jsou tři obsluhy událostí, které se nazývají podle výchozích obsluh událostí v Gtk::Window. Jedna je Gtk::Entry, druhá Gtk::Grid a třetí Gtk::Window.
V Gtk::Window máme také přepsanou výchozí obsluhu (on_key_release_event()) a další obsluha je volaná před výchozí obsluhou (windowKeyReleaseBefore()).
Účelem tohoto příkladu je ukázat kroky, které událost podstoupí, když je vyslána.
Když píšete do vstupního pole, vyšle se událost o uvolnění klávesy, která jde nejprve do okna nejvyšší úrovně (Gtk::Window), protože máme nastavenou jednu obsluhu události, aby byla zavolaná napřed, bude to ona, co bude zavolána první (windowKeyReleaseBefore()). Pak je zavolána výchozí obsluha (kterou máme přepsanou) a následně je událost zaslána widgetu, který má zaměření, což je v našem příkladu Entry. Podle toho, jak budeme událost propagovat, může dorazit až k obsluhám událostí objektů Grid a Window. Pokud bude propagována, bude se text, který píšete objevovat v popisku Label nad vstupním polem Entry.
File: examplewindow.h (For use with gtkmm 4)
#ifndef GTKMM_EVENT_PROPAGATION_H #define GTKMM_EVENT_PROPAGATION_H #include <gtkmm.h> class ExampleWindow : public Gtk::Window { public: ExampleWindow(); virtual ~ExampleWindow(); private: // Signal handlers: bool label2_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state, const Glib::ustring& phase); bool grid_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state, const Glib::ustring& phase); bool window_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state, const Glib::ustring& phase); bool m_first = true; Gtk::Box m_container; Gtk::Frame m_frame; Gtk::Label m_label1; Gtk::Label m_label2; Gtk::CheckButton m_checkbutton_can_propagate_down; Gtk::CheckButton m_checkbutton_can_propagate_up; }; #endif //GTKMM_EVENT_PROPAGATION_H
File: main.cc (For use with gtkmm 4)
#include "examplewindow.h" #include <gtkmm/application.h> int main(int argc, char *argv[]) { auto app = Gtk::Application::create("org.gtkmm.example"); //Shows the window and returns when it is closed. return app->make_window_and_run<ExampleWindow>(argc, argv); }
File: examplewindow.cc (For use with gtkmm 4)
#include "examplewindow.h" #include <iostream> ExampleWindow::ExampleWindow() : m_label1("A label"), m_label2("Write here"), m_checkbutton_can_propagate_down("Can propagate down"), m_checkbutton_can_propagate_up("Can propagate up") { set_title("Event Propagation"); m_container.set_margin(10); set_child(m_container); m_frame.set_child(m_label2); m_label2.set_selectable(); m_checkbutton_can_propagate_down.set_active(); m_checkbutton_can_propagate_up.set_active(); // Main container m_container.set_orientation(Gtk::Orientation::VERTICAL); m_container.append(m_label1); m_container.append(m_frame); m_container.append(m_checkbutton_can_propagate_down); m_container.append(m_checkbutton_can_propagate_up); // Events const bool after = false; // Run before or after the default signal handlers. // Called in the capture phase of the event handling. auto controller = Gtk::EventControllerKey::create(); controller->set_propagation_phase(Gtk::PropagationPhase::CAPTURE); controller->signal_key_pressed().connect( sigc::bind(sigc::mem_fun(*this, &ExampleWindow::label2_key_pressed), "capture"), after); m_label2.add_controller(controller); controller = Gtk::EventControllerKey::create(); controller->set_propagation_phase(Gtk::PropagationPhase::CAPTURE); controller->signal_key_pressed().connect( sigc::bind(sigc::mem_fun(*this, &ExampleWindow::grid_key_pressed), "capture"), after); m_container.add_controller(controller); controller = Gtk::EventControllerKey::create(); controller->set_propagation_phase(Gtk::PropagationPhase::CAPTURE); controller->signal_key_pressed().connect( sigc::bind(sigc::mem_fun(*this, &ExampleWindow::window_key_pressed), "capture"), after); add_controller(controller); // Called in the target phase of the event handling. controller = Gtk::EventControllerKey::create(); controller->set_propagation_phase(Gtk::PropagationPhase::TARGET); controller->signal_key_pressed().connect( sigc::bind(sigc::mem_fun(*this, &ExampleWindow::label2_key_pressed), "target"), after); m_label2.add_controller(controller); controller = Gtk::EventControllerKey::create(); controller->set_propagation_phase(Gtk::PropagationPhase::TARGET); controller->signal_key_pressed().connect( sigc::bind(sigc::mem_fun(*this, &ExampleWindow::grid_key_pressed), "target"), after); m_container.add_controller(controller); controller = Gtk::EventControllerKey::create(); controller->set_propagation_phase(Gtk::PropagationPhase::TARGET); controller->signal_key_pressed().connect( sigc::bind(sigc::mem_fun(*this, &ExampleWindow::window_key_pressed), "target"), after); add_controller(controller); // Called in the bubble phase of the event handling. // This is the default, if set_propagation_phase() is not called. controller = Gtk::EventControllerKey::create(); controller->set_propagation_phase(Gtk::PropagationPhase::BUBBLE); controller->signal_key_pressed().connect( sigc::bind(sigc::mem_fun(*this, &ExampleWindow::label2_key_pressed), "bubble"), after); m_label2.add_controller(controller); controller = Gtk::EventControllerKey::create(); controller->set_propagation_phase(Gtk::PropagationPhase::BUBBLE); controller->signal_key_pressed().connect( sigc::bind(sigc::mem_fun(*this, &ExampleWindow::grid_key_pressed), "bubble"), after); m_container.add_controller(controller); controller = Gtk::EventControllerKey::create(); controller->set_propagation_phase(Gtk::PropagationPhase::BUBBLE); controller->signal_key_pressed().connect( sigc::bind(sigc::mem_fun(*this, &ExampleWindow::window_key_pressed), "bubble"), after); add_controller(controller); } // By changing the return value we allow, or don't allow, the event to propagate to other elements. bool ExampleWindow::label2_key_pressed(guint keyval, guint, Gdk::ModifierType, const Glib::ustring& phase) { std::cout << "Label, " << phase << " phase" << std::endl; if (phase == "bubble") { const gunichar unichar = gdk_keyval_to_unicode(keyval); if (unichar != 0) { if (m_first) { m_label2.set_label(""); m_first = false; } if (unichar == '\b') m_label2.set_label(""); else { const Glib::ustring newchar(1, unichar); m_label2.set_label(m_label2.get_label() + newchar); } } if (!m_checkbutton_can_propagate_up.get_active()) return true; // Don't propagate } return false; } bool ExampleWindow::grid_key_pressed(guint, guint, Gdk::ModifierType, const Glib::ustring& phase) { std::cout << "Grid, " << phase << " phase" << std::endl; // Let it propagate return false; } // This will set the second label's text in the first label every time a key is pressed. bool ExampleWindow::window_key_pressed(guint, guint, Gdk::ModifierType, const Glib::ustring& phase) { if (phase == "capture") std::cout << std::endl; std::cout << "Window, " << phase << " phase"; // Checking if the second label is on focus, otherwise the label would get // changed by pressing keys on the window (when the label is not on focus), // even if m_checkbutton_can_propagate_up wasn't active. if (phase == "bubble" && m_label2.has_focus()) { m_label1.set_text(m_label2.get_text()); std::cout << ", " << m_label2.get_text(); } std::cout << std::endl; if (phase == "capture" && !m_checkbutton_can_propagate_down.get_active()) return true; // Don't propagate return false; } ExampleWindow::~ExampleWindow() { }