gtkeventcontrollerkey.c 11.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* GTK - The GIMP Toolkit
 * Copyright (C) 2017, Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Author(s): Carlos Garnacho <carlosg@gnome.org>
 */

20
/**
21
 * GtkEventControllerKey:
22
 *
23
24
25
 * `GtkEventControllerKey` is an event controller that provides access
 * to key events.
 */
26

27
28
29
#include "config.h"

#include "gtkintl.h"
30
#include "gtkmarshalers.h"
31
#include "gtkprivate.h"
32
#include "gtkwidgetprivate.h"
33
34
#include "gtkeventcontrollerprivate.h"
#include "gtkeventcontrollerkey.h"
35
#include "gtkenums.h"
36
#include "gtkmain.h"
Matthias Clasen's avatar
Matthias Clasen committed
37
#include "gtktypebuiltins.h"
38
39
40
41
42
43
44
45
46

#include <gdk/gdk.h>

struct _GtkEventControllerKey
{
  GtkEventController parent_instance;
  GtkIMContext *im_context;
  GHashTable *pressed_keys;

47
48
  GdkModifierType state;

49
  gboolean is_focus;
50
51

  GdkEvent *current_event;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
};

struct _GtkEventControllerKeyClass
{
  GtkEventControllerClass parent_class;
};

enum {
  KEY_PRESSED,
  KEY_RELEASED,
  MODIFIERS,
  IM_UPDATE,
  N_SIGNALS
};

static guint signals[N_SIGNALS] = { 0 };

G_DEFINE_TYPE (GtkEventControllerKey, gtk_event_controller_key,
               GTK_TYPE_EVENT_CONTROLLER)

static void
73
gtk_event_controller_key_finalize (GObject *object)
74
75
76
77
78
79
80
81
82
83
84
{
  GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (object);

  g_hash_table_destroy (key->pressed_keys);
  g_clear_object (&key->im_context);

  G_OBJECT_CLASS (gtk_event_controller_key_parent_class)->finalize (object);
}

static gboolean
gtk_event_controller_key_handle_event (GtkEventController *controller,
Matthias Clasen's avatar
Matthias Clasen committed
85
                                       GdkEvent           *event,
86
87
                                       double              x,
                                       double              y)
88
89
90
91
92
93
{
  GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller);
  GdkEventType event_type = gdk_event_get_event_type (event);
  GdkModifierType state;
  guint16 keycode;
  guint keyval;
94
  gboolean handled = FALSE;
95
96
97
98
99

  if (event_type != GDK_KEY_PRESS && event_type != GDK_KEY_RELEASE)
    return FALSE;

  if (key->im_context &&
Matthias Clasen's avatar
Matthias Clasen committed
100
      gtk_im_context_filter_keypress (key->im_context, event))
101
102
103
104
105
106
107
    {
      g_signal_emit (controller, signals[IM_UPDATE], 0);
      return TRUE;
    }

  key->current_event = event;

Matthias Clasen's avatar
Matthias Clasen committed
108
  state = gdk_event_get_modifier_state (event);
109
  if (key->state != state)
110
    {
111
      gboolean unused;
112

113
114
      key->state = state;
      g_signal_emit (controller, signals[MODIFIERS], 0, state, &unused);
115
116
    }

Matthias Clasen's avatar
Matthias Clasen committed
117
118
  keycode = gdk_key_event_get_keycode (event);
  keyval = gdk_key_event_get_keyval (event);
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

  if (event_type == GDK_KEY_PRESS)
    {
      g_signal_emit (controller, signals[KEY_PRESSED], 0,
                     keyval, keycode, state, &handled);
      if (handled)
        g_hash_table_add (key->pressed_keys, GUINT_TO_POINTER (keyval));
    }
  else if (event_type == GDK_KEY_RELEASE)
    {
      g_signal_emit (controller, signals[KEY_RELEASED], 0,
                     keyval, keycode, state);

      handled = g_hash_table_lookup (key->pressed_keys, GUINT_TO_POINTER (keyval)) != NULL;
      g_hash_table_remove (key->pressed_keys, GUINT_TO_POINTER (keyval));
    }
  else
    handled = FALSE;

  key->current_event = NULL;

  return handled;
}

143
144
145
146
147
148
149
150
151
152
153
static void
gtk_event_controller_key_handle_crossing (GtkEventController    *controller,
                                          const GtkCrossingData *crossing,
                                          double                 x,
                                          double                 y)
{
  GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller);
  GtkWidget *widget = gtk_event_controller_get_widget (controller);
  gboolean start_crossing, end_crossing;
  gboolean is_focus;

