Mouse events are similar to keyboard events. There are some differences, though.
You use Gtk::GestureClick
instead of
Gtk::EventControllerKey
.
There are other event classes that can be used for handling
mouse events, for instance Gtk::EventControllerMotion
and
Gtk::EventControllerScroll
.
Many signal handlers don't return a bool
.
You can't stop those events from propagating.
The event classes that handle mouse events are also useful for handling
events from touchscreens. There are also many subclasses of
Gtk::EventController
which are only (or mainly) useful
for touchscreens. Examples: Gtk::GestureRotate
,
Gtk::GestureZoom
, Gtk::GestureSwipe
,
Gtk::GestureLongPress
.
This is an expanded version of the simple keyboard events example.
File: examplewindow.h
(For use with gtkmm 4)
#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H
#include <gtkmm.h>
class ExampleWindow : public Gtk::Window
{
public:
ExampleWindow();
~ExampleWindow() override;
private:
// Signal handlers:
void on_window_mouse_pressed(int n_press, double x, double y);
void on_first_button_mouse_pressed(int n_press, double x, double y);
void on_second_button_mouse_pressed(int n_press, double x, double y);
void on_third_button_mouse_pressed(int n_press, double x, double y);
void on_third_button_clicked();
void on_mouse_released(int n_press, double x, double y);
void on_mouse_motion(double x, double y);
bool on_mouse_scroll(double dx, double dy);
bool on_key_pressed(guint keyval, guint keycode, Gdk::ModifierType state);
Gtk::Box m_box;
Gtk::CheckButton m_first;
Gtk::CheckButton m_second;
Gtk::Button m_third;
Glib::RefPtr<Gtk::GestureClick> m_window_click;
Glib::RefPtr<Gtk::GestureClick> m_first_button_click;
Glib::RefPtr<Gtk::GestureClick> m_second_button_click;
};
#endif //GTKMM_EXAMPLEWINDOW_H
File: examplewindow.cc
(For use with gtkmm 4)
#include "examplewindow.h"
#include <map>
#include <iostream>
namespace
{
std::map<unsigned int, Glib::ustring> mouse_buttons
{
{0, "No button"},
{GDK_BUTTON_PRIMARY, "Primary"},
{GDK_BUTTON_MIDDLE, "Middle"},
{GDK_BUTTON_SECONDARY, "Secondary"},
};
}
ExampleWindow::ExampleWindow()
: m_first("First"),
m_second("Second"),
m_third("Click me")
{
set_title("Keyboard and Mouse Events");
m_box.set_margin(10);
set_child(m_box);
// Radio buttons
m_second.set_group(m_first);
m_first.set_active();
// Main container
m_box.set_orientation(Gtk::Orientation::HORIZONTAL);
m_box.append(m_first);
m_box.append(m_second);
m_box.append(m_third);
// Keyboard events
auto key_controller = Gtk::EventControllerKey::create();
key_controller->signal_key_pressed().connect(
sigc::mem_fun(*this, &ExampleWindow::on_key_pressed), false);
add_controller(key_controller);
// Mouse events, window
auto motion_controller = Gtk::EventControllerMotion::create();
motion_controller->signal_motion().connect(
sigc::mem_fun(*this, &ExampleWindow::on_mouse_motion));
add_controller(motion_controller);
m_window_click = Gtk::GestureClick::create();
m_window_click->set_button(0); // All mouse buttons
m_window_click->signal_pressed().connect(
sigc::mem_fun(*this, &ExampleWindow::on_window_mouse_pressed));
add_controller(m_window_click);
auto mouse_click = Gtk::GestureClick::create();
mouse_click->set_button(GDK_BUTTON_PRIMARY);
mouse_click->signal_released().connect(sigc::mem_fun(*this, &ExampleWindow::on_mouse_released));
add_controller(mouse_click);
auto mouse_scroll = Gtk::EventControllerScroll::create();
mouse_scroll->set_flags(Gtk::EventControllerScroll::Flags::VERTICAL);
// Handle mouse wheel rotations
mouse_scroll->signal_scroll().connect(
sigc::mem_fun(*this, &ExampleWindow::on_mouse_scroll), true);
add_controller(mouse_scroll);
// Mouse events, buttons
m_first_button_click = Gtk::GestureClick::create();
m_first_button_click->set_button(GDK_BUTTON_PRIMARY);
m_first_button_click->signal_pressed().connect(
sigc::mem_fun(*this, &ExampleWindow::on_first_button_mouse_pressed));
m_first.add_controller(m_first_button_click);
m_second_button_click = Gtk::GestureClick::create();
m_second_button_click->set_button(GDK_BUTTON_PRIMARY);
m_second_button_click->signal_pressed().connect(
sigc::mem_fun(*this, &ExampleWindow::on_second_button_mouse_pressed));
m_second.add_controller(m_second_button_click);
mouse_click = Gtk::GestureClick::create();
// This mouse event must be handled in the capture phase.
// The GtkButton C class handles the event in the capture phase and
// does not let it propagate to the bubble phase where events are
// handled by default.
mouse_click->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
mouse_click->set_button(GDK_BUTTON_PRIMARY);
mouse_click->signal_pressed().connect(
sigc::mem_fun(*this, &ExampleWindow::on_third_button_mouse_pressed));
m_third.add_controller(mouse_click);
m_third.signal_clicked().connect(
sigc::mem_fun(*this, &ExampleWindow::on_third_button_clicked));
}
void ExampleWindow::on_window_mouse_pressed(int n_press, double x, double y)
{
const auto current_button = mouse_buttons[m_window_click->get_current_button()];
std::cout << "Mouse pressed in window: " << current_button
<< ", " << n_press << ", " << x << ", " << y << std::endl;
}
void ExampleWindow::on_first_button_mouse_pressed(int n_press, double x, double y)
{
const auto current_button = mouse_buttons[m_first_button_click->get_current_button()];
std::cout << "Mouse pressed in first button: " << current_button
<< ", " << n_press << ", " << x << ", " << y << std::endl;
}
void ExampleWindow::on_second_button_mouse_pressed(int n_press, double x, double y)
{
const auto current_button = mouse_buttons[m_second_button_click->get_current_button()];
std::cout << "Mouse pressed in second button: " << current_button
<< ", " << n_press << ", " << x << ", " << y << std::endl;
}
void ExampleWindow::on_third_button_mouse_pressed(int n_press, double x, double y)
{
std::cout << "Mouse pressed in third button: "
<< n_press << ", " << x << ", " << y << std::endl;
}
void ExampleWindow::on_third_button_clicked()
{
std::cout << "Third button clicked" << std::endl;
}
void ExampleWindow::on_mouse_released(int n_press, double x, double y)
{
std::cout << "Mouse released: " << n_press << ", " << x << ", " << y << std::endl;
}
void ExampleWindow::on_mouse_motion(double x, double y)
{
std::cout << "Mouse motion: " << x << ", " << y << std::endl;
}
bool ExampleWindow::on_mouse_scroll(double dx, double dy)
{
std::cout << "Mouse scroll: " << dx << ", " << dy << std::endl;
return true; // handled
}
bool ExampleWindow::on_key_pressed(guint keyval, guint, Gdk::ModifierType state)
{
// Gdk::ModifierType::ALT_MASK -> the 'Alt' key(mask)
// GDK_KEY_1 -> the '1' key
// GDK_KEY_2 -> the '2' key
// Select the first radio button, when we press Alt + 1
if ((keyval == GDK_KEY_1) &&
(state & (Gdk::ModifierType::SHIFT_MASK | Gdk::ModifierType::CONTROL_MASK | Gdk::ModifierType::ALT_MASK)) == Gdk::ModifierType::ALT_MASK)
{
m_first.set_active();
// Returning true, cancels the propagation of the event
return true;
}
else if ((keyval == GDK_KEY_2) &&
(state & (Gdk::ModifierType::SHIFT_MASK | Gdk::ModifierType::CONTROL_MASK | Gdk::ModifierType::ALT_MASK)) == Gdk::ModifierType::ALT_MASK)
{
// and the second radio button, when we press Alt + 2
m_second.set_active();
return true;
}
else if (keyval == GDK_KEY_Escape)
{
// Close the window, when the 'Esc' key is pressed
set_visible(false);
return true;
}
// The event has not been handled
return false;
}
ExampleWindow::~ExampleWindow()
{
}
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);
}