[gtk/tooltip-redo: 56/57] tooltips: Stop using GTK_WINDOW_POPUP



commit f31a016efb3674ac5c660f66475f44a2075c44df
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Dec 28 21:04:19 2019 -0500

    tooltips: Stop using GTK_WINDOW_POPUP
    
    Make GtkTooltipWindow a GtkNative implementation,
    instead of using a GTK_WINDOW_POPUP window.

 gtk/gtktooltip.c              | 185 +++++++++---------
 gtk/gtktooltipwindow.c        | 424 +++++++++++++++++++++++++++++++++++++++++-
 gtk/gtktooltipwindowprivate.h |   9 +
 gtk/ui/gtktooltipwindow.ui    |   7 +-
 4 files changed, 513 insertions(+), 112 deletions(-)
---
diff --git a/gtk/gtktooltip.c b/gtk/gtktooltip.c
index 9b1ef188c3..88cf89651d 100644
--- a/gtk/gtktooltip.c
+++ b/gtk/gtktooltip.c
@@ -87,6 +87,13 @@
 #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))
 
+/* 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;
 
 struct _GtkTooltip
@@ -95,11 +102,9 @@ struct _GtkTooltip
 
   GtkWidget *window;
 
-  GtkWindow *current_window;
-
   GtkWidget *tooltip_widget;
 
-  GdkSurface *last_surface;
+  GtkWidget *native;
 
   guint timeout_id;
   guint browse_mode_timeout_id;
@@ -116,7 +121,7 @@ struct _GtkTooltipClass
   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);
 
@@ -125,7 +130,7 @@ static void       gtk_tooltip_window_hide          (GtkWidget       *widget,
 static void       gtk_tooltip_display_closed       (GdkDisplay      *display,
                                                    gboolean         was_error,
                                                    GtkTooltip      *tooltip);
-static void       gtk_tooltip_set_last_surface      (GtkTooltip      *tooltip,
+static void       gtk_tooltip_set_surface          (GtkTooltip      *tooltip,
                                                    GdkSurface       *surface);
 
 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       dy);
 
-static inline GQuark tooltip_quark (void)
-{
-  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()
+static GQuark quark_current_tooltip;
 
 G_DEFINE_TYPE (GtkTooltip, gtk_tooltip, G_TYPE_OBJECT);
 
@@ -152,6 +148,8 @@ gtk_tooltip_class_init (GtkTooltipClass *klass)
 {
   GObjectClass *object_class;
 
+  quark_current_tooltip = g_quark_from_static_string ("gdk-display-current-tooltip");
+
   object_class = G_OBJECT_CLASS (klass);
 
   object_class->dispose = gtk_tooltip_dispose;
@@ -165,13 +163,12 @@ gtk_tooltip_init (GtkTooltip *tooltip)
 
   tooltip->browse_mode_enabled = FALSE;
 
-  tooltip->current_window = NULL;
-
   tooltip->tooltip_widget = NULL;
 
-  tooltip->last_surface = NULL;
+  tooltip->native = NULL;
 
   tooltip->window = gtk_tooltip_window_new ();
+  g_object_ref_sink (tooltip->window);
   g_signal_connect (tooltip->window, "hide",
                     G_CALLBACK (gtk_tooltip_window_hide),
                     tooltip);
@@ -195,7 +192,7 @@ gtk_tooltip_dispose (GObject *object)
     }
 
   gtk_tooltip_set_custom (tooltip, NULL);
-  gtk_tooltip_set_last_surface (tooltip, NULL);
+  gtk_tooltip_set_surface (tooltip, NULL);
 
   if (tooltip->window)
     {
@@ -205,8 +202,8 @@ gtk_tooltip_dispose (GObject *object)
       g_signal_handlers_disconnect_by_func (display,
                                            gtk_tooltip_display_closed,
                                            tooltip);
-      gtk_widget_destroy (tooltip->window);
-      tooltip->window = NULL;
+      gtk_tooltip_window_set_relative_to (GTK_TOOLTIP_WINDOW (tooltip->window), NULL);
+      g_clear_object (&tooltip->window);
     }
 
   G_OBJECT_CLASS (gtk_tooltip_parent_class)->dispose (object);
@@ -476,37 +473,40 @@ gtk_tooltip_display_closed (GdkDisplay *display,
 }
 
 static void
-gtk_tooltip_set_last_surface (GtkTooltip *tooltip,
-                              GdkSurface  *surface)
+native_weak_notify (gpointer data, GObject *former_object)
 {
-  GtkWidget *window_widget = NULL;
+  GtkTooltip *tooltip = data;
 
-  if (tooltip->last_surface == surface)
-    return;
+  gtk_tooltip_window_set_relative_to (GTK_TOOLTIP_WINDOW (tooltip->window), NULL);
+  tooltip->native = NULL;
+}
 
-  if (tooltip->last_surface)
-    g_object_remove_weak_pointer (G_OBJECT (tooltip->last_surface),
-                                 (gpointer *) &tooltip->last_surface);
+static void
+gtk_tooltip_set_surface (GtkTooltip *tooltip,
+                         GdkSurface  *surface)
+{
+  GtkWidget *native;
 
-  tooltip->last_surface = surface;
+  if (surface)
+    native = gtk_native_get_for_surface (surface);
+  else
+    native = NULL;
 
-  if (tooltip->last_surface)
-    g_object_add_weak_pointer (G_OBJECT (tooltip->last_surface),
-                              (gpointer *) &tooltip->last_surface);
+  if (tooltip->native == native)
+    return;
 
-  if (surface)
-    window_widget = gtk_native_get_for_surface (surface);
+  if (tooltip->native)
+    g_object_weak_unref (G_OBJECT (tooltip->native), native_weak_notify, tooltip);
+
+  tooltip->native = native;
 
-  if (window_widget)
-    window_widget = GTK_WIDGET (gtk_widget_get_root (window_widget));
+  if (tooltip->native)
+    g_object_weak_ref (G_OBJECT (tooltip->native), native_weak_notify, tooltip);
 
-  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));
+  if (native)
+    gtk_tooltip_window_set_relative_to (GTK_TOOLTIP_WINDOW (tooltip->window), native);
   else
-    gtk_window_set_transient_for (GTK_WINDOW (tooltip->window), NULL);
+    gtk_tooltip_window_set_relative_to (GTK_TOOLTIP_WINDOW (tooltip->window), NULL);
 }
 
 static gboolean
@@ -567,19 +567,17 @@ gtk_tooltip_position (GtkTooltip *tooltip,
   GtkSettings *settings;
   graphene_rect_t anchor_bounds;
   GdkRectangle anchor_rect;
-  GdkSurface *surface;
   GdkSurface *effective_toplevel;
   GtkWidget *toplevel;
   int rect_anchor_dx = 0;
   int cursor_size;
   int anchor_rect_padding;
 
-  gtk_widget_realize (GTK_WIDGET (tooltip->current_window));
-  surface = gtk_native_get_surface (GTK_NATIVE (tooltip->current_window));
+  gtk_widget_realize (GTK_WIDGET (tooltip->window));
 
   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))
     {
       anchor_rect = (GdkRectangle) {
@@ -657,16 +655,12 @@ gtk_tooltip_position (GtkTooltip *tooltip,
         }
     }
 
-  gtk_window_set_transient_for (GTK_WINDOW (tooltip->current_window),
-                                GTK_WINDOW (toplevel));
-
-  gdk_surface_move_to_rect (surface,
-                            &anchor_rect,
-                            GDK_GRAVITY_SOUTH,
-                            GDK_GRAVITY_NORTH,
-                            GDK_ANCHOR_FLIP_Y | GDK_ANCHOR_SLIDE_X,
-                            rect_anchor_dx, 0);
-  gtk_widget_show (GTK_WIDGET (tooltip->current_window));
+  gtk_tooltip_window_position (GTK_TOOLTIP_WINDOW (tooltip->window),
+                               &anchor_rect,
+                               GDK_GRAVITY_SOUTH,
+                               GDK_GRAVITY_NORTH,
+                               GDK_ANCHOR_FLIP_Y | GDK_ANCHOR_SLIDE_X,
+                               rect_anchor_dx, 0);
 }
 
 static void
@@ -682,20 +676,18 @@ gtk_tooltip_show_tooltip (GdkDisplay *display)
 
   tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
 
-  {
-    surface = tooltip->last_surface;
+  if (!tooltip->native)
+    return;
 
-    if (!GDK_IS_SURFACE (surface))
-      return;
+  surface = gtk_native_get_surface (GTK_NATIVE (tooltip->native));
 
-    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);
-    x = round (px);
-    y = round (py);
+  gdk_surface_get_device_position (surface, device, &px, &py, NULL);
+  x = round (px);
+  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)
     return;
@@ -704,10 +696,7 @@ gtk_tooltip_show_tooltip (GdkDisplay *display)
   if (!return_value)
     return;
 
-  if (!tooltip->current_window)
-    tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
-
-  /* FIXME: should use tooltip->current_window iso tooltip->window */
+  /* FIXME: should use tooltip->window iso tooltip->window */
   if (display != gtk_widget_get_display (tooltip->window))
     {
       g_signal_handlers_disconnect_by_func (display,
@@ -722,6 +711,8 @@ gtk_tooltip_show_tooltip (GdkDisplay *display)
 
   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
    * mode is enabled.
    */
@@ -765,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");
     }
 
-  if (tooltip->current_window)
-    {
-      gtk_widget_hide (GTK_WIDGET (tooltip->current_window));
-      tooltip->current_window = NULL;
-    }
+  if (tooltip->window)
+    gtk_widget_hide (tooltip->window);
 }
 
 static gint