154
155
  if (crossing->type != GTK_CROSSING_FOCUS &&
      crossing->type != GTK_CROSSING_ACTIVE)
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
    return;

  start_crossing = crossing->direction == GTK_CROSSING_OUT &&
                   widget == crossing->old_target;
  end_crossing = crossing->direction == GTK_CROSSING_IN &&
                 widget == crossing->new_target;

  if (!start_crossing && !end_crossing)
    return;

  is_focus = end_crossing;

  if (key->is_focus != is_focus)
    {
      key->is_focus = is_focus;

      if (key->im_context)
        {
          if (is_focus)
            gtk_im_context_focus_in (key->im_context);
          else
            gtk_im_context_focus_out (key->im_context);
        }
    }
}

182
183
184
185
186
187
static void
gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass)
{
  GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

188
  object_class->finalize = gtk_event_controller_key_finalize;
189
  controller_class->handle_event = gtk_event_controller_key_handle_event;
190
  controller_class->handle_crossing = gtk_event_controller_key_handle_crossing;
191

192
193
194
195
196
  /**
   * GtkEventControllerKey::key-pressed:
   * @controller: the object which received the signal.
   * @keyval: the pressed key.
   * @keycode: the raw code of the pressed key.
Matthias Clasen's avatar
Matthias Clasen committed
197
   * @state: the bitmask, representing the state of modifier keys and pointer buttons. See `GdkModifierType`.
198
   *
199
   * Emitted whenever a key is pressed.
200
201
202
   *
   * Returns: %TRUE if the key press was handled, %FALSE otherwise.
   */
203
204
205
206
  signals[KEY_PRESSED] =
    g_signal_new (I_("key-pressed"),
                  GTK_TYPE_EVENT_CONTROLLER_KEY,
                  G_SIGNAL_RUN_LAST,
207
208
                  0, _gtk_boolean_handled_accumulator, NULL,
                  _gtk_marshal_BOOLEAN__UINT_UINT_FLAGS,
209
                  G_TYPE_BOOLEAN, 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE);
210
211
212
  g_signal_set_va_marshaller (signals[KEY_PRESSED],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_BOOLEAN__UINT_UINT_FLAGSv);
213

214
215
216
217
218
  /**
   * GtkEventControllerKey::key-released:
   * @controller: the object which received the signal.
   * @keyval: the released key.
   * @keycode: the raw code of the released key.
Matthias Clasen's avatar
Matthias Clasen committed
219
   * @state: the bitmask, representing the state of modifier keys and pointer buttons. See `GdkModifierType`.
220
   *
221
   * Emitted whenever a key is released.
222
   */
223
224
225
226
  signals[KEY_RELEASED] =
    g_signal_new (I_("key-released"),
                  GTK_TYPE_EVENT_CONTROLLER_KEY,
                  G_SIGNAL_RUN_LAST,
227
228
                  0, NULL, NULL,
                  _gtk_marshal_VOID__UINT_UINT_FLAGS,
229
                  G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE);
230
231
232
  g_signal_set_va_marshaller (signals[KEY_RELEASED],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_VOID__UINT_UINT_FLAGSv);
233
234
235
236
237
238

  /**
   * GtkEventControllerKey::modifiers:
   * @controller: the object which received the signal.
   * @keyval: the released key.
   * @state: the bitmask, representing the new state of modifier keys and
Matthias Clasen's avatar
Matthias Clasen committed
239
   *   pointer buttons. See `GdkModifierType`.
240
   *
241
   * Emitted whenever the state of modifier keys and pointer buttons change.
242
   */
243
244
245
246
  signals[MODIFIERS] =
    g_signal_new (I_("modifiers"),
                  GTK_TYPE_EVENT_CONTROLLER_KEY,
                  G_SIGNAL_RUN_LAST,
247
248
249
                  0, NULL,
                  NULL,
                  _gtk_marshal_BOOLEAN__FLAGS,
250
                  G_TYPE_BOOLEAN, 1, GDK_TYPE_MODIFIER_TYPE);
251
252
253
  g_signal_set_va_marshaller (signals[MODIFIERS],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_BOOLEAN__FLAGSv);
254
255
256

  /**
   * GtkEventControllerKey::im-update:
Matthias Clasen's avatar
Matthias Clasen committed
257
   * @controller: the object which received the signal
258
   *
259
260
261
262
263
   * Emitted whenever the input method context filters away
   * a keypress and prevents the @controller receiving it.
   *
   * See [method@Gtk.EventControllerKey.set_im_context] and
   * [method@Gtk.IMContext.filter_keypress].
264
   */
265
266
267
268
269
  signals[IM_UPDATE] =
    g_signal_new (I_("im-update"),
                  GTK_TYPE_EVENT_CONTROLLER_KEY,
                  G_SIGNAL_RUN_LAST,
                  0, NULL, NULL,
270
                  NULL,
271
272
273
274
275
276
277
278
279
                  G_TYPE_NONE, 0);
}

