2013-01-11 16:32:08 +00:00
|
|
|
/* GTK - The GIMP Toolkit
|
|
|
|
* Copyright © 2013 Carlos Garnacho <carlosg@gnome.org>
|
|
|
|
*
|
|
|
|
* 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 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2013-11-13 14:11:21 +00:00
|
|
|
/**
|
|
|
|
* SECTION:gtkpopover
|
|
|
|
* @Short_description: Context dependent bubbles
|
|
|
|
* @Title: GtkPopover
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* GtkPopover is a bubble-like context window, primarily meant to
|
|
|
|
* provide context-dependent information or options. Popovers are
|
|
|
|
* attached to a widget, passed at construction time on gtk_popover_new(),
|
|
|
|
* or updated afterwards through gtk_popover_set_relative_to(), by
|
|
|
|
* default they will point to the whole widget area, although this
|
|
|
|
* behavior can be changed through gtk_popover_set_pointing_to().
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* The position of a popover relative to the widget it is attached to
|
|
|
|
* can also be changed through gtk_popover_set_position().
|
|
|
|
*
|
2014-01-22 15:26:13 +00:00
|
|
|
* By default, #GtkPopover performs a GTK+ grab, in order to ensure
|
|
|
|
* input events get redirected to it while it is shown, and also so
|
2014-01-23 22:41:37 +00:00
|
|
|
* the popover is dismissed in the expected situations (clicks outside
|
2014-01-22 15:26:13 +00:00
|
|
|
* the popover, or the Esc key being pressed). If no such modal behavior
|
|
|
|
* is desired on a popover, gtk_popover_set_modal() may be called on it
|
|
|
|
* to tweak its behavior.
|
|
|
|
*
|
|
|
|
* Since: 3.12
|
2013-01-11 16:32:08 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include <gdk/gdk.h>
|
|
|
|
#include <cairo-gobject.h>
|
2013-11-12 15:55:23 +00:00
|
|
|
#include "gtkpopover.h"
|
2013-01-18 02:49:24 +00:00
|
|
|
#include "gtktypebuiltins.h"
|
|
|
|
#include "gtkmain.h"
|
2014-01-17 17:12:31 +00:00
|
|
|
#include "gtkwindowprivate.h"
|
2014-01-22 15:43:37 +00:00
|
|
|
#include "gtkscrollable.h"
|
|
|
|
#include "gtkadjustment.h"
|
2013-01-11 16:32:08 +00:00
|
|
|
#include "gtkprivate.h"
|
|
|
|
#include "gtkintl.h"
|
|
|
|
|
|
|
|
#define TAIL_GAP_WIDTH 24
|
|
|
|
#define TAIL_HEIGHT 12
|
|
|
|
|
|
|
|
#define POS_IS_VERTICAL(p) ((p) == GTK_POS_TOP || (p) == GTK_POS_BOTTOM)
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
typedef struct _GtkPopoverPrivate GtkPopoverPrivate;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
enum {
|
|
|
|
PROP_RELATIVE_TO = 1,
|
|
|
|
PROP_POINTING_TO,
|
2014-01-10 11:04:17 +00:00
|
|
|
PROP_POSITION,
|
|
|
|
PROP_MODAL
|
2013-01-11 16:32:08 +00:00
|
|
|
};
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
struct _GtkPopoverPrivate
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-11 09:36:51 +00:00
|
|
|
GtkWidget *widget;
|
|
|
|
GtkWindow *window;
|
2014-01-10 14:23:12 +00:00
|
|
|
GtkWidget *prev_focus_widget;
|
2014-01-22 15:43:37 +00:00
|
|
|
GtkScrollable *parent_scrollable;
|
|
|
|
GtkAdjustment *vadj;
|
|
|
|
GtkAdjustment *hadj;
|
2013-01-11 16:32:08 +00:00
|
|
|
cairo_rectangle_int_t pointing_to;
|
2013-11-13 09:47:13 +00:00
|
|
|
guint hierarchy_changed_id;
|
|
|
|
guint size_allocate_id;
|
|
|
|
guint unmap_id;
|
2014-01-22 15:43:37 +00:00
|
|
|
guint scrollable_notify_id;
|
2013-01-11 16:32:08 +00:00
|
|
|
guint has_pointing_to : 1;
|
|
|
|
guint preferred_position : 2;
|
|
|
|
guint final_position : 2;
|
2013-11-12 15:55:23 +00:00
|
|
|
guint current_position : 2;
|
2014-01-10 11:04:17 +00:00
|
|
|
guint modal : 1;
|
2014-01-28 13:05:08 +00:00
|
|
|
guint button_pressed : 1;
|
2013-01-11 16:32:08 +00:00
|
|
|
};
|
|
|
|
|
popover: Fix memory management of popovers
Popovers are strange in the sense that they aren't attached to a
parent directly, they rely on the relative_to widget so the toplevel
is shared, and when they have a parent, it is the toplevel itself,
not relative_to. This also means that there are conditions where the
popover loses it's parent, so they must survive unparenting.
The previous code would be floating the last reference as soon as the
parent is gone, but it was non-obvious who'd own that reference. So
fix this situation by granting the ownership of popovers to their
relative_to widget, an extra reference may be held by the toplevel
when the popover has a parent, but the popover object will be
guaranteed to be alive as long as the parent lives.
This way, memory management of popovers is as hidden from the user
as regular widgets within containers are, users are free to call
gtk_widget_destroy() on a popover, but it'd eventually become
destructed when relative_to is.
2014-01-09 16:21:43 +00:00
|
|
|
static GQuark quark_widget_popovers = 0;
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
static void gtk_popover_update_position (GtkPopover *popover);
|
2013-11-13 09:47:13 +00:00
|
|
|
static void gtk_popover_update_relative_to (GtkPopover *popover,
|
|
|
|
GtkWidget *relative_to);
|
2013-11-12 15:55:23 +00:00
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GtkPopover, gtk_popover, GTK_TYPE_BIN)
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_init (GtkPopover *popover)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2014-01-10 11:04:17 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
GtkWidget *widget;
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
widget = GTK_WIDGET (popover);
|
2013-11-11 09:36:51 +00:00
|
|
|
gtk_widget_set_has_window (widget, TRUE);
|
2014-01-10 11:04:17 +00:00
|
|
|
popover->priv = priv = gtk_popover_get_instance_private (popover);
|
|
|
|
priv->modal = TRUE;
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_set_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_RELATIVE_TO:
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_set_relative_to (GTK_POPOVER (object),
|
|
|
|
g_value_get_object (value));
|
2013-01-11 16:32:08 +00:00
|
|
|
break;
|
|
|
|
case PROP_POINTING_TO:
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_set_pointing_to (GTK_POPOVER (object),
|
|
|
|
g_value_get_boxed (value));
|
2013-01-11 16:32:08 +00:00
|
|
|
break;
|
|
|
|
case PROP_POSITION:
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_set_position (GTK_POPOVER (object),
|
|
|
|
g_value_get_enum (value));
|
2013-01-11 16:32:08 +00:00
|
|
|
break;
|
2014-01-10 11:04:17 +00:00
|
|
|
case PROP_MODAL:
|
|
|
|
gtk_popover_set_modal (GTK_POPOVER (object),
|
|
|
|
g_value_get_boolean (value));
|
|
|
|
break;
|
2013-01-11 16:32:08 +00:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv = GTK_POPOVER (object)->priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_RELATIVE_TO:
|
2013-11-11 13:27:19 +00:00
|
|
|
g_value_set_object (value, priv->widget);
|
2013-01-11 16:32:08 +00:00
|
|
|
break;
|
|
|
|
case PROP_POINTING_TO:
|
|
|
|
g_value_set_boxed (value, &priv->pointing_to);
|
|
|
|
break;
|
|
|
|
case PROP_POSITION:
|
|
|
|
g_value_set_enum (value, priv->preferred_position);
|
|
|
|
break;
|
2014-01-10 11:04:17 +00:00
|
|
|
case PROP_MODAL:
|
|
|
|
g_value_set_boolean (value, priv->modal);
|
|
|
|
break;
|
2013-01-11 16:32:08 +00:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_finalize (GObject *object)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopover *popover = GTK_POPOVER (object);
|
2013-11-13 09:47:13 +00:00
|
|
|
GtkPopoverPrivate *priv = popover->priv;
|
|
|
|
|
|
|
|
if (priv->widget)
|
|
|
|
gtk_popover_update_relative_to (popover, NULL);
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
G_OBJECT_CLASS (gtk_popover_parent_class)->finalize (object);
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
2013-11-11 09:36:51 +00:00
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_dispose (GObject *object)
|
2013-11-11 09:36:51 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopover *popover = GTK_POPOVER (object);
|
|
|
|
GtkPopoverPrivate *priv = popover->priv;
|
2013-11-11 09:36:51 +00:00
|
|
|
|
|
|
|
if (priv->window)
|
2014-01-17 17:12:31 +00:00
|
|
|
_gtk_window_remove_popover (priv->window, GTK_WIDGET (object));
|
2013-11-11 09:36:51 +00:00
|
|
|
|
|
|
|
priv->window = NULL;
|
2013-11-13 09:47:13 +00:00
|
|
|
|
|
|
|
if (priv->widget)
|
|
|
|
gtk_popover_update_relative_to (popover, NULL);
|
2013-11-11 09:36:51 +00:00
|
|
|
|
2014-01-10 14:23:12 +00:00
|
|
|
if (priv->prev_focus_widget)
|
|
|
|
{
|
|
|
|
g_object_unref (priv->prev_focus_widget);
|
|
|
|
priv->prev_focus_widget = NULL;
|
|
|
|
}
|
|
|
|
|
popover: Fix memory management of popovers
Popovers are strange in the sense that they aren't attached to a
parent directly, they rely on the relative_to widget so the toplevel
is shared, and when they have a parent, it is the toplevel itself,
not relative_to. This also means that there are conditions where the
popover loses it's parent, so they must survive unparenting.
The previous code would be floating the last reference as soon as the
parent is gone, but it was non-obvious who'd own that reference. So
fix this situation by granting the ownership of popovers to their
relative_to widget, an extra reference may be held by the toplevel
when the popover has a parent, but the popover object will be
guaranteed to be alive as long as the parent lives.
This way, memory management of popovers is as hidden from the user
as regular widgets within containers are, users are free to call
gtk_widget_destroy() on a popover, but it'd eventually become
destructed when relative_to is.
2014-01-09 16:21:43 +00:00
|
|
|
gtk_widget_set_visible (GTK_WIDGET (object), FALSE);
|
2013-11-12 15:55:23 +00:00
|
|
|
G_OBJECT_CLASS (gtk_popover_parent_class)->dispose (object);
|
2013-11-11 09:36:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_realize (GtkWidget *widget)
|
2013-11-11 09:36:51 +00:00
|
|
|
{
|
|
|
|
GtkAllocation allocation;
|
|
|
|
GdkWindowAttr attributes;
|
|
|
|
gint attributes_mask;
|
|
|
|
GdkWindow *window;
|
|
|
|
|
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
|
|
|
|
attributes.x = 0;
|
|
|
|
attributes.y = 0;
|
|
|
|
attributes.width = allocation.width;
|
|
|
|
attributes.height = allocation.height;
|
|
|
|
attributes.window_type = GDK_WINDOW_CHILD;
|
|
|
|
attributes.visual = gtk_widget_get_visual (widget);
|
|
|
|
attributes.wclass = GDK_INPUT_OUTPUT;
|
|
|
|
attributes.event_mask =
|
|
|
|
gtk_widget_get_events (widget) |
|
|
|
|
GDK_BUTTON_MOTION_MASK |
|
|
|
|
GDK_BUTTON_PRESS_MASK |
|
|
|
|
GDK_BUTTON_RELEASE_MASK |
|
|
|
|
GDK_EXPOSURE_MASK |
|
|
|
|
GDK_ENTER_NOTIFY_MASK |
|
|
|
|
GDK_LEAVE_NOTIFY_MASK;
|
|
|
|
|
|
|
|
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
|
|
|
|
window = gdk_window_new (gtk_widget_get_parent_window (widget),
|
|
|
|
&attributes, attributes_mask);
|
|
|
|
gtk_widget_set_window (widget, window);
|
|
|
|
gtk_widget_register_window (widget, window);
|
|
|
|
gtk_widget_set_realized (widget, TRUE);
|
|
|
|
}
|
|
|
|
|
2014-01-10 14:23:12 +00:00
|
|
|
static void
|
|
|
|
gtk_popover_apply_modality (GtkPopover *popover,
|
|
|
|
gboolean modal)
|
|
|
|
{
|
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
|
|
|
|
priv = popover->priv;
|
|
|
|
|
|
|
|
if (modal)
|
|
|
|
{
|
|
|
|
GtkWidget *prev_focus;
|
|
|
|
|
|
|
|
prev_focus = gtk_window_get_focus (priv->window);
|
2014-01-30 13:11:41 +00:00
|
|
|
priv->prev_focus_widget = prev_focus;
|
|
|
|
if (priv->prev_focus_widget)
|
|
|
|
g_object_ref (prev_focus);
|
2014-01-10 14:23:12 +00:00
|
|
|
gtk_grab_add (GTK_WIDGET (popover));
|
|
|
|
gtk_widget_grab_focus (GTK_WIDGET (popover));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gtk_grab_remove (GTK_WIDGET (popover));
|
|
|
|
|
|
|
|
/* Let prev_focus_widget regain focus */
|
|
|
|
if (priv->prev_focus_widget)
|
|
|
|
{
|
|
|
|
gtk_widget_grab_focus (priv->prev_focus_widget);
|
|
|
|
g_object_unref (priv->prev_focus_widget);
|
|
|
|
priv->prev_focus_widget = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-11 09:36:51 +00:00
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_map (GtkWidget *widget)
|
2013-11-11 09:36:51 +00:00
|
|
|
{
|
2014-01-10 11:04:17 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
|
|
|
|
priv = GTK_POPOVER (widget)->priv;
|
2013-11-12 15:55:23 +00:00
|
|
|
GTK_WIDGET_CLASS (gtk_popover_parent_class)->map (widget);
|
2013-11-11 09:36:51 +00:00
|
|
|
gdk_window_show (gtk_widget_get_window (widget));
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_update_position (GTK_POPOVER (widget));
|
2014-01-10 11:04:17 +00:00
|
|
|
|
|
|
|
if (priv->modal)
|
2014-01-10 14:23:12 +00:00
|
|
|
gtk_popover_apply_modality (GTK_POPOVER (widget), TRUE);
|
2013-11-11 09:36:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_unmap (GtkWidget *widget)
|
2013-11-11 09:36:51 +00:00
|
|
|
{
|
2014-01-10 14:23:12 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
|
|
|
|
priv = GTK_POPOVER (widget)->priv;
|
2014-01-28 13:05:08 +00:00
|
|
|
priv->button_pressed = FALSE;
|
2014-01-10 14:23:12 +00:00
|
|
|
|
|
|
|
if (priv->modal)
|
|
|
|
gtk_popover_apply_modality (GTK_POPOVER (widget), FALSE);
|
|
|
|
|
2013-11-11 09:36:51 +00:00
|
|
|
gdk_window_hide (gtk_widget_get_window (widget));
|
2013-11-12 15:55:23 +00:00
|
|
|
GTK_WIDGET_CLASS (gtk_popover_parent_class)->unmap (widget);
|
2013-11-11 09:36:51 +00:00
|
|
|
}
|
|
|
|
|
2013-01-11 16:32:08 +00:00
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_pointed_to_coords (GtkPopover *popover,
|
|
|
|
cairo_rectangle_int_t *rect_out)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv = popover->priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
cairo_rectangle_int_t rect;
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
if (!rect_out)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gtk_popover_get_pointing_to (popover, &rect);
|
2013-11-11 09:36:51 +00:00
|
|
|
gtk_widget_translate_coordinates (priv->widget, GTK_WIDGET (priv->window),
|
|
|
|
rect.x, rect.y, &rect.x, &rect.y);
|
2013-11-12 15:55:23 +00:00
|
|
|
*rect_out = rect;
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
2014-01-14 11:31:25 +00:00
|
|
|
static GtkPositionType
|
|
|
|
get_effective_position (GtkPopover *popover,
|
|
|
|
GtkPositionType pos)
|
|
|
|
{
|
|
|
|
if (gtk_widget_get_direction (GTK_WIDGET (popover)) == GTK_TEXT_DIR_RTL)
|
|
|
|
{
|
|
|
|
if (pos == GTK_POS_LEFT)
|
|
|
|
pos = GTK_POS_RIGHT;
|
|
|
|
else if (pos == GTK_POS_RIGHT)
|
|
|
|
pos = GTK_POS_LEFT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2014-01-14 17:02:38 +00:00
|
|
|
static void
|
|
|
|
get_margin (GtkWidget *widget,
|
|
|
|
GtkBorder *border)
|
|
|
|
{
|
|
|
|
GtkStyleContext *context;
|
|
|
|
GtkStateFlags state;
|
|
|
|
|
|
|
|
context = gtk_widget_get_style_context (widget);
|
|
|
|
state = gtk_widget_get_state_flags (widget);
|
|
|
|
gtk_style_context_get_margin (context, state, border);
|
|
|
|
}
|
|
|
|
|
2013-01-11 16:32:08 +00:00
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_gap_coords (GtkPopover *popover,
|
|
|
|
gint *initial_x_out,
|
|
|
|
gint *initial_y_out,
|
|
|
|
gint *tip_x_out,
|
|
|
|
gint *tip_y_out,
|
|
|
|
gint *final_x_out,
|
|
|
|
gint *final_y_out,
|
|
|
|
GtkPositionType *gap_side_out)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkWidget *widget = GTK_WIDGET (popover);
|
|
|
|
GtkPopoverPrivate *priv = popover->priv;
|
|
|
|
cairo_rectangle_int_t rect;
|
|
|
|
gint base, tip, tip_pos;
|
2013-03-05 16:13:54 +00:00
|
|
|
gint initial_x, initial_y;
|
|
|
|
gint tip_x, tip_y;
|
|
|
|
gint final_x, final_y;
|
2014-01-14 11:31:25 +00:00
|
|
|
GtkPositionType gap_side, pos;
|
2013-03-05 16:13:54 +00:00
|
|
|
GtkAllocation allocation;
|
2013-11-12 11:41:24 +00:00
|
|
|
gint border_radius;
|
2014-01-22 17:32:16 +00:00
|
|
|
GtkStateFlags state;
|
|
|
|
GtkStyleContext *context;
|
|
|
|
GtkBorder margin, border;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_pointing_to (popover, &rect);
|
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
gtk_widget_translate_coordinates (priv->widget, widget,
|
|
|
|
rect.x, rect.y, &rect.x, &rect.y);
|
2014-01-14 17:02:38 +00:00
|
|
|
get_margin (widget, &margin);
|
2013-03-05 16:13:54 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
|
|
|
|
rect.x += gtk_widget_get_margin_start (widget);
|
|
|
|
else
|
|
|
|
rect.x += gtk_widget_get_margin_end (widget);
|
|
|
|
|
|
|
|
rect.y += gtk_widget_get_margin_top (widget);
|
|
|
|
|
2014-01-22 17:32:16 +00:00
|
|
|
context = gtk_widget_get_style_context (GTK_WIDGET (popover));
|
|
|
|
state = gtk_widget_get_state_flags (GTK_WIDGET (popover));
|
|
|
|
|
|
|
|
gtk_style_context_get_border (context, state, &border);
|
|
|
|
gtk_style_context_get (context, state,
|
2013-11-12 11:41:24 +00:00
|
|
|
GTK_STYLE_PROPERTY_BORDER_RADIUS, &border_radius,
|
|
|
|
NULL);
|
2014-01-14 11:31:25 +00:00
|
|
|
pos = get_effective_position (popover, priv->final_position);
|
2013-11-12 11:41:24 +00:00
|
|
|
|
2014-01-14 11:31:25 +00:00
|
|
|
if (pos == GTK_POS_BOTTOM || pos == GTK_POS_RIGHT)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2014-01-22 17:32:16 +00:00
|
|
|
base = TAIL_HEIGHT + ((pos == GTK_POS_BOTTOM) ? border.top : border.left);
|
2013-01-11 16:32:08 +00:00
|
|
|
tip = 0;
|
2013-03-05 16:13:54 +00:00
|
|
|
gap_side = (priv->final_position == GTK_POS_BOTTOM) ? GTK_POS_TOP : GTK_POS_LEFT;
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
2014-01-14 11:31:25 +00:00
|
|
|
else if (pos == GTK_POS_TOP)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2014-01-22 17:32:16 +00:00
|
|
|
base = allocation.height - TAIL_HEIGHT - border.bottom;
|
2013-03-05 16:13:54 +00:00
|
|
|
tip = allocation.height;
|
|
|
|
gap_side = GTK_POS_BOTTOM;
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
2014-01-14 11:31:25 +00:00
|
|
|
else if (pos == GTK_POS_LEFT)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2014-01-22 17:32:16 +00:00
|
|
|
base = allocation.width - TAIL_HEIGHT - border.right;
|
2013-03-05 16:13:54 +00:00
|
|
|
tip = allocation.width;
|
|
|
|
gap_side = GTK_POS_RIGHT;
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
2013-11-12 15:55:23 +00:00
|
|
|
else
|
|
|
|
g_assert_not_reached ();
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2014-01-14 11:31:25 +00:00
|
|
|
if (POS_IS_VERTICAL (pos))
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
tip_pos = rect.x + (rect.width / 2);
|
|
|
|
initial_x = CLAMP (tip_pos - TAIL_GAP_WIDTH / 2,
|
2014-01-14 17:02:38 +00:00
|
|
|
border_radius + margin.left,
|
|
|
|
allocation.width - TAIL_GAP_WIDTH - margin.right - border_radius);
|
2013-03-05 16:13:54 +00:00
|
|
|
initial_y = base;
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
tip_x = CLAMP (tip_pos, 0, allocation.width);
|
2013-03-05 16:13:54 +00:00
|
|
|
tip_y = tip;
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
final_x = CLAMP (tip_pos + TAIL_GAP_WIDTH / 2,
|
2014-01-14 17:02:38 +00:00
|
|
|
border_radius + margin.left + TAIL_GAP_WIDTH,
|
|
|
|
allocation.width - margin.right - border_radius);
|
2013-03-05 16:13:54 +00:00
|
|
|
final_y = base;
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
tip_pos = rect.y + (rect.height / 2);
|
|
|
|
|
2013-03-05 16:13:54 +00:00
|
|
|
initial_x = base;
|
2013-11-12 15:55:23 +00:00
|
|
|
initial_y = CLAMP (tip_pos - TAIL_GAP_WIDTH / 2,
|
2014-01-14 17:02:38 +00:00
|
|
|
border_radius + margin.top,
|
|
|
|
allocation.height - TAIL_GAP_WIDTH - margin.bottom - border_radius);
|
2013-03-05 16:13:54 +00:00
|
|
|
|
|
|
|
tip_x = tip;
|
2013-11-12 15:55:23 +00:00
|
|
|
tip_y = CLAMP (tip_pos, 0, allocation.height);
|
2013-03-05 16:13:54 +00:00
|
|
|
|
|
|
|
final_x = base;
|
2013-11-12 15:55:23 +00:00
|
|
|
final_y = CLAMP (tip_pos + TAIL_GAP_WIDTH / 2,
|
2014-01-14 17:02:38 +00:00
|
|
|
border_radius + margin.top + TAIL_GAP_WIDTH,
|
|
|
|
allocation.height - margin.right - border_radius);
|
2013-03-05 16:13:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (initial_x_out)
|
|
|
|
*initial_x_out = initial_x;
|
|
|
|
if (initial_y_out)
|
|
|
|
*initial_y_out = initial_y;
|
|
|
|
|
|
|
|
if (tip_x_out)
|
|
|
|
*tip_x_out = tip_x;
|
|
|
|
if (tip_y_out)
|
|
|
|
*tip_y_out = tip_y;
|
|
|
|
|
|
|
|
if (final_x_out)
|
|
|
|
*final_x_out = final_x;
|
|
|
|
if (final_y_out)
|
|
|
|
*final_y_out = final_y;
|
|
|
|
|
|
|
|
if (gap_side_out)
|
|
|
|
*gap_side_out = gap_side;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_rect_coords (GtkPopover *popover,
|
|
|
|
gint *x1_out,
|
|
|
|
gint *y1_out,
|
|
|
|
gint *x2_out,
|
|
|
|
gint *y2_out)
|
2013-03-05 16:13:54 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkWidget *widget = GTK_WIDGET (popover);
|
|
|
|
GtkPopoverPrivate *priv = popover->priv;
|
2013-03-05 16:13:54 +00:00
|
|
|
GtkAllocation allocation;
|
2014-01-14 11:31:25 +00:00
|
|
|
GtkPositionType pos;
|
2013-11-12 15:55:23 +00:00
|
|
|
gint x1, x2, y1, y2;
|
2014-01-14 17:02:38 +00:00
|
|
|
GtkBorder margin;
|
2013-03-05 16:13:54 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
2014-01-14 17:02:38 +00:00
|
|
|
get_margin (widget, &margin);
|
2013-11-12 15:55:23 +00:00
|
|
|
|
|
|
|
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
|
|
|
|
x1 = gtk_widget_get_margin_start (widget);
|
|
|
|
else
|
|
|
|
x1 = gtk_widget_get_margin_end (widget);
|
|
|
|
|
|
|
|
y1 = gtk_widget_get_margin_top (widget);
|
|
|
|
x2 = allocation.width -
|
|
|
|
gtk_widget_get_margin_end (widget) + x1;
|
|
|
|
y2 = allocation.height -
|
|
|
|
gtk_widget_get_margin_bottom (widget) + y1;
|
2013-03-05 16:13:54 +00:00
|
|
|
|
2014-01-14 11:31:25 +00:00
|
|
|
pos = get_effective_position (popover, priv->final_position);
|
|
|
|
|
|
|
|
if (pos == GTK_POS_TOP)
|
2014-01-14 17:02:38 +00:00
|
|
|
y2 -= MAX (TAIL_HEIGHT, margin.bottom);
|
2014-01-14 11:31:25 +00:00
|
|
|
else if (pos == GTK_POS_BOTTOM)
|
2014-01-14 17:02:38 +00:00
|
|
|
y1 += MAX (TAIL_HEIGHT, margin.top);
|
2014-01-14 11:31:25 +00:00
|
|
|
else if (pos == GTK_POS_LEFT)
|
2014-01-14 17:02:38 +00:00
|
|
|
x2 -= MAX (TAIL_HEIGHT, margin.right);
|
2014-01-14 11:31:25 +00:00
|
|
|
else if (pos == GTK_POS_RIGHT)
|
2014-01-14 17:02:38 +00:00
|
|
|
x1 += MAX (TAIL_HEIGHT, margin.left);
|
|
|
|
|
|
|
|
if (pos != GTK_POS_BOTTOM)
|
|
|
|
y1 += margin.top;
|
|
|
|
if (pos != GTK_POS_TOP)
|
|
|
|
y2 -= margin.bottom;
|
|
|
|
if (pos != GTK_POS_RIGHT)
|
|
|
|
x1 += margin.left;
|
|
|
|
if (pos != GTK_POS_LEFT)
|
|
|
|
x2 -= margin.right;
|
2013-03-05 16:13:54 +00:00
|
|
|
|
|
|
|
if (x1_out)
|
|
|
|
*x1_out = x1;
|
|
|
|
if (y1_out)
|
|
|
|
*y1_out = y1;
|
|
|
|
if (x2_out)
|
|
|
|
*x2_out = x2;
|
|
|
|
if (y2_out)
|
|
|
|
*y2_out = y2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_apply_tail_path (GtkPopover *popover,
|
|
|
|
cairo_t *cr)
|
2013-03-05 16:13:54 +00:00
|
|
|
{
|
|
|
|
gint initial_x, initial_y;
|
|
|
|
gint tip_x, tip_y;
|
|
|
|
gint final_x, final_y;
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_gap_coords (popover,
|
|
|
|
&initial_x, &initial_y,
|
|
|
|
&tip_x, &tip_y,
|
|
|
|
&final_x, &final_y,
|
|
|
|
NULL);
|
2013-03-05 16:13:54 +00:00
|
|
|
|
|
|
|
cairo_move_to (cr, initial_x, initial_y);
|
|
|
|
cairo_line_to (cr, tip_x, tip_y);
|
|
|
|
cairo_line_to (cr, final_x, final_y);
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_apply_border_path (GtkPopover *popover,
|
|
|
|
cairo_t *cr)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
GtkAllocation allocation;
|
2013-03-05 16:13:54 +00:00
|
|
|
gint x1, y1, x2, y2;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
priv = popover->priv;
|
|
|
|
gtk_widget_get_allocation (GTK_WIDGET (popover), &allocation);
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_apply_tail_path (popover, cr);
|
|
|
|
gtk_popover_get_rect_coords (popover, &x1, &y1, &x2, &y2);
|
2013-01-15 16:32:42 +00:00
|
|
|
|
2013-01-11 16:32:08 +00:00
|
|
|
if (priv->final_position == GTK_POS_TOP)
|
2013-01-15 16:32:42 +00:00
|
|
|
{
|
2013-03-05 16:13:54 +00:00
|
|
|
cairo_line_to (cr, x2, y2);
|
|
|
|
cairo_line_to (cr, x2, y1);
|
|
|
|
cairo_line_to (cr, x1, y1);
|
|
|
|
cairo_line_to (cr, x1, y2);
|
2013-01-15 16:32:42 +00:00
|
|
|
}
|
2013-01-11 16:32:08 +00:00
|
|
|
else if (priv->final_position == GTK_POS_BOTTOM)
|
2013-01-15 16:32:42 +00:00
|
|
|
{
|
2013-03-05 16:13:54 +00:00
|
|
|
cairo_line_to (cr, x2, y1);
|
|
|
|
cairo_line_to (cr, x2, y2);
|
|
|
|
cairo_line_to (cr, x1, y2);
|
|
|
|
cairo_line_to (cr, x1, y1);
|
2013-01-15 16:32:42 +00:00
|
|
|
}
|
2013-01-11 16:32:08 +00:00
|
|
|
else if (priv->final_position == GTK_POS_LEFT)
|
2013-01-15 16:32:42 +00:00
|
|
|
{
|
2013-03-05 16:13:54 +00:00
|
|
|
cairo_line_to (cr, x2, y2);
|
|
|
|
cairo_line_to (cr, x1, y2);
|
|
|
|
cairo_line_to (cr, x1, y1);
|
|
|
|
cairo_line_to (cr, x2, y1);
|
2013-01-15 16:32:42 +00:00
|
|
|
}
|
2013-01-11 16:32:08 +00:00
|
|
|
else if (priv->final_position == GTK_POS_RIGHT)
|
2013-01-15 16:32:42 +00:00
|
|
|
{
|
2013-03-05 16:13:54 +00:00
|
|
|
cairo_line_to (cr, x1, y1);
|
|
|
|
cairo_line_to (cr, x2, y1);
|
|
|
|
cairo_line_to (cr, x2, y2);
|
|
|
|
cairo_line_to (cr, x1, y2);
|
2013-01-15 16:32:42 +00:00
|
|
|
}
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-01-15 16:32:42 +00:00
|
|
|
cairo_close_path (cr);
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_update_shape (GtkPopover *popover)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
cairo_region_t *region;
|
|
|
|
GdkWindow *win;
|
|
|
|
cairo_t *cr;
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
win = gtk_widget_get_window (GTK_WIDGET (popover));
|
2013-01-11 16:32:08 +00:00
|
|
|
surface =
|
|
|
|
gdk_window_create_similar_surface (win,
|
|
|
|
CAIRO_CONTENT_COLOR_ALPHA,
|
|
|
|
gdk_window_get_width (win),
|
|
|
|
gdk_window_get_height (win));
|
|
|
|
|
|
|
|
cr = cairo_create (surface);
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_apply_border_path (popover, cr);
|
2013-01-11 16:32:08 +00:00
|
|
|
cairo_fill (cr);
|
|
|
|
cairo_destroy (cr);
|
|
|
|
|
|
|
|
region = gdk_cairo_region_create_from_surface (surface);
|
|
|
|
cairo_surface_destroy (surface);
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_widget_shape_combine_region (GTK_WIDGET (popover), region);
|
2013-01-11 16:32:08 +00:00
|
|
|
cairo_region_destroy (region);
|
|
|
|
}
|
|
|
|
|
2014-01-22 15:45:01 +00:00
|
|
|
static void
|
|
|
|
_gtk_popover_update_child_visible (GtkPopover *popover)
|
|
|
|
{
|
|
|
|
cairo_rectangle_int_t rect;
|
|
|
|
GtkAllocation allocation;
|
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
GtkWidget *parent;
|
|
|
|
|
|
|
|
priv = popover->priv;
|
|
|
|
|
|
|
|
if (!priv->parent_scrollable)
|
|
|
|
{
|
|
|
|
gtk_widget_set_child_visible (GTK_WIDGET (popover), TRUE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
parent = gtk_widget_get_parent (GTK_WIDGET (priv->parent_scrollable));
|
|
|
|
rect = priv->pointing_to;
|
|
|
|
|
|
|
|
gtk_widget_translate_coordinates (priv->widget, parent,
|
|
|
|
rect.x, rect.y, &rect.x, &rect.y);
|
|
|
|
|
|
|
|
gtk_widget_get_allocation (GTK_WIDGET (parent), &allocation);
|
|
|
|
|
|
|
|
if (rect.x < 0 || rect.x + rect.width > allocation.width ||
|
|
|
|
rect.y < 0 || rect.y + rect.height > allocation.height)
|
|
|
|
gtk_widget_set_child_visible (GTK_WIDGET (popover), FALSE);
|
|
|
|
else
|
|
|
|
gtk_widget_set_child_visible (GTK_WIDGET (popover), TRUE);
|
|
|
|
}
|
|
|
|
|
2013-01-11 16:32:08 +00:00
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_update_position (GtkPopover *popover)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkAllocation window_alloc;
|
2013-01-11 16:32:08 +00:00
|
|
|
cairo_rectangle_int_t rect;
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
2014-01-14 11:31:25 +00:00
|
|
|
GtkPositionType pos;
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkRequisition req;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
priv = popover->priv;
|
|
|
|
|
|
|
|
if (!priv->window)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gtk_widget_get_preferred_size (GTK_WIDGET (popover), NULL, &req);
|
2013-11-12 11:15:53 +00:00
|
|
|
gtk_widget_get_allocation (GTK_WIDGET (priv->window), &window_alloc);
|
2013-01-11 16:32:08 +00:00
|
|
|
priv->final_position = priv->preferred_position;
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_pointed_to_coords (popover, &rect);
|
2014-01-14 11:31:25 +00:00
|
|
|
pos = get_effective_position (popover, priv->preferred_position);
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
/* Check whether there's enough room on the
|
|
|
|
* preferred side, move to the opposite one if not.
|
|
|
|
*/
|
2014-01-14 11:31:25 +00:00
|
|
|
if (pos == GTK_POS_TOP && rect.y < req.height)
|
2013-01-11 16:32:08 +00:00
|
|
|
priv->final_position = GTK_POS_BOTTOM;
|
2014-01-14 11:31:25 +00:00
|
|
|
else if (pos == GTK_POS_BOTTOM && rect.y > window_alloc.height - req.height)
|
2013-01-11 16:32:08 +00:00
|
|
|
priv->final_position = GTK_POS_TOP;
|
2014-01-14 11:31:25 +00:00
|
|
|
else if (pos == GTK_POS_LEFT && rect.x < req.width)
|
|
|
|
priv->final_position = get_effective_position (popover, GTK_POS_RIGHT);
|
|
|
|
else if (pos == GTK_POS_RIGHT && rect.x > window_alloc.width - req.width)
|
|
|
|
priv->final_position = get_effective_position (popover, GTK_POS_LEFT);
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2014-01-17 17:12:31 +00:00
|
|
|
_gtk_window_set_popover_position (priv->window, GTK_WIDGET (popover),
|
|
|
|
priv->final_position, &rect);
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
if (priv->final_position != priv->current_position)
|
|
|
|
{
|
|
|
|
if (gtk_widget_is_drawable (GTK_WIDGET (popover)))
|
|
|
|
gtk_popover_update_shape (popover);
|
2013-11-11 09:36:51 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
priv->current_position = priv->final_position;
|
|
|
|
}
|
2014-01-22 15:45:01 +00:00
|
|
|
|
|
|
|
_gtk_popover_update_child_visible (popover);
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_draw (GtkWidget *widget,
|
|
|
|
cairo_t *cr)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
|
|
|
GtkStyleContext *context;
|
|
|
|
GtkAllocation allocation;
|
|
|
|
GtkWidget *child;
|
2013-03-05 16:13:54 +00:00
|
|
|
GtkBorder border;
|
|
|
|
GdkRGBA border_color;
|
|
|
|
gint rect_x1, rect_x2, rect_y1, rect_y2;
|
|
|
|
gint initial_x, initial_y, final_x, final_y;
|
|
|
|
gint gap_start, gap_end;
|
|
|
|
GtkPositionType gap_side;
|
|
|
|
GtkStateFlags state;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
context = gtk_widget_get_style_context (widget);
|
2013-03-05 16:13:54 +00:00
|
|
|
state = gtk_widget_get_state_flags (widget);
|
2013-01-11 16:32:08 +00:00
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
|
2014-01-22 17:32:16 +00:00
|
|
|
gtk_style_context_get_border (context, state, &border);
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_rect_coords (GTK_POPOVER (widget),
|
|
|
|
&rect_x1, &rect_y1,
|
|
|
|
&rect_x2, &rect_y2);
|
2013-03-05 16:13:54 +00:00
|
|
|
|
|
|
|
/* Render the rect background */
|
|
|
|
gtk_render_background (context, cr,
|
2014-01-22 17:32:16 +00:00
|
|
|
rect_x1 + border.left, rect_y1 + border.top,
|
|
|
|
rect_x2 - rect_x1 - border.left - border.right,
|
|
|
|
rect_y2 - rect_y1 - border.top - border.bottom);
|
2013-03-05 16:13:54 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_gap_coords (GTK_POPOVER (widget),
|
|
|
|
&initial_x, &initial_y,
|
|
|
|
NULL, NULL,
|
|
|
|
&final_x, &final_y,
|
|
|
|
&gap_side);
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-03-05 16:13:54 +00:00
|
|
|
if (POS_IS_VERTICAL (gap_side))
|
|
|
|
{
|
2014-01-14 17:02:38 +00:00
|
|
|
gap_start = initial_x - rect_x1;
|
|
|
|
gap_end = final_x - rect_x1;
|
2013-03-05 16:13:54 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-01-14 17:02:38 +00:00
|
|
|
gap_start = initial_y - rect_y1;
|
|
|
|
gap_end = final_y - rect_y1;
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 16:13:54 +00:00
|
|
|
/* Now render the frame, without the gap for the arrow tip */
|
|
|
|
gtk_render_frame_gap (context, cr,
|
|
|
|
rect_x1, rect_y1,
|
|
|
|
rect_x2 - rect_x1, rect_y2 - rect_y1,
|
|
|
|
gap_side,
|
|
|
|
gap_start, gap_end);
|
|
|
|
|
|
|
|
/* Clip to the arrow shape */
|
|
|
|
cairo_save (cr);
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_apply_tail_path (GTK_POPOVER (widget), cr);
|
2013-03-05 16:13:54 +00:00
|
|
|
cairo_clip (cr);
|
|
|
|
|
|
|
|
/* Render the arrow background */
|
|
|
|
gtk_render_background (context, cr,
|
|
|
|
0, 0,
|
2013-01-11 16:32:08 +00:00
|
|
|
allocation.width, allocation.height);
|
2013-01-15 16:32:42 +00:00
|
|
|
|
2013-03-05 16:13:54 +00:00
|
|
|
/* Render the border of the arrow tip */
|
|
|
|
if (border.bottom > 0)
|
|
|
|
{
|
|
|
|
gtk_style_context_get_border_color (context, state, &border_color);
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_apply_tail_path (GTK_POPOVER (widget), cr);
|
2013-03-05 16:13:54 +00:00
|
|
|
gdk_cairo_set_source_rgba (cr, &border_color);
|
|
|
|
|
2014-01-14 17:02:38 +00:00
|
|
|
cairo_set_line_width (cr, border.bottom + 1);
|
2013-03-05 16:13:54 +00:00
|
|
|
cairo_stroke (cr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We're done */
|
|
|
|
cairo_restore (cr);
|
2013-01-15 16:32:42 +00:00
|
|
|
|
2013-01-11 16:32:08 +00:00
|
|
|
child = gtk_bin_get_child (GTK_BIN (widget));
|
|
|
|
|
|
|
|
if (child)
|
|
|
|
gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-03-05 15:55:41 +00:00
|
|
|
static void
|
|
|
|
get_padding_and_border (GtkWidget *widget,
|
|
|
|
GtkBorder *border)
|
|
|
|
{
|
|
|
|
GtkStyleContext *context;
|
|
|
|
GtkStateFlags state;
|
2013-11-13 10:02:47 +00:00
|
|
|
gint border_width;
|
2013-03-05 15:55:41 +00:00
|
|
|
GtkBorder tmp;
|
|
|
|
|
|
|
|
context = gtk_widget_get_style_context (widget);
|
|
|
|
state = gtk_widget_get_state_flags (widget);
|
|
|
|
|
2013-11-13 10:02:47 +00:00
|
|
|
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
|
|
|
|
|
2013-03-05 15:55:41 +00:00
|
|
|
gtk_style_context_get_padding (context, state, border);
|
|
|
|
gtk_style_context_get_border (context, state, &tmp);
|
2013-11-13 10:02:47 +00:00
|
|
|
border->top += tmp.top + border_width;
|
|
|
|
border->right += tmp.right + border_width;
|
|
|
|
border->bottom += tmp.bottom + border_width;
|
|
|
|
border->left += tmp.left + border_width;
|
2013-03-05 15:55:41 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 15:02:47 +00:00
|
|
|
static gint
|
|
|
|
get_border_radius (GtkWidget *widget)
|
|
|
|
{
|
|
|
|
GtkStyleContext *context;
|
|
|
|
GtkStateFlags state;
|
|
|
|
gint border_radius;
|
|
|
|
|
|
|
|
context = gtk_widget_get_style_context (widget);
|
|
|
|
state = gtk_widget_get_state_flags (widget);
|
|
|
|
gtk_style_context_get (context, state,
|
|
|
|
GTK_STYLE_PROPERTY_BORDER_RADIUS, &border_radius,
|
|
|
|
NULL);
|
|
|
|
return border_radius;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint
|
|
|
|
get_minimal_size (GtkPopover *popover,
|
|
|
|
GtkOrientation orientation)
|
|
|
|
{
|
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
GtkPositionType pos;
|
|
|
|
gint minimal_size;
|
|
|
|
|
|
|
|
priv = gtk_popover_get_instance_private (popover);
|
|
|
|
minimal_size = 2 * get_border_radius (GTK_WIDGET (popover));
|
|
|
|
pos = get_effective_position (popover, priv->preferred_position);
|
|
|
|
|
|
|
|
if ((orientation == GTK_ORIENTATION_HORIZONTAL && POS_IS_VERTICAL (pos)) ||
|
|
|
|
(orientation == GTK_ORIENTATION_VERTICAL && !POS_IS_VERTICAL (pos)))
|
|
|
|
minimal_size += TAIL_GAP_WIDTH;
|
|
|
|
|
|
|
|
return minimal_size;
|
|
|
|
}
|
|
|
|
|
2013-01-11 16:32:08 +00:00
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_preferred_width (GtkWidget *widget,
|
|
|
|
gint *minimum_width,
|
|
|
|
gint *natural_width)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
GtkWidget *child;
|
2014-01-14 17:02:38 +00:00
|
|
|
GtkPositionType pos;
|
2014-01-28 15:02:47 +00:00
|
|
|
gint min, nat, extra, minimal_size;
|
2014-01-14 17:02:38 +00:00
|
|
|
GtkBorder border, margin;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
priv = GTK_POPOVER (widget)->priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
child = gtk_bin_get_child (GTK_BIN (widget));
|
|
|
|
min = nat = 0;
|
|
|
|
|
|
|
|
if (child)
|
|
|
|
gtk_widget_get_preferred_width (child, &min, &nat);
|
|
|
|
|
2013-03-05 15:55:41 +00:00
|
|
|
get_padding_and_border (widget, &border);
|
2014-01-14 17:02:38 +00:00
|
|
|
get_margin (widget, &margin);
|
2014-01-28 15:02:47 +00:00
|
|
|
minimal_size = get_minimal_size (GTK_POPOVER (widget),
|
|
|
|
GTK_ORIENTATION_HORIZONTAL);
|
2014-01-14 17:02:38 +00:00
|
|
|
|
2014-01-28 15:02:47 +00:00
|
|
|
min = MAX (min, minimal_size) + border.left + border.right;
|
|
|
|
nat = MAX (nat, minimal_size) + border.left + border.right;
|
2013-03-05 15:55:41 +00:00
|
|
|
|
2014-01-14 17:02:38 +00:00
|
|
|
pos = get_effective_position (GTK_POPOVER (widget), priv->preferred_position);
|
|
|
|
|
|
|
|
if (pos == GTK_POS_LEFT)
|
|
|
|
extra = margin.left + MAX (TAIL_HEIGHT, margin.right);
|
|
|
|
else if (pos == GTK_POS_RIGHT)
|
|
|
|
extra = MAX (TAIL_HEIGHT, margin.left) + margin.right;
|
|
|
|
else
|
|
|
|
extra = margin.left + margin.right;
|
|
|
|
|
|
|
|
min += extra;
|
|
|
|
nat += extra;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
if (minimum_width)
|
2014-01-28 15:02:47 +00:00
|
|
|
*minimum_width = min;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
if (natural_width)
|
2014-01-28 15:02:47 +00:00
|
|
|
*natural_width = nat;
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 22:43:42 +00:00
|
|
|
static void
|
|
|
|
gtk_popover_get_preferred_width_for_height (GtkWidget *widget,
|
|
|
|
gint height,
|
|
|
|
gint *minimum_width,
|
|
|
|
gint *natural_width)
|
|
|
|
{
|
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
GtkWidget *child;
|
2014-01-14 17:02:38 +00:00
|
|
|
GtkPositionType pos;
|
2014-01-28 15:02:47 +00:00
|
|
|
gint min, nat, extra, minimal_size;
|
2014-01-10 22:43:42 +00:00
|
|
|
gint child_height;
|
2014-01-14 17:02:38 +00:00
|
|
|
GtkBorder border, margin;
|
2014-01-10 22:43:42 +00:00
|
|
|
|
|
|
|
priv = GTK_POPOVER (widget)->priv;
|
|
|
|
child = gtk_bin_get_child (GTK_BIN (widget));
|
|
|
|
min = nat = 0;
|
|
|
|
|
|
|
|
child_height = height;
|
|
|
|
|
|
|
|
if (POS_IS_VERTICAL (priv->preferred_position))
|
|
|
|
child_height -= TAIL_HEIGHT;
|
|
|
|
|
|
|
|
get_padding_and_border (widget, &border);
|
2014-01-14 17:02:38 +00:00
|
|
|
get_margin (widget, &margin);
|
2014-01-10 22:43:42 +00:00
|
|
|
child_height -= border.top + border.bottom;
|
2014-01-28 15:02:47 +00:00
|
|
|
minimal_size = get_minimal_size (GTK_POPOVER (widget),
|
|
|
|
GTK_ORIENTATION_HORIZONTAL);
|
2014-01-10 22:43:42 +00:00
|
|
|
|
|
|
|
if (child)
|
|
|
|
gtk_widget_get_preferred_width_for_height (child, child_height, &min, &nat);
|
|
|
|
|
2014-01-28 15:02:47 +00:00
|
|
|
min = MAX (min, minimal_size) + border.left + border.right;
|
|
|
|
nat = MAX (nat, minimal_size) + border.left + border.right;
|
2014-01-10 22:43:42 +00:00
|
|
|
|
2014-01-14 17:02:38 +00:00
|
|
|
pos = get_effective_position (GTK_POPOVER (widget), priv->preferred_position);
|
|
|
|
|
|
|
|
if (pos == GTK_POS_LEFT)
|
|
|
|
extra = margin.left + MAX (TAIL_HEIGHT, margin.right);
|
|
|
|
else if (pos == GTK_POS_RIGHT)
|
|
|
|
extra = MAX (TAIL_HEIGHT, margin.left) + margin.right;
|
|
|
|
else
|
|
|
|
extra = margin.left + margin.right;
|
|
|
|
|
|
|
|
min += extra;
|
|
|
|
nat += extra;
|
2014-01-10 22:43:42 +00:00
|
|
|
|
|
|
|
if (minimum_width)
|
2014-01-28 15:02:47 +00:00
|
|
|
*minimum_width = min;
|
2014-01-10 22:43:42 +00:00
|
|
|
|
|
|
|
if (natural_width)
|
2014-01-28 15:02:47 +00:00
|
|
|
*natural_width = nat;
|
2014-01-10 22:43:42 +00:00
|
|
|
}
|
|
|
|
|
2013-01-11 16:32:08 +00:00
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_preferred_height (GtkWidget *widget,
|
|
|
|
gint *minimum_height,
|
|
|
|
gint *natural_height)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
GtkWidget *child;
|
2014-01-14 17:02:38 +00:00
|
|
|
GtkPositionType pos;
|
2014-01-28 15:02:47 +00:00
|
|
|
gint min, nat, extra, minimal_size;
|
2014-01-14 17:02:38 +00:00
|
|
|
GtkBorder border, margin;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
priv = GTK_POPOVER (widget)->priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
child = gtk_bin_get_child (GTK_BIN (widget));
|
|
|
|
min = nat = 0;
|
|
|
|
|
|
|
|
if (child)
|
2013-03-05 15:55:41 +00:00
|
|
|
gtk_widget_get_preferred_height (child, &min, &nat);
|
|
|
|
|
|
|
|
get_padding_and_border (widget, &border);
|
2014-01-14 17:02:38 +00:00
|
|
|
get_margin (widget, &margin);
|
2014-01-28 15:02:47 +00:00
|
|
|
minimal_size = get_minimal_size (GTK_POPOVER (widget),
|
|
|
|
GTK_ORIENTATION_VERTICAL);
|
|
|
|
|
|
|
|
min = MAX (min, minimal_size) + border.top + border.bottom;
|
|
|
|
nat = MAX (nat, minimal_size) + border.top + border.bottom;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2014-01-14 17:02:38 +00:00
|
|
|
pos = get_effective_position (GTK_POPOVER (widget), priv->preferred_position);
|
|
|
|
|
|
|
|
if (pos == GTK_POS_TOP)
|
|
|
|
extra = margin.top + MAX (TAIL_HEIGHT, margin.bottom);
|
|
|
|
else if (pos == GTK_POS_BOTTOM)
|
|
|
|
extra = MAX (TAIL_HEIGHT, margin.top) + margin.bottom;
|
|
|
|
else
|
|
|
|
extra = margin.top + margin.bottom;
|
|
|
|
|
|
|
|
min += extra;
|
|
|
|
nat += extra;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
if (minimum_height)
|
2014-01-28 15:02:47 +00:00
|
|
|
*minimum_height = min;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
if (natural_height)
|
2014-01-28 15:02:47 +00:00
|
|
|
*natural_height = nat;
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 22:43:42 +00:00
|
|
|
static void
|
|
|
|
gtk_popover_get_preferred_height_for_width (GtkWidget *widget,
|
|
|
|
gint width,
|
|
|
|
gint *minimum_height,
|
|
|
|
gint *natural_height)
|
|
|
|
{
|
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
GtkWidget *child;
|
2014-01-14 17:02:38 +00:00
|
|
|
GtkPositionType pos;
|
2014-01-28 15:02:47 +00:00
|
|
|
gint min, nat, extra, minimal_size;
|
2014-01-10 22:43:42 +00:00
|
|
|
gint child_width;
|
2014-01-14 17:02:38 +00:00
|
|
|
GtkBorder border, margin;
|
2014-01-10 22:43:42 +00:00
|
|
|
|
|
|
|
priv = GTK_POPOVER (widget)->priv;
|
|
|
|
child = gtk_bin_get_child (GTK_BIN (widget));
|
|
|
|
min = nat = 0;
|
|
|
|
|
|
|
|
child_width = width;
|
|
|
|
|
|
|
|
if (!POS_IS_VERTICAL (priv->preferred_position))
|
|
|
|
child_width -= TAIL_HEIGHT;
|
|
|
|
|
|
|
|
get_padding_and_border (widget, &border);
|
2014-01-14 17:02:38 +00:00
|
|
|
get_margin (widget, &margin);
|
2014-01-10 22:43:42 +00:00
|
|
|
child_width -= border.left + border.right;
|
2014-01-28 15:02:47 +00:00
|
|
|
minimal_size = get_minimal_size (GTK_POPOVER (widget),
|
|
|
|
GTK_ORIENTATION_VERTICAL);
|
2014-01-10 22:43:42 +00:00
|
|
|
if (child)
|
|
|
|
gtk_widget_get_preferred_height_for_width (child, child_width, &min, &nat);
|
|
|
|
|
2014-01-28 15:02:47 +00:00
|
|
|
min = MAX (min, minimal_size) + border.top + border.bottom;
|
|
|
|
nat = MAX (nat, minimal_size) + border.top + border.bottom;
|
2014-01-10 22:43:42 +00:00
|
|
|
|
2014-01-14 17:02:38 +00:00
|
|
|
pos = get_effective_position (GTK_POPOVER (widget), priv->preferred_position);
|
|
|
|
|
|
|
|
if (pos == GTK_POS_TOP)
|
|
|
|
extra = margin.top + MAX (TAIL_HEIGHT, margin.bottom);
|
|
|
|
else if (pos == GTK_POS_BOTTOM)
|
|
|
|
extra = MAX (TAIL_HEIGHT, margin.top) + margin.bottom;
|
|
|
|
else
|
|
|
|
extra = margin.top + margin.bottom;
|
|
|
|
|
|
|
|
min += extra;
|
|
|
|
nat += extra;
|
2014-01-10 22:43:42 +00:00
|
|
|
|
|
|
|
if (minimum_height)
|
2014-01-28 15:02:47 +00:00
|
|
|
*minimum_height = min;
|
2014-01-10 22:43:42 +00:00
|
|
|
|
|
|
|
if (natural_height)
|
2014-01-28 15:02:47 +00:00
|
|
|
*natural_height = nat;
|
2014-01-10 22:43:42 +00:00
|
|
|
}
|
|
|
|
|
2013-01-11 16:32:08 +00:00
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_size_allocate (GtkWidget *widget,
|
|
|
|
GtkAllocation *allocation)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
|
|
|
GtkWidget *child;
|
|
|
|
|
|
|
|
gtk_widget_set_allocation (widget, allocation);
|
|
|
|
child = gtk_bin_get_child (GTK_BIN (widget));
|
|
|
|
if (child)
|
|
|
|
{
|
|
|
|
GtkAllocation child_alloc;
|
2013-11-12 15:55:23 +00:00
|
|
|
gint x1, y1, x2, y2;
|
2013-03-05 15:55:41 +00:00
|
|
|
GtkBorder border;
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_rect_coords (GTK_POPOVER (widget),
|
|
|
|
&x1, &y1, &x2, &y2);
|
2013-03-05 15:55:41 +00:00
|
|
|
get_padding_and_border (widget, &border);
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
child_alloc.x = x1 + border.left;
|
|
|
|
child_alloc.y = y1 + border.top;
|
|
|
|
child_alloc.width = (x2 - x1) - border.left - border.right;
|
|
|
|
child_alloc.height = (y2 - y1) - border.top - border.bottom;
|
2013-01-11 16:32:08 +00:00
|
|
|
gtk_widget_size_allocate (child, &child_alloc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gtk_widget_get_realized (widget))
|
2013-11-11 09:36:51 +00:00
|
|
|
{
|
|
|
|
gdk_window_move_resize (gtk_widget_get_window (widget),
|
|
|
|
0, 0, allocation->width, allocation->height);
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_update_shape (GTK_POPOVER (widget));
|
2013-11-11 09:36:51 +00:00
|
|
|
}
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_button_press (GtkWidget *widget,
|
|
|
|
GdkEventButton *event)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2014-01-28 13:05:08 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
|
|
|
|
if (event->type != GDK_BUTTON_PRESS)
|
|
|
|
return GDK_EVENT_PROPAGATE;
|
|
|
|
|
|
|
|
priv = gtk_popover_get_instance_private (GTK_POPOVER (widget));
|
|
|
|
priv->button_pressed = TRUE;
|
|
|
|
|
|
|
|
return GDK_EVENT_PROPAGATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gtk_popover_button_release (GtkWidget *widget,
|
|
|
|
GdkEventButton *event)
|
|
|
|
{
|
|
|
|
GtkPopoverPrivate *priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
GtkWidget *child;
|
|
|
|
|
2014-01-28 13:05:08 +00:00
|
|
|
priv = gtk_popover_get_instance_private (GTK_POPOVER (widget));
|
2013-01-11 16:32:08 +00:00
|
|
|
child = gtk_bin_get_child (GTK_BIN (widget));
|
|
|
|
|
2014-01-28 13:05:08 +00:00
|
|
|
if (!priv->button_pressed)
|
|
|
|
return GDK_EVENT_PROPAGATE;
|
|
|
|
|
2013-01-11 16:32:08 +00:00
|
|
|
if (child && event->window == gtk_widget_get_window (widget))
|
|
|
|
{
|
|
|
|
GtkAllocation child_alloc;
|
|
|
|
|
|
|
|
gtk_widget_get_allocation (child, &child_alloc);
|
|
|
|
|
|
|
|
if (event->x < child_alloc.x ||
|
|
|
|
event->x > child_alloc.x + child_alloc.width ||
|
|
|
|
event->y < child_alloc.y ||
|
|
|
|
event->y > child_alloc.y + child_alloc.height)
|
2013-11-12 13:57:39 +00:00
|
|
|
gtk_widget_hide (widget);
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
else
|
2013-11-12 13:57:39 +00:00
|
|
|
gtk_widget_hide (widget);
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
return GDK_EVENT_PROPAGATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_key_press (GtkWidget *widget,
|
|
|
|
GdkEventKey *event)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
|
|
|
if (event->keyval == GDK_KEY_Escape)
|
|
|
|
{
|
2013-11-12 13:57:39 +00:00
|
|
|
gtk_widget_hide (widget);
|
2013-01-11 16:32:08 +00:00
|
|
|
return GDK_EVENT_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return GDK_EVENT_PROPAGATE;
|
|
|
|
}
|
|
|
|
|
2014-01-09 11:53:29 +00:00
|
|
|
static void
|
|
|
|
gtk_popover_grab_focus (GtkWidget *widget)
|
|
|
|
{
|
|
|
|
/* Focus the first natural child */
|
|
|
|
gtk_widget_child_focus (gtk_bin_get_child (GTK_BIN (widget)),
|
|
|
|
GTK_DIR_TAB_FORWARD);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gtk_popover_focus (GtkWidget *widget,
|
|
|
|
GtkDirectionType direction)
|
|
|
|
{
|
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
|
|
|
|
priv = gtk_popover_get_instance_private (GTK_POPOVER (widget));
|
|
|
|
|
|
|
|
if (!GTK_WIDGET_CLASS (gtk_popover_parent_class)->focus (widget, direction))
|
|
|
|
{
|
|
|
|
GtkWidget *focus;
|
|
|
|
|
|
|
|
focus = gtk_window_get_focus (priv->window);
|
|
|
|
focus = gtk_widget_get_parent (focus);
|
|
|
|
|
|
|
|
/* Unset focus child through children, so it is next stepped from
|
|
|
|
* scratch.
|
|
|
|
*/
|
|
|
|
while (focus && focus != widget)
|
|
|
|
{
|
|
|
|
gtk_container_set_focus_child (GTK_CONTAINER (focus), NULL);
|
|
|
|
focus = gtk_widget_get_parent (focus);
|
|
|
|
}
|
|
|
|
|
|
|
|
return gtk_widget_child_focus (gtk_bin_get_child (GTK_BIN (widget)),
|
|
|
|
direction);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-11 16:32:08 +00:00
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_class_init (GtkPopoverClass *klass)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
object_class->set_property = gtk_popover_set_property;
|
|
|
|
object_class->get_property = gtk_popover_get_property;
|
|
|
|
object_class->finalize = gtk_popover_finalize;
|
|
|
|
object_class->dispose = gtk_popover_dispose;
|
|
|
|
|
|
|
|
widget_class->realize = gtk_popover_realize;
|
|
|
|
widget_class->map = gtk_popover_map;
|
|
|
|
widget_class->unmap = gtk_popover_unmap;
|
|
|
|
widget_class->get_preferred_width = gtk_popover_get_preferred_width;
|
|
|
|
widget_class->get_preferred_height = gtk_popover_get_preferred_height;
|
2014-01-10 22:43:42 +00:00
|
|
|
widget_class->get_preferred_width_for_height = gtk_popover_get_preferred_width_for_height;
|
|
|
|
widget_class->get_preferred_height_for_width = gtk_popover_get_preferred_height_for_width;
|
2013-11-12 15:55:23 +00:00
|
|
|
widget_class->size_allocate = gtk_popover_size_allocate;
|
|
|
|
widget_class->draw = gtk_popover_draw;
|
|
|
|
widget_class->button_press_event = gtk_popover_button_press;
|
2014-01-28 13:05:08 +00:00
|
|
|
widget_class->button_release_event = gtk_popover_button_release;
|
2013-11-12 15:55:23 +00:00
|
|
|
widget_class->key_press_event = gtk_popover_key_press;
|
2014-01-09 11:53:29 +00:00
|
|
|
widget_class->grab_focus = gtk_popover_grab_focus;
|
|
|
|
widget_class->focus = gtk_popover_focus;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-13 14:11:21 +00:00
|
|
|
/**
|
|
|
|
* GtkPopover:relative-to:
|
|
|
|
*
|
|
|
|
* Sets the attached widget.
|
|
|
|
*
|
|
|
|
* Since: 3.12
|
|
|
|
*/
|
2013-01-11 16:32:08 +00:00
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_RELATIVE_TO,
|
|
|
|
g_param_spec_object ("relative-to",
|
|
|
|
P_("Relative to"),
|
2013-11-13 14:11:21 +00:00
|
|
|
P_("Widget the bubble window points to"),
|
2013-11-11 13:27:19 +00:00
|
|
|
GTK_TYPE_WIDGET,
|
2013-01-11 16:32:08 +00:00
|
|
|
GTK_PARAM_READWRITE));
|
2013-11-13 14:11:21 +00:00
|
|
|
/**
|
|
|
|
* GtkPopover:pointing-to:
|
|
|
|
*
|
|
|
|
* Marks a specific rectangle to be pointed.
|
|
|
|
*
|
|
|
|
* Since: 3.12
|
|
|
|
*/
|
2013-01-11 16:32:08 +00:00
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_POINTING_TO,
|
|
|
|
g_param_spec_boxed ("pointing-to",
|
|
|
|
P_("Pointing to"),
|
|
|
|
P_("Rectangle the bubble window points to"),
|
|
|
|
CAIRO_GOBJECT_TYPE_RECTANGLE_INT,
|
|
|
|
GTK_PARAM_READWRITE));
|
2013-11-13 14:11:21 +00:00
|
|
|
/**
|
|
|
|
* GtkPopover:position
|
|
|
|
*
|
|
|
|
* Sets the preferred position of the popover.
|
|
|
|
*
|
|
|
|
* Since: 3.12
|
|
|
|
*/
|
2013-01-11 16:32:08 +00:00
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_POSITION,
|
|
|
|
g_param_spec_enum ("position",
|
|
|
|
P_("Position"),
|
|
|
|
P_("Position to place the bubble window"),
|
|
|
|
GTK_TYPE_POSITION_TYPE, GTK_POS_TOP,
|
|
|
|
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
popover: Fix memory management of popovers
Popovers are strange in the sense that they aren't attached to a
parent directly, they rely on the relative_to widget so the toplevel
is shared, and when they have a parent, it is the toplevel itself,
not relative_to. This also means that there are conditions where the
popover loses it's parent, so they must survive unparenting.
The previous code would be floating the last reference as soon as the
parent is gone, but it was non-obvious who'd own that reference. So
fix this situation by granting the ownership of popovers to their
relative_to widget, an extra reference may be held by the toplevel
when the popover has a parent, but the popover object will be
guaranteed to be alive as long as the parent lives.
This way, memory management of popovers is as hidden from the user
as regular widgets within containers are, users are free to call
gtk_widget_destroy() on a popover, but it'd eventually become
destructed when relative_to is.
2014-01-09 16:21:43 +00:00
|
|
|
|
2014-01-10 11:04:17 +00:00
|
|
|
/**
|
|
|
|
* GtkPopover:modal
|
|
|
|
*
|
2014-01-23 22:41:37 +00:00
|
|
|
* Sets whether the popover is modal (so other elements in the window do not
|
|
|
|
* receive input while the popover is visible).
|
2014-01-10 11:04:17 +00:00
|
|
|
*
|
|
|
|
* Since: 3.12
|
|
|
|
*/
|
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_MODAL,
|
|
|
|
g_param_spec_boolean ("modal",
|
|
|
|
P_("Modal"),
|
|
|
|
P_("Whether the popover is modal"),
|
|
|
|
TRUE,
|
|
|
|
GTK_PARAM_READWRITE));
|
|
|
|
|
popover: Fix memory management of popovers
Popovers are strange in the sense that they aren't attached to a
parent directly, they rely on the relative_to widget so the toplevel
is shared, and when they have a parent, it is the toplevel itself,
not relative_to. This also means that there are conditions where the
popover loses it's parent, so they must survive unparenting.
The previous code would be floating the last reference as soon as the
parent is gone, but it was non-obvious who'd own that reference. So
fix this situation by granting the ownership of popovers to their
relative_to widget, an extra reference may be held by the toplevel
when the popover has a parent, but the popover object will be
guaranteed to be alive as long as the parent lives.
This way, memory management of popovers is as hidden from the user
as regular widgets within containers are, users are free to call
gtk_widget_destroy() on a popover, but it'd eventually become
destructed when relative_to is.
2014-01-09 16:21:43 +00:00
|
|
|
quark_widget_popovers = g_quark_from_static_string ("gtk-quark-widget-popovers");
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
_gtk_popover_update_context_parent (GtkPopover *popover)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkStyleContext *context, *parent_context = NULL;
|
|
|
|
GtkPopoverPrivate *priv = popover->priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
context = gtk_widget_get_style_context (GTK_WIDGET (popover));
|
|
|
|
|
|
|
|
if (priv->widget)
|
|
|
|
parent_context = gtk_widget_get_style_context (priv->widget);
|
|
|
|
|
|
|
|
gtk_style_context_set_parent (context, parent_context);
|
|
|
|
}
|
|
|
|
|
2013-11-13 09:47:13 +00:00
|
|
|
static void
|
|
|
|
_gtk_popover_parent_hierarchy_changed (GtkWidget *widget,
|
|
|
|
GtkWidget *previous_toplevel,
|
|
|
|
GtkPopover *popover)
|
|
|
|
{
|
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
GtkWindow *new_window;
|
|
|
|
|
|
|
|
priv = popover->priv;
|
|
|
|
new_window = GTK_WINDOW (gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW));
|
|
|
|
|
|
|
|
if (priv->window == new_window)
|
|
|
|
return;
|
|
|
|
|
|
|
|
g_object_ref (popover);
|
|
|
|
|
|
|
|
if (priv->window)
|
2014-01-17 17:12:31 +00:00
|
|
|
_gtk_window_remove_popover (priv->window, GTK_WIDGET (popover));
|
2013-11-13 09:47:13 +00:00
|
|
|
|
|
|
|
if (new_window)
|
2014-01-17 17:12:31 +00:00
|
|
|
_gtk_window_add_popover (new_window, GTK_WIDGET (popover));
|
2013-11-13 09:47:13 +00:00
|
|
|
|
|
|
|
priv->window = new_window;
|
|
|
|
|
|
|
|
if (new_window)
|
|
|
|
gtk_popover_update_position (popover);
|
|
|
|
|
|
|
|
_gtk_popover_update_context_parent (popover);
|
|
|
|
|
|
|
|
if (gtk_widget_is_visible (GTK_WIDGET (popover)))
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (popover));
|
popover: Fix memory management of popovers
Popovers are strange in the sense that they aren't attached to a
parent directly, they rely on the relative_to widget so the toplevel
is shared, and when they have a parent, it is the toplevel itself,
not relative_to. This also means that there are conditions where the
popover loses it's parent, so they must survive unparenting.
The previous code would be floating the last reference as soon as the
parent is gone, but it was non-obvious who'd own that reference. So
fix this situation by granting the ownership of popovers to their
relative_to widget, an extra reference may be held by the toplevel
when the popover has a parent, but the popover object will be
guaranteed to be alive as long as the parent lives.
This way, memory management of popovers is as hidden from the user
as regular widgets within containers are, users are free to call
gtk_widget_destroy() on a popover, but it'd eventually become
destructed when relative_to is.
2014-01-09 16:21:43 +00:00
|
|
|
|
|
|
|
g_object_unref (popover);
|
2013-11-13 09:47:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_gtk_popover_parent_unmap (GtkWidget *widget,
|
|
|
|
GtkPopover *popover)
|
|
|
|
{
|
|
|
|
gtk_widget_unmap (GTK_WIDGET (popover));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_gtk_popover_parent_size_allocate (GtkWidget *widget,
|
|
|
|
GtkAllocation *allocation,
|
|
|
|
GtkPopover *popover)
|
|
|
|
{
|
|
|
|
gtk_popover_update_position (popover);
|
|
|
|
}
|
|
|
|
|
popover: Fix memory management of popovers
Popovers are strange in the sense that they aren't attached to a
parent directly, they rely on the relative_to widget so the toplevel
is shared, and when they have a parent, it is the toplevel itself,
not relative_to. This also means that there are conditions where the
popover loses it's parent, so they must survive unparenting.
The previous code would be floating the last reference as soon as the
parent is gone, but it was non-obvious who'd own that reference. So
fix this situation by granting the ownership of popovers to their
relative_to widget, an extra reference may be held by the toplevel
when the popover has a parent, but the popover object will be
guaranteed to be alive as long as the parent lives.
This way, memory management of popovers is as hidden from the user
as regular widgets within containers are, users are free to call
gtk_widget_destroy() on a popover, but it'd eventually become
destructed when relative_to is.
2014-01-09 16:21:43 +00:00
|
|
|
static void
|
|
|
|
widget_manage_popover (GtkWidget *widget,
|
|
|
|
GtkPopover *popover)
|
|
|
|
{
|
|
|
|
GHashTable *popovers;
|
|
|
|
|
|
|
|
popovers = g_object_get_qdata (G_OBJECT (widget), quark_widget_popovers);
|
|
|
|
|
|
|
|
if (G_UNLIKELY (!popovers))
|
|
|
|
{
|
|
|
|
popovers = g_hash_table_new_full (NULL, NULL,
|
|
|
|
(GDestroyNotify) g_object_unref, NULL);
|
|
|
|
g_object_set_qdata_full (G_OBJECT (widget),
|
|
|
|
quark_widget_popovers, popovers,
|
|
|
|
(GDestroyNotify) g_hash_table_unref);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_hash_table_add (popovers, g_object_ref_sink (popover));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
widget_unmanage_popover (GtkWidget *widget,
|
|
|
|
GtkPopover *popover)
|
|
|
|
{
|
|
|
|
GHashTable *popovers;
|
|
|
|
|
|
|
|
popovers = g_object_get_qdata (G_OBJECT (widget), quark_widget_popovers);
|
|
|
|
|
|
|
|
if (G_UNLIKELY (!popovers))
|
|
|
|
return;
|
|
|
|
|
|
|
|
g_hash_table_remove (popovers, popover);
|
|
|
|
}
|
|
|
|
|
2014-01-22 15:43:37 +00:00
|
|
|
static void
|
|
|
|
adjustment_changed_cb (GtkAdjustment *adjustment,
|
|
|
|
GtkPopover *popover)
|
|
|
|
{
|
|
|
|
gtk_popover_update_position (popover);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_gtk_popover_set_scrollable (GtkPopover *popover,
|
|
|
|
GtkScrollable *scrollable)
|
|
|
|
{
|
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
|
|
|
|
priv = popover->priv;
|
|
|
|
|
|
|
|
if (priv->parent_scrollable)
|
|
|
|
{
|
|
|
|
if (priv->vadj)
|
|
|
|
{
|
|
|
|
g_signal_handlers_disconnect_by_data (priv->vadj, popover);
|
|
|
|
g_object_unref (priv->vadj);
|
|
|
|
priv->vadj = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (priv->hadj)
|
|
|
|
{
|
|
|
|
g_signal_handlers_disconnect_by_data (priv->hadj, popover);
|
|
|
|
g_object_unref (priv->hadj);
|
|
|
|
priv->hadj = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
priv->parent_scrollable = scrollable;
|
|
|
|
|
|
|
|
if (scrollable)
|
|
|
|
{
|
|
|
|
priv->vadj = gtk_scrollable_get_vadjustment (scrollable);
|
|
|
|
priv->hadj = gtk_scrollable_get_hadjustment (scrollable);
|
|
|
|
|
|
|
|
if (priv->vadj)
|
|
|
|
{
|
|
|
|
g_object_ref (priv->vadj);
|
|
|
|
g_signal_connect (priv->vadj, "changed",
|
|
|
|
G_CALLBACK (adjustment_changed_cb), popover);
|
|
|
|
g_signal_connect (priv->vadj, "value-changed",
|
|
|
|
G_CALLBACK (adjustment_changed_cb), popover);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (priv->hadj)
|
|
|
|
{
|
|
|
|
g_object_ref (priv->hadj);
|
|
|
|
g_signal_connect (priv->hadj, "changed",
|
|
|
|
G_CALLBACK (adjustment_changed_cb), popover);
|
|
|
|
g_signal_connect (priv->hadj, "value-changed",
|
|
|
|
G_CALLBACK (adjustment_changed_cb), popover);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scrollable_notify_cb (GObject *object,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
GtkPopover *popover)
|
|
|
|
{
|
|
|
|
if (pspec->value_type == GTK_TYPE_ADJUSTMENT)
|
|
|
|
_gtk_popover_set_scrollable (popover, GTK_SCROLLABLE (object));
|
|
|
|
}
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
static void
|
|
|
|
gtk_popover_update_relative_to (GtkPopover *popover,
|
|
|
|
GtkWidget *relative_to)
|
|
|
|
{
|
2014-01-22 15:43:37 +00:00
|
|
|
GtkScrollable *scrollable = NULL;
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
|
|
|
|
priv = popover->priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-11 13:27:19 +00:00
|
|
|
if (priv->widget == relative_to)
|
2013-01-11 16:32:08 +00:00
|
|
|
return;
|
|
|
|
|
popover: Fix memory management of popovers
Popovers are strange in the sense that they aren't attached to a
parent directly, they rely on the relative_to widget so the toplevel
is shared, and when they have a parent, it is the toplevel itself,
not relative_to. This also means that there are conditions where the
popover loses it's parent, so they must survive unparenting.
The previous code would be floating the last reference as soon as the
parent is gone, but it was non-obvious who'd own that reference. So
fix this situation by granting the ownership of popovers to their
relative_to widget, an extra reference may be held by the toplevel
when the popover has a parent, but the popover object will be
guaranteed to be alive as long as the parent lives.
This way, memory management of popovers is as hidden from the user
as regular widgets within containers are, users are free to call
gtk_widget_destroy() on a popover, but it'd eventually become
destructed when relative_to is.
2014-01-09 16:21:43 +00:00
|
|
|
g_object_ref (popover);
|
|
|
|
|
2013-11-11 09:36:51 +00:00
|
|
|
if (priv->window)
|
|
|
|
{
|
2014-01-17 17:12:31 +00:00
|
|
|
_gtk_window_remove_popover (priv->window, GTK_WIDGET (popover));
|
2013-11-11 09:36:51 +00:00
|
|
|
priv->window = NULL;
|
|
|
|
}
|
|
|
|
|
2013-11-13 09:47:13 +00:00
|
|
|
if (priv->widget)
|
|
|
|
{
|
|
|
|
if (g_signal_handler_is_connected (priv->widget, priv->hierarchy_changed_id))
|
|
|
|
g_signal_handler_disconnect (priv->widget, priv->hierarchy_changed_id);
|
|
|
|
if (g_signal_handler_is_connected (priv->widget, priv->size_allocate_id))
|
|
|
|
g_signal_handler_disconnect (priv->widget, priv->size_allocate_id);
|
|
|
|
if (g_signal_handler_is_connected (priv->widget, priv->unmap_id))
|
|
|
|
g_signal_handler_disconnect (priv->widget, priv->unmap_id);
|
popover: Fix memory management of popovers
Popovers are strange in the sense that they aren't attached to a
parent directly, they rely on the relative_to widget so the toplevel
is shared, and when they have a parent, it is the toplevel itself,
not relative_to. This also means that there are conditions where the
popover loses it's parent, so they must survive unparenting.
The previous code would be floating the last reference as soon as the
parent is gone, but it was non-obvious who'd own that reference. So
fix this situation by granting the ownership of popovers to their
relative_to widget, an extra reference may be held by the toplevel
when the popover has a parent, but the popover object will be
guaranteed to be alive as long as the parent lives.
This way, memory management of popovers is as hidden from the user
as regular widgets within containers are, users are free to call
gtk_widget_destroy() on a popover, but it'd eventually become
destructed when relative_to is.
2014-01-09 16:21:43 +00:00
|
|
|
|
|
|
|
widget_unmanage_popover (priv->widget, popover);
|
2013-11-13 09:47:13 +00:00
|
|
|
}
|
|
|
|
|
2014-01-22 15:43:37 +00:00
|
|
|
if (priv->parent_scrollable)
|
|
|
|
{
|
|
|
|
if (g_signal_handler_is_connected (priv->parent_scrollable, priv->scrollable_notify_id))
|
|
|
|
g_signal_handler_disconnect (priv->parent_scrollable, priv->scrollable_notify_id);
|
|
|
|
_gtk_popover_set_scrollable (popover, NULL);
|
|
|
|
}
|
|
|
|
|
2013-11-11 13:27:19 +00:00
|
|
|
priv->widget = relative_to;
|
2013-11-12 15:55:23 +00:00
|
|
|
g_object_notify (G_OBJECT (popover), "relative-to");
|
2013-11-11 09:36:51 +00:00
|
|
|
|
2013-11-11 13:27:19 +00:00
|
|
|
if (priv->widget)
|
2013-11-13 09:47:13 +00:00
|
|
|
{
|
|
|
|
priv->window =
|
|
|
|
GTK_WINDOW (gtk_widget_get_ancestor (priv->widget, GTK_TYPE_WINDOW));
|
|
|
|
|
|
|
|
priv->hierarchy_changed_id =
|
|
|
|
g_signal_connect (priv->widget, "hierarchy-changed",
|
|
|
|
G_CALLBACK (_gtk_popover_parent_hierarchy_changed),
|
|
|
|
popover);
|
|
|
|
priv->size_allocate_id =
|
|
|
|
g_signal_connect (priv->widget, "size-allocate",
|
|
|
|
G_CALLBACK (_gtk_popover_parent_size_allocate),
|
|
|
|
popover);
|
|
|
|
priv->unmap_id =
|
|
|
|
g_signal_connect (priv->widget, "unmap",
|
|
|
|
G_CALLBACK (_gtk_popover_parent_unmap),
|
|
|
|
popover);
|
popover: Fix memory management of popovers
Popovers are strange in the sense that they aren't attached to a
parent directly, they rely on the relative_to widget so the toplevel
is shared, and when they have a parent, it is the toplevel itself,
not relative_to. This also means that there are conditions where the
popover loses it's parent, so they must survive unparenting.
The previous code would be floating the last reference as soon as the
parent is gone, but it was non-obvious who'd own that reference. So
fix this situation by granting the ownership of popovers to their
relative_to widget, an extra reference may be held by the toplevel
when the popover has a parent, but the popover object will be
guaranteed to be alive as long as the parent lives.
This way, memory management of popovers is as hidden from the user
as regular widgets within containers are, users are free to call
gtk_widget_destroy() on a popover, but it'd eventually become
destructed when relative_to is.
2014-01-09 16:21:43 +00:00
|
|
|
|
|
|
|
/* Give ownership of the popover to widget */
|
|
|
|
widget_manage_popover (priv->widget, popover);
|
2013-11-13 09:47:13 +00:00
|
|
|
}
|
2013-11-11 09:36:51 +00:00
|
|
|
|
|
|
|
if (priv->window)
|
2014-01-17 17:12:31 +00:00
|
|
|
_gtk_window_add_popover (priv->window, GTK_WIDGET (popover));
|
2013-11-12 15:55:23 +00:00
|
|
|
|
2014-01-22 15:43:37 +00:00
|
|
|
if (relative_to)
|
|
|
|
scrollable = GTK_SCROLLABLE (gtk_widget_get_ancestor (priv->widget, GTK_TYPE_SCROLLABLE));
|
|
|
|
|
|
|
|
if (scrollable)
|
|
|
|
{
|
|
|
|
_gtk_popover_set_scrollable (popover, scrollable);
|
|
|
|
|
|
|
|
priv->scrollable_notify_id =
|
|
|
|
g_signal_connect (priv->parent_scrollable, "notify",
|
|
|
|
G_CALLBACK (scrollable_notify_cb), popover);
|
|
|
|
}
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
_gtk_popover_update_context_parent (popover);
|
popover: Fix memory management of popovers
Popovers are strange in the sense that they aren't attached to a
parent directly, they rely on the relative_to widget so the toplevel
is shared, and when they have a parent, it is the toplevel itself,
not relative_to. This also means that there are conditions where the
popover loses it's parent, so they must survive unparenting.
The previous code would be floating the last reference as soon as the
parent is gone, but it was non-obvious who'd own that reference. So
fix this situation by granting the ownership of popovers to their
relative_to widget, an extra reference may be held by the toplevel
when the popover has a parent, but the popover object will be
guaranteed to be alive as long as the parent lives.
This way, memory management of popovers is as hidden from the user
as regular widgets within containers are, users are free to call
gtk_widget_destroy() on a popover, but it'd eventually become
destructed when relative_to is.
2014-01-09 16:21:43 +00:00
|
|
|
g_object_unref (popover);
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_update_pointing_to (GtkPopover *popover,
|
|
|
|
cairo_rectangle_int_t *pointing_to)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
priv = popover->priv;
|
2013-11-12 11:56:40 +00:00
|
|
|
|
|
|
|
if (pointing_to)
|
|
|
|
{
|
|
|
|
priv->pointing_to = *pointing_to;
|
|
|
|
priv->has_pointing_to = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
priv->has_pointing_to = FALSE;
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
g_object_notify (G_OBJECT (popover), "pointing-to");
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_update_preferred_position (GtkPopover *popover,
|
|
|
|
GtkPositionType position)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
priv = popover->priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
priv->preferred_position = position;
|
2013-11-12 15:55:23 +00:00
|
|
|
g_object_notify (G_OBJECT (popover), "position");
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
2013-11-13 14:11:21 +00:00
|
|
|
/**
|
2013-11-12 15:55:23 +00:00
|
|
|
* gtk_popover_new:
|
2013-11-13 14:11:21 +00:00
|
|
|
* @relative_to: #GtkWidget the popover is related to
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Creates a new popover to point to @relative_to
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-12 15:55:23 +00:00
|
|
|
* Returns: a new #GtkPopover
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Since: 3.12
|
|
|
|
**/
|
2013-01-11 16:32:08 +00:00
|
|
|
GtkWidget *
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_new (GtkWidget *relative_to)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
return g_object_new (GTK_TYPE_POPOVER,
|
2013-11-11 13:27:19 +00:00
|
|
|
"relative-to", relative_to,
|
|
|
|
NULL);
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
2013-11-13 14:11:21 +00:00
|
|
|
/**
|
2013-11-12 15:55:23 +00:00
|
|
|
* gtk_popover_set_relative_to:
|
2013-11-13 14:11:21 +00:00
|
|
|
* @popover: a #GtkPopover
|
|
|
|
* @relative_to: a #GtkWidget
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Sets a new widget to be attached to @popover. If @popover is
|
|
|
|
* visible, the position will be updated.
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Since: 3.12
|
|
|
|
**/
|
2013-01-11 16:32:08 +00:00
|
|
|
void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_set_relative_to (GtkPopover *popover,
|
|
|
|
GtkWidget *relative_to)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
g_return_if_fail (GTK_IS_POPOVER (popover));
|
2013-11-11 13:27:19 +00:00
|
|
|
g_return_if_fail (GTK_IS_WIDGET (relative_to));
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_update_relative_to (popover, relative_to);
|
|
|
|
gtk_popover_update_position (popover);
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
2013-11-13 14:11:21 +00:00
|
|
|
/**
|
2013-11-12 15:55:23 +00:00
|
|
|
* gtk_popover_get_relative_to:
|
2013-11-13 14:11:21 +00:00
|
|
|
* @popover: a #GtkPopover
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Returns the wigdet @popover is currently attached to
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2014-01-22 15:27:44 +00:00
|
|
|
* Returns: (transfer none): a #GtkWidget
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Since: 3.12
|
|
|
|
**/
|
2013-11-11 13:27:19 +00:00
|
|
|
GtkWidget *
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_relative_to (GtkPopover *popover)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
g_return_val_if_fail (GTK_IS_POPOVER (popover), NULL);
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
priv = popover->priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-11 13:27:19 +00:00
|
|
|
return priv->widget;
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
2013-11-13 14:11:21 +00:00
|
|
|
/**
|
2013-11-12 15:55:23 +00:00
|
|
|
* gtk_popover_set_pointing_to:
|
2013-11-13 14:11:21 +00:00
|
|
|
* @popover: a #GtkPopover
|
2013-01-11 16:32:08 +00:00
|
|
|
* @rect: rectangle to point to
|
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Sets the rectangle that @popover will point to, in the coordinate
|
|
|
|
* space of the widget @popover is attached to, see
|
|
|
|
* gtk_popover_set_relative_to()
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Since: 3.12
|
|
|
|
**/
|
2013-01-11 16:32:08 +00:00
|
|
|
void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_set_pointing_to (GtkPopover *popover,
|
|
|
|
cairo_rectangle_int_t *rect)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
g_return_if_fail (GTK_IS_POPOVER (popover));
|
2013-01-11 16:32:08 +00:00
|
|
|
g_return_if_fail (rect != NULL);
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_update_pointing_to (popover, rect);
|
|
|
|
gtk_popover_update_position (popover);
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
2013-11-13 14:11:21 +00:00
|
|
|
/**
|
2013-11-12 15:55:23 +00:00
|
|
|
* gtk_popover_get_pointing_to:
|
2013-11-13 14:11:21 +00:00
|
|
|
* @popover: a #GtkPopover
|
2013-01-11 16:32:08 +00:00
|
|
|
* @rect: (out): location to store the rectangle
|
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* If a rectangle to point to has been set, this function will
|
|
|
|
* return %TRUE and fill in @rect with such rectangle, otherwise
|
|
|
|
* it will return %FALSE and fill in @rect with the attached
|
|
|
|
* widget coordinates.
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Returns: %TRUE if a rectangle to point to was set.
|
|
|
|
**/
|
2013-01-11 16:32:08 +00:00
|
|
|
gboolean
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_pointing_to (GtkPopover *popover,
|
|
|
|
cairo_rectangle_int_t *rect)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
g_return_val_if_fail (GTK_IS_POPOVER (popover), FALSE);
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
priv = popover->priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
if (rect)
|
2013-11-12 11:56:40 +00:00
|
|
|
{
|
|
|
|
if (priv->has_pointing_to)
|
|
|
|
*rect = priv->pointing_to;
|
|
|
|
else if (priv->widget)
|
|
|
|
{
|
|
|
|
gtk_widget_get_allocation (priv->widget, rect);
|
|
|
|
rect->x = rect->y = 0;
|
|
|
|
}
|
|
|
|
}
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
return priv->has_pointing_to;
|
|
|
|
}
|
|
|
|
|
2013-11-13 14:11:21 +00:00
|
|
|
/**
|
2013-11-12 15:55:23 +00:00
|
|
|
* gtk_popover_set_position:
|
2013-11-13 14:11:21 +00:00
|
|
|
* @popover: a #GtkPopover
|
|
|
|
* @position: preferred popover position
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Sets the preferred position for @popover to appear. If the @popover
|
|
|
|
* is currently visible, it will be immediately updated.
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
|
|
|
* <note>
|
|
|
|
* This preference will be respected where possible, although
|
2013-11-13 14:11:21 +00:00
|
|
|
* on lack of space (eg. if close to the window edges), the
|
2013-11-12 15:55:23 +00:00
|
|
|
* #GtkPopover may choose to appear on the opposite side
|
2013-01-11 16:32:08 +00:00
|
|
|
* </note>
|
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Since: 3.12
|
|
|
|
**/
|
2013-01-11 16:32:08 +00:00
|
|
|
void
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_set_position (GtkPopover *popover,
|
|
|
|
GtkPositionType position)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
g_return_if_fail (GTK_IS_POPOVER (popover));
|
2013-01-11 16:32:08 +00:00
|
|
|
g_return_if_fail (position >= GTK_POS_LEFT && position <= GTK_POS_BOTTOM);
|
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_update_preferred_position (popover, position);
|
|
|
|
gtk_popover_update_position (popover);
|
2013-01-11 16:32:08 +00:00
|
|
|
}
|
|
|
|
|
2013-11-13 14:11:21 +00:00
|
|
|
/**
|
2013-11-12 15:55:23 +00:00
|
|
|
* gtk_popover_get_position:
|
2013-11-13 14:11:21 +00:00
|
|
|
* @popover: a #GtkPopover
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Returns the preferred position of @popover.
|
2013-01-11 16:32:08 +00:00
|
|
|
*
|
2013-11-13 14:11:21 +00:00
|
|
|
* Returns: The preferred position.
|
|
|
|
**/
|
2013-01-11 16:32:08 +00:00
|
|
|
GtkPositionType
|
2013-11-12 15:55:23 +00:00
|
|
|
gtk_popover_get_position (GtkPopover *popover)
|
2013-01-11 16:32:08 +00:00
|
|
|
{
|
2013-11-12 15:55:23 +00:00
|
|
|
GtkPopoverPrivate *priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
g_return_val_if_fail (GTK_IS_POPOVER (popover), GTK_POS_TOP);
|
2013-01-11 16:32:08 +00:00
|
|
|
|
2013-11-12 15:55:23 +00:00
|
|
|
priv = popover->priv;
|
2013-01-11 16:32:08 +00:00
|
|
|
|
|
|
|
return priv->preferred_position;
|
|
|
|
}
|
2014-01-10 11:04:17 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* gtk_popover_set_modal:
|
|
|
|
* @popover: a #GtkPopover
|
|
|
|
* @modal: #TRUE to make popover claim all input within the toplevel
|
|
|
|
*
|
|
|
|
* Sets whether @popover is modal, a modal popover will grab all input
|
|
|
|
* within the toplevel and grab the keyboard focus on it when being
|
|
|
|
* displayed. Clicking outside the popover area or pressing Esc will
|
|
|
|
* dismiss the popover and ungrab input.
|
|
|
|
*
|
|
|
|
* Since: 3.12
|
|
|
|
**/
|
|
|
|
void
|
|
|
|
gtk_popover_set_modal (GtkPopover *popover,
|
|
|
|
gboolean modal)
|
|
|
|
{
|
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (GTK_IS_POPOVER (popover));
|
|
|
|
|
|
|
|
priv = popover->priv;
|
|
|
|
|
|
|
|
if ((priv->modal == TRUE) == (modal == TRUE))
|
|
|
|
return;
|
|
|
|
|
|
|
|
priv->modal = (modal != FALSE);
|
|
|
|
|
|
|
|
if (gtk_widget_is_visible (GTK_WIDGET (popover)))
|
2014-01-10 14:23:12 +00:00
|
|
|
gtk_popover_apply_modality (popover, priv->modal);
|
2014-01-10 11:04:17 +00:00
|
|
|
|
|
|
|
g_object_notify (G_OBJECT (popover), "modal");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gtk_popover_get_modal:
|
|
|
|
* @popover: a #GtkPopover
|
|
|
|
*
|
|
|
|
* Returns whether the popover is modal, see gtk_popover_set_modal to
|
|
|
|
* see the implications of this.
|
|
|
|
*
|
|
|
|
* Returns: #TRUE if @popover is modal
|
|
|
|
*
|
|
|
|
* Since: 3.12
|
|
|
|
**/
|
|
|
|
gboolean
|
|
|
|
gtk_popover_get_modal (GtkPopover *popover)
|
|
|
|
{
|
|
|
|
GtkPopoverPrivate *priv;
|
|
|
|
|
|
|
|
g_return_val_if_fail (GTK_IS_POPOVER (popover), FALSE);
|
|
|
|
|
|
|
|
priv = popover->priv;
|
|
|
|
|
|
|
|
return priv->modal;
|
|
|
|
}
|