事件传播
事件传播意味着,当事件在特定的部件中发出时,它可以被传递到该部件的父部件(这个部件又可以将信号传递给它的父部件,以此类推),如果父部件有对该事件的事件处理程序,该处理程序将被调用。
与其他事件相反,键盘事件首先被发送到顶级窗口(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。如果该事件被传播,你写入的文本将出现在Entry上方的Label中。
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() { }