1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | /* |
19 | * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
20 | * file for a list of people on the GTK+ Team. See the ChangeLog |
21 | * files for a list of changes. These files are distributed with |
22 | * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | |
27 | #include "gtkwindowprivate.h" |
28 | |
29 | #include "gtkaccessibleprivate.h" |
30 | #include "gtkaccelgroupprivate.h" |
31 | #include "gtkactionable.h" |
32 | #include "gtkapplicationprivate.h" |
33 | #include "gtkbox.h" |
34 | #include "gtkbuildable.h" |
35 | #include "gtkbuilderprivate.h" |
36 | #include "gtkcheckbutton.h" |
37 | #include "gtkcsscornervalueprivate.h" |
38 | #include "gtkcsscolorvalueprivate.h" |
39 | #include "gtkcssshadowvalueprivate.h" |
40 | #include "gtkdroptargetasync.h" |
41 | #include "gtkeventcontrollerlegacy.h" |
42 | #include "gtkeventcontrollerkey.h" |
43 | #include "gtkeventcontrollermotion.h" |
44 | #include "gtkgestureclick.h" |
45 | #include "gtkheaderbar.h" |
46 | #include "gtkicontheme.h" |
47 | #include "gtkintl.h" |
48 | #include "gtkmain.h" |
49 | #include "gtkmarshalers.h" |
50 | #include "gtkmessagedialog.h" |
51 | #include "gtkpointerfocusprivate.h" |
52 | #include "gtkprivate.h" |
53 | #include "gtkroot.h" |
54 | #include "gtknativeprivate.h" |
55 | #include "gtksettings.h" |
56 | #include "gtkshortcut.h" |
57 | #include "gtkshortcutcontrollerprivate.h" |
58 | #include "gtkshortcutmanager.h" |
59 | #include "gtkshortcuttrigger.h" |
60 | #include "gtksizerequest.h" |
61 | #include "gtksnapshot.h" |
62 | #include "gtktypebuiltins.h" |
63 | #include "gtkwidgetprivate.h" |
64 | #include "gtkwindowgroup.h" |
65 | #include "gtkpopovermenubarprivate.h" |
66 | #include "gtkcssboxesimplprivate.h" |
67 | #include "gtktooltipprivate.h" |
68 | #include "gtkmenubutton.h" |
69 | |
70 | #include "inspector/window.h" |
71 | |
72 | #include "gdk/gdkprofilerprivate.h" |
73 | #include "gdk/gdksurfaceprivate.h" |
74 | #include "gdk/gdktextureprivate.h" |
75 | |
76 | #include <cairo-gobject.h> |
77 | #include <errno.h> |
78 | #include <graphene.h> |
79 | #include <limits.h> |
80 | #include <string.h> |
81 | #include <stdlib.h> |
82 | |
83 | #ifdef GDK_WINDOWING_X11 |
84 | #include "x11/gdkx.h" |
85 | #endif |
86 | |
87 | #ifdef GDK_WINDOWING_WIN32 |
88 | #include "win32/gdkwin32.h" |
89 | #endif |
90 | |
91 | #ifdef GDK_WINDOWING_WAYLAND |
92 | #include "wayland/gdkwayland.h" |
93 | #include "wayland/gdkdisplay-wayland.h" |
94 | #include "wayland/gdksurface-wayland.h" |
95 | #endif |
96 | |
97 | #ifdef GDK_WINDOWING_BROADWAY |
98 | #include "broadway/gdkbroadway.h" |
99 | #endif |
100 | |
101 | /** |
102 | * GtkWindow: |
103 | * |
104 | * A `GtkWindow` is a toplevel window which can contain other widgets. |
105 | * |
106 | * ![An example GtkWindow](window.png) |
107 | * |
108 | * Windows normally have decorations that are under the control |
109 | * of the windowing system and allow the user to manipulate the window |
110 | * (resize it, move it, close it,...). |
111 | * |
112 | * # GtkWindow as GtkBuildable |
113 | * |
114 | * The `GtkWindow` implementation of the [iface@Gtk.Buildable] interface supports |
115 | * setting a child as the titlebar by specifying “titlebar” as the “type” |
116 | * attribute of a <child> element. |
117 | * |
118 | * # CSS nodes |
119 | * |
120 | * ``` |
121 | * window.background [.csd / .solid-csd / .ssd] [.maximized / .fullscreen / .tiled] |
122 | * ├── <child> |
123 | * ╰── <titlebar child>.titlebar [.default-decoration] |
124 | * ``` |
125 | * |
126 | * `GtkWindow` has a main CSS node with name window and style class .background. |
127 | * |
128 | * Style classes that are typically used with the main CSS node are .csd (when |
129 | * client-side decorations are in use), .solid-csd (for client-side decorations |
130 | * without invisible borders), .ssd (used by mutter when rendering server-side |
131 | * decorations). GtkWindow also represents window states with the following |
132 | * style classes on the main node: .maximized, .fullscreen, .tiled (when supported, |
133 | * also .tiled-top, .tiled-left, .tiled-right, .tiled-bottom). |
134 | * |
135 | * `GtkWindow` subclasses often add their own discriminating style classes, |
136 | * such as .dialog, .popup or .tooltip. |
137 | * |
138 | * Generally, some CSS properties don't make sense on the toplevel window node, |
139 | * such as margins or padding. When client-side decorations without invisible |
140 | * borders are in use (i.e. the .solid-csd style class is added to the |
141 | * main window node), the CSS border of the toplevel window is used for |
142 | * resize drags. In the .csd case, the shadow area outside of the window |
143 | * can be used to resize it. |
144 | * |
145 | * `GtkWindow` adds the .titlebar and .default-decoration style classes to the |
146 | * widget that is added as a titlebar child. |
147 | * |
148 | * # Accessibility |
149 | * |
150 | * `GtkWindow` uses the %GTK_ACCESSIBLE_ROLE_WINDOW role. |
151 | * |
152 | * # Actions |
153 | * |
154 | * `GtkWindow` defines a set of built-in actions: |
155 | * - `default.activate`: Activate the default widget. |
156 | * - `window.minimize`: Minimize the window. |
157 | * - `window.toggle-maximized`: Maximize or restore the window. |
158 | * - `window.close`: Close the window. |
159 | */ |
160 | |
161 | #define GDK_KEY_F10 |
162 | #define RESIZE_HANDLE_SIZE 12 /* Width of resize borders */ |
163 | #define RESIZE_HANDLE_CORNER_SIZE 24 /* How resize corners extend */ |
164 | #define MNEMONICS_DELAY 300 /* ms */ |
165 | #define NO_CONTENT_CHILD_NAT 200 /* ms */ |
166 | #define VISIBLE_FOCUS_DURATION 3 /* s */ |
167 | |
168 | |
169 | /* In case the content (excluding header bar and shadows) of the window |
170 | * would be empty, either because there is no visible child widget or only an |
171 | * empty container widget, we use NO_CONTENT_CHILD_NAT as natural width/height |
172 | * instead. |
173 | */ |
174 | |
175 | typedef struct _GtkWindowGeometryInfo GtkWindowGeometryInfo; |
176 | |
177 | typedef struct |
178 | { |
179 | GtkWidget *child; |
180 | |
181 | GtkWidget *default_widget; |
182 | GtkWidget *focus_widget; |
183 | GtkWidget *move_focus_widget; |
184 | GtkWindow *transient_parent; |
185 | GtkWindowGeometryInfo *geometry_info; |
186 | GtkWindowGroup *group; |
187 | GdkDisplay *display; |
188 | GtkApplication *application; |
189 | |
190 | int default_width; |
191 | int default_height; |
192 | |
193 | char *startup_id; |
194 | char *title; |
195 | |
196 | guint keys_changed_handler; |
197 | |
198 | guint32 initial_timestamp; |
199 | |
200 | guint mnemonics_display_timeout_id; |
201 | |
202 | guint focus_visible_timeout; |
203 | |
204 | int scale; |
205 | |
206 | int title_height; |
207 | GtkWidget *title_box; |
208 | GtkWidget *titlebar; |
209 | GtkWidget *key_press_focus; |
210 | |
211 | GdkMonitor *initial_fullscreen_monitor; |
212 | guint edge_constraints; |
213 | |
214 | GdkToplevelState state; |
215 | |
216 | /* The following flags are initially TRUE (before a window is mapped). |
217 | * They cause us to compute a configure request that involves |
218 | * default-only parameters. Once mapped, we set them to FALSE. |
219 | * Then we set them to TRUE again on unmap (for position) |
220 | * and on unrealize (for size). |
221 | */ |
222 | guint need_default_size : 1; |
223 | |
224 | guint decorated : 1; |
225 | guint deletable : 1; |
226 | guint destroy_with_parent : 1; |
227 | guint minimize_initially : 1; |
228 | guint is_active : 1; |
229 | guint mnemonics_visible : 1; |
230 | guint focus_visible : 1; |
231 | guint modal : 1; |
232 | guint resizable : 1; |
233 | guint transient_parent_group : 1; |
234 | guint csd_requested : 1; |
235 | guint client_decorated : 1; /* Decorations drawn client-side */ |
236 | guint use_client_shadow : 1; /* Decorations use client-side shadows */ |
237 | guint maximized : 1; |
238 | guint fullscreen : 1; |
239 | guint tiled : 1; |
240 | |
241 | guint hide_on_close : 1; |
242 | guint in_emit_close_request : 1; |
243 | guint move_focus : 1; |
244 | guint unset_default : 1; |
245 | |
246 | |
247 | GtkGesture *click_gesture; |
248 | GtkEventController *application_shortcut_controller; |
249 | |
250 | GdkSurface *surface; |
251 | GskRenderer *renderer; |
252 | |
253 | GList *foci; |
254 | |
255 | GtkConstraintSolver *constraint_solver; |
256 | |
257 | int surface_width; |
258 | int surface_height; |
259 | |
260 | GdkCursor *resize_cursor; |
261 | |
262 | GtkEventController *; |
263 | } GtkWindowPrivate; |
264 | |
265 | enum { |
266 | SET_FOCUS, |
267 | ACTIVATE_FOCUS, |
268 | ACTIVATE_DEFAULT, |
269 | KEYS_CHANGED, |
270 | ENABLE_DEBUGGING, |
271 | CLOSE_REQUEST, |
272 | LAST_SIGNAL |
273 | }; |
274 | |
275 | enum { |
276 | PROP_0, |
277 | |
278 | /* Normal Props */ |
279 | PROP_TITLE, |
280 | PROP_RESIZABLE, |
281 | PROP_MODAL, |
282 | PROP_DEFAULT_WIDTH, |
283 | PROP_DEFAULT_HEIGHT, |
284 | PROP_DESTROY_WITH_PARENT, |
285 | PROP_HIDE_ON_CLOSE, |
286 | PROP_ICON_NAME, |
287 | PROP_DISPLAY, |
288 | PROP_DECORATED, |
289 | PROP_DELETABLE, |
290 | PROP_TRANSIENT_FOR, |
291 | PROP_APPLICATION, |
292 | PROP_DEFAULT_WIDGET, |
293 | PROP_FOCUS_WIDGET, |
294 | PROP_CHILD, |
295 | PROP_TITLEBAR, |
296 | PROP_HANDLE_MENUBAR_ACCEL, |
297 | |
298 | /* Readonly properties */ |
299 | PROP_IS_ACTIVE, |
300 | |
301 | /* Writeonly properties */ |
302 | PROP_STARTUP_ID, |
303 | |
304 | PROP_MNEMONICS_VISIBLE, |
305 | PROP_FOCUS_VISIBLE, |
306 | |
307 | PROP_MAXIMIZED, |
308 | PROP_FULLSCREENED, |
309 | |
310 | LAST_ARG |
311 | }; |
312 | |
313 | static GParamSpec *window_props[LAST_ARG] = { NULL, }; |
314 | |
315 | /* Must be kept in sync with GdkSurfaceEdge ! */ |
316 | typedef enum |
317 | { |
318 | GTK_WINDOW_REGION_EDGE_NW, |
319 | GTK_WINDOW_REGION_EDGE_N, |
320 | GTK_WINDOW_REGION_EDGE_NE, |
321 | GTK_WINDOW_REGION_EDGE_W, |
322 | GTK_WINDOW_REGION_EDGE_E, |
323 | GTK_WINDOW_REGION_EDGE_SW, |
324 | GTK_WINDOW_REGION_EDGE_S, |
325 | GTK_WINDOW_REGION_EDGE_SE, |
326 | GTK_WINDOW_REGION_CONTENT, |
327 | } GtkWindowRegion; |
328 | |
329 | typedef struct |
330 | { |
331 | char *icon_name; |
332 | guint realized : 1; |
333 | guint using_default_icon : 1; |
334 | guint using_themed_icon : 1; |
335 | } GtkWindowIconInfo; |
336 | |
337 | typedef struct { |
338 | GdkGeometry geometry; /* Last set of geometry hints we set */ |
339 | GdkSurfaceHints flags; |
340 | GdkRectangle configure_request; |
341 | } GtkWindowLastGeometryInfo; |
342 | |
343 | struct _GtkWindowGeometryInfo |
344 | { |
345 | GtkWindowLastGeometryInfo last; |
346 | }; |
347 | |
348 | static void gtk_window_constructed (GObject *object); |
349 | static void gtk_window_dispose (GObject *object); |
350 | static void gtk_window_finalize (GObject *object); |
351 | static void gtk_window_show (GtkWidget *widget); |
352 | static void gtk_window_hide (GtkWidget *widget); |
353 | static void gtk_window_map (GtkWidget *widget); |
354 | static void gtk_window_unmap (GtkWidget *widget); |
355 | static void gtk_window_realize (GtkWidget *widget); |
356 | static void gtk_window_unrealize (GtkWidget *widget); |
357 | static void gtk_window_size_allocate (GtkWidget *widget, |
358 | int width, |
359 | int height, |
360 | int baseline); |
361 | static gboolean gtk_window_close_request (GtkWindow *window); |
362 | static gboolean gtk_window_handle_focus (GtkWidget *widget, |
363 | GdkEvent *event, |
364 | double x, |
365 | double y); |
366 | static gboolean gtk_window_key_pressed (GtkWidget *widget, |
367 | guint keyval, |
368 | guint keycode, |
369 | GdkModifierType state, |
370 | gpointer data); |
371 | static gboolean gtk_window_key_released (GtkWidget *widget, |
372 | guint keyval, |
373 | guint keycode, |
374 | GdkModifierType state, |
375 | gpointer data); |
376 | |
377 | static void surface_state_changed (GtkWidget *widget); |
378 | static void surface_size_changed (GtkWidget *widget, |
379 | int width, |
380 | int height); |
381 | static gboolean surface_render (GdkSurface *surface, |
382 | cairo_region_t *region, |
383 | GtkWidget *widget); |
384 | static gboolean surface_event (GdkSurface *surface, |
385 | GdkEvent *event, |
386 | GtkWidget *widget); |
387 | static void after_paint (GdkFrameClock *clock, |
388 | GtkWindow *window); |
389 | |
390 | static int gtk_window_focus (GtkWidget *widget, |
391 | GtkDirectionType direction); |
392 | static void gtk_window_move_focus (GtkWidget *widget, |
393 | GtkDirectionType dir); |
394 | |
395 | static void gtk_window_get_remembered_size (GtkWindow *window, |
396 | int *width, |
397 | int *height); |
398 | |
399 | static void gtk_window_real_activate_default (GtkWindow *window); |
400 | static void gtk_window_real_activate_focus (GtkWindow *window); |
401 | static void gtk_window_keys_changed (GtkWindow *window); |
402 | static gboolean gtk_window_enable_debugging (GtkWindow *window, |
403 | gboolean toggle); |
404 | static void gtk_window_unset_transient_for (GtkWindow *window); |
405 | static void gtk_window_transient_parent_realized (GtkWidget *parent, |
406 | GtkWidget *window); |
407 | static void gtk_window_transient_parent_unrealized (GtkWidget *parent, |
408 | GtkWidget *window); |
409 | |
410 | static GtkWindowGeometryInfo* gtk_window_get_geometry_info (GtkWindow *window, |
411 | gboolean create); |
412 | |
413 | static void gtk_window_set_default_size_internal (GtkWindow *window, |
414 | gboolean change_width, |
415 | int width, |
416 | gboolean change_height, |
417 | int height); |
418 | |
419 | static void update_themed_icon (GtkWindow *window); |
420 | static GList *icon_list_from_theme (GtkWindow *window, |
421 | const char *name); |
422 | static void gtk_window_realize_icon (GtkWindow *window); |
423 | static void gtk_window_unrealize_icon (GtkWindow *window); |
424 | static void update_window_actions (GtkWindow *window); |
425 | static void get_shadow_width (GtkWindow *window, |
426 | GtkBorder *shadow_width); |
427 | |
428 | static gboolean gtk_window_activate_menubar (GtkWidget *widget, |
429 | GVariant *args, |
430 | gpointer unused); |
431 | #ifdef GDK_WINDOWING_X11 |
432 | static void gtk_window_on_theme_variant_changed (GtkSettings *settings, |
433 | GParamSpec *pspec, |
434 | GtkWindow *window); |
435 | #endif |
436 | static void gtk_window_set_theme_variant (GtkWindow *window); |
437 | |
438 | static void gtk_window_activate_default_activate (GtkWidget *widget, |
439 | const char *action_name, |
440 | GVariant *parameter); |
441 | static void gtk_window_activate_minimize (GtkWidget *widget, |
442 | const char *action_name, |
443 | GVariant *parameter); |
444 | static void gtk_window_activate_toggle_maximized (GtkWidget *widget, |
445 | const char *name, |
446 | GVariant *parameter); |
447 | static void gtk_window_activate_close (GtkWidget *widget, |
448 | const char *action_name, |
449 | GVariant *parameter); |
450 | |
451 | static void gtk_window_css_changed (GtkWidget *widget, |
452 | GtkCssStyleChange *change); |
453 | static void _gtk_window_set_is_active (GtkWindow *window, |
454 | gboolean is_active); |
455 | static void gtk_window_present_toplevel (GtkWindow *window); |
456 | static void gtk_window_update_toplevel (GtkWindow *window, |
457 | GdkToplevelLayout *layout); |
458 | |
459 | static void gtk_window_release_application (GtkWindow *window); |
460 | |
461 | static GListStore *toplevel_list = NULL; |
462 | static guint window_signals[LAST_SIGNAL] = { 0 }; |
463 | static char *default_icon_name = NULL; |
464 | static gboolean disable_startup_notification = FALSE; |
465 | |
466 | static GQuark quark_gtk_window_icon_info = 0; |
467 | |
468 | static GtkBuildableIface *parent_buildable_iface; |
469 | |
470 | static void gtk_window_set_property (GObject *object, |
471 | guint prop_id, |
472 | const GValue *value, |
473 | GParamSpec *pspec); |
474 | static void gtk_window_get_property (GObject *object, |
475 | guint prop_id, |
476 | GValue *value, |
477 | GParamSpec *pspec); |
478 | |
479 | /* GtkBuildable */ |
480 | static void gtk_window_buildable_interface_init (GtkBuildableIface *iface); |
481 | static void gtk_window_buildable_add_child (GtkBuildable *buildable, |
482 | GtkBuilder *builder, |
483 | GObject *child, |
484 | const char *type); |
485 | |
486 | static void gtk_window_shortcut_manager_interface_init (GtkShortcutManagerInterface *iface); |
487 | /* GtkRoot */ |
488 | static void gtk_window_root_interface_init (GtkRootInterface *iface); |
489 | static void gtk_window_native_interface_init (GtkNativeInterface *iface); |
490 | |
491 | static void gtk_window_accessible_interface_init (GtkAccessibleInterface *iface); |
492 | |
493 | |
494 | static void ensure_state_flag_backdrop (GtkWidget *widget); |
495 | static void unset_titlebar (GtkWindow *window); |
496 | |
497 | #define INCLUDE_CSD_SIZE 1 |
498 | #define EXCLUDE_CSD_SIZE -1 |
499 | |
500 | static void |
501 | gtk_window_update_csd_size (GtkWindow *window, |
502 | int *width, |
503 | int *height, |
504 | int apply); |
505 | |
506 | G_DEFINE_TYPE_WITH_CODE (GtkWindow, gtk_window, GTK_TYPE_WIDGET, |
507 | G_ADD_PRIVATE (GtkWindow) |
508 | G_IMPLEMENT_INTERFACE (GTK_TYPE_ACCESSIBLE, |
509 | gtk_window_accessible_interface_init) |
510 | G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, |
511 | gtk_window_buildable_interface_init) |
512 | G_IMPLEMENT_INTERFACE (GTK_TYPE_NATIVE, |
513 | gtk_window_native_interface_init) |
514 | G_IMPLEMENT_INTERFACE (GTK_TYPE_SHORTCUT_MANAGER, |
515 | gtk_window_shortcut_manager_interface_init) |
516 | G_IMPLEMENT_INTERFACE (GTK_TYPE_ROOT, |
517 | gtk_window_root_interface_init)) |
518 | |
519 | static GtkAccessibleInterface *parent_accessible_iface; |
520 | |
521 | static gboolean |
522 | gtk_window_accessible_get_platform_state (GtkAccessible *self, |
523 | GtkAccessiblePlatformState state) |
524 | { |
525 | switch (state) |
526 | { |
527 | case GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSABLE: |
528 | case GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSED: |
529 | return parent_accessible_iface->get_platform_state (self, state); |
530 | case GTK_ACCESSIBLE_PLATFORM_STATE_ACTIVE: |
531 | return gtk_window_is_active (GTK_WINDOW (self)); |
532 | default: |
533 | g_assert_not_reached (); |
534 | } |
535 | } |
536 | |
537 | static void |
538 | gtk_window_accessible_interface_init (GtkAccessibleInterface *iface) |
539 | { |
540 | parent_accessible_iface = g_type_interface_peek_parent (g_iface: iface); |
541 | iface->get_at_context = parent_accessible_iface->get_at_context; |
542 | iface->get_platform_state = gtk_window_accessible_get_platform_state; |
543 | } |
544 | |
545 | static void |
546 | add_tab_bindings (GtkWidgetClass *widget_class, |
547 | GdkModifierType modifiers, |
548 | GtkDirectionType direction) |
549 | { |
550 | GtkShortcut *shortcut; |
551 | |
552 | shortcut = gtk_shortcut_new_with_arguments ( |
553 | trigger: gtk_alternative_trigger_new (first: gtk_keyval_trigger_new (GDK_KEY_Tab, modifiers), |
554 | second: gtk_keyval_trigger_new (GDK_KEY_KP_Tab, modifiers)), |
555 | action: gtk_signal_action_new (signal_name: "move-focus" ), |
556 | format_string: "(i)" , direction); |
557 | |
558 | gtk_widget_class_add_shortcut (widget_class, shortcut); |
559 | |
560 | g_object_unref (object: shortcut); |
561 | } |
562 | |
563 | static void |
564 | add_arrow_bindings (GtkWidgetClass *widget_class, |
565 | guint keysym, |
566 | GtkDirectionType direction) |
567 | { |
568 | guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left; |
569 | |
570 | gtk_widget_class_add_binding_signal (widget_class, keyval: keysym, mods: 0, |
571 | signal: "move-focus" , |
572 | format_string: "(i)" , |
573 | direction); |
574 | gtk_widget_class_add_binding_signal (widget_class, keyval: keysym, mods: GDK_CONTROL_MASK, |
575 | signal: "move-focus" , |
576 | format_string: "(i)" , |
577 | direction); |
578 | gtk_widget_class_add_binding_signal (widget_class, keyval: keypad_keysym, mods: 0, |
579 | signal: "move-focus" , |
580 | format_string: "(i)" , |
581 | direction); |
582 | gtk_widget_class_add_binding_signal (widget_class, keyval: keypad_keysym, mods: GDK_CONTROL_MASK, |
583 | signal: "move-focus" , |
584 | format_string: "(i)" , |
585 | direction); |
586 | } |
587 | |
588 | static guint32 |
589 | (const char * startup_id) |
590 | { |
591 | char *timestr = g_strrstr (haystack: startup_id, needle: "_TIME" ); |
592 | guint32 retval = GDK_CURRENT_TIME; |
593 | |
594 | if (timestr) |
595 | { |
596 | char *end; |
597 | guint32 timestamp; |
598 | |
599 | /* Skip past the "_TIME" part */ |
600 | timestr += 5; |
601 | |
602 | end = NULL; |
603 | errno = 0; |
604 | timestamp = g_ascii_strtoull (nptr: timestr, endptr: &end, base: 0); |
605 | if (errno == 0 && end != timestr) |
606 | retval = timestamp; |
607 | } |
608 | |
609 | return retval; |
610 | } |
611 | |
612 | static gboolean |
613 | startup_id_is_fake (const char * startup_id) |
614 | { |
615 | return strncmp (s1: startup_id, s2: "_TIME" , n: 5) == 0; |
616 | } |
617 | |
618 | static void |
619 | gtk_window_measure (GtkWidget *widget, |
620 | GtkOrientation orientation, |
621 | int for_size, |
622 | int *minimum, |
623 | int *natural, |
624 | int *minimum_baseline, |
625 | int *natural_baseline) |
626 | { |
627 | GtkWindow *window = GTK_WINDOW (widget); |
628 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
629 | GtkWidget *child = priv->child; |
630 | gboolean has_size_request = gtk_widget_has_size_request (widget); |
631 | int title_for_size = for_size; |
632 | int title_min_size = 0; |
633 | int title_nat_size = 0; |
634 | int child_for_size = for_size; |
635 | int child_min_size = 0; |
636 | int child_nat_size = 0; |
637 | |
638 | if (priv->decorated && !priv->fullscreen) |
639 | { |
640 | if (priv->title_box != NULL && |
641 | gtk_widget_get_visible (widget: priv->title_box) && |
642 | gtk_widget_get_child_visible (widget: priv->title_box)) |
643 | { |
644 | if (orientation == GTK_ORIENTATION_HORIZONTAL && for_size >= 0 && |
645 | child != NULL && gtk_widget_get_visible (widget: child)) |
646 | { |
647 | GtkRequestedSize sizes[2]; |
648 | |
649 | gtk_widget_measure (widget: priv->title_box, |
650 | orientation: GTK_ORIENTATION_VERTICAL, |
651 | for_size: -1, |
652 | minimum: &sizes[0].minimum_size, natural: &sizes[0].natural_size, |
653 | NULL, NULL); |
654 | gtk_widget_measure (widget: child, |
655 | orientation: GTK_ORIENTATION_VERTICAL, |
656 | for_size: -1, |
657 | minimum: &sizes[1].minimum_size, natural: &sizes[1].natural_size, |
658 | NULL, NULL); |
659 | for_size -= sizes[0].minimum_size + sizes[1].minimum_size; |
660 | for_size = gtk_distribute_natural_allocation (extra_space: for_size, n_requested_sizes: 2, sizes); |
661 | title_for_size = sizes[0].minimum_size; |
662 | child_for_size = sizes[1].minimum_size + for_size; |
663 | } |
664 | |
665 | gtk_widget_measure (widget: priv->title_box, |
666 | orientation, |
667 | for_size: title_for_size, |
668 | minimum: &title_min_size, natural: &title_nat_size, |
669 | NULL, NULL); |
670 | } |
671 | } |
672 | |
673 | if (child != NULL && gtk_widget_get_visible (widget: child)) |
674 | { |
675 | gtk_widget_measure (widget: child, |
676 | orientation, |
677 | for_size: child_for_size, |
678 | minimum: &child_min_size, natural: &child_nat_size, |
679 | NULL, NULL); |
680 | |
681 | if (child_nat_size == 0 && !has_size_request) |
682 | child_nat_size = NO_CONTENT_CHILD_NAT; |
683 | } |
684 | else if (!has_size_request) |
685 | { |
686 | child_nat_size = NO_CONTENT_CHILD_NAT; |
687 | } |
688 | |
689 | if (orientation == GTK_ORIENTATION_HORIZONTAL) |
690 | { |
691 | *minimum = MAX (title_min_size, child_min_size); |
692 | *natural = MAX (title_nat_size, child_nat_size); |
693 | } |
694 | else |
695 | { |
696 | *minimum = title_min_size + child_min_size; |
697 | *natural = title_nat_size + child_nat_size; |
698 | } |
699 | } |
700 | |
701 | static void |
702 | gtk_window_compute_expand (GtkWidget *widget, |
703 | gboolean *hexpand, |
704 | gboolean *vexpand) |
705 | { |
706 | GtkWindow *window = GTK_WINDOW (widget); |
707 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
708 | |
709 | if (priv->child) |
710 | { |
711 | *hexpand = gtk_widget_compute_expand (widget: priv->child, orientation: GTK_ORIENTATION_HORIZONTAL); |
712 | *vexpand = gtk_widget_compute_expand (widget: priv->child, orientation: GTK_ORIENTATION_VERTICAL); |
713 | } |
714 | else |
715 | { |
716 | *hexpand = FALSE; |
717 | *vexpand = FALSE; |
718 | } |
719 | } |
720 | |
721 | static GtkSizeRequestMode |
722 | gtk_window_get_request_mode (GtkWidget *widget) |
723 | { |
724 | GtkWindow *window = GTK_WINDOW (widget); |
725 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
726 | |
727 | if (priv->child) |
728 | return gtk_widget_get_request_mode (widget: priv->child); |
729 | else |
730 | return GTK_SIZE_REQUEST_CONSTANT_SIZE; |
731 | } |
732 | |
733 | static void |
734 | gtk_window_class_init (GtkWindowClass *klass) |
735 | { |
736 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
737 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
738 | |
739 | quark_gtk_window_icon_info = g_quark_from_static_string (string: "gtk-window-icon-info" ); |
740 | |
741 | if (toplevel_list == NULL) |
742 | toplevel_list = g_list_store_new (GTK_TYPE_WIDGET); |
743 | |
744 | gobject_class->constructed = gtk_window_constructed; |
745 | gobject_class->dispose = gtk_window_dispose; |
746 | gobject_class->finalize = gtk_window_finalize; |
747 | |
748 | gobject_class->set_property = gtk_window_set_property; |
749 | gobject_class->get_property = gtk_window_get_property; |
750 | |
751 | widget_class->show = gtk_window_show; |
752 | widget_class->hide = gtk_window_hide; |
753 | widget_class->map = gtk_window_map; |
754 | widget_class->unmap = gtk_window_unmap; |
755 | widget_class->realize = gtk_window_realize; |
756 | widget_class->unrealize = gtk_window_unrealize; |
757 | widget_class->size_allocate = gtk_window_size_allocate; |
758 | widget_class->compute_expand = gtk_window_compute_expand; |
759 | widget_class->get_request_mode = gtk_window_get_request_mode; |
760 | widget_class->focus = gtk_window_focus; |
761 | widget_class->move_focus = gtk_window_move_focus; |
762 | widget_class->measure = gtk_window_measure; |
763 | widget_class->css_changed = gtk_window_css_changed; |
764 | |
765 | klass->activate_default = gtk_window_real_activate_default; |
766 | klass->activate_focus = gtk_window_real_activate_focus; |
767 | klass->keys_changed = gtk_window_keys_changed; |
768 | klass->enable_debugging = gtk_window_enable_debugging; |
769 | klass->close_request = gtk_window_close_request; |
770 | |
771 | /** |
772 | * GtkWindow:title: (attributes org.gtk.Property.get=gtk_window_get_title org.gtk.Property.set=gtk_window_set_title) |
773 | * |
774 | * The title of the window. |
775 | */ |
776 | window_props[PROP_TITLE] = |
777 | g_param_spec_string (name: "title" , |
778 | P_("Window Title" ), |
779 | P_("The title of the window" ), |
780 | NULL, |
781 | GTK_PARAM_READWRITE); |
782 | |
783 | /** |
784 | * GtkWindow:startup-id: (attributes org.gtk.Property.set=gtk_window_set_startup_id) |
785 | * |
786 | * A write-only property for setting window's startup notification identifier. |
787 | */ |
788 | window_props[PROP_STARTUP_ID] = |
789 | g_param_spec_string (name: "startup-id" , |
790 | P_("Startup ID" ), |
791 | P_("Unique startup identifier for the window used by startup-notification" ), |
792 | NULL, |
793 | GTK_PARAM_WRITABLE); |
794 | |
795 | /** |
796 | * GtkWindow:resizable: (attributes org.gtk.Property.get=gtk_window_get_resizable org.gtk.Property.set=gtk_window_set_resizable) |
797 | * |
798 | * If %TRUE, users can resize the window. |
799 | */ |
800 | window_props[PROP_RESIZABLE] = |
801 | g_param_spec_boolean (name: "resizable" , |
802 | P_("Resizable" ), |
803 | P_("If TRUE, users can resize the window" ), |
804 | TRUE, |
805 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
806 | |
807 | /** |
808 | * GtkWindow:modal: (attributes org.gtk.Property.get=gtk_window_get_modal org.gtk.Property.set=gtk_window_set_modal) |
809 | * |
810 | * If %TRUE, the window is modal. |
811 | */ |
812 | window_props[PROP_MODAL] = |
813 | g_param_spec_boolean (name: "modal" , |
814 | P_("Modal" ), |
815 | P_("If TRUE, the window is modal (other windows are not usable while this one is up)" ), |
816 | FALSE, |
817 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
818 | |
819 | /** |
820 | * GtkWindow:default-width: |
821 | * |
822 | * The default width of the window. |
823 | */ |
824 | window_props[PROP_DEFAULT_WIDTH] = |
825 | g_param_spec_int (name: "default-width" , |
826 | P_("Default Width" ), |
827 | P_("The default width of the window" ), |
828 | minimum: -1, G_MAXINT, |
829 | default_value: 0, |
830 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
831 | |
832 | /** |
833 | * GtkWindow:default-height: |
834 | * |
835 | * The default height of the window. |
836 | */ |
837 | window_props[PROP_DEFAULT_HEIGHT] = |
838 | g_param_spec_int (name: "default-height" , |
839 | P_("Default Height" ), |
840 | P_("The default height of the window" ), |
841 | minimum: -1, G_MAXINT, |
842 | default_value: 0, |
843 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
844 | |
845 | /** |
846 | * GtkWindow:destroy-with-parent: (attributes org.gtk.Property.get=gtk_window_get_destroy_with_parent org.gtk.Property.set=gtk_window_set_destroy_with_parent) |
847 | * |
848 | * If this window should be destroyed when the parent is destroyed. |
849 | */ |
850 | window_props[PROP_DESTROY_WITH_PARENT] = |
851 | g_param_spec_boolean (name: "destroy-with-parent" , |
852 | P_("Destroy with Parent" ), |
853 | P_("If this window should be destroyed when the parent is destroyed" ), |
854 | FALSE, |
855 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
856 | |
857 | /** |
858 | * GtkWindow:hide-on-close: (attributes org.gtk.Property.get=gtk_window_get_hide_on_close org.gtk.Property.set=gtk_window_set_hide_on_close) |
859 | * |
860 | * If this window should be hidden when the users clicks the close button. |
861 | */ |
862 | window_props[PROP_HIDE_ON_CLOSE] = |
863 | g_param_spec_boolean (name: "hide-on-close" , |
864 | P_("Hide on close" ), |
865 | P_("If this window should be hidden when the user clicks the close button" ), |
866 | FALSE, |
867 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
868 | |
869 | /** |
870 | * GtkWindow:mnemonics-visible: (attributes org.gtk.Property.get=gtk_window_get_mnemonics_visible org.gtk.Property.set=gtk_window_set_mnemonics_visible) |
871 | * |
872 | * Whether mnemonics are currently visible in this window. |
873 | * |
874 | * This property is maintained by GTK based on user input, |
875 | * and should not be set by applications. |
876 | */ |
877 | window_props[PROP_MNEMONICS_VISIBLE] = |
878 | g_param_spec_boolean (name: "mnemonics-visible" , |
879 | P_("Mnemonics Visible" ), |
880 | P_("Whether mnemonics are currently visible in this window" ), |
881 | FALSE, |
882 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
883 | |
884 | /** |
885 | * GtkWindow:focus-visible: (attributes org.gtk.Property.get=gtk_window_get_focus_visible org.gtk.Property.set=gtk_window_set_focus_visible) |
886 | * |
887 | * Whether 'focus rectangles' are currently visible in this window. |
888 | * |
889 | * This property is maintained by GTK based on user input |
890 | * and should not be set by applications. |
891 | */ |
892 | window_props[PROP_FOCUS_VISIBLE] = |
893 | g_param_spec_boolean (name: "focus-visible" , |
894 | P_("Focus Visible" ), |
895 | P_("Whether focus rectangles are currently visible in this window" ), |
896 | TRUE, |
897 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
898 | |
899 | /** |
900 | * GtkWindow:icon-name: (attributes org.gtk.Property.get=gtk_window_get_icon_name org.gtk.Property.set=gtk_window_set_icon_name) |
901 | * |
902 | * Specifies the name of the themed icon to use as the window icon. |
903 | * |
904 | * See [class@Gtk.IconTheme] for more details. |
905 | */ |
906 | window_props[PROP_ICON_NAME] = |
907 | g_param_spec_string (name: "icon-name" , |
908 | P_("Icon Name" ), |
909 | P_("Name of the themed icon for this window" ), |
910 | NULL, |
911 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
912 | |
913 | /** |
914 | * GtkWindow:display: (attributes org.gtk.Property.set=gtk_window_set_display) |
915 | * |
916 | * The display that will display this window. |
917 | */ |
918 | window_props[PROP_DISPLAY] = |
919 | g_param_spec_object (name: "display" , |
920 | P_("Display" ), |
921 | P_("The display that will display this window" ), |
922 | GDK_TYPE_DISPLAY, |
923 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
924 | |
925 | /** |
926 | * GtkWindow:is-active: (attributes org.gtk.Property.get=gtk_window_is_active) |
927 | * |
928 | * Whether the toplevel is the currently active window. |
929 | */ |
930 | window_props[PROP_IS_ACTIVE] = |
931 | g_param_spec_boolean (name: "is-active" , |
932 | P_("Is Active" ), |
933 | P_("Whether the toplevel is the current active window" ), |
934 | FALSE, |
935 | GTK_PARAM_READABLE); |
936 | |
937 | /** |
938 | * GtkWindow:decorated: (attributes org.gtk.Property.get=gtk_window_get_decorated org.gtk.Property.set=gtk_window_set_decorated) |
939 | * |
940 | * Whether the window should have a frame (also known as *decorations*). |
941 | */ |
942 | window_props[PROP_DECORATED] = |
943 | g_param_spec_boolean (name: "decorated" , |
944 | P_("Decorated" ), |
945 | P_("Whether the window should be decorated by the window manager" ), |
946 | TRUE, |
947 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
948 | |
949 | /** |
950 | * GtkWindow:deletable: (attributes org.gtk.Property.get=gtk_window_get_deletable org.gtk.Property.set=gtk_window_set_deletable) |
951 | * |
952 | * Whether the window frame should have a close button. |
953 | */ |
954 | window_props[PROP_DELETABLE] = |
955 | g_param_spec_boolean (name: "deletable" , |
956 | P_("Deletable" ), |
957 | P_("Whether the window frame should have a close button" ), |
958 | TRUE, |
959 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
960 | |
961 | /** |
962 | * GtkWindow:transient-for: (attributes org.gtk.Property.get=gtk_window_get_transient_for org.gtk.Property.set=gtk_window_set_transient_for) |
963 | * |
964 | * The transient parent of the window. |
965 | */ |
966 | window_props[PROP_TRANSIENT_FOR] = |
967 | g_param_spec_object (name: "transient-for" , |
968 | P_("Transient for Window" ), |
969 | P_("The transient parent of the dialog" ), |
970 | GTK_TYPE_WINDOW, |
971 | GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY); |
972 | |
973 | /** |
974 | * GtkWindow:maximized: (attributes org.gtk.Property.get=gtk_window_is_maximized) |
975 | * |
976 | * Whether the window is maximized. |
977 | * |
978 | * Setting this property is the equivalent of calling |
979 | * [method@Gtk.Window.maximize] or [method@Gtk.Window.unmaximize]; |
980 | * either operation is asynchronous, which means you will need to |
981 | * connect to the ::notify signal in order to know whether the |
982 | * operation was successful. |
983 | */ |
984 | window_props[PROP_MAXIMIZED] = |
985 | g_param_spec_boolean (name: "maximized" , |
986 | P_("Is Maximized" ), |
987 | P_("Whether the window is maximized" ), |
988 | FALSE, |
989 | GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY); |
990 | |
991 | /** |
992 | * GtkWindow:fullscreened: (attributes org.gtk.Property.get=gtk_window_is_fullscreen) |
993 | * |
994 | * Whether the window is fullscreen. |
995 | * |
996 | * Setting this property is the equivalent of calling |
997 | * [method@Gtk.Window.fullscreen] or [method@Gtk.Window.unfullscreen]; |
998 | * either operation is asynchronous, which means you will need to |
999 | * connect to the ::notify signal in order to know whether the |
1000 | * operation was successful. |
1001 | */ |
1002 | window_props[PROP_FULLSCREENED] = |
1003 | g_param_spec_boolean (name: "fullscreened" , |
1004 | P_("Is fullscreen" ), |
1005 | P_("Whether the window is fullscreen" ), |
1006 | FALSE, |
1007 | GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY); |
1008 | |
1009 | /** |
1010 | * GtkWindow:application: (attributes org.gtk.Property.get=gtk_window_get_application org.gtk.Property.set=gtk_window_set_application) |
1011 | * |
1012 | * The `GtkApplication` associated with the window. |
1013 | * |
1014 | * The application will be kept alive for at least as long as it |
1015 | * has any windows associated with it (see g_application_hold() |
1016 | * for a way to keep it alive without windows). |
1017 | * |
1018 | * Normally, the connection between the application and the window |
1019 | * will remain until the window is destroyed, but you can explicitly |
1020 | * remove it by setting the :application property to %NULL. |
1021 | */ |
1022 | window_props[PROP_APPLICATION] = |
1023 | g_param_spec_object (name: "application" , |
1024 | P_("GtkApplication" ), |
1025 | P_("The GtkApplication for the window" ), |
1026 | GTK_TYPE_APPLICATION, |
1027 | GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY); |
1028 | |
1029 | /** |
1030 | * GtkWindow:default-widget: (attributes org.gtk.Property.get=gtk_window_get_default_widget org.gtk.Property.set=gtk_window_set_default_widget) |
1031 | * |
1032 | * The default widget. |
1033 | */ |
1034 | window_props[PROP_DEFAULT_WIDGET] = |
1035 | g_param_spec_object (name: "default-widget" , |
1036 | P_("Default widget" ), |
1037 | P_("The default widget" ), |
1038 | GTK_TYPE_WIDGET, |
1039 | GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY); |
1040 | |
1041 | /** |
1042 | * GtkWindow:focus-widget: (attributes org.gtk.Property.get=gtk_window_get_focus org.gtk.Property.set=gtk_window_set_focus) |
1043 | * |
1044 | * The focus widget. |
1045 | */ |
1046 | window_props[PROP_FOCUS_WIDGET] = |
1047 | g_param_spec_object (name: "focus-widget" , |
1048 | P_("Focus widget" ), |
1049 | P_("The focus widget" ), |
1050 | GTK_TYPE_WIDGET, |
1051 | GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY); |
1052 | |
1053 | /** |
1054 | * GtkWindow:child: (attributes org.gtk.Property.get=gtk_window_get_child org.gtk.Property.set=gtk_window_set_child) |
1055 | * |
1056 | * The child widget. |
1057 | */ |
1058 | window_props[PROP_CHILD] = |
1059 | g_param_spec_object (name: "child" , |
1060 | P_("Child" ), |
1061 | P_("The child widget" ), |
1062 | GTK_TYPE_WIDGET, |
1063 | GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY); |
1064 | |
1065 | /** |
1066 | * GtkWindow:titlebar: (attributes org.gtk.Property.get=gtk_window_get_titlebar org.gtk.Property.set=gtk_window_set_titlebar) |
1067 | * |
1068 | * The titlebar widget. |
1069 | * |
1070 | * Since: 4.6 |
1071 | */ |
1072 | window_props[PROP_TITLEBAR] = |
1073 | g_param_spec_object (name: "titlebar" , |
1074 | P_("Titlebar" ), |
1075 | P_("The titlebar widget" ), |
1076 | GTK_TYPE_WIDGET, |
1077 | GTK_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY); |
1078 | |
1079 | /** |
1080 | * GtkWindow:handle-menubar-accel: (attributes org.gtk.Property.get=gtk_window_get_handle_menubar_accel org.gtk.Property.set=gtk_window_set_handle_menubar_accel) |
1081 | * |
1082 | * Whether the window frame should handle F10 for activating |
1083 | * menubars. |
1084 | * |
1085 | * Since: 4.2 |
1086 | */ |
1087 | window_props[PROP_HANDLE_MENUBAR_ACCEL] = |
1088 | g_param_spec_boolean (name: "handle-menubar-accel" , |
1089 | P_("Handle Menubar accels" ), |
1090 | P_("Whether the window should handle F10" ), |
1091 | TRUE, |
1092 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
1093 | |
1094 | g_object_class_install_properties (oclass: gobject_class, n_pspecs: LAST_ARG, pspecs: window_props); |
1095 | |
1096 | /** |
1097 | * GtkWindow::activate-focus: |
1098 | * @window: the window which received the signal |
1099 | * |
1100 | * Emitted when the user activates the currently focused |
1101 | * widget of @window. |
1102 | * |
1103 | * This is a [keybinding signal](class.SignalAction.html). |
1104 | */ |
1105 | window_signals[ACTIVATE_FOCUS] = |
1106 | g_signal_new (I_("activate-focus" ), |
1107 | G_TYPE_FROM_CLASS (gobject_class), |
1108 | signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, |
1109 | G_STRUCT_OFFSET (GtkWindowClass, activate_focus), |
1110 | NULL, NULL, |
1111 | NULL, |
1112 | G_TYPE_NONE, |
1113 | n_params: 0); |
1114 | |
1115 | /** |
1116 | * GtkWindow::activate-default: |
1117 | * @window: the window which received the signal |
1118 | * |
1119 | * Emitted when the user activates the default widget |
1120 | * of @window. |
1121 | * |
1122 | * This is a [keybinding signal](class.SignalAction.html). |
1123 | */ |
1124 | window_signals[ACTIVATE_DEFAULT] = |
1125 | g_signal_new (I_("activate-default" ), |
1126 | G_TYPE_FROM_CLASS (gobject_class), |
1127 | signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, |
1128 | G_STRUCT_OFFSET (GtkWindowClass, activate_default), |
1129 | NULL, NULL, |
1130 | NULL, |
1131 | G_TYPE_NONE, |
1132 | n_params: 0); |
1133 | |
1134 | /** |
1135 | * GtkWindow::keys-changed: |
1136 | * @window: the window which received the signal |
1137 | * |
1138 | * emitted when the set of accelerators or mnemonics that |
1139 | * are associated with @window changes. |
1140 | */ |
1141 | window_signals[KEYS_CHANGED] = |
1142 | g_signal_new (I_("keys-changed" ), |
1143 | G_TYPE_FROM_CLASS (gobject_class), |
1144 | signal_flags: G_SIGNAL_RUN_FIRST, |
1145 | G_STRUCT_OFFSET (GtkWindowClass, keys_changed), |
1146 | NULL, NULL, |
1147 | NULL, |
1148 | G_TYPE_NONE, |
1149 | n_params: 0); |
1150 | |
1151 | /** |
1152 | * GtkWindow::enable-debugging: |
1153 | * @window: the window on which the signal is emitted |
1154 | * @toggle: toggle the debugger |
1155 | * |
1156 | * Emitted when the user enables or disables interactive debugging. |
1157 | * |
1158 | * When @toggle is %TRUE, interactive debugging is toggled on or off, |
1159 | * when it is %FALSE, the debugger will be pointed at the widget |
1160 | * under the pointer. |
1161 | * |
1162 | * This is a [keybinding signal](class.SignalAction.html). |
1163 | * |
1164 | * The default bindings for this signal are Ctrl-Shift-I |
1165 | * and Ctrl-Shift-D. |
1166 | * |
1167 | * Return: %TRUE if the key binding was handled |
1168 | */ |
1169 | window_signals[ENABLE_DEBUGGING] = |
1170 | g_signal_new (I_("enable-debugging" ), |
1171 | G_TYPE_FROM_CLASS (gobject_class), |
1172 | signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, |
1173 | G_STRUCT_OFFSET (GtkWindowClass, enable_debugging), |
1174 | NULL, NULL, |
1175 | c_marshaller: _gtk_marshal_BOOLEAN__BOOLEAN, |
1176 | G_TYPE_BOOLEAN, |
1177 | n_params: 1, G_TYPE_BOOLEAN); |
1178 | |
1179 | /** |
1180 | * GtkWindow::close-request: |
1181 | * @window: the window on which the signal is emitted |
1182 | * |
1183 | * Emitted when the user clicks on the close button of the window. |
1184 | * |
1185 | * Return: %TRUE to stop other handlers from being invoked for the signal |
1186 | */ |
1187 | window_signals[CLOSE_REQUEST] = |
1188 | g_signal_new (I_("close-request" ), |
1189 | G_TYPE_FROM_CLASS (gobject_class), |
1190 | signal_flags: G_SIGNAL_RUN_LAST, |
1191 | G_STRUCT_OFFSET (GtkWindowClass, close_request), |
1192 | accumulator: _gtk_boolean_handled_accumulator, NULL, |
1193 | NULL, |
1194 | G_TYPE_BOOLEAN, |
1195 | n_params: 0); |
1196 | |
1197 | /* |
1198 | * Key bindings |
1199 | */ |
1200 | |
1201 | /** |
1202 | * GtkWindow|default.activate: |
1203 | * |
1204 | * Activate the default widget. |
1205 | */ |
1206 | gtk_widget_class_install_action (widget_class, action_name: "default.activate" , NULL, |
1207 | activate: gtk_window_activate_default_activate); |
1208 | |
1209 | /** |
1210 | * GtkWindow|window.minimize: |
1211 | * |
1212 | * Minimize the window. |
1213 | */ |
1214 | gtk_widget_class_install_action (widget_class, action_name: "window.minimize" , NULL, |
1215 | activate: gtk_window_activate_minimize); |
1216 | |
1217 | /** |
1218 | * GtkWindow|window.toggle-maximized: |
1219 | * |
1220 | * Maximize or restore the window. |
1221 | */ |
1222 | gtk_widget_class_install_action (widget_class, action_name: "window.toggle-maximized" , NULL, |
1223 | activate: gtk_window_activate_toggle_maximized); |
1224 | |
1225 | /** |
1226 | * GtkWindow|window.close: |
1227 | * |
1228 | * Close the window. |
1229 | */ |
1230 | gtk_widget_class_install_action (widget_class, action_name: "window.close" , NULL, |
1231 | activate: gtk_window_activate_close); |
1232 | |
1233 | gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, mods: 0, |
1234 | signal: "activate-focus" , NULL); |
1235 | gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, mods: 0, |
1236 | signal: "activate-focus" , NULL); |
1237 | |
1238 | gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, mods: 0, |
1239 | signal: "activate-default" , NULL); |
1240 | gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, mods: 0, |
1241 | signal: "activate-default" , NULL); |
1242 | gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter, mods: 0, |
1243 | signal: "activate-default" , NULL); |
1244 | |
1245 | gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_I, mods: GDK_CONTROL_MASK|GDK_SHIFT_MASK, |
1246 | signal: "enable-debugging" , format_string: "(b)" , FALSE); |
1247 | gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_D, mods: GDK_CONTROL_MASK|GDK_SHIFT_MASK, |
1248 | signal: "enable-debugging" , format_string: "(b)" , TRUE); |
1249 | |
1250 | add_arrow_bindings (widget_class, GDK_KEY_Up, direction: GTK_DIR_UP); |
1251 | add_arrow_bindings (widget_class, GDK_KEY_Down, direction: GTK_DIR_DOWN); |
1252 | add_arrow_bindings (widget_class, GDK_KEY_Left, direction: GTK_DIR_LEFT); |
1253 | add_arrow_bindings (widget_class, GDK_KEY_Right, direction: GTK_DIR_RIGHT); |
1254 | |
1255 | add_tab_bindings (widget_class, modifiers: 0, direction: GTK_DIR_TAB_FORWARD); |
1256 | add_tab_bindings (widget_class, modifiers: GDK_CONTROL_MASK, direction: GTK_DIR_TAB_FORWARD); |
1257 | add_tab_bindings (widget_class, modifiers: GDK_SHIFT_MASK, direction: GTK_DIR_TAB_BACKWARD); |
1258 | add_tab_bindings (widget_class, modifiers: GDK_CONTROL_MASK | GDK_SHIFT_MASK, direction: GTK_DIR_TAB_BACKWARD); |
1259 | |
1260 | gtk_widget_class_set_css_name (widget_class, I_("window" )); |
1261 | |
1262 | gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_WINDOW); |
1263 | } |
1264 | |
1265 | /** |
1266 | * gtk_window_is_maximized: (attributes org.gtk.Method.get_property=maximized) |
1267 | * @window: a `GtkWindow` |
1268 | * |
1269 | * Retrieves the current maximized state of @window. |
1270 | * |
1271 | * Note that since maximization is ultimately handled by the window |
1272 | * manager and happens asynchronously to an application request, you |
1273 | * shouldn’t assume the return value of this function changing |
1274 | * immediately (or at all), as an effect of calling |
1275 | * [method@Gtk.Window.maximize] or [method@Gtk.Window.unmaximize]. |
1276 | * |
1277 | * If the window isn't yet mapped, the value returned will whether the |
1278 | * initial requested state is maximized. |
1279 | * |
1280 | * Returns: whether the window has a maximized state. |
1281 | */ |
1282 | gboolean |
1283 | gtk_window_is_maximized (GtkWindow *window) |
1284 | { |
1285 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1286 | |
1287 | g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); |
1288 | |
1289 | return priv->maximized; |
1290 | } |
1291 | |
1292 | /** |
1293 | * gtk_window_is_fullscreen: (attributes org.gtk.Property.get=fullscreened) |
1294 | * @window: a `GtkWindow` |
1295 | * |
1296 | * Retrieves the current fullscreen state of @window. |
1297 | * |
1298 | * Note that since fullscreening is ultimately handled by the window |
1299 | * manager and happens asynchronously to an application request, you |
1300 | * shouldn’t assume the return value of this function changing |
1301 | * immediately (or at all), as an effect of calling |
1302 | * [method@Gtk.Window.fullscreen] or [method@Gtk.Window.unfullscreen]. |
1303 | * |
1304 | * If the window isn't yet mapped, the value returned will whether the |
1305 | * initial requested state is fullscreen. |
1306 | * |
1307 | * Returns: whether the window has a fullscreen state. |
1308 | */ |
1309 | gboolean |
1310 | gtk_window_is_fullscreen (GtkWindow *window) |
1311 | { |
1312 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1313 | |
1314 | g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); |
1315 | |
1316 | return priv->fullscreen; |
1317 | } |
1318 | |
1319 | void |
1320 | _gtk_window_toggle_maximized (GtkWindow *window) |
1321 | { |
1322 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1323 | |
1324 | if (priv->maximized) |
1325 | gtk_window_unmaximize (window); |
1326 | else |
1327 | gtk_window_maximize (window); |
1328 | } |
1329 | |
1330 | /** |
1331 | * gtk_window_close: |
1332 | * @window: a `GtkWindow` |
1333 | * |
1334 | * Requests that the window is closed. |
1335 | * |
1336 | * This is similar to what happens when a window manager |
1337 | * close button is clicked. |
1338 | * |
1339 | * This function can be used with close buttons in custom |
1340 | * titlebars. |
1341 | */ |
1342 | void |
1343 | gtk_window_close (GtkWindow *window) |
1344 | { |
1345 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1346 | |
1347 | if (!_gtk_widget_get_realized (GTK_WIDGET (window))) |
1348 | return; |
1349 | |
1350 | if (priv->in_emit_close_request) |
1351 | return; |
1352 | |
1353 | g_object_ref (window); |
1354 | |
1355 | if (!gtk_window_emit_close_request (window)) |
1356 | gtk_window_destroy (window); |
1357 | |
1358 | g_object_unref (object: window); |
1359 | } |
1360 | |
1361 | static guint |
1362 | constraints_for_edge (GdkSurfaceEdge edge) |
1363 | { |
1364 | switch (edge) |
1365 | { |
1366 | case GDK_SURFACE_EDGE_NORTH_WEST: |
1367 | return GDK_TOPLEVEL_STATE_LEFT_RESIZABLE | GDK_TOPLEVEL_STATE_TOP_RESIZABLE; |
1368 | case GDK_SURFACE_EDGE_NORTH: |
1369 | return GDK_TOPLEVEL_STATE_TOP_RESIZABLE; |
1370 | case GDK_SURFACE_EDGE_NORTH_EAST: |
1371 | return GDK_TOPLEVEL_STATE_RIGHT_RESIZABLE | GDK_TOPLEVEL_STATE_TOP_RESIZABLE; |
1372 | case GDK_SURFACE_EDGE_WEST: |
1373 | return GDK_TOPLEVEL_STATE_LEFT_RESIZABLE; |
1374 | case GDK_SURFACE_EDGE_EAST: |
1375 | return GDK_TOPLEVEL_STATE_RIGHT_RESIZABLE; |
1376 | case GDK_SURFACE_EDGE_SOUTH_WEST: |
1377 | return GDK_TOPLEVEL_STATE_LEFT_RESIZABLE | GDK_TOPLEVEL_STATE_BOTTOM_RESIZABLE; |
1378 | case GDK_SURFACE_EDGE_SOUTH: |
1379 | return GDK_TOPLEVEL_STATE_BOTTOM_RESIZABLE; |
1380 | case GDK_SURFACE_EDGE_SOUTH_EAST: |
1381 | return GDK_TOPLEVEL_STATE_RIGHT_RESIZABLE | GDK_TOPLEVEL_STATE_BOTTOM_RESIZABLE; |
1382 | default: |
1383 | g_warn_if_reached (); |
1384 | return 0; |
1385 | } |
1386 | } |
1387 | |
1388 | static int |
1389 | get_number (GtkCssValue *value) |
1390 | { |
1391 | double d = _gtk_css_number_value_get (number: value, one_hundred_percent: 100); |
1392 | |
1393 | if (d < 1) |
1394 | return ceil (x: d); |
1395 | else |
1396 | return floor (x: d); |
1397 | } |
1398 | |
1399 | static void |
1400 | get_box_border (GtkCssStyle *style, |
1401 | GtkBorder *border) |
1402 | { |
1403 | border->top = get_number (value: style->border->border_top_width) + get_number (value: style->size->padding_top); |
1404 | border->left = get_number (value: style->border->border_left_width) + get_number (value: style->size->padding_left); |
1405 | border->bottom = get_number (value: style->border->border_bottom_width) + get_number (value: style->size->padding_bottom); |
1406 | border->right = get_number (value: style->border->border_right_width) + get_number (value: style->size->padding_right); |
1407 | } |
1408 | |
1409 | static int |
1410 | get_edge_for_coordinates (GtkWindow *window, |
1411 | double x, |
1412 | double y) |
1413 | { |
1414 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1415 | gboolean supports_edge_constraints; |
1416 | GtkBorder handle_size; |
1417 | GtkCssBoxes css_boxes; |
1418 | const graphene_rect_t *border_rect; |
1419 | float left, top; |
1420 | |
1421 | #define edge_or_minus_one(edge) ((supports_edge_constraints && (priv->edge_constraints & constraints_for_edge (edge)) != constraints_for_edge (edge)) ? -1 : edge) |
1422 | |
1423 | if (!priv->client_decorated || |
1424 | !priv->resizable || |
1425 | priv->fullscreen || |
1426 | priv->maximized) |
1427 | return -1; |
1428 | |
1429 | supports_edge_constraints = gdk_toplevel_supports_edge_constraints (toplevel: GDK_TOPLEVEL (ptr: priv->surface)); |
1430 | |
1431 | if (!supports_edge_constraints && priv->tiled) |
1432 | return -1; |
1433 | |
1434 | gtk_css_boxes_init (boxes: &css_boxes, GTK_WIDGET (window)); |
1435 | border_rect = gtk_css_boxes_get_content_rect (boxes: &css_boxes); |
1436 | |
1437 | get_box_border (style: gtk_css_node_get_style (cssnode: gtk_widget_get_css_node (GTK_WIDGET (window))), |
1438 | border: &handle_size); |
1439 | |
1440 | if (priv->use_client_shadow) |
1441 | { |
1442 | /* We use a maximum of RESIZE_HANDLE_SIZE pixels for the handle size */ |
1443 | GtkBorder shadow; |
1444 | |
1445 | get_shadow_width (window, shadow_width: &shadow); |
1446 | /* This logic is duplicated in update_realized_window_properties() */ |
1447 | handle_size.left += shadow.left; |
1448 | handle_size.top += shadow.top; |
1449 | handle_size.right += shadow.right; |
1450 | handle_size.bottom += shadow.bottom; |
1451 | } |
1452 | |
1453 | left = border_rect->origin.x; |
1454 | top = border_rect->origin.y; |
1455 | |
1456 | if (x < left && x >= left - handle_size.left) |
1457 | { |
1458 | if (y < top + RESIZE_HANDLE_CORNER_SIZE && y >= top - handle_size.top) |
1459 | return edge_or_minus_one (GDK_SURFACE_EDGE_NORTH_WEST); |
1460 | |
1461 | if (y > top + border_rect->size.height - RESIZE_HANDLE_CORNER_SIZE && |
1462 | y <= top + border_rect->size.height + handle_size.bottom) |
1463 | return edge_or_minus_one (GDK_SURFACE_EDGE_SOUTH_WEST); |
1464 | |
1465 | return edge_or_minus_one (GDK_SURFACE_EDGE_WEST); |
1466 | } |
1467 | else if (x > left + border_rect->size.width && |
1468 | x <= left + border_rect->size.width + handle_size.right) |
1469 | { |
1470 | if (y < top + RESIZE_HANDLE_CORNER_SIZE && y >= top - handle_size.top) |
1471 | return edge_or_minus_one (GDK_SURFACE_EDGE_NORTH_EAST); |
1472 | |
1473 | if (y > top + border_rect->size.height - RESIZE_HANDLE_CORNER_SIZE && |
1474 | y <= top + border_rect->size.height + handle_size.bottom) |
1475 | return edge_or_minus_one (GDK_SURFACE_EDGE_SOUTH_EAST); |
1476 | |
1477 | return edge_or_minus_one (GDK_SURFACE_EDGE_EAST); |
1478 | } |
1479 | else if (y < top && y >= top - handle_size.top) |
1480 | { |
1481 | if (x < left + RESIZE_HANDLE_CORNER_SIZE && x >= left - handle_size.left) |
1482 | return edge_or_minus_one (GDK_SURFACE_EDGE_NORTH_WEST); |
1483 | |
1484 | if (x > left + border_rect->size.width - RESIZE_HANDLE_CORNER_SIZE && |
1485 | x <= left + border_rect->size.width + handle_size.right) |
1486 | return edge_or_minus_one (GDK_SURFACE_EDGE_NORTH_EAST); |
1487 | |
1488 | return edge_or_minus_one (GDK_SURFACE_EDGE_NORTH); |
1489 | } |
1490 | else if (y > top + border_rect->size.height && |
1491 | y <= top + border_rect->size.height + handle_size.bottom) |
1492 | { |
1493 | if (x < left + RESIZE_HANDLE_CORNER_SIZE && x >= left - handle_size.left) |
1494 | return edge_or_minus_one (GDK_SURFACE_EDGE_SOUTH_WEST); |
1495 | |
1496 | if (x > left + border_rect->size.width - RESIZE_HANDLE_CORNER_SIZE && |
1497 | x <= left + border_rect->size.width + handle_size.right) |
1498 | return edge_or_minus_one (GDK_SURFACE_EDGE_SOUTH_EAST); |
1499 | |
1500 | return edge_or_minus_one (GDK_SURFACE_EDGE_SOUTH); |
1501 | } |
1502 | |
1503 | return -1; |
1504 | } |
1505 | |
1506 | static void |
1507 | click_gesture_pressed_cb (GtkGestureClick *gesture, |
1508 | int n_press, |
1509 | double x, |
1510 | double y, |
1511 | GtkWindow *window) |
1512 | { |
1513 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1514 | GdkEventSequence *sequence; |
1515 | GtkWindowRegion region; |
1516 | GdkEvent *event; |
1517 | GdkDevice *device; |
1518 | guint button; |
1519 | double tx, ty; |
1520 | int edge; |
1521 | |
1522 | sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); |
1523 | button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); |
1524 | event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); |
1525 | device = gtk_gesture_get_device (GTK_GESTURE (gesture)); |
1526 | |
1527 | if (!event) |
1528 | return; |
1529 | |
1530 | if (button != GDK_BUTTON_PRIMARY) |
1531 | return; |
1532 | |
1533 | if (priv->maximized) |
1534 | return; |
1535 | |
1536 | if (gdk_display_device_is_grabbed (display: gtk_widget_get_display (GTK_WIDGET (window)), device)) |
1537 | return; |
1538 | |
1539 | if (!priv->client_decorated) |
1540 | return; |
1541 | |
1542 | edge = get_edge_for_coordinates (window, x, y); |
1543 | |
1544 | if (edge == -1) |
1545 | return; |
1546 | |
1547 | region = (GtkWindowRegion)edge; |
1548 | |
1549 | gtk_gesture_set_state (GTK_GESTURE (gesture), state: GTK_EVENT_SEQUENCE_CLAIMED); |
1550 | |
1551 | gdk_event_get_position (event, x: &tx, y: &ty); |
1552 | gdk_toplevel_begin_resize (toplevel: GDK_TOPLEVEL (ptr: priv->surface), |
1553 | edge: (GdkSurfaceEdge) region, |
1554 | device, |
1555 | GDK_BUTTON_PRIMARY, |
1556 | x: tx, y: ty, |
1557 | timestamp: gdk_event_get_time (event)); |
1558 | |
1559 | gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture)); |
1560 | } |
1561 | |
1562 | static void |
1563 | device_removed_cb (GdkSeat *seat, |
1564 | GdkDevice *device, |
1565 | gpointer user_data) |
1566 | { |
1567 | GtkWindow *window = user_data; |
1568 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1569 | GList *l = priv->foci; |
1570 | |
1571 | while (l) |
1572 | { |
1573 | GList *next; |
1574 | GtkPointerFocus *focus = l->data; |
1575 | |
1576 | next = l->next; |
1577 | |
1578 | if (focus->device == device) |
1579 | { |
1580 | priv->foci = g_list_delete_link (list: priv->foci, link_: l); |
1581 | gtk_pointer_focus_unref (focus); |
1582 | } |
1583 | |
1584 | l = next; |
1585 | } |
1586 | } |
1587 | |
1588 | static void |
1589 | gtk_window_capture_motion (GtkWidget *widget, |
1590 | double x, |
1591 | double y) |
1592 | { |
1593 | GtkWindow *window = GTK_WINDOW (widget); |
1594 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1595 | const char *cursor_names[8] = { |
1596 | "nw-resize" , "n-resize" , "ne-resize" , |
1597 | "w-resize" , "e-resize" , |
1598 | "sw-resize" , "s-resize" , "se-resize" |
1599 | }; |
1600 | int edge; |
1601 | |
1602 | edge = get_edge_for_coordinates (window, x, y); |
1603 | if (edge != -1 && |
1604 | priv->resize_cursor && |
1605 | strcmp (s1: gdk_cursor_get_name (cursor: priv->resize_cursor), s2: cursor_names[edge]) == 0) |
1606 | return; |
1607 | |
1608 | g_clear_object (&priv->resize_cursor); |
1609 | |
1610 | if (edge != -1) |
1611 | priv->resize_cursor = gdk_cursor_new_from_name (name: cursor_names[edge], NULL); |
1612 | |
1613 | gtk_window_maybe_update_cursor (window, widget, NULL); |
1614 | } |
1615 | |
1616 | static void |
1617 | gtk_window_activate_default_activate (GtkWidget *widget, |
1618 | const char *name, |
1619 | GVariant *parameter) |
1620 | { |
1621 | gtk_window_real_activate_default (GTK_WINDOW (widget)); |
1622 | } |
1623 | |
1624 | static void |
1625 | gtk_window_activate_minimize (GtkWidget *widget, |
1626 | const char *name, |
1627 | GVariant *parameter) |
1628 | { |
1629 | gtk_window_minimize (GTK_WINDOW (widget)); |
1630 | } |
1631 | |
1632 | static void |
1633 | gtk_window_activate_toggle_maximized (GtkWidget *widget, |
1634 | const char *name, |
1635 | GVariant *parameter) |
1636 | { |
1637 | _gtk_window_toggle_maximized (GTK_WINDOW (widget)); |
1638 | } |
1639 | |
1640 | static void |
1641 | gtk_window_activate_close (GtkWidget *widget, |
1642 | const char *name, |
1643 | GVariant *parameter) |
1644 | { |
1645 | gtk_window_close (GTK_WINDOW (widget)); |
1646 | } |
1647 | |
1648 | static gboolean |
1649 | gtk_window_accept_rootwindow_drop (GtkDropTargetAsync *self, |
1650 | GdkDrop *drop, |
1651 | double x, |
1652 | double y, |
1653 | gpointer unused) |
1654 | { |
1655 | gdk_drop_finish (self: drop, action: GDK_ACTION_MOVE); |
1656 | |
1657 | return TRUE; |
1658 | } |
1659 | |
1660 | static void |
1661 | gtk_window_init (GtkWindow *window) |
1662 | { |
1663 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1664 | GtkWidget *widget; |
1665 | GdkSeat *seat; |
1666 | GtkEventController *controller; |
1667 | GtkDropTargetAsync *target; |
1668 | GtkShortcut *shortcut; |
1669 | |
1670 | widget = GTK_WIDGET (window); |
1671 | |
1672 | gtk_widget_set_overflow (widget, overflow: GTK_OVERFLOW_HIDDEN); |
1673 | |
1674 | priv->title = NULL; |
1675 | priv->geometry_info = NULL; |
1676 | priv->focus_widget = NULL; |
1677 | priv->default_widget = NULL; |
1678 | priv->resizable = TRUE; |
1679 | priv->need_default_size = TRUE; |
1680 | priv->modal = FALSE; |
1681 | priv->decorated = TRUE; |
1682 | priv->display = gdk_display_get_default (); |
1683 | |
1684 | priv->state = 0; |
1685 | |
1686 | priv->deletable = TRUE; |
1687 | priv->startup_id = NULL; |
1688 | priv->initial_timestamp = GDK_CURRENT_TIME; |
1689 | priv->mnemonics_visible = FALSE; |
1690 | priv->focus_visible = TRUE; |
1691 | priv->initial_fullscreen_monitor = NULL; |
1692 | |
1693 | g_object_ref_sink (window); |
1694 | |
1695 | #ifdef GDK_WINDOWING_X11 |
1696 | g_signal_connect (gtk_settings_get_for_display (priv->display), |
1697 | "notify::gtk-application-prefer-dark-theme" , |
1698 | G_CALLBACK (gtk_window_on_theme_variant_changed), window); |
1699 | #endif |
1700 | |
1701 | gtk_widget_add_css_class (widget, css_class: "background" ); |
1702 | |
1703 | priv->scale = gtk_widget_get_scale_factor (widget); |
1704 | |
1705 | target = gtk_drop_target_async_new (formats: gdk_content_formats_new (mime_types: (const char*[1]) { "application/x-rootwindow-drop" }, n_mime_types: 1), |
1706 | actions: GDK_ACTION_MOVE); |
1707 | g_signal_connect (target, "drop" , G_CALLBACK (gtk_window_accept_rootwindow_drop), NULL); |
1708 | gtk_widget_add_controller (GTK_WIDGET (window), GTK_EVENT_CONTROLLER (target)); |
1709 | |
1710 | seat = gdk_display_get_default_seat (display: gtk_widget_get_display (widget)); |
1711 | if (seat) |
1712 | g_signal_connect (seat, "device-removed" , |
1713 | G_CALLBACK (device_removed_cb), window); |
1714 | |
1715 | controller = gtk_event_controller_motion_new (); |
1716 | gtk_event_controller_set_propagation_phase (controller, |
1717 | phase: GTK_PHASE_CAPTURE); |
1718 | g_signal_connect_swapped (controller, "motion" , |
1719 | G_CALLBACK (gtk_window_capture_motion), window); |
1720 | gtk_widget_add_controller (widget, controller); |
1721 | |
1722 | controller = gtk_event_controller_key_new (); |
1723 | gtk_event_controller_set_propagation_phase (controller, phase: GTK_PHASE_CAPTURE); |
1724 | g_signal_connect_swapped (controller, "key-pressed" , |
1725 | G_CALLBACK (gtk_window_key_pressed), window); |
1726 | g_signal_connect_swapped (controller, "key-released" , |
1727 | G_CALLBACK (gtk_window_key_released), window); |
1728 | gtk_widget_add_controller (widget, controller); |
1729 | |
1730 | controller = gtk_event_controller_legacy_new (); |
1731 | gtk_event_controller_set_name (controller, name: "gtk-window-toplevel-focus" ); |
1732 | g_signal_connect_swapped (controller, "event" , |
1733 | G_CALLBACK (gtk_window_handle_focus), window); |
1734 | gtk_widget_add_controller (widget, controller); |
1735 | |
1736 | controller = gtk_shortcut_controller_new (); |
1737 | gtk_event_controller_set_propagation_phase (controller, phase: GTK_PHASE_CAPTURE); |
1738 | |
1739 | shortcut = gtk_shortcut_new (trigger: gtk_keyval_trigger_new (MENU_BAR_ACCEL, modifiers: 0), |
1740 | action: gtk_callback_action_new (callback: gtk_window_activate_menubar, NULL, NULL)); |
1741 | gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); |
1742 | gtk_event_controller_set_name (controller, name: "gtk-window-menubar-accel" ); |
1743 | gtk_widget_add_controller (widget, controller); |
1744 | |
1745 | priv->menubar_controller = controller; |
1746 | } |
1747 | |
1748 | static void |
1749 | gtk_window_constructed (GObject *object) |
1750 | { |
1751 | GtkWindow *window = GTK_WINDOW (object); |
1752 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1753 | |
1754 | G_OBJECT_CLASS (gtk_window_parent_class)->constructed (object); |
1755 | |
1756 | priv->click_gesture = gtk_gesture_click_new (); |
1757 | gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->click_gesture), button: 0); |
1758 | gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->click_gesture), |
1759 | phase: GTK_PHASE_BUBBLE); |
1760 | g_signal_connect (priv->click_gesture, "pressed" , |
1761 | G_CALLBACK (click_gesture_pressed_cb), object); |
1762 | gtk_widget_add_controller (GTK_WIDGET (object), GTK_EVENT_CONTROLLER (priv->click_gesture)); |
1763 | |
1764 | g_list_store_append (store: toplevel_list, item: window); |
1765 | |
1766 | gtk_accessible_update_state (self: GTK_ACCESSIBLE (ptr: window), |
1767 | first_state: GTK_ACCESSIBLE_STATE_HIDDEN, TRUE, |
1768 | -1); |
1769 | |
1770 | g_object_unref (object: window); |
1771 | } |
1772 | |
1773 | static void |
1774 | gtk_window_set_property (GObject *object, |
1775 | guint prop_id, |
1776 | const GValue *value, |
1777 | GParamSpec *pspec) |
1778 | { |
1779 | GtkWindow *window = GTK_WINDOW (object); |
1780 | |
1781 | switch (prop_id) |
1782 | { |
1783 | case PROP_TITLE: |
1784 | gtk_window_set_title (window, title: g_value_get_string (value)); |
1785 | break; |
1786 | case PROP_STARTUP_ID: |
1787 | gtk_window_set_startup_id (window, startup_id: g_value_get_string (value)); |
1788 | break; |
1789 | case PROP_RESIZABLE: |
1790 | gtk_window_set_resizable (window, resizable: g_value_get_boolean (value)); |
1791 | break; |
1792 | case PROP_MODAL: |
1793 | gtk_window_set_modal (window, modal: g_value_get_boolean (value)); |
1794 | break; |
1795 | case PROP_DEFAULT_WIDTH: |
1796 | gtk_window_set_default_size_internal (window, |
1797 | TRUE, width: g_value_get_int (value), |
1798 | FALSE, height: -1); |
1799 | gtk_widget_queue_resize (GTK_WIDGET (window)); |
1800 | break; |
1801 | case PROP_DEFAULT_HEIGHT: |
1802 | gtk_window_set_default_size_internal (window, |
1803 | FALSE, width: -1, |
1804 | TRUE, height: g_value_get_int (value)); |
1805 | gtk_widget_queue_resize (GTK_WIDGET (window)); |
1806 | break; |
1807 | case PROP_DESTROY_WITH_PARENT: |
1808 | gtk_window_set_destroy_with_parent (window, setting: g_value_get_boolean (value)); |
1809 | break; |
1810 | case PROP_HIDE_ON_CLOSE: |
1811 | gtk_window_set_hide_on_close (window, setting: g_value_get_boolean (value)); |
1812 | break; |
1813 | case PROP_ICON_NAME: |
1814 | gtk_window_set_icon_name (window, name: g_value_get_string (value)); |
1815 | break; |
1816 | case PROP_DISPLAY: |
1817 | gtk_window_set_display (window, display: g_value_get_object (value)); |
1818 | break; |
1819 | case PROP_DECORATED: |
1820 | gtk_window_set_decorated (window, setting: g_value_get_boolean (value)); |
1821 | break; |
1822 | case PROP_DELETABLE: |
1823 | gtk_window_set_deletable (window, setting: g_value_get_boolean (value)); |
1824 | break; |
1825 | case PROP_TRANSIENT_FOR: |
1826 | gtk_window_set_transient_for (window, parent: g_value_get_object (value)); |
1827 | break; |
1828 | case PROP_APPLICATION: |
1829 | gtk_window_set_application (window, application: g_value_get_object (value)); |
1830 | break; |
1831 | case PROP_DEFAULT_WIDGET: |
1832 | gtk_window_set_default_widget (window, default_widget: g_value_get_object (value)); |
1833 | break; |
1834 | case PROP_MNEMONICS_VISIBLE: |
1835 | gtk_window_set_mnemonics_visible (window, setting: g_value_get_boolean (value)); |
1836 | break; |
1837 | case PROP_FOCUS_VISIBLE: |
1838 | gtk_window_set_focus_visible (window, setting: g_value_get_boolean (value)); |
1839 | break; |
1840 | case PROP_MAXIMIZED: |
1841 | if (g_value_get_boolean (value)) |
1842 | gtk_window_maximize (window); |
1843 | else |
1844 | gtk_window_unmaximize (window); |
1845 | break; |
1846 | case PROP_FULLSCREENED: |
1847 | if (g_value_get_boolean (value)) |
1848 | gtk_window_fullscreen (window); |
1849 | else |
1850 | gtk_window_unfullscreen (window); |
1851 | break; |
1852 | case PROP_FOCUS_WIDGET: |
1853 | gtk_window_set_focus (window, focus: g_value_get_object (value)); |
1854 | break; |
1855 | case PROP_CHILD: |
1856 | gtk_window_set_child (window, child: g_value_get_object (value)); |
1857 | break; |
1858 | case PROP_TITLEBAR: |
1859 | gtk_window_set_titlebar (window, titlebar: g_value_get_object (value)); |
1860 | break; |
1861 | case PROP_HANDLE_MENUBAR_ACCEL: |
1862 | gtk_window_set_handle_menubar_accel (window, handle_menubar_accel: g_value_get_boolean (value)); |
1863 | break; |
1864 | default: |
1865 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
1866 | break; |
1867 | } |
1868 | } |
1869 | |
1870 | static void |
1871 | gtk_window_get_property (GObject *object, |
1872 | guint prop_id, |
1873 | GValue *value, |
1874 | GParamSpec *pspec) |
1875 | { |
1876 | GtkWindow *window = GTK_WINDOW (object); |
1877 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1878 | |
1879 | switch (prop_id) |
1880 | { |
1881 | case PROP_TITLE: |
1882 | g_value_set_string (value, v_string: priv->title); |
1883 | break; |
1884 | case PROP_RESIZABLE: |
1885 | g_value_set_boolean (value, v_boolean: priv->resizable); |
1886 | break; |
1887 | case PROP_MODAL: |
1888 | g_value_set_boolean (value, v_boolean: priv->modal); |
1889 | break; |
1890 | case PROP_DEFAULT_WIDTH: |
1891 | g_value_set_int (value, v_int: priv->default_width); |
1892 | break; |
1893 | case PROP_DEFAULT_HEIGHT: |
1894 | g_value_set_int (value, v_int: priv->default_height); |
1895 | break; |
1896 | case PROP_DESTROY_WITH_PARENT: |
1897 | g_value_set_boolean (value, v_boolean: priv->destroy_with_parent); |
1898 | break; |
1899 | case PROP_HIDE_ON_CLOSE: |
1900 | g_value_set_boolean (value, v_boolean: priv->hide_on_close); |
1901 | break; |
1902 | case PROP_ICON_NAME: |
1903 | g_value_set_string (value, v_string: gtk_window_get_icon_name (window)); |
1904 | break; |
1905 | case PROP_DISPLAY: |
1906 | g_value_set_object (value, v_object: priv->display); |
1907 | break; |
1908 | case PROP_IS_ACTIVE: |
1909 | g_value_set_boolean (value, v_boolean: priv->is_active); |
1910 | break; |
1911 | case PROP_DECORATED: |
1912 | g_value_set_boolean (value, v_boolean: gtk_window_get_decorated (window)); |
1913 | break; |
1914 | case PROP_DELETABLE: |
1915 | g_value_set_boolean (value, v_boolean: gtk_window_get_deletable (window)); |
1916 | break; |
1917 | case PROP_TRANSIENT_FOR: |
1918 | g_value_set_object (value, v_object: gtk_window_get_transient_for (window)); |
1919 | break; |
1920 | case PROP_APPLICATION: |
1921 | g_value_set_object (value, v_object: gtk_window_get_application (window)); |
1922 | break; |
1923 | case PROP_DEFAULT_WIDGET: |
1924 | g_value_set_object (value, v_object: gtk_window_get_default_widget (window)); |
1925 | break; |
1926 | case PROP_MNEMONICS_VISIBLE: |
1927 | g_value_set_boolean (value, v_boolean: priv->mnemonics_visible); |
1928 | break; |
1929 | case PROP_FOCUS_VISIBLE: |
1930 | g_value_set_boolean (value, v_boolean: priv->focus_visible); |
1931 | break; |
1932 | case PROP_MAXIMIZED: |
1933 | g_value_set_boolean (value, v_boolean: gtk_window_is_maximized (window)); |
1934 | break; |
1935 | case PROP_FULLSCREENED: |
1936 | g_value_set_boolean (value, v_boolean: gtk_window_is_fullscreen (window)); |
1937 | break; |
1938 | case PROP_FOCUS_WIDGET: |
1939 | g_value_set_object (value, v_object: gtk_window_get_focus (window)); |
1940 | break; |
1941 | case PROP_CHILD: |
1942 | g_value_set_object (value, v_object: gtk_window_get_child (window)); |
1943 | break; |
1944 | case PROP_TITLEBAR: |
1945 | g_value_set_object (value, v_object: gtk_window_get_titlebar (window)); |
1946 | break; |
1947 | case PROP_HANDLE_MENUBAR_ACCEL: |
1948 | g_value_set_boolean (value, v_boolean: gtk_window_get_handle_menubar_accel (window)); |
1949 | break; |
1950 | default: |
1951 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
1952 | break; |
1953 | } |
1954 | } |
1955 | |
1956 | static void |
1957 | gtk_window_buildable_interface_init (GtkBuildableIface *iface) |
1958 | { |
1959 | parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface); |
1960 | iface->add_child = gtk_window_buildable_add_child; |
1961 | } |
1962 | |
1963 | static void |
1964 | gtk_window_buildable_add_child (GtkBuildable *buildable, |
1965 | GtkBuilder *builder, |
1966 | GObject *child, |
1967 | const char *type) |
1968 | { |
1969 | if (type && strcmp (s1: type, s2: "titlebar" ) == 0) |
1970 | gtk_window_set_titlebar (GTK_WINDOW (buildable), GTK_WIDGET (child)); |
1971 | else if (GTK_IS_WIDGET (child)) |
1972 | gtk_window_set_child (GTK_WINDOW (buildable), GTK_WIDGET (child)); |
1973 | else |
1974 | parent_buildable_iface->add_child (buildable, builder, child, type); |
1975 | } |
1976 | |
1977 | static void |
1978 | gtk_window_shortcut_manager_interface_init (GtkShortcutManagerInterface *iface) |
1979 | { |
1980 | } |
1981 | |
1982 | static GdkDisplay * |
1983 | gtk_window_root_get_display (GtkRoot *root) |
1984 | { |
1985 | GtkWindow *window = GTK_WINDOW (root); |
1986 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
1987 | |
1988 | return priv->display; |
1989 | } |
1990 | |
1991 | static GdkSurface * |
1992 | gtk_window_native_get_surface (GtkNative *native) |
1993 | { |
1994 | GtkWindow *self = GTK_WINDOW (native); |
1995 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self); |
1996 | |
1997 | return priv->surface; |
1998 | } |
1999 | |
2000 | static GskRenderer * |
2001 | gtk_window_native_get_renderer (GtkNative *native) |
2002 | { |
2003 | GtkWindow *self = GTK_WINDOW (native); |
2004 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self); |
2005 | |
2006 | return priv->renderer; |
2007 | } |
2008 | |
2009 | static GtkConstraintSolver * |
2010 | gtk_window_root_get_constraint_solver (GtkRoot *root) |
2011 | { |
2012 | GtkWindow *self = GTK_WINDOW (root); |
2013 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self); |
2014 | |
2015 | if (!priv->constraint_solver) |
2016 | { |
2017 | /* Shared constraint solver */ |
2018 | priv->constraint_solver = gtk_constraint_solver_new (); |
2019 | } |
2020 | |
2021 | return priv->constraint_solver; |
2022 | } |
2023 | |
2024 | static GtkWidget * |
2025 | gtk_window_root_get_focus (GtkRoot *root) |
2026 | { |
2027 | GtkWindow *self = GTK_WINDOW (root); |
2028 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self); |
2029 | |
2030 | return priv->focus_widget; |
2031 | } |
2032 | |
2033 | static void synthesize_focus_change_events (GtkWindow *window, |
2034 | GtkWidget *old_focus, |
2035 | GtkWidget *new_focus, |
2036 | GtkCrossingType type); |
2037 | |
2038 | static void |
2039 | gtk_window_root_set_focus (GtkRoot *root, |
2040 | GtkWidget *focus) |
2041 | { |
2042 | GtkWindow *self = GTK_WINDOW (root); |
2043 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self); |
2044 | GtkWidget *old_focus = NULL; |
2045 | |
2046 | if (focus && !gtk_widget_is_sensitive (widget: focus)) |
2047 | return; |
2048 | |
2049 | if (focus == priv->focus_widget) |
2050 | { |
2051 | if (priv->move_focus && |
2052 | focus && gtk_widget_is_visible (widget: focus)) |
2053 | { |
2054 | priv->move_focus = FALSE; |
2055 | g_clear_object (&priv->move_focus_widget); |
2056 | } |
2057 | return; |
2058 | } |
2059 | |
2060 | if (priv->focus_widget) |
2061 | old_focus = g_object_ref (priv->focus_widget); |
2062 | g_set_object (&priv->focus_widget, NULL); |
2063 | |
2064 | if (old_focus) |
2065 | gtk_widget_set_has_focus (widget: old_focus, FALSE); |
2066 | |
2067 | synthesize_focus_change_events (window: self, old_focus, new_focus: focus, type: GTK_CROSSING_FOCUS); |
2068 | |
2069 | if (focus) |
2070 | gtk_widget_set_has_focus (widget: focus, TRUE); |
2071 | |
2072 | g_set_object (&priv->focus_widget, focus); |
2073 | |
2074 | g_clear_object (&old_focus); |
2075 | |
2076 | if (priv->move_focus && |
2077 | focus && gtk_widget_is_visible (widget: focus)) |
2078 | { |
2079 | priv->move_focus = FALSE; |
2080 | g_clear_object (&priv->move_focus_widget); |
2081 | } |
2082 | |
2083 | g_object_notify (G_OBJECT (self), property_name: "focus-widget" ); |
2084 | } |
2085 | |
2086 | static void |
2087 | gtk_window_native_get_surface_transform (GtkNative *native, |
2088 | double *x, |
2089 | double *y) |
2090 | { |
2091 | GtkBorder shadow; |
2092 | GtkCssBoxes css_boxes; |
2093 | const graphene_rect_t *margin_rect; |
2094 | |
2095 | get_shadow_width (GTK_WINDOW (native), shadow_width: &shadow); |
2096 | gtk_css_boxes_init (boxes: &css_boxes, GTK_WIDGET (native)); |
2097 | margin_rect = gtk_css_boxes_get_margin_rect (boxes: &css_boxes); |
2098 | |
2099 | *x = shadow.left - margin_rect->origin.x; |
2100 | *y = shadow.top - margin_rect->origin.y; |
2101 | } |
2102 | |
2103 | static void |
2104 | gtk_window_native_layout (GtkNative *native, |
2105 | int width, |
2106 | int height) |
2107 | { |
2108 | GtkWindow *window = GTK_WINDOW (native); |
2109 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2110 | GtkWidget *widget = GTK_WIDGET (native); |
2111 | |
2112 | if (priv->surface_width != width || priv->surface_height != height) |
2113 | { |
2114 | surface_size_changed (widget, width, height); |
2115 | priv->surface_width = width; |
2116 | priv->surface_height = height; |
2117 | } |
2118 | |
2119 | /* This fake motion event is needed for getting up to date pointer focus |
2120 | * and coordinates when tho pointer didn't move but the layout changed |
2121 | * within the window. |
2122 | */ |
2123 | if (gtk_widget_needs_allocate (widget)) |
2124 | { |
2125 | GdkSeat *seat; |
2126 | |
2127 | seat = gdk_display_get_default_seat (display: gtk_widget_get_display (widget)); |
2128 | if (seat) |
2129 | { |
2130 | GdkDevice *device; |
2131 | GtkWidget *focus; |
2132 | |
2133 | device = gdk_seat_get_pointer (seat); |
2134 | focus = gtk_window_lookup_pointer_focus_widget (GTK_WINDOW (widget), |
2135 | device, NULL); |
2136 | if (focus) |
2137 | { |
2138 | GdkSurface *focus_surface = |
2139 | gtk_native_get_surface (self: gtk_widget_get_native (widget: focus)); |
2140 | |
2141 | gdk_surface_request_motion (surface: focus_surface); |
2142 | } |
2143 | } |
2144 | } |
2145 | |
2146 | if (gtk_widget_needs_allocate (widget)) |
2147 | { |
2148 | gtk_window_update_csd_size (window, |
2149 | width: &width, height: &height, |
2150 | EXCLUDE_CSD_SIZE); |
2151 | gtk_widget_allocate (widget, width, height, baseline: -1, NULL); |
2152 | } |
2153 | else |
2154 | { |
2155 | gtk_widget_ensure_allocate (widget); |
2156 | } |
2157 | } |
2158 | |
2159 | static void |
2160 | gtk_window_root_interface_init (GtkRootInterface *iface) |
2161 | { |
2162 | iface->get_display = gtk_window_root_get_display; |
2163 | iface->get_constraint_solver = gtk_window_root_get_constraint_solver; |
2164 | iface->get_focus = gtk_window_root_get_focus; |
2165 | iface->set_focus = gtk_window_root_set_focus; |
2166 | } |
2167 | |
2168 | static void |
2169 | gtk_window_native_interface_init (GtkNativeInterface *iface) |
2170 | { |
2171 | iface->get_surface = gtk_window_native_get_surface; |
2172 | iface->get_renderer = gtk_window_native_get_renderer; |
2173 | iface->get_surface_transform = gtk_window_native_get_surface_transform; |
2174 | iface->layout = gtk_window_native_layout; |
2175 | } |
2176 | |
2177 | /** |
2178 | * gtk_window_new: |
2179 | * |
2180 | * Creates a new `GtkWindow`. |
2181 | * |
2182 | * To get an undecorated window (no window borders), use |
2183 | * [method@Gtk.Window.set_decorated]. |
2184 | * |
2185 | * All top-level windows created by gtk_window_new() are stored |
2186 | * in an internal top-level window list. This list can be obtained |
2187 | * from [func@Gtk.Window.list_toplevels]. Due to GTK keeping a |
2188 | * reference to the window internally, gtk_window_new() does not |
2189 | * return a reference to the caller. |
2190 | * |
2191 | * To delete a `GtkWindow`, call [method@Gtk.Window.destroy]. |
2192 | * |
2193 | * Returns: a new `GtkWindow`. |
2194 | */ |
2195 | GtkWidget* |
2196 | gtk_window_new (void) |
2197 | { |
2198 | return g_object_new (GTK_TYPE_WINDOW, NULL); |
2199 | } |
2200 | |
2201 | /** |
2202 | * gtk_window_set_title: (attributes org.gtk.Method.set_property=title) |
2203 | * @window: a `GtkWindow` |
2204 | * @title: (nullable): title of the window |
2205 | * |
2206 | * Sets the title of the `GtkWindow`. |
2207 | * |
2208 | * The title of a window will be displayed in its title bar; on the |
2209 | * X Window System, the title bar is rendered by the window manager |
2210 | * so exactly how the title appears to users may vary according to a |
2211 | * user’s exact configuration. The title should help a user distinguish |
2212 | * this window from other windows they may have open. A good title might |
2213 | * include the application name and current document filename, for example. |
2214 | * |
2215 | * Passing %NULL does the same as setting the title to an empty string. |
2216 | */ |
2217 | void |
2218 | gtk_window_set_title (GtkWindow *window, |
2219 | const char *title) |
2220 | { |
2221 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2222 | char *new_title; |
2223 | |
2224 | g_return_if_fail (GTK_IS_WINDOW (window)); |
2225 | |
2226 | new_title = g_strdup (str: title); |
2227 | g_free (mem: priv->title); |
2228 | priv->title = new_title; |
2229 | |
2230 | if (_gtk_widget_get_realized (GTK_WIDGET (window))) |
2231 | gdk_toplevel_set_title (toplevel: GDK_TOPLEVEL (ptr: priv->surface), title: new_title != NULL ? new_title : "" ); |
2232 | |
2233 | gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: window), |
2234 | first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, priv->title, |
2235 | -1); |
2236 | |
2237 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_TITLE]); |
2238 | } |
2239 | |
2240 | /** |
2241 | * gtk_window_get_title: (attributes org.gtk.Method.get_property=title) |
2242 | * @window: a `GtkWindow` |
2243 | * |
2244 | * Retrieves the title of the window. |
2245 | * |
2246 | * Returns: (nullable): the title of the window |
2247 | */ |
2248 | const char * |
2249 | gtk_window_get_title (GtkWindow *window) |
2250 | { |
2251 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2252 | |
2253 | g_return_val_if_fail (GTK_IS_WINDOW (window), NULL); |
2254 | |
2255 | return priv->title; |
2256 | } |
2257 | |
2258 | /** |
2259 | * gtk_window_set_startup_id: (attributes org.gtk.Method.set_property=startup-id) |
2260 | * @window: a `GtkWindow` |
2261 | * @startup_id: a string with startup-notification identifier |
2262 | * |
2263 | * Sets the startup notification ID. |
2264 | * |
2265 | * Startup notification identifiers are used by desktop environment |
2266 | * to track application startup, to provide user feedback and other |
2267 | * features. This function changes the corresponding property on the |
2268 | * underlying `GdkSurface`. |
2269 | * |
2270 | * Normally, startup identifier is managed automatically and you should |
2271 | * only use this function in special cases like transferring focus from |
2272 | * other processes. You should use this function before calling |
2273 | * [method@Gtk.Window.present] or any equivalent function generating |
2274 | * a window map event. |
2275 | * |
2276 | * This function is only useful on X11, not with other GTK targets. |
2277 | */ |
2278 | void |
2279 | gtk_window_set_startup_id (GtkWindow *window, |
2280 | const char *startup_id) |
2281 | { |
2282 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2283 | GtkWidget *widget; |
2284 | |
2285 | g_return_if_fail (GTK_IS_WINDOW (window)); |
2286 | |
2287 | widget = GTK_WIDGET (window); |
2288 | |
2289 | g_free (mem: priv->startup_id); |
2290 | priv->startup_id = g_strdup (str: startup_id); |
2291 | |
2292 | if (_gtk_widget_get_realized (widget)) |
2293 | { |
2294 | guint32 timestamp = extract_time_from_startup_id (startup_id: priv->startup_id); |
2295 | |
2296 | #ifdef GDK_WINDOWING_X11 |
2297 | if (timestamp != GDK_CURRENT_TIME && GDK_IS_X11_SURFACE (priv->surface)) |
2298 | gdk_x11_surface_set_user_time (surface: priv->surface, timestamp); |
2299 | #endif |
2300 | |
2301 | /* Here we differentiate real and "fake" startup notification IDs, |
2302 | * constructed on purpose just to pass interaction timestamp |
2303 | */ |
2304 | if (startup_id_is_fake (startup_id: priv->startup_id)) |
2305 | gtk_window_present_with_time (window, timestamp); |
2306 | else |
2307 | { |
2308 | gdk_toplevel_set_startup_id (toplevel: GDK_TOPLEVEL (ptr: priv->surface), startup_id: priv->startup_id); |
2309 | |
2310 | /* If window is mapped, terminate the startup-notification too */ |
2311 | if (_gtk_widget_get_mapped (widget) && !disable_startup_notification) |
2312 | gdk_display_notify_startup_complete (display: gtk_widget_get_display (widget), startup_id: priv->startup_id); |
2313 | } |
2314 | } |
2315 | |
2316 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_STARTUP_ID]); |
2317 | } |
2318 | |
2319 | /** |
2320 | * gtk_window_set_default_widget: (attributes org.gtk.Property.set=default-widget) |
2321 | * @window: a `GtkWindow` |
2322 | * @default_widget: (nullable): widget to be the default |
2323 | * to unset the default widget for the toplevel |
2324 | * |
2325 | * Sets the default widget. |
2326 | * |
2327 | * The default widget is the widget that is activated when the user |
2328 | * presses Enter in a dialog (for example). |
2329 | */ |
2330 | void |
2331 | gtk_window_set_default_widget (GtkWindow *window, |
2332 | GtkWidget *default_widget) |
2333 | { |
2334 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2335 | |
2336 | g_return_if_fail (GTK_IS_WINDOW (window)); |
2337 | |
2338 | if (priv->default_widget != default_widget) |
2339 | { |
2340 | GtkWidget *old_default_widget = NULL; |
2341 | |
2342 | if (default_widget) |
2343 | g_object_ref (default_widget); |
2344 | |
2345 | if (priv->default_widget) |
2346 | { |
2347 | old_default_widget = priv->default_widget; |
2348 | |
2349 | if (priv->focus_widget != priv->default_widget || |
2350 | !gtk_widget_get_receives_default (widget: priv->default_widget)) |
2351 | _gtk_widget_set_has_default (widget: priv->default_widget, FALSE); |
2352 | |
2353 | gtk_widget_queue_draw (widget: priv->default_widget); |
2354 | } |
2355 | |
2356 | priv->default_widget = default_widget; |
2357 | |
2358 | priv->unset_default = FALSE; |
2359 | |
2360 | if (priv->default_widget) |
2361 | { |
2362 | if (priv->focus_widget == NULL || |
2363 | !gtk_widget_get_receives_default (widget: priv->focus_widget)) |
2364 | _gtk_widget_set_has_default (widget: priv->default_widget, TRUE); |
2365 | |
2366 | gtk_widget_queue_draw (widget: priv->default_widget); |
2367 | } |
2368 | |
2369 | if (old_default_widget) |
2370 | g_object_notify (G_OBJECT (old_default_widget), property_name: "has-default" ); |
2371 | |
2372 | if (default_widget) |
2373 | { |
2374 | g_object_notify (G_OBJECT (default_widget), property_name: "has-default" ); |
2375 | g_object_unref (object: default_widget); |
2376 | } |
2377 | |
2378 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_DEFAULT_WIDGET]); |
2379 | } |
2380 | } |
2381 | |
2382 | /** |
2383 | * gtk_window_get_default_widget: (attributes org.gtk.Property.get=default-widget) |
2384 | * @window: a `GtkWindow` |
2385 | * |
2386 | * Returns the default widget for @window. |
2387 | * |
2388 | * Returns: (nullable) (transfer none): the default widget |
2389 | */ |
2390 | GtkWidget * |
2391 | gtk_window_get_default_widget (GtkWindow *window) |
2392 | { |
2393 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2394 | |
2395 | g_return_val_if_fail (GTK_IS_WINDOW (window), NULL); |
2396 | |
2397 | return priv->default_widget; |
2398 | } |
2399 | |
2400 | static gboolean |
2401 | handle_keys_changed (gpointer data) |
2402 | { |
2403 | GtkWindow *window = GTK_WINDOW (data); |
2404 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2405 | |
2406 | if (priv->keys_changed_handler) |
2407 | { |
2408 | g_source_remove (tag: priv->keys_changed_handler); |
2409 | priv->keys_changed_handler = 0; |
2410 | } |
2411 | |
2412 | if (priv->application_shortcut_controller) |
2413 | gtk_shortcut_controller_update_accels (GTK_SHORTCUT_CONTROLLER (priv->application_shortcut_controller)); |
2414 | g_signal_emit (instance: window, signal_id: window_signals[KEYS_CHANGED], detail: 0); |
2415 | |
2416 | return FALSE; |
2417 | } |
2418 | |
2419 | void |
2420 | _gtk_window_notify_keys_changed (GtkWindow *window) |
2421 | { |
2422 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2423 | |
2424 | if (!priv->keys_changed_handler) |
2425 | { |
2426 | priv->keys_changed_handler = g_idle_add (function: handle_keys_changed, data: window); |
2427 | gdk_source_set_static_name_by_id (tag: priv->keys_changed_handler, name: "[gtk] handle_keys_changed" ); |
2428 | } |
2429 | } |
2430 | |
2431 | /** |
2432 | * gtk_window_get_focus: (attributes org.gtk.Property.get=focus-widget) |
2433 | * @window: a `GtkWindow` |
2434 | * |
2435 | * Retrieves the current focused widget within the window. |
2436 | * |
2437 | * Note that this is the widget that would have the focus |
2438 | * if the toplevel window focused; if the toplevel window |
2439 | * is not focused then `gtk_widget_has_focus (widget)` will |
2440 | * not be %TRUE for the widget. |
2441 | * |
2442 | * Returns: (nullable) (transfer none): the currently focused widget |
2443 | */ |
2444 | GtkWidget * |
2445 | gtk_window_get_focus (GtkWindow *window) |
2446 | { |
2447 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2448 | |
2449 | g_return_val_if_fail (GTK_IS_WINDOW (window), NULL); |
2450 | |
2451 | return priv->focus_widget; |
2452 | } |
2453 | |
2454 | static void |
2455 | gtk_window_real_activate_default (GtkWindow *window) |
2456 | { |
2457 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2458 | |
2459 | if (priv->default_widget && gtk_widget_is_sensitive (widget: priv->default_widget) && |
2460 | (!priv->focus_widget || !gtk_widget_get_receives_default (widget: priv->focus_widget))) |
2461 | gtk_widget_activate (widget: priv->default_widget); |
2462 | else if (priv->focus_widget && gtk_widget_is_sensitive (widget: priv->focus_widget)) |
2463 | gtk_widget_activate (widget: priv->focus_widget); |
2464 | } |
2465 | |
2466 | /** |
2467 | * gtk_window_set_modal: (attributes org.gtk.Method.set_property=modal) |
2468 | * @window: a `GtkWindow` |
2469 | * @modal: whether the window is modal |
2470 | * |
2471 | * Sets a window modal or non-modal. |
2472 | * |
2473 | * Modal windows prevent interaction with other windows in the same |
2474 | * application. To keep modal dialogs on top of main application windows, |
2475 | * use [method@Gtk.Window.set_transient_for] to make the dialog transient |
2476 | * for the parent; most window managers will then disallow lowering the |
2477 | * dialog below the parent. |
2478 | */ |
2479 | void |
2480 | gtk_window_set_modal (GtkWindow *window, |
2481 | gboolean modal) |
2482 | { |
2483 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2484 | GtkWidget *widget; |
2485 | |
2486 | g_return_if_fail (GTK_IS_WINDOW (window)); |
2487 | |
2488 | modal = modal != FALSE; |
2489 | if (priv->modal == modal) |
2490 | return; |
2491 | |
2492 | priv->modal = modal; |
2493 | widget = GTK_WIDGET (window); |
2494 | |
2495 | if (_gtk_widget_get_realized (widget)) |
2496 | gdk_toplevel_set_modal (toplevel: GDK_TOPLEVEL (ptr: priv->surface), modal); |
2497 | |
2498 | if (gtk_widget_get_visible (widget)) |
2499 | { |
2500 | if (priv->modal) |
2501 | gtk_grab_add (widget); |
2502 | else |
2503 | gtk_grab_remove (widget); |
2504 | } |
2505 | |
2506 | update_window_actions (window); |
2507 | |
2508 | gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: window), |
2509 | first_property: GTK_ACCESSIBLE_PROPERTY_MODAL, modal, |
2510 | -1); |
2511 | |
2512 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_MODAL]); |
2513 | } |
2514 | |
2515 | /** |
2516 | * gtk_window_get_modal: (attributes org.gtk.Method.get_property=modal) |
2517 | * @window: a `GtkWindow` |
2518 | * |
2519 | * Returns whether the window is modal. |
2520 | * |
2521 | * Returns: %TRUE if the window is set to be modal and |
2522 | * establishes a grab when shown |
2523 | */ |
2524 | gboolean |
2525 | gtk_window_get_modal (GtkWindow *window) |
2526 | { |
2527 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2528 | |
2529 | g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); |
2530 | |
2531 | return priv->modal; |
2532 | } |
2533 | |
2534 | /** |
2535 | * gtk_window_get_toplevels: |
2536 | * |
2537 | * Returns a list of all existing toplevel windows. |
2538 | * |
2539 | * If you want to iterate through the list and perform actions involving |
2540 | * callbacks that might destroy the widgets or add new ones, be aware that |
2541 | * the list of toplevels will change and emit the "items-changed" signal. |
2542 | * |
2543 | * Returns: (transfer none) (attributes element-type=GtkWindow): the list |
2544 | * of toplevel widgets |
2545 | */ |
2546 | GListModel * |
2547 | gtk_window_get_toplevels (void) |
2548 | { |
2549 | if (toplevel_list == NULL) |
2550 | toplevel_list = g_list_store_new (GTK_TYPE_WIDGET); |
2551 | |
2552 | return G_LIST_MODEL (ptr: toplevel_list); |
2553 | } |
2554 | |
2555 | /** |
2556 | * gtk_window_list_toplevels: |
2557 | * |
2558 | * Returns a list of all existing toplevel windows. |
2559 | * |
2560 | * The widgets in the list are not individually referenced. |
2561 | * If you want to iterate through the list and perform actions |
2562 | * involving callbacks that might destroy the widgets, you must |
2563 | * call `g_list_foreach (result, (GFunc)g_object_ref, NULL)` first, |
2564 | * and then unref all the widgets afterwards. |
2565 | * |
2566 | * Returns: (element-type GtkWidget) (transfer container): list of |
2567 | * toplevel widgets |
2568 | */ |
2569 | GList* |
2570 | gtk_window_list_toplevels (void) |
2571 | { |
2572 | GListModel *toplevels; |
2573 | GList *list = NULL; |
2574 | guint i; |
2575 | |
2576 | toplevels = gtk_window_get_toplevels (); |
2577 | |
2578 | for (i = 0; i < g_list_model_get_n_items (list: toplevels); i++) |
2579 | { |
2580 | gpointer item = g_list_model_get_item (list: toplevels, position: i); |
2581 | list = g_list_prepend (list, data: item); |
2582 | g_object_unref (object: item); |
2583 | } |
2584 | |
2585 | return list; |
2586 | } |
2587 | |
2588 | static void |
2589 | gtk_window_dispose (GObject *object) |
2590 | { |
2591 | GtkWindow *window = GTK_WINDOW (object); |
2592 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2593 | |
2594 | gtk_window_release_application (window); |
2595 | |
2596 | if (priv->transient_parent) |
2597 | gtk_window_set_transient_for (window, NULL); |
2598 | |
2599 | if (priv->group) |
2600 | gtk_window_group_remove_window (window_group: priv->group, window); |
2601 | |
2602 | g_list_free_full (list: priv->foci, free_func: (GDestroyNotify) gtk_pointer_focus_unref); |
2603 | priv->foci = NULL; |
2604 | |
2605 | g_clear_object (&priv->move_focus_widget); |
2606 | gtk_window_set_focus (window, NULL); |
2607 | gtk_window_set_default_widget (window, NULL); |
2608 | |
2609 | g_clear_pointer (&priv->child, gtk_widget_unparent); |
2610 | unset_titlebar (window); |
2611 | |
2612 | G_OBJECT_CLASS (gtk_window_parent_class)->dispose (object); |
2613 | } |
2614 | |
2615 | static void |
2616 | gtk_window_transient_parent_destroyed (GtkWindow *parent, |
2617 | GtkWindow *window) |
2618 | { |
2619 | GtkWindowPrivate *priv = gtk_window_get_instance_private (GTK_WINDOW (window)); |
2620 | |
2621 | if (priv->destroy_with_parent) |
2622 | gtk_window_destroy (window); |
2623 | else |
2624 | priv->transient_parent = NULL; |
2625 | } |
2626 | |
2627 | static void |
2628 | gtk_window_transient_parent_realized (GtkWidget *parent, |
2629 | GtkWidget *window) |
2630 | { |
2631 | GtkWindowPrivate *priv = gtk_window_get_instance_private (GTK_WINDOW (window)); |
2632 | GtkWindowPrivate *parent_priv = gtk_window_get_instance_private (GTK_WINDOW (parent)); |
2633 | if (_gtk_widget_get_realized (widget: window)) |
2634 | gdk_toplevel_set_transient_for (toplevel: GDK_TOPLEVEL (ptr: priv->surface), parent: parent_priv->surface); |
2635 | } |
2636 | |
2637 | static void |
2638 | gtk_window_transient_parent_unrealized (GtkWidget *parent, |
2639 | GtkWidget *window) |
2640 | { |
2641 | GtkWindowPrivate *priv = gtk_window_get_instance_private (GTK_WINDOW (window)); |
2642 | if (_gtk_widget_get_realized (widget: window)) |
2643 | gdk_toplevel_set_transient_for (toplevel: GDK_TOPLEVEL (ptr: priv->surface), NULL); |
2644 | } |
2645 | |
2646 | static void |
2647 | gtk_window_transient_parent_display_changed (GtkWindow *parent, |
2648 | GParamSpec *pspec, |
2649 | GtkWindow *window) |
2650 | { |
2651 | GtkWindowPrivate *parent_priv = gtk_window_get_instance_private (self: parent); |
2652 | |
2653 | gtk_window_set_display (window, display: parent_priv->display); |
2654 | } |
2655 | |
2656 | static void |
2657 | gtk_window_unset_transient_for (GtkWindow *window) |
2658 | { |
2659 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2660 | |
2661 | if (priv->transient_parent) |
2662 | { |
2663 | g_signal_handlers_disconnect_by_func (priv->transient_parent, |
2664 | gtk_window_transient_parent_realized, |
2665 | window); |
2666 | g_signal_handlers_disconnect_by_func (priv->transient_parent, |
2667 | gtk_window_transient_parent_unrealized, |
2668 | window); |
2669 | g_signal_handlers_disconnect_by_func (priv->transient_parent, |
2670 | gtk_window_transient_parent_display_changed, |
2671 | window); |
2672 | g_signal_handlers_disconnect_by_func (priv->transient_parent, |
2673 | gtk_window_transient_parent_destroyed, |
2674 | window); |
2675 | |
2676 | priv->transient_parent = NULL; |
2677 | |
2678 | if (priv->transient_parent_group) |
2679 | { |
2680 | priv->transient_parent_group = FALSE; |
2681 | gtk_window_group_remove_window (window_group: priv->group, window); |
2682 | } |
2683 | } |
2684 | } |
2685 | |
2686 | /** |
2687 | * gtk_window_set_transient_for: (attributes org.gtk.Method.set_property=transient-for) |
2688 | * @window: a `GtkWindow` |
2689 | * @parent: (nullable): parent window |
2690 | * |
2691 | * Dialog windows should be set transient for the main application |
2692 | * window they were spawned from. This allows window managers to e.g. |
2693 | * keep the dialog on top of the main window, or center the dialog |
2694 | * over the main window. [ctor@Gtk.Dialog.new_with_buttons] and other |
2695 | * convenience functions in GTK will sometimes call |
2696 | * gtk_window_set_transient_for() on your behalf. |
2697 | * |
2698 | * Passing %NULL for @parent unsets the current transient window. |
2699 | * |
2700 | * On Windows, this function puts the child window on top of the parent, |
2701 | * much as the window manager would have done on X. |
2702 | */ |
2703 | void |
2704 | gtk_window_set_transient_for (GtkWindow *window, |
2705 | GtkWindow *parent) |
2706 | { |
2707 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2708 | |
2709 | g_return_if_fail (GTK_IS_WINDOW (window)); |
2710 | g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent)); |
2711 | g_return_if_fail (window != parent); |
2712 | |
2713 | if (priv->transient_parent) |
2714 | { |
2715 | if (_gtk_widget_get_realized (GTK_WIDGET (window)) && |
2716 | _gtk_widget_get_realized (GTK_WIDGET (priv->transient_parent)) && |
2717 | (!parent || !_gtk_widget_get_realized (GTK_WIDGET (parent)))) |
2718 | gtk_window_transient_parent_unrealized (GTK_WIDGET (priv->transient_parent), |
2719 | GTK_WIDGET (window)); |
2720 | gtk_window_unset_transient_for (window); |
2721 | } |
2722 | |
2723 | priv->transient_parent = parent; |
2724 | |
2725 | if (parent) |
2726 | { |
2727 | GtkWindowPrivate *parent_priv = gtk_window_get_instance_private (self: parent); |
2728 | g_signal_connect (parent, "realize" , |
2729 | G_CALLBACK (gtk_window_transient_parent_realized), window); |
2730 | g_signal_connect (parent, "unrealize" , |
2731 | G_CALLBACK (gtk_window_transient_parent_unrealized), window); |
2732 | g_signal_connect (parent, "notify::display" , |
2733 | G_CALLBACK (gtk_window_transient_parent_display_changed), window); |
2734 | g_signal_connect (parent, "destroy" , |
2735 | G_CALLBACK (gtk_window_transient_parent_destroyed), window); |
2736 | |
2737 | gtk_window_set_display (window, display: parent_priv->display); |
2738 | |
2739 | |
2740 | if (_gtk_widget_get_realized (GTK_WIDGET (window)) && |
2741 | _gtk_widget_get_realized (GTK_WIDGET (parent))) |
2742 | gtk_window_transient_parent_realized (GTK_WIDGET (parent), GTK_WIDGET (window)); |
2743 | |
2744 | if (parent_priv->group) |
2745 | { |
2746 | gtk_window_group_add_window (window_group: parent_priv->group, window); |
2747 | priv->transient_parent_group = TRUE; |
2748 | } |
2749 | } |
2750 | |
2751 | update_window_actions (window); |
2752 | |
2753 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_TRANSIENT_FOR]); |
2754 | } |
2755 | |
2756 | /** |
2757 | * gtk_window_get_transient_for: (attributes org.gtk.Method.get_property=transient-for) |
2758 | * @window: a `GtkWindow` |
2759 | * |
2760 | * Fetches the transient parent for this window. |
2761 | * |
2762 | * Returns: (nullable) (transfer none): the transient parent for this window |
2763 | */ |
2764 | GtkWindow * |
2765 | gtk_window_get_transient_for (GtkWindow *window) |
2766 | { |
2767 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2768 | |
2769 | g_return_val_if_fail (GTK_IS_WINDOW (window), NULL); |
2770 | |
2771 | return priv->transient_parent; |
2772 | } |
2773 | |
2774 | /** |
2775 | * gtk_window_get_application: (attributes org.gtk.Method.get_property=application) |
2776 | * @window: a `GtkWindow` |
2777 | * |
2778 | * Gets the `GtkApplication` associated with the window. |
2779 | * |
2780 | * Returns: (nullable) (transfer none): a `GtkApplication` |
2781 | */ |
2782 | GtkApplication * |
2783 | gtk_window_get_application (GtkWindow *window) |
2784 | { |
2785 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2786 | |
2787 | g_return_val_if_fail (GTK_IS_WINDOW (window), NULL); |
2788 | |
2789 | return priv->application; |
2790 | } |
2791 | |
2792 | static void |
2793 | gtk_window_release_application (GtkWindow *window) |
2794 | { |
2795 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2796 | |
2797 | if (priv->application) |
2798 | { |
2799 | GtkApplication *application; |
2800 | |
2801 | /* steal reference into temp variable */ |
2802 | application = priv->application; |
2803 | priv->application = NULL; |
2804 | gtk_widget_remove_controller (GTK_WIDGET (window), |
2805 | controller: priv->application_shortcut_controller); |
2806 | priv->application_shortcut_controller = NULL; |
2807 | |
2808 | gtk_application_remove_window (application, window); |
2809 | g_object_unref (object: application); |
2810 | } |
2811 | } |
2812 | |
2813 | /** |
2814 | * gtk_window_set_application: (attributes org.gtk.Method.set_property=application) |
2815 | * @window: a `GtkWindow` |
2816 | * @application: (nullable): a `GtkApplication`, or %NULL to unset |
2817 | * |
2818 | * Sets or unsets the `GtkApplication` associated with the window. |
2819 | * |
2820 | * The application will be kept alive for at least as long as it has |
2821 | * any windows associated with it (see g_application_hold() for a way |
2822 | * to keep it alive without windows). |
2823 | * |
2824 | * Normally, the connection between the application and the window will |
2825 | * remain until the window is destroyed, but you can explicitly remove |
2826 | * it by setting the @application to %NULL. |
2827 | * |
2828 | * This is equivalent to calling [method@Gtk.Application.remove_window] |
2829 | * and/or [method@Gtk.Application.add_window] on the old/new applications |
2830 | * as relevant. |
2831 | */ |
2832 | void |
2833 | gtk_window_set_application (GtkWindow *window, |
2834 | GtkApplication *application) |
2835 | { |
2836 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2837 | |
2838 | g_return_if_fail (GTK_IS_WINDOW (window)); |
2839 | |
2840 | if (priv->application != application) |
2841 | { |
2842 | gtk_window_release_application (window); |
2843 | |
2844 | priv->application = application; |
2845 | |
2846 | if (priv->application != NULL) |
2847 | { |
2848 | GtkApplicationAccels *app_accels; |
2849 | |
2850 | g_object_ref (priv->application); |
2851 | |
2852 | gtk_application_add_window (application: priv->application, window); |
2853 | |
2854 | app_accels = gtk_application_get_application_accels (application: priv->application); |
2855 | priv->application_shortcut_controller = gtk_shortcut_controller_new_for_model (model: gtk_application_accels_get_shortcuts (accels: app_accels)); |
2856 | gtk_event_controller_set_name (controller: priv->application_shortcut_controller, name: "gtk-application-shortcuts" ); |
2857 | gtk_event_controller_set_propagation_phase (controller: priv->application_shortcut_controller, phase: GTK_PHASE_CAPTURE); |
2858 | gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER (priv->application_shortcut_controller), scope: GTK_SHORTCUT_SCOPE_GLOBAL); |
2859 | gtk_widget_add_controller (GTK_WIDGET (window), controller: priv->application_shortcut_controller); |
2860 | } |
2861 | |
2862 | _gtk_widget_update_parent_muxer (GTK_WIDGET (window)); |
2863 | |
2864 | _gtk_window_notify_keys_changed (window); |
2865 | |
2866 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_APPLICATION]); |
2867 | } |
2868 | } |
2869 | |
2870 | /** |
2871 | * gtk_window_set_destroy_with_parent: (attributes org.gtk.Method.set_property=destroy-with-parent) |
2872 | * @window: a `GtkWindow` |
2873 | * @setting: whether to destroy @window with its transient parent |
2874 | * |
2875 | * If @setting is %TRUE, then destroying the transient parent of @window |
2876 | * will also destroy @window itself. |
2877 | * |
2878 | * This is useful for dialogs that shouldn’t persist beyond the lifetime |
2879 | * of the main window they are associated with, for example. |
2880 | */ |
2881 | void |
2882 | gtk_window_set_destroy_with_parent (GtkWindow *window, |
2883 | gboolean setting) |
2884 | { |
2885 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2886 | |
2887 | g_return_if_fail (GTK_IS_WINDOW (window)); |
2888 | |
2889 | if (priv->destroy_with_parent == (setting != FALSE)) |
2890 | return; |
2891 | |
2892 | priv->destroy_with_parent = setting; |
2893 | |
2894 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_DESTROY_WITH_PARENT]); |
2895 | } |
2896 | |
2897 | /** |
2898 | * gtk_window_get_destroy_with_parent: (attributes org.gtk.Method.get_property=destroy-with-parent) |
2899 | * @window: a `GtkWindow` |
2900 | * |
2901 | * Returns whether the window will be destroyed with its transient parent. |
2902 | * |
2903 | * Returns: %TRUE if the window will be destroyed with its transient parent. |
2904 | */ |
2905 | gboolean |
2906 | gtk_window_get_destroy_with_parent (GtkWindow *window) |
2907 | { |
2908 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2909 | |
2910 | g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); |
2911 | |
2912 | return priv->destroy_with_parent; |
2913 | } |
2914 | |
2915 | /** |
2916 | * gtk_window_set_hide_on_close: (attributes org.gtk.Method.set_property=hide-on-close) |
2917 | * @window: a `GtkWindow` |
2918 | * @setting: whether to hide the window when it is closed |
2919 | * |
2920 | * If @setting is %TRUE, then clicking the close button on the window |
2921 | * will not destroy it, but only hide it. |
2922 | */ |
2923 | void |
2924 | gtk_window_set_hide_on_close (GtkWindow *window, |
2925 | gboolean setting) |
2926 | { |
2927 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2928 | |
2929 | g_return_if_fail (GTK_IS_WINDOW (window)); |
2930 | |
2931 | if (priv->hide_on_close == setting) |
2932 | return; |
2933 | |
2934 | priv->hide_on_close = setting; |
2935 | |
2936 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_HIDE_ON_CLOSE]); |
2937 | } |
2938 | |
2939 | /** |
2940 | * gtk_window_get_hide_on_close: (attributes org.gtk.Method.get_property=hide-on-close) |
2941 | * @window: a `GtkWindow` |
2942 | * |
2943 | * Returns whether the window will be hidden when the close button is clicked. |
2944 | * |
2945 | * Returns: %TRUE if the window will be hidden |
2946 | */ |
2947 | gboolean |
2948 | gtk_window_get_hide_on_close (GtkWindow *window) |
2949 | { |
2950 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2951 | |
2952 | g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); |
2953 | |
2954 | return priv->hide_on_close; |
2955 | } |
2956 | |
2957 | static GtkWindowGeometryInfo* |
2958 | gtk_window_get_geometry_info (GtkWindow *window, |
2959 | gboolean create) |
2960 | { |
2961 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2962 | GtkWindowGeometryInfo *info; |
2963 | |
2964 | info = priv->geometry_info; |
2965 | if (!info && create) |
2966 | { |
2967 | info = g_new0 (GtkWindowGeometryInfo, 1); |
2968 | |
2969 | info->last.configure_request.x = 0; |
2970 | info->last.configure_request.y = 0; |
2971 | info->last.configure_request.width = -1; |
2972 | info->last.configure_request.height = -1; |
2973 | priv->geometry_info = info; |
2974 | } |
2975 | |
2976 | return info; |
2977 | } |
2978 | |
2979 | static void |
2980 | unset_titlebar (GtkWindow *window) |
2981 | { |
2982 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2983 | |
2984 | if (priv->title_box != NULL) |
2985 | { |
2986 | gtk_widget_unparent (widget: priv->title_box); |
2987 | priv->title_box = NULL; |
2988 | priv->titlebar = NULL; |
2989 | } |
2990 | } |
2991 | |
2992 | static gboolean |
2993 | gtk_window_supports_client_shadow (GtkWindow *window) |
2994 | { |
2995 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
2996 | GdkDisplay *display; |
2997 | |
2998 | display = priv->display; |
2999 | |
3000 | if (!gdk_display_is_rgba (display)) |
3001 | return FALSE; |
3002 | |
3003 | if (!gdk_display_is_composited (display)) |
3004 | return FALSE; |
3005 | |
3006 | #ifdef GDK_WINDOWING_X11 |
3007 | if (GDK_IS_X11_DISPLAY (display)) |
3008 | { |
3009 | if (!gdk_x11_screen_supports_net_wm_hint (screen: gdk_x11_display_get_screen (display), |
3010 | property_name: g_intern_static_string (string: "_GTK_FRAME_EXTENTS" ))) |
3011 | return FALSE; |
3012 | } |
3013 | #endif |
3014 | |
3015 | return TRUE; |
3016 | } |
3017 | |
3018 | static void |
3019 | gtk_window_enable_csd (GtkWindow *window) |
3020 | { |
3021 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3022 | GtkWidget *widget = GTK_WIDGET (window); |
3023 | |
3024 | /* We need a visual with alpha for client shadows */ |
3025 | if (priv->use_client_shadow) |
3026 | gtk_widget_add_css_class (widget, css_class: "csd" ); |
3027 | else |
3028 | gtk_widget_add_css_class (widget, css_class: "solid-csd" ); |
3029 | |
3030 | priv->client_decorated = TRUE; |
3031 | } |
3032 | |
3033 | /** |
3034 | * gtk_window_set_titlebar: (attributes org.gtk.Method.set_property=titlebar) |
3035 | * @window: a `GtkWindow` |
3036 | * @titlebar: (nullable): the widget to use as titlebar |
3037 | * |
3038 | * Sets a custom titlebar for @window. |
3039 | * |
3040 | * A typical widget used here is [class@Gtk.HeaderBar], as it |
3041 | * provides various features expected of a titlebar while allowing |
3042 | * the addition of child widgets to it. |
3043 | * |
3044 | * If you set a custom titlebar, GTK will do its best to convince |
3045 | * the window manager not to put its own titlebar on the window. |
3046 | * Depending on the system, this function may not work for a window |
3047 | * that is already visible, so you set the titlebar before calling |
3048 | * [method@Gtk.Widget.show]. |
3049 | */ |
3050 | void |
3051 | gtk_window_set_titlebar (GtkWindow *window, |
3052 | GtkWidget *titlebar) |
3053 | { |
3054 | GtkWidget *widget = GTK_WIDGET (window); |
3055 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3056 | gboolean was_mapped; |
3057 | |
3058 | g_return_if_fail (GTK_IS_WINDOW (window)); |
3059 | |
3060 | if (priv->titlebar == titlebar) |
3061 | return; |
3062 | |
3063 | if ((!priv->title_box && titlebar) || (priv->title_box && !titlebar)) |
3064 | { |
3065 | was_mapped = _gtk_widget_get_mapped (widget); |
3066 | if (_gtk_widget_get_realized (widget)) |
3067 | { |
3068 | g_warning ("gtk_window_set_titlebar() called on a realized window" ); |
3069 | gtk_widget_unrealize (widget); |
3070 | } |
3071 | } |
3072 | else |
3073 | was_mapped = FALSE; |
3074 | |
3075 | unset_titlebar (window); |
3076 | |
3077 | if (titlebar == NULL) |
3078 | { |
3079 | /* these are updated in realize() */ |
3080 | priv->client_decorated = FALSE; |
3081 | gtk_widget_remove_css_class (widget, css_class: "csd" ); |
3082 | gtk_widget_remove_css_class (widget, css_class: "solid-csd" ); |
3083 | } |
3084 | else |
3085 | { |
3086 | priv->use_client_shadow = gtk_window_supports_client_shadow (window); |
3087 | |
3088 | gtk_window_enable_csd (window); |
3089 | priv->titlebar = titlebar; |
3090 | priv->title_box = titlebar; |
3091 | gtk_widget_insert_before (widget: priv->title_box, parent: widget, NULL); |
3092 | |
3093 | gtk_widget_add_css_class (widget: titlebar, css_class: "titlebar" ); |
3094 | } |
3095 | |
3096 | if (was_mapped) |
3097 | gtk_widget_map (widget); |
3098 | |
3099 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_TITLEBAR]); |
3100 | } |
3101 | |
3102 | /** |
3103 | * gtk_window_get_titlebar: (attributes org.gtk.Method.get_property=titlebar) |
3104 | * @window: a `GtkWindow` |
3105 | * |
3106 | * Returns the custom titlebar that has been set with |
3107 | * gtk_window_set_titlebar(). |
3108 | * |
3109 | * Returns: (nullable) (transfer none): the custom titlebar |
3110 | */ |
3111 | GtkWidget * |
3112 | gtk_window_get_titlebar (GtkWindow *window) |
3113 | { |
3114 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3115 | |
3116 | g_return_val_if_fail (GTK_IS_WINDOW (window), NULL); |
3117 | |
3118 | return priv->titlebar; |
3119 | } |
3120 | |
3121 | /** |
3122 | * gtk_window_set_decorated: (attributes org.gtk.Method.set_property=decorated) |
3123 | * @window: a `GtkWindow` |
3124 | * @setting: %TRUE to decorate the window |
3125 | * |
3126 | * Sets whether the window should be decorated. |
3127 | * |
3128 | * By default, windows are decorated with a title bar, resize |
3129 | * controls, etc. Some window managers allow GTK to disable these |
3130 | * decorations, creating a borderless window. If you set the decorated |
3131 | * property to %FALSE using this function, GTK will do its best to |
3132 | * convince the window manager not to decorate the window. Depending on |
3133 | * the system, this function may not have any effect when called on a |
3134 | * window that is already visible, so you should call it before calling |
3135 | * [method@Gtk.Widget.show]. |
3136 | * |
3137 | * On Windows, this function always works, since there’s no window manager |
3138 | * policy involved. |
3139 | */ |
3140 | void |
3141 | gtk_window_set_decorated (GtkWindow *window, |
3142 | gboolean setting) |
3143 | { |
3144 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3145 | |
3146 | g_return_if_fail (GTK_IS_WINDOW (window)); |
3147 | |
3148 | setting = setting != FALSE; |
3149 | |
3150 | if (setting == priv->decorated) |
3151 | return; |
3152 | |
3153 | priv->decorated = setting; |
3154 | |
3155 | if (priv->surface) |
3156 | gdk_toplevel_set_decorated (toplevel: GDK_TOPLEVEL (ptr: priv->surface), decorated: priv->decorated && !priv->client_decorated); |
3157 | |
3158 | update_window_actions (window); |
3159 | gtk_widget_queue_resize (GTK_WIDGET (window)); |
3160 | |
3161 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_DECORATED]); |
3162 | } |
3163 | |
3164 | /** |
3165 | * gtk_window_get_decorated: (attributes org.gtk.Method.get_property=decorated) |
3166 | * @window: a `GtkWindow` |
3167 | * |
3168 | * Returns whether the window has been set to have decorations. |
3169 | * |
3170 | * Returns: %TRUE if the window has been set to have decorations |
3171 | */ |
3172 | gboolean |
3173 | gtk_window_get_decorated (GtkWindow *window) |
3174 | { |
3175 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3176 | |
3177 | g_return_val_if_fail (GTK_IS_WINDOW (window), TRUE); |
3178 | |
3179 | return priv->decorated; |
3180 | } |
3181 | |
3182 | /** |
3183 | * gtk_window_set_deletable: (attributes org.gtk.Method.set_property=deletable) |
3184 | * @window: a `GtkWindow` |
3185 | * @setting: %TRUE to decorate the window as deletable |
3186 | * |
3187 | * Sets whether the window should be deletable. |
3188 | * |
3189 | * By default, windows have a close button in the window frame. |
3190 | * Some window managers allow GTK to disable this button. If you |
3191 | * set the deletable property to %FALSE using this function, GTK |
3192 | * will do its best to convince the window manager not to show a |
3193 | * close button. Depending on the system, this function may not |
3194 | * have any effect when called on a window that is already visible, |
3195 | * so you should call it before calling [method@Gtk.Widget.show]. |
3196 | * |
3197 | * On Windows, this function always works, since there’s no window |
3198 | * manager policy involved. |
3199 | */ |
3200 | void |
3201 | gtk_window_set_deletable (GtkWindow *window, |
3202 | gboolean setting) |
3203 | { |
3204 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3205 | |
3206 | g_return_if_fail (GTK_IS_WINDOW (window)); |
3207 | |
3208 | setting = setting != FALSE; |
3209 | |
3210 | if (setting == priv->deletable) |
3211 | return; |
3212 | |
3213 | priv->deletable = setting; |
3214 | |
3215 | if (priv->surface) |
3216 | gdk_toplevel_set_deletable (toplevel: GDK_TOPLEVEL (ptr: priv->surface), deletable: priv->deletable); |
3217 | |
3218 | update_window_actions (window); |
3219 | |
3220 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_DELETABLE]); |
3221 | } |
3222 | |
3223 | /** |
3224 | * gtk_window_get_deletable: (attributes org.gtk.Method.get_property=deletable) |
3225 | * @window: a `GtkWindow` |
3226 | * |
3227 | * Returns whether the window has been set to have a close button. |
3228 | * |
3229 | * Returns: %TRUE if the window has been set to have a close button |
3230 | */ |
3231 | gboolean |
3232 | gtk_window_get_deletable (GtkWindow *window) |
3233 | { |
3234 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3235 | |
3236 | g_return_val_if_fail (GTK_IS_WINDOW (window), TRUE); |
3237 | |
3238 | return priv->deletable; |
3239 | } |
3240 | |
3241 | static GtkWindowIconInfo* |
3242 | get_icon_info (GtkWindow *window) |
3243 | { |
3244 | return g_object_get_qdata (G_OBJECT (window), quark: quark_gtk_window_icon_info); |
3245 | } |
3246 | |
3247 | static void |
3248 | free_icon_info (GtkWindowIconInfo *info) |
3249 | { |
3250 | g_free (mem: info->icon_name); |
3251 | g_slice_free (GtkWindowIconInfo, info); |
3252 | } |
3253 | |
3254 | |
3255 | static GtkWindowIconInfo* |
3256 | ensure_icon_info (GtkWindow *window) |
3257 | { |
3258 | GtkWindowIconInfo *info; |
3259 | |
3260 | info = get_icon_info (window); |
3261 | |
3262 | if (info == NULL) |
3263 | { |
3264 | info = g_slice_new0 (GtkWindowIconInfo); |
3265 | g_object_set_qdata_full (G_OBJECT (window), |
3266 | quark: quark_gtk_window_icon_info, |
3267 | data: info, |
3268 | destroy: (GDestroyNotify)free_icon_info); |
3269 | } |
3270 | |
3271 | return info; |
3272 | } |
3273 | |
3274 | static int |
3275 | icon_size_compare (GdkTexture *a, |
3276 | GdkTexture *b) |
3277 | { |
3278 | int area_a, area_b; |
3279 | |
3280 | area_a = gdk_texture_get_width (texture: a) * gdk_texture_get_height (texture: a); |
3281 | area_b = gdk_texture_get_width (texture: b) * gdk_texture_get_height (texture: b); |
3282 | |
3283 | return area_a - area_b; |
3284 | } |
3285 | |
3286 | static GdkTexture * |
3287 | render_paintable_to_texture (GdkPaintable *paintable) |
3288 | { |
3289 | GtkSnapshot *snapshot; |
3290 | GskRenderNode *node; |
3291 | int width, height; |
3292 | cairo_surface_t *surface; |
3293 | cairo_t *cr; |
3294 | GdkTexture *texture; |
3295 | |
3296 | width = gdk_paintable_get_intrinsic_width (paintable); |
3297 | height = gdk_paintable_get_intrinsic_height (paintable); |
3298 | |
3299 | surface = cairo_image_surface_create (format: CAIRO_FORMAT_ARGB32, width, height); |
3300 | |
3301 | snapshot = gtk_snapshot_new (); |
3302 | gdk_paintable_snapshot (paintable, snapshot, width, height); |
3303 | node = gtk_snapshot_free_to_node (snapshot); |
3304 | |
3305 | cr = cairo_create (target: surface); |
3306 | gsk_render_node_draw (node, cr); |
3307 | cairo_destroy (cr); |
3308 | |
3309 | gsk_render_node_unref (node); |
3310 | |
3311 | texture = gdk_texture_new_for_surface (surface); |
3312 | cairo_surface_destroy (surface); |
3313 | |
3314 | return texture; |
3315 | } |
3316 | |
3317 | static GList * |
3318 | icon_list_from_theme (GtkWindow *window, |
3319 | const char *name) |
3320 | { |
3321 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3322 | GList *list; |
3323 | GtkIconTheme *icon_theme; |
3324 | GtkIconPaintable *info; |
3325 | GdkTexture *texture; |
3326 | int *sizes; |
3327 | int i; |
3328 | |
3329 | icon_theme = gtk_icon_theme_get_for_display (display: priv->display); |
3330 | |
3331 | sizes = gtk_icon_theme_get_icon_sizes (self: icon_theme, icon_name: name); |
3332 | |
3333 | list = NULL; |
3334 | for (i = 0; sizes[i]; i++) |
3335 | { |
3336 | /* FIXME |
3337 | * We need an EWMH extension to handle scalable icons |
3338 | * by passing their name to the WM. For now just use a |
3339 | * fixed size of 48. |
3340 | */ |
3341 | if (sizes[i] == -1) |
3342 | info = gtk_icon_theme_lookup_icon (self: icon_theme, icon_name: name, NULL, |
3343 | size: 48, scale: priv->scale, |
3344 | direction: gtk_widget_get_direction (GTK_WIDGET (window)), |
3345 | flags: 0); |
3346 | else |
3347 | info = gtk_icon_theme_lookup_icon (self: icon_theme, icon_name: name, NULL, |
3348 | size: sizes[i], scale: priv->scale, |
3349 | direction: gtk_widget_get_direction (GTK_WIDGET (window)), |
3350 | flags: 0); |
3351 | |
3352 | texture = render_paintable_to_texture (paintable: GDK_PAINTABLE (ptr: info)); |
3353 | list = g_list_insert_sorted (list, data: texture, func: (GCompareFunc) icon_size_compare); |
3354 | g_object_unref (object: info); |
3355 | } |
3356 | |
3357 | g_free (mem: sizes); |
3358 | |
3359 | return list; |
3360 | } |
3361 | |
3362 | static void |
3363 | gtk_window_realize_icon (GtkWindow *window) |
3364 | { |
3365 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3366 | GtkWindowIconInfo *info; |
3367 | GList *icon_list = NULL; |
3368 | |
3369 | g_return_if_fail (priv->surface != NULL); |
3370 | |
3371 | info = ensure_icon_info (window); |
3372 | |
3373 | if (info->realized) |
3374 | return; |
3375 | |
3376 | info->using_default_icon = FALSE; |
3377 | info->using_themed_icon = FALSE; |
3378 | |
3379 | /* Look up themed icon */ |
3380 | if (icon_list == NULL && info->icon_name) |
3381 | { |
3382 | icon_list = icon_list_from_theme (window, name: info->icon_name); |
3383 | if (icon_list) |
3384 | info->using_themed_icon = TRUE; |
3385 | } |
3386 | |
3387 | /* Look up themed icon */ |
3388 | if (icon_list == NULL && default_icon_name) |
3389 | { |
3390 | icon_list = icon_list_from_theme (window, name: default_icon_name); |
3391 | info->using_default_icon = TRUE; |
3392 | info->using_themed_icon = TRUE; |
3393 | } |
3394 | |
3395 | info->realized = TRUE; |
3396 | |
3397 | gdk_toplevel_set_icon_list (toplevel: GDK_TOPLEVEL (ptr: priv->surface), surfaces: icon_list); |
3398 | |
3399 | if (info->using_themed_icon) |
3400 | g_list_free_full (list: icon_list, free_func: g_object_unref); |
3401 | } |
3402 | |
3403 | GdkPaintable * |
3404 | gtk_window_get_icon_for_size (GtkWindow *window, |
3405 | int size) |
3406 | { |
3407 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3408 | const char *name; |
3409 | GtkIconPaintable *info; |
3410 | |
3411 | name = gtk_window_get_icon_name (window); |
3412 | |
3413 | if (!name) |
3414 | name = default_icon_name; |
3415 | if (!name) |
3416 | return NULL; |
3417 | |
3418 | info = gtk_icon_theme_lookup_icon (self: gtk_icon_theme_get_for_display (display: gtk_widget_get_display (GTK_WIDGET (window))), |
3419 | icon_name: name, NULL, size, scale: priv->scale, |
3420 | direction: gtk_widget_get_direction (GTK_WIDGET (window)), |
3421 | flags: 0); |
3422 | if (info == NULL) |
3423 | return NULL; |
3424 | |
3425 | return GDK_PAINTABLE (ptr: info); |
3426 | } |
3427 | |
3428 | static void |
3429 | gtk_window_unrealize_icon (GtkWindow *window) |
3430 | { |
3431 | GtkWindowIconInfo *info; |
3432 | |
3433 | info = get_icon_info (window); |
3434 | |
3435 | if (info == NULL) |
3436 | return; |
3437 | |
3438 | /* We don't clear the properties on the window, just figure the |
3439 | * window is going away. |
3440 | */ |
3441 | |
3442 | info->realized = FALSE; |
3443 | |
3444 | } |
3445 | |
3446 | static void |
3447 | update_themed_icon (GtkWindow *window) |
3448 | { |
3449 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_ICON_NAME]); |
3450 | |
3451 | gtk_window_unrealize_icon (window); |
3452 | |
3453 | if (_gtk_widget_get_realized (GTK_WIDGET (window))) |
3454 | gtk_window_realize_icon (window); |
3455 | } |
3456 | |
3457 | /** |
3458 | * gtk_window_set_icon_name: (attributes org.gtk.Method.set_property=icon-name) |
3459 | * @window: a `GtkWindow` |
3460 | * @name: (nullable): the name of the themed icon |
3461 | * |
3462 | * Sets the icon for the window from a named themed icon. |
3463 | * |
3464 | * See the docs for [class@Gtk.IconTheme] for more details. |
3465 | * On some platforms, the window icon is not used at all. |
3466 | * |
3467 | * Note that this has nothing to do with the WM_ICON_NAME |
3468 | * property which is mentioned in the ICCCM. |
3469 | */ |
3470 | void |
3471 | gtk_window_set_icon_name (GtkWindow *window, |
3472 | const char *name) |
3473 | { |
3474 | GtkWindowIconInfo *info; |
3475 | char *tmp; |
3476 | |
3477 | g_return_if_fail (GTK_IS_WINDOW (window)); |
3478 | |
3479 | info = ensure_icon_info (window); |
3480 | |
3481 | if (g_strcmp0 (str1: info->icon_name, str2: name) == 0) |
3482 | return; |
3483 | |
3484 | tmp = info->icon_name; |
3485 | info->icon_name = g_strdup (str: name); |
3486 | g_free (mem: tmp); |
3487 | |
3488 | update_themed_icon (window); |
3489 | |
3490 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_ICON_NAME]); |
3491 | } |
3492 | |
3493 | /** |
3494 | * gtk_window_get_icon_name: (attributes org.gtk.Method.get_property=icon-name) |
3495 | * @window: a `GtkWindow` |
3496 | * |
3497 | * Returns the name of the themed icon for the window. |
3498 | * |
3499 | * Returns: (nullable): the icon name |
3500 | */ |
3501 | const char * |
3502 | gtk_window_get_icon_name (GtkWindow *window) |
3503 | { |
3504 | GtkWindowIconInfo *info; |
3505 | |
3506 | g_return_val_if_fail (GTK_IS_WINDOW (window), NULL); |
3507 | |
3508 | info = ensure_icon_info (window); |
3509 | |
3510 | return info->icon_name; |
3511 | } |
3512 | |
3513 | /** |
3514 | * gtk_window_set_default_icon_name: |
3515 | * @name: the name of the themed icon |
3516 | * |
3517 | * Sets an icon to be used as fallback. |
3518 | * |
3519 | * The fallback icon is used for windows that |
3520 | * haven't had [method@Gtk.Window.set_icon_name] |
3521 | * called on them. |
3522 | */ |
3523 | void |
3524 | gtk_window_set_default_icon_name (const char *name) |
3525 | { |
3526 | GList *tmp_list; |
3527 | GList *toplevels; |
3528 | |
3529 | g_free (mem: default_icon_name); |
3530 | default_icon_name = g_strdup (str: name); |
3531 | |
3532 | /* Update all toplevels */ |
3533 | toplevels = gtk_window_list_toplevels (); |
3534 | tmp_list = toplevels; |
3535 | while (tmp_list != NULL) |
3536 | { |
3537 | GtkWindowIconInfo *info; |
3538 | GtkWindow *w = tmp_list->data; |
3539 | |
3540 | info = get_icon_info (window: w); |
3541 | if (info && info->using_default_icon && info->using_themed_icon) |
3542 | { |
3543 | gtk_window_unrealize_icon (window: w); |
3544 | if (_gtk_widget_get_realized (GTK_WIDGET (w))) |
3545 | gtk_window_realize_icon (window: w); |
3546 | } |
3547 | |
3548 | tmp_list = tmp_list->next; |
3549 | } |
3550 | g_list_free (list: toplevels); |
3551 | } |
3552 | |
3553 | /** |
3554 | * gtk_window_get_default_icon_name: |
3555 | * |
3556 | * Returns the fallback icon name for windows. |
3557 | * |
3558 | * The returned string is owned by GTK and should not |
3559 | * be modified. It is only valid until the next call to |
3560 | * [func@Gtk.Window.set_default_icon_name]. |
3561 | * |
3562 | * Returns: (nullable): the fallback icon name for windows |
3563 | */ |
3564 | const char * |
3565 | gtk_window_get_default_icon_name (void) |
3566 | { |
3567 | return default_icon_name; |
3568 | } |
3569 | |
3570 | static void |
3571 | gtk_window_update_csd_size (GtkWindow *window, |
3572 | int *width, |
3573 | int *height, |
3574 | int apply) |
3575 | { |
3576 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3577 | GtkBorder window_border = { 0 }; |
3578 | int w, h; |
3579 | |
3580 | if (!priv->decorated || |
3581 | priv->fullscreen) |
3582 | return; |
3583 | |
3584 | get_shadow_width (window, shadow_width: &window_border); |
3585 | w = *width + apply * (window_border.left + window_border.right); |
3586 | h = *height + apply * (window_border.top + window_border.bottom); |
3587 | |
3588 | /* Make sure the size remains acceptable */ |
3589 | if (w < 1) |
3590 | w = 1; |
3591 | if (h < 1) |
3592 | h = 1; |
3593 | |
3594 | /* Only update given size if not negative */ |
3595 | if (*width > -1) |
3596 | *width = w; |
3597 | if (*height > -1) |
3598 | *height = h; |
3599 | } |
3600 | |
3601 | static void |
3602 | gtk_window_set_default_size_internal (GtkWindow *window, |
3603 | gboolean change_width, |
3604 | int width, |
3605 | gboolean change_height, |
3606 | int height) |
3607 | { |
3608 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3609 | |
3610 | g_return_if_fail (change_width == FALSE || width >= -1); |
3611 | g_return_if_fail (change_height == FALSE || height >= -1); |
3612 | |
3613 | g_object_freeze_notify (G_OBJECT (window)); |
3614 | |
3615 | if (change_width) |
3616 | { |
3617 | if (priv->default_width != width) |
3618 | { |
3619 | priv->default_width = width; |
3620 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_DEFAULT_WIDTH]); |
3621 | } |
3622 | } |
3623 | |
3624 | if (change_height) |
3625 | { |
3626 | if (priv->default_height != height) |
3627 | { |
3628 | priv->default_height = height; |
3629 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_DEFAULT_HEIGHT]); |
3630 | } |
3631 | } |
3632 | |
3633 | g_object_thaw_notify (G_OBJECT (window)); |
3634 | } |
3635 | |
3636 | /** |
3637 | * gtk_window_set_default_size: |
3638 | * @window: a `GtkWindow` |
3639 | * @width: width in pixels, or -1 to unset the default width |
3640 | * @height: height in pixels, or -1 to unset the default height |
3641 | * |
3642 | * Sets the default size of a window. |
3643 | * |
3644 | * If the window’s “natural” size (its size request) is larger than |
3645 | * the default, the default will be ignored. |
3646 | * |
3647 | * Unlike [method@Gtk.Widget.set_size_request], which sets a size |
3648 | * request for a widget and thus would keep users from shrinking |
3649 | * the window, this function only sets the initial size, just as |
3650 | * if the user had resized the window themselves. Users can still |
3651 | * shrink the window again as they normally would. Setting a default |
3652 | * size of -1 means to use the “natural” default size (the size request |
3653 | * of the window). |
3654 | * |
3655 | * The default size of a window only affects the first time a window is |
3656 | * shown; if a window is hidden and re-shown, it will remember the size |
3657 | * it had prior to hiding, rather than using the default size. |
3658 | * |
3659 | * Windows can’t actually be 0x0 in size, they must be at least 1x1, but |
3660 | * passing 0 for @width and @height is OK, resulting in a 1x1 default size. |
3661 | * |
3662 | * If you use this function to reestablish a previously saved window size, |
3663 | * note that the appropriate size to save is the one returned by |
3664 | * [method@Gtk.Window.get_default_size]. Using the window allocation |
3665 | * directly will not work in all circumstances and can lead to growing |
3666 | * or shrinking windows. |
3667 | */ |
3668 | void |
3669 | gtk_window_set_default_size (GtkWindow *window, |
3670 | int width, |
3671 | int height) |
3672 | { |
3673 | g_return_if_fail (GTK_IS_WINDOW (window)); |
3674 | g_return_if_fail (width >= -1); |
3675 | g_return_if_fail (height >= -1); |
3676 | |
3677 | gtk_window_set_default_size_internal (window, TRUE, width, TRUE, height); |
3678 | gtk_widget_queue_resize (GTK_WIDGET (window)); |
3679 | } |
3680 | |
3681 | /** |
3682 | * gtk_window_get_default_size: |
3683 | * @window: a `GtkWindow` |
3684 | * @width: (out) (optional): location to store the default width |
3685 | * @height: (out) (optional): location to store the default height |
3686 | * |
3687 | * Gets the default size of the window. |
3688 | * |
3689 | * A value of 0 for the width or height indicates that a default |
3690 | * size has not been explicitly set for that dimension, so the |
3691 | * “natural” size of the window will be used. |
3692 | */ |
3693 | void |
3694 | gtk_window_get_default_size (GtkWindow *window, |
3695 | int *width, |
3696 | int *height) |
3697 | { |
3698 | g_return_if_fail (GTK_IS_WINDOW (window)); |
3699 | |
3700 | gtk_window_get_remembered_size (window, width, height); |
3701 | } |
3702 | |
3703 | static gboolean |
3704 | gtk_window_close_request (GtkWindow *window) |
3705 | { |
3706 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3707 | |
3708 | if (priv->hide_on_close) |
3709 | { |
3710 | gtk_widget_hide (GTK_WIDGET (window)); |
3711 | return TRUE; |
3712 | } |
3713 | |
3714 | return FALSE; |
3715 | } |
3716 | |
3717 | gboolean |
3718 | gtk_window_emit_close_request (GtkWindow *window) |
3719 | { |
3720 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3721 | gboolean handled; |
3722 | |
3723 | /* Avoid re-entrancy issues when calling gtk_window_close from a |
3724 | * close-request handler */ |
3725 | if (priv->in_emit_close_request) |
3726 | return TRUE; |
3727 | |
3728 | priv->in_emit_close_request = TRUE; |
3729 | g_signal_emit (instance: window, signal_id: window_signals[CLOSE_REQUEST], detail: 0, &handled); |
3730 | priv->in_emit_close_request = FALSE; |
3731 | |
3732 | return handled; |
3733 | } |
3734 | |
3735 | static void |
3736 | gtk_window_finalize (GObject *object) |
3737 | { |
3738 | GtkWindow *window = GTK_WINDOW (object); |
3739 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3740 | GdkSeat *seat; |
3741 | |
3742 | g_free (mem: priv->title); |
3743 | gtk_window_release_application (window); |
3744 | |
3745 | if (priv->geometry_info) |
3746 | { |
3747 | g_free (mem: priv->geometry_info); |
3748 | } |
3749 | |
3750 | if (priv->keys_changed_handler) |
3751 | { |
3752 | g_source_remove (tag: priv->keys_changed_handler); |
3753 | priv->keys_changed_handler = 0; |
3754 | } |
3755 | |
3756 | seat = gdk_display_get_default_seat (display: priv->display); |
3757 | if (seat) |
3758 | g_signal_handlers_disconnect_by_func (seat, device_removed_cb, window); |
3759 | |
3760 | #ifdef GDK_WINDOWING_X11 |
3761 | g_signal_handlers_disconnect_by_func (gtk_settings_get_for_display (priv->display), |
3762 | gtk_window_on_theme_variant_changed, |
3763 | window); |
3764 | #endif |
3765 | |
3766 | g_free (mem: priv->startup_id); |
3767 | |
3768 | if (priv->mnemonics_display_timeout_id) |
3769 | { |
3770 | g_source_remove (tag: priv->mnemonics_display_timeout_id); |
3771 | priv->mnemonics_display_timeout_id = 0; |
3772 | } |
3773 | |
3774 | if (priv->focus_visible_timeout) |
3775 | { |
3776 | g_source_remove (tag: priv->focus_visible_timeout); |
3777 | priv->focus_visible_timeout = 0; |
3778 | } |
3779 | |
3780 | g_clear_object (&priv->constraint_solver); |
3781 | g_clear_object (&priv->renderer); |
3782 | g_clear_object (&priv->resize_cursor); |
3783 | |
3784 | G_OBJECT_CLASS (gtk_window_parent_class)->finalize (object); |
3785 | } |
3786 | |
3787 | static gboolean |
3788 | update_csd_visibility (GtkWindow *window) |
3789 | { |
3790 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3791 | gboolean visible; |
3792 | |
3793 | if (priv->title_box == NULL) |
3794 | return FALSE; |
3795 | |
3796 | visible = !priv->fullscreen && |
3797 | priv->decorated; |
3798 | |
3799 | gtk_widget_set_child_visible (widget: priv->title_box, child_visible: visible); |
3800 | |
3801 | return visible; |
3802 | } |
3803 | |
3804 | static void |
3805 | update_window_actions (GtkWindow *window) |
3806 | { |
3807 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3808 | gboolean is_sovereign_window = !priv->modal && !priv->transient_parent; |
3809 | |
3810 | gtk_widget_action_set_enabled (GTK_WIDGET (window), action_name: "window.minimize" , |
3811 | enabled: is_sovereign_window); |
3812 | gtk_widget_action_set_enabled (GTK_WIDGET (window), action_name: "window.toggle-maximized" , |
3813 | enabled: priv->resizable && is_sovereign_window); |
3814 | gtk_widget_action_set_enabled (GTK_WIDGET (window), action_name: "window.close" , |
3815 | enabled: priv->deletable); |
3816 | |
3817 | update_csd_visibility (window); |
3818 | } |
3819 | |
3820 | void |
3821 | _gtk_window_request_csd (GtkWindow *window) |
3822 | { |
3823 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3824 | |
3825 | priv->csd_requested = TRUE; |
3826 | } |
3827 | |
3828 | static gboolean |
3829 | gtk_window_should_use_csd (GtkWindow *window) |
3830 | { |
3831 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3832 | const char *csd_env; |
3833 | |
3834 | if (priv->csd_requested) |
3835 | return TRUE; |
3836 | |
3837 | if (!priv->decorated) |
3838 | return FALSE; |
3839 | |
3840 | csd_env = g_getenv (variable: "GTK_CSD" ); |
3841 | |
3842 | #ifdef GDK_WINDOWING_BROADWAY |
3843 | if (GDK_IS_BROADWAY_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) |
3844 | return TRUE; |
3845 | #endif |
3846 | |
3847 | #ifdef GDK_WINDOWING_WAYLAND |
3848 | if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) |
3849 | { |
3850 | GdkDisplay *gdk_display = gtk_widget_get_display (GTK_WIDGET (window)); |
3851 | return !gdk_wayland_display_prefers_ssd (display: gdk_display); |
3852 | } |
3853 | #endif |
3854 | |
3855 | #ifdef GDK_WINDOWING_WIN32 |
3856 | if (g_strcmp0 (csd_env, "0" ) != 0 && |
3857 | GDK_IS_WIN32_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) |
3858 | return TRUE; |
3859 | #endif |
3860 | |
3861 | return (g_strcmp0 (str1: csd_env, str2: "1" ) == 0); |
3862 | } |
3863 | |
3864 | static void |
3865 | gtk_window_show (GtkWidget *widget) |
3866 | { |
3867 | GtkWindow *window = GTK_WINDOW (widget); |
3868 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3869 | |
3870 | _gtk_widget_set_visible_flag (widget, TRUE); |
3871 | |
3872 | gtk_css_node_validate (cssnode: gtk_widget_get_css_node (widget)); |
3873 | |
3874 | gtk_widget_realize (widget); |
3875 | |
3876 | gtk_window_present_toplevel (window); |
3877 | |
3878 | gtk_widget_map (widget); |
3879 | |
3880 | if (!priv->focus_widget) |
3881 | gtk_window_move_focus (widget, dir: GTK_DIR_TAB_FORWARD); |
3882 | |
3883 | if (priv->modal) |
3884 | gtk_grab_add (widget); |
3885 | } |
3886 | |
3887 | static void |
3888 | gtk_window_hide (GtkWidget *widget) |
3889 | { |
3890 | GtkWindow *window = GTK_WINDOW (widget); |
3891 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3892 | |
3893 | _gtk_widget_set_visible_flag (widget, FALSE); |
3894 | gtk_widget_unmap (widget); |
3895 | |
3896 | if (priv->modal) |
3897 | gtk_grab_remove (widget); |
3898 | } |
3899 | |
3900 | static GdkToplevelLayout * |
3901 | gtk_window_compute_base_layout (GtkWindow *window) |
3902 | { |
3903 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3904 | GdkToplevelLayout *layout; |
3905 | |
3906 | layout = gdk_toplevel_layout_new (); |
3907 | |
3908 | gdk_toplevel_layout_set_resizable (layout, resizable: priv->resizable); |
3909 | |
3910 | return layout; |
3911 | } |
3912 | |
3913 | static void |
3914 | gtk_window_present_toplevel (GtkWindow *window) |
3915 | { |
3916 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3917 | GdkToplevelLayout *layout; |
3918 | |
3919 | layout = gtk_window_compute_base_layout (window); |
3920 | gdk_toplevel_layout_set_maximized (layout, maximized: priv->maximized); |
3921 | gdk_toplevel_layout_set_fullscreen (layout, fullscreen: priv->fullscreen, |
3922 | monitor: priv->initial_fullscreen_monitor); |
3923 | gdk_toplevel_present (toplevel: GDK_TOPLEVEL (ptr: priv->surface), layout); |
3924 | gdk_toplevel_layout_unref (layout); |
3925 | } |
3926 | |
3927 | static void |
3928 | gtk_window_update_toplevel (GtkWindow *window, |
3929 | GdkToplevelLayout *layout) |
3930 | { |
3931 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3932 | |
3933 | if (_gtk_widget_get_mapped (GTK_WIDGET (window))) |
3934 | gdk_toplevel_present (toplevel: GDK_TOPLEVEL (ptr: priv->surface), layout); |
3935 | gdk_toplevel_layout_unref (layout); |
3936 | } |
3937 | |
3938 | static void |
3939 | gtk_window_map (GtkWidget *widget) |
3940 | { |
3941 | GtkWindow *window = GTK_WINDOW (widget); |
3942 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3943 | GtkWidget *child = priv->child; |
3944 | |
3945 | GTK_WIDGET_CLASS (gtk_window_parent_class)->map (widget); |
3946 | |
3947 | if (child != NULL && gtk_widget_get_visible (widget: child)) |
3948 | gtk_widget_map (widget: child); |
3949 | |
3950 | if (priv->title_box != NULL && |
3951 | gtk_widget_get_visible (widget: priv->title_box) && |
3952 | gtk_widget_get_child_visible (widget: priv->title_box)) |
3953 | gtk_widget_map (widget: priv->title_box); |
3954 | |
3955 | gtk_window_present_toplevel (window); |
3956 | |
3957 | if (priv->minimize_initially) |
3958 | gdk_toplevel_minimize (toplevel: GDK_TOPLEVEL (ptr: priv->surface)); |
3959 | |
3960 | gtk_window_set_theme_variant (window); |
3961 | |
3962 | if (!disable_startup_notification) |
3963 | { |
3964 | /* Do we have a custom startup-notification id? */ |
3965 | if (priv->startup_id != NULL) |
3966 | { |
3967 | /* Make sure we have a "real" id */ |
3968 | if (!startup_id_is_fake (startup_id: priv->startup_id)) |
3969 | gdk_display_notify_startup_complete (display: gtk_widget_get_display (widget), startup_id: priv->startup_id); |
3970 | |
3971 | g_free (mem: priv->startup_id); |
3972 | priv->startup_id = NULL; |
3973 | } |
3974 | else |
3975 | gdk_display_notify_startup_complete (display: gtk_widget_get_display (widget), NULL); |
3976 | } |
3977 | |
3978 | /* inherit from transient parent, so that a dialog that is |
3979 | * opened via keynav shows focus initially |
3980 | */ |
3981 | if (priv->transient_parent) |
3982 | gtk_window_set_focus_visible (window, setting: gtk_window_get_focus_visible (window: priv->transient_parent)); |
3983 | else |
3984 | gtk_window_set_focus_visible (window, FALSE); |
3985 | |
3986 | if (priv->application) |
3987 | gtk_application_handle_window_map (application: priv->application, window); |
3988 | |
3989 | gtk_widget_realize_at_context (widget); |
3990 | } |
3991 | |
3992 | static void |
3993 | gtk_window_unmap (GtkWidget *widget) |
3994 | { |
3995 | GtkWindow *window = GTK_WINDOW (widget); |
3996 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
3997 | GtkWidget *child = priv->child; |
3998 | |
3999 | GTK_WIDGET_CLASS (gtk_window_parent_class)->unmap (widget); |
4000 | gdk_surface_hide (surface: priv->surface); |
4001 | |
4002 | gtk_widget_unrealize_at_context (widget); |
4003 | |
4004 | if (priv->title_box != NULL) |
4005 | gtk_widget_unmap (widget: priv->title_box); |
4006 | |
4007 | if (child != NULL) |
4008 | gtk_widget_unmap (widget: child); |
4009 | } |
4010 | |
4011 | static void |
4012 | gtk_window_get_remembered_size (GtkWindow *window, |
4013 | int *width, |
4014 | int *height) |
4015 | { |
4016 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4017 | |
4018 | *width = priv->default_width; |
4019 | *height = priv->default_height; |
4020 | } |
4021 | |
4022 | static void |
4023 | check_scale_changed (GtkWindow *window) |
4024 | { |
4025 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4026 | GtkWidget *widget = GTK_WIDGET (window); |
4027 | int old_scale; |
4028 | |
4029 | old_scale = priv->scale; |
4030 | priv->scale = gtk_widget_get_scale_factor (widget); |
4031 | if (old_scale != priv->scale) |
4032 | _gtk_widget_scale_changed (widget); |
4033 | } |
4034 | |
4035 | static void |
4036 | get_shadow_width (GtkWindow *window, |
4037 | GtkBorder *shadow_width) |
4038 | { |
4039 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4040 | GtkCssStyle *style; |
4041 | |
4042 | if (!priv->decorated) |
4043 | goto out; |
4044 | |
4045 | if (!priv->client_decorated || |
4046 | !priv->use_client_shadow) |
4047 | goto out; |
4048 | |
4049 | if (priv->maximized || |
4050 | priv->fullscreen) |
4051 | goto out; |
4052 | |
4053 | style = gtk_css_node_get_style (cssnode: gtk_widget_get_css_node (GTK_WIDGET (window))); |
4054 | |
4055 | /* Calculate the size of the drop shadows ... */ |
4056 | gtk_css_shadow_value_get_extents (shadow: style->background->box_shadow, border: shadow_width); |
4057 | |
4058 | shadow_width->left = MAX (shadow_width->left, RESIZE_HANDLE_SIZE); |
4059 | shadow_width->top = MAX (shadow_width->top, RESIZE_HANDLE_SIZE); |
4060 | shadow_width->bottom = MAX (shadow_width->bottom, RESIZE_HANDLE_SIZE); |
4061 | shadow_width->right = MAX (shadow_width->right, RESIZE_HANDLE_SIZE); |
4062 | |
4063 | return; |
4064 | |
4065 | out: |
4066 | *shadow_width = (GtkBorder) {0, 0, 0, 0}; |
4067 | } |
4068 | |
4069 | static void |
4070 | update_opaque_region (GtkWindow *window) |
4071 | { |
4072 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4073 | gboolean subtract_decoration_corners; |
4074 | gboolean subtract_shadow; |
4075 | |
4076 | subtract_decoration_corners = (priv->client_decorated && |
4077 | priv->decorated && |
4078 | !priv->fullscreen && |
4079 | !priv->maximized); |
4080 | subtract_shadow = (priv->client_decorated && |
4081 | priv->decorated && |
4082 | priv->use_client_shadow && |
4083 | !priv->maximized && |
4084 | !priv->fullscreen); |
4085 | |
4086 | gtk_native_update_opaque_region (native: GTK_NATIVE (ptr: window), |
4087 | NULL, |
4088 | subtract_decoration_corners, |
4089 | subtract_shadow, |
4090 | RESIZE_HANDLE_SIZE); |
4091 | } |
4092 | |
4093 | static void |
4094 | update_realized_window_properties (GtkWindow *window) |
4095 | { |
4096 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4097 | GdkRectangle rect; |
4098 | GtkCssBoxes css_boxes; |
4099 | const graphene_rect_t *border_rect; |
4100 | double native_x, native_y; |
4101 | |
4102 | update_opaque_region (window); |
4103 | |
4104 | if (!priv->client_decorated || !priv->use_client_shadow) |
4105 | return; |
4106 | |
4107 | gtk_native_get_surface_transform (self: GTK_NATIVE (ptr: window), x: &native_x, y: &native_y); |
4108 | |
4109 | /* update the input shape, which makes it so that clicks |
4110 | * outside the border windows go through. */ |
4111 | gtk_css_boxes_init (boxes: &css_boxes, GTK_WIDGET (window)); |
4112 | border_rect = gtk_css_boxes_get_border_rect (boxes: &css_boxes); |
4113 | |
4114 | /* This logic is duplicated in get_edge_for_coordinates() */ |
4115 | rect.x = native_x + border_rect->origin.x - RESIZE_HANDLE_SIZE; |
4116 | rect.y = native_y + border_rect->origin.y - RESIZE_HANDLE_SIZE; |
4117 | rect.width = border_rect->size.width + 2 * RESIZE_HANDLE_SIZE; |
4118 | rect.height = border_rect->size.height + 2 * RESIZE_HANDLE_SIZE; |
4119 | |
4120 | if (rect.width > 0 && rect.height > 0) |
4121 | { |
4122 | cairo_region_t *region = cairo_region_create_rectangle (rectangle: &rect); |
4123 | |
4124 | gdk_surface_set_input_region (surface: priv->surface, region); |
4125 | cairo_region_destroy (region); |
4126 | } |
4127 | } |
4128 | |
4129 | /* NB: When orientation is VERTICAL, width/height are flipped. |
4130 | * The code uses the terms nonetheless to make it more intuitive |
4131 | * to understand. |
4132 | */ |
4133 | static void |
4134 | gtk_window_compute_min_size (GtkWidget *window, |
4135 | GtkOrientation orientation, |
4136 | double ideal_ratio, |
4137 | int *min_width, |
4138 | int *min_height) |
4139 | { |
4140 | int start, end, mid, other; |
4141 | double ratio; |
4142 | |
4143 | /* start = min width, end = min width for min height (ie max width) */ |
4144 | gtk_widget_measure (widget: window, orientation, for_size: -1, minimum: &start, NULL, NULL, NULL); |
4145 | gtk_widget_measure (widget: window, OPPOSITE_ORIENTATION (orientation), for_size: start, minimum: &other, NULL, NULL, NULL); |
4146 | if ((double) start / other >= ideal_ratio) |
4147 | { |
4148 | *min_width = start; |
4149 | *min_height = other; |
4150 | return; |
4151 | } |
4152 | gtk_widget_measure (widget: window, OPPOSITE_ORIENTATION (orientation), for_size: -1, minimum: &other, NULL, NULL, NULL); |
4153 | gtk_widget_measure (widget: window, orientation, for_size: other, minimum: &end, NULL, NULL, NULL); |
4154 | if ((double) end / other <= ideal_ratio) |
4155 | { |
4156 | *min_width = end; |
4157 | *min_height = other; |
4158 | return; |
4159 | } |
4160 | |
4161 | while (start < end) |
4162 | { |
4163 | mid = (start + end) / 2; |
4164 | |
4165 | gtk_widget_measure (widget: window, OPPOSITE_ORIENTATION (orientation), for_size: mid, minimum: &other, NULL, NULL, NULL); |
4166 | ratio = (double) mid / other; |
4167 | if(ratio == ideal_ratio) |
4168 | { |
4169 | *min_width = mid; |
4170 | *min_height = other; |
4171 | return; |
4172 | } |
4173 | else if (ratio < ideal_ratio) |
4174 | start = mid + 1; |
4175 | else |
4176 | end = mid - 1; |
4177 | } |
4178 | |
4179 | gtk_widget_measure (widget: window, orientation, for_size: other, minimum: &start, NULL, NULL, NULL); |
4180 | *min_width = start; |
4181 | *min_height = other; |
4182 | } |
4183 | |
4184 | static void |
4185 | gtk_window_compute_default_size (GtkWindow *window, |
4186 | int cur_width, |
4187 | int cur_height, |
4188 | int max_width, |
4189 | int max_height, |
4190 | int *min_width, |
4191 | int *min_height, |
4192 | int *width, |
4193 | int *height) |
4194 | { |
4195 | GtkWidget *widget = GTK_WIDGET (window); |
4196 | GtkSizeRequestMode request_mode = gtk_widget_get_request_mode (widget); |
4197 | |
4198 | if (request_mode == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT) |
4199 | { |
4200 | int minimum, natural; |
4201 | |
4202 | gtk_widget_measure (widget, orientation: GTK_ORIENTATION_VERTICAL, for_size: -1, |
4203 | minimum: &minimum, natural: &natural, |
4204 | NULL, NULL); |
4205 | *min_height = minimum; |
4206 | if (cur_height <= 0) |
4207 | cur_height = natural; |
4208 | *height = MAX (minimum, MIN (max_height, cur_height)); |
4209 | |
4210 | gtk_widget_measure (widget, orientation: GTK_ORIENTATION_HORIZONTAL, |
4211 | for_size: *height, |
4212 | minimum: &minimum, natural: &natural, |
4213 | NULL, NULL); |
4214 | *min_width = minimum; |
4215 | if (cur_width <= 0) |
4216 | cur_width = natural; |
4217 | *width = MAX (minimum, MIN (max_width, cur_width)); |
4218 | |
4219 | gtk_window_compute_min_size (window: widget, orientation: GTK_ORIENTATION_VERTICAL, ideal_ratio: (double) *height / *width, min_width: min_height, min_height: min_width); |
4220 | } |
4221 | else /* GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH or CONSTANT_SIZE */ |
4222 | { |
4223 | int minimum, natural; |
4224 | |
4225 | gtk_widget_measure (widget, orientation: GTK_ORIENTATION_HORIZONTAL, for_size: -1, |
4226 | minimum: &minimum, natural: &natural, |
4227 | NULL, NULL); |
4228 | *min_width = minimum; |
4229 | if (cur_width <= 0) |
4230 | cur_width = natural; |
4231 | *width = MAX (minimum, MIN (max_width, cur_width)); |
4232 | |
4233 | gtk_widget_measure (widget, orientation: GTK_ORIENTATION_VERTICAL, |
4234 | for_size: *width, |
4235 | minimum: &minimum, natural: &natural, |
4236 | NULL, NULL); |
4237 | *min_height = minimum; |
4238 | if (cur_height <= 0) |
4239 | cur_height = natural; |
4240 | |
4241 | *height = MAX (minimum, MIN (max_height, cur_height)); |
4242 | |
4243 | if (request_mode != GTK_SIZE_REQUEST_CONSTANT_SIZE) |
4244 | gtk_window_compute_min_size (window: widget, orientation: GTK_ORIENTATION_HORIZONTAL, ideal_ratio: (double) *width / *height, min_width, min_height); |
4245 | } |
4246 | } |
4247 | |
4248 | static gboolean |
4249 | should_remember_size (GtkWindow *window) |
4250 | { |
4251 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4252 | |
4253 | if (!priv->resizable) |
4254 | return FALSE; |
4255 | |
4256 | return !(priv->state & (GDK_TOPLEVEL_STATE_FULLSCREEN | |
4257 | GDK_TOPLEVEL_STATE_MAXIMIZED | |
4258 | GDK_TOPLEVEL_STATE_TILED | |
4259 | GDK_TOPLEVEL_STATE_TOP_TILED | |
4260 | GDK_TOPLEVEL_STATE_RIGHT_TILED | |
4261 | GDK_TOPLEVEL_STATE_BOTTOM_TILED | |
4262 | GDK_TOPLEVEL_STATE_LEFT_TILED | |
4263 | GDK_TOPLEVEL_STATE_MINIMIZED)); |
4264 | } |
4265 | |
4266 | static void |
4267 | toplevel_compute_size (GdkToplevel *toplevel, |
4268 | GdkToplevelSize *size, |
4269 | GtkWidget *widget) |
4270 | { |
4271 | GtkWindow *window = GTK_WINDOW (widget); |
4272 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4273 | int width, height; |
4274 | GtkBorder shadow; |
4275 | int bounds_width, bounds_height; |
4276 | int min_width, min_height; |
4277 | |
4278 | gdk_toplevel_size_get_bounds (size, bounds_width: &bounds_width, bounds_height: &bounds_height); |
4279 | |
4280 | gtk_window_compute_default_size (window, |
4281 | cur_width: priv->default_width, cur_height: priv->default_height, |
4282 | max_width: bounds_width, max_height: bounds_height, |
4283 | min_width: &min_width, min_height: &min_height, |
4284 | width: &width, height: &height); |
4285 | |
4286 | if (width < min_width) |
4287 | width = min_width; |
4288 | if (height < min_height) |
4289 | height = min_height; |
4290 | |
4291 | if (should_remember_size (window)) |
4292 | gtk_window_set_default_size_internal (window, TRUE, width, TRUE, height); |
4293 | |
4294 | gtk_window_update_csd_size (window, |
4295 | width: &width, height: &height, |
4296 | INCLUDE_CSD_SIZE); |
4297 | gtk_window_update_csd_size (window, |
4298 | width: &min_width, height: &min_height, |
4299 | INCLUDE_CSD_SIZE); |
4300 | |
4301 | gdk_toplevel_size_set_min_size (size, min_width, min_height); |
4302 | |
4303 | gdk_toplevel_size_set_size (size, width, height); |
4304 | |
4305 | if (priv->use_client_shadow) |
4306 | { |
4307 | get_shadow_width (window, shadow_width: &shadow); |
4308 | gdk_toplevel_size_set_shadow_width (size, |
4309 | left: shadow.left, right: shadow.right, |
4310 | top: shadow.top, bottom: shadow.bottom); |
4311 | } |
4312 | |
4313 | gtk_widget_ensure_resize (widget); |
4314 | } |
4315 | |
4316 | static void |
4317 | gtk_window_realize (GtkWidget *widget) |
4318 | { |
4319 | GtkWindow *window = GTK_WINDOW (widget); |
4320 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4321 | GdkSurface *surface; |
4322 | GdkFrameClock *frame_clock; |
4323 | |
4324 | /* Create default title bar */ |
4325 | if (!priv->client_decorated && gtk_window_should_use_csd (window)) |
4326 | { |
4327 | priv->use_client_shadow = gtk_window_supports_client_shadow (window); |
4328 | if (priv->use_client_shadow) |
4329 | { |
4330 | gtk_window_enable_csd (window); |
4331 | |
4332 | if (priv->title_box == NULL) |
4333 | { |
4334 | priv->title_box = gtk_header_bar_new (); |
4335 | gtk_widget_add_css_class (widget: priv->title_box, css_class: "titlebar" ); |
4336 | gtk_widget_add_css_class (widget: priv->title_box, css_class: "default-decoration" ); |
4337 | |
4338 | gtk_widget_insert_before (widget: priv->title_box, parent: widget, NULL); |
4339 | } |
4340 | |
4341 | update_window_actions (window); |
4342 | } |
4343 | } |
4344 | |
4345 | surface = gdk_surface_new_toplevel (display: gtk_widget_get_display (widget)); |
4346 | priv->surface = surface; |
4347 | gdk_surface_set_widget (self: surface, widget); |
4348 | |
4349 | if (priv->renderer == NULL) |
4350 | priv->renderer = gsk_renderer_new_for_surface (surface); |
4351 | |
4352 | g_signal_connect_swapped (surface, "notify::state" , G_CALLBACK (surface_state_changed), widget); |
4353 | g_signal_connect_swapped (surface, "notify::mapped" , G_CALLBACK (surface_state_changed), widget); |
4354 | g_signal_connect (surface, "render" , G_CALLBACK (surface_render), widget); |
4355 | g_signal_connect (surface, "event" , G_CALLBACK (surface_event), widget); |
4356 | g_signal_connect (surface, "compute-size" , G_CALLBACK (toplevel_compute_size), widget); |
4357 | |
4358 | frame_clock = gdk_surface_get_frame_clock (surface); |
4359 | g_signal_connect (frame_clock, "after-paint" , G_CALLBACK (after_paint), widget); |
4360 | |
4361 | GTK_WIDGET_CLASS (gtk_window_parent_class)->realize (widget); |
4362 | |
4363 | gtk_root_start_layout (self: GTK_ROOT (ptr: window)); |
4364 | |
4365 | if (priv->transient_parent && |
4366 | _gtk_widget_get_realized (GTK_WIDGET (priv->transient_parent))) |
4367 | { |
4368 | GtkWindowPrivate *parent_priv = gtk_window_get_instance_private (self: priv->transient_parent); |
4369 | gdk_toplevel_set_transient_for (toplevel: GDK_TOPLEVEL (ptr: surface), parent: parent_priv->surface); |
4370 | } |
4371 | |
4372 | if (priv->title) |
4373 | gdk_toplevel_set_title (toplevel: GDK_TOPLEVEL (ptr: surface), title: priv->title); |
4374 | |
4375 | gdk_toplevel_set_decorated (toplevel: GDK_TOPLEVEL (ptr: surface), decorated: priv->decorated && !priv->client_decorated); |
4376 | gdk_toplevel_set_deletable (toplevel: GDK_TOPLEVEL (ptr: surface), deletable: priv->deletable); |
4377 | |
4378 | #ifdef GDK_WINDOWING_WAYLAND |
4379 | if (GDK_IS_WAYLAND_SURFACE (surface)) |
4380 | { |
4381 | if (priv->client_decorated) |
4382 | gdk_wayland_toplevel_announce_csd (toplevel: GDK_TOPLEVEL (ptr: surface)); |
4383 | else |
4384 | gdk_wayland_toplevel_announce_ssd (toplevel: GDK_TOPLEVEL (ptr: surface)); |
4385 | } |
4386 | #endif |
4387 | |
4388 | gdk_toplevel_set_modal (toplevel: GDK_TOPLEVEL (ptr: surface), modal: priv->modal); |
4389 | |
4390 | if (priv->startup_id) |
4391 | { |
4392 | #ifdef GDK_WINDOWING_X11 |
4393 | if (GDK_IS_X11_SURFACE (surface)) |
4394 | { |
4395 | guint32 timestamp = extract_time_from_startup_id (startup_id: priv->startup_id); |
4396 | if (timestamp != GDK_CURRENT_TIME) |
4397 | gdk_x11_surface_set_user_time (surface, timestamp); |
4398 | } |
4399 | #endif |
4400 | if (!startup_id_is_fake (startup_id: priv->startup_id)) |
4401 | gdk_toplevel_set_startup_id (toplevel: GDK_TOPLEVEL (ptr: surface), startup_id: priv->startup_id); |
4402 | } |
4403 | |
4404 | #ifdef GDK_WINDOWING_X11 |
4405 | if (priv->initial_timestamp != GDK_CURRENT_TIME) |
4406 | { |
4407 | if (GDK_IS_X11_SURFACE (surface)) |
4408 | gdk_x11_surface_set_user_time (surface, timestamp: priv->initial_timestamp); |
4409 | } |
4410 | #endif |
4411 | |
4412 | update_realized_window_properties (window); |
4413 | |
4414 | if (priv->application) |
4415 | gtk_application_handle_window_realize (application: priv->application, window); |
4416 | |
4417 | /* Icons */ |
4418 | gtk_window_realize_icon (window); |
4419 | |
4420 | check_scale_changed (window); |
4421 | |
4422 | gtk_native_realize (self: GTK_NATIVE (ptr: window)); |
4423 | } |
4424 | |
4425 | static void |
4426 | gtk_window_unrealize (GtkWidget *widget) |
4427 | { |
4428 | GtkWindow *window = GTK_WINDOW (widget); |
4429 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4430 | GtkWindowGeometryInfo *info; |
4431 | GdkSurface *surface; |
4432 | GdkFrameClock *frame_clock; |
4433 | |
4434 | gtk_native_unrealize (self: GTK_NATIVE (ptr: window)); |
4435 | |
4436 | /* On unrealize, we reset the size of the window such |
4437 | * that we will re-apply the default sizing stuff |
4438 | * next time we show the window. |
4439 | * |
4440 | * Default positioning is reset on unmap, instead of unrealize. |
4441 | */ |
4442 | priv->need_default_size = TRUE; |
4443 | info = gtk_window_get_geometry_info (window, FALSE); |
4444 | if (info) |
4445 | { |
4446 | info->last.configure_request.x = 0; |
4447 | info->last.configure_request.y = 0; |
4448 | info->last.configure_request.width = -1; |
4449 | info->last.configure_request.height = -1; |
4450 | /* be sure we reset geom hints on re-realize */ |
4451 | info->last.flags = 0; |
4452 | } |
4453 | |
4454 | gsk_renderer_unrealize (renderer: priv->renderer); |
4455 | |
4456 | /* Icons */ |
4457 | gtk_window_unrealize_icon (window); |
4458 | |
4459 | if (priv->title_box) |
4460 | gtk_widget_unrealize (widget: priv->title_box); |
4461 | |
4462 | if (priv->child) |
4463 | gtk_widget_unrealize (widget: priv->child); |
4464 | |
4465 | g_clear_object (&priv->renderer); |
4466 | |
4467 | surface = priv->surface; |
4468 | |
4469 | g_signal_handlers_disconnect_by_func (surface, surface_state_changed, widget); |
4470 | g_signal_handlers_disconnect_by_func (surface, surface_render, widget); |
4471 | g_signal_handlers_disconnect_by_func (surface, surface_event, widget); |
4472 | |
4473 | frame_clock = gdk_surface_get_frame_clock (surface); |
4474 | |
4475 | g_signal_handlers_disconnect_by_func (frame_clock, after_paint, widget); |
4476 | |
4477 | gtk_root_stop_layout (self: GTK_ROOT (ptr: window)); |
4478 | |
4479 | GTK_WIDGET_CLASS (gtk_window_parent_class)->unrealize (widget); |
4480 | |
4481 | gdk_surface_set_widget (self: surface, NULL); |
4482 | gdk_surface_destroy (surface); |
4483 | g_clear_object (&priv->surface); |
4484 | } |
4485 | |
4486 | static void |
4487 | update_window_style_classes (GtkWindow *window) |
4488 | { |
4489 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4490 | GtkWidget *widget = GTK_WIDGET (window); |
4491 | guint edge_constraints; |
4492 | |
4493 | edge_constraints = priv->edge_constraints; |
4494 | |
4495 | if (!priv->edge_constraints) |
4496 | { |
4497 | if (priv->tiled) |
4498 | gtk_widget_add_css_class (widget, css_class: "tiled" ); |
4499 | else |
4500 | gtk_widget_remove_css_class (widget, css_class: "tiled" ); |
4501 | } |
4502 | else |
4503 | { |
4504 | if (edge_constraints & GDK_TOPLEVEL_STATE_TOP_TILED) |
4505 | gtk_widget_add_css_class (widget, css_class: "tiled-top" ); |
4506 | else |
4507 | gtk_widget_remove_css_class (widget, css_class: "tiled-top" ); |
4508 | |
4509 | if (edge_constraints & GDK_TOPLEVEL_STATE_RIGHT_TILED) |
4510 | gtk_widget_add_css_class (widget, css_class: "tiled-right" ); |
4511 | else |
4512 | gtk_widget_remove_css_class (widget, css_class: "tiled-right" ); |
4513 | |
4514 | if (edge_constraints & GDK_TOPLEVEL_STATE_BOTTOM_TILED) |
4515 | gtk_widget_add_css_class (widget, css_class: "tiled-bottom" ); |
4516 | else |
4517 | gtk_widget_remove_css_class (widget, css_class: "tiled-bottom" ); |
4518 | |
4519 | if (edge_constraints & GDK_TOPLEVEL_STATE_LEFT_TILED) |
4520 | gtk_widget_add_css_class (widget, css_class: "tiled-left" ); |
4521 | else |
4522 | gtk_widget_remove_css_class (widget, css_class: "tiled-left" ); |
4523 | } |
4524 | |
4525 | if (priv->maximized) |
4526 | gtk_widget_add_css_class (widget, css_class: "maximized" ); |
4527 | else |
4528 | gtk_widget_remove_css_class (widget, css_class: "maximized" ); |
4529 | |
4530 | if (priv->fullscreen) |
4531 | gtk_widget_add_css_class (widget, css_class: "fullscreen" ); |
4532 | else |
4533 | gtk_widget_remove_css_class (widget, css_class: "fullscreen" ); |
4534 | } |
4535 | |
4536 | /* _gtk_window_set_allocation: |
4537 | * @window: a `GtkWindow` |
4538 | * @allocation: the original allocation for the window |
4539 | * @allocation_out: @allocation taking decorations into |
4540 | * consideration |
4541 | * |
4542 | * This function is like gtk_widget_set_allocation() |
4543 | * but does the necessary extra work to update |
4544 | * the resize grip positioning, etc. |
4545 | * |
4546 | * Call this instead of gtk_widget_set_allocation() |
4547 | * when overriding ::size_allocate in a GtkWindow |
4548 | * subclass without chaining up. |
4549 | * |
4550 | * The @allocation parameter will be adjusted to |
4551 | * reflect any internal decorations that the window |
4552 | * may have. That revised allocation will then be |
4553 | * returned in the @allocation_out parameter. |
4554 | */ |
4555 | void |
4556 | _gtk_window_set_allocation (GtkWindow *window, |
4557 | int width, |
4558 | int height, |
4559 | GtkAllocation *allocation_out) |
4560 | { |
4561 | GtkWidget *widget = (GtkWidget *)window; |
4562 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4563 | GtkAllocation child_allocation; |
4564 | |
4565 | g_assert (allocation_out != NULL); |
4566 | |
4567 | child_allocation.x = 0; |
4568 | child_allocation.y = 0; |
4569 | child_allocation.width = width; |
4570 | child_allocation.height = height; |
4571 | |
4572 | if (_gtk_widget_get_realized (widget)) |
4573 | { |
4574 | update_realized_window_properties (window); |
4575 | } |
4576 | |
4577 | priv->title_height = 0; |
4578 | |
4579 | if (priv->title_box != NULL && |
4580 | gtk_widget_get_visible (widget: priv->title_box) && |
4581 | gtk_widget_get_child_visible (widget: priv->title_box) && |
4582 | priv->decorated && |
4583 | !priv->fullscreen) |
4584 | { |
4585 | GtkAllocation title_allocation; |
4586 | |
4587 | title_allocation.x = 0; |
4588 | title_allocation.y = 0; |
4589 | title_allocation.width = width; |
4590 | |
4591 | gtk_widget_measure (widget: priv->title_box, orientation: GTK_ORIENTATION_VERTICAL, |
4592 | for_size: title_allocation.width, |
4593 | NULL, natural: &priv->title_height, |
4594 | NULL, NULL); |
4595 | |
4596 | title_allocation.height = priv->title_height; |
4597 | |
4598 | gtk_widget_size_allocate (widget: priv->title_box, allocation: &title_allocation, baseline: -1); |
4599 | } |
4600 | |
4601 | if (priv->decorated && |
4602 | !priv->fullscreen) |
4603 | { |
4604 | child_allocation.y += priv->title_height; |
4605 | child_allocation.height -= priv->title_height; |
4606 | } |
4607 | |
4608 | *allocation_out = child_allocation; |
4609 | } |
4610 | |
4611 | static void |
4612 | gtk_window_size_allocate (GtkWidget *widget, |
4613 | int width, |
4614 | int height, |
4615 | int baseline) |
4616 | { |
4617 | GtkWindow *window = GTK_WINDOW (widget); |
4618 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4619 | GtkWidget *child = priv->child; |
4620 | GtkAllocation child_allocation; |
4621 | |
4622 | _gtk_window_set_allocation (window, width, height, allocation_out: &child_allocation); |
4623 | |
4624 | if (child && gtk_widget_get_visible (widget: child)) |
4625 | gtk_widget_size_allocate (widget: child, allocation: &child_allocation, baseline: -1); |
4626 | |
4627 | gtk_tooltip_maybe_allocate (native: GTK_NATIVE (ptr: widget)); |
4628 | } |
4629 | |
4630 | static void |
4631 | update_edge_constraints (GtkWindow *window, |
4632 | GdkToplevelState state) |
4633 | { |
4634 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4635 | |
4636 | priv->edge_constraints = (state & GDK_TOPLEVEL_STATE_TOP_TILED) | |
4637 | (state & GDK_TOPLEVEL_STATE_TOP_RESIZABLE) | |
4638 | (state & GDK_TOPLEVEL_STATE_RIGHT_TILED) | |
4639 | (state & GDK_TOPLEVEL_STATE_RIGHT_RESIZABLE) | |
4640 | (state & GDK_TOPLEVEL_STATE_BOTTOM_TILED) | |
4641 | (state & GDK_TOPLEVEL_STATE_BOTTOM_RESIZABLE) | |
4642 | (state & GDK_TOPLEVEL_STATE_LEFT_TILED) | |
4643 | (state & GDK_TOPLEVEL_STATE_LEFT_RESIZABLE); |
4644 | |
4645 | priv->tiled = (state & GDK_TOPLEVEL_STATE_TILED) ? 1 : 0; |
4646 | } |
4647 | |
4648 | static void |
4649 | surface_state_changed (GtkWidget *widget) |
4650 | { |
4651 | GtkWindow *window = GTK_WINDOW (widget); |
4652 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4653 | GdkToplevelState new_surface_state; |
4654 | GdkToplevelState changed_mask; |
4655 | |
4656 | new_surface_state = gdk_toplevel_get_state (toplevel: GDK_TOPLEVEL (ptr: priv->surface)); |
4657 | changed_mask = new_surface_state ^ priv->state; |
4658 | priv->state = new_surface_state; |
4659 | |
4660 | if (changed_mask & GDK_TOPLEVEL_STATE_FOCUSED) |
4661 | { |
4662 | gboolean focused = new_surface_state & GDK_TOPLEVEL_STATE_FOCUSED; |
4663 | |
4664 | ensure_state_flag_backdrop (widget); |
4665 | |
4666 | if (!focused) |
4667 | gtk_window_set_mnemonics_visible (window, FALSE); |
4668 | } |
4669 | |
4670 | if (changed_mask & GDK_TOPLEVEL_STATE_FULLSCREEN) |
4671 | { |
4672 | priv->fullscreen = (new_surface_state & GDK_TOPLEVEL_STATE_FULLSCREEN) ? TRUE : FALSE; |
4673 | |
4674 | g_object_notify_by_pspec (G_OBJECT (widget), pspec: window_props[PROP_FULLSCREENED]); |
4675 | } |
4676 | |
4677 | if (changed_mask & GDK_TOPLEVEL_STATE_MAXIMIZED) |
4678 | { |
4679 | priv->maximized = (new_surface_state & GDK_TOPLEVEL_STATE_MAXIMIZED) ? TRUE : FALSE; |
4680 | |
4681 | g_object_notify_by_pspec (G_OBJECT (widget), pspec: window_props[PROP_MAXIMIZED]); |
4682 | } |
4683 | |
4684 | update_edge_constraints (window, state: new_surface_state); |
4685 | |
4686 | if (changed_mask & (GDK_TOPLEVEL_STATE_FULLSCREEN | |
4687 | GDK_TOPLEVEL_STATE_MAXIMIZED | |
4688 | GDK_TOPLEVEL_STATE_TILED | |
4689 | GDK_TOPLEVEL_STATE_TOP_TILED | |
4690 | GDK_TOPLEVEL_STATE_RIGHT_TILED | |
4691 | GDK_TOPLEVEL_STATE_BOTTOM_TILED | |
4692 | GDK_TOPLEVEL_STATE_LEFT_TILED | |
4693 | GDK_TOPLEVEL_STATE_MINIMIZED)) |
4694 | { |
4695 | update_window_style_classes (window); |
4696 | update_window_actions (window); |
4697 | gtk_widget_queue_resize (widget); |
4698 | } |
4699 | } |
4700 | |
4701 | static void |
4702 | surface_size_changed (GtkWidget *widget, |
4703 | int width, |
4704 | int height) |
4705 | { |
4706 | GtkWindow *window = GTK_WINDOW (widget); |
4707 | |
4708 | check_scale_changed (GTK_WINDOW (widget)); |
4709 | |
4710 | if (should_remember_size (window)) |
4711 | { |
4712 | int width_to_remember; |
4713 | int height_to_remember; |
4714 | |
4715 | width_to_remember = width; |
4716 | height_to_remember = height; |
4717 | gtk_window_update_csd_size (window, |
4718 | width: &width_to_remember, height: &height_to_remember, |
4719 | EXCLUDE_CSD_SIZE); |
4720 | gtk_window_set_default_size_internal (window, |
4721 | TRUE, width: width_to_remember, |
4722 | TRUE, height: height_to_remember); |
4723 | } |
4724 | |
4725 | gtk_widget_queue_allocate (widget); |
4726 | } |
4727 | |
4728 | static void |
4729 | maybe_unset_focus_and_default (GtkWindow *window) |
4730 | { |
4731 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4732 | |
4733 | if (priv->move_focus) |
4734 | { |
4735 | GtkWidget *parent; |
4736 | |
4737 | parent = _gtk_widget_get_parent (widget: priv->move_focus_widget); |
4738 | |
4739 | while (parent) |
4740 | { |
4741 | if (_gtk_widget_get_visible (widget: parent)) |
4742 | { |
4743 | if (gtk_widget_grab_focus (widget: parent)) |
4744 | break; |
4745 | } |
4746 | |
4747 | parent = _gtk_widget_get_parent (widget: parent); |
4748 | } |
4749 | |
4750 | if (!parent) |
4751 | gtk_widget_child_focus (GTK_WIDGET (window), direction: GTK_DIR_TAB_FORWARD); |
4752 | |
4753 | priv->move_focus = FALSE; |
4754 | g_clear_object (&priv->move_focus_widget); |
4755 | } |
4756 | |
4757 | if (priv->unset_default) |
4758 | gtk_window_set_default_widget (window, NULL); |
4759 | } |
4760 | |
4761 | static gboolean |
4762 | surface_render (GdkSurface *surface, |
4763 | cairo_region_t *region, |
4764 | GtkWidget *widget) |
4765 | { |
4766 | gtk_widget_render (widget, surface, region); |
4767 | |
4768 | return TRUE; |
4769 | } |
4770 | |
4771 | static void |
4772 | after_paint (GdkFrameClock *clock, |
4773 | GtkWindow *window) |
4774 | { |
4775 | maybe_unset_focus_and_default (window); |
4776 | } |
4777 | |
4778 | static gboolean |
4779 | surface_event (GdkSurface *surface, |
4780 | GdkEvent *event, |
4781 | GtkWidget *widget) |
4782 | { |
4783 | gtk_main_do_event (event); |
4784 | return TRUE; |
4785 | } |
4786 | |
4787 | static void |
4788 | gtk_window_real_activate_focus (GtkWindow *window) |
4789 | { |
4790 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4791 | |
4792 | if (priv->focus_widget && gtk_widget_is_sensitive (widget: priv->focus_widget)) |
4793 | gtk_widget_activate (widget: priv->focus_widget); |
4794 | } |
4795 | |
4796 | static gboolean |
4797 | gtk_window_has_mnemonic_modifier_pressed (GtkWindow *window) |
4798 | { |
4799 | GList *seats, *s; |
4800 | gboolean retval = FALSE; |
4801 | |
4802 | seats = gdk_display_list_seats (display: gtk_widget_get_display (GTK_WIDGET (window))); |
4803 | |
4804 | for (s = seats; s; s = s->next) |
4805 | { |
4806 | GdkDevice *dev = gdk_seat_get_keyboard (seat: s->data); |
4807 | GdkModifierType mask; |
4808 | |
4809 | mask = gdk_device_get_modifier_state (device: dev); |
4810 | if ((mask & gtk_accelerator_get_default_mod_mask ()) == GDK_ALT_MASK) |
4811 | { |
4812 | retval = TRUE; |
4813 | break; |
4814 | } |
4815 | } |
4816 | |
4817 | g_list_free (list: seats); |
4818 | |
4819 | return retval; |
4820 | } |
4821 | |
4822 | static gboolean |
4823 | gtk_window_handle_focus (GtkWidget *widget, |
4824 | GdkEvent *event, |
4825 | double x, |
4826 | double y) |
4827 | { |
4828 | GtkWindow *window = GTK_WINDOW (widget); |
4829 | |
4830 | if (gdk_event_get_event_type (event) != GDK_FOCUS_CHANGE) |
4831 | return FALSE; |
4832 | |
4833 | if (gdk_focus_event_get_in (event)) |
4834 | { |
4835 | _gtk_window_set_is_active (window, TRUE); |
4836 | |
4837 | if (gtk_window_has_mnemonic_modifier_pressed (window)) |
4838 | _gtk_window_schedule_mnemonics_visible (window); |
4839 | } |
4840 | else |
4841 | { |
4842 | _gtk_window_set_is_active (window, FALSE); |
4843 | |
4844 | gtk_window_set_mnemonics_visible (window, FALSE); |
4845 | } |
4846 | |
4847 | return TRUE; |
4848 | } |
4849 | |
4850 | static void |
4851 | update_mnemonics_visible (GtkWindow *window, |
4852 | guint keyval, |
4853 | GdkModifierType state, |
4854 | gboolean visible) |
4855 | { |
4856 | if ((keyval == GDK_KEY_Alt_L || keyval == GDK_KEY_Alt_R) && |
4857 | ((state & (gtk_accelerator_get_default_mod_mask ()) & ~(GDK_ALT_MASK)) == 0)) |
4858 | { |
4859 | if (visible) |
4860 | _gtk_window_schedule_mnemonics_visible (window); |
4861 | else |
4862 | gtk_window_set_mnemonics_visible (window, FALSE); |
4863 | } |
4864 | } |
4865 | |
4866 | void |
4867 | _gtk_window_update_focus_visible (GtkWindow *window, |
4868 | guint keyval, |
4869 | GdkModifierType state, |
4870 | gboolean visible) |
4871 | { |
4872 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4873 | |
4874 | if (visible) |
4875 | { |
4876 | if (priv->focus_visible) |
4877 | priv->key_press_focus = NULL; |
4878 | else |
4879 | priv->key_press_focus = priv->focus_widget; |
4880 | |
4881 | if ((keyval == GDK_KEY_Alt_L || keyval == GDK_KEY_Alt_R) && |
4882 | ((state & (gtk_accelerator_get_default_mod_mask ()) & ~(GDK_ALT_MASK)) == 0)) |
4883 | gtk_window_set_focus_visible (window, TRUE); |
4884 | } |
4885 | else |
4886 | { |
4887 | if (priv->key_press_focus == priv->focus_widget) |
4888 | gtk_window_set_focus_visible (window, FALSE); |
4889 | else |
4890 | gtk_window_set_focus_visible (window, TRUE); |
4891 | |
4892 | priv->key_press_focus = NULL; |
4893 | } |
4894 | } |
4895 | |
4896 | static gboolean |
4897 | gtk_window_key_pressed (GtkWidget *widget, |
4898 | guint keyval, |
4899 | guint keycode, |
4900 | GdkModifierType state, |
4901 | gpointer data) |
4902 | { |
4903 | GtkWindow *window = GTK_WINDOW (widget); |
4904 | |
4905 | _gtk_window_update_focus_visible (window, keyval, state, TRUE); |
4906 | update_mnemonics_visible (window, keyval, state, TRUE); |
4907 | |
4908 | return FALSE; |
4909 | } |
4910 | |
4911 | static gboolean |
4912 | gtk_window_key_released (GtkWidget *widget, |
4913 | guint keyval, |
4914 | guint keycode, |
4915 | GdkModifierType state, |
4916 | gpointer data) |
4917 | { |
4918 | GtkWindow *window = GTK_WINDOW (widget); |
4919 | |
4920 | _gtk_window_update_focus_visible (window, keyval, state, FALSE); |
4921 | update_mnemonics_visible (window, keyval, state, FALSE); |
4922 | |
4923 | return FALSE; |
4924 | } |
4925 | |
4926 | static gboolean |
4927 | gtk_window_focus (GtkWidget *widget, |
4928 | GtkDirectionType direction) |
4929 | { |
4930 | GtkWindow *window = GTK_WINDOW (widget); |
4931 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
4932 | GtkWidget *child; |
4933 | GtkWidget *old_focus_child; |
4934 | GtkWidget *parent; |
4935 | |
4936 | old_focus_child = gtk_widget_get_focus_child (widget); |
4937 | |
4938 | /* We need a special implementation here to deal properly with wrapping |
4939 | * around in the tab chain without the danger of going into an |
4940 | * infinite loop. |
4941 | */ |
4942 | if (old_focus_child) |
4943 | { |
4944 | if (gtk_widget_child_focus (widget: old_focus_child, direction)) |
4945 | return TRUE; |
4946 | } |
4947 | |
4948 | if (priv->focus_widget) |
4949 | { |
4950 | if (direction == GTK_DIR_LEFT || |
4951 | direction == GTK_DIR_RIGHT || |
4952 | direction == GTK_DIR_UP || |
4953 | direction == GTK_DIR_DOWN) |
4954 | { |
4955 | return FALSE; |
4956 | } |
4957 | |
4958 | /* Wrapped off the end, clear the focus setting for the toplpevel */ |
4959 | parent = _gtk_widget_get_parent (widget: priv->focus_widget); |
4960 | while (parent) |
4961 | { |
4962 | gtk_widget_set_focus_child (widget: parent, NULL); |
4963 | parent = _gtk_widget_get_parent (widget: parent); |
4964 | } |
4965 | |
4966 | gtk_window_set_focus (window, NULL); |
4967 | } |
4968 | |
4969 | /* Now try to focus the first widget in the window, |
4970 | * taking care to hook titlebar widgets into the |
4971 | * focus chain. |
4972 | */ |
4973 | if (priv->title_box != NULL && |
4974 | old_focus_child != NULL && |
4975 | priv->title_box != old_focus_child) |
4976 | child = priv->title_box; |
4977 | else |
4978 | child = priv->child; |
4979 | |
4980 | if (child) |
4981 | { |
4982 | if (gtk_widget_child_focus (widget: child, direction)) |
4983 | return TRUE; |
4984 | else if (priv->title_box != NULL && |
4985 | priv->title_box != child && |
4986 | gtk_widget_child_focus (widget: priv->title_box, direction)) |
4987 | return TRUE; |
4988 | else if (priv->title_box == child && |
4989 | gtk_widget_child_focus (widget: priv->child, direction)) |
4990 | return TRUE; |
4991 | } |
4992 | |
4993 | return FALSE; |
4994 | } |
4995 | |
4996 | static void |
4997 | gtk_window_move_focus (GtkWidget *widget, |
4998 | GtkDirectionType dir) |
4999 | { |
5000 | gtk_widget_child_focus (widget, direction: dir); |
5001 | |
5002 | if (!gtk_widget_get_focus_child (widget)) |
5003 | gtk_window_set_focus (GTK_WINDOW (widget), NULL); |
5004 | } |
5005 | |
5006 | void |
5007 | check_crossing_invariants (GtkWidget *widget, |
5008 | GtkCrossingData *crossing) |
5009 | { |
5010 | #ifdef G_ENBABLE_DEBUG |
5011 | if (crossing->old_target == NULL) |
5012 | g_assert (crossing->old_descendent == NULL); |
5013 | else if (crossing->old_descendent == NULL) |
5014 | g_assert (crossing->old_target == widget || !gtk_widget_is_ancestor (crossing->old_target, widget)); |
5015 | else |
5016 | { |
5017 | g_assert (gtk_widget_get_parent (crossing->old_descendent) == widget); |
5018 | g_assert (gtk_widget_is_ancestor (crossing->old_descendent, widget)); |
5019 | g_assert (crossing->old_target == crossing->old_descendent || gtk_widget_is_ancestor (crossing->old_target, crossing->old_descendent)); |
5020 | } |
5021 | if (crossing->new_target == NULL) |
5022 | g_assert (crossing->new_descendent == NULL); |
5023 | else if (crossing->new_descendent == NULL) |
5024 | g_assert (crossing->new_target == widget || !gtk_widget_is_ancestor (crossing->new_target, widget)); |
5025 | else |
5026 | { |
5027 | g_assert (gtk_widget_get_parent (crossing->new_descendent) == widget); |
5028 | g_assert (gtk_widget_is_ancestor (crossing->new_descendent, widget)); |
5029 | g_assert (crossing->new_target == crossing->new_descendent || gtk_widget_is_ancestor (crossing->new_target, crossing->new_descendent)); |
5030 | } |
5031 | #endif |
5032 | } |
5033 | |
5034 | static void |
5035 | synthesize_focus_change_events (GtkWindow *window, |
5036 | GtkWidget *old_focus, |
5037 | GtkWidget *new_focus, |
5038 | GtkCrossingType type) |
5039 | { |
5040 | GtkCrossingData crossing; |
5041 | GtkWidget *ancestor; |
5042 | GtkWidget *widget, *focus_child; |
5043 | GList *list, *l; |
5044 | GtkStateFlags flags; |
5045 | GtkWidget *prev; |
5046 | gboolean seen_ancestor; |
5047 | |
5048 | if (old_focus == new_focus) |
5049 | return; |
5050 | |
5051 | if (old_focus && new_focus) |
5052 | ancestor = gtk_widget_common_ancestor (widget_a: old_focus, widget_b: new_focus); |
5053 | else |
5054 | ancestor = NULL; |
5055 | |
5056 | flags = GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_FOCUS_WITHIN; |
5057 | if (gtk_window_get_focus_visible (GTK_WINDOW (window))) |
5058 | flags |= GTK_STATE_FLAG_FOCUS_VISIBLE; |
5059 | |
5060 | crossing.type = type; |
5061 | crossing.mode = GDK_CROSSING_NORMAL; |
5062 | crossing.old_target = old_focus; |
5063 | crossing.old_descendent = NULL; |
5064 | crossing.new_target = new_focus; |
5065 | crossing.new_descendent = NULL; |
5066 | |
5067 | crossing.direction = GTK_CROSSING_OUT; |
5068 | |
5069 | prev = NULL; |
5070 | seen_ancestor = FALSE; |
5071 | widget = old_focus; |
5072 | while (widget) |
5073 | { |
5074 | crossing.old_descendent = prev; |
5075 | if (seen_ancestor) |
5076 | { |
5077 | crossing.new_descendent = new_focus ? prev : NULL; |
5078 | } |
5079 | else if (widget == ancestor) |
5080 | { |
5081 | GtkWidget *w; |
5082 | |
5083 | crossing.new_descendent = NULL; |
5084 | for (w = new_focus; w != ancestor; w = gtk_widget_get_parent (widget: w)) |
5085 | crossing.new_descendent = w; |
5086 | |
5087 | seen_ancestor = TRUE; |
5088 | } |
5089 | else |
5090 | { |
5091 | crossing.new_descendent = NULL; |
5092 | } |
5093 | |
5094 | check_crossing_invariants (widget, crossing: &crossing); |
5095 | gtk_widget_handle_crossing (widget, crossing: &crossing, x: 0, y: 0); |
5096 | gtk_widget_unset_state_flags (widget, flags); |
5097 | gtk_widget_set_focus_child (widget, NULL); |
5098 | prev = widget; |
5099 | widget = gtk_widget_get_parent (widget); |
5100 | |
5101 | flags = flags & ~GTK_STATE_FLAG_FOCUSED; |
5102 | } |
5103 | |
5104 | flags = GTK_STATE_FLAG_FOCUS_WITHIN; |
5105 | if (gtk_window_get_focus_visible (GTK_WINDOW (window))) |
5106 | flags |= GTK_STATE_FLAG_FOCUS_VISIBLE; |
5107 | |
5108 | list = NULL; |
5109 | for (widget = new_focus; widget; widget = gtk_widget_get_parent (widget)) |
5110 | list = g_list_prepend (list, data: widget); |
5111 | |
5112 | crossing.direction = GTK_CROSSING_IN; |
5113 | |
5114 | seen_ancestor = FALSE; |
5115 | for (l = list; l; l = l->next) |
5116 | { |
5117 | widget = l->data; |
5118 | if (l->next) |
5119 | focus_child = l->next->data; |
5120 | else |
5121 | focus_child = NULL; |
5122 | |
5123 | crossing.new_descendent = focus_child; |
5124 | if (seen_ancestor) |
5125 | { |
5126 | crossing.old_descendent = NULL; |
5127 | } |
5128 | else if (widget == ancestor) |
5129 | { |
5130 | GtkWidget *w; |
5131 | |
5132 | crossing.old_descendent = NULL; |
5133 | for (w = old_focus; w != ancestor; w = gtk_widget_get_parent (widget: w)) |
5134 | { |
5135 | crossing.old_descendent = w; |
5136 | } |
5137 | |
5138 | seen_ancestor = TRUE; |
5139 | } |
5140 | else |
5141 | { |
5142 | crossing.old_descendent = old_focus ? focus_child : NULL; |
5143 | } |
5144 | check_crossing_invariants (widget, crossing: &crossing); |
5145 | gtk_widget_handle_crossing (widget, crossing: &crossing, x: 0, y: 0); |
5146 | |
5147 | if (l->next == NULL) |
5148 | flags = flags | GTK_STATE_FLAG_FOCUSED; |
5149 | |
5150 | gtk_widget_set_state_flags (widget, flags, FALSE); |
5151 | gtk_widget_set_focus_child (widget, child: focus_child); |
5152 | } |
5153 | |
5154 | g_list_free (list); |
5155 | } |
5156 | |
5157 | /** |
5158 | * gtk_window_set_focus: (attributes org.gtk.Method.set_property=focus-widget) |
5159 | * @window: a `GtkWindow` |
5160 | * @focus: (nullable): widget to be the new focus widget, or %NULL to unset |
5161 | * any focus widget for the toplevel window. |
5162 | * |
5163 | * Sets the focus widget. |
5164 | * |
5165 | * If @focus is not the current focus widget, and is focusable, |
5166 | * sets it as the focus widget for the window. If @focus is %NULL, |
5167 | * unsets the focus widget for this window. To set the focus to a |
5168 | * particular widget in the toplevel, it is usually more convenient |
5169 | * to use [method@Gtk.Widget.grab_focus] instead of this function. |
5170 | */ |
5171 | void |
5172 | gtk_window_set_focus (GtkWindow *window, |
5173 | GtkWidget *focus) |
5174 | { |
5175 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5176 | |
5177 | if (focus) |
5178 | gtk_widget_grab_focus (widget: focus); |
5179 | else |
5180 | gtk_window_root_set_focus (root: GTK_ROOT (ptr: window), NULL); |
5181 | } |
5182 | |
5183 | static void |
5184 | gtk_window_css_changed (GtkWidget *widget, |
5185 | GtkCssStyleChange *change) |
5186 | { |
5187 | GtkWindow *window = GTK_WINDOW (widget); |
5188 | |
5189 | GTK_WIDGET_CLASS (gtk_window_parent_class)->css_changed (widget, change); |
5190 | |
5191 | if (!_gtk_widget_get_alloc_needed (widget) && |
5192 | (change == NULL || gtk_css_style_change_changes_property (change, id: GTK_CSS_PROPERTY_BACKGROUND_COLOR))) |
5193 | update_opaque_region (window); |
5194 | } |
5195 | |
5196 | /* |
5197 | * _gtk_window_unset_focus_and_default: |
5198 | * @window: a `GtkWindow` |
5199 | * @widget: a widget inside of @window |
5200 | * |
5201 | * Checks whether the focus and default widgets of @window are |
5202 | * @widget or a descendent of @widget, and if so, unset them. |
5203 | */ |
5204 | void |
5205 | _gtk_window_unset_focus_and_default (GtkWindow *window, |
5206 | GtkWidget *widget) |
5207 | |
5208 | { |
5209 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5210 | GtkWidget *child; |
5211 | |
5212 | child = priv->focus_widget; |
5213 | if (child && (child == widget || gtk_widget_is_ancestor (widget: child, ancestor: widget))) |
5214 | { |
5215 | priv->move_focus_widget = g_object_ref (widget); |
5216 | priv->move_focus = TRUE; |
5217 | } |
5218 | |
5219 | child = priv->default_widget; |
5220 | if (child && (child == widget || gtk_widget_is_ancestor (widget: child, ancestor: widget))) |
5221 | priv->unset_default = TRUE; |
5222 | |
5223 | if ((priv->move_focus || priv->unset_default) && |
5224 | priv->surface != NULL) |
5225 | { |
5226 | GdkFrameClock *frame_clock; |
5227 | |
5228 | frame_clock = gdk_surface_get_frame_clock (surface: priv->surface); |
5229 | gdk_frame_clock_request_phase (frame_clock, |
5230 | phase: GDK_FRAME_CLOCK_PHASE_AFTER_PAINT); |
5231 | } |
5232 | } |
5233 | |
5234 | #undef INCLUDE_CSD_SIZE |
5235 | #undef EXCLUDE_CSD_SIZE |
5236 | |
5237 | /** |
5238 | * gtk_window_present: |
5239 | * @window: a `GtkWindow` |
5240 | * |
5241 | * Presents a window to the user. |
5242 | * |
5243 | * This function should not be used as when it is called, |
5244 | * it is too late to gather a valid timestamp to allow focus |
5245 | * stealing prevention to work correctly. |
5246 | */ |
5247 | void |
5248 | gtk_window_present (GtkWindow *window) |
5249 | { |
5250 | gtk_window_present_with_time (window, GDK_CURRENT_TIME); |
5251 | } |
5252 | |
5253 | /** |
5254 | * gtk_window_present_with_time: |
5255 | * @window: a `GtkWindow` |
5256 | * @timestamp: the timestamp of the user interaction (typically a |
5257 | * button or key press event) which triggered this call |
5258 | * |
5259 | * Presents a window to the user. |
5260 | * |
5261 | * This may mean raising the window in the stacking order, |
5262 | * unminimizing it, moving it to the current desktop, and/or |
5263 | * giving it the keyboard focus, possibly dependent on the user’s |
5264 | * platform, window manager, and preferences. |
5265 | * |
5266 | * If @window is hidden, this function calls [method@Gtk.Widget.show] |
5267 | * as well. |
5268 | * |
5269 | * This function should be used when the user tries to open a window |
5270 | * that’s already open. Say for example the preferences dialog is |
5271 | * currently open, and the user chooses Preferences from the menu |
5272 | * a second time; use [method@Gtk.Window.present] to move the |
5273 | * already-open dialog where the user can see it. |
5274 | * |
5275 | * Presents a window to the user in response to a user interaction. |
5276 | * The timestamp should be gathered when the window was requested |
5277 | * to be shown (when clicking a link for example), rather than once |
5278 | * the window is ready to be shown. |
5279 | */ |
5280 | void |
5281 | gtk_window_present_with_time (GtkWindow *window, |
5282 | guint32 timestamp) |
5283 | { |
5284 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5285 | GtkWidget *widget; |
5286 | GdkSurface *surface; |
5287 | |
5288 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5289 | |
5290 | widget = GTK_WIDGET (window); |
5291 | |
5292 | if (gtk_widget_get_visible (widget)) |
5293 | { |
5294 | surface = priv->surface; |
5295 | |
5296 | g_assert (surface != NULL); |
5297 | |
5298 | /* Translate a timestamp of GDK_CURRENT_TIME appropriately */ |
5299 | if (timestamp == GDK_CURRENT_TIME) |
5300 | { |
5301 | #ifdef GDK_WINDOWING_X11 |
5302 | if (GDK_IS_X11_SURFACE (surface)) |
5303 | { |
5304 | GdkDisplay *display; |
5305 | |
5306 | display = gtk_widget_get_display (widget); |
5307 | timestamp = gdk_x11_display_get_user_time (display); |
5308 | } |
5309 | else |
5310 | #endif |
5311 | timestamp = gtk_get_current_event_time (); |
5312 | } |
5313 | } |
5314 | else |
5315 | { |
5316 | priv->initial_timestamp = timestamp; |
5317 | gtk_widget_show (widget); |
5318 | } |
5319 | |
5320 | g_assert (priv->surface != NULL); |
5321 | gdk_toplevel_focus (toplevel: GDK_TOPLEVEL (ptr: priv->surface), timestamp); |
5322 | } |
5323 | |
5324 | /** |
5325 | * gtk_window_minimize: |
5326 | * @window: a `GtkWindow` |
5327 | * |
5328 | * Asks to minimize the specified @window. |
5329 | * |
5330 | * Note that you shouldn’t assume the window is definitely minimized |
5331 | * afterward, because the windowing system might not support this |
5332 | * functionality; other entities (e.g. the user or the window manager |
5333 | * could unminimize it again, or there may not be a window manager in |
5334 | * which case minimization isn’t possible, etc. |
5335 | * |
5336 | * It’s permitted to call this function before showing a window, |
5337 | * in which case the window will be minimized before it ever appears |
5338 | * onscreen. |
5339 | * |
5340 | * You can track result of this operation via the |
5341 | * [property@Gdk.Toplevel:state] property. |
5342 | */ |
5343 | void |
5344 | gtk_window_minimize (GtkWindow *window) |
5345 | { |
5346 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5347 | |
5348 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5349 | |
5350 | priv->minimize_initially = TRUE; |
5351 | |
5352 | if (priv->surface) |
5353 | gdk_toplevel_minimize (toplevel: GDK_TOPLEVEL (ptr: priv->surface)); |
5354 | } |
5355 | |
5356 | /** |
5357 | * gtk_window_unminimize: |
5358 | * @window: a `GtkWindow` |
5359 | * |
5360 | * Asks to unminimize the specified @window. |
5361 | * |
5362 | * Note that you shouldn’t assume the window is definitely unminimized |
5363 | * afterward, because the windowing system might not support this |
5364 | * functionality; other entities (e.g. the user or the window manager |
5365 | * could minimize it again, or there may not be a window manager in |
5366 | * which case minimization isn’t possible, etc. |
5367 | * |
5368 | * You can track result of this operation via the |
5369 | * [property@Gdk.Toplevel:state] property. |
5370 | */ |
5371 | void |
5372 | gtk_window_unminimize (GtkWindow *window) |
5373 | { |
5374 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5375 | |
5376 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5377 | |
5378 | priv->minimize_initially = FALSE; |
5379 | |
5380 | gtk_window_update_toplevel (window, |
5381 | layout: gtk_window_compute_base_layout (window)); |
5382 | } |
5383 | |
5384 | /** |
5385 | * gtk_window_maximize: |
5386 | * @window: a `GtkWindow` |
5387 | * |
5388 | * Asks to maximize @window, so that it fills the screen. |
5389 | * |
5390 | * Note that you shouldn’t assume the window is definitely maximized |
5391 | * afterward, because other entities (e.g. the user or window manager |
5392 | * could unmaximize it again, and not all window managers support |
5393 | * maximization. |
5394 | * |
5395 | * It’s permitted to call this function before showing a window, |
5396 | * in which case the window will be maximized when it appears onscreen |
5397 | * initially. |
5398 | * |
5399 | * You can track the result of this operation via the |
5400 | * [property@Gdk.Toplevel:state] property, or by listening to |
5401 | * notifications on the [property@Gtk.Window:maximized] |
5402 | * property. |
5403 | */ |
5404 | void |
5405 | gtk_window_maximize (GtkWindow *window) |
5406 | { |
5407 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5408 | |
5409 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5410 | |
5411 | if (_gtk_widget_get_mapped (GTK_WIDGET (window))) |
5412 | { |
5413 | GdkToplevelLayout *layout; |
5414 | |
5415 | layout = gtk_window_compute_base_layout (window); |
5416 | gdk_toplevel_layout_set_maximized (layout, TRUE); |
5417 | gtk_window_update_toplevel (window, layout); |
5418 | } |
5419 | else if (!priv->maximized) |
5420 | { |
5421 | priv->maximized = TRUE; |
5422 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_MAXIMIZED]); |
5423 | } |
5424 | } |
5425 | |
5426 | /** |
5427 | * gtk_window_unmaximize: |
5428 | * @window: a `GtkWindow` |
5429 | * |
5430 | * Asks to unmaximize @window. |
5431 | * |
5432 | * Note that you shouldn’t assume the window is definitely unmaximized |
5433 | * afterward, because other entities (e.g. the user or window manager |
5434 | * maximize it again, and not all window managers honor requests to |
5435 | * unmaximize. |
5436 | * |
5437 | * You can track the result of this operation via the |
5438 | * [property@Gdk.Toplevel:state] property, or by listening to |
5439 | * notifications on the [property@Gtk.Window:maximized] property. |
5440 | */ |
5441 | void |
5442 | gtk_window_unmaximize (GtkWindow *window) |
5443 | { |
5444 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5445 | |
5446 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5447 | |
5448 | if (_gtk_widget_get_mapped (GTK_WIDGET (window))) |
5449 | { |
5450 | GdkToplevelLayout *layout; |
5451 | |
5452 | layout = gtk_window_compute_base_layout (window); |
5453 | gdk_toplevel_layout_set_maximized (layout, FALSE); |
5454 | gtk_window_update_toplevel (window, layout); |
5455 | } |
5456 | else if (priv->maximized) |
5457 | { |
5458 | priv->maximized = FALSE; |
5459 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_MAXIMIZED]); |
5460 | } |
5461 | } |
5462 | |
5463 | static void |
5464 | unset_fullscreen_monitor (GtkWindow *window) |
5465 | { |
5466 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5467 | |
5468 | if (priv->initial_fullscreen_monitor) |
5469 | { |
5470 | g_signal_handlers_disconnect_by_func (priv->initial_fullscreen_monitor, unset_fullscreen_monitor, window); |
5471 | g_object_unref (object: priv->initial_fullscreen_monitor); |
5472 | priv->initial_fullscreen_monitor = NULL; |
5473 | } |
5474 | } |
5475 | |
5476 | /** |
5477 | * gtk_window_fullscreen: |
5478 | * @window: a `GtkWindow` |
5479 | * |
5480 | * Asks to place @window in the fullscreen state. |
5481 | * |
5482 | * Note that you shouldn’t assume the window is definitely fullscreen |
5483 | * afterward, because other entities (e.g. the user or window manager |
5484 | * unfullscreen it again, and not all window managers honor requests |
5485 | * to fullscreen windows. |
5486 | * |
5487 | * You can track the result of this operation via the |
5488 | * [property@Gdk.Toplevel:state] property, or by listening to |
5489 | * notifications of the [property@Gtk.Window:fullscreened] property. |
5490 | */ |
5491 | void |
5492 | gtk_window_fullscreen (GtkWindow *window) |
5493 | { |
5494 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5495 | |
5496 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5497 | |
5498 | unset_fullscreen_monitor (window); |
5499 | |
5500 | if (_gtk_widget_get_mapped (GTK_WIDGET (window))) |
5501 | { |
5502 | GdkToplevelLayout *layout; |
5503 | |
5504 | layout = gtk_window_compute_base_layout (window); |
5505 | gdk_toplevel_layout_set_fullscreen (layout, TRUE, NULL); |
5506 | gtk_window_update_toplevel (window, layout); |
5507 | } |
5508 | else if (!priv->fullscreen) |
5509 | { |
5510 | priv->fullscreen = TRUE; |
5511 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_FULLSCREENED]); |
5512 | } |
5513 | } |
5514 | |
5515 | /** |
5516 | * gtk_window_fullscreen_on_monitor: |
5517 | * @window: a `GtkWindow` |
5518 | * @monitor: which monitor to go fullscreen on |
5519 | * |
5520 | * Asks to place @window in the fullscreen state on the given @monitor. |
5521 | * |
5522 | * Note that you shouldn't assume the window is definitely fullscreen |
5523 | * afterward, or that the windowing system allows fullscreen windows on |
5524 | * any given monitor. |
5525 | * |
5526 | * You can track the result of this operation via the |
5527 | * [property@Gdk.Toplevel:state] property, or by listening to |
5528 | * notifications of the [property@Gtk.Window:fullscreened] property. |
5529 | */ |
5530 | void |
5531 | gtk_window_fullscreen_on_monitor (GtkWindow *window, |
5532 | GdkMonitor *monitor) |
5533 | { |
5534 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5535 | |
5536 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5537 | g_return_if_fail (GDK_IS_MONITOR (monitor)); |
5538 | g_return_if_fail (gdk_monitor_is_valid (monitor)); |
5539 | |
5540 | gtk_window_set_display (window, display: gdk_monitor_get_display (monitor)); |
5541 | |
5542 | unset_fullscreen_monitor (window); |
5543 | priv->initial_fullscreen_monitor = monitor; |
5544 | g_signal_connect_swapped (priv->initial_fullscreen_monitor, "invalidate" , |
5545 | G_CALLBACK (unset_fullscreen_monitor), window); |
5546 | g_object_ref (priv->initial_fullscreen_monitor); |
5547 | |
5548 | if (_gtk_widget_get_mapped (GTK_WIDGET (window))) |
5549 | { |
5550 | GdkToplevelLayout *layout; |
5551 | |
5552 | layout = gtk_window_compute_base_layout (window); |
5553 | gdk_toplevel_layout_set_fullscreen (layout, TRUE, monitor); |
5554 | gtk_window_update_toplevel (window, layout); |
5555 | } |
5556 | else if (!priv->fullscreen) |
5557 | { |
5558 | priv->fullscreen = TRUE; |
5559 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_FULLSCREENED]); |
5560 | } |
5561 | } |
5562 | |
5563 | /** |
5564 | * gtk_window_unfullscreen: |
5565 | * @window: a `GtkWindow` |
5566 | * |
5567 | * Asks to remove the fullscreen state for @window, and return to |
5568 | * its previous state. |
5569 | * |
5570 | * Note that you shouldn’t assume the window is definitely not |
5571 | * fullscreen afterward, because other entities (e.g. the user or |
5572 | * window manager could fullscreen it again, and not all window |
5573 | * managers honor requests to unfullscreen windows; normally the |
5574 | * window will end up restored to its normal state. Just don’t |
5575 | * write code that crashes if not. |
5576 | * |
5577 | * You can track the result of this operation via the |
5578 | * [property@Gdk.Toplevel:state] property, or by listening to |
5579 | * notifications of the [property@Gtk.Window:fullscreened] property. |
5580 | */ |
5581 | void |
5582 | gtk_window_unfullscreen (GtkWindow *window) |
5583 | { |
5584 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5585 | |
5586 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5587 | |
5588 | unset_fullscreen_monitor (window); |
5589 | |
5590 | if (_gtk_widget_get_mapped (GTK_WIDGET (window))) |
5591 | { |
5592 | GdkToplevelLayout *layout; |
5593 | |
5594 | layout = gtk_window_compute_base_layout (window); |
5595 | gdk_toplevel_layout_set_fullscreen (layout, FALSE, NULL); |
5596 | gtk_window_update_toplevel (window, layout); |
5597 | } |
5598 | else if (priv->fullscreen) |
5599 | { |
5600 | priv->fullscreen = FALSE; |
5601 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_FULLSCREENED]); |
5602 | } |
5603 | } |
5604 | |
5605 | /** |
5606 | * gtk_window_set_resizable: (attributes org.gtk.Method.set_property=resizable) |
5607 | * @window: a `GtkWindow` |
5608 | * @resizable: %TRUE if the user can resize this window |
5609 | * |
5610 | * Sets whether the user can resize a window. |
5611 | * |
5612 | * Windows are user resizable by default. |
5613 | **/ |
5614 | void |
5615 | gtk_window_set_resizable (GtkWindow *window, |
5616 | gboolean resizable) |
5617 | { |
5618 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5619 | |
5620 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5621 | |
5622 | resizable = (resizable != FALSE); |
5623 | |
5624 | if (priv->resizable != resizable) |
5625 | { |
5626 | priv->resizable = resizable; |
5627 | |
5628 | update_window_actions (window); |
5629 | |
5630 | gtk_window_update_toplevel (window, |
5631 | layout: gtk_window_compute_base_layout (window)); |
5632 | |
5633 | gtk_widget_queue_resize (GTK_WIDGET (window)); |
5634 | |
5635 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_RESIZABLE]); |
5636 | } |
5637 | } |
5638 | |
5639 | /** |
5640 | * gtk_window_get_resizable: (attributes org.gtk.Method.get_property=resizable) |
5641 | * @window: a `GtkWindow` |
5642 | * |
5643 | * Gets the value set by gtk_window_set_resizable(). |
5644 | * |
5645 | * Returns: %TRUE if the user can resize the window |
5646 | **/ |
5647 | gboolean |
5648 | gtk_window_get_resizable (GtkWindow *window) |
5649 | { |
5650 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5651 | |
5652 | g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); |
5653 | |
5654 | return priv->resizable; |
5655 | } |
5656 | |
5657 | /** |
5658 | * gtk_window_set_display: (attributes org.gtk.Method.set_property=display) |
5659 | * @window: a `GtkWindow` |
5660 | * @display: a `GdkDisplay` |
5661 | * |
5662 | * Sets the `GdkDisplay` where the @window is displayed. |
5663 | * |
5664 | * If the window is already mapped, it will be unmapped, |
5665 | * and then remapped on the new display. |
5666 | */ |
5667 | void |
5668 | gtk_window_set_display (GtkWindow *window, |
5669 | GdkDisplay *display) |
5670 | { |
5671 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5672 | GtkWidget *widget; |
5673 | gboolean was_mapped; |
5674 | |
5675 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5676 | g_return_if_fail (GDK_IS_DISPLAY (display)); |
5677 | |
5678 | if (display == priv->display) |
5679 | return; |
5680 | |
5681 | /* reset initial_fullscreen_monitor since they are relative to the screen */ |
5682 | unset_fullscreen_monitor (window); |
5683 | |
5684 | widget = GTK_WIDGET (window); |
5685 | |
5686 | was_mapped = _gtk_widget_get_mapped (widget); |
5687 | |
5688 | if (was_mapped) |
5689 | gtk_widget_unmap (widget); |
5690 | if (_gtk_widget_get_realized (widget)) |
5691 | gtk_widget_unrealize (widget); |
5692 | |
5693 | if (priv->transient_parent && gtk_widget_get_display (GTK_WIDGET (priv->transient_parent)) != display) |
5694 | gtk_window_set_transient_for (window, NULL); |
5695 | |
5696 | #ifdef GDK_WINDOWING_X11 |
5697 | g_signal_handlers_disconnect_by_func (gtk_settings_get_for_display (priv->display), |
5698 | gtk_window_on_theme_variant_changed, window); |
5699 | g_signal_connect (gtk_settings_get_for_display (display), |
5700 | "notify::gtk-application-prefer-dark-theme" , |
5701 | G_CALLBACK (gtk_window_on_theme_variant_changed), window); |
5702 | #endif |
5703 | |
5704 | gtk_widget_unroot (widget); |
5705 | priv->display = display; |
5706 | |
5707 | gtk_widget_root (widget); |
5708 | |
5709 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_DISPLAY]); |
5710 | |
5711 | if (was_mapped) |
5712 | gtk_widget_map (widget); |
5713 | |
5714 | check_scale_changed (window); |
5715 | |
5716 | gtk_widget_system_setting_changed (GTK_WIDGET (window), setting: GTK_SYSTEM_SETTING_DISPLAY); |
5717 | } |
5718 | |
5719 | static void |
5720 | gtk_window_set_theme_variant (GtkWindow *window) |
5721 | { |
5722 | #ifdef GDK_WINDOWING_X11 |
5723 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5724 | gboolean dark_theme_requested; |
5725 | |
5726 | g_object_get (object: gtk_settings_get_for_display (display: priv->display), |
5727 | first_property_name: "gtk-application-prefer-dark-theme" , &dark_theme_requested, |
5728 | NULL); |
5729 | |
5730 | if (GDK_IS_X11_SURFACE (priv->surface)) |
5731 | gdk_x11_surface_set_theme_variant (surface: priv->surface, |
5732 | variant: dark_theme_requested ? "dark" : NULL); |
5733 | #endif |
5734 | } |
5735 | |
5736 | #ifdef GDK_WINDOWING_X11 |
5737 | static void |
5738 | gtk_window_on_theme_variant_changed (GtkSettings *settings, |
5739 | GParamSpec *pspec, |
5740 | GtkWindow *window) |
5741 | { |
5742 | gtk_window_set_theme_variant (window); |
5743 | } |
5744 | #endif |
5745 | |
5746 | /** |
5747 | * gtk_window_is_active: (attributes org.gtk.Method.get_property=is-active) |
5748 | * @window: a `GtkWindow` |
5749 | * |
5750 | * Returns whether the window is part of the current active toplevel. |
5751 | * |
5752 | * The active toplevel is the window receiving keystrokes. |
5753 | * |
5754 | * The return value is %TRUE if the window is active toplevel itself. |
5755 | * You might use this function if you wanted to draw a widget |
5756 | * differently in an active window from a widget in an inactive window. |
5757 | * |
5758 | * Returns: %TRUE if the window part of the current active window. |
5759 | */ |
5760 | gboolean |
5761 | gtk_window_is_active (GtkWindow *window) |
5762 | { |
5763 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5764 | |
5765 | g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); |
5766 | |
5767 | return priv->is_active; |
5768 | } |
5769 | |
5770 | /** |
5771 | * gtk_window_get_group: |
5772 | * @window: (nullable): a `GtkWindow` |
5773 | * |
5774 | * Returns the group for @window. |
5775 | * |
5776 | * If the window has no group, then the default group is returned. |
5777 | * |
5778 | * Returns: (transfer none): the `GtkWindowGroup` for a window |
5779 | * or the default group |
5780 | */ |
5781 | GtkWindowGroup * |
5782 | gtk_window_get_group (GtkWindow *window) |
5783 | { |
5784 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5785 | |
5786 | if (window && priv->group) |
5787 | return priv->group; |
5788 | else |
5789 | { |
5790 | static GtkWindowGroup *default_group = NULL; |
5791 | |
5792 | if (!default_group) |
5793 | default_group = gtk_window_group_new (); |
5794 | |
5795 | return default_group; |
5796 | } |
5797 | } |
5798 | |
5799 | /** |
5800 | * gtk_window_has_group: |
5801 | * @window: a `GtkWindow` |
5802 | * |
5803 | * Returns whether @window has an explicit window group. |
5804 | * |
5805 | * Returns: %TRUE if @window has an explicit window group. |
5806 | */ |
5807 | gboolean |
5808 | gtk_window_has_group (GtkWindow *window) |
5809 | { |
5810 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5811 | |
5812 | g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); |
5813 | |
5814 | return priv->group != NULL; |
5815 | } |
5816 | |
5817 | GtkWindowGroup * |
5818 | _gtk_window_get_window_group (GtkWindow *window) |
5819 | { |
5820 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5821 | |
5822 | return priv->group; |
5823 | } |
5824 | |
5825 | void |
5826 | _gtk_window_set_window_group (GtkWindow *window, |
5827 | GtkWindowGroup *group) |
5828 | { |
5829 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5830 | |
5831 | priv->group = group; |
5832 | } |
5833 | |
5834 | static gboolean |
5835 | (GtkWidget *widget, |
5836 | GVariant *args, |
5837 | gpointer unused) |
5838 | { |
5839 | GtkWindow *window = GTK_WINDOW (widget); |
5840 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5841 | GList *, *l; |
5842 | GPtrArray *; |
5843 | GtkWidget *focus; |
5844 | GtkWidget *first; |
5845 | |
5846 | tmp_menubars = gtk_popover_menu_bar_get_viewable_menu_bars (window); |
5847 | if (tmp_menubars == NULL) |
5848 | { |
5849 | focus = gtk_window_get_focus (window); |
5850 | return priv->title_box != NULL && |
5851 | (focus == NULL || !gtk_widget_is_ancestor (widget: focus, ancestor: priv->title_box)) && |
5852 | gtk_widget_child_focus (widget: priv->title_box, direction: GTK_DIR_TAB_FORWARD); |
5853 | } |
5854 | |
5855 | menubars = g_ptr_array_sized_new (reserved_size: g_list_length (list: tmp_menubars));; |
5856 | for (l = tmp_menubars; l; l = l->next) |
5857 | g_ptr_array_add (array: menubars, data: l->data); |
5858 | |
5859 | g_list_free (list: tmp_menubars); |
5860 | |
5861 | gtk_widget_focus_sort (GTK_WIDGET (window), direction: GTK_DIR_TAB_FORWARD, focus_order: menubars); |
5862 | |
5863 | first = g_ptr_array_index (menubars, 0); |
5864 | if (GTK_IS_POPOVER_MENU_BAR (first)) |
5865 | gtk_popover_menu_bar_select_first (GTK_POPOVER_MENU_BAR (first)); |
5866 | else if (GTK_IS_MENU_BUTTON (first)) |
5867 | gtk_menu_button_popup (GTK_MENU_BUTTON (first)); |
5868 | |
5869 | g_ptr_array_free (array: menubars, TRUE); |
5870 | |
5871 | return TRUE; |
5872 | } |
5873 | |
5874 | static void |
5875 | gtk_window_keys_changed (GtkWindow *window) |
5876 | { |
5877 | } |
5878 | |
5879 | /* |
5880 | * _gtk_window_set_is_active: |
5881 | * @window: a `GtkWindow` |
5882 | * @is_active: %TRUE if the window is in the currently active toplevel |
5883 | * |
5884 | * Internal function that sets whether the `GtkWindow` is part |
5885 | * of the currently active toplevel window (taking into account |
5886 | * inter-process embedding.) |
5887 | */ |
5888 | static void |
5889 | _gtk_window_set_is_active (GtkWindow *window, |
5890 | gboolean is_active) |
5891 | { |
5892 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5893 | |
5894 | if (priv->is_active == is_active) |
5895 | return; |
5896 | |
5897 | priv->is_active = is_active; |
5898 | |
5899 | if (priv->focus_widget) |
5900 | { |
5901 | GtkWidget *focus; |
5902 | |
5903 | focus = g_object_ref (priv->focus_widget); |
5904 | |
5905 | if (is_active) |
5906 | { |
5907 | synthesize_focus_change_events (window, NULL, new_focus: focus, type: GTK_CROSSING_ACTIVE); |
5908 | gtk_widget_set_has_focus (widget: focus, TRUE); |
5909 | } |
5910 | else |
5911 | { |
5912 | synthesize_focus_change_events (window, old_focus: focus, NULL, type: GTK_CROSSING_ACTIVE); |
5913 | gtk_widget_set_has_focus (widget: focus, FALSE); |
5914 | } |
5915 | |
5916 | g_object_unref (object: focus); |
5917 | } |
5918 | |
5919 | gtk_accessible_platform_changed (self: GTK_ACCESSIBLE (ptr: window), change: GTK_ACCESSIBLE_PLATFORM_CHANGE_ACTIVE); |
5920 | |
5921 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_IS_ACTIVE]); |
5922 | } |
5923 | |
5924 | /** |
5925 | * gtk_window_set_auto_startup_notification: |
5926 | * @setting: %TRUE to automatically do startup notification |
5927 | * |
5928 | * Sets whether the window should request startup notification. |
5929 | * |
5930 | * By default, after showing the first `GtkWindow`, GTK calls |
5931 | * [method@Gdk.Display.notify_startup_complete]. Call this function |
5932 | * to disable the automatic startup notification. You might do this |
5933 | * if your first window is a splash screen, and you want to delay |
5934 | * notification until after your real main window has been shown, |
5935 | * for example. |
5936 | * |
5937 | * In that example, you would disable startup notification |
5938 | * temporarily, show your splash screen, then re-enable it so that |
5939 | * showing the main window would automatically result in notification. |
5940 | */ |
5941 | void |
5942 | gtk_window_set_auto_startup_notification (gboolean setting) |
5943 | { |
5944 | disable_startup_notification = !setting; |
5945 | } |
5946 | |
5947 | /** |
5948 | * gtk_window_get_mnemonics_visible: (attributes org.gtk.Method.get_property=mnemonics-visible) |
5949 | * @window: a `GtkWindow` |
5950 | * |
5951 | * Gets whether mnemonics are supposed to be visible. |
5952 | * |
5953 | * Returns: %TRUE if mnemonics are supposed to be visible |
5954 | * in this window. |
5955 | */ |
5956 | gboolean |
5957 | gtk_window_get_mnemonics_visible (GtkWindow *window) |
5958 | { |
5959 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5960 | |
5961 | g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); |
5962 | |
5963 | return priv->mnemonics_visible; |
5964 | } |
5965 | |
5966 | /** |
5967 | * gtk_window_set_mnemonics_visible: (attributes org.gtk.Method.set_property=mnemonics-visible) |
5968 | * @window: a `GtkWindow` |
5969 | * @setting: the new value |
5970 | * |
5971 | * Sets whether mnemonics are supposed to be visible. |
5972 | */ |
5973 | void |
5974 | gtk_window_set_mnemonics_visible (GtkWindow *window, |
5975 | gboolean setting) |
5976 | { |
5977 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
5978 | |
5979 | g_return_if_fail (GTK_IS_WINDOW (window)); |
5980 | |
5981 | setting = setting != FALSE; |
5982 | |
5983 | if (priv->mnemonics_visible != setting) |
5984 | { |
5985 | priv->mnemonics_visible = setting; |
5986 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_MNEMONICS_VISIBLE]); |
5987 | } |
5988 | |
5989 | if (priv->mnemonics_display_timeout_id) |
5990 | { |
5991 | g_source_remove (tag: priv->mnemonics_display_timeout_id); |
5992 | priv->mnemonics_display_timeout_id = 0; |
5993 | } |
5994 | } |
5995 | |
5996 | static gboolean |
5997 | schedule_mnemonics_visible_cb (gpointer data) |
5998 | { |
5999 | GtkWindow *window = data; |
6000 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6001 | |
6002 | priv->mnemonics_display_timeout_id = 0; |
6003 | |
6004 | gtk_window_set_mnemonics_visible (window, TRUE); |
6005 | |
6006 | return FALSE; |
6007 | } |
6008 | |
6009 | void |
6010 | _gtk_window_schedule_mnemonics_visible (GtkWindow *window) |
6011 | { |
6012 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6013 | |
6014 | g_return_if_fail (GTK_IS_WINDOW (window)); |
6015 | |
6016 | if (priv->mnemonics_display_timeout_id) |
6017 | return; |
6018 | |
6019 | priv->mnemonics_display_timeout_id = |
6020 | g_timeout_add (MNEMONICS_DELAY, function: schedule_mnemonics_visible_cb, data: window); |
6021 | gdk_source_set_static_name_by_id (tag: priv->mnemonics_display_timeout_id, name: "[gtk] schedule_mnemonics_visible_cb" ); |
6022 | } |
6023 | |
6024 | /** |
6025 | * gtk_window_get_focus_visible: (attributes org.gtk.Method.get_property=focus-visible) |
6026 | * @window: a `GtkWindow` |
6027 | * |
6028 | * Gets whether “focus rectangles” are supposed to be visible. |
6029 | * |
6030 | * Returns: %TRUE if “focus rectangles” are supposed to be visible |
6031 | * in this window. |
6032 | */ |
6033 | gboolean |
6034 | gtk_window_get_focus_visible (GtkWindow *window) |
6035 | { |
6036 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6037 | |
6038 | g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); |
6039 | |
6040 | return priv->focus_visible; |
6041 | } |
6042 | |
6043 | static gboolean |
6044 | unset_focus_visible (gpointer data) |
6045 | { |
6046 | GtkWindow *window = data; |
6047 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6048 | |
6049 | priv->focus_visible_timeout = 0; |
6050 | |
6051 | gtk_window_set_focus_visible (window, FALSE); |
6052 | |
6053 | return G_SOURCE_REMOVE; |
6054 | } |
6055 | |
6056 | /** |
6057 | * gtk_window_set_focus_visible: (attributes org.gtk.MEthod.set_property=focus-visible) |
6058 | * @window: a `GtkWindow` |
6059 | * @setting: the new value |
6060 | * |
6061 | * Sets whether “focus rectangles” are supposed to be visible. |
6062 | */ |
6063 | void |
6064 | gtk_window_set_focus_visible (GtkWindow *window, |
6065 | gboolean setting) |
6066 | { |
6067 | gboolean changed; |
6068 | |
6069 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6070 | |
6071 | g_return_if_fail (GTK_IS_WINDOW (window)); |
6072 | |
6073 | changed = priv->focus_visible != setting; |
6074 | |
6075 | priv->focus_visible = setting; |
6076 | |
6077 | if (priv->focus_visible_timeout) |
6078 | { |
6079 | g_source_remove (tag: priv->focus_visible_timeout); |
6080 | priv->focus_visible_timeout = 0; |
6081 | } |
6082 | |
6083 | if (priv->focus_visible) |
6084 | priv->focus_visible_timeout = g_timeout_add_seconds (VISIBLE_FOCUS_DURATION, function: unset_focus_visible, data: window); |
6085 | |
6086 | if (changed) |
6087 | { |
6088 | if (priv->focus_widget) |
6089 | { |
6090 | GtkWidget *widget; |
6091 | |
6092 | for (widget = priv->focus_widget; widget; widget = gtk_widget_get_parent (widget)) |
6093 | { |
6094 | if (priv->focus_visible) |
6095 | gtk_widget_set_state_flags (widget, flags: GTK_STATE_FLAG_FOCUS_VISIBLE, FALSE); |
6096 | else |
6097 | gtk_widget_unset_state_flags (widget, flags: GTK_STATE_FLAG_FOCUS_VISIBLE); |
6098 | } |
6099 | } |
6100 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_FOCUS_VISIBLE]); |
6101 | } |
6102 | } |
6103 | |
6104 | static void |
6105 | ensure_state_flag_backdrop (GtkWidget *widget) |
6106 | { |
6107 | GtkWindowPrivate *priv = gtk_window_get_instance_private (GTK_WINDOW (widget)); |
6108 | gboolean surface_focused = TRUE; |
6109 | |
6110 | surface_focused = gdk_toplevel_get_state (toplevel: GDK_TOPLEVEL (ptr: priv->surface)) & GDK_TOPLEVEL_STATE_FOCUSED; |
6111 | |
6112 | if (!surface_focused) |
6113 | gtk_widget_set_state_flags (widget, flags: GTK_STATE_FLAG_BACKDROP, FALSE); |
6114 | else |
6115 | gtk_widget_unset_state_flags (widget, flags: GTK_STATE_FLAG_BACKDROP); |
6116 | } |
6117 | |
6118 | static void set_warn_again (gboolean warn); |
6119 | static void gtk_window_set_debugging (GdkDisplay *display, |
6120 | gboolean enable, |
6121 | gboolean toggle, |
6122 | gboolean select, |
6123 | gboolean warn); |
6124 | |
6125 | static void |
6126 | warn_response (GtkDialog *dialog, |
6127 | int response) |
6128 | { |
6129 | GtkWidget *check; |
6130 | gboolean remember; |
6131 | GtkWidget *inspector_window; |
6132 | GdkDisplay *display; |
6133 | |
6134 | inspector_window = GTK_WIDGET (gtk_window_get_transient_for (GTK_WINDOW (dialog))); |
6135 | display = gtk_inspector_window_get_inspected_display (GTK_INSPECTOR_WINDOW (inspector_window)); |
6136 | |
6137 | check = g_object_get_data (G_OBJECT (dialog), key: "check" ); |
6138 | remember = gtk_check_button_get_active (GTK_CHECK_BUTTON (check)); |
6139 | |
6140 | gtk_window_destroy (GTK_WINDOW (dialog)); |
6141 | g_object_set_data (G_OBJECT (inspector_window), key: "warning_dialog" , NULL); |
6142 | |
6143 | if (response == GTK_RESPONSE_NO) |
6144 | gtk_window_set_debugging (display, FALSE, FALSE, FALSE, FALSE); |
6145 | else |
6146 | set_warn_again (!remember); |
6147 | } |
6148 | |
6149 | static void |
6150 | gtk_window_set_debugging (GdkDisplay *display, |
6151 | gboolean enable, |
6152 | gboolean toggle, |
6153 | gboolean select, |
6154 | gboolean warn) |
6155 | { |
6156 | GtkWidget *dialog = NULL; |
6157 | GtkWidget *area; |
6158 | GtkWidget *check; |
6159 | GtkWidget *inspector_window; |
6160 | gboolean was_debugging; |
6161 | |
6162 | was_debugging = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (display), "-gtk-debugging-enabled" )); |
6163 | |
6164 | if (toggle) |
6165 | enable = !was_debugging; |
6166 | |
6167 | g_object_set_data (G_OBJECT (display), key: "-gtk-debugging-enabled" , GINT_TO_POINTER (enable)); |
6168 | |
6169 | if (enable) |
6170 | { |
6171 | inspector_window = gtk_inspector_window_get (display); |
6172 | |
6173 | gtk_window_present (GTK_WINDOW (inspector_window)); |
6174 | |
6175 | if (warn) |
6176 | { |
6177 | dialog = gtk_message_dialog_new (GTK_WINDOW (inspector_window), |
6178 | flags: GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, |
6179 | type: GTK_MESSAGE_QUESTION, |
6180 | buttons: GTK_BUTTONS_NONE, |
6181 | _("Do you want to use GTK Inspector?" )); |
6182 | gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), |
6183 | _("GTK Inspector is an interactive debugger that lets you explore and " |
6184 | "modify the internals of any GTK application. Using it may cause the " |
6185 | "application to break or crash." )); |
6186 | |
6187 | area = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG (dialog)); |
6188 | check = gtk_check_button_new_with_label (_("Don’t show this message again" )); |
6189 | gtk_widget_set_margin_start (widget: check, margin: 10); |
6190 | gtk_widget_show (widget: check); |
6191 | gtk_box_append (GTK_BOX (area), child: check); |
6192 | g_object_set_data (G_OBJECT (dialog), key: "check" , data: check); |
6193 | gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Cancel" ), response_id: GTK_RESPONSE_NO); |
6194 | gtk_dialog_add_button (GTK_DIALOG (dialog), _("_OK" ), response_id: GTK_RESPONSE_YES); |
6195 | g_signal_connect (dialog, "response" , G_CALLBACK (warn_response), inspector_window); |
6196 | g_object_set_data (G_OBJECT (inspector_window), key: "warning_dialog" , data: dialog); |
6197 | |
6198 | gtk_widget_show (widget: dialog); |
6199 | } |
6200 | |
6201 | if (select) |
6202 | gtk_inspector_window_select_widget_under_pointer (GTK_INSPECTOR_WINDOW (inspector_window)); |
6203 | } |
6204 | else if (was_debugging) |
6205 | { |
6206 | inspector_window = gtk_inspector_window_get (display); |
6207 | |
6208 | gtk_widget_hide (widget: inspector_window); |
6209 | } |
6210 | } |
6211 | |
6212 | /** |
6213 | * gtk_window_set_interactive_debugging: |
6214 | * @enable: %TRUE to enable interactive debugging |
6215 | * |
6216 | * Opens or closes the [interactive debugger](running.html#interactive-debugging). |
6217 | * |
6218 | * The debugger offers access to the widget hierarchy of the application |
6219 | * and to useful debugging tools. |
6220 | */ |
6221 | void |
6222 | gtk_window_set_interactive_debugging (gboolean enable) |
6223 | { |
6224 | GdkDisplay *display = gdk_display_get_default (); |
6225 | |
6226 | gtk_window_set_debugging (display, enable, FALSE, FALSE, FALSE); |
6227 | } |
6228 | |
6229 | static gboolean |
6230 | inspector_keybinding_enabled (gboolean *warn) |
6231 | { |
6232 | GSettingsSchema *schema; |
6233 | GSettings *settings; |
6234 | gboolean enabled; |
6235 | |
6236 | enabled = TRUE; |
6237 | *warn = TRUE; |
6238 | |
6239 | schema = g_settings_schema_source_lookup (source: g_settings_schema_source_get_default (), |
6240 | schema_id: "org.gtk.gtk4.Settings.Debug" , |
6241 | TRUE); |
6242 | |
6243 | if (schema) |
6244 | { |
6245 | settings = g_settings_new_full (schema, NULL, NULL); |
6246 | enabled = g_settings_get_boolean (settings, key: "enable-inspector-keybinding" ); |
6247 | *warn = g_settings_get_boolean (settings, key: "inspector-warning" ); |
6248 | g_object_unref (object: settings); |
6249 | g_settings_schema_unref (schema); |
6250 | } |
6251 | |
6252 | return enabled; |
6253 | } |
6254 | |
6255 | static void |
6256 | set_warn_again (gboolean warn) |
6257 | { |
6258 | GSettingsSchema *schema; |
6259 | GSettings *settings; |
6260 | |
6261 | schema = g_settings_schema_source_lookup (source: g_settings_schema_source_get_default (), |
6262 | schema_id: "org.gtk.gtk4.Settings.Debug" , |
6263 | TRUE); |
6264 | |
6265 | if (schema) |
6266 | { |
6267 | settings = g_settings_new_full (schema, NULL, NULL); |
6268 | g_settings_set_boolean (settings, key: "inspector-warning" , value: warn); |
6269 | g_object_unref (object: settings); |
6270 | g_settings_schema_unref (schema); |
6271 | } |
6272 | } |
6273 | |
6274 | static gboolean |
6275 | gtk_window_enable_debugging (GtkWindow *window, |
6276 | gboolean toggle) |
6277 | { |
6278 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6279 | gboolean warn; |
6280 | |
6281 | if (!inspector_keybinding_enabled (warn: &warn)) |
6282 | return FALSE; |
6283 | |
6284 | gtk_window_set_debugging (display: priv->display, TRUE, toggle, select: !toggle, warn); |
6285 | |
6286 | return TRUE; |
6287 | } |
6288 | |
6289 | #ifdef GDK_WINDOWING_WAYLAND |
6290 | typedef struct { |
6291 | GtkWindow *window; |
6292 | GtkWindowHandleExported callback; |
6293 | gpointer user_data; |
6294 | } WaylandSurfaceHandleExportedData; |
6295 | |
6296 | static void |
6297 | wayland_surface_handle_exported (GdkToplevel *toplevel, |
6298 | const char *wayland_handle_str, |
6299 | gpointer user_data) |
6300 | { |
6301 | WaylandSurfaceHandleExportedData *data = user_data; |
6302 | char *handle_str; |
6303 | |
6304 | handle_str = g_strdup_printf (format: "wayland:%s" , wayland_handle_str); |
6305 | data->callback (data->window, handle_str, data->user_data); |
6306 | g_free (mem: handle_str); |
6307 | } |
6308 | #endif |
6309 | |
6310 | gboolean |
6311 | gtk_window_export_handle (GtkWindow *window, |
6312 | GtkWindowHandleExported callback, |
6313 | gpointer user_data) |
6314 | { |
6315 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6316 | |
6317 | #ifdef GDK_WINDOWING_X11 |
6318 | if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) |
6319 | { |
6320 | char *handle_str; |
6321 | guint32 xid = (guint32) gdk_x11_surface_get_xid (surface: priv->surface); |
6322 | |
6323 | handle_str = g_strdup_printf (format: "x11:%x" , xid); |
6324 | callback (window, handle_str, user_data); |
6325 | g_free (mem: handle_str); |
6326 | |
6327 | return TRUE; |
6328 | } |
6329 | #endif |
6330 | #ifdef GDK_WINDOWING_WAYLAND |
6331 | if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) |
6332 | { |
6333 | WaylandSurfaceHandleExportedData *data; |
6334 | |
6335 | data = g_new0 (WaylandSurfaceHandleExportedData, 1); |
6336 | data->window = window; |
6337 | data->callback = callback; |
6338 | data->user_data = user_data; |
6339 | |
6340 | if (!gdk_wayland_toplevel_export_handle (toplevel: GDK_TOPLEVEL (ptr: priv->surface), |
6341 | callback: wayland_surface_handle_exported, |
6342 | user_data: data, |
6343 | destroy_func: g_free)) |
6344 | { |
6345 | g_free (mem: data); |
6346 | return FALSE; |
6347 | } |
6348 | else |
6349 | { |
6350 | return TRUE; |
6351 | } |
6352 | } |
6353 | #endif |
6354 | |
6355 | g_warning ("Couldn't export handle for %s surface, unsupported windowing system" , |
6356 | G_OBJECT_TYPE_NAME (priv->surface)); |
6357 | |
6358 | return FALSE; |
6359 | } |
6360 | |
6361 | void |
6362 | gtk_window_unexport_handle (GtkWindow *window) |
6363 | { |
6364 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6365 | |
6366 | #ifdef GDK_WINDOWING_WAYLAND |
6367 | if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) |
6368 | { |
6369 | gdk_wayland_toplevel_unexport_handle (toplevel: GDK_TOPLEVEL (ptr: priv->surface)); |
6370 | return; |
6371 | } |
6372 | #endif |
6373 | |
6374 | g_warning ("Couldn't unexport handle for %s surface, unsupported windowing system" , |
6375 | G_OBJECT_TYPE_NAME (priv->surface)); |
6376 | } |
6377 | |
6378 | static GtkPointerFocus * |
6379 | gtk_window_lookup_pointer_focus (GtkWindow *window, |
6380 | GdkDevice *device, |
6381 | GdkEventSequence *sequence) |
6382 | { |
6383 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6384 | GList *l; |
6385 | |
6386 | for (l = priv->foci; l; l = l->next) |
6387 | { |
6388 | GtkPointerFocus *focus = l->data; |
6389 | |
6390 | if (focus->device == device && focus->sequence == sequence) |
6391 | return focus; |
6392 | } |
6393 | |
6394 | return NULL; |
6395 | } |
6396 | |
6397 | GtkWidget * |
6398 | gtk_window_lookup_pointer_focus_widget (GtkWindow *window, |
6399 | GdkDevice *device, |
6400 | GdkEventSequence *sequence) |
6401 | { |
6402 | GtkPointerFocus *focus; |
6403 | |
6404 | focus = gtk_window_lookup_pointer_focus (window, device, sequence); |
6405 | return focus ? gtk_pointer_focus_get_target (focus) : NULL; |
6406 | } |
6407 | |
6408 | GtkWidget * |
6409 | gtk_window_lookup_effective_pointer_focus_widget (GtkWindow *window, |
6410 | GdkDevice *device, |
6411 | GdkEventSequence *sequence) |
6412 | { |
6413 | GtkPointerFocus *focus; |
6414 | |
6415 | focus = gtk_window_lookup_pointer_focus (window, device, sequence); |
6416 | return focus ? gtk_pointer_focus_get_effective_target (focus) : NULL; |
6417 | } |
6418 | |
6419 | GtkWidget * |
6420 | gtk_window_lookup_pointer_focus_implicit_grab (GtkWindow *window, |
6421 | GdkDevice *device, |
6422 | GdkEventSequence *sequence) |
6423 | { |
6424 | GtkPointerFocus *focus; |
6425 | |
6426 | focus = gtk_window_lookup_pointer_focus (window, device, sequence); |
6427 | return focus ? gtk_pointer_focus_get_implicit_grab (focus) : NULL; |
6428 | } |
6429 | |
6430 | void |
6431 | gtk_window_update_pointer_focus (GtkWindow *window, |
6432 | GdkDevice *device, |
6433 | GdkEventSequence *sequence, |
6434 | GtkWidget *target, |
6435 | double x, |
6436 | double y) |
6437 | { |
6438 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6439 | GtkPointerFocus *focus; |
6440 | |
6441 | focus = gtk_window_lookup_pointer_focus (window, device, sequence); |
6442 | if (focus) |
6443 | { |
6444 | gtk_pointer_focus_ref (focus); |
6445 | |
6446 | if (target) |
6447 | { |
6448 | gtk_pointer_focus_set_target (focus, target); |
6449 | gtk_pointer_focus_set_coordinates (focus, x, y); |
6450 | } |
6451 | else |
6452 | { |
6453 | GList *pos; |
6454 | |
6455 | pos = g_list_find (list: priv->foci, data: focus); |
6456 | if (pos) |
6457 | { |
6458 | priv->foci = g_list_remove (list: priv->foci, data: focus); |
6459 | gtk_pointer_focus_unref (focus); |
6460 | } |
6461 | } |
6462 | |
6463 | gtk_pointer_focus_unref (focus); |
6464 | } |
6465 | else if (target) |
6466 | { |
6467 | focus = gtk_pointer_focus_new (toplevel: window, widget: target, device, sequence, x, y); |
6468 | priv->foci = g_list_prepend (list: priv->foci, data: focus); |
6469 | } |
6470 | } |
6471 | |
6472 | void |
6473 | gtk_window_update_pointer_focus_on_state_change (GtkWindow *window, |
6474 | GtkWidget *widget) |
6475 | { |
6476 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6477 | GList *l = priv->foci; |
6478 | |
6479 | while (l) |
6480 | { |
6481 | GList *next; |
6482 | |
6483 | GtkPointerFocus *focus = l->data; |
6484 | |
6485 | next = l->next; |
6486 | |
6487 | gtk_pointer_focus_ref (focus); |
6488 | |
6489 | if (focus->grab_widget && |
6490 | (focus->grab_widget == widget || |
6491 | gtk_widget_is_ancestor (widget: focus->grab_widget, ancestor: widget))) |
6492 | gtk_pointer_focus_set_implicit_grab (focus, NULL); |
6493 | |
6494 | if (GTK_WIDGET (focus->toplevel) == widget) |
6495 | { |
6496 | /* Unmapping the toplevel, remove pointer focus */ |
6497 | priv->foci = g_list_remove_link (list: priv->foci, llink: l); |
6498 | gtk_pointer_focus_unref (focus); |
6499 | g_list_free (list: l); |
6500 | } |
6501 | else if (focus->target == widget || |
6502 | gtk_widget_is_ancestor (widget: focus->target, ancestor: widget)) |
6503 | { |
6504 | GtkWidget *old_target; |
6505 | |
6506 | old_target = g_object_ref (focus->target); |
6507 | gtk_pointer_focus_repick_target (focus); |
6508 | gtk_synthesize_crossing_events (toplevel: GTK_ROOT (ptr: window), |
6509 | crossing_type: GTK_CROSSING_POINTER, |
6510 | old_target, new_target: focus->target, |
6511 | surface_x: focus->x, surface_y: focus->y, |
6512 | mode: GDK_CROSSING_NORMAL, |
6513 | NULL); |
6514 | g_object_unref (object: old_target); |
6515 | } |
6516 | |
6517 | gtk_pointer_focus_unref (focus); |
6518 | |
6519 | l = next; |
6520 | } |
6521 | } |
6522 | |
6523 | void |
6524 | gtk_window_maybe_revoke_implicit_grab (GtkWindow *window, |
6525 | GdkDevice *device, |
6526 | GtkWidget *grab_widget) |
6527 | { |
6528 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6529 | GList *l = priv->foci; |
6530 | |
6531 | while (l) |
6532 | { |
6533 | GtkPointerFocus *focus = l->data; |
6534 | |
6535 | l = l->next; |
6536 | |
6537 | if (focus->toplevel != window) |
6538 | continue; |
6539 | |
6540 | if ((!device || focus->device == device) && |
6541 | focus->target != grab_widget && |
6542 | !gtk_widget_is_ancestor (widget: focus->target, ancestor: grab_widget)) |
6543 | gtk_window_set_pointer_focus_grab (window, |
6544 | device: focus->device, |
6545 | sequence: focus->sequence, |
6546 | NULL); |
6547 | } |
6548 | } |
6549 | |
6550 | void |
6551 | gtk_window_set_pointer_focus_grab (GtkWindow *window, |
6552 | GdkDevice *device, |
6553 | GdkEventSequence *sequence, |
6554 | GtkWidget *grab_widget) |
6555 | { |
6556 | GtkPointerFocus *focus; |
6557 | |
6558 | focus = gtk_window_lookup_pointer_focus (window, device, sequence); |
6559 | if (!focus && !grab_widget) |
6560 | return; |
6561 | g_assert (focus != NULL); |
6562 | gtk_pointer_focus_set_implicit_grab (focus, grab_widget); |
6563 | } |
6564 | |
6565 | static void |
6566 | update_cursor (GtkWindow *toplevel, |
6567 | GdkDevice *device, |
6568 | GtkWidget *grab_widget, |
6569 | GtkWidget *target) |
6570 | { |
6571 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: toplevel); |
6572 | GdkCursor *cursor = NULL; |
6573 | GtkNative *native; |
6574 | GdkSurface *surface; |
6575 | |
6576 | native = gtk_widget_get_native (widget: target); |
6577 | surface = gtk_native_get_surface (self: native); |
6578 | |
6579 | if (grab_widget && !gtk_widget_is_ancestor (widget: target, ancestor: grab_widget) && target != grab_widget) |
6580 | { |
6581 | /* Outside the grab widget, cursor stays to whatever the grab |
6582 | * widget says. |
6583 | */ |
6584 | if (gtk_widget_get_native (widget: grab_widget) == native) |
6585 | cursor = gtk_widget_get_cursor (widget: grab_widget); |
6586 | else |
6587 | cursor = NULL; |
6588 | } |
6589 | else |
6590 | { |
6591 | /* Inside the grab widget or in absence of grabs, allow walking |
6592 | * up the hierarchy to find out the cursor. |
6593 | */ |
6594 | while (target) |
6595 | { |
6596 | /* Don't inherit cursors across surfaces */ |
6597 | if (native != gtk_widget_get_native (widget: target)) |
6598 | break; |
6599 | |
6600 | if (target == GTK_WIDGET (toplevel) && priv->resize_cursor != NULL) |
6601 | cursor = priv->resize_cursor; |
6602 | else |
6603 | cursor = gtk_widget_get_cursor (widget: target); |
6604 | |
6605 | if (cursor) |
6606 | break; |
6607 | |
6608 | if (grab_widget && target == grab_widget) |
6609 | break; |
6610 | |
6611 | target = _gtk_widget_get_parent (widget: target); |
6612 | } |
6613 | } |
6614 | |
6615 | gdk_surface_set_device_cursor (surface, device, cursor); |
6616 | } |
6617 | |
6618 | void |
6619 | gtk_window_maybe_update_cursor (GtkWindow *window, |
6620 | GtkWidget *widget, |
6621 | GdkDevice *device) |
6622 | { |
6623 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6624 | GList *l; |
6625 | |
6626 | for (l = priv->foci; l; l = l->next) |
6627 | { |
6628 | GtkPointerFocus *focus = l->data; |
6629 | GtkWidget *grab_widget, *target; |
6630 | GtkWindowGroup *group; |
6631 | |
6632 | if (focus->sequence) |
6633 | continue; |
6634 | if (device && device != focus->device) |
6635 | continue; |
6636 | |
6637 | group = gtk_window_get_group (window); |
6638 | |
6639 | grab_widget = gtk_window_group_get_current_grab (window_group: group); |
6640 | if (!grab_widget) |
6641 | grab_widget = gtk_pointer_focus_get_implicit_grab (focus); |
6642 | |
6643 | target = gtk_pointer_focus_get_target (focus); |
6644 | |
6645 | if (widget) |
6646 | { |
6647 | /* Check whether the changed widget affects the current cursor |
6648 | * lookups. |
6649 | */ |
6650 | if (grab_widget && grab_widget != widget && |
6651 | !gtk_widget_is_ancestor (widget, ancestor: grab_widget)) |
6652 | continue; |
6653 | if (target != widget && |
6654 | !gtk_widget_is_ancestor (widget: target, ancestor: widget)) |
6655 | continue; |
6656 | } |
6657 | |
6658 | update_cursor (toplevel: focus->toplevel, device: focus->device, grab_widget, target); |
6659 | |
6660 | if (device) |
6661 | break; |
6662 | } |
6663 | } |
6664 | |
6665 | /** |
6666 | * gtk_window_set_child: (attributes org.gtk.Method.set_property=child) |
6667 | * @window: a `GtkWindow` |
6668 | * @child: (nullable): the child widget |
6669 | * |
6670 | * Sets the child widget of @window. |
6671 | */ |
6672 | void |
6673 | gtk_window_set_child (GtkWindow *window, |
6674 | GtkWidget *child) |
6675 | { |
6676 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6677 | |
6678 | g_return_if_fail (GTK_IS_WINDOW (window)); |
6679 | g_return_if_fail (child == NULL || GTK_IS_WIDGET (child)); |
6680 | |
6681 | g_clear_pointer (&priv->child, gtk_widget_unparent); |
6682 | |
6683 | if (child) |
6684 | { |
6685 | priv->child = child; |
6686 | gtk_widget_insert_before (widget: child, GTK_WIDGET (window), next_sibling: priv->title_box); |
6687 | } |
6688 | |
6689 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_CHILD]); |
6690 | } |
6691 | |
6692 | /** |
6693 | * gtk_window_get_child: (attributes org.gtk.Method.get_property=child) |
6694 | * @window: a `GtkWindow` |
6695 | * |
6696 | * Gets the child widget of @window. |
6697 | * |
6698 | * Returns: (nullable) (transfer none): the child widget of @window |
6699 | */ |
6700 | GtkWidget * |
6701 | gtk_window_get_child (GtkWindow *window) |
6702 | { |
6703 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6704 | |
6705 | g_return_val_if_fail (GTK_IS_WINDOW (window), NULL); |
6706 | |
6707 | return priv->child; |
6708 | } |
6709 | |
6710 | /** |
6711 | * gtk_window_destroy: |
6712 | * @window: The window to destroy |
6713 | * |
6714 | * Drop the internal reference GTK holds on toplevel windows. |
6715 | */ |
6716 | void |
6717 | gtk_window_destroy (GtkWindow *window) |
6718 | { |
6719 | guint i; |
6720 | |
6721 | g_return_if_fail (GTK_IS_WINDOW (window)); |
6722 | |
6723 | /* If gtk_window_destroy() has been called before. Can happen |
6724 | * when destroying a dialog manually in a ::close handler for example. */ |
6725 | if (!g_list_store_find (store: toplevel_list, item: window, position: &i)) |
6726 | return; |
6727 | |
6728 | g_object_ref (window); |
6729 | |
6730 | gtk_tooltip_unset_surface (native: GTK_NATIVE (ptr: window)); |
6731 | |
6732 | gtk_window_hide (GTK_WIDGET (window)); |
6733 | gtk_accessible_update_state (self: GTK_ACCESSIBLE (ptr: window), |
6734 | first_state: GTK_ACCESSIBLE_STATE_HIDDEN, TRUE, |
6735 | -1); |
6736 | |
6737 | g_list_store_remove (store: toplevel_list, position: i); |
6738 | |
6739 | gtk_window_release_application (window); |
6740 | |
6741 | gtk_widget_unrealize (GTK_WIDGET (window)); |
6742 | |
6743 | g_object_unref (object: window); |
6744 | } |
6745 | |
6746 | GdkDevice** |
6747 | gtk_window_get_foci_on_widget (GtkWindow *window, |
6748 | GtkWidget *widget, |
6749 | guint *n_devices) |
6750 | { |
6751 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6752 | GPtrArray *array = g_ptr_array_new (); |
6753 | GList *l; |
6754 | |
6755 | for (l = priv->foci; l; l = l->next) |
6756 | { |
6757 | GtkPointerFocus *focus = l->data; |
6758 | GtkWidget *target; |
6759 | |
6760 | target = gtk_pointer_focus_get_effective_target (focus); |
6761 | |
6762 | if (target == widget || gtk_widget_is_ancestor (widget: target, ancestor: widget)) |
6763 | g_ptr_array_add (array, data: focus->device); |
6764 | } |
6765 | |
6766 | if (n_devices) |
6767 | *n_devices = array->len; |
6768 | |
6769 | return (GdkDevice**) g_ptr_array_free (array, FALSE); |
6770 | } |
6771 | |
6772 | static void |
6773 | gtk_synthesize_grab_crossing (GtkWidget *child, |
6774 | GdkDevice *device, |
6775 | GtkWidget *new_grab_widget, |
6776 | GtkWidget *old_grab_widget, |
6777 | gboolean from_grab, |
6778 | gboolean was_shadowed, |
6779 | gboolean is_shadowed) |
6780 | { |
6781 | g_object_ref (child); |
6782 | |
6783 | if (is_shadowed) |
6784 | { |
6785 | if (!was_shadowed && |
6786 | gtk_widget_is_sensitive (widget: child)) |
6787 | _gtk_widget_synthesize_crossing (from: child, |
6788 | to: new_grab_widget, |
6789 | device, |
6790 | mode: GDK_CROSSING_GTK_GRAB); |
6791 | } |
6792 | else |
6793 | { |
6794 | if (was_shadowed && |
6795 | gtk_widget_is_sensitive (widget: child)) |
6796 | _gtk_widget_synthesize_crossing (from: old_grab_widget, to: child, |
6797 | device, |
6798 | mode: from_grab ? GDK_CROSSING_GTK_GRAB : |
6799 | GDK_CROSSING_GTK_UNGRAB); |
6800 | } |
6801 | |
6802 | g_object_unref (object: child); |
6803 | } |
6804 | |
6805 | static void |
6806 | gtk_window_propagate_grab_notify (GtkWindow *window, |
6807 | GtkWidget *target, |
6808 | GdkDevice *device, |
6809 | GtkWidget *old_grab_widget, |
6810 | GtkWidget *new_grab_widget, |
6811 | gboolean from_grab) |
6812 | { |
6813 | GList *l, *widgets = NULL; |
6814 | gboolean was_grabbed = FALSE, is_grabbed = FALSE; |
6815 | |
6816 | while (target) |
6817 | { |
6818 | if (target == old_grab_widget) |
6819 | was_grabbed = TRUE; |
6820 | if (target == new_grab_widget) |
6821 | is_grabbed = TRUE; |
6822 | widgets = g_list_prepend (list: widgets, g_object_ref (target)); |
6823 | target = gtk_widget_get_parent (widget: target); |
6824 | } |
6825 | |
6826 | widgets = g_list_reverse (list: widgets); |
6827 | |
6828 | for (l = widgets; l; l = l->next) |
6829 | { |
6830 | gboolean was_shadowed, is_shadowed; |
6831 | |
6832 | was_shadowed = old_grab_widget && !was_grabbed; |
6833 | is_shadowed = new_grab_widget && !is_grabbed; |
6834 | |
6835 | if (l->data == old_grab_widget) |
6836 | was_grabbed = FALSE; |
6837 | if (l->data == new_grab_widget) |
6838 | is_grabbed = FALSE; |
6839 | |
6840 | if (was_shadowed == is_shadowed) |
6841 | break; |
6842 | |
6843 | gtk_synthesize_grab_crossing (child: l->data, |
6844 | device, |
6845 | new_grab_widget: old_grab_widget, |
6846 | old_grab_widget: new_grab_widget, |
6847 | from_grab, |
6848 | was_shadowed, |
6849 | is_shadowed); |
6850 | |
6851 | gtk_widget_reset_controllers (widget: l->data); |
6852 | } |
6853 | |
6854 | g_list_free_full (list: widgets, free_func: g_object_unref); |
6855 | } |
6856 | |
6857 | void |
6858 | gtk_window_grab_notify (GtkWindow *window, |
6859 | GtkWidget *old_grab_widget, |
6860 | GtkWidget *new_grab_widget, |
6861 | gboolean from_grab) |
6862 | { |
6863 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6864 | GList *l; |
6865 | |
6866 | for (l = priv->foci; l; l = l->next) |
6867 | { |
6868 | GtkPointerFocus *focus = l->data; |
6869 | |
6870 | gtk_window_propagate_grab_notify (window, |
6871 | target: gtk_pointer_focus_get_effective_target (focus), |
6872 | device: focus->device, |
6873 | old_grab_widget, |
6874 | new_grab_widget, |
6875 | from_grab); |
6876 | } |
6877 | } |
6878 | |
6879 | /** |
6880 | * gtk_window_set_handle_menubar_accel: (attributes org.gtk.Method.set_property=handle-menubar-accel) |
6881 | * @window: a `GtkWindow` |
6882 | * @handle_menubar_accel: %TRUE to make @window handle F10 |
6883 | * |
6884 | * Sets whether this window should react to F10 key presses |
6885 | * by activating a menubar it contains. |
6886 | * |
6887 | * Since: 4.2 |
6888 | */ |
6889 | void |
6890 | gtk_window_set_handle_menubar_accel (GtkWindow *window, |
6891 | gboolean handle_menubar_accel) |
6892 | { |
6893 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6894 | GtkPropagationPhase phase; |
6895 | |
6896 | g_return_if_fail (GTK_IS_WINDOW (window)); |
6897 | |
6898 | phase = handle_menubar_accel ? GTK_PHASE_CAPTURE : GTK_PHASE_NONE; |
6899 | |
6900 | if (gtk_event_controller_get_propagation_phase (controller: priv->menubar_controller) == phase) |
6901 | return; |
6902 | |
6903 | gtk_event_controller_set_propagation_phase (controller: priv->menubar_controller, phase); |
6904 | |
6905 | g_object_notify_by_pspec (G_OBJECT (window), pspec: window_props[PROP_HANDLE_MENUBAR_ACCEL]); |
6906 | } |
6907 | |
6908 | /** |
6909 | * gtk_window_get_handle_menubar_accel: (attributes org.gtk.Method.get_property=handle-menubar-accel) |
6910 | * @window: a `GtkWindow` |
6911 | * |
6912 | * Returns whether this window reacts to F10 key presses by |
6913 | * activating a menubar it contains. |
6914 | * |
6915 | * Returns: %TRUE if the window handles F10 |
6916 | * |
6917 | * Since: 4.2 |
6918 | */ |
6919 | gboolean |
6920 | gtk_window_get_handle_menubar_accel (GtkWindow *window) |
6921 | { |
6922 | GtkWindowPrivate *priv = gtk_window_get_instance_private (self: window); |
6923 | GtkPropagationPhase phase; |
6924 | |
6925 | g_return_val_if_fail (GTK_IS_WINDOW (window), TRUE); |
6926 | |
6927 | phase = gtk_event_controller_get_propagation_phase (controller: priv->menubar_controller); |
6928 | |
6929 | return phase == GTK_PHASE_CAPTURE; |
6930 | } |
6931 | |