From dfbde3da4f53b750c2dbe321ac1b70f6063241fa Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 29 Feb 2020 10:07:43 -0500 Subject: [PATCH] Introduce GdkToplevel This is a new interface for toplevel surfaces. --- gdk/gdk.h | 14 +- gdk/gdksurface.c | 268 ++++++++++++++++++-- gdk/gdktoplevel.c | 518 +++++++++++++++++++++++++++++++++++++++ gdk/gdktoplevel.h | 108 ++++++++ gdk/gdktoplevelprivate.h | 48 ++++ gdk/meson.build | 2 + 6 files changed, 937 insertions(+), 21 deletions(-) create mode 100644 gdk/gdktoplevel.c create mode 100644 gdk/gdktoplevel.h create mode 100644 gdk/gdktoplevelprivate.h diff --git a/gdk/gdk.h b/gdk/gdk.h index a4f434511e..30ab0d7590 100644 --- a/gdk/gdk.h +++ b/gdk/gdk.h @@ -27,12 +27,11 @@ #define __GDK_H_INSIDE__ -#include -#include #include #include #include #include +#include #include #include #include @@ -60,16 +59,19 @@ #include #include #include +#include +#include #include #include #include #include -#include -#include -#include #include -#include +#include +#include #include +#include +#include +#include #include diff --git a/gdk/gdksurface.c b/gdk/gdksurface.c index 70a62487d2..6b822ffb2f 100644 --- a/gdk/gdksurface.c +++ b/gdk/gdksurface.c @@ -40,6 +40,7 @@ #include "gdkmarshalers.h" #include "gdkpopupprivate.h" #include "gdkrectangle.h" +#include "gdktoplevelprivate.h" #include @@ -83,7 +84,6 @@ enum { PROP_CURSOR, PROP_DISPLAY, PROP_FRAME_CLOCK, - PROP_STATE, PROP_MAPPED, LAST_PROP }; @@ -112,10 +112,13 @@ static guint signals[LAST_SIGNAL] = { 0 }; static GParamSpec *properties[LAST_PROP] = { NULL, }; static void gdk_surface_popup_init (GdkPopupInterface *iface); +static void gdk_surface_toplevel_init (GdkToplevelInterface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkSurface, gdk_surface, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GDK_TYPE_POPUP, - gdk_surface_popup_init)) + gdk_surface_popup_init) + G_IMPLEMENT_INTERFACE (GDK_TYPE_TOPLEVEL, + gdk_surface_toplevel_init)) static gboolean gdk_surface_real_beep (GdkSurface *surface) @@ -443,13 +446,6 @@ gdk_surface_class_init (GdkSurfaceClass *klass) GDK_TYPE_FRAME_CLOCK, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - properties[PROP_STATE] = - g_param_spec_flags ("state", - P_("State"), - P_("State"), - GDK_TYPE_SURFACE_STATE, GDK_SURFACE_STATE_WITHDRAWN, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - properties[PROP_MAPPED] = g_param_spec_boolean ("mapped", P_("Mapped"), @@ -466,6 +462,7 @@ gdk_surface_class_init (GdkSurfaceClass *klass) g_object_class_install_properties (object_class, LAST_PROP, properties); gdk_popup_install_properties (object_class, LAST_PROP); + gdk_toplevel_install_properties (object_class, LAST_PROP + GDK_POPUP_NUM_PROPERTIES); /** * GdkSurface::popup-layout-changed @@ -644,12 +641,81 @@ gdk_surface_set_property (GObject *object, surface->autohide = g_value_get_boolean (value); break; + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_TITLE: + gdk_surface_set_title (surface, g_value_get_string (value)); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_STARTUP_ID: + gdk_surface_set_startup_id (surface, g_value_get_string (value)); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_TRANSIENT_FOR: + gdk_surface_set_transient_for (surface, g_value_get_object (value)); + break; + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_MODAL: + GDK_SURFACE_GET_CLASS (surface)->set_modal_hint (surface, g_value_get_boolean (value)); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_ICON_LIST: + gdk_surface_set_icon_list (surface, g_value_get_pointer (value)); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_STICKY: + if (g_value_get_boolean (value)) + GDK_SURFACE_GET_CLASS (surface)->stick (surface); + else + GDK_SURFACE_GET_CLASS (surface)->unstick (surface); + g_object_notify_by_pspec (G_OBJECT (surface), pspec); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_KEEP_ABOVE: + GDK_SURFACE_GET_CLASS (surface)->set_keep_above (surface, g_value_get_boolean (value)); + g_object_notify_by_pspec (G_OBJECT (surface), pspec); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_KEEP_BELOW: + GDK_SURFACE_GET_CLASS (surface)->set_keep_below (surface, g_value_get_boolean (value)); + g_object_notify_by_pspec (G_OBJECT (surface), pspec); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_ACCEPT_FOCUS: + if (surface->accept_focus != g_value_get_boolean (value)) + { + surface->accept_focus = g_value_get_boolean (value); + GDK_SURFACE_GET_CLASS (surface)->set_accept_focus (surface, surface->accept_focus); + g_object_notify_by_pspec (G_OBJECT (surface), pspec); + } + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_FOCUS_ON_MAP: + if (surface->focus_on_map != g_value_get_boolean (value)) + { + surface->focus_on_map = g_value_get_boolean (value); + GDK_SURFACE_GET_CLASS (surface)->set_focus_on_map (surface, surface->focus_on_map); + g_object_notify_by_pspec (G_OBJECT (surface), pspec); + } + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_DECORATED: + GDK_SURFACE_GET_CLASS (surface)->set_decorations (surface, g_value_get_boolean (value) ? GDK_DECOR_ALL : 0); + g_object_notify_by_pspec (G_OBJECT (surface), pspec); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_DELETABLE: + GDK_SURFACE_GET_CLASS (surface)->set_functions (surface, g_value_get_boolean (value) ? GDK_FUNC_ALL : GDK_FUNC_ALL | GDK_FUNC_CLOSE); + g_object_notify_by_pspec (G_OBJECT (surface), pspec); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +#define GDK_SURFACE_IS_STICKY(surface) (((surface)->state & GDK_SURFACE_STATE_STICKY)) +#define GDK_SURFACE_IS_ABOVE(surface) (((surface)->state & GDK_SURFACE_STATE_ABOVE)) +#define GDK_SURFACE_IS_BELOW(surface) (((surface)->state & GDK_SURFACE_STATE_BELOW)) + static void gdk_surface_get_property (GObject *object, guint prop_id, @@ -672,10 +738,6 @@ gdk_surface_get_property (GObject *object, g_value_set_object (value, surface->frame_clock); break; - case PROP_STATE: - g_value_set_flags (value, surface->state); - break; - case PROP_MAPPED: g_value_set_boolean (value, GDK_SURFACE_IS_MAPPED (surface)); break; @@ -692,6 +754,58 @@ gdk_surface_get_property (GObject *object, g_value_set_boolean (value, surface->autohide); break; + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_STATE: + g_value_set_flags (value, surface->state); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_TITLE: + g_value_set_string (value, ""); // FIXME + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_STARTUP_ID: + g_value_set_string (value, ""); // FIXME + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_TRANSIENT_FOR: + g_value_set_object (value, NULL); // FIXME + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_MODAL: + g_value_set_boolean (value, surface->modal_hint); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_ICON_LIST: + g_value_set_pointer (value, NULL); // FIXME + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_STICKY: + g_value_set_boolean (value, GDK_SURFACE_IS_STICKY (surface)); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_KEEP_ABOVE: + g_value_set_boolean (value, GDK_SURFACE_IS_ABOVE (surface)); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_KEEP_BELOW: + g_value_set_boolean (value, GDK_SURFACE_IS_BELOW (surface)); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_ACCEPT_FOCUS: + g_value_set_boolean (value, surface->accept_focus); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_FOCUS_ON_MAP: + g_value_set_boolean (value, surface->focus_on_map); + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_DECORATED: + g_value_set_boolean (value, FALSE); // FIXME + break; + + case LAST_PROP + GDK_POPUP_NUM_PROPERTIES + GDK_TOPLEVEL_PROP_DELETABLE: + g_value_set_boolean (value, FALSE); // FIXME + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -867,7 +981,8 @@ _gdk_surface_destroy_hierarchy (GdkSurface *surface, surface_remove_from_pointer_info (surface, surface->display); - g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_STATE]); + if (GDK_IS_TOPLEVEL (surface)) + g_object_notify (G_OBJECT (surface), "state"); g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_MAPPED]); } @@ -2078,6 +2193,116 @@ gdk_surface_popup_init (GdkPopupInterface *iface) iface->get_position_y = gdk_popup_surface_get_position_y; } +static gboolean +gdk_toplevel_surface_present (GdkToplevel *toplevel, + int width, + int height, + GdkToplevelLayout *layout) +{ + GdkSurface *surface = GDK_SURFACE (toplevel); + GdkGeometry geometry; + GdkSurfaceHints mask; + + g_return_val_if_fail (surface->surface_type == GDK_SURFACE_TOPLEVEL, FALSE); + g_return_val_if_fail (!GDK_SURFACE_DESTROYED (surface), FALSE); + + GDK_SURFACE_GET_CLASS (surface)->unminimize (surface); + + if (gdk_toplevel_layout_get_resizable (layout)) + { + geometry.min_width = gdk_toplevel_layout_get_min_width (layout); + geometry.min_height = gdk_toplevel_layout_get_min_height (layout); + mask = GDK_HINT_MIN_SIZE; + } + else + { + geometry.max_width = geometry.min_width = width; + geometry.max_height = geometry.min_height = height; + mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE; + } + + GDK_SURFACE_GET_CLASS (surface)->set_geometry_hints (surface, &geometry, mask); + gdk_surface_constrain_size (&geometry, mask, width, height, &width, &height); + GDK_SURFACE_GET_CLASS (surface)->toplevel_resize (surface, width, height); + + if (gdk_toplevel_layout_get_maximized (layout)) + gdk_surface_maximize (surface); + else + gdk_surface_unmaximize (surface); + + if (gdk_toplevel_layout_get_fullscreen (layout)) + { + GdkMonitor *monitor = gdk_toplevel_layout_get_fullscreen_monitor (layout); + if (monitor) + GDK_SURFACE_GET_CLASS (surface)->fullscreen_on_monitor (surface, monitor); + else + GDK_SURFACE_GET_CLASS (surface)->fullscreen (surface); + } + else + GDK_SURFACE_GET_CLASS (surface)->unfullscreen (surface); + + + gdk_surface_show_internal (surface, TRUE); + + return TRUE; +} + +static gboolean +gdk_toplevel_surface_minimize (GdkToplevel *toplevel) +{ + GdkSurface *surface = GDK_SURFACE (toplevel); + + g_return_val_if_fail (surface->surface_type == GDK_SURFACE_TOPLEVEL, FALSE); + + GDK_SURFACE_GET_CLASS (surface)->minimize (surface); + + return TRUE; +} + +static gboolean +gdk_toplevel_surface_lower (GdkToplevel *toplevel) +{ + GdkSurface *surface = GDK_SURFACE (toplevel); + + g_return_val_if_fail (surface->surface_type == GDK_SURFACE_TOPLEVEL, FALSE); + + GDK_SURFACE_GET_CLASS (surface)->lower (surface); + + return TRUE; +} + +static void +gdk_toplevel_surface_focus (GdkToplevel *toplevel, + guint32 timestamp) +{ + GdkSurface *surface = GDK_SURFACE (toplevel); + + g_return_if_fail (surface->surface_type == GDK_SURFACE_TOPLEVEL); + + GDK_SURFACE_GET_CLASS (surface)->focus (surface, timestamp); +} + +static gboolean +gdk_toplevel_surface_show_window_menu (GdkToplevel *toplevel, + GdkEvent *event) +{ + GdkSurface *surface = GDK_SURFACE (toplevel); + + g_return_val_if_fail (surface->surface_type == GDK_SURFACE_TOPLEVEL, FALSE); + + return GDK_SURFACE_GET_CLASS (surface)->show_window_menu (surface, event); +} + +static void +gdk_surface_toplevel_init (GdkToplevelInterface *iface) +{ + iface->present = gdk_toplevel_surface_present; + iface->minimize = gdk_toplevel_surface_minimize; + iface->lower = gdk_toplevel_surface_lower; + iface->focus = gdk_toplevel_surface_focus; + iface->show_window_menu = gdk_toplevel_surface_show_window_menu; +} + static void gdk_surface_set_cursor_internal (GdkSurface *surface, GdkDevice *device, @@ -2867,6 +3092,8 @@ gdk_surface_set_startup_id (GdkSurface *surface, const gchar *startup_id) { GDK_SURFACE_GET_CLASS (surface)->set_startup_id (surface, startup_id); + + g_object_notify (G_OBJECT (surface), "startup-id"); } /** @@ -2889,6 +3116,8 @@ gdk_surface_set_transient_for (GdkSurface *surface, surface->transient_for = parent; GDK_SURFACE_GET_CLASS (surface)->set_transient_for (surface, parent); + + g_object_notify (G_OBJECT (surface), "transient-for"); } /** @@ -2951,6 +3180,8 @@ gdk_surface_set_icon_list (GdkSurface *surface, GList *textures) { GDK_SURFACE_GET_CLASS (surface)->set_icon_list (surface, textures); + + g_object_notify (G_OBJECT (surface), "icon-list"); } /** @@ -3790,6 +4021,7 @@ gdk_surface_set_state (GdkSurface *surface, GdkSurfaceState new_state) { gboolean was_mapped, mapped; + gboolean was_sticky, sticky; g_return_if_fail (GDK_IS_SURFACE (surface)); if (new_state == surface->state) @@ -3801,17 +4033,23 @@ gdk_surface_set_state (GdkSurface *surface, */ was_mapped = GDK_SURFACE_IS_MAPPED (surface); + was_sticky = GDK_SURFACE_IS_STICKY (surface); surface->state = new_state; mapped = GDK_SURFACE_IS_MAPPED (surface); + sticky = GDK_SURFACE_IS_STICKY (surface); _gdk_surface_update_viewable (surface); - g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_STATE]); + if (GDK_IS_TOPLEVEL (surface)) + g_object_notify (G_OBJECT (surface), "state"); if (was_mapped != mapped) g_object_notify_by_pspec (G_OBJECT (surface), properties[PROP_MAPPED]); + + if (was_sticky != sticky) + g_object_notify (G_OBJECT (surface), "sticky"); } void diff --git a/gdk/gdktoplevel.c b/gdk/gdktoplevel.c new file mode 100644 index 0000000000..c2c64f368e --- /dev/null +++ b/gdk/gdktoplevel.c @@ -0,0 +1,518 @@ +/* + * Copyright © 2020 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Matthias Clasen + */ + +#include "config.h" + +#include "gdkintl.h" +#include "gdk-private.h" +#include "gdktoplevelprivate.h" + +/** + * SECTION:gdktoplevel + * @Short_description: Interface for toplevel surfaces + * @Title: Toplevels + * + * A #GdkToplevel is a freestanding toplevel surface. + */ + + +/* FIXME: this can't have GdkSurface as a prerequisite + * as long as GdkSurface implements this interface itself + */ +G_DEFINE_INTERFACE (GdkToplevel, gdk_toplevel, G_TYPE_OBJECT) + +static gboolean +gdk_toplevel_default_present (GdkToplevel *toplevel, + int width, + int height, + GdkToplevelLayout *layout) +{ + return FALSE; +} + +static gboolean +gdk_toplevel_default_minimize (GdkToplevel *toplevel) +{ + return FALSE; +} + +static gboolean +gdk_toplevel_default_lower (GdkToplevel *toplevel) +{ + return FALSE; +} + +static void +gdk_toplevel_default_focus (GdkToplevel *toplevel, + guint32 timestamp) +{ +} + +static gboolean +gdk_toplevel_default_show_window_menu (GdkToplevel *toplevel, + GdkEvent *event) +{ + return FALSE; +} + +static void +gdk_toplevel_default_init (GdkToplevelInterface *iface) +{ + iface->present = gdk_toplevel_default_present; + iface->minimize = gdk_toplevel_default_minimize; + iface->lower = gdk_toplevel_default_lower; + iface->focus = gdk_toplevel_default_focus; + iface->show_window_menu = gdk_toplevel_default_show_window_menu; + + g_object_interface_install_property (iface, + g_param_spec_flags ("state", + P_("State"), + P_("State"), + GDK_TYPE_SURFACE_STATE, GDK_SURFACE_STATE_WITHDRAWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_interface_install_property (iface, + g_param_spec_string ("title", + "Title", + "The title of the surface", + NULL, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + g_object_interface_install_property (iface, + g_param_spec_string ("startup-id", + "Startup ID", + "The startup ID of the surface", + NULL, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + g_object_interface_install_property (iface, + g_param_spec_object ("transient-for", + "Transient For", + "The transient parent of the surface", + GDK_TYPE_SURFACE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + g_object_interface_install_property (iface, + g_param_spec_boolean ("modal", + "Modal", + "Whether the surface is modal", + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + g_object_interface_install_property (iface, + g_param_spec_pointer ("icon-list", + "Icon List", + "The list of icon textures", + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + g_object_interface_install_property (iface, + g_param_spec_boolean ("sticky", + "Sticky", + "Whether the surface is on all workspaces", + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + g_object_interface_install_property (iface, + g_param_spec_boolean ("keep-above", + "Keep above", + "Whether the surface is on above all other surfaces", + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + g_object_interface_install_property (iface, + g_param_spec_boolean ("keep-below", + "Keep below", + "Whether the surface is below all other surfaces", + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + g_object_interface_install_property (iface, + g_param_spec_boolean ("accept-focus", + "Accept focus", + "Whether the surface should accept keyboard focus", + TRUE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + g_object_interface_install_property (iface, + g_param_spec_boolean ("focus-on-map", + "Focus on map", + "Whether the surface should receive keyboard focus on map", + TRUE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + g_object_interface_install_property (iface, + g_param_spec_boolean ("decorated", + "Decorated", + "Decorated", + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); + g_object_interface_install_property (iface, + g_param_spec_boolean ("deletable", + "Deletable", + "Deletable", + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); +} + +guint +gdk_toplevel_install_properties (GObjectClass *object_class, + guint first_prop) +{ + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_STATE, "state"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_TITLE, "title"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_STARTUP_ID, "startup-id"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_TRANSIENT_FOR, "transient-for"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_MODAL, "modal"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_ICON_LIST, "icon-list"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_STICKY, "sticky"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_KEEP_ABOVE, "keep-above"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_KEEP_BELOW, "keep-below"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_ACCEPT_FOCUS, "accept-focus"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_FOCUS_ON_MAP, "focus-on-map"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_DECORATED, "decorated"); + g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_DELETABLE, "deletable"); + + return GDK_TOPLEVEL_NUM_PROPERTIES; +} + +/** + * gdk_toplevel_present: + * @toplevel: the #GdkToplevel to show + * @width: the unconstrained toplevel width to layout + * @height: the unconstrained toplevel height to layout + * @layout: the #GdkToplevelLayout object used to layout + * + * Present @toplevel after having processed the #GdkToplevelLayout rules. + * If the toplevel was previously now showing, it will be showed, + * otherwise it will change layout according to @layout. + * + * Presenting may fail. + * + * Returns: %FALSE if @toplevel failed to be presented, otherwise %TRUE. + */ +gboolean +gdk_toplevel_present (GdkToplevel *toplevel, + int width, + int height, + GdkToplevelLayout *layout) +{ + g_return_val_if_fail (GDK_IS_TOPLEVEL (toplevel), FALSE); + g_return_val_if_fail (width > 0, FALSE); + g_return_val_if_fail (height > 0, FALSE); + g_return_val_if_fail (layout != NULL, FALSE); + + return GDK_TOPLEVEL_GET_IFACE (toplevel)->present (toplevel, width, height, layout); +} + +/** + * gdk_toplevel_minimize: + * @toplevel: a #GdkToplevel + * + * Asks to minimize the @toplevel. + * + * The windowing system may choose to ignore the request. + * + * Returns: %TRUE if the surface was minimized + */ +gboolean +gdk_toplevel_minimize (GdkToplevel *toplevel) +{ + g_return_val_if_fail (GDK_IS_TOPLEVEL (toplevel), FALSE); + + return GDK_TOPLEVEL_GET_IFACE (toplevel)->minimize (toplevel); +} + +/** + * gdk_toplevel_lower: + * @toplevel: a #GdkToplevel + * + * Asks to lower the @toplevel below other windows. + * + * The windowing system may choose to ignore the request. + * + * Returns: %TRUE if the surface was lowered + */ +gboolean +gdk_toplevel_lower (GdkToplevel *toplevel) +{ + g_return_val_if_fail (GDK_IS_TOPLEVEL (toplevel), FALSE); + + return GDK_TOPLEVEL_GET_IFACE (toplevel)->lower (toplevel); +} + +/** + * gdk_toplevel_focus: + * @toplevel: a #GdkToplevel + * @timestamp: timestamp of the event triggering the surface focus + * + * Sets keyboard focus to @surface. + * + * In most cases, gtk_window_present_with_time() should be used + * on a #GtkWindow, rather than calling this function. + */ +void +gdk_toplevel_focus (GdkToplevel *toplevel, + guint32 timestamp) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + return GDK_TOPLEVEL_GET_IFACE (toplevel)->focus (toplevel, timestamp); +} + +/** + * gdk_toplevel_get_state: + * @toplevel: a #GdkToplevel + * + * Gets the bitwise OR of the currently active surface state flags, + * from the #GdkSurfaceState enumeration. + * + * Returns: surface state bitfield + */ +GdkSurfaceState +gdk_toplevel_get_state (GdkToplevel *toplevel) +{ + GdkSurfaceState state; + + g_return_val_if_fail (GDK_IS_TOPLEVEL (toplevel), 0); + + g_object_get (toplevel, "state", &state, NULL); + + return state; +} + +/** + * gdk_toplevel_set_title: + * @toplevel: a #GdkToplevel + * @title: title of @surface + * + * Sets the title of a toplevel surface, to be displayed in the titlebar, + * in lists of windows, etc. + */ +void +gdk_toplevel_set_title (GdkToplevel *toplevel, + const char *title) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "title", title, NULL); +} + +/** + * gdk_toplevel_set_startup_id: + * @toplevel: a #GdkToplevel + * @startup_id: a string with startup-notification identifier + * + * When using GTK, typically you should use gtk_window_set_startup_id() + * instead of this low-level function. + */ +void +gdk_toplevel_set_startup_id (GdkToplevel *toplevel, + const char *startup_id) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "startup-id", startup_id, NULL); +} + +/** + * gdk_toplevel_set_transient_for: + * @toplevel: a #GdkToplevel + * @parent: another toplevel #GdkSurface + * + * Indicates to the window manager that @surface is a transient dialog + * associated with the application surface @parent. This allows the + * window manager to do things like center @surface on @parent and + * keep @surface above @parent. + * + * See gtk_window_set_transient_for() if you’re using #GtkWindow or + * #GtkDialog. + */ +void +gdk_toplevel_set_transient_for (GdkToplevel *toplevel, + GdkSurface *parent) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "transient-for", parent, NULL); +} + +void +gdk_toplevel_set_modal (GdkToplevel *toplevel, + gboolean modal) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "modal", modal, NULL); +} + +/** + * gdk_toplevel_set_icon_list: + * @toplevel: a #GdkToplevel + * @surfaces: (transfer none) (element-type GdkTexture): + * A list of textures to use as icon, of different sizes + * + * Sets a list of icons for the surface. + * + * One of these will be used to represent the surface in iconic form. + * The icon may be shown in window lists or task bars. Which icon + * size is shown depends on the window manager. The window manager + * can scale the icon but setting several size icons can give better + * image quality. + * + * Note that some platforms don't support surface icons. + */ +void +gdk_toplevel_set_icon_list (GdkToplevel *toplevel, + GList *surfaces) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "icon-list", surfaces, NULL); +} + +/** + * gdk_toplevel_show_window_menu: + * @toplevel: a #GdkToplevel + * @event: a #GdkEvent to show the menu for + * + * Asks the windowing system to show the window menu. + * + * The window menu is the menu shown when right-clicking the titlebar + * on traditional windows managed by the window manager. This is useful + * for windows using client-side decorations, activating it with a + * right-click on the window decorations. + * + * Returns: %TRUE if the window menu was shown and %FALSE otherwise. + */ +gboolean +gdk_toplevel_show_window_menu (GdkToplevel *toplevel, + GdkEvent *event) +{ + g_return_val_if_fail (GDK_IS_TOPLEVEL (toplevel), FALSE); + + return GDK_TOPLEVEL_GET_IFACE (toplevel)->show_window_menu (toplevel, event); +} + +/** + * gdk_toplevel_set_sticky: + * @toplevel: a #GdkToplevel + * @sticky: whether to make @toplevel show on all workspaces + * + * Set if @surface is sticky. + **/ +void +gdk_toplevel_set_sticky (GdkToplevel *toplevel, + gboolean sticky) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "sticky", sticky, NULL); +} + +/** + * gdk_toplevel_set_keep_above: + * @toplevel: a #GdkToplevel + * @above: whether to keep @toplevel above other surfaces + * + * Set if @surface must be kept above other surfaces. + **/ +void +gdk_toplevel_set_keep_above (GdkToplevel *toplevel, + gboolean above) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "keep-above", above, NULL); +} + +/** + * gdk_toplevel_set_keep_below: + * @toplevel: a #GdkToplevel + * @below: whether to keep @toplevel below other surfaces + * + * Set if @surface must be kept below other surfaces. + **/ +void +gdk_toplevel_set_keep_below (GdkToplevel *toplevel, + gboolean below) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "keep-below", below, NULL); +} + +/** + * gdk_toplevel_set_accept_focus: + * @toplevel: a #GdkToplevel + * @accept_focus: whether @toplevel should accept keyboard focus + * + * Setting @accept_focus to %FALSE hints the desktop environment + * that the surface doesn’t want to receive input focus. + */ +void +gdk_toplevel_set_accept_focus (GdkToplevel *toplevel, + gboolean accept_focus) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "accept-focus", accept_focus, NULL); +} + +/** + * gdk_toplevel_set_focus_on_map: + * @toplevel: a #GdkToplevel + * @focus_on_map: whether @toplevel should receive input focus when mapped + * + * Setting @focus_on_map to %FALSE hints the desktop environment that the + * surface doesn’t want to receive input focus when it is mapped. + * focus_on_map should be turned off for surfaces that aren’t triggered + * interactively (such as popups from network activity). + */ +void +gdk_toplevel_set_focus_on_map (GdkToplevel *toplevel, + gboolean focus_on_map) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "focus-on-map", focus_on_map, NULL); +} + +/** + * gdk_toplevel_set_decorated: + * @toplevel: a #GdkToplevel + * @decorated: %TRUE to request decorations + * + * Setting @decorated to %FALSE hints the desktop environment + * that the surface has its own, client-side decorations and + * does not need to have window decorations added. + */ +void +gdk_toplevel_set_decorated (GdkToplevel *toplevel, + gboolean decorated) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "decorated", decorated, NULL); +} + +/** + * gdk_toplevel_set_deletable: + * @toplevel: a #GdkToplevel + * @deletable: %TRUE to request a delete button + * + * Setting @deletable to %TRUE hints the desktop environment + * that it should offer the user a way to close the surface. + */ +void +gdk_toplevel_set_deletable (GdkToplevel *toplevel, + gboolean deletable) +{ + g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); + + g_object_set (toplevel, "deletable", deletable, NULL); +} diff --git a/gdk/gdktoplevel.h b/gdk/gdktoplevel.h new file mode 100644 index 0000000000..e513ad9adf --- /dev/null +++ b/gdk/gdktoplevel.h @@ -0,0 +1,108 @@ +/* + * Copyright © 2020 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Matthias Clasen + */ + +#ifndef __GDK_TOPLEVEL_H__ +#define __GDK_TOPLEVEL_H__ + +#if !defined(__GDK_H_INSIDE__) && !defined(GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +#define GDK_TYPE_TOPLEVEL (gdk_toplevel_get_type ()) + +GDK_AVAILABLE_IN_ALL +G_DECLARE_INTERFACE (GdkToplevel, gdk_toplevel, GDK, TOPLEVEL, GObject) + +GDK_AVAILABLE_IN_ALL +gboolean gdk_toplevel_present (GdkToplevel *toplevel, + int width, + int height, + GdkToplevelLayout *layout); + +GDK_AVAILABLE_IN_ALL +gboolean gdk_toplevel_minimize (GdkToplevel *toplevel); + +GDK_AVAILABLE_IN_ALL +gboolean gdk_toplevel_lower (GdkToplevel *toplevel); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_focus (GdkToplevel *toplevel, + guint32 timestamp); + +GDK_AVAILABLE_IN_ALL +GdkSurfaceState gdk_toplevel_get_state (GdkToplevel *toplevel); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_title (GdkToplevel *toplevel, + const char *title); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_startup_id (GdkToplevel *toplevel, + const char *startup_id); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_transient_for (GdkToplevel *toplevel, + GdkSurface *parent); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_modal (GdkToplevel *toplevel, + gboolean modal); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_icon_list (GdkToplevel *toplevel, + GList *surfaces); + +GDK_AVAILABLE_IN_ALL +gboolean gdk_toplevel_show_window_menu (GdkToplevel *toplevel, + GdkEvent *event); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_sticky (GdkToplevel *toplevel, + gboolean sticky); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_keep_above (GdkToplevel *toplevel, + gboolean above); +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_keep_below (GdkToplevel *toplevel, + gboolean below); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_accept_focus (GdkToplevel *toplevel, + gboolean accept_focus); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_focus_on_map (GdkToplevel *toplevel, + gboolean focus_on_map); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_decorated (GdkToplevel *toplevel, + gboolean decorated); + +GDK_AVAILABLE_IN_ALL +void gdk_toplevel_set_deletable (GdkToplevel *toplevel, + gboolean deletable); +G_END_DECLS + +#endif /* __GDK_TOPLEVEL_H__ */ diff --git a/gdk/gdktoplevelprivate.h b/gdk/gdktoplevelprivate.h new file mode 100644 index 0000000000..bd5c9672f3 --- /dev/null +++ b/gdk/gdktoplevelprivate.h @@ -0,0 +1,48 @@ +#ifndef __GDK_TOPLEVEL_PRIVATE_H__ +#define __GDK_TOPLEVEL_PRIVATE_H__ + +#include "gdktoplevel.h" + +G_BEGIN_DECLS + + +struct _GdkToplevelInterface +{ + GTypeInterface g_iface; + + gboolean (* present) (GdkToplevel *toplevel, + int width, + int height, + GdkToplevelLayout *layout); + gboolean (* minimize) (GdkToplevel *toplevel); + gboolean (* lower) (GdkToplevel *toplevel); + void (* focus) (GdkToplevel *toplevel, + guint32 timestamp); + gboolean (* show_window_menu) (GdkToplevel *toplevel, + GdkEvent *event); +}; + +typedef enum +{ + GDK_TOPLEVEL_PROP_STATE, + GDK_TOPLEVEL_PROP_TITLE, + GDK_TOPLEVEL_PROP_STARTUP_ID, + GDK_TOPLEVEL_PROP_TRANSIENT_FOR, + GDK_TOPLEVEL_PROP_MODAL, + GDK_TOPLEVEL_PROP_ICON_LIST, + GDK_TOPLEVEL_PROP_STICKY, + GDK_TOPLEVEL_PROP_KEEP_ABOVE, + GDK_TOPLEVEL_PROP_KEEP_BELOW, + GDK_TOPLEVEL_PROP_ACCEPT_FOCUS, + GDK_TOPLEVEL_PROP_FOCUS_ON_MAP, + GDK_TOPLEVEL_PROP_DECORATED, + GDK_TOPLEVEL_PROP_DELETABLE, + GDK_TOPLEVEL_NUM_PROPERTIES +} GdkToplevelProperties; + +guint gdk_toplevel_install_properties (GObjectClass *object_class, + guint first_prop); + +G_END_DECLS + +#endif /* __GDK_TOPLEVEL_PRIVATE_H__ */ diff --git a/gdk/meson.build b/gdk/meson.build index 27d1bc3cda..0c1a277549 100644 --- a/gdk/meson.build +++ b/gdk/meson.build @@ -47,6 +47,7 @@ gdk_public_sources = files([ 'gdkprofiler.c', 'gdkpopup.c', 'gdktoplevellayout.c', + 'gdktoplevel.c', ]) gdk_public_headers = files([ @@ -93,6 +94,7 @@ gdk_public_headers = files([ 'gdkpopuplayout.h', 'gdkpopup.h', 'gdktoplevellayout.h', + 'gdktoplevel.h', ]) install_headers(gdk_public_headers, subdir: 'gtk-4.0/gdk/')