static void
gtk_event_controller_key_init (GtkEventControllerKey *controller)
{
  controller->pressed_keys = g_hash_table_new (NULL, NULL);
}

280
281
282
283
284
/**
 * gtk_event_controller_key_new:
 *
 * Creates a new event controller that will handle key events.
 *
285
 * Returns: a new `GtkEventControllerKey`
286
 **/
287
GtkEventController *
288
gtk_event_controller_key_new (void)
289
{
290
  return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY, NULL);
291
292
}

293
294
/**
 * gtk_event_controller_key_set_im_context:
295
296
 * @controller: a `GtkEventControllerKey`
 * @im_context: a `GtkIMContext`
297
298
 *
 * Sets the input method context of the key @controller.
299
 */
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
void
gtk_event_controller_key_set_im_context (GtkEventControllerKey *controller,
                                         GtkIMContext          *im_context)
{
  g_return_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller));
  g_return_if_fail (!im_context || GTK_IS_IM_CONTEXT (im_context));

  if (controller->im_context)
    gtk_im_context_reset (controller->im_context);

  g_set_object (&controller->im_context, im_context);
}

/**
 * gtk_event_controller_key_get_im_context:
315
 * @controller: a `GtkEventControllerKey`
316
 *
317
 * Gets the input method context of the key @controller.
318
 *
319
 * Returns: (transfer none): the `GtkIMContext`
320
321
322
323
324
325
326
327
 **/
GtkIMContext *
gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller)
{
  g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL);

  return controller->im_context;
}
328

329
330
/**
 * gtk_event_controller_key_forward:
331
332
 * @controller: a `GtkEventControllerKey`
 * @widget: a `GtkWidget`
333
334
335
 *
 * Forwards the current event of this @controller to a @widget.
 *
336
 * This function can only be used in handlers for the
337
338
339
 * [signal@Gtk.EventControllerKey::key-pressed],
 * [signal@Gtk.EventControllerKey::key-released]
 * or [signal@Gtk.EventControllerKey::modifiers] signals.
340
 *
341
 * Returns: whether the @widget handled the event
342
 */
343
344
345
346
347
348
349
gboolean
gtk_event_controller_key_forward (GtkEventControllerKey *controller,
                                  GtkWidget             *widget)
{
  g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE);
  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
  g_return_val_if_fail (controller->current_event != NULL, FALSE);
350
351
  g_return_val_if_fail (gdk_event_get_event_type (controller->current_event) == GDK_KEY_PRESS ||
                        gdk_event_get_event_type (controller->current_event) == GDK_KEY_RELEASE, FALSE);
352
353
354
355

  if (!gtk_widget_get_realized (widget))
    gtk_widget_realize (widget);

356
  if (gtk_widget_run_controllers (widget, controller->current_event, widget, 0, 0,
357
                                  GTK_PHASE_CAPTURE))
358
    return TRUE;
359
  if (gtk_widget_run_controllers (widget, controller->current_event, widget, 0, 0,
360
                                  GTK_PHASE_TARGET))
361
    return TRUE;
362
  if (gtk_widget_run_controllers (widget, controller->current_event, widget, 0, 0,
363
                                  GTK_PHASE_BUBBLE))
364
365
366
367
    return TRUE;

  return FALSE;
}
368

369
370
/**
 * gtk_event_controller_key_get_group:
371
 * @controller: a `GtkEventControllerKey`
372
373
 *
 * Gets the key group of the current event of this @controller.
374
375
 *
 * See [method@Gdk.KeyEvent.get_layout].
376
377
 *
 * Returns: the key group
378
 */
379
380
381
382
383
384
guint
gtk_event_controller_key_get_group (GtkEventControllerKey *controller)
{
  g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE);
  g_return_val_if_fail (controller->current_event != NULL, FALSE);

Matthias Clasen's avatar
Matthias Clasen committed
385
  return gdk_key_event_get_layout (controller->current_event);
386
}