Διάδοση συμβάντος
Διάδοση συμβάντος σημαίνει ότι, όταν ένα συμβάν εκπέμπεται σε ένα συγκεκριμένο γραφικό συστατικό, μπορεί να περαστεί στο γονικό τους γραφικό συστατικό (και αυτό το γραφικό συστατικό μπορεί να το περάσει στο γονικό του, και ούτω καθεξής) και, αν το γονικό έχει έναν χειριστή συμβάντος, αυτός ο χειριστής θα κληθεί.
Αντίθετα με άλλα συμβάντα, τα συμβάντα πληκτρολογίου στέλνονται πρώτα στο παράθυρο ανωτάτου επιπέδου (Gtk::Window), όπου θα ελεγχθεί για οποιεσδήποτε συντομεύσεις πληκτρολογίου που μπορούν να οριστούν (πλήκτρα επιταχυντή και μνημονικά, που χρησιμοποιούνται για επιλογή στοιχείων μενού από το πληκτρολόγιο). Μετά από αυτό (και θεωρώντας ότι το συμβάν δεν επεξεργάστηκε), στέλνεται στο γραφικό συστατικό που έχει την εστίαση και η διάδοση ξεκινά από εκεί.
Το συμβάν θα διαδοθεί μέχρι να φτάσει στο γραφικό συστατικό ανωτάτου επιπέδου, ή μέχρι να σταματήστε τη διάδοση επιστρέφοντας true από έναν χειριστή συμβάντος.
Σημειώστε, ότι μετά την ακύρωση ενός συμβάντος, καμιά άλλη συνάρτηση δεν θα κληθεί (ακόμα κι αν είναι από το ίδιο γραφικό συστατικό).
- 21.2.1. Παράδειγμα
21.2.1. Παράδειγμα
Σε αυτό το παράδειγμα υπάρχουν τρεις χειριστές συμβάντων που καλούνται μετά τον προεπιλεγμένο χειριστή συμβάντος της Gtk::Window, ένας στην Gtk::Entry, ένας στην Gtk::Grid και ένας στην Gtk::Window.
Στην Gtk::Window, έχουμε επίσης αντικαταστήσει τον προεπιλεγμένο χειριστή (on_key_release_event()), και ένας άλλος χειριστής καλείται πριν τον προεπιλεγμένο χειριστή (windowKeyReleaseBefore()).
Ο σκοπός αυτού του παραδείγματος είναι να εμφανίσει τα βήματα που παίρνει το συμβάν όταν εκπέμπεται.
Όταν γράφετε στην καταχώριση, ένα συμβάν απελευθέρωσης πλήκτρου θα εκπεμφθεί, που θα πάει πρώτα στο παράθυρο ανωτάτου επιπέδου (Gtk::Window), αφού έχουμε έναν ορισμένο χειριστή συμβάντος να κληθεί πριν, αυτό λέγεται πρώτο (windowKeyReleaseBefore()). Έπειτα ο προεπιλεγμένος χειριστής καλείται (τον οποίο έχουμε αντικαταστήσει) και μετά από αυτό το συμβάν στέλνεται στο γραφικό συστατικό που έχει την εστίαση, η Entry στο παράδειγμά μας και ανάλογα με το αν το επιτρέψουμε, μπορεί να φτάσει στους χειριστές συμβάντων των Grid και Window. Αν διαδοθεί, το κείμενο που γράφετε θα εμφανιστεί στην Label πάνω από την 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() { }