Merge branch 'tooltip-redo' into 'master'

Tooltip redo

See merge request GNOME/gtk!1271
This commit is contained in:
Matthias Clasen 2019-12-30 03:24:58 +00:00
commit 53a634e23e
8 changed files with 554 additions and 234 deletions

View File

@ -893,6 +893,16 @@
radioitem as proxy. radioitem as proxy.
</para> </para>
</section> </section>
<section>
<title>Stop using custom tooltip windows</title>
<para>
Tooltips no longer use GtkWindows in GTK 4, and it is no longer
possible to provide a custom window for tooltips. Replacing the content
of the tooltip with a custom widget is still possible, with
gtk_tooltip_set_custom().
</para>
</section>
</section> </section>
</chapter> </chapter>

View File

@ -87,6 +87,13 @@
#define GTK_IS_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TOOLTIP)) #define GTK_IS_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TOOLTIP))
#define GTK_TOOLTIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TOOLTIP, GtkTooltipClass)) #define GTK_TOOLTIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TOOLTIP, GtkTooltipClass))
/* We keep a single GtkTooltip object per display. The tooltip object
* owns a GtkTooltipWindow widget, which is using a popup surface, similar
* to what a GtkPopover does. It gets reparented to the right native widget
* whenever a tooltip is to be shown. The tooltip object keeps a weak
* ref on the native in order to remove the tooltip window when the
* native goes away.
*/
typedef struct _GtkTooltipClass GtkTooltipClass; typedef struct _GtkTooltipClass GtkTooltipClass;
struct _GtkTooltip struct _GtkTooltip
@ -95,11 +102,9 @@ struct _GtkTooltip
GtkWidget *window; GtkWidget *window;
GtkWindow *current_window;
GtkWidget *tooltip_widget; GtkWidget *tooltip_widget;
GdkSurface *last_surface; GtkWidget *native;
guint timeout_id; guint timeout_id;
guint browse_mode_timeout_id; guint browse_mode_timeout_id;
@ -116,7 +121,7 @@ struct _GtkTooltipClass
GObjectClass parent_class; GObjectClass parent_class;
}; };
#define GTK_TOOLTIP_VISIBLE(tooltip) ((tooltip)->current_window && gtk_widget_get_visible (GTK_WIDGET((tooltip)->current_window))) #define GTK_TOOLTIP_VISIBLE(tooltip) ((tooltip)->window && gtk_widget_get_visible (GTK_WIDGET((tooltip)->window)))
static void gtk_tooltip_dispose (GObject *object); static void gtk_tooltip_dispose (GObject *object);
@ -125,7 +130,7 @@ static void gtk_tooltip_window_hide (GtkWidget *widget,
static void gtk_tooltip_display_closed (GdkDisplay *display, static void gtk_tooltip_display_closed (GdkDisplay *display,
gboolean was_error, gboolean was_error,
GtkTooltip *tooltip); GtkTooltip *tooltip);
static void gtk_tooltip_set_last_surface (GtkTooltip *tooltip, static void gtk_tooltip_set_surface (GtkTooltip *tooltip,
GdkSurface *surface); GdkSurface *surface);
static void gtk_tooltip_handle_event_internal (GdkEventType event_type, static void gtk_tooltip_handle_event_internal (GdkEventType event_type,
@ -134,16 +139,7 @@ static void gtk_tooltip_handle_event_internal (GdkEventType event_type,
gdouble dx, gdouble dx,
gdouble dy); gdouble dy);
static inline GQuark tooltip_quark (void) static GQuark quark_current_tooltip;
{
static GQuark quark;
if G_UNLIKELY (quark == 0)
quark = g_quark_from_static_string ("gdk-display-current-tooltip");
return quark;
}
#define quark_current_tooltip tooltip_quark()
G_DEFINE_TYPE (GtkTooltip, gtk_tooltip, G_TYPE_OBJECT); G_DEFINE_TYPE (GtkTooltip, gtk_tooltip, G_TYPE_OBJECT);
@ -152,6 +148,8 @@ gtk_tooltip_class_init (GtkTooltipClass *klass)
{ {
GObjectClass *object_class; GObjectClass *object_class;
quark_current_tooltip = g_quark_from_static_string ("gdk-display-current-tooltip");
object_class = G_OBJECT_CLASS (klass); object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gtk_tooltip_dispose; object_class->dispose = gtk_tooltip_dispose;
@ -165,13 +163,12 @@ gtk_tooltip_init (GtkTooltip *tooltip)
tooltip->browse_mode_enabled = FALSE; tooltip->browse_mode_enabled = FALSE;
tooltip->current_window = NULL;
tooltip->tooltip_widget = NULL; tooltip->tooltip_widget = NULL;
tooltip->last_surface = NULL; tooltip->native = NULL;
tooltip->window = gtk_tooltip_window_new (); tooltip->window = gtk_tooltip_window_new ();
g_object_ref_sink (tooltip->window);
g_signal_connect (tooltip->window, "hide", g_signal_connect (tooltip->window, "hide",
G_CALLBACK (gtk_tooltip_window_hide), G_CALLBACK (gtk_tooltip_window_hide),
tooltip); tooltip);
@ -195,7 +192,7 @@ gtk_tooltip_dispose (GObject *object)
} }
gtk_tooltip_set_custom (tooltip, NULL); gtk_tooltip_set_custom (tooltip, NULL);
gtk_tooltip_set_last_surface (tooltip, NULL); gtk_tooltip_set_surface (tooltip, NULL);
if (tooltip->window) if (tooltip->window)
{ {
@ -205,8 +202,8 @@ gtk_tooltip_dispose (GObject *object)
g_signal_handlers_disconnect_by_func (display, g_signal_handlers_disconnect_by_func (display,
gtk_tooltip_display_closed, gtk_tooltip_display_closed,
tooltip); tooltip);
gtk_widget_destroy (tooltip->window); gtk_tooltip_window_set_relative_to (GTK_TOOLTIP_WINDOW (tooltip->window), NULL);
tooltip->window = NULL; g_clear_object (&tooltip->window);
} }
G_OBJECT_CLASS (gtk_tooltip_parent_class)->dispose (object); G_OBJECT_CLASS (gtk_tooltip_parent_class)->dispose (object);
@ -476,37 +473,40 @@ gtk_tooltip_display_closed (GdkDisplay *display,
} }
static void static void
gtk_tooltip_set_last_surface (GtkTooltip *tooltip, native_weak_notify (gpointer data, GObject *former_object)
GdkSurface *surface)
{ {
GtkWidget *window_widget = NULL; GtkTooltip *tooltip = data;
if (tooltip->last_surface == surface) gtk_tooltip_window_set_relative_to (GTK_TOOLTIP_WINDOW (tooltip->window), NULL);
return; tooltip->native = NULL;
}
if (tooltip->last_surface) static void
g_object_remove_weak_pointer (G_OBJECT (tooltip->last_surface), gtk_tooltip_set_surface (GtkTooltip *tooltip,
(gpointer *) &tooltip->last_surface); GdkSurface *surface)
{
tooltip->last_surface = surface; GtkWidget *native;
if (tooltip->last_surface)
g_object_add_weak_pointer (G_OBJECT (tooltip->last_surface),
(gpointer *) &tooltip->last_surface);
if (surface) if (surface)
window_widget = gtk_native_get_for_surface (surface); native = gtk_native_get_for_surface (surface);
if (window_widget)
window_widget = GTK_WIDGET (gtk_widget_get_root (window_widget));
if (window_widget &&
window_widget != tooltip->window &&
GTK_IS_WINDOW (window_widget))
gtk_window_set_transient_for (GTK_WINDOW (tooltip->window),
GTK_WINDOW (window_widget));
else else
gtk_window_set_transient_for (GTK_WINDOW (tooltip->window), NULL); native = NULL;
if (tooltip->native == native)
return;
if (tooltip->native)
g_object_weak_unref (G_OBJECT (tooltip->native), native_weak_notify, tooltip);
tooltip->native = native;
if (tooltip->native)
g_object_weak_ref (G_OBJECT (tooltip->native), native_weak_notify, tooltip);
if (native)
gtk_tooltip_window_set_relative_to (GTK_TOOLTIP_WINDOW (tooltip->window), native);
else
gtk_tooltip_window_set_relative_to (GTK_TOOLTIP_WINDOW (tooltip->window), NULL);
} }
static gboolean static gboolean
@ -567,19 +567,17 @@ gtk_tooltip_position (GtkTooltip *tooltip,
GtkSettings *settings; GtkSettings *settings;
graphene_rect_t anchor_bounds; graphene_rect_t anchor_bounds;
GdkRectangle anchor_rect; GdkRectangle anchor_rect;
GdkSurface *surface;
GdkSurface *effective_toplevel; GdkSurface *effective_toplevel;
GtkWidget *toplevel; GtkWidget *toplevel;
int rect_anchor_dx = 0; int rect_anchor_dx = 0;
int cursor_size; int cursor_size;
int anchor_rect_padding; int anchor_rect_padding;
gtk_widget_realize (GTK_WIDGET (tooltip->current_window)); gtk_widget_realize (GTK_WIDGET (tooltip->window));
surface = gtk_native_get_surface (GTK_NATIVE (tooltip->current_window));
tooltip->tooltip_widget = new_tooltip_widget; tooltip->tooltip_widget = new_tooltip_widget;
toplevel = GTK_WIDGET (gtk_widget_get_root (new_tooltip_widget)); toplevel = GTK_WIDGET (gtk_widget_get_native (new_tooltip_widget));
if (gtk_widget_compute_bounds (new_tooltip_widget, toplevel, &anchor_bounds)) if (gtk_widget_compute_bounds (new_tooltip_widget, toplevel, &anchor_bounds))
{ {
anchor_rect = (GdkRectangle) { anchor_rect = (GdkRectangle) {
@ -657,16 +655,12 @@ gtk_tooltip_position (GtkTooltip *tooltip,
} }
} }
gtk_window_set_transient_for (GTK_WINDOW (tooltip->current_window), gtk_tooltip_window_position (GTK_TOOLTIP_WINDOW (tooltip->window),
GTK_WINDOW (toplevel)); &anchor_rect,
GDK_GRAVITY_SOUTH,
gdk_surface_move_to_rect (surface, GDK_GRAVITY_NORTH,
&anchor_rect, GDK_ANCHOR_FLIP_Y | GDK_ANCHOR_SLIDE_X,
GDK_GRAVITY_SOUTH, rect_anchor_dx, 0);
GDK_GRAVITY_NORTH,
GDK_ANCHOR_FLIP_Y | GDK_ANCHOR_SLIDE_X,
rect_anchor_dx, 0);
gtk_widget_show (GTK_WIDGET (tooltip->current_window));
} }
static void static void
@ -682,20 +676,18 @@ gtk_tooltip_show_tooltip (GdkDisplay *display)
tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip); tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
{ if (!tooltip->native)
surface = tooltip->last_surface; return;
if (!GDK_IS_SURFACE (surface)) surface = gtk_native_get_surface (GTK_NATIVE (tooltip->native));
return;
device = gdk_seat_get_pointer (gdk_display_get_default_seat (display)); device = gdk_seat_get_pointer (gdk_display_get_default_seat (display));
gdk_surface_get_device_position (surface, device, &px, &py, NULL); gdk_surface_get_device_position (surface, device, &px, &py, NULL);
x = round (px); x = round (px);
y = round (py); y = round (py);
tooltip_widget = _gtk_widget_find_at_coords (surface, x, y, &x, &y); tooltip_widget = _gtk_widget_find_at_coords (surface, x, y, &x, &y);
}
if (!tooltip_widget) if (!tooltip_widget)
return; return;
@ -704,15 +696,7 @@ gtk_tooltip_show_tooltip (GdkDisplay *display)
if (!return_value) if (!return_value)
return; return;
if (!tooltip->current_window) /* FIXME: should use tooltip->window iso tooltip->window */
{
if (gtk_widget_get_tooltip_window (tooltip_widget))
tooltip->current_window = gtk_widget_get_tooltip_window (tooltip_widget);
else
tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
}
/* FIXME: should use tooltip->current_window iso tooltip->window */
if (display != gtk_widget_get_display (tooltip->window)) if (display != gtk_widget_get_display (tooltip->window))
{ {
g_signal_handlers_disconnect_by_func (display, g_signal_handlers_disconnect_by_func (display,
@ -727,6 +711,8 @@ gtk_tooltip_show_tooltip (GdkDisplay *display)
gtk_tooltip_position (tooltip, display, tooltip_widget, device); gtk_tooltip_position (tooltip, display, tooltip_widget, device);
gtk_widget_show (GTK_WIDGET (tooltip->window));
/* Now a tooltip is visible again on the display, make sure browse /* Now a tooltip is visible again on the display, make sure browse
* mode is enabled. * mode is enabled.
*/ */
@ -770,11 +756,8 @@ gtk_tooltip_hide_tooltip (GtkTooltip *tooltip)
g_source_set_name_by_id (tooltip->browse_mode_timeout_id, "[gtk] tooltip_browse_mode_expired"); g_source_set_name_by_id (tooltip->browse_mode_timeout_id, "[gtk] tooltip_browse_mode_expired");
} }
if (tooltip->current_window) if (tooltip->window)
{ gtk_widget_hide (tooltip->window);
gtk_widget_hide (GTK_WIDGET (tooltip->current_window));
tooltip->current_window = NULL;
}
} }
static gint static gint
@ -834,7 +817,7 @@ _gtk_tooltip_hide (GtkWidget *widget)
display = gtk_widget_get_display (widget); display = gtk_widget_get_display (widget);
tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip); tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
if (!tooltip || !GTK_TOOLTIP_VISIBLE (tooltip) || !tooltip->tooltip_widget) if (!tooltip || !tooltip->window || !tooltip->tooltip_widget)
return; return;
if (widget == tooltip->tooltip_widget) if (widget == tooltip->tooltip_widget)
@ -916,19 +899,19 @@ gtk_tooltip_handle_event_internal (GdkEventType event_type,
{ {
int x = dx, y = dy; int x = dx, y = dy;
GdkDisplay *display; GdkDisplay *display;
GtkTooltip *current_tooltip; GtkTooltip *tooltip;
display = gdk_surface_get_display (surface); display = gdk_surface_get_display (surface);
current_tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip); tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
if (current_tooltip) if (tooltip)
gtk_tooltip_set_last_surface (current_tooltip, surface); gtk_tooltip_set_surface (tooltip, surface);
/* Hide the tooltip when there's no new tooltip widget */ /* Hide the tooltip when there's no new tooltip widget */
if (!target_widget) if (!target_widget)
{ {
if (current_tooltip) if (tooltip)
gtk_tooltip_hide_tooltip (current_tooltip); gtk_tooltip_hide_tooltip (tooltip);
return; return;
} }
@ -940,54 +923,49 @@ gtk_tooltip_handle_event_internal (GdkEventType event_type,
case GDK_DRAG_ENTER: case GDK_DRAG_ENTER:
case GDK_GRAB_BROKEN: case GDK_GRAB_BROKEN:
case GDK_SCROLL: case GDK_SCROLL:
gtk_tooltip_hide_tooltip (current_tooltip); gtk_tooltip_hide_tooltip (tooltip);
break; break;
case GDK_MOTION_NOTIFY: case GDK_MOTION_NOTIFY:
case GDK_ENTER_NOTIFY: case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY: case GDK_LEAVE_NOTIFY:
if (current_tooltip) if (tooltip)
{ {
gboolean tip_area_set; gboolean tip_area_set;
GdkRectangle tip_area; GdkRectangle tip_area;
gboolean hide_tooltip; gboolean hide_tooltip;
tip_area_set = current_tooltip->tip_area_set; tip_area_set = tooltip->tip_area_set;
tip_area = current_tooltip->tip_area; tip_area = tooltip->tip_area;
gtk_tooltip_run_requery (&target_widget, gtk_tooltip_run_requery (&target_widget, tooltip, &x, &y);
current_tooltip,
&x, &y);
/* Leave notify should override the query function */ /* Leave notify should override the query function */
hide_tooltip = (event_type == GDK_LEAVE_NOTIFY); hide_tooltip = (event_type == GDK_LEAVE_NOTIFY);
/* Is the pointer above another widget now? */ /* Is the pointer above another widget now? */
if (GTK_TOOLTIP_VISIBLE (current_tooltip)) if (GTK_TOOLTIP_VISIBLE (tooltip))
hide_tooltip |= target_widget != current_tooltip->tooltip_widget; hide_tooltip |= target_widget != tooltip->tooltip_widget;
/* Did the pointer move out of the previous "context area"? */ /* Did the pointer move out of the previous "context area"? */
if (tip_area_set) if (tip_area_set)
hide_tooltip |= !gdk_rectangle_contains_point (&tip_area, x, y); hide_tooltip |= !gdk_rectangle_contains_point (&tip_area, x, y);
if (hide_tooltip) if (hide_tooltip)
gtk_tooltip_hide_tooltip (current_tooltip); gtk_tooltip_hide_tooltip (tooltip);
else else
gtk_tooltip_start_delay (display); gtk_tooltip_start_delay (display);
} }
else else
{ {
/* Need a new tooltip for this display */ /* Need a new tooltip for this display */
current_tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL); tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
g_object_set_qdata_full (G_OBJECT (display), g_object_set_qdata_full (G_OBJECT (display), quark_current_tooltip,
quark_current_tooltip, tooltip, g_object_unref);
current_tooltip,
g_object_unref);
g_signal_connect (display, "closed", g_signal_connect (display, "closed",
G_CALLBACK (gtk_tooltip_display_closed), G_CALLBACK (gtk_tooltip_display_closed), tooltip);
current_tooltip);
gtk_tooltip_set_last_surface (current_tooltip, surface); gtk_tooltip_set_surface (tooltip, surface);
gtk_tooltip_start_delay (display); gtk_tooltip_start_delay (display);
} }

View File

@ -38,11 +38,27 @@
#include "gtksizerequest.h" #include "gtksizerequest.h"
#include "gtkwindowprivate.h" #include "gtkwindowprivate.h"
#include "gtkwidgetprivate.h" #include "gtkwidgetprivate.h"
#include "gtknative.h"
#include "gtkstylecontext.h"
#include "gtkcssnodeprivate.h"
struct _GtkTooltipWindow struct _GtkTooltipWindow
{ {
GtkWindow parent_type; GtkWindow parent_type;
GdkSurface *surface;
GskRenderer *renderer;
GdkSurfaceState state;
GtkWidget *relative_to;
GdkRectangle rect;
GdkGravity rect_anchor;
GdkGravity surface_anchor;
GdkAnchorHints anchor_hints;
int dx;
int dy;
guint surface_transform_changed_cb;
GtkWidget *box; GtkWidget *box;
GtkWidget *image; GtkWidget *image;
GtkWidget *label; GtkWidget *label;
@ -54,13 +70,349 @@ struct _GtkTooltipWindowClass
GtkWindowClass parent_class; GtkWindowClass parent_class;
}; };
G_DEFINE_TYPE (GtkTooltipWindow, gtk_tooltip_window, GTK_TYPE_WINDOW) static void gtk_tooltip_window_native_init (GtkNativeInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GtkTooltipWindow, gtk_tooltip_window, GTK_TYPE_BIN,
G_IMPLEMENT_INTERFACE (GTK_TYPE_NATIVE,
gtk_tooltip_window_native_init))
static GdkSurface *
gtk_tooltip_window_native_get_surface (GtkNative *native)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (native);
return window->surface;
}
static GskRenderer *
gtk_tooltip_window_native_get_renderer (GtkNative *native)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (native);
return window->renderer;
}
static void
gtk_tooltip_window_native_get_surface_transform (GtkNative *native,
int *x,
int *y)
{
GtkStyleContext *context;
GtkBorder margin, border, padding;
context = gtk_widget_get_style_context (GTK_WIDGET (native));
gtk_style_context_get_margin (context, &margin);
gtk_style_context_get_border (context, &border);
gtk_style_context_get_padding (context, &padding);
*x = margin.left + border.left + padding.left;
*y = margin.top + border.top + padding.top;
}
static void
move_to_rect (GtkTooltipWindow *window)
{
gdk_surface_move_to_rect (window->surface,
&window->rect,
window->rect_anchor,
window->surface_anchor,
window->anchor_hints,
window->dx,
window->dy);
}
static void
gtk_tooltip_window_move_resize (GtkTooltipWindow *window)
{
GtkRequisition req;
if (window->surface)
{
gtk_widget_get_preferred_size (GTK_WIDGET (window), NULL, &req);
gdk_surface_resize (window->surface, req.width, req.height);
move_to_rect (window);
}
}
static void
gtk_tooltip_window_native_check_resize (GtkNative *native)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (native);
GtkWidget *widget = GTK_WIDGET (native);
if (!_gtk_widget_get_alloc_needed (widget))
gtk_widget_ensure_allocate (widget);
else if (gtk_widget_get_visible (widget))
{
gtk_tooltip_window_move_resize (window);
if (window->surface)
gtk_widget_allocate (GTK_WIDGET (window),
gdk_surface_get_width (window->surface),
gdk_surface_get_height (window->surface),
-1, NULL);
}
}
static void
gtk_tooltip_window_native_init (GtkNativeInterface *iface)
{
iface->get_surface = gtk_tooltip_window_native_get_surface;
iface->get_renderer = gtk_tooltip_window_native_get_renderer;
iface->get_surface_transform = gtk_tooltip_window_native_get_surface_transform;
iface->check_resize = gtk_tooltip_window_native_check_resize;
}
static void
surface_state_changed (GtkWidget *widget)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (widget);
GdkSurfaceState new_surface_state;
GdkSurfaceState changed_mask;
new_surface_state = gdk_surface_get_state (window->surface);
changed_mask = new_surface_state ^ window->state;
window->state = new_surface_state;
if (changed_mask & GDK_SURFACE_STATE_WITHDRAWN)
{
if (window->state & GDK_SURFACE_STATE_WITHDRAWN)
gtk_widget_hide (widget);
}
}
static void
surface_size_changed (GtkWidget *widget,
guint width,
guint height)
{
}
static gboolean
surface_render (GdkSurface *surface,
cairo_region_t *region,
GtkWidget *widget)
{
gtk_widget_render (widget, surface, region);
return TRUE;
}
static gboolean
surface_event (GdkSurface *surface,
GdkEvent *event,
GtkWidget *widget)
{
gtk_main_do_event (event);
return TRUE;
}
static void
surface_moved_to_rect (GdkSurface *surface,
GdkRectangle *flipped_rect,
GdkRectangle *final_rect,
gboolean flipped_x,
gboolean flipped_y,
GtkWidget *widget)
{
}
static void
gtk_tooltip_window_realize (GtkWidget *widget)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (widget);
GdkDisplay *display;
GdkSurface *parent;
display = gtk_widget_get_display (window->relative_to);
parent = gtk_native_get_surface (gtk_widget_get_native (window->relative_to));
window->surface = gdk_surface_new_popup (display, parent, FALSE);
gdk_surface_set_widget (window->surface, widget);
g_signal_connect_swapped (window->surface, "notify::state", G_CALLBACK (surface_state_changed), widget);
g_signal_connect_swapped (window->surface, "size-changed", G_CALLBACK (surface_size_changed), widget);
g_signal_connect (window->surface, "render", G_CALLBACK (surface_render), widget);
g_signal_connect (window->surface, "event", G_CALLBACK (surface_event), widget);
g_signal_connect (window->surface, "moved-to-rect", G_CALLBACK (surface_moved_to_rect), widget);
GTK_WIDGET_CLASS (gtk_tooltip_window_parent_class)->realize (widget);
window->renderer = gsk_renderer_new_for_surface (window->surface);
}
static void
gtk_tooltip_window_unrealize (GtkWidget *widget)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (widget);
GTK_WIDGET_CLASS (gtk_tooltip_window_parent_class)->unrealize (widget);
gsk_renderer_unrealize (window->renderer);
g_clear_object (&window->renderer);
g_signal_handlers_disconnect_by_func (window->surface, surface_state_changed, widget);
g_signal_handlers_disconnect_by_func (window->surface, surface_size_changed, widget);
g_signal_handlers_disconnect_by_func (window->surface, surface_render, widget);
g_signal_handlers_disconnect_by_func (window->surface, surface_event, widget);
g_signal_handlers_disconnect_by_func (window->surface, surface_moved_to_rect, widget);
gdk_surface_set_widget (window->surface, NULL);
gdk_surface_destroy (window->surface);
g_clear_object (&window->surface);
}
static void
unset_surface_transform_changed_cb (gpointer data)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (data);
window->surface_transform_changed_cb = 0;
}
static gboolean
surface_transform_changed_cb (GtkWidget *widget,
const graphene_matrix_t *transform,
gpointer user_data)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (widget);
move_to_rect (window);
return G_SOURCE_CONTINUE;
}
static void
gtk_tooltip_window_map (GtkWidget *widget)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (widget);
GtkWidget *child;
gdk_surface_show (window->surface);
move_to_rect (window);
window->surface_transform_changed_cb =
gtk_widget_add_surface_transform_changed_callback (window->relative_to,
surface_transform_changed_cb,
window,
unset_surface_transform_changed_cb);
GTK_WIDGET_CLASS (gtk_tooltip_window_parent_class)->map (widget);
child = gtk_bin_get_child (GTK_BIN (widget));
if (child != NULL && gtk_widget_get_visible (child))
gtk_widget_map (child);
}
static void
gtk_tooltip_window_unmap (GtkWidget *widget)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (widget);
GtkWidget *child;
gtk_widget_remove_surface_transform_changed_callback (window->relative_to,
window->surface_transform_changed_cb);
window->surface_transform_changed_cb = 0;
GTK_WIDGET_CLASS (gtk_tooltip_window_parent_class)->unmap (widget);
gdk_surface_hide (window->surface);
child = gtk_bin_get_child (GTK_BIN (widget));
if (child != NULL)
gtk_widget_unmap (child);
}
static void
gtk_tooltip_window_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkWidget *child;
child = gtk_bin_get_child (GTK_BIN (widget));
if (child)
gtk_widget_measure (child,
orientation, for_size,
minimum, natural,
minimum_baseline, natural_baseline);
}
static void
gtk_tooltip_window_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (widget);
GtkWidget *child;
gtk_tooltip_window_move_resize (window);
child = gtk_bin_get_child (GTK_BIN (window));
if (child)
gtk_widget_allocate (child, width, height, baseline, NULL);
}
static void
gtk_tooltip_window_show (GtkWidget *widget)
{
_gtk_widget_set_visible_flag (widget, TRUE);
gtk_css_node_validate (gtk_widget_get_css_node (widget));
gtk_widget_realize (widget);
gtk_tooltip_window_native_check_resize (GTK_NATIVE (widget));
gtk_widget_map (widget);
}
static void
gtk_tooltip_window_hide (GtkWidget *widget)
{
_gtk_widget_set_visible_flag (widget, FALSE);
gtk_widget_unmap (widget);
}
static void size_changed (GtkWidget *widget,
int width,
int height,
int baseline,
GtkTooltipWindow *window);
static void
gtk_tooltip_window_dispose (GObject *object)
{
GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (object);
if (window->relative_to)
{
g_signal_handlers_disconnect_by_func (window->relative_to, size_changed, window);
gtk_widget_unparent (GTK_WIDGET (window));
}
G_OBJECT_CLASS (gtk_tooltip_window_parent_class)->dispose (object);
}
static void static void
gtk_tooltip_window_class_init (GtkTooltipWindowClass *klass) gtk_tooltip_window_class_init (GtkTooltipWindowClass *klass)
{ {
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = gtk_tooltip_window_dispose;
widget_class->realize = gtk_tooltip_window_realize;
widget_class->unrealize = gtk_tooltip_window_unrealize;
widget_class->map = gtk_tooltip_window_map;
widget_class->unmap = gtk_tooltip_window_unmap;
widget_class->measure = gtk_tooltip_window_measure;
widget_class->size_allocate = gtk_tooltip_window_size_allocate;
widget_class->show = gtk_tooltip_window_show;
widget_class->hide = gtk_tooltip_window_hide;
gtk_widget_class_set_css_name (widget_class, I_("tooltip")); gtk_widget_class_set_css_name (widget_class, I_("tooltip"));
gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_TOOL_TIP); gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_TOOL_TIP);
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtktooltipwindow.ui"); gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtktooltipwindow.ui");
@ -68,24 +420,20 @@ gtk_tooltip_window_class_init (GtkTooltipWindowClass *klass)
gtk_widget_class_bind_template_child (widget_class, GtkTooltipWindow, box); gtk_widget_class_bind_template_child (widget_class, GtkTooltipWindow, box);
gtk_widget_class_bind_template_child (widget_class, GtkTooltipWindow, image); gtk_widget_class_bind_template_child (widget_class, GtkTooltipWindow, image);
gtk_widget_class_bind_template_child (widget_class, GtkTooltipWindow, label); gtk_widget_class_bind_template_child (widget_class, GtkTooltipWindow, label);
gtk_widget_class_set_css_name (widget_class, "tooltip");
} }
static void static void
gtk_tooltip_window_init (GtkTooltipWindow *self) gtk_tooltip_window_init (GtkTooltipWindow *self)
{ {
GtkWindow *window = GTK_WINDOW (self);
gtk_widget_init_template (GTK_WIDGET (self)); gtk_widget_init_template (GTK_WIDGET (self));
_gtk_window_request_csd (window);
} }
GtkWidget * GtkWidget *
gtk_tooltip_window_new (void) gtk_tooltip_window_new (void)
{ {
return g_object_new (GTK_TYPE_TOOLTIP_WINDOW, return g_object_new (GTK_TYPE_TOOLTIP_WINDOW, NULL);
"type", GTK_WINDOW_POPUP,
NULL);
} }
void void
@ -191,5 +539,65 @@ gtk_tooltip_window_set_custom_widget (GtkTooltipWindow *window,
gtk_container_add (GTK_CONTAINER (window->box), custom_widget); gtk_container_add (GTK_CONTAINER (window->box), custom_widget);
gtk_widget_show (custom_widget); gtk_widget_show (custom_widget);
gtk_widget_hide (window->image);
gtk_widget_hide (window->label);
} }
} }
static void
size_changed (GtkWidget *widget,
int width,
int height,
int baseline,
GtkTooltipWindow *window)
{
gtk_native_check_resize (GTK_NATIVE (window));
}
void
gtk_tooltip_window_set_relative_to (GtkTooltipWindow *window,
GtkWidget *relative_to)
{
if (window->relative_to == relative_to)
return;
g_object_ref (window);
if (window->relative_to)
{
g_signal_handlers_disconnect_by_func (window->relative_to, size_changed, window);
gtk_widget_unparent (GTK_WIDGET (window));
}
window->relative_to = relative_to;
if (window->relative_to)
{
g_signal_connect (window->relative_to, "size-allocate", G_CALLBACK (size_changed), window);
gtk_css_node_set_parent (gtk_widget_get_css_node (GTK_WIDGET (window)),
gtk_widget_get_css_node (relative_to));
gtk_widget_set_parent (GTK_WIDGET (window), relative_to);
}
g_object_unref (window);
}
void
gtk_tooltip_window_position (GtkTooltipWindow *window,
GdkRectangle *rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor,
GdkAnchorHints anchor_hints,
int dx,
int dy)
{
window->rect = *rect;
window->rect_anchor = rect_anchor;
window->surface_anchor = surface_anchor;
window->anchor_hints = anchor_hints;
window->dx = dx;
window->dy = dy;
move_to_rect (window);
}

