[aisleriot] board: Modernise moving card drawing



commit 4c3f20582f818506ce5b1d59ed899809d0a01de0
Author: Christian Persch <chpe gnome org>
Date:   Sun Feb 22 13:10:18 2015 +0100

    board: Modernise moving card drawing
    
    Using a child GdkWindow has been broken in gtk+ for some time.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=702951

 src/board-noclutter.c |  228 ++++++++++++++++++++++++-------------------------
 1 files changed, 111 insertions(+), 117 deletions(-)
---
diff --git a/src/board-noclutter.c b/src/board-noclutter.c
index e604728..8ff985c 100644
--- a/src/board-noclutter.c
+++ b/src/board-noclutter.c
@@ -112,8 +112,7 @@ struct _AisleriotBoardPrivate
   /* Moving cards */
   ArSlot *moving_cards_origin_slot;
   int moving_cards_origin_card_id; /* The index of the card that was clicked on in hslot->cards; or -1 if 
the click wasn't on a card */
-  GdkWindow *moving_cards_window;
-  GByteArray *moving_cards;
+  ArSlot *moving_cards_slot; /* if non-NULL, a move is in progress */
 
   /* The 'reveal card' action's slot and card link */
   ArSlot *show_card_slot;
@@ -811,6 +810,12 @@ aisleriot_board_setup_geometry (AisleriotBoard *board)
     slot_update_card_images (board, slot);
   }
 
+  if (priv->moving_cards_slot != NULL) {
+    GdkRectangle *rect = &priv->moving_cards_slot->rect;
+    slot_update_geometry (board, priv->moving_cards_slot);
+    slot_update_card_images (board, priv->moving_cards_slot);
+  }
+
   /* Update the focus and selection rects */
   get_focus_rect (board, &priv->focus_rect);
   get_selection_rect (board, &priv->selection_rect);
@@ -821,18 +826,11 @@ drag_begin (AisleriotBoard *board)
 {
   AisleriotBoardPrivate *priv = board->priv;
   GtkWidget *widget = GTK_WIDGET (board);
-  ArSlot *hslot;
-  int delta, height, width;
+  ArSlot *hslot, *mslot;
+  int delta;
   int x, y;
   int num_moving_cards;
-  guint i;
   GByteArray *cards;
-  GdkWindowAttr attributes;
-  GdkWindow *window;
-  cairo_t *cr;
-  cairo_surface_t *surface;
-  cairo_region_t *shape;
-  cairo_pattern_t *pattern;
 
   if (!priv->selection_slot ||
       priv->selection_start_card_id < 0) {
@@ -869,95 +867,30 @@ drag_begin (AisleriotBoard *board)
   priv->last_click_x -= x;
   priv->last_click_y -= y = hslot->rect.y + delta * hslot->pixeldy;;
 
-  g_byte_array_set_size (priv->moving_cards, 0);
-  g_byte_array_append (priv->moving_cards,
+  /* Create temporary slot for moving cards */
+  priv->moving_cards_slot = mslot = g_slice_dup (ArSlot, hslot);
+  mslot->cards = g_byte_array_sized_new (SLOT_CARDS_N_PREALLOC);
+  g_byte_array_append (mslot->cards,
                        cards->data + priv->moving_cards_origin_card_id,
                        cards->len - priv->moving_cards_origin_card_id);
-
+  mslot->expansion_depth = 0;
+  mslot->exposed = mslot->cards->len;
+  mslot->id = -1;
+  mslot->x = mslot->y = 0.;
+  mslot->card_images = g_ptr_array_sized_new (SLOT_CARDS_N_PREALLOC);
+  mslot->needs_update = TRUE;
+  slot_update_geometry (board, mslot);
+  slot_update_card_images_full (board, mslot, G_MAXINT);
+  mslot->rect.x = x;
+  mslot->rect.y = y;
+  
   /* Take the cards off of the stack */
   g_byte_array_set_size (cards, priv->moving_cards_origin_card_id);
   g_ptr_array_set_size (hslot->card_images, priv->moving_cards_origin_card_id);
-
-  width = priv->card_size.width + (num_moving_cards - 1) * hslot->pixeldx;
-  height = priv->card_size.height + (num_moving_cards - 1) * hslot->pixeldy;
-
-  window = gtk_widget_get_window (widget);
-
-  attributes.wclass = GDK_INPUT_OUTPUT;
-  attributes.window_type = GDK_WINDOW_CHILD;
-  attributes.event_mask = 0;
-  attributes.x = x;
-  attributes.y = y;
-  attributes.width = width;
-  attributes.height = height;
-  attributes.visual = gdk_window_get_visual (window);
-
-  priv->moving_cards_window = gdk_window_new (window, &attributes,
-                                              GDK_WA_VISUAL | GDK_WA_X | GDK_WA_Y);
-
-  /* FIXME: workaround for gtk+ bug #702951 */
-  gdk_window_ensure_native (priv->moving_cards_window);
-
-  surface = gdk_window_create_similar_surface (priv->moving_cards_window, CAIRO_CONTENT_COLOR_ALPHA,
-                                               width, height);
-  cr = cairo_create (surface);
-
-  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
-  cairo_paint (cr);
-
-  cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
-
-  /* FIXMEchpe: RTL issue: this doesn't work right when we allow dragging of
-   * more than one card from a expand-right slot. (But right now no game .scm
-   * does allow that.)
-   */
-  x = y = 0;
-  width = priv->card_size.width;
-  height = priv->card_size.height;
-
-  for (i = 0; i < priv->moving_cards->len; ++i) {
-    Card hcard = CARD (priv->moving_cards->data[i]);
-
-    cairo_surface_t *card_surface;
-    cairo_pattern_t *card_pattern;
-    cairo_matrix_t matrix;
-
-    card_surface = ar_card_surface_cache_get_card_surface (priv->card_cache, hcard);
-    if (card_surface == NULL)
-      goto next;
-
-    card_pattern = cairo_pattern_create_for_surface (card_surface);
-    cairo_matrix_init_translate (&matrix, -x, -y);
-    cairo_pattern_set_matrix (card_pattern, &matrix);
-
-    cairo_set_source (cr, card_pattern);
-    cairo_paint (cr);
-
-    cairo_pattern_destroy (card_pattern);
-
-  next:
-
-    x += hslot->pixeldx;
-    y += hslot->pixeldy;
-  }
-
-  cairo_destroy (cr);
-
-  pattern = cairo_pattern_create_for_surface (surface);
-  gdk_window_set_background_pattern (priv->moving_cards_window, pattern);
-  cairo_pattern_destroy (pattern);
-
-  shape = gdk_cairo_region_create_from_surface (surface);
-  gdk_window_shape_combine_region (priv->moving_cards_window, shape, 0, 0);
-  cairo_region_destroy (shape);
-
-  cairo_surface_destroy (surface);
-
-  gdk_window_show (priv->moving_cards_window);
-
   slot_update_geometry (board, hslot);
   slot_update_card_images (board, hslot);
 
+  /* Change cursor */
   set_cursor (board, AR_CURSOR_CLOSED);
 }
 
@@ -966,27 +899,34 @@ drag_end (AisleriotBoard *board,
           gboolean moved)
 {
   AisleriotBoardPrivate *priv = board->priv;
-
-  if (priv->moving_cards_window != NULL) {
-    gdk_window_hide (priv->moving_cards_window);
-    gdk_window_destroy (priv->moving_cards_window);
-    priv->moving_cards_window = NULL;
-  }
+  GtkWidget *widget = GTK_WIDGET (board);
 
   /* FIXMEchpe: check that slot->cards->len == moving_cards_origin_card_id !!! FIXMEchpe what to do if not, 
abort the game? */
   /* Add the origin cards back to the origin slot */
   if (!moved &&
       priv->moving_cards_origin_slot != NULL &&
-      priv->moving_cards->len > 0) {
+      priv->moving_cards_slot != NULL &&
+      priv->moving_cards_slot->cards->len > 0) {
     aisleriot_game_slot_add_cards (priv->game,
                                    priv->moving_cards_origin_slot,
-                                   priv->moving_cards->data,
-                                   priv->moving_cards->len);
+                                   priv->moving_cards_slot->cards->data,
+                                   priv->moving_cards_slot->cards->len);
   }
 
   priv->click_status = STATUS_NONE;
   priv->moving_cards_origin_slot = NULL;
   priv->moving_cards_origin_card_id = -1;
+
+  if (priv->moving_cards_slot != NULL) {
+    GdkRectangle *rect = &priv->moving_cards_slot->rect;
+
+    gtk_widget_queue_draw_area (widget, rect->x, rect->y, rect->width, rect->height);
+
+    g_byte_array_free (priv->moving_cards_slot->cards, TRUE);
+    g_ptr_array_free (priv->moving_cards_slot->card_images, TRUE);
+    g_slice_free (ArSlot, priv->moving_cards_slot);
+    priv->moving_cards_slot = NULL;
+  }
 }
 
 static gboolean
@@ -996,12 +936,13 @@ cards_are_droppable (AisleriotBoard *board,
   AisleriotBoardPrivate *priv = board->priv;
 
   return slot != NULL &&
-         priv->moving_cards_origin_slot &&
+         priv->moving_cards_origin_slot != NULL &&
+         priv->moving_cards_slot != NULL &&
          aisleriot_game_drop_valid (priv->game,
                                     priv->moving_cards_origin_slot->id,
                                     slot->id,
-                                    priv->moving_cards->data,
-                                    priv->moving_cards->len);
+                                    priv->moving_cards_slot->cards->data,
+                                    priv->moving_cards_slot->cards->len);
 }
 
 static ArSlot *
@@ -1070,8 +1011,8 @@ drop_moving_cards (AisleriotBoard *board,
     moved = aisleriot_game_drop_cards (priv->game,
                                        priv->moving_cards_origin_slot->id,
                                        hslot->id,
-                                       priv->moving_cards->data,
-                                       priv->moving_cards->len);
+                                       priv->moving_cards_slot->cards->data,
+                                       priv->moving_cards_slot->cards->len);
   }
 
   if (moved) {
@@ -2807,8 +2748,9 @@ aisleriot_board_motion_notify (GtkWidget *widget,
   }
 
   if (priv->click_status == STATUS_IS_DRAG) {
-    ArSlot *slot;
+    ArSlot *slot, *mslot;
     int x, y;
+    cairo_region_t *region;
 
     x = event->x - priv->last_click_x;
     y = event->y - priv->last_click_y;
@@ -2816,7 +2758,13 @@ aisleriot_board_motion_notify (GtkWidget *widget,
     slot = find_drop_target (board, x, y);
     highlight_drop_target (board, slot);
 
-    gdk_window_move (priv->moving_cards_window, x, y);
+    mslot = priv->moving_cards_slot;
+    region = cairo_region_create_rectangle (&mslot->rect);
+    mslot->rect.x = x;
+    mslot->rect.y = y;
+    cairo_region_union_rectangle (region, &mslot->rect);
+    gtk_widget_queue_draw_region (widget, region);
+    cairo_region_destroy (region);
 
     set_cursor (board, AR_CURSOR_CLOSED);
   } else if (priv->click_status == STATUS_MAYBE_DRAG &&
@@ -3130,7 +3078,7 @@ draw_focus:
     /* Check whether this needs to be drawn */
 #ifdef OPTIMISED_EXPOSE
     if (cairo_region_contains_rectangle (region, &priv->focus_rect) == CAIRO_REGION_OVERLAP_OUT)
-      goto expose_done;
+      goto draw_moving_cards;
 #endif /* OPTIMISED_EXPOSE */
 
     if (ar_style_get_interior_focus (priv->style)) {
@@ -3149,11 +3097,61 @@ draw_focus:
                       focus_rect.width, focus_rect.height);
   }
 
+#endif /* ENABLE_KEYNAV */
+
 #ifdef OPTIMISED_EXPOSE
-expose_done:
-#endif
+ draw_moving_cards:
+#endif /* OPTIMISED_EXPOSE */
 
-#endif /* ENABLE_KEYNAV */
+  /* Now draw the moving cards, if any */
+  if (priv->moving_cards_slot != NULL) {
+    ArSlot *hslot = priv->moving_cards_slot;
+    GByteArray *cards = hslot->cards;
+    gpointer *card_images = hslot->card_images->pdata;
+    GdkRectangle card_rect;
+    guint j, n_cards;
+
+    n_cards = cards->len;
+    if (n_cards == 0)
+      goto expose_done;
+
+    card_rect.x = hslot->rect.x;
+    card_rect.y = hslot->rect.y;
+    card_rect.width = priv->card_size.width;
+    card_rect.height = priv->card_size.height;
+
+    if (priv->is_rtl &&
+        hslot->expanded_right) {
+      card_rect.x += hslot->rect.width - priv->card_size.width;
+    }
+
+    for (j = n_cards - hslot->exposed; j < n_cards; ++j) {
+      /* Check whether this card needs to be drawn */
+#ifdef OPTIMISED_EXPOSE
+      if (cairo_region_contains_rectangle (region, &card_rect) == CAIRO_REGION_OVERLAP_OUT)
+        goto next_moving_card;
+#endif /* OPTIMISED_EXPOSE */
+
+      surface = card_images[j];
+      if (surface == NULL)
+        goto next_moving_card;
+
+      pattern = cairo_pattern_create_for_surface (surface);
+      cairo_matrix_init_translate (&matrix, -card_rect.x, -card_rect.y);
+      cairo_pattern_set_matrix (pattern, &matrix);
+      cairo_set_source (cr, pattern);
+      cairo_pattern_destroy (pattern);
+
+      cairo_paint (cr);
+
+    next_moving_card:
+
+      card_rect.x += hslot->pixeldx;
+      card_rect.y += hslot->pixeldy;
+    }
+  }
+
+expose_done:
 
   #if DEBUG_DRAWING
   if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
@@ -3165,7 +3163,7 @@ expose_done:
   cairo_region_destroy (region);
 #endif
 
-  /* Parent class has no expose handler, no need to chain up */
+  /* Parent class has no draw handler, no need to chain up */
   return TRUE;
 }
 
@@ -3191,8 +3189,6 @@ aisleriot_board_init (AisleriotBoard *board)
 
   priv->show_card_id = -1;
 
-  priv->moving_cards = g_byte_array_sized_new (SLOT_CARDS_N_PREALLOC);
-
   priv->card_cache = ar_card_surface_cache_new ();
   // FIXMEchpe connect changed handler
 
@@ -3238,8 +3234,6 @@ aisleriot_board_finalize (GObject *object)
                                         0, 0, NULL, NULL, board);
   g_object_unref (priv->game);
 
-  g_byte_array_free (priv->moving_cards, TRUE);
-
   g_object_unref (priv->card_cache);
 
   G_OBJECT_CLASS (aisleriot_board_parent_class)->finalize (object);


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