mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-17 06:10:15 +00:00
window: Add lowlevel popovers API
Popovers are transient floating widgets that are confined to the window space. These have their own GdkWindow that is set on top of the regular window contents, so they can be used for popup menu alike UIs with custom popup/popdown/grabs behavior.
This commit is contained in:
parent
89a51bd15d
commit
85a19c894f
322
gtk/gtkwindow.c
322
gtk/gtkwindow.c
@ -123,6 +123,15 @@
|
||||
#define MNEMONICS_DELAY 300 /* ms */
|
||||
|
||||
typedef struct _GtkDeviceGrabInfo GtkDeviceGrabInfo;
|
||||
typedef struct _GtkWindowPopover GtkWindowPopover;
|
||||
|
||||
struct _GtkWindowPopover
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GdkWindow *window;
|
||||
GtkPositionType pos;
|
||||
cairo_rectangle_int_t rect;
|
||||
};
|
||||
|
||||
struct _GtkWindowPrivate
|
||||
{
|
||||
@ -137,6 +146,8 @@ struct _GtkWindowPrivate
|
||||
GdkScreen *screen;
|
||||
GtkApplication *application;
|
||||
|
||||
GHashTable *popovers;
|
||||
|
||||
GdkModifierType mnemonic_modifier;
|
||||
GdkWindowTypeHint gdk_type_hint;
|
||||
|
||||
@ -1318,6 +1329,18 @@ gtk_window_close (GtkWindow *window)
|
||||
gdk_threads_add_idle (send_delete_event, window);
|
||||
}
|
||||
|
||||
static void
|
||||
popover_destroy (GtkWindowPopover *popover)
|
||||
{
|
||||
if (popover->widget && gtk_widget_get_parent (popover->widget))
|
||||
gtk_widget_unparent (popover->widget);
|
||||
|
||||
if (popover->window)
|
||||
gdk_window_destroy (popover->window);
|
||||
|
||||
g_free (popover);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_window_init (GtkWindow *window)
|
||||
{
|
||||
@ -1368,6 +1391,8 @@ gtk_window_init (GtkWindow *window)
|
||||
priv->has_resize_grip = TRUE;
|
||||
priv->mnemonics_visible = TRUE;
|
||||
priv->focus_visible = TRUE;
|
||||
priv->popovers = g_hash_table_new_full (NULL, NULL, NULL,
|
||||
(GDestroyNotify) popover_destroy);
|
||||
|
||||
g_object_ref_sink (window);
|
||||
priv->has_user_ref_count = TRUE;
|
||||
@ -5166,6 +5191,8 @@ gtk_window_finalize (GObject *object)
|
||||
priv->mnemonics_display_timeout_id = 0;
|
||||
}
|
||||
|
||||
g_hash_table_destroy (priv->popovers);
|
||||
|
||||
G_OBJECT_CLASS (gtk_window_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
@ -5398,6 +5425,19 @@ gtk_window_hide (GtkWidget *widget)
|
||||
gtk_grab_remove (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
popover_map (GtkWidget *widget,
|
||||
GtkWindowPopover *popover)
|
||||
{
|
||||
if (popover->window)
|
||||
{
|
||||
gdk_window_show (popover->window);
|
||||
|
||||
if (gtk_widget_get_visible (popover->widget))
|
||||
gtk_widget_map (popover->widget);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_window_map (GtkWidget *widget)
|
||||
{
|
||||
@ -5511,6 +5551,8 @@ gtk_window_map (GtkWidget *widget)
|
||||
|
||||
if (priv->application)
|
||||
gtk_application_handle_window_map (priv->application, window);
|
||||
|
||||
g_hash_table_foreach (priv->popovers, (GHFunc) popover_map, window);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -5531,6 +5573,18 @@ gtk_window_map_event (GtkWidget *widget,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
popover_unmap (GtkWidget *widget,
|
||||
GtkWindowPopover *popover)
|
||||
{
|
||||
if (popover->window)
|
||||
{
|
||||
if (gtk_widget_is_visible (popover->widget))
|
||||
gtk_widget_unmap (popover->widget);
|
||||
gdk_window_hide (popover->window);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_window_unmap (GtkWidget *widget)
|
||||
{
|
||||
@ -5547,6 +5601,7 @@ gtk_window_unmap (GtkWidget *widget)
|
||||
return;
|
||||
}
|
||||
|
||||
g_hash_table_foreach (priv->popovers, (GHFunc) popover_unmap, window);
|
||||
gdk_window = gtk_widget_get_window (widget);
|
||||
|
||||
gtk_widget_set_mapped (widget, FALSE);
|
||||
@ -5676,6 +5731,117 @@ gtk_window_get_remembered_size (GtkWindow *window,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
popover_get_rect (GtkWindowPopover *popover,
|
||||
GtkWindow *window,
|
||||
cairo_rectangle_int_t *rect)
|
||||
{
|
||||
GtkAllocation win_alloc;
|
||||
GtkRequisition req;
|
||||
|
||||
gtk_widget_get_preferred_size (popover->widget, NULL, &req);
|
||||
gtk_widget_get_allocation (GTK_WIDGET (window), &win_alloc);
|
||||
rect->width = req.width;
|
||||
rect->height = req.height;
|
||||
|
||||
if (popover->pos == GTK_POS_LEFT || popover->pos == GTK_POS_RIGHT)
|
||||
{
|
||||
if (req.height < win_alloc.height &&
|
||||
gtk_widget_get_vexpand (popover->widget))
|
||||
{
|
||||
rect->y = 0;
|
||||
rect->height = win_alloc.height;
|
||||
}
|
||||
else
|
||||
rect->y = CLAMP (popover->rect.y + (popover->rect.height / 2) -
|
||||
(req.height / 2), 0, win_alloc.height - req.height);
|
||||
|
||||
if (popover->pos == GTK_POS_LEFT)
|
||||
{
|
||||
rect->x = popover->rect.x - req.width;
|
||||
|
||||
if (rect->x > 0 && gtk_widget_get_hexpand (popover->widget))
|
||||
{
|
||||
rect->x = 0;
|
||||
rect->width = popover->rect.x;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rect->x = popover->rect.x + popover->rect.width;
|
||||
|
||||
if (rect->x + rect->width < win_alloc.width &&
|
||||
gtk_widget_get_hexpand (popover->widget))
|
||||
rect->width = win_alloc.width - rect->x;
|
||||
}
|
||||
}
|
||||
else if (popover->pos == GTK_POS_TOP || popover->pos == GTK_POS_BOTTOM)
|
||||
{
|
||||
if (req.width < win_alloc.width &&
|
||||
gtk_widget_get_hexpand (popover->widget))
|
||||
{
|
||||
rect->x = 0;
|
||||
rect->width = win_alloc.width;
|
||||
}
|
||||
else
|
||||
rect->x = CLAMP (popover->rect.x + (popover->rect.width / 2) -
|
||||
(req.width / 2), 0, win_alloc.width - req.width);
|
||||
|
||||
if (popover->pos == GTK_POS_TOP)
|
||||
{
|
||||
rect->y = popover->rect.y - req.height;
|
||||
|
||||
if (rect->y > 0 && gtk_widget_get_vexpand (popover->widget))
|
||||
{
|
||||
rect->y = 0;
|
||||
rect->height = popover->rect.y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rect->y = popover->rect.y + popover->rect.height;
|
||||
|
||||
if (rect->y + rect->height < win_alloc.height &&
|
||||
gtk_widget_get_vexpand (popover->widget))
|
||||
rect->height = win_alloc.height - rect->y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
popover_realize (GtkWidget *widget,
|
||||
GtkWindowPopover *popover,
|
||||
GtkWindow *window)
|
||||
{
|
||||
cairo_rectangle_int_t rect;
|
||||
GdkWindow *parent_window;
|
||||
GdkWindowAttr attributes;
|
||||
gint attributes_mask;
|
||||
|
||||
if (popover->window)
|
||||
return;
|
||||
|
||||
popover_get_rect (popover, window, &rect);
|
||||
|
||||
attributes.window_type = GDK_WINDOW_CHILD;
|
||||
attributes.wclass = GDK_INPUT_OUTPUT;
|
||||
attributes.x = rect.x;
|
||||
attributes.y = rect.y;
|
||||
attributes.width = rect.width;
|
||||
attributes.height = rect.height;
|
||||
attributes.visual = gtk_widget_get_visual (widget);
|
||||
attributes.event_mask = gtk_widget_get_events (popover->widget) |
|
||||
GDK_EXPOSURE_MASK;
|
||||
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
|
||||
|
||||
parent_window = gtk_widget_get_window (GTK_WIDGET (window));
|
||||
popover->window =
|
||||
gdk_window_new (parent_window, &attributes, attributes_mask);
|
||||
gtk_widget_register_window (GTK_WIDGET (window), popover->window);
|
||||
|
||||
gtk_widget_set_parent_window (popover->widget, popover->window);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_window_realize (GtkWidget *widget)
|
||||
{
|
||||
@ -5926,12 +6092,23 @@ gtk_window_realize (GtkWidget *widget)
|
||||
if (priv->has_resize_grip)
|
||||
resize_grip_create_window (window);
|
||||
|
||||
g_hash_table_foreach (priv->popovers, (GHFunc) popover_realize, window);
|
||||
|
||||
old_scale = priv->scale;
|
||||
priv->scale = gtk_widget_get_scale_factor (widget);
|
||||
if (old_scale != priv->scale)
|
||||
_gtk_widget_scale_changed (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
popover_unrealize (GtkWidget *widget,
|
||||
GtkWindowPopover *popover)
|
||||
{
|
||||
gtk_widget_unrealize (popover->widget);
|
||||
gdk_window_destroy (popover->window);
|
||||
popover->window = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_window_unrealize (GtkWidget *widget)
|
||||
{
|
||||
@ -5982,6 +6159,8 @@ gtk_window_unrealize (GtkWidget *widget)
|
||||
}
|
||||
}
|
||||
|
||||
g_hash_table_foreach (priv->popovers, (GHFunc) popover_unrealize, window);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_window_parent_class)->unrealize (widget);
|
||||
}
|
||||
|
||||
@ -6697,6 +6876,32 @@ _gtk_window_set_allocation (GtkWindow *window,
|
||||
*allocation_out = child_allocation;
|
||||
}
|
||||
|
||||
static void
|
||||
popover_size_allocate (GtkWidget *widget,
|
||||
GtkWindowPopover *popover,
|
||||
GtkWindow *window)
|
||||
{
|
||||
cairo_rectangle_int_t rect;
|
||||
|
||||
if (!popover->window)
|
||||
return;
|
||||
|
||||
popover_get_rect (popover, window, &rect);
|
||||
gdk_window_move_resize (popover->window, rect.x, rect.y,
|
||||
rect.width, rect.height);
|
||||
rect.x = rect.y = 0;
|
||||
gtk_widget_size_allocate (widget, &rect);
|
||||
|
||||
if (gtk_widget_is_drawable (GTK_WIDGET (window)) &&
|
||||
gtk_widget_is_visible (widget))
|
||||
{
|
||||
if (!gdk_window_is_visible (popover->window))
|
||||
gdk_window_show (popover->window);
|
||||
}
|
||||
else if (gdk_window_is_visible (popover->window))
|
||||
gdk_window_hide (popover->window);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_window_size_allocate (GtkWidget *widget,
|
||||
GtkAllocation *allocation)
|
||||
@ -6710,6 +6915,9 @@ gtk_window_size_allocate (GtkWidget *widget,
|
||||
child = gtk_bin_get_child (GTK_BIN (window));
|
||||
if (child && gtk_widget_get_visible (child))
|
||||
gtk_widget_size_allocate (child, &child_allocation);
|
||||
|
||||
g_hash_table_foreach (window->priv->popovers,
|
||||
(GHFunc) popover_size_allocate, widget);
|
||||
}
|
||||
|
||||
static gint
|
||||
@ -7619,8 +7827,11 @@ gtk_window_remove (GtkContainer *container,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkWindow *window = GTK_WINDOW (container);
|
||||
|
||||
if (widget == window->priv->title_box)
|
||||
unset_titlebar (window);
|
||||
else if (g_hash_table_contains (window->priv->popovers, widget))
|
||||
gtk_window_remove_popover (window, widget);
|
||||
else
|
||||
GTK_CONTAINER_CLASS (gtk_window_parent_class)->remove (container, widget);
|
||||
}
|
||||
@ -9395,10 +9606,12 @@ gtk_window_draw (GtkWidget *widget,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GtkWindowPrivate *priv = GTK_WINDOW (widget)->priv;
|
||||
GtkWindowPopover *popover;
|
||||
GtkStyleContext *context;
|
||||
gboolean ret = FALSE;
|
||||
GtkAllocation allocation;
|
||||
GtkBorder window_border;
|
||||
GHashTableIter iter;
|
||||
gint title_height;
|
||||
|
||||
context = gtk_widget_get_style_context (widget);
|
||||
@ -9483,6 +9696,16 @@ gtk_window_draw (GtkWidget *widget,
|
||||
gtk_style_context_restore (context);
|
||||
}
|
||||
|
||||
g_hash_table_iter_init (&iter, priv->popovers);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &popover))
|
||||
{
|
||||
if (popover->window && gtk_widget_is_visible (popover->widget) &&
|
||||
gtk_cairo_should_draw_window (cr, popover->window))
|
||||
gtk_container_propagate_draw (GTK_CONTAINER (widget),
|
||||
popover->widget, cr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -11697,3 +11920,102 @@ _gtk_window_get_shadow_width (GtkWindow *window,
|
||||
{
|
||||
get_shadow_width (GTK_WIDGET (window), border);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_window_add_popover (GtkWindow *window,
|
||||
GtkWidget *popover)
|
||||
{
|
||||
GtkWindowPrivate *priv;
|
||||
GtkWindowPopover *data;
|
||||
|
||||
g_return_if_fail (GTK_IS_WINDOW (window));
|
||||
g_return_if_fail (GTK_IS_WIDGET (popover));
|
||||
g_return_if_fail (gtk_widget_get_parent (popover) == NULL);
|
||||
|
||||
priv = window->priv;
|
||||
|
||||
if (g_hash_table_contains (priv->popovers, popover))
|
||||
return;
|
||||
|
||||
data = g_new0 (GtkWindowPopover, 1);
|
||||
data->widget = popover;
|
||||
g_hash_table_insert (priv->popovers, popover, data);
|
||||
|
||||
if (gtk_widget_get_realized (GTK_WIDGET (window)))
|
||||
popover_realize (popover, data, window);
|
||||
|
||||
gtk_widget_set_parent (popover, GTK_WIDGET (window));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_window_remove_popover (GtkWindow *window,
|
||||
GtkWidget *popover)
|
||||
{
|
||||
GtkWindowPrivate *priv;
|
||||
|
||||
g_return_if_fail (GTK_IS_WINDOW (window));
|
||||
g_return_if_fail (GTK_IS_WIDGET (popover));
|
||||
|
||||
priv = window->priv;
|
||||
g_hash_table_remove (priv->popovers, popover);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_window_set_popover_position:
|
||||
* @window: a #GtkWindow
|
||||
* @popover: #GtkWidget to be positioned on top of window contents
|
||||
* @pos: Position of the popover, relative of the contents it's related to
|
||||
* @rect: Disclosure rectangle, in @window coordinates
|
||||
*
|
||||
* Positions @popover so it is at the side given by @pos of the rectangle @rect.
|
||||
* If @popover is set to expand horizontally or vertically, the position given
|
||||
* by @pos will be used to determine how the popover is allowed to expand
|
||||
* without covering the content it's logically attached to.
|
||||
*
|
||||
* Since: 3.12
|
||||
**/
|
||||
void
|
||||
gtk_window_set_popover_position (GtkWindow *window,
|
||||
GtkWidget *popover,
|
||||
GtkPositionType pos,
|
||||
const cairo_rectangle_int_t *rect)
|
||||
{
|
||||
GtkWindowPopover *data;
|
||||
GtkWindowPrivate *priv;
|
||||
|
||||
g_return_if_fail (GTK_IS_WINDOW (window));
|
||||
g_return_if_fail (GTK_IS_WIDGET (popover));
|
||||
|
||||
priv = window->priv;
|
||||
data = g_hash_table_lookup (priv->popovers, popover);
|
||||
|
||||
if (!data)
|
||||
{
|
||||
g_warning ("Widget %s(%p) is not a popover of window %s",
|
||||
gtk_widget_get_name (popover), popover,
|
||||
gtk_widget_get_name (GTK_WIDGET (window)));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data->pos == pos &&
|
||||
memcmp (&data->rect, rect, sizeof (cairo_rectangle_int_t)) == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
data->rect = *rect;
|
||||
data->pos = pos;
|
||||
|
||||
if (gtk_widget_is_visible (popover))
|
||||
{
|
||||
if (!data->window)
|
||||
{
|
||||
popover_realize (popover, data, window);
|
||||
popover_map (popover, data);
|
||||
}
|
||||
else
|
||||
gdk_window_raise (data->window);
|
||||
}
|
||||
|
||||
gtk_widget_queue_resize (popover);
|
||||
}
|
||||
|
@ -483,6 +483,18 @@ void gtk_window_set_titlebar (GtkWindow *window,
|
||||
GDK_AVAILABLE_IN_3_12
|
||||
gboolean gtk_window_is_maximized (GtkWindow *window);
|
||||
|
||||
GDK_AVAILABLE_IN_3_12
|
||||
void gtk_window_add_popover (GtkWindow *window,
|
||||
GtkWidget *popover);
|
||||
GDK_AVAILABLE_IN_3_12
|
||||
void gtk_window_remove_popover (GtkWindow *window,
|
||||
GtkWidget *popover);
|
||||
GDK_AVAILABLE_IN_3_12
|
||||
void gtk_window_set_popover_position (GtkWindow *window,
|
||||
GtkWidget *popover,
|
||||
GtkPositionType pos,
|
||||
const cairo_rectangle_int_t *rect);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_WINDOW_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user