forked from AuroraMiddleware/gtk
Merge branch 'wip/exalm/headerbar-dragging' into 'master'
Extract GtkWindow dragging and titlebar actions Closes #2689 See merge request GNOME/gtk!1814
This commit is contained in:
commit
eae4a194b8
@ -298,6 +298,7 @@
|
||||
<xi:include href="xml/gtktooltip.xml" />
|
||||
<xi:include href="xml/gtkwidgetpaintable.xml" />
|
||||
<xi:include href="xml/gtkwindowcontrols.xml" />
|
||||
<xi:include href="xml/gtkwindowhandle.xml" />
|
||||
</chapter>
|
||||
|
||||
<chapter id="AbstractObjects">
|
||||
|
@ -4309,6 +4309,24 @@ GTK_WINDOW_CONTROLS_GET_CLASS
|
||||
gtk_window_controls_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkwindowhandle</FILE>
|
||||
<TITLE>GtkWindowHandle</TITLE>
|
||||
GtkWindowHandle
|
||||
gtk_window_handle_new
|
||||
gtk_window_handle_get_child
|
||||
gtk_window_handle_set_child
|
||||
<SUBSECTION Standard>
|
||||
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
|
||||
<SUBSECTION Private>
|
||||
gtk_window_handle_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkmain</FILE>
|
||||
<TITLE>General</TITLE>
|
||||
|
@ -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
|
||||
|
@ -253,6 +253,7 @@
|
||||
#include <gtk/gtkwindow.h>
|
||||
#include <gtk/gtkwindowcontrols.h>
|
||||
#include <gtk/gtkwindowgroup.h>
|
||||
#include <gtk/gtkwindowhandle.h>
|
||||
|
||||
#include <gtk/gtk-autocleanups.h>
|
||||
|
||||
|
@ -21,9 +21,10 @@
|
||||
|
||||
#include "gtkheaderbarprivate.h"
|
||||
|
||||
#include "gtkbinlayout.h"
|
||||
#include "gtkbox.h"
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkcenterlayout.h"
|
||||
#include "gtkcenterbox.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklabel.h"
|
||||
@ -33,6 +34,7 @@
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkwindowcontrols.h"
|
||||
#include "gtkwindowhandle.h"
|
||||
|
||||
#include "a11y/gtkcontaineraccessible.h"
|
||||
|
||||
@ -86,18 +88,21 @@
|
||||
*
|
||||
* |[<!-- language="plain" -->
|
||||
* headerbar
|
||||
* ├── box.start
|
||||
* │ ├── windowcontrols.start
|
||||
* │ ╰── [other children]
|
||||
* ├── [Title Widget]
|
||||
* ╰── box.end
|
||||
* ├── [other children]
|
||||
* ╰── windowcontrols.end
|
||||
* ╰── windowhandle
|
||||
* ╰── box
|
||||
* ├── box.start
|
||||
* │ ├── windowcontrols.start
|
||||
* │ ╰── [other children]
|
||||
* ├── [Title Widget]
|
||||
* ╰── box.end
|
||||
* ├── [other children]
|
||||
* ╰── windowcontrols.end
|
||||
* ]|
|
||||
*
|
||||
* A #GtkHeaderBar's CSS node is called headerbar. It contains two box subnodes
|
||||
* at the start and end of the headerbar, as well as a center node that
|
||||
* represents the title.
|
||||
* A #GtkHeaderBar's CSS node is called headerbar. It contains a windowhandle
|
||||
* subnode, which contains a box subnode, which contains two box subnodes at
|
||||
* the start and end of the headerbar, as well as a center node that represents
|
||||
* the title.
|
||||
*
|
||||
* Each of the boxes contains a windowcontrols subnode, see #GtkWindowControls
|
||||
* for details, as well as other children.
|
||||
@ -120,6 +125,8 @@ struct _GtkHeaderBarClass
|
||||
|
||||
struct _GtkHeaderBarPrivate
|
||||
{
|
||||
GtkWidget *handle;
|
||||
GtkWidget *center_box;
|
||||
GtkWidget *start_box;
|
||||
GtkWidget *end_box;
|
||||
|
||||
@ -184,11 +191,10 @@ static void
|
||||
update_default_decoration (GtkHeaderBar *bar)
|
||||
{
|
||||
GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
|
||||
GtkLayoutManager *layout = gtk_widget_get_layout_manager (GTK_WIDGET (bar));
|
||||
gboolean have_children = FALSE;
|
||||
|
||||
/* Check whether we have any child widgets that we didn't add ourselves */
|
||||
if (gtk_center_layout_get_center_widget (GTK_CENTER_LAYOUT (layout)) != NULL)
|
||||
if (gtk_center_box_get_center_widget (GTK_CENTER_BOX (priv->center_box)) != NULL)
|
||||
{
|
||||
have_children = TRUE;
|
||||
}
|
||||
@ -264,7 +270,6 @@ static void
|
||||
construct_title_label (GtkHeaderBar *bar)
|
||||
{
|
||||
GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
|
||||
GtkLayoutManager *layout = gtk_widget_get_layout_manager (GTK_WIDGET (bar));
|
||||
GtkWidget *label;
|
||||
|
||||
g_assert (priv->title_label == NULL);
|
||||
@ -276,9 +281,7 @@ construct_title_label (GtkHeaderBar *bar)
|
||||
gtk_label_set_single_line_mode (GTK_LABEL (label), TRUE);
|
||||
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
|
||||
gtk_label_set_width_chars (GTK_LABEL (label), MIN_TITLE_CHARS);
|
||||
|
||||
gtk_widget_insert_after (label, GTK_WIDGET (bar), priv->start_box);
|
||||
gtk_center_layout_set_center_widget (GTK_CENTER_LAYOUT (layout), label);
|
||||
gtk_center_box_set_center_widget (GTK_CENTER_BOX (priv->center_box), label);
|
||||
|
||||
priv->title_label = label;
|
||||
|
||||
@ -319,13 +322,11 @@ gtk_header_bar_set_title_widget (GtkHeaderBar *bar,
|
||||
|
||||
if (title_widget != NULL)
|
||||
{
|
||||
GtkLayoutManager *layout = gtk_widget_get_layout_manager (GTK_WIDGET (bar));
|
||||
priv->title_widget = title_widget;
|
||||
|
||||
gtk_widget_insert_after (priv->title_widget, GTK_WIDGET (bar), priv->start_box);
|
||||
gtk_center_layout_set_center_widget (GTK_CENTER_LAYOUT (layout), title_widget);
|
||||
gtk_center_box_set_center_widget (GTK_CENTER_BOX (priv->center_box), title_widget);
|
||||
|
||||
g_clear_pointer (&priv->title_label, gtk_widget_unparent);
|
||||
priv->title_label = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -386,13 +387,14 @@ gtk_header_bar_dispose (GObject *object)
|
||||
{
|
||||
GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (GTK_HEADER_BAR (object));
|
||||
|
||||
g_clear_pointer (&priv->title_widget, gtk_widget_unparent);
|
||||
g_clear_pointer (&priv->title_label, gtk_widget_unparent);
|
||||
priv->title_widget = NULL;
|
||||
priv->title_label = NULL;
|
||||
priv->start_box = NULL;
|
||||
priv->end_box = NULL;
|
||||
|
||||
g_clear_pointer (&priv->handle, gtk_widget_unparent);
|
||||
|
||||
G_OBJECT_CLASS (gtk_header_bar_parent_class)->dispose (object);
|
||||
|
||||
g_clear_pointer (&priv->start_box, gtk_widget_unparent);
|
||||
g_clear_pointer (&priv->end_box, gtk_widget_unparent);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -498,7 +500,6 @@ gtk_header_bar_remove (GtkContainer *container,
|
||||
{
|
||||
GtkHeaderBar *bar = GTK_HEADER_BAR (container);
|
||||
GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
|
||||
GtkLayoutManager *layout = gtk_widget_get_layout_manager (GTK_WIDGET (bar));
|
||||
GtkWidget *parent;
|
||||
gboolean removed = FALSE;
|
||||
|
||||
@ -515,7 +516,7 @@ gtk_header_bar_remove (GtkContainer *container,
|
||||
removed = TRUE;
|
||||
}
|
||||
else if (parent == GTK_WIDGET (container) &&
|
||||
gtk_center_layout_get_center_widget (GTK_CENTER_LAYOUT (layout)) == widget)
|
||||
gtk_center_box_get_center_widget (GTK_CENTER_BOX (priv->center_box)) == widget)
|
||||
{
|
||||
gtk_widget_unparent (widget);
|
||||
removed = TRUE;
|
||||
@ -636,7 +637,7 @@ gtk_header_bar_class_init (GtkHeaderBarClass *class)
|
||||
g_object_class_install_properties (object_class, LAST_PROP, header_bar_props);
|
||||
|
||||
gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_PANEL);
|
||||
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CENTER_LAYOUT);
|
||||
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
|
||||
gtk_widget_class_set_css_name (widget_class, I_("headerbar"));
|
||||
}
|
||||
|
||||
@ -644,21 +645,24 @@ static void
|
||||
gtk_header_bar_init (GtkHeaderBar *bar)
|
||||
{
|
||||
GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
|
||||
GtkLayoutManager *layout;
|
||||
|
||||
priv->title_widget = NULL;
|
||||
priv->decoration_layout = NULL;
|
||||
priv->state = GDK_SURFACE_STATE_WITHDRAWN;
|
||||
|
||||
layout = gtk_widget_get_layout_manager (GTK_WIDGET (bar));
|
||||
priv->handle = gtk_window_handle_new ();
|
||||
gtk_widget_set_parent (priv->handle, GTK_WIDGET (bar));
|
||||
|
||||
priv->center_box = gtk_center_box_new ();
|
||||
gtk_window_handle_set_child (GTK_WINDOW_HANDLE (priv->handle), priv->center_box);
|
||||
|
||||
priv->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_widget_add_css_class (priv->start_box, "start");
|
||||
gtk_widget_set_parent (priv->start_box, GTK_WIDGET (bar));
|
||||
gtk_center_layout_set_start_widget (GTK_CENTER_LAYOUT (layout), priv->start_box);
|
||||
gtk_center_box_set_start_widget (GTK_CENTER_BOX (priv->center_box), priv->start_box);
|
||||
|
||||
priv->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_widget_add_css_class (priv->end_box, "end");
|
||||
gtk_widget_set_parent (priv->end_box, GTK_WIDGET (bar));
|
||||
gtk_center_layout_set_end_widget (GTK_CENTER_LAYOUT (layout), priv->end_box);
|
||||
gtk_center_box_set_end_widget (GTK_CENTER_BOX (priv->center_box), priv->end_box);
|
||||
|
||||
construct_title_label (bar);
|
||||
}
|
||||
|
@ -11440,12 +11440,13 @@ gtk_widget_remove_controller (GtkWidget *widget,
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gtk_widget_consumes_motion (GtkWidget *widget,
|
||||
GdkEventSequence *sequence)
|
||||
gtk_widget_consumes_motion (GtkWidget *widget,
|
||||
GtkWidget *parent,
|
||||
GdkEventSequence *sequence)
|
||||
{
|
||||
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
|
||||
|
||||
while (widget != NULL && !GTK_IS_WINDOW (widget))
|
||||
while (widget != NULL && widget != parent)
|
||||
{
|
||||
guint i;
|
||||
|
||||
|
@ -281,8 +281,9 @@ void _gtk_widget_update_parent_muxer (GtkWidget *widget
|
||||
GtkActionMuxer * _gtk_widget_get_action_muxer (GtkWidget *widget,
|
||||
gboolean create);
|
||||
|
||||
gboolean _gtk_widget_consumes_motion (GtkWidget *widget,
|
||||
GdkEventSequence *sequence);
|
||||
gboolean gtk_widget_consumes_motion (GtkWidget *widget,
|
||||
GtkWidget *parent,
|
||||
GdkEventSequence *sequence);
|
||||
|
||||
gboolean gtk_widget_has_tick_callback (GtkWidget *widget);
|
||||
|
||||
|
540
gtk/gtkwindow.c
540
gtk/gtkwindow.c
@ -29,10 +29,8 @@
|
||||
#include "gtkaccelgroupprivate.h"
|
||||
#include "gtkactionable.h"
|
||||
#include "gtkapplicationprivate.h"
|
||||
#include "gtkbox.h"
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkbuilderprivate.h"
|
||||
#include "gtkbutton.h"
|
||||
#include "gtkcheckbutton.h"
|
||||
#include "gtkcsscornervalueprivate.h"
|
||||
#include "gtkcsscolorvalueprivate.h"
|
||||
@ -42,9 +40,7 @@
|
||||
#include "gtkeventcontrollerlegacy.h"
|
||||
#include "gtkeventcontrollerkey.h"
|
||||
#include "gtkeventcontrollermotion.h"
|
||||
#include "gtkgesturedrag.h"
|
||||
#include "gtkgestureclick.h"
|
||||
#include "gtkgestureprivate.h"
|
||||
#include "gtkheaderbar.h"
|
||||
#include "gtkicontheme.h"
|
||||
#include "gtkintl.h"
|
||||
@ -52,9 +48,6 @@
|
||||
#include "gtkmarshalers.h"
|
||||
#include "gtkmessagedialog.h"
|
||||
#include "gtkpointerfocusprivate.h"
|
||||
#include "gtkpopovermenuprivate.h"
|
||||
#include "gtkmodelbuttonprivate.h"
|
||||
#include "gtkseparator.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkroot.h"
|
||||
#include "gtknative.h"
|
||||
@ -249,8 +242,6 @@ typedef struct
|
||||
GdkSurfaceTypeHint type_hint;
|
||||
|
||||
GtkGesture *click_gesture;
|
||||
GtkGesture *drag_gesture;
|
||||
GtkGesture *bubble_drag_gesture;
|
||||
GtkEventController *key_controller;
|
||||
GtkEventController *application_shortcut_controller;
|
||||
|
||||
@ -328,7 +319,6 @@ typedef enum
|
||||
GTK_WINDOW_REGION_EDGE_S,
|
||||
GTK_WINDOW_REGION_EDGE_SE,
|
||||
GTK_WINDOW_REGION_CONTENT,
|
||||
GTK_WINDOW_REGION_TITLE,
|
||||
} GtkWindowRegion;
|
||||
|
||||
typedef struct
|
||||
@ -486,8 +476,6 @@ static void gtk_window_activate_close (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter);
|
||||
|
||||
static void gtk_window_do_popup (GtkWindow *window,
|
||||
GdkEvent *event);
|
||||
static void gtk_window_css_changed (GtkWidget *widget,
|
||||
GtkCssStyleChange *change);
|
||||
static void gtk_window_state_flags_changed (GtkWidget *widget,
|
||||
@ -1222,66 +1210,6 @@ gtk_window_close (GtkWindow *window)
|
||||
g_object_unref (window);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_window_titlebar_action (GtkWindow *window,
|
||||
GdkEvent *event,
|
||||
guint button,
|
||||
gint n_press)
|
||||
{
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
GtkSettings *settings;
|
||||
gchar *action = NULL;
|
||||
gboolean retval = TRUE;
|
||||
|
||||
settings = gtk_widget_get_settings (GTK_WIDGET (window));
|
||||
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;
|
||||
}
|
||||
|
||||
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"))
|
||||
{
|
||||
/*
|
||||
* gtk header bar won't show the maximize button if the following
|
||||
* properties are not met, apply the same to title bar actions for
|
||||
* consistency.
|
||||
*/
|
||||
if (gtk_window_get_resizable (window))
|
||||
_gtk_window_toggle_maximized (window);
|
||||
}
|
||||
else if (g_str_equal (action, "lower"))
|
||||
gdk_toplevel_lower (GDK_TOPLEVEL (priv->surface));
|
||||
else if (g_str_equal (action, "minimize"))
|
||||
gdk_toplevel_minimize (GDK_TOPLEVEL (priv->surface));
|
||||
else if (g_str_equal (action, "menu"))
|
||||
gtk_window_do_popup (window, event);
|
||||
else
|
||||
{
|
||||
g_warning ("Unsupported titlebar action %s", action);
|
||||
retval = FALSE;
|
||||
}
|
||||
|
||||
g_free (action);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
click_gesture_pressed_cb (GtkGestureClick *gesture,
|
||||
gint n_press,
|
||||
@ -1290,14 +1218,12 @@ click_gesture_pressed_cb (GtkGestureClick *gesture,
|
||||
GtkWindow *window)
|
||||
{
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
GtkWidget *event_widget, *widget;
|
||||
GdkEventSequence *sequence;
|
||||
GtkWindowRegion region;
|
||||
GdkEvent *event;
|
||||
guint button;
|
||||
gboolean window_drag = FALSE;
|
||||
double tx, ty;
|
||||
|
||||
widget = GTK_WIDGET (window);
|
||||
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);
|
||||
@ -1305,192 +1231,32 @@ click_gesture_pressed_cb (GtkGestureClick *gesture,
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
if (n_press > 1)
|
||||
gtk_gesture_set_state (priv->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 (priv->drag_gesture, GTK_EVENT_SEQUENCE_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
region = get_active_region_type (window, x, y);
|
||||
|
||||
if (button == GDK_BUTTON_SECONDARY && region == GTK_WINDOW_REGION_TITLE)
|
||||
{
|
||||
if (gtk_window_titlebar_action (window, 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 (priv->drag_gesture));
|
||||
return;
|
||||
}
|
||||
else if (button == GDK_BUTTON_MIDDLE && region == GTK_WINDOW_REGION_TITLE)
|
||||
{
|
||||
if (gtk_window_titlebar_action (window, event, button, n_press))
|
||||
gtk_gesture_set_sequence_state (GTK_GESTURE (gesture),
|
||||
sequence, GTK_EVENT_SEQUENCE_CLAIMED);
|
||||
return;
|
||||
}
|
||||
else if (button != GDK_BUTTON_PRIMARY)
|
||||
if (button != GDK_BUTTON_PRIMARY)
|
||||
return;
|
||||
|
||||
event_widget = gtk_get_event_widget ((GdkEvent *) event);
|
||||
if (priv->maximized)
|
||||
return;
|
||||
|
||||
if (region == GTK_WINDOW_REGION_TITLE)
|
||||
gtk_window_update_toplevel (window);
|
||||
|
||||
switch (region)
|
||||
{
|
||||
case GTK_WINDOW_REGION_CONTENT:
|
||||
if (event_widget != widget)
|
||||
{
|
||||
/* TODO: Have some way of enabling/disabling window-dragging on random widgets */
|
||||
}
|
||||
|
||||
if (!window_drag)
|
||||
{
|
||||
gtk_gesture_set_sequence_state (GTK_GESTURE (gesture),
|
||||
sequence, GTK_EVENT_SEQUENCE_DENIED);
|
||||
return;
|
||||
}
|
||||
G_GNUC_FALLTHROUGH;
|
||||
|
||||
case GTK_WINDOW_REGION_TITLE:
|
||||
if (n_press == 2)
|
||||
gtk_window_titlebar_action (window, event, button, n_press);
|
||||
|
||||
if (gtk_widget_has_grab (widget))
|
||||
gtk_gesture_set_sequence_state (GTK_GESTURE (gesture),
|
||||
sequence, GTK_EVENT_SEQUENCE_CLAIMED);
|
||||
break;
|
||||
case GTK_WINDOW_REGION_EDGE_NW:
|
||||
case GTK_WINDOW_REGION_EDGE_N:
|
||||
case GTK_WINDOW_REGION_EDGE_NE:
|
||||
case GTK_WINDOW_REGION_EDGE_W:
|
||||
case GTK_WINDOW_REGION_EDGE_E:
|
||||
case GTK_WINDOW_REGION_EDGE_SW:
|
||||
case GTK_WINDOW_REGION_EDGE_S:
|
||||
case GTK_WINDOW_REGION_EDGE_SE:
|
||||
default:
|
||||
if (!priv->maximized)
|
||||
{
|
||||
double tx, ty;
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
|
||||
|
||||
gdk_event_get_position (event, &tx, &ty);
|
||||
gdk_surface_begin_resize_drag (priv->surface,
|
||||
(GdkSurfaceEdge) region,
|
||||
gdk_event_get_device ((GdkEvent *) event),
|
||||
GDK_BUTTON_PRIMARY,
|
||||
tx, ty,
|
||||
gdk_event_get_time (event));
|
||||
|
||||
gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
|
||||
gtk_event_controller_reset (GTK_EVENT_CONTROLLER (priv->drag_gesture));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
drag_gesture_begin_cb (GtkGestureDrag *gesture,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
GtkWindow *window)
|
||||
{
|
||||
GtkWindowRegion region;
|
||||
gboolean widget_drag = FALSE;
|
||||
if (gdk_display_device_is_grabbed (gtk_widget_get_display (GTK_WIDGET (window)),
|
||||
gtk_gesture_get_device (GTK_GESTURE (gesture))))
|
||||
return;
|
||||
|
||||
region = get_active_region_type (window, x, y);
|
||||
|
||||
switch (region)
|
||||
{
|
||||
case GTK_WINDOW_REGION_TITLE:
|
||||
/* Claim it */
|
||||
break;
|
||||
case GTK_WINDOW_REGION_CONTENT:
|
||||
/* TODO: Have some way of enabling/disabling window-dragging on random widgets */
|
||||
if (region == GTK_WINDOW_REGION_CONTENT)
|
||||
return;
|
||||
|
||||
if (!widget_drag)
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
|
||||
|
||||
break;
|
||||
gdk_event_get_position (event, &tx, &ty);
|
||||
gdk_surface_begin_resize_drag (priv->surface,
|
||||
(GdkSurfaceEdge) region,
|
||||
gdk_event_get_device ((GdkEvent *) event),
|
||||
GDK_BUTTON_PRIMARY,
|
||||
tx, ty,
|
||||
gdk_event_get_time (event));
|
||||
|
||||
case GTK_WINDOW_REGION_EDGE_NW:
|
||||
case GTK_WINDOW_REGION_EDGE_N:
|
||||
case GTK_WINDOW_REGION_EDGE_NE:
|
||||
case GTK_WINDOW_REGION_EDGE_W:
|
||||
case GTK_WINDOW_REGION_EDGE_E:
|
||||
case GTK_WINDOW_REGION_EDGE_SW:
|
||||
case GTK_WINDOW_REGION_EDGE_S:
|
||||
case GTK_WINDOW_REGION_EDGE_SE:
|
||||
default:
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
drag_gesture_update_cb (GtkGestureDrag *gesture,
|
||||
gdouble offset_x,
|
||||
gdouble offset_y,
|
||||
GtkWindow *window)
|
||||
{
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
gint double_click_distance;
|
||||
GtkSettings *settings;
|
||||
|
||||
settings = gtk_widget_get_settings (GTK_WIDGET (window));
|
||||
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;
|
||||
gdouble start_x, start_y;
|
||||
|
||||
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 (window) &&
|
||||
!gtk_widget_has_grab (event_widget) &&
|
||||
_gtk_widget_consumes_motion (event_widget, 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);
|
||||
|
||||
gdk_surface_begin_move_drag (priv->surface,
|
||||
gtk_gesture_get_device (GTK_GESTURE (gesture)),
|
||||
gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)),
|
||||
(int)start_x, (int)start_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 (priv->click_gesture));
|
||||
}
|
||||
gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1853,21 +1619,6 @@ gtk_window_init (GtkWindow *window)
|
||||
gtk_widget_add_controller (widget, controller);
|
||||
}
|
||||
|
||||
static GtkGesture *
|
||||
create_drag_gesture (GtkWindow *window)
|
||||
{
|
||||
GtkGesture *gesture;
|
||||
|
||||
gesture = gtk_gesture_drag_new ();
|
||||
g_signal_connect (gesture, "drag-begin",
|
||||
G_CALLBACK (drag_gesture_begin_cb), window);
|
||||
g_signal_connect (gesture, "drag-update",
|
||||
G_CALLBACK (drag_gesture_update_cb), window);
|
||||
gtk_widget_add_controller (GTK_WIDGET (window), GTK_EVENT_CONTROLLER (gesture));
|
||||
|
||||
return gesture;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_window_constructed (GObject *object)
|
||||
{
|
||||
@ -1884,14 +1635,6 @@ gtk_window_constructed (GObject *object)
|
||||
G_CALLBACK (click_gesture_pressed_cb), object);
|
||||
gtk_widget_add_controller (GTK_WIDGET (object), GTK_EVENT_CONTROLLER (priv->click_gesture));
|
||||
|
||||
priv->drag_gesture = create_drag_gesture (window);
|
||||
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->drag_gesture),
|
||||
GTK_PHASE_CAPTURE);
|
||||
|
||||
priv->bubble_drag_gesture = create_drag_gesture (window);
|
||||
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->bubble_drag_gesture),
|
||||
GTK_PHASE_BUBBLE);
|
||||
|
||||
g_list_store_append (toplevel_list, window);
|
||||
g_object_unref (window);
|
||||
}
|
||||
@ -4887,12 +4630,6 @@ gtk_window_unrealize (GtkWidget *widget)
|
||||
|
||||
gsk_renderer_unrealize (priv->renderer);
|
||||
|
||||
if (priv->popup_menu)
|
||||
{
|
||||
gtk_widget_destroy (priv->popup_menu);
|
||||
priv->popup_menu = NULL;
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
gtk_window_unrealize_icon (window);
|
||||
|
||||
@ -5230,7 +4967,6 @@ static GtkWindowRegion
|
||||
get_active_region_type (GtkWindow *window, gint x, gint y)
|
||||
{
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
GtkAllocation allocation;
|
||||
gint i;
|
||||
|
||||
if (priv->client_decorated)
|
||||
@ -5242,16 +4978,6 @@ get_active_region_type (GtkWindow *window, gint x, gint y)
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->title_box != NULL &&
|
||||
gtk_widget_get_visible (priv->title_box) &&
|
||||
gtk_widget_get_child_visible (priv->title_box))
|
||||
{
|
||||
gtk_widget_get_allocation (priv->title_box, &allocation);
|
||||
if (allocation.x <= x && allocation.x + allocation.width > x &&
|
||||
allocation.y <= y && allocation.y + allocation.height > y)
|
||||
return GTK_WINDOW_REGION_TITLE;
|
||||
}
|
||||
|
||||
return GTK_WINDOW_REGION_CONTENT;
|
||||
}
|
||||
|
||||
@ -5749,236 +5475,6 @@ _gtk_window_unset_focus_and_default (GtkWindow *window,
|
||||
g_object_unref (window);
|
||||
}
|
||||
|
||||
static void
|
||||
popup_menu_closed (GtkPopover *popover,
|
||||
GtkWindow *widget)
|
||||
{
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (GTK_WINDOW (widget));
|
||||
|
||||
g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
|
||||
}
|
||||
|
||||
static GdkSurfaceState
|
||||
gtk_window_get_state (GtkWindow *window)
|
||||
{
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
|
||||
if (priv->surface)
|
||||
return gdk_toplevel_get_state (GDK_TOPLEVEL (priv->surface));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
restore_window_clicked (GtkModelButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkWindow *window = GTK_WINDOW (user_data);
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
GdkSurfaceState state;
|
||||
|
||||
if (priv->maximized)
|
||||
{
|
||||
gtk_window_unmaximize (window);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
state = gtk_window_get_state (window);
|
||||
|
||||
if (state & GDK_SURFACE_STATE_MINIMIZED)
|
||||
gtk_window_unminimize (window);
|
||||
}
|
||||
|
||||
static void
|
||||
move_window_clicked (GtkModelButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkWindow *window = GTK_WINDOW (user_data);
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
|
||||
gdk_surface_begin_move_drag (priv->surface,
|
||||
NULL,
|
||||
0, /* 0 means "use keyboard" */
|
||||
0, 0,
|
||||
GDK_CURRENT_TIME);
|
||||
}
|
||||
|
||||
static void
|
||||
resize_window_clicked (GtkModelButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkWindow *window = GTK_WINDOW (user_data);
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
|
||||
gdk_surface_begin_resize_drag (priv->surface,
|
||||
0,
|
||||
NULL,
|
||||
0, /* 0 means "use keyboard" */
|
||||
0, 0,
|
||||
GDK_CURRENT_TIME);
|
||||
}
|
||||
|
||||
static void
|
||||
minimize_window_clicked (GtkModelButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkWindow *window = GTK_WINDOW (user_data);
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
|
||||
/* Turns out, we can't minimize a maximized window */
|
||||
if (priv->maximized)
|
||||
gtk_window_unmaximize (window);
|
||||
|
||||
gtk_window_minimize (window);
|
||||
}
|
||||
|
||||
static void
|
||||
maximize_window_clicked (GtkModelButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
gtk_window_maximize (GTK_WINDOW (user_data));
|
||||
}
|
||||
|
||||
static void
|
||||
ontop_window_clicked (GtkModelButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkWindow *window = (GtkWindow *)user_data;
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
|
||||
gtk_popover_popdown (GTK_POPOVER (priv->popup_menu));
|
||||
}
|
||||
|
||||
static void
|
||||
close_window_clicked (GtkModelButton *button,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkWindow *window = (GtkWindow *)user_data;
|
||||
|
||||
gtk_window_close (window);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_window_do_popup_fallback (GtkWindow *window,
|
||||
GdkEvent *event)
|
||||
{
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
GtkWidget *menuitem;
|
||||
GdkSurfaceState state;
|
||||
gboolean maximized, minimized;
|
||||
GtkWidget *box;
|
||||
|
||||
if (priv->popup_menu)
|
||||
gtk_widget_destroy (priv->popup_menu);
|
||||
|
||||
state = gtk_window_get_state (window);
|
||||
|
||||
minimized = (state & GDK_SURFACE_STATE_MINIMIZED) == GDK_SURFACE_STATE_MINIMIZED;
|
||||
maximized = priv->maximized && !minimized;
|
||||
|
||||
priv->popup_menu = gtk_popover_menu_new ();
|
||||
gtk_widget_set_parent (priv->popup_menu, priv->title_box);
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_popover_menu_add_submenu (GTK_POPOVER_MENU (priv->popup_menu), box, "main");
|
||||
|
||||
menuitem = gtk_model_button_new ();
|
||||
g_object_set (menuitem, "text", _("Restore"), NULL);
|
||||
|
||||
/* "Restore" means "Unmaximize" or "Unminimize"
|
||||
* (yes, some WMs allow window menu to be shown for minimized windows).
|
||||
* Not restorable:
|
||||
* - visible windows that are not maximized or minimized
|
||||
* - non-resizable windows that are not minimized
|
||||
* - non-normal windows
|
||||
*/
|
||||
if ((gtk_widget_is_visible (GTK_WIDGET (window)) &&
|
||||
!(maximized || minimized)) ||
|
||||
(!minimized && !priv->resizable))
|
||||
gtk_widget_set_sensitive (menuitem, FALSE);
|
||||
g_signal_connect (G_OBJECT (menuitem), "clicked",
|
||||
G_CALLBACK (restore_window_clicked), window);
|
||||
gtk_container_add (GTK_CONTAINER (box), menuitem);
|
||||
|
||||
menuitem = gtk_model_button_new ();
|
||||
g_object_set (menuitem, "text", _("Move"), NULL);
|
||||
|
||||
if (maximized || minimized)
|
||||
gtk_widget_set_sensitive (menuitem, FALSE);
|
||||
g_signal_connect (G_OBJECT (menuitem), "clicked",
|
||||
G_CALLBACK (move_window_clicked), window);
|
||||
gtk_container_add (GTK_CONTAINER (box), menuitem);
|
||||
|
||||
menuitem = gtk_model_button_new ();
|
||||
g_object_set (menuitem, "text", _("Resize"), NULL);
|
||||
|
||||
if (!priv->resizable || maximized || minimized)
|
||||
gtk_widget_set_sensitive (menuitem, FALSE);
|
||||
g_signal_connect (G_OBJECT (menuitem), "clicked",
|
||||
G_CALLBACK (resize_window_clicked), window);
|
||||
gtk_container_add (GTK_CONTAINER (box), menuitem);
|
||||
|
||||
menuitem = gtk_model_button_new ();
|
||||
g_object_set (menuitem, "text", _("Minimize"), NULL);
|
||||
|
||||
if (minimized)
|
||||
gtk_widget_set_sensitive (menuitem, FALSE);
|
||||
g_signal_connect (G_OBJECT (menuitem), "clicked",
|
||||
G_CALLBACK (minimize_window_clicked), window);
|
||||
gtk_container_add (GTK_CONTAINER (box), menuitem);
|
||||
|
||||
menuitem = gtk_model_button_new ();
|
||||
g_object_set (menuitem, "text", _("Maximize"), NULL);
|
||||
|
||||
if (maximized || !priv->resizable)
|
||||
gtk_widget_set_sensitive (menuitem, FALSE);
|
||||
g_signal_connect (G_OBJECT (menuitem), "clicked",
|
||||
G_CALLBACK (maximize_window_clicked), window);
|
||||
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", _("Always on Top"),
|
||||
"role", GTK_BUTTON_ROLE_CHECK,
|
||||
NULL);
|
||||
|
||||
if (maximized)
|
||||
gtk_widget_set_sensitive (menuitem, FALSE);
|
||||
g_signal_connect (G_OBJECT (menuitem), "clicked",
|
||||
G_CALLBACK (ontop_window_clicked), window);
|
||||
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);
|
||||
|
||||
if (!priv->deletable)
|
||||
gtk_widget_set_sensitive (menuitem, FALSE);
|
||||
g_signal_connect (G_OBJECT (menuitem), "clicked",
|
||||
G_CALLBACK (close_window_clicked), window);
|
||||
gtk_container_add (GTK_CONTAINER (box), menuitem);
|
||||
|
||||
g_signal_connect (priv->popup_menu, "closed",
|
||||
G_CALLBACK (popup_menu_closed), window);
|
||||
gtk_popover_popup (GTK_POPOVER (priv->popup_menu));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_window_do_popup (GtkWindow *window,
|
||||
GdkEvent *event)
|
||||
{
|
||||
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
|
||||
|
||||
if (!gdk_toplevel_show_window_menu (GDK_TOPLEVEL (priv->surface), event))
|
||||
gtk_window_do_popup_fallback (window, event);
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* Functions related to resizing *
|
||||
*********************************/
|
||||
|
687
gtk/gtkwindowhandle.c
Normal file
687
gtk/gtkwindowhandle.c
Normal file
@ -0,0 +1,687 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Alexander Mikhaylenko <alexm@gnome.org>
|
||||
*
|
||||
* 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]);
|
||||
}
|
44
gtk/gtkwindowhandle.h
Normal file
44
gtk/gtkwindowhandle.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Alexander Mikhaylenko <alexm@gnome.org>
|
||||
*
|
||||
* 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 <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
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
|
@ -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',
|
||||
|
@ -1391,8 +1391,8 @@ headerbar {
|
||||
transition: $backdrop_transition;
|
||||
}
|
||||
|
||||
> box.start,
|
||||
> box.end {
|
||||
box.start,
|
||||
box.end {
|
||||
border-spacing: 6px;
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,21 @@
|
||||
window.background.csd:dir(ltr)
|
||||
decoration:dir(ltr)
|
||||
headerbar.titlebar:dir(ltr)
|
||||
box.horizontal.start:dir(ltr)
|
||||
stackswitcher.linked:dir(ltr)
|
||||
button.text-button.toggle:dir(ltr):checked
|
||||
color: rgb(255,192,203); /* bloomfilter-not.css:1:61-73 */
|
||||
windowhandle:dir(ltr)
|
||||
box:dir(ltr)
|
||||
box.horizontal.start:dir(ltr)
|
||||
stackswitcher.linked:dir(ltr)
|
||||
button.text-button.toggle:dir(ltr):checked
|
||||
color: rgb(255,192,203); /* bloomfilter-not.css:1:61-73 */
|
||||
|
||||
label:dir(ltr)
|
||||
button.text-button.toggle:dir(ltr)
|
||||
color: rgb(255,192,203); /* bloomfilter-not.css:1:61-73 */
|
||||
label:dir(ltr)
|
||||
button.text-button.toggle:dir(ltr)
|
||||
color: rgb(255,192,203); /* bloomfilter-not.css:1:61-73 */
|
||||
|
||||
label:dir(ltr)
|
||||
button.text-button.toggle:dir(ltr)
|
||||
label:dir(ltr)
|
||||
box.end.horizontal:dir(ltr)
|
||||
label:dir(ltr)
|
||||
button.text-button.toggle:dir(ltr)
|
||||
label:dir(ltr)
|
||||
box.end.horizontal:dir(ltr)
|
||||
stack:dir(ltr)
|
||||
box.horizontal:dir(ltr)
|
||||
box.horizontal:dir(ltr)
|
||||
|
Loading…
Reference in New Issue
Block a user