2

In GTK3, How do I set the mouse cursor to cross hair when hovering over a GtkWidget, in this case a GtkDrawingArea?

PintoDoido
  • 1,011
  • 16
  • 35

1 Answers1

3

First of all, you must tell the GtkDrawingArea widget to use a backing window, in order to receive events:

gtk_widget_set_has_window (GTK_WIDGET (darea), TRUE);

Then you must tell it which events you wish to subscribe to; in this case, you want the crossing events, in order to receive notification of the pointer entering and leaving the widget:

int crossing_mask = GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
gtk_widget_add_events (GTK_WIDGET (darea), crossing_mask);

At this point, you can connect to the GtkWidget::enter-notify-event and GtkWidget::leave-notify-event signals:

g_signal_connect (darea, "enter-notify-event", G_CALLBACK (on_crossing), NULL);
g_signal_connect (darea, "leave-notify-event", G_CALLBACK (on_crossing), NULL);

You can use two separate signal handlers, if you want, but unless you're doing something complex in them, the code is going to be pretty much identical.

The on_crossing() handler will look something like this:

static gboolean
on_crossing (GtkWidget *darea, GdkEventCrossing *event)
{
  switch (gdk_event_get_event_type (event))
    {
    case GDK_ENTER_NOTIFY:
      // Do something on enter
      break;

    case GDK_LEAVE_NOTIFY:
      // Do something on leave
      break;
    }
}

Now you have specify the cursor to use depending on the event type. GTK+ uses the same cursor names as CSS does; you need to create a cursor instance using one of those names and then associate it to the GdkWindow used by the drawing area widget:

// Get the display server connection
GdkDisplay *display = gtk_widget_get_display (darea);
GdkCursor *cursor;

switch (gdk_event_get_event_type (event))
  {
  case GDK_ENTER_NOTIFY:
    cursor = gdk_cursor_new_from_name (display, "crosshair");
    break;
  case GDK_ENTER_NOTIFY:
    cursor = gdk_cursor_new_from_name (display, "default");
    break;
  }

// Assign the cursor to the window
gdk_window_set_cursor (gtk_widget_get_window (darea), cursor);

// Release the reference on the cursor
g_object_unref (cursor);
ebassi
  • 8,648
  • 27
  • 29
  • Thanks for your detailed answer! Please check out my follow-up question: https://stackoverflow.com/questions/43723638/how-do-i-change-the-mouse-cursor-over-a-gtkdrawingarea-in-gtk3 – PintoDoido May 01 '17 at 17:40
  • The documentation for ['gtk_widget_set_has_window()'](https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-set-has-window) says: "This function should only be called by widget implementations, and they should call it in their init() function." .... Wouldn't be an `eventbox_box` as container for the `drawing_area` sufficient? – Gerhardh May 03 '17 at 15:41
  • Yes, you could also use a GtkEventBox and then add the GtkDrawingArea inside it; or you could subclass GtkDrawingArea and call gtk_widget_set_has_window() inside the instance initialization function of your own new type. There are more ways to do this with the existing API. – ebassi May 04 '17 at 15:36