]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktooltip.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtktooltip.c
index 97b707a81035388f574a6d32d898f4c14d4954f6..aba9ffb38a173b8be9a9a377bc19c76c4c9f05d2 100644 (file)
@@ -14,9 +14,7 @@
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
 #include "gtkmain.h"
 #include "gtklabel.h"
 #include "gtkimage.h"
-#include "gtkhbox.h"
-#include "gtkalignment.h"
+#include "gtkbox.h"
+#include "gtksettings.h"
 #include "gtksizerequest.h"
+#include "gtkstylecontext.h"
 #include "gtkwindowprivate.h"
 
 
+#ifdef GDK_WINDOWING_WAYLAND
+#include "wayland/gdkwayland.h"
+#endif
+
+
 /**
  * SECTION:gtktooltip
  * @Short_description: Add tips to your widgets
@@ -117,7 +121,6 @@ struct _GtkTooltip
   GObject parent_instance;
 
   GtkWidget *window;
-  GtkWidget *alignment;
   GtkWidget *box;
   GtkWidget *image;
   GtkWidget *label;
@@ -156,9 +159,12 @@ static void       gtk_tooltip_class_init           (GtkTooltipClass *klass);
 static void       gtk_tooltip_init                 (GtkTooltip      *tooltip);
 static void       gtk_tooltip_dispose              (GObject         *object);
 
-static void       gtk_tooltip_window_style_updated (GtkTooltip      *tooltip);
 static gboolean   gtk_tooltip_paint_window         (GtkTooltip      *tooltip,
                                                     cairo_t         *cr);
+static void       gtk_tooltip_realize_window       (GtkTooltip      *tooltip,
+                                                    GtkWidget       *widget);
+static void       gtk_tooltip_composited_changed   (GtkTooltip      *tooltip,
+                                                    GtkWidget       *widget);
 static void       gtk_tooltip_window_hide          (GtkWidget       *widget,
                                                    gpointer         user_data);
 static void       gtk_tooltip_display_closed       (GdkDisplay      *display,
@@ -184,7 +190,12 @@ static void
 gtk_tooltip_init (GtkTooltip *tooltip)
 {
   GtkStyleContext *context;
-  GtkBorder padding, border;
+  GtkWidget *window;
+  GtkWidget *box;
+  GtkWidget *image;
+  GtkWidget *label;
+  GdkScreen *screen;
+  GdkVisual *visual;
 
   tooltip->timeout_id = 0;
   tooltip->browse_mode_timeout_id = 0;
@@ -200,48 +211,50 @@ gtk_tooltip_init (GtkTooltip *tooltip)
 
   tooltip->last_window = NULL;
 
-  tooltip->window = g_object_ref (gtk_window_new (GTK_WINDOW_POPUP));
-  gtk_window_set_type_hint (GTK_WINDOW (tooltip->window),
-                           GDK_WINDOW_TYPE_HINT_TOOLTIP);
-  gtk_widget_set_app_paintable (tooltip->window, TRUE);
-  gtk_window_set_resizable (GTK_WINDOW (tooltip->window), FALSE);
-  gtk_widget_set_name (tooltip->window, "gtk-tooltip");
-  g_signal_connect (tooltip->window, "hide",
-                   G_CALLBACK (gtk_tooltip_window_hide), tooltip);
-
-  context = gtk_widget_get_style_context (tooltip->window);
-  gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOOLTIP);
-
-  gtk_style_context_get_padding (context, 0, &padding);
-  gtk_style_context_get_border (context, 0, &border);
-
-  tooltip->alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
-  gtk_alignment_set_padding (GTK_ALIGNMENT (tooltip->alignment),
-                             border.top + padding.top,
-                             border.bottom + padding.bottom,
-                             border.left + padding.left,
-                             border.right + padding.right);
-  gtk_container_add (GTK_CONTAINER (tooltip->window), tooltip->alignment);
-  gtk_widget_show (tooltip->alignment);
+  window = gtk_window_new (GTK_WINDOW_POPUP);
+  screen = gtk_widget_get_screen (window);
+  visual = gdk_screen_get_rgba_visual (screen);
 
-  g_signal_connect_swapped (tooltip->window, "style-updated",
-                           G_CALLBACK (gtk_tooltip_window_style_updated), tooltip);
-  g_signal_connect_swapped (tooltip->window, "draw",
-                           G_CALLBACK (gtk_tooltip_paint_window), tooltip);
+  if (visual != NULL)
+    gtk_widget_set_visual (window, visual);
 
-  tooltip->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, padding.left);
-  gtk_container_add (GTK_CONTAINER (tooltip->alignment), tooltip->box);
-  gtk_widget_show (tooltip->box);
+  gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_TOOLTIP);
+  gtk_widget_set_app_paintable (window, TRUE);
+  gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
+  gtk_widget_set_name (window, "gtk-tooltip");
+  g_signal_connect (window, "hide",
+                    G_CALLBACK (gtk_tooltip_window_hide), tooltip);
 
-  tooltip->image = gtk_image_new ();
-  gtk_box_pack_start (GTK_BOX (tooltip->box), tooltip->image,
-                     FALSE, FALSE, 0);
-
-  tooltip->label = gtk_label_new ("");
-  gtk_label_set_line_wrap (GTK_LABEL (tooltip->label), TRUE);
-  gtk_box_pack_start (GTK_BOX (tooltip->box), tooltip->label,
-                     FALSE, FALSE, 0);
+  context = gtk_widget_get_style_context (window);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOOLTIP);
 
+  g_signal_connect_swapped (window, "draw",
+                            G_CALLBACK (gtk_tooltip_paint_window), tooltip);
+  g_signal_connect_swapped (window, "realize",
+                            G_CALLBACK (gtk_tooltip_realize_window), tooltip);
+  g_signal_connect_swapped (window, "composited-changed",
+                            G_CALLBACK (gtk_tooltip_composited_changed), tooltip);
+
+  /* FIXME: don't hardcode the padding */
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+  gtk_widget_set_margin_left (box, 6);
+  gtk_widget_set_margin_right (box, 6);
+  gtk_widget_set_margin_top (box, 6);
+  gtk_widget_set_margin_bottom (box, 6);
+  gtk_container_add (GTK_CONTAINER (window), box);
+  gtk_widget_show (box);
+
+  image = gtk_image_new ();
+  gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
+
+  label = gtk_label_new ("");
+  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+  gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+
+  tooltip->window = window;
+  tooltip->box = box;
+  tooltip->image = image;
+  tooltip->label = label;
   tooltip->custom_widget = NULL;
 }
 
@@ -577,40 +590,83 @@ gtk_tooltip_reset (GtkTooltip *tooltip)
 }
 
 static void
-gtk_tooltip_window_style_updated (GtkTooltip *tooltip)
+paint_background_and_frame (GtkTooltip *tooltip,
+                            cairo_t *cr)
 {
   GtkStyleContext *context;
-  GtkBorder padding, border;
+  gint width, height;
 
+  width = gtk_widget_get_allocated_width (tooltip->window);
+  height = gtk_widget_get_allocated_height (tooltip->window);
   context = gtk_widget_get_style_context (tooltip->window);
-  gtk_style_context_get_padding (context, 0, &padding);
-  gtk_style_context_get_border (context, 0, &border);
 
-  gtk_alignment_set_padding (GTK_ALIGNMENT (tooltip->alignment),
-                             border.top + padding.top,
-                             border.bottom + padding.bottom,
-                             border.left + padding.left,
-                             border.right + padding.right);
+  gtk_render_background (context, cr,
+                         0, 0, width, height);
+  gtk_render_frame (context, cr,
+                    0, 0, width, height);  
+}
+
+static void
+maybe_update_shape (GtkTooltip *tooltip)
+{
+  cairo_t *cr;
+  cairo_surface_t *surface;
+  cairo_region_t *region;
+
+  /* fallback to XShape only for non-composited clients */
+  if (gtk_widget_is_composited (tooltip->window))
+    {
+      gtk_widget_shape_combine_region (tooltip->window, NULL);
+      return;
+    }
+
+  surface = gdk_window_create_similar_surface (gtk_widget_get_window (tooltip->window),
+                                               CAIRO_CONTENT_COLOR_ALPHA,
+                                               gtk_widget_get_allocated_width (tooltip->window),
+                                               gtk_widget_get_allocated_height (tooltip->window));
+
+  cr = cairo_create (surface);
+  paint_background_and_frame (tooltip, cr);
+  cairo_destroy (cr);
+
+  region = gdk_cairo_region_create_from_surface (surface);
+  gtk_widget_shape_combine_region (tooltip->window, region);
 
-  gtk_box_set_spacing (GTK_BOX (tooltip->box), padding.left);
+  cairo_surface_destroy (surface);
+  cairo_region_destroy (region);
+}
 
-  gtk_widget_queue_draw (tooltip->window);
+static void
+gtk_tooltip_composited_changed (GtkTooltip *tooltip,
+                                GtkWidget  *widget)
+{
+  if (gtk_widget_get_realized (tooltip->window))
+    maybe_update_shape (tooltip);
+}
+
+static void
+gtk_tooltip_realize_window (GtkTooltip *tooltip,
+                            GtkWidget *widget)
+{
+  maybe_update_shape (tooltip);
 }
 
 static gboolean
 gtk_tooltip_paint_window (GtkTooltip *tooltip,
                           cairo_t    *cr)
 {
-  GtkStyleContext *context;
-
-  context = gtk_widget_get_style_context (tooltip->window);
+  if (gtk_widget_is_composited (tooltip->window))
+    {
+      /* clear any background */
+      cairo_save (cr);
+      cairo_set_source_rgba (cr, 0, 0, 0, 0);
+      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+      cairo_paint (cr);
+      cairo_restore (cr);
+    }
 
-  gtk_render_background (context, cr, 0, 0,
-                        gtk_widget_get_allocated_width (tooltip->window),
-                        gtk_widget_get_allocated_height (tooltip->window));
-  gtk_render_frame (context, cr, 0, 0,
-                    gtk_widget_get_allocated_width (tooltip->window),
-                    gtk_widget_get_allocated_height (tooltip->window));
+  maybe_update_shape (tooltip);
+  paint_background_and_frame (tooltip, cr);
 
   return FALSE;
 }
@@ -1026,7 +1082,7 @@ gtk_tooltip_position (GtkTooltip *tooltip,
   monitor_num = gdk_screen_get_monitor_at_point (screen,
                                                  tooltip->last_x,
                                                  tooltip->last_y);
-  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+  gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
 
   get_bounding_box (new_tooltip_widget, &bounds);
 
@@ -1149,6 +1205,20 @@ found:
             y = tooltip->last_y - height - 2;
         }
 
+#ifdef GDK_WINDOWING_WAYLAND
+      /* set the transient parent on the tooltip when running with the Wayland
+       * backend to allow correct positioning of the tooltip windows */
+      if (GDK_IS_WAYLAND_DISPLAY (display))
+        {
+          GtkWidget *toplevel;
+
+          toplevel = gtk_widget_get_toplevel (tooltip->tooltip_widget);
+          if (GTK_IS_WINDOW (toplevel))
+            gtk_window_set_transient_for (GTK_WINDOW (tooltip->current_window),
+                                          GTK_WINDOW (toplevel));
+        }
+#endif
+
       gtk_window_move (GTK_WINDOW (tooltip->current_window), x, y);
       gtk_widget_show (GTK_WIDGET (tooltip->current_window));
     }
@@ -1176,6 +1246,7 @@ gtk_tooltip_show_tooltip (GdkDisplay *display)
     }
   else
     {
+      GdkDevice *device;
       gint tx, ty;
 
       window = tooltip->last_window;
@@ -1183,7 +1254,9 @@ gtk_tooltip_show_tooltip (GdkDisplay *display)
       if (!GDK_IS_WINDOW (window))
         return;
 
-      gdk_window_get_pointer (window, &x, &y, NULL);
+      device = gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
+
+      gdk_window_get_device_position (window, device, &x, &y, NULL);
 
       gdk_window_get_root_coords (window, x, y, &tx, &ty);
       tooltip->last_x = tx;
@@ -1483,22 +1556,33 @@ _gtk_tooltip_hide (GtkWidget *widget)
 }
 
 static gboolean
-tooltips_enabled (GdkWindow *window)
+tooltips_enabled (GdkEvent *event)
 {
+  GdkDevice *source_device;
+  GdkInputSource source;
+  GdkWindow *window;
   gboolean enabled;
-  gboolean touchscreen;
   GdkScreen *screen;
   GtkSettings *settings;
 
+  window = event->any.window;
+  source_device = gdk_event_get_source_device (event);
+
+  if (!source_device)
+    return FALSE;
+
+  source = gdk_device_get_source (source_device);
   screen = gdk_window_get_screen (window);
   settings = gtk_settings_get_for_screen (screen);
 
   g_object_get (settings,
-               "gtk-touchscreen-mode", &touchscreen,
                "gtk-enable-tooltips", &enabled,
                NULL);
 
-  return (!touchscreen && enabled);
+  if (enabled && source != GDK_SOURCE_TOUCHSCREEN)
+    return TRUE;
+
+  return FALSE;
 }
 
 void
@@ -1510,7 +1594,7 @@ _gtk_tooltip_handle_event (GdkEvent *event)
   GdkDisplay *display;
   GtkTooltip *current_tooltip;
 
-  if (!tooltips_enabled (event->any.window))
+  if (!tooltips_enabled (event))
     return;
 
   /* Returns coordinates relative to has_tooltip_widget's allocation. */
@@ -1573,13 +1657,13 @@ _gtk_tooltip_handle_event (GdkEvent *event)
       case GDK_KEY_PRESS:
       case GDK_DRAG_ENTER:
       case GDK_GRAB_BROKEN:
+      case GDK_SCROLL:
        gtk_tooltip_hide_tooltip (current_tooltip);
        break;
 
       case GDK_MOTION_NOTIFY:
       case GDK_ENTER_NOTIFY:
       case GDK_LEAVE_NOTIFY:
-      case GDK_SCROLL:
        if (current_tooltip)
          {
            gboolean tip_area_set;