@@ -829,7 +817,7 @@ _gtk_tooltip_hide (GtkWidget *widget)
   display = gtk_widget_get_display (widget);
   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;
 
   if (widget == tooltip->tooltip_widget)
@@ -911,19 +899,19 @@ gtk_tooltip_handle_event_internal (GdkEventType   event_type,
 {
   int x = dx, y = dy;
   GdkDisplay *display;
-  GtkTooltip *current_tooltip;
+  GtkTooltip *tooltip;
 
   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)
-    gtk_tooltip_set_last_surface (current_tooltip, surface);
+  if (tooltip)
+    gtk_tooltip_set_surface (tooltip, surface);
 
   /* Hide the tooltip when there's no new tooltip widget */
   if (!target_widget)
     {
-      if (current_tooltip)
-       gtk_tooltip_hide_tooltip (current_tooltip);
+      if (tooltip)
+       gtk_tooltip_hide_tooltip (tooltip);
 
       return;
     }
@@ -935,54 +923,49 @@ gtk_tooltip_handle_event_internal (GdkEventType   event_type,
       case GDK_DRAG_ENTER:
       case GDK_GRAB_BROKEN:
       case GDK_SCROLL:
-       gtk_tooltip_hide_tooltip (current_tooltip);
+       gtk_tooltip_hide_tooltip (tooltip);
        break;
 
       case GDK_MOTION_NOTIFY:
       case GDK_ENTER_NOTIFY:
       case GDK_LEAVE_NOTIFY:
-       if (current_tooltip)
+       if (tooltip)
          {
            gboolean tip_area_set;
             GdkRectangle tip_area;
            gboolean hide_tooltip;
 
-           tip_area_set = current_tooltip->tip_area_set;
-           tip_area = current_tooltip->tip_area;
+           tip_area_set = tooltip->tip_area_set;
+           tip_area = tooltip->tip_area;
 
-           gtk_tooltip_run_requery (&target_widget,
-                                     current_tooltip,
-                                     &x, &y);
+           gtk_tooltip_run_requery (&target_widget, tooltip, &x, &y);
 
            /* Leave notify should override the query function */
            hide_tooltip = (event_type == GDK_LEAVE_NOTIFY);
 
            /* Is the pointer above another widget now? */
-           if (GTK_TOOLTIP_VISIBLE (current_tooltip))
-             hide_tooltip |= target_widget != current_tooltip->tooltip_widget;
+           if (GTK_TOOLTIP_VISIBLE (tooltip))
+             hide_tooltip |= target_widget != tooltip->tooltip_widget;
 
            /* Did the pointer move out of the previous "context area"? */
            if (tip_area_set)
              hide_tooltip |= !gdk_rectangle_contains_point (&tip_area, x, y);
 
            if (hide_tooltip)
-             gtk_tooltip_hide_tooltip (current_tooltip);
+             gtk_tooltip_hide_tooltip (tooltip);
            else
              gtk_tooltip_start_delay (display);
          }
        else
          {
            /* Need a new tooltip for this display */
-           current_tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
-           g_object_set_qdata_full (G_OBJECT (display),
-                                    quark_current_tooltip,
-                                    current_tooltip,
-                                     g_object_unref);
+           tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
+           g_object_set_qdata_full (G_OBJECT (display), quark_current_tooltip,
+                                    tooltip, g_object_unref);
            g_signal_connect (display, "closed",
-                             G_CALLBACK (gtk_tooltip_display_closed),
-                             current_tooltip);
+                             G_CALLBACK (gtk_tooltip_display_closed), tooltip);
 
-           gtk_tooltip_set_last_surface (current_tooltip, surface);
+           gtk_tooltip_set_surface (tooltip, surface);
 
            gtk_tooltip_start_delay (display);
          }
diff --git a/gtk/gtktooltipwindow.c b/gtk/gtktooltipwindow.c
index f614acb60e..d4615647c7 100644
--- a/gtk/gtktooltipwindow.c
+++ b/gtk/gtktooltipwindow.c
@@ -38,11 +38,27 @@
 #include "gtksizerequest.h"
 #include "gtkwindowprivate.h"
 #include "gtkwidgetprivate.h"
+#include "gtknative.h"
+#include "gtkstylecontext.h"
+#include "gtkcssnodeprivate.h"
 
 struct _GtkTooltipWindow
 {
   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 *image;
   GtkWidget *label;
@@ -54,13 +70,349 @@ struct _GtkTooltipWindowClass
   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
 gtk_tooltip_window_class_init (GtkTooltipWindowClass *klass)
 {
+  GObjectClass *object_class = G_OBJECT_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_accessible_role (widget_class, ATK_ROLE_TOOL_TIP);
   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, image);
   gtk_widget_class_bind_template_child (widget_class, GtkTooltipWindow, label);
+
+  gtk_widget_class_set_css_name (widget_class, "tooltip");
 }
 
 static void
 gtk_tooltip_window_init (GtkTooltipWindow *self)
 {
-  GtkWindow *window = GTK_WINDOW (self);
-
   gtk_widget_init_template (GTK_WIDGET (self));
-
-  _gtk_window_request_csd (window);
 }
 
 GtkWidget *
 gtk_tooltip_window_new (void)
 {
-  return g_object_new (GTK_TYPE_TOOLTIP_WINDOW,
-                       "type", GTK_WINDOW_POPUP,
-                       NULL);
+  return g_object_new (GTK_TYPE_TOOLTIP_WINDOW, NULL);
 }
 
 void
@@ -191,5 +539,65 @@ gtk_tooltip_window_set_custom_widget (GtkTooltipWindow *window,
 
       gtk_container_add (GTK_CONTAINER (window->box), 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);
+}
+
diff --git a/gtk/gtktooltipwindowprivate.h b/gtk/gtktooltipwindowprivate.h
index ad26f8dd92..13556d41e3 100644
--- a/gtk/gtktooltipwindowprivate.h
+++ b/gtk/gtktooltipwindowprivate.h
@@ -49,6 +49,15 @@ void            gtk_tooltip_window_set_image_icon_from_gicon    (GtkTooltipWindo
                                                                  GIcon            *gicon);
 void            gtk_tooltip_window_set_custom_widget            (GtkTooltipWindow *window,
                                                                  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
 
diff --git a/gtk/ui/gtktooltipwindow.ui b/gtk/ui/gtktooltipwindow.ui
index bb4329f18b..2dd5584646 100644
--- a/gtk/ui/gtktooltipwindow.ui
+++ b/gtk/ui/gtktooltipwindow.ui
@@ -1,8 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface domain="gtk40">
-  <template class="GtkTooltipWindow" parent="GtkWindow">
-    <property name="resizable">0</property>
-    <property name="type-hint">tooltip</property>
+  <template class="GtkTooltipWindow" parent="GtkBin">
+    <style>
+      <class name="background"/>
+    </style>
     <child>
       <object class="GtkBox" id="box">
         <property name="spacing">6</property>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]