View File

@ -49,6 +49,15 @@ void gtk_tooltip_window_set_image_icon_from_gicon (GtkTooltipWindo
GIcon *gicon); GIcon *gicon);
void gtk_tooltip_window_set_custom_widget (GtkTooltipWindow *window, void gtk_tooltip_window_set_custom_widget (GtkTooltipWindow *window,
GtkWidget *custom_widget); GtkWidget *custom_widget);
void gtk_tooltip_window_set_relative_to (GtkTooltipWindow *window,
GtkWidget *relative_to);
void gtk_tooltip_window_position (GtkTooltipWindow *window,
GdkRectangle *rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor,
GdkAnchorHints anchor_hints,
int dx,
int dy);
G_END_DECLS G_END_DECLS

View File

@ -726,7 +726,6 @@ static GQuark quark_input_shape_info = 0;
static GQuark quark_pango_context = 0; static GQuark quark_pango_context = 0;
static GQuark quark_mnemonic_labels = 0; static GQuark quark_mnemonic_labels = 0;
static GQuark quark_tooltip_markup = 0; static GQuark quark_tooltip_markup = 0;
static GQuark quark_tooltip_window = 0;
static GQuark quark_size_groups = 0; static GQuark quark_size_groups = 0;
static GQuark quark_auto_children = 0; static GQuark quark_auto_children = 0;
static GQuark quark_widget_path = 0; static GQuark quark_widget_path = 0;
@ -885,7 +884,6 @@ gtk_widget_class_init (GtkWidgetClass *klass)
quark_pango_context = g_quark_from_static_string ("gtk-pango-context"); quark_pango_context = g_quark_from_static_string ("gtk-pango-context");
quark_mnemonic_labels = g_quark_from_static_string ("gtk-mnemonic-labels"); quark_mnemonic_labels = g_quark_from_static_string ("gtk-mnemonic-labels");
quark_tooltip_markup = g_quark_from_static_string ("gtk-tooltip-markup"); quark_tooltip_markup = g_quark_from_static_string ("gtk-tooltip-markup");
quark_tooltip_window = g_quark_from_static_string ("gtk-tooltip-window");
quark_size_groups = g_quark_from_static_string ("gtk-widget-size-groups"); quark_size_groups = g_quark_from_static_string ("gtk-widget-size-groups");
quark_auto_children = g_quark_from_static_string ("gtk-widget-auto-children"); quark_auto_children = g_quark_from_static_string ("gtk-widget-auto-children");
quark_widget_path = g_quark_from_static_string ("gtk-widget-path"); quark_widget_path = g_quark_from_static_string ("gtk-widget-path");
@ -2157,10 +2155,8 @@ gtk_widget_set_property (GObject *object,
switch (prop_id) switch (prop_id)
{ {
gboolean tmp;
gchar *tooltip_markup; gchar *tooltip_markup;
const gchar *tooltip_text; const gchar *tooltip_text;
GtkWindow *tooltip_window;
case PROP_NAME: case PROP_NAME:
gtk_widget_set_name (widget, g_value_get_string (value)); gtk_widget_set_name (widget, g_value_get_string (value));
@ -2204,7 +2200,6 @@ gtk_widget_set_property (GObject *object,
gtk_widget_set_has_tooltip (widget, g_value_get_boolean (value)); gtk_widget_set_has_tooltip (widget, g_value_get_boolean (value));
break; break;
case PROP_TOOLTIP_MARKUP: case PROP_TOOLTIP_MARKUP:
tooltip_window = g_object_get_qdata (object, quark_tooltip_window);
tooltip_markup = g_value_dup_string (value); tooltip_markup = g_value_dup_string (value);
/* Treat an empty string as a NULL string, /* Treat an empty string as a NULL string,
@ -2219,14 +2214,11 @@ gtk_widget_set_property (GObject *object,
g_object_set_qdata_full (object, quark_tooltip_markup, g_object_set_qdata_full (object, quark_tooltip_markup,
tooltip_markup, g_free); tooltip_markup, g_free);
tmp = (tooltip_window != NULL || tooltip_markup != NULL); gtk_widget_set_has_tooltip (widget, tooltip_markup != NULL);
gtk_widget_set_has_tooltip (widget, tmp);
if (_gtk_widget_get_visible (widget)) if (_gtk_widget_get_visible (widget))
gtk_widget_trigger_tooltip_query (widget); gtk_widget_trigger_tooltip_query (widget);
break; break;
case PROP_TOOLTIP_TEXT: case PROP_TOOLTIP_TEXT:
tooltip_window = g_object_get_qdata (object, quark_tooltip_window);
tooltip_text = g_value_get_string (value); tooltip_text = g_value_get_string (value);
/* Treat an empty string as a NULL string, /* Treat an empty string as a NULL string,
@ -2240,8 +2232,7 @@ gtk_widget_set_property (GObject *object,
g_object_set_qdata_full (object, quark_tooltip_markup, g_object_set_qdata_full (object, quark_tooltip_markup,
tooltip_markup, g_free); tooltip_markup, g_free);
tmp = (tooltip_window != NULL || tooltip_markup != NULL); gtk_widget_set_has_tooltip (widget, tooltip_markup != NULL);
gtk_widget_set_has_tooltip (widget, tmp);
if (_gtk_widget_get_visible (widget)) if (_gtk_widget_get_visible (widget))
gtk_widget_trigger_tooltip_query (widget); gtk_widget_trigger_tooltip_query (widget);
break; break;
@ -10649,60 +10640,6 @@ gtk_widget_remove_mnemonic_label (GtkWidget *widget,
new_list, (GDestroyNotify) g_slist_free); new_list, (GDestroyNotify) g_slist_free);
} }
/**
* gtk_widget_set_tooltip_window:
* @widget: a #GtkWidget
* @custom_window: (allow-none): a #GtkWindow, or %NULL
*
* Replaces the default window used for displaying
* tooltips with @custom_window. GTK+ will take care of showing and
* hiding @custom_window at the right moment, to behave likewise as
* the default tooltip window. If @custom_window is %NULL, the default
* tooltip window will be used.
*/
void
gtk_widget_set_tooltip_window (GtkWidget *widget,
GtkWindow *custom_window)
{
gboolean has_tooltip;
gchar *tooltip_markup;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (custom_window == NULL || GTK_IS_WINDOW (custom_window));
tooltip_markup = g_object_get_qdata (G_OBJECT (widget), quark_tooltip_markup);
if (custom_window)
g_object_ref (custom_window);
g_object_set_qdata_full (G_OBJECT (widget), quark_tooltip_window,
custom_window, g_object_unref);
has_tooltip = (custom_window != NULL || tooltip_markup != NULL);
gtk_widget_set_has_tooltip (widget, has_tooltip);
if (has_tooltip && _gtk_widget_get_visible (widget))
gtk_widget_trigger_tooltip_query (widget);
}
/**
* gtk_widget_get_tooltip_window:
* @widget: a #GtkWidget
*
* Returns the #GtkWindow of the current tooltip. This can be the
* GtkWindow created by default, or the custom tooltip window set
* using gtk_widget_set_tooltip_window().
*
* Returns: (transfer none): The #GtkWindow of the current tooltip.
*/
GtkWindow *
gtk_widget_get_tooltip_window (GtkWidget *widget)
{
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
return g_object_get_qdata (G_OBJECT (widget), quark_tooltip_window);
}
/** /**
* gtk_widget_trigger_tooltip_query: * gtk_widget_trigger_tooltip_query:
* @widget: a #GtkWidget * @widget: a #GtkWidget

View File

@ -778,11 +778,6 @@ GDK_AVAILABLE_IN_ALL
void gtk_widget_remove_mnemonic_label (GtkWidget *widget, void gtk_widget_remove_mnemonic_label (GtkWidget *widget,
GtkWidget *label); GtkWidget *label);
GDK_AVAILABLE_IN_ALL
void gtk_widget_set_tooltip_window (GtkWidget *widget,
GtkWindow *custom_window);
GDK_AVAILABLE_IN_ALL
GtkWindow *gtk_widget_get_tooltip_window (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL GDK_AVAILABLE_IN_ALL
void gtk_widget_trigger_tooltip_query (GtkWidget *widget); void gtk_widget_trigger_tooltip_query (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL GDK_AVAILABLE_IN_ALL

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface domain="gtk40"> <interface domain="gtk40">
<template class="GtkTooltipWindow" parent="GtkWindow"> <template class="GtkTooltipWindow" parent="GtkBin">
<property name="resizable">0</property> <style>
<property name="type-hint">tooltip</property> <class name="background"/>
</style>
<child> <child>
<object class="GtkBox" id="box"> <object class="GtkBox" id="box">
<property name="spacing">6</property> <property name="spacing">6</property>

View File

@ -69,17 +69,6 @@ query_tooltip_cb (GtkWidget *widget,
return TRUE; return TRUE;
} }
static gboolean
query_tooltip_custom_cb (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip *tooltip,
gpointer data)
{
return TRUE;
}
static gboolean static gboolean
query_tooltip_text_view_cb (GtkWidget *widget, query_tooltip_text_view_cb (GtkWidget *widget,
gint x, gint x,
@ -278,11 +267,10 @@ main (int argc, char *argv[])
GtkWidget *box; GtkWidget *box;
GtkWidget *drawing_area; GtkWidget *drawing_area;
GtkWidget *button; GtkWidget *button;
GtkWidget *label;
GtkWidget *tooltip; GtkWidget *tooltip;
GtkWidget *popover;
GtkWidget *tooltip_window; GtkWidget *box2;
GtkWidget *tooltip_button; GtkWidget *custom;
GtkWidget *tree_view; GtkWidget *tree_view;
GtkTreeViewColumn *column; GtkTreeViewColumn *column;
@ -352,25 +340,6 @@ main (int argc, char *argv[])
g_assert (g_str_equal ("<b>Another</b> Label tooltip", markup)); g_assert (g_str_equal ("<b>Another</b> Label tooltip", markup));
g_free (text); g_free (markup); g_free (text); g_free (markup);
/* Another one, with a custom tooltip window */
button = gtk_check_button_new_with_label ("This one has a custom tooltip window!");
gtk_container_add (GTK_CONTAINER (box), button);
tooltip_window = gtk_window_new (GTK_WINDOW_POPUP);
tooltip_button = gtk_label_new ("blaat!");
gtk_container_add (GTK_CONTAINER (tooltip_window), tooltip_button);
gtk_widget_show (tooltip_button);
gtk_widget_set_tooltip_window (button, GTK_WINDOW (tooltip_window));
gtk_window_set_type_hint (GTK_WINDOW (tooltip_window),
GDK_SURFACE_TYPE_HINT_TOOLTIP);
gtk_window_set_transient_for (GTK_WINDOW (tooltip_window),
GTK_WINDOW (window));
g_signal_connect (button, "query-tooltip",
G_CALLBACK (query_tooltip_custom_cb), NULL);
g_object_set (button, "has-tooltip", TRUE, NULL);
/* An insensitive button */ /* An insensitive button */
button = gtk_button_new_with_label ("This one is insensitive"); button = gtk_button_new_with_label ("This one is insensitive");
gtk_widget_set_sensitive (button, FALSE); gtk_widget_set_sensitive (button, FALSE);
@ -435,21 +404,34 @@ main (int argc, char *argv[])
G_CALLBACK (query_tooltip_drawing_area_cb), NULL); G_CALLBACK (query_tooltip_drawing_area_cb), NULL);
gtk_container_add (GTK_CONTAINER (box), drawing_area); gtk_container_add (GTK_CONTAINER (box), drawing_area);
button = gtk_label_new ("Custom tooltip I"); button = gtk_menu_button_new ();
label = gtk_label_new ("See, custom"); gtk_widget_set_halign (button, GTK_ALIGN_CENTER);
g_object_ref_sink (label); gtk_menu_button_set_label (GTK_MENU_BUTTON (button), "Custom tooltip I");
g_object_set (button, "has-tooltip", TRUE, NULL);
g_signal_connect (button, "query-tooltip",
G_CALLBACK (query_tooltip_label_cb), label);
gtk_container_add (GTK_CONTAINER (box), button); gtk_container_add (GTK_CONTAINER (box), button);
popover = gtk_popover_new (NULL);
gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), popover);
box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (popover), box2);
button = gtk_label_new ("Hidden here");
custom = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
gtk_container_add (GTK_CONTAINER (custom), gtk_label_new ("See, custom"));
gtk_container_add (GTK_CONTAINER (custom), g_object_new (GTK_TYPE_SPINNER, "active", TRUE, NULL));
g_object_ref_sink (custom);
g_object_set (button, "has-tooltip", TRUE, NULL);
gtk_container_add (GTK_CONTAINER (box2), button);
g_signal_connect (button, "query-tooltip",
G_CALLBACK (query_tooltip_label_cb), custom);
button = gtk_label_new ("Custom tooltip II"); button = gtk_label_new ("Custom tooltip II");
label = gtk_label_new ("See, custom, too"); custom = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
g_object_ref_sink (label); gtk_container_add (GTK_CONTAINER (custom), gtk_label_new ("See, custom too"));
gtk_container_add (GTK_CONTAINER (custom), g_object_new (GTK_TYPE_SPINNER, "active", TRUE, NULL));
g_object_ref_sink (custom);
g_object_set (button, "has-tooltip", TRUE, NULL); g_object_set (button, "has-tooltip", TRUE, NULL);
gtk_container_add (GTK_CONTAINER (box), button);
g_signal_connect (button, "query-tooltip", g_signal_connect (button, "query-tooltip",
G_CALLBACK (query_tooltip_label_cb), label); G_CALLBACK (query_tooltip_label_cb), custom);
gtk_container_add (GTK_CONTAINER (box), button);
/* Done! */ /* Done! */
gtk_widget_show (window); gtk_widget_show (window);