]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktexthandle.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtktexthandle.c
index 00f43f8473604d7812d5904dfb95e565123a9d46..bb0f8515353b0be0a0814648007d9d5ef513285d 100644 (file)
@@ -46,6 +46,9 @@ struct _HandleWindow
   gint dx;
   gint dy;
   guint dragged : 1;
+  guint mode_visible : 1;
+  guint user_visible : 1;
+  guint has_point : 1;
 };
 
 struct _GtkTextHandlePrivate
@@ -103,13 +106,22 @@ _gtk_text_handle_draw (GtkTextHandle         *handle,
   cairo_set_source_rgba (cr, 0, 0, 0, 0);
   cairo_paint (cr);
 
+  if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
+    cairo_translate (cr, 0, priv->windows[pos].pointing_to.height);
+
   gtk_style_context_save (priv->style_context);
   gtk_style_context_add_class (priv->style_context,
                                GTK_STYLE_CLASS_CURSOR_HANDLE);
 
   if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
-    gtk_style_context_add_class (priv->style_context,
-                                 GTK_STYLE_CLASS_BOTTOM);
+    {
+      gtk_style_context_add_class (priv->style_context,
+                                   GTK_STYLE_CLASS_BOTTOM);
+
+      if (priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR)
+        gtk_style_context_add_class (priv->style_context,
+                                     GTK_STYLE_CLASS_INSERTION_CURSOR);
+    }
   else
     gtk_style_context_add_class (priv->style_context,
                                  GTK_STYLE_CLASS_TOP);
@@ -122,49 +134,53 @@ _gtk_text_handle_draw (GtkTextHandle         *handle,
 }
 
 static void
-_gtk_text_handle_update_shape (GtkTextHandle *handle,
-                               GdkWindow     *window)
+_gtk_text_handle_update_shape (GtkTextHandle         *handle,
+                               GdkWindow             *window,
+                               GtkTextHandlePosition  pos)
 {
   GtkTextHandlePrivate *priv;
+  cairo_rectangle_int_t rect;
+  cairo_surface_t *surface;
+  cairo_region_t *region;
+  cairo_t *cr;
 
   priv = handle->priv;
 
+  surface =
+    gdk_window_create_similar_surface (window,
+                                       CAIRO_CONTENT_COLOR_ALPHA,
+                                       gdk_window_get_width (window),
+                                       gdk_window_get_height (window));
+
+  cr = cairo_create (surface);
+  _gtk_text_handle_draw (handle, cr, pos);
+  cairo_destroy (cr);
+
+  region = gdk_cairo_region_create_from_surface (surface);
+
   if (gtk_widget_is_composited (priv->parent))
     gdk_window_shape_combine_region (window, NULL, 0, 0);
   else
-    {
-      GtkTextHandlePosition pos;
-      cairo_surface_t *surface;
-      cairo_region_t *region;
-      cairo_t *cr;
-
-      if (window == priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window)
-        pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
-      else if (window == priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window)
-        pos = GTK_TEXT_HANDLE_POSITION_SELECTION_END;
-      else
-        return;
-
-      surface =
-        gdk_window_create_similar_surface (window,
-                                           CAIRO_CONTENT_COLOR_ALPHA,
-                                           gdk_window_get_width (window),
-                                           gdk_window_get_height (window));
-
-      cr = cairo_create (surface);
-      _gtk_text_handle_draw (handle, cr, pos);
-      cairo_destroy (cr);
-
-      region = gdk_cairo_region_create_from_surface (surface);
-      gdk_window_shape_combine_region (window, region, 0, 0);
-
-      cairo_surface_destroy (surface);
-      cairo_region_destroy (region);
-    }
+    gdk_window_shape_combine_region (window, region, 0, 0);
+
+  cairo_region_get_extents (region, &rect);
+  cairo_region_destroy (region);
+
+  /* Preserve x/width, but extend input shape
+   * vertically to all window height */
+  rect.y = 0;
+  rect.height = gdk_window_get_height (window);
+  region = cairo_region_create_rectangle (&rect);
+
+  gdk_window_input_shape_combine_region (window, region, 0, 0);
+
+  cairo_surface_destroy (surface);
+  cairo_region_destroy (region);
 }
 
 static GdkWindow *
-_gtk_text_handle_create_window (GtkTextHandle *handle)
+_gtk_text_handle_create_window (GtkTextHandle         *handle,
+                                GtkTextHandlePosition  pos)
 {
   GtkTextHandlePrivate *priv;
   GdkRGBA bg = { 0, 0, 0, 0 };
@@ -195,11 +211,12 @@ _gtk_text_handle_create_window (GtkTextHandle *handle)
       mask |= GDK_WA_VISUAL;
     }
 
-  window = gdk_window_new (NULL, &attributes, mask);
-  gdk_window_set_user_data (window, priv->parent);
+  window = gdk_window_new (gtk_widget_get_root_window (priv->parent),
+                          &attributes, mask);
+  gtk_widget_register_window (priv->parent, window);
   gdk_window_set_background_rgba (window, &bg);
 
-  _gtk_text_handle_update_shape (handle, window);
+  _gtk_text_handle_update_shape (handle, window, pos);
 
   return window;
 }
@@ -211,6 +228,7 @@ gtk_text_handle_widget_draw (GtkWidget     *widget,
 {
   GtkTextHandlePrivate *priv;
   GtkTextHandlePosition pos;
+  HandleWindow *handle_window;
 
   priv = handle->priv;
 
@@ -224,8 +242,11 @@ gtk_text_handle_widget_draw (GtkWidget     *widget,
   else
     return FALSE;
 
-  _gtk_text_handle_draw (handle, cr, pos);
-  return TRUE;
+  handle_window = &priv->windows[pos];
+  if (gdk_window_is_visible (handle_window->window))
+    _gtk_text_handle_draw (handle, cr, pos);
+
+  return FALSE;
 }
 
 static gboolean
@@ -270,6 +291,8 @@ gtk_text_handle_widget_event (GtkWidget     *widget,
       if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_START)
         y += height;
 
+      y += priv->windows[pos].pointing_to.height / 2;
+
       g_signal_emit (handle, signals[HANDLE_DRAGGED], 0, pos, x, y);
     }
 
@@ -277,13 +300,11 @@ gtk_text_handle_widget_event (GtkWidget     *widget,
 }
 
 static void
-_gtk_text_handle_update_window (GtkTextHandle         *handle,
-                                GtkTextHandlePosition  pos)
+_gtk_text_handle_update_window_state (GtkTextHandle         *handle,
+                                      GtkTextHandlePosition  pos)
 {
   GtkTextHandlePrivate *priv;
   HandleWindow *handle_window;
-  gboolean visible;
-  gint x, y;
 
   priv = handle->priv;
   handle_window = &priv->windows[pos];
@@ -291,29 +312,50 @@ _gtk_text_handle_update_window (GtkTextHandle         *handle,
   if (!handle_window->window)
     return;
 
-  /* Get current state and destroy */
-  visible = gdk_window_is_visible (handle_window->window);
-
-  if (visible)
+  if (handle_window->has_point &&
+      handle_window->mode_visible && handle_window->user_visible)
     {
-      gint width;
+      gint x, y, width, height;
 
-      _gtk_text_handle_get_size (handle, &width, NULL);
-      gdk_window_get_root_coords (handle_window->window,
-                                  width / 2, 0, &x, &y);
+      x = handle_window->pointing_to.x;
+      y = handle_window->pointing_to.y;
+      _gtk_text_handle_get_size (handle, &width, &height);
+
+      if (pos != GTK_TEXT_HANDLE_POSITION_CURSOR)
+        y -= height;
+
+      height += handle_window->pointing_to.height;
+      x -= width / 2;
+
+      gdk_window_move_resize (handle_window->window, x, y, width, height);
+      gdk_window_show (handle_window->window);
     }
+  else
+    gdk_window_hide (handle_window->window);
+}
+
+static void
+_gtk_text_handle_update_window (GtkTextHandle         *handle,
+                                GtkTextHandlePosition  pos,
+                                gboolean               recreate)
+{
+  GtkTextHandlePrivate *priv;
+  HandleWindow *handle_window;
 
-  gdk_window_destroy (handle_window->window);
+  priv = handle->priv;
+  handle_window = &priv->windows[pos];
 
-  /* Create new window and apply old state */
-  handle_window->window = _gtk_text_handle_create_window (handle);
+  if (!handle_window->window)
+    return;
 
-  if (visible)
+  if (recreate)
     {
-      gdk_window_show (handle_window->window);
-      _gtk_text_handle_set_position (handle, pos,
-                                     &handle_window->pointing_to);
+      gtk_widget_unregister_window (priv->parent, handle_window->window);
+      gdk_window_destroy (handle_window->window);
+      handle_window->window = _gtk_text_handle_create_window (handle, pos);
     }
+
+  _gtk_text_handle_update_window_state (handle, pos);
 }
 
 static void
@@ -322,8 +364,15 @@ _gtk_text_handle_update_windows (GtkTextHandle *handle)
   GtkTextHandlePrivate *priv = handle->priv;
 
   gtk_style_context_invalidate (priv->style_context);
-  _gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START);
-  _gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END);
+  _gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START, FALSE);
+  _gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END, FALSE);
+}
+
+static void
+_gtk_text_handle_composited_changed (GtkTextHandle *handle)
+{
+  _gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START, TRUE);
+  _gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END, TRUE);
 }
 
 static void
@@ -344,7 +393,7 @@ gtk_text_handle_constructed (GObject *object)
                       object);
   priv->composited_changed_id =
     g_signal_connect_swapped (priv->parent, "composited-changed",
-                              G_CALLBACK (_gtk_text_handle_update_windows),
+                              G_CALLBACK (_gtk_text_handle_composited_changed),
                               object);
   priv->style_updated_id =
     g_signal_connect_swapped (priv->parent, "style-updated",
@@ -363,10 +412,18 @@ gtk_text_handle_finalize (GObject *object)
     g_object_unref (priv->relative_to);
 
   if (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window)
-    gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
+    {
+      gtk_widget_unregister_window (priv->parent,
+                                   priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
+      gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
+    }
 
   if (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window)
-    gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
+    {
+      gtk_widget_unregister_window (priv->parent,
+                                   priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
+      gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
+    }
 
   if (g_signal_handler_is_connected (priv->parent, priv->draw_signal_id))
     g_signal_handler_disconnect (priv->parent, priv->draw_signal_id);
@@ -521,7 +578,11 @@ _gtk_text_handle_set_relative_to (GtkTextHandle *handle,
 
   if (priv->relative_to)
     {
+      gtk_widget_unregister_window (priv->parent,
+                                   priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
       gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
+      gtk_widget_unregister_window (priv->parent,
+                                   priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
       gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
       g_object_unref (priv->relative_to);
     }
@@ -530,9 +591,9 @@ _gtk_text_handle_set_relative_to (GtkTextHandle *handle,
     {
       priv->relative_to = g_object_ref (window);
       priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window =
-        _gtk_text_handle_create_window (handle);
+        _gtk_text_handle_create_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START);
       priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window =
-        _gtk_text_handle_create_window (handle);
+        _gtk_text_handle_create_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END);
       priv->realized = TRUE;
     }
   else
@@ -559,26 +620,34 @@ _gtk_text_handle_set_mode (GtkTextHandle     *handle,
   if (priv->mode == mode)
     return;
 
+  priv->mode = mode;
+
   switch (mode)
     {
     case GTK_TEXT_HANDLE_MODE_CURSOR:
-      /* Only display one handle */
-      gdk_window_show (priv->windows[GTK_TEXT_HANDLE_POSITION_CURSOR].window);
-      gdk_window_hide (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
+      priv->windows[GTK_TEXT_HANDLE_POSITION_CURSOR].mode_visible = TRUE;
+      priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].mode_visible = FALSE;
       break;
-      case GTK_TEXT_HANDLE_MODE_SELECTION:
-        /* Display both handles */
-      gdk_window_show (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
-      gdk_window_show (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
+    case GTK_TEXT_HANDLE_MODE_SELECTION:
+      priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].mode_visible = TRUE;
+      priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].mode_visible = TRUE;
       break;
     case GTK_TEXT_HANDLE_MODE_NONE:
     default:
-      gdk_window_hide (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
-      gdk_window_hide (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
+      priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].mode_visible = FALSE;
+      priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].mode_visible = FALSE;
       break;
     }
 
-  priv->mode = mode;
+  _gtk_text_handle_update_shape (handle,
+                                 priv->windows[GTK_TEXT_HANDLE_POSITION_CURSOR].window,
+                                 GTK_TEXT_HANDLE_POSITION_CURSOR);
+  _gtk_text_handle_update_shape (handle,
+                                 priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window,
+                                 GTK_TEXT_HANDLE_POSITION_SELECTION_START);
+
+  _gtk_text_handle_update_window_state (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START);
+  _gtk_text_handle_update_window_state (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END);
 }
 
 GtkTextHandleMode
@@ -598,14 +667,15 @@ _gtk_text_handle_set_position (GtkTextHandle         *handle,
                                GdkRectangle          *rect)
 {
   GtkTextHandlePrivate *priv;
-  gint x, y, width, height;
   HandleWindow *handle_window;
+  gboolean size_changed;
 
   g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
 
   priv = handle->priv;
   pos = CLAMP (pos, GTK_TEXT_HANDLE_POSITION_CURSOR,
                GTK_TEXT_HANDLE_POSITION_SELECTION_START);
+  handle_window = &priv->windows[pos];
 
   if (!priv->realized)
     return;
@@ -615,21 +685,20 @@ _gtk_text_handle_set_position (GtkTextHandle         *handle,
        pos != GTK_TEXT_HANDLE_POSITION_CURSOR))
     return;
 
+  size_changed = (rect->width != handle_window->pointing_to.width ||
+                  rect->height != handle_window->pointing_to.height);
+
+  handle_window->pointing_to = *rect;
+  handle_window->has_point = TRUE;
   gdk_window_get_root_coords (priv->relative_to,
                               rect->x, rect->y,
-                              &x, &y);
-  _gtk_text_handle_get_size (handle, &width, &height);
-  handle_window = &priv->windows[pos];
-
-  if (pos == GTK_TEXT_HANDLE_POSITION_CURSOR)
-    y += rect->height;
-  else
-    y -= height;
+                              &handle_window->pointing_to.x,
+                              &handle_window->pointing_to.y);
 
-  x -= width / 2;
+  _gtk_text_handle_update_window_state (handle, pos);
 
-  gdk_window_move (handle_window->window, x, y);
-  handle_window->pointing_to = *rect;
+  if (size_changed)
+    _gtk_text_handle_update_shape (handle, handle_window->window, pos);
 }
 
 void
@@ -654,18 +723,11 @@ _gtk_text_handle_set_visible (GtkTextHandle         *handle,
   if (!window)
     return;
 
-  if (!visible)
-    gdk_window_hide (window);
-  else
-    {
-      if (priv->mode == GTK_TEXT_HANDLE_MODE_NONE ||
-          (priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR &&
-           pos != GTK_TEXT_HANDLE_POSITION_CURSOR))
-        return;
+  if (!gdk_window_is_visible (window))
+    _gtk_text_handle_update_shape (handle, window, pos);
 
-      if (!gdk_window_is_visible (window))
-        gdk_window_show (window);
-    }
+  priv->windows[pos].user_visible = visible;
+  _gtk_text_handle_update_window_state (handle, pos);
 }
 
 gboolean