diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 63cc7d7579..6fcf4d2540 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -298,6 +298,7 @@ + diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 6a3f16b13a..a3ee19ba46 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -4309,6 +4309,24 @@ GTK_WINDOW_CONTROLS_GET_CLASS gtk_window_controls_get_type +
+gtkwindowhandle +GtkWindowHandle +GtkWindowHandle +gtk_window_handle_new +gtk_window_handle_get_child +gtk_window_handle_set_child + +GTK_WINDOW_HANDLE +GTK_IS_WINDOW_HANDLE +GTK_TYPE_WINDOW_HANDLE +GTK_WINDOW_HANDLE_CLASS +GTK_IS_WINDOW_HANDLE_CLASS +GTK_WINDOW_HANDLE_GET_CLASS + +gtk_window_handle_get_type +
+
gtkmain General diff --git a/docs/reference/gtk/gtk4.types.in b/docs/reference/gtk/gtk4.types.in index 6fa275416c..8001a45b68 100644 --- a/docs/reference/gtk/gtk4.types.in +++ b/docs/reference/gtk/gtk4.types.in @@ -215,3 +215,4 @@ gtk_widget_get_type gtk_window_get_type gtk_window_controls_get_type gtk_window_group_get_type +gtk_window_handle_get_type diff --git a/gtk/gtk.h b/gtk/gtk.h index e9bf5e1cff..ca76e135fc 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -253,6 +253,7 @@ #include #include #include +#include #include diff --git a/gtk/gtkwindowhandle.c b/gtk/gtkwindowhandle.c new file mode 100644 index 0000000000..e2817a235a --- /dev/null +++ b/gtk/gtkwindowhandle.c @@ -0,0 +1,687 @@ +/* + * Copyright (c) 2020 Alexander Mikhaylenko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "gtkwindowhandle.h" + +#include "gtkbinlayout.h" +#include "gtkbox.h" +#include "gtkbuildable.h" +#include "gtkgestureclick.h" +#include "gtkgesturedrag.h" +#include "gtkgestureprivate.h" +#include "gtkintl.h" +#include "gtkmodelbuttonprivate.h" +#include "gtknative.h" +#include "gtkpopovermenuprivate.h" +#include "gtkprivate.h" +#include "gtkseparator.h" +#include "gtkwidgetprivate.h" + +/** + * SECTION:gtkwindowhandle + * @Short_description: A titlebar area widget + * @Title: GtkWindowHandle + * @See_also: #GtkWindow, #GtkHeaderBar + * + * GtkWindowHandle is a titlebar area widget. When added into a window, it can + * be dragged to move the window, and handles right click double click and + * middle click as expected of a titlebar. + * + * # CSS nodes + * + * #GtkWindowHandle has a single CSS node with the name windowhandle. + */ + +struct _GtkWindowHandle { + GtkWidget parent_instance; + + GtkGesture *click_gesture; + GtkGesture *drag_gesture; + GtkGesture *bubble_drag_gesture; + + GtkWidget *child; + GtkWidget *fallback_menu; +}; + +enum { + PROP_0, + PROP_CHILD, + LAST_PROP +}; + +static GParamSpec *props[LAST_PROP] = { NULL, }; + +static void gtk_window_handle_buildable_iface_init (GtkBuildableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GtkWindowHandle, gtk_window_handle, GTK_TYPE_WIDGET, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_window_handle_buildable_iface_init)) + +static void +lower_window (GtkWindowHandle *self) +{ + GdkSurface *surface = + gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (self))); + + gdk_toplevel_lower (GDK_TOPLEVEL (surface)); +} + +static GtkWindow * +get_window (GtkWindowHandle *self) +{ + GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (self)); + + if (GTK_IS_WINDOW (root)) + return GTK_WINDOW (root); + + return NULL; +} + +static void +restore_window_clicked (GtkModelButton *button, + GtkWindowHandle *self) +{ + GtkWindow *window = get_window (self); + + if (!window) + return; + + if (gtk_window_is_maximized (window)) + gtk_window_unmaximize (window); +} + +static void +move_window_clicked (GtkModelButton *button, + GtkWindowHandle *self) +{ + GtkNative *native = gtk_widget_get_native (GTK_WIDGET (self)); + GdkSurface *surface = gtk_native_get_surface (native); + + gdk_surface_begin_move_drag (surface, + NULL, + 0, /* 0 means "use keyboard" */ + 0, 0, + GDK_CURRENT_TIME); +} + +static void +resize_window_clicked (GtkModelButton *button, + GtkWindowHandle *self) +{ + GtkNative *native = gtk_widget_get_native (GTK_WIDGET (self)); + GdkSurface *surface = gtk_native_get_surface (native); + + gdk_surface_begin_resize_drag (surface, + 0, + NULL, + 0, /* 0 means "use keyboard" */ + 0, 0, + GDK_CURRENT_TIME); +} + +static void +minimize_window_clicked (GtkModelButton *button, + GtkWindowHandle *self) +{ + GtkWindow *window = get_window (self); + + if (!window) + return; + + /* Turns out, we can't minimize a maximized window */ + if (gtk_window_is_maximized (window)) + gtk_window_unmaximize (window); + + gtk_window_minimize (window); +} + +static void +maximize_window_clicked (GtkModelButton *button, + GtkWindowHandle *self) +{ + GtkWindow *window = get_window (self); + + if (window) + gtk_window_maximize (window); +} + +static void +close_window_clicked (GtkModelButton *button, + GtkWindowHandle *self) +{ + GtkWindow *window = get_window (self); + + if (window) + gtk_window_close (window); +} + +static void +popup_menu_closed (GtkPopover *popover, + GtkWindowHandle *self) +{ + g_clear_pointer (&self->fallback_menu, gtk_widget_unparent); +} + +static void +do_popup_fallback (GtkWindowHandle *self, + GdkEvent *event) +{ + GdkRectangle rect = { 0, 0, 1, 1 }; + GdkDevice *device; + GtkWidget *box, *menuitem; + GtkWindow *window; + gboolean maximized, resizable, deletable; + + g_clear_pointer (&self->fallback_menu, gtk_widget_destroy); + + window = get_window (self); + + if (window) + { + maximized = gtk_window_is_maximized (window); + resizable = gtk_window_get_resizable (window); + deletable = gtk_window_get_deletable (window); + } + else + { + maximized = FALSE; + resizable = FALSE; + deletable = FALSE; + } + + self->fallback_menu = gtk_popover_menu_new (); + gtk_widget_set_parent (self->fallback_menu, GTK_WIDGET (self)); + + gtk_popover_set_has_arrow (GTK_POPOVER (self->fallback_menu), FALSE); + gtk_widget_set_halign (self->fallback_menu, GTK_ALIGN_START); + + + device = gdk_event_get_device (event); + + if (device && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) + device = gdk_device_get_associated_device (device); + + if (device) + { + GdkSurface *surface; + double px, py; + + surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (self))); + gdk_surface_get_device_position (surface, device, &px, &py, NULL); + rect.x = round (px); + rect.y = round (py); + + gtk_widget_translate_coordinates (GTK_WIDGET (gtk_widget_get_native (GTK_WIDGET (self))), + GTK_WIDGET (self), + rect.x, rect.y, + &rect.x, &rect.y); + } + + gtk_popover_set_pointing_to (GTK_POPOVER (self->fallback_menu), &rect); + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_popover_menu_add_submenu (GTK_POPOVER_MENU (self->fallback_menu), box, "main"); + + menuitem = gtk_model_button_new (); + g_object_set (menuitem, "text", _("Restore"), NULL); + gtk_widget_set_sensitive (menuitem, maximized && resizable); + g_signal_connect (G_OBJECT (menuitem), "clicked", + G_CALLBACK (restore_window_clicked), self); + gtk_container_add (GTK_CONTAINER (box), menuitem); + + menuitem = gtk_model_button_new (); + g_object_set (menuitem, "text", _("Move"), NULL); + gtk_widget_set_sensitive (menuitem, !maximized); + g_signal_connect (G_OBJECT (menuitem), "clicked", + G_CALLBACK (move_window_clicked), self); + gtk_container_add (GTK_CONTAINER (box), menuitem); + + menuitem = gtk_model_button_new (); + g_object_set (menuitem, "text", _("Resize"), NULL); + gtk_widget_set_sensitive (menuitem, resizable && !maximized); + g_signal_connect (G_OBJECT (menuitem), "clicked", + G_CALLBACK (resize_window_clicked), self); + gtk_container_add (GTK_CONTAINER (box), menuitem); + + menuitem = gtk_model_button_new (); + g_object_set (menuitem, "text", _("Minimize"), NULL); + g_signal_connect (G_OBJECT (menuitem), "clicked", + G_CALLBACK (minimize_window_clicked), self); + gtk_container_add (GTK_CONTAINER (box), menuitem); + + menuitem = gtk_model_button_new (); + g_object_set (menuitem, "text", _("Maximize"), NULL); + gtk_widget_set_sensitive (menuitem, resizable && !maximized); + g_signal_connect (G_OBJECT (menuitem), "clicked", + G_CALLBACK (maximize_window_clicked), self); + gtk_container_add (GTK_CONTAINER (box), menuitem); + + menuitem = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + gtk_container_add (GTK_CONTAINER (box), menuitem); + + menuitem = gtk_model_button_new (); + g_object_set (menuitem, "text", _("Close"), NULL); + gtk_widget_set_sensitive (menuitem, deletable); + g_signal_connect (G_OBJECT (menuitem), "clicked", + G_CALLBACK (close_window_clicked), self); + gtk_container_add (GTK_CONTAINER (box), menuitem); + + g_signal_connect (self->fallback_menu, "closed", + G_CALLBACK (popup_menu_closed), self); + gtk_popover_popup (GTK_POPOVER (self->fallback_menu)); +} + +static void +do_popup (GtkWindowHandle *self, + GdkEvent *event) +{ + GdkSurface *surface = + gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (self))); + + if (!gdk_toplevel_show_window_menu (GDK_TOPLEVEL (surface), event)) + do_popup_fallback (self, event); +} + +static gboolean +perform_titlebar_action (GtkWindowHandle *self, + GdkEvent *event, + guint button, + gint n_press) +{ + GtkSettings *settings; + gchar *action = NULL; + gboolean retval = TRUE; + GtkActionMuxer *context; + + settings = gtk_widget_get_settings (GTK_WIDGET (self)); + switch (button) + { + case GDK_BUTTON_PRIMARY: + if (n_press == 2) + g_object_get (settings, "gtk-titlebar-double-click", &action, NULL); + break; + case GDK_BUTTON_MIDDLE: + g_object_get (settings, "gtk-titlebar-middle-click", &action, NULL); + break; + case GDK_BUTTON_SECONDARY: + g_object_get (settings, "gtk-titlebar-right-click", &action, NULL); + break; + default: + break; + } + + context = _gtk_widget_get_action_muxer (GTK_WIDGET (self), TRUE); + + if (action == NULL) + retval = FALSE; + else if (g_str_equal (action, "none")) + retval = FALSE; + /* treat all maximization variants the same */ + else if (g_str_has_prefix (action, "toggle-maximize")) + g_action_group_activate_action (G_ACTION_GROUP (context), + "window.toggle-maximized", + NULL); + else if (g_str_equal (action, "lower")) + lower_window (self); + else if (g_str_equal (action, "minimize")) + g_action_group_activate_action (G_ACTION_GROUP (context), + "window.minimize", + NULL); + else if (g_str_equal (action, "menu")) + do_popup (self, event); + else + { + g_warning ("Unsupported titlebar action %s", action); + retval = FALSE; + } + + g_free (action); + + return retval; +} + +static void +click_gesture_pressed_cb (GtkGestureClick *gesture, + int n_press, + double x, + double y, + GtkWindowHandle *self) +{ + GtkWidget *widget; + GdkEventSequence *sequence; + GdkEvent *event; + guint button; + GtkRoot *root; + + widget = GTK_WIDGET (self); + sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); + button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); + event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); + root = gtk_widget_get_root (widget); + + if (!event) + return; + + if (n_press > 1) + gtk_gesture_set_state (self->drag_gesture, GTK_EVENT_SEQUENCE_DENIED); + + if (gdk_display_device_is_grabbed (gtk_widget_get_display (widget), + gtk_gesture_get_device (GTK_GESTURE (gesture)))) + { + gtk_gesture_set_state (self->drag_gesture, GTK_EVENT_SEQUENCE_DENIED); + return; + } + + switch (button) + { + case GDK_BUTTON_PRIMARY: + if (n_press == 2) + perform_titlebar_action (self, event, button, n_press); + + if (gtk_widget_has_grab (GTK_WIDGET (root))) + gtk_gesture_set_sequence_state (GTK_GESTURE (gesture), + sequence, GTK_EVENT_SEQUENCE_CLAIMED); + break; + + case GDK_BUTTON_SECONDARY: + if (perform_titlebar_action (self, event, button, n_press)) + gtk_gesture_set_sequence_state (GTK_GESTURE (gesture), + sequence, GTK_EVENT_SEQUENCE_CLAIMED); + + gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture)); + gtk_event_controller_reset (GTK_EVENT_CONTROLLER (self->drag_gesture)); + break; + + case GDK_BUTTON_MIDDLE: + if (perform_titlebar_action (self, event, button, n_press)) + gtk_gesture_set_sequence_state (GTK_GESTURE (gesture), + sequence, GTK_EVENT_SEQUENCE_CLAIMED); + break; + + default: + return; + } +} + +static void +drag_gesture_update_cb (GtkGestureDrag *gesture, + double offset_x, + double offset_y, + GtkWindowHandle *self) +{ + int double_click_distance; + GtkSettings *settings; + + settings = gtk_widget_get_settings (GTK_WIDGET (self)); + g_object_get (settings, + "gtk-double-click-distance", &double_click_distance, + NULL); + + if (ABS (offset_x) > double_click_distance || + ABS (offset_y) > double_click_distance) + { + GdkEventSequence *sequence; + double start_x, start_y; + gint window_x, window_y; + GtkNative *native; + GdkSurface *surface; + + sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); + + if (gtk_event_controller_get_propagation_phase (GTK_EVENT_CONTROLLER (gesture)) == GTK_PHASE_CAPTURE) + { + GtkWidget *event_widget = gtk_gesture_get_last_target (GTK_GESTURE (gesture), sequence); + + /* Check whether the target widget should be left alone at handling + * the sequence, this is better done late to give room for gestures + * there to go denied. + * + * Besides claiming gestures, we must bail out too if there's gestures + * in the "none" state at this point, as those are still handling events + * and can potentially go claimed, and we don't want to stop the target + * widget from doing anything. + */ + if (event_widget != GTK_WIDGET (self) && + !gtk_widget_has_grab (event_widget) && + gtk_widget_consumes_motion (event_widget, GTK_WIDGET (self), sequence)) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + return; + } + } + + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + + gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y); + + native = gtk_widget_get_native (GTK_WIDGET (self)); + gtk_widget_translate_coordinates (GTK_WIDGET (self), + GTK_WIDGET (native), + start_x, start_y, + &window_x, &window_y); + + surface = gtk_native_get_surface (native); + gdk_surface_begin_move_drag (surface, + gtk_gesture_get_device (GTK_GESTURE (gesture)), + gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)), + window_x, window_y, + gtk_event_controller_get_current_event_time (GTK_EVENT_CONTROLLER (gesture))); + + gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture)); + gtk_event_controller_reset (GTK_EVENT_CONTROLLER (self->click_gesture)); + } +} + +static GtkGesture * +create_drag_gesture (GtkWindowHandle *self) +{ + GtkGesture *gesture; + + gesture = gtk_gesture_drag_new (); + g_signal_connect (gesture, "drag-update", + G_CALLBACK (drag_gesture_update_cb), self); + gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture)); + + return gesture; +} + +static void +gtk_window_handle_unrealize (GtkWidget *widget) +{ + GtkWindowHandle *self = GTK_WINDOW_HANDLE (widget); + + g_clear_pointer (&self->fallback_menu, gtk_widget_destroy); + + GTK_WIDGET_CLASS (gtk_window_handle_parent_class)->unrealize (widget); +} + +static void +gtk_window_handle_dispose (GObject *object) +{ + GtkWindowHandle *self = GTK_WINDOW_HANDLE (object); + + g_clear_pointer (&self->child, gtk_widget_unparent); + + G_OBJECT_CLASS (gtk_window_handle_parent_class)->dispose (object); +} + +static void +gtk_window_handle_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkWindowHandle *self = GTK_WINDOW_HANDLE (object); + + switch (prop_id) + { + case PROP_CHILD: + g_value_set_object (value, gtk_window_handle_get_child (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_window_handle_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkWindowHandle *self = GTK_WINDOW_HANDLE (object); + + switch (prop_id) + { + case PROP_CHILD: + gtk_window_handle_set_child (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_window_handle_class_init (GtkWindowHandleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = gtk_window_handle_dispose; + object_class->get_property = gtk_window_handle_get_property; + object_class->set_property = gtk_window_handle_set_property; + + widget_class->unrealize = gtk_window_handle_unrealize; + widget_class->grab_focus = gtk_widget_grab_focus_none; + widget_class->focus = gtk_widget_focus_child; + + props[PROP_CHILD] = + g_param_spec_object ("child", + P_("Child"), + P_("The child widget"), + GTK_TYPE_WIDGET, + GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, LAST_PROP, props); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + gtk_widget_class_set_css_name (widget_class, I_("windowhandle")); +} + +static void +gtk_window_handle_init (GtkWindowHandle *self) +{ + self->click_gesture = gtk_gesture_click_new (); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->click_gesture), 0); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->click_gesture), + GTK_PHASE_BUBBLE); + g_signal_connect (self->click_gesture, "pressed", + G_CALLBACK (click_gesture_pressed_cb), self); + gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (self->click_gesture)); + + self->drag_gesture = create_drag_gesture (self); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->drag_gesture), + GTK_PHASE_CAPTURE); + + self->bubble_drag_gesture = create_drag_gesture (self); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->bubble_drag_gesture), + GTK_PHASE_BUBBLE); +} + +static GtkBuildableIface *parent_buildable_iface; + +static void +gtk_window_handle_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + if (GTK_IS_WIDGET (child)) + gtk_window_handle_set_child (GTK_WINDOW_HANDLE (buildable), GTK_WIDGET (child)); + else + parent_buildable_iface->add_child (buildable, builder, child, type); +} + +static void +gtk_window_handle_buildable_iface_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + + iface->add_child = gtk_window_handle_buildable_add_child; +} + +/** + * gtk_window_handle_new: + * + * Creates a new #GtkWindowHandle. + * + * Returns: a new #GtkWindowHandle. + **/ +GtkWidget * +gtk_window_handle_new (void) +{ + return g_object_new (GTK_TYPE_WINDOW_HANDLE, NULL); +} + +/** + * gtk_window_handle_get_child: + * @self: a #GtkWindowHandle + * + * Gets the child widget of @self. + * + * Returns: (nullable) (transfer none): the child widget of @self + */ +GtkWidget * +gtk_window_handle_get_child (GtkWindowHandle *self) +{ + g_return_val_if_fail (GTK_IS_WINDOW_HANDLE (self), NULL); + + return self->child; +} + +/** + * gtk_window_handle_set_child: + * @self: a #GtkWindowHandle + * @child: (allow-none): the child widget + * + * Sets the child widget of @self. + */ +void +gtk_window_handle_set_child (GtkWindowHandle *self, + GtkWidget *child) +{ + g_return_if_fail (GTK_IS_WINDOW_HANDLE (self)); + g_return_if_fail (child == NULL || GTK_IS_WIDGET (child)); + + if (self->child == child) + return; + + g_clear_pointer (&self->child, gtk_widget_unparent); + + self->child = child; + + if (child) + gtk_widget_set_parent (child, GTK_WIDGET (self)); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CHILD]); +} diff --git a/gtk/gtkwindowhandle.h b/gtk/gtkwindowhandle.h new file mode 100644 index 0000000000..1a64d08062 --- /dev/null +++ b/gtk/gtkwindowhandle.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Alexander Mikhaylenko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_WINDOW_HANDLE (gtk_window_handle_get_type ()) + +GDK_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (GtkWindowHandle, gtk_window_handle, GTK, WINDOW_HANDLE, GtkWidget) + +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_window_handle_new (void); + +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_window_handle_get_child (GtkWindowHandle *self); + +GDK_AVAILABLE_IN_ALL +void gtk_window_handle_set_child (GtkWindowHandle *self, + GtkWidget *child); + +G_END_DECLS diff --git a/gtk/meson.build b/gtk/meson.build index 3307c1aac1..8287477888 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -391,6 +391,7 @@ gtk_public_sources = files([ 'gtkwindow.c', 'gtkwindowcontrols.c', 'gtkwindowgroup.c', + 'gtkwindowhandle.c', ]) gtk_private_type_headers = files([ @@ -621,6 +622,7 @@ gtk_public_headers = files([ 'gtkwindow.h', 'gtkwindowcontrols.h', 'gtkwindowgroup.h', + 'gtkwindowhandle.h', 'gtk-a11y.h', 'gtk-autocleanups.h', 'gtk.h',