Mouse Events

Mouse events are similar to keyboard events. There are some differences, though.

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.

Reference

Example

This is an expanded version of the simple keyboard events example.

Figure 23.3. Keyboard and Mouse Events

Keyboard and Mouse Events

Source Code

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);
}