[gtk+] GDK W32: Draw snap indicators for AeroSnap



commit b0131616b259449d7478b1552f9370443aa28eab
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Sat Mar 12 16:26:19 2016 +0000

    GDK W32: Draw snap indicators for AeroSnap
    
    Indicator is a bare layered click-through native window,
    painted completely by GDK, including animation.
    
    This commit also isolates some of the more spam-ish debug logging
    under ifdef.
    
    This commit also changes the system metric used for maximal window
    height for the snapping purposes. Turns out, SM_CYMAXTRACK is way
    too large, use SM_CYVIRTUALSCREEN instead.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=763013

 gdk/win32/gdkwindow-win32.c |  527 +++++++++++++++++++++++++++++++++++++++++--
 gdk/win32/gdkwindow-win32.h |   33 +++
 2 files changed, 545 insertions(+), 15 deletions(-)
---
diff --git a/gdk/win32/gdkwindow-win32.c b/gdk/win32/gdkwindow-win32.c
index 78bf49b..1446da9 100644
--- a/gdk/win32/gdkwindow-win32.c
+++ b/gdk/win32/gdkwindow-win32.c
@@ -97,6 +97,34 @@ typedef struct _AeroSnapEdgeRegion AeroSnapEdgeRegion;
  */
 #define AEROSNAP_REGION_TRIGGER_THICKNESS (1)
 
+/* The gap between the snap indicator and the edge of the work area
+ * (in pixels).
+ */
+#define AEROSNAP_INDICATOR_EDGE_GAP (10)
+
+/* Width of the outline of the snap indicator
+ * (in pixels).
+ */
+#define AEROSNAP_INDICATOR_LINE_WIDTH (3.0)
+
+/* Corner radius of the snap indicator.
+ */
+#define AEROSNAP_INDICATOR_CORNER_RADIUS (3.0)
+
+/* The time it takes for snap indicator to expand/shrink
+ * from current window size to future position of the
+ * snapped window (in microseconds).
+ */
+#define AEROSNAP_INDICATOR_ANIMATION_DURATION (200 * 1000)
+
+/* Opacity if the snap indicator. */
+#define AEROSNAP_INDICATOR_OPACITY (0.5)
+
+/* The interval between snap indicator redraws (in milliseconds).
+ * 16 is ~ 1/60 of a second, for ~60 FPS.
+ */
+#define AEROSNAP_INDICATOR_ANIMATION_TICK (16)
+
 static gboolean _gdk_window_get_functions (GdkWindow         *window,
                                            GdkWMFunction     *functions);
 static HDC     _gdk_win32_impl_acquire_dc (GdkWindowImplWin32 *impl);
@@ -2952,6 +2980,7 @@ _gdk_window_get_functions (GdkWindow     *window,
   return (functions_set != NULL);
 }
 
+#if defined(MORE_AEROSNAP_DEBUGGING)
 static void
 log_region (gchar *prefix, AeroSnapEdgeRegion *region)
 {
@@ -2968,6 +2997,7 @@ log_region (gchar *prefix, AeroSnapEdgeRegion *region)
                            region->trigger.x,
                            region->trigger.y));
 }
+#endif
 
 static void
 calculate_aerosnap_regions (GdkW32DragMoveResizeContext *context)
@@ -2975,6 +3005,9 @@ calculate_aerosnap_regions (GdkW32DragMoveResizeContext *context)
   GdkDisplay *display;
   GdkScreen *screen;
   gint n_monitors, monitor, other_monitor;
+#if defined(MORE_AEROSNAP_DEBUGGING)
+  gint i;
+#endif
 
   display = gdk_display_get_default ();
   screen = gdk_display_get_default_screen (display);
@@ -3110,8 +3143,7 @@ calculate_aerosnap_regions (GdkW32DragMoveResizeContext *context)
 #undef _M_LEFT
 #undef _M_RIGHT
 
-  gint i;
-
+#if defined(MORE_AEROSNAP_DEBUGGING)
   for (i = 0; i < context->maximize_regions->len; i++)
     log_region ("maximize", &g_array_index (context->maximize_regions, AeroSnapEdgeRegion, i));
 
@@ -3123,6 +3155,7 @@ calculate_aerosnap_regions (GdkW32DragMoveResizeContext *context)
 
   for (i = 0; i < context->fullup_regions->len; i++)
     log_region ("fullup", &g_array_index (context->fullup_regions, AeroSnapEdgeRegion, i));
+#endif
 }
 
 static void
@@ -3305,7 +3338,7 @@ snap_up (GdkWindow *window,
 
   impl->snap_state = GDK_WIN32_AEROSNAP_STATE_FULLUP;
 
-  maxysize = GetSystemMetrics (SM_CYMAXTRACK);
+  maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN);
   gdk_window_get_position (window, &x, &y);
   width = gdk_window_get_width (window);
 
@@ -3495,27 +3528,440 @@ apply_snap (GdkWindow             *window,
     }
 }
 
+static gboolean
+ensure_snap_indicator_exists (GdkW32DragMoveResizeContext *context)
+{
+  if (context->shape_indicator == NULL)
+    {
+      HWND handle;
+      ATOM klass;
+      klass = RegisterGdkClass (GDK_WINDOW_TOPLEVEL, GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
+
+      handle = CreateWindowExW (WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE,
+                                MAKEINTRESOURCEW (klass),
+                                L"",
+                                WS_POPUP,
+                                0,
+                                0,
+                                0, 0,
+                                NULL,
+                                NULL,
+                                _gdk_app_hmodule,
+                                NULL);
+
+      context->shape_indicator = handle;
+    }
+
+  return context->shape_indicator != NULL;
+}
+
+static gboolean
+ensure_snap_indicator_surface (GdkW32DragMoveResizeContext *context,
+                          gint                         width,
+                          gint                         height)
+{
+  if (context->indicator_surface != NULL &&
+      (context->indicator_surface_width < width ||
+       context->indicator_surface_height < height))
+    {
+      cairo_surface_destroy (context->indicator_surface);
+      context->indicator_surface = NULL;
+    }
+
+  if (context->indicator_surface == NULL)
+    context->indicator_surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height);
+
+  if (cairo_surface_status (context->indicator_surface) != CAIRO_STATUS_SUCCESS)
+    {
+      cairo_surface_destroy (context->indicator_surface);
+      context->indicator_surface = NULL;
+
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+/* Indicator is drawn with some inward offset, so that it does
+ * not hug screen edges.
+ */
+static void
+adjust_indicator_rectangle (GdkRectangle *rect,
+                            gboolean      inward)
+{
+  gdouble inverter;
+  gint gap;
+#if defined(MORE_AEROSNAP_DEBUGGING)
+  GdkRectangle cache = *rect;
+#endif
+
+  if (inward)
+    inverter = 1.0;
+  else
+    inverter = -1.0;
+
+  /* TODO: Adjust for HiDPI? */
+  gap = AEROSNAP_INDICATOR_EDGE_GAP;
+
+  rect->x += gap * inverter;
+  rect->y += gap * inverter;
+  rect->width -= gap * 2 * inverter;
+  rect->height -= gap * 2 * inverter;
+
+#if defined(MORE_AEROSNAP_DEBUGGING)
+  GDK_NOTE (MISC, g_print ("Adjusted %d x %d @ %d : %d -> %d x %d @ %d : %d\n",
+                           cache.width, cache.height, cache.x, cache.y,
+                           rect->width, rect->height, rect->x, rect->y));
+#endif
+}
+
+static void
+rounded_rectangle (cairo_t  *cr,
+                   gint      x,
+                   gint      y,
+                   gint      width,
+                   gint      height,
+                   gdouble   radius,
+                   gdouble   line_width,
+                   GdkRGBA  *fill,
+                   GdkRGBA  *outline)
+{
+  gdouble degrees = M_PI / 180.0;
+
+  if (fill == NULL && outline == NULL)
+    return;
+
+  cairo_save (cr);
+  cairo_new_sub_path (cr);
+  cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees);
+  cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees);
+  cairo_arc (cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees);
+  cairo_arc (cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
+  cairo_close_path (cr);
+
+  if (fill)
+    {
+      cairo_set_source_rgba (cr, fill->red, fill->green, fill->blue, fill->alpha);
+
+      if (outline)
+        cairo_fill_preserve (cr);
+      else
+        cairo_fill (cr);
+    }
+
+  if (outline)
+    {
+      cairo_set_source_rgba (cr, outline->red, outline->green, outline->blue, outline->alpha);
+      cairo_set_line_width (cr, line_width);
+      cairo_stroke (cr);
+    }
+
+  cairo_restore (cr);
+}
+
+/* Translates linear animation scale into some kind of curve */
+static gdouble
+curve (gdouble val)
+{
+  /* TODO: try different curves. For now it's just linear */
+  return val;
+}
+
+static gboolean
+draw_indicator (GdkW32DragMoveResizeContext *context,
+                gint64                       timestamp)
+{
+  cairo_t *cr;
+  GdkRGBA outline = {0, 0, 1.0, 1.0};
+  GdkRGBA fill = {0, 0, 1.0, 0.8};
+  GdkRectangle current_rect;
+  gint64 current_time = g_get_monotonic_time ();
+  gdouble animation_progress;
+  gboolean last_draw;
+  gdouble line_width;
+  gdouble corner_radius;
+  gint64 animation_duration;
+
+  /* TODO: Adjust for HiDPI? */
+  line_width = AEROSNAP_INDICATOR_LINE_WIDTH;
+  /* TODO: Adjust for HiDPI? */
+  corner_radius = AEROSNAP_INDICATOR_CORNER_RADIUS;
+  animation_duration = AEROSNAP_INDICATOR_ANIMATION_DURATION;
+  last_draw = FALSE;
+
+  if (timestamp == 0 &&
+      current_time - context->indicator_start_time > animation_duration)
+    {
+      timestamp = context->indicator_start_time + animation_duration;
+      last_draw = TRUE;
+    }
+
+  if (timestamp != 0)
+    current_time = timestamp;
+
+  animation_progress = (gdouble) (current_time - context->indicator_start_time) / animation_duration;
+
+  if (animation_progress > 1.0)
+    animation_progress = 1.0;
+
+  if (animation_progress < 0)
+    animation_progress = 0;
+
+  animation_progress = curve (animation_progress);
+
+  current_rect = context->indicator_start;
+  current_rect.x += (context->indicator_target.x - context->indicator_start.x) * animation_progress;
+  current_rect.y += (context->indicator_target.y - context->indicator_start.y) * animation_progress;
+  current_rect.width += (context->indicator_target.width - context->indicator_start.width) * 
animation_progress;
+  current_rect.height += (context->indicator_target.height - context->indicator_start.height) * 
animation_progress;
+
+  if (context->op == GDK_WIN32_DRAGOP_RESIZE && last_draw)
+    {
+      switch (context->edge)
+        {
+        case GDK_WINDOW_EDGE_NORTH_WEST:
+          current_rect.x = context->indicator_target.x + (context->indicator_target.width - 
current_rect.width);
+          current_rect.y = context->indicator_target.y + (context->indicator_target.height - 
current_rect.height);
+          break;
+        case GDK_WINDOW_EDGE_NORTH:
+          current_rect.y = context->indicator_target.y + (context->indicator_target.height - 
current_rect.height);
+          break;
+        case GDK_WINDOW_EDGE_WEST:
+          current_rect.x = context->indicator_target.x + (context->indicator_target.width - 
current_rect.width);
+          break;
+        case GDK_WINDOW_EDGE_SOUTH_WEST:
+          current_rect.x = context->indicator_target.x + (context->indicator_target.width - 
current_rect.width);
+          current_rect.y = context->indicator_target.y;
+          break;
+        case GDK_WINDOW_EDGE_NORTH_EAST:
+          current_rect.x = context->indicator_target.x;
+          current_rect.y = context->indicator_target.y + (context->indicator_target.height - 
current_rect.height);
+          break;
+        case GDK_WINDOW_EDGE_SOUTH_EAST:
+          current_rect.x = context->indicator_target.x;
+          current_rect.y = context->indicator_target.y;
+          break;
+        case GDK_WINDOW_EDGE_SOUTH:
+          current_rect.y = context->indicator_target.y;
+          break;
+        case GDK_WINDOW_EDGE_EAST:
+          current_rect.x = context->indicator_target.x;
+          break;
+        }
+    }
+
+  cr = cairo_create (context->indicator_surface);
+  rounded_rectangle (cr,
+                     current_rect.x - context->indicator_window_rect.x,
+                     current_rect.y - context->indicator_window_rect.y,
+                     current_rect.width, current_rect.height,
+                     corner_radius,
+                     line_width,
+                     &fill, &outline);
+  cairo_destroy (cr);
+
+#if defined(MORE_AEROSNAP_DEBUGGING)
+  GDK_NOTE (MISC, g_print ("Indicator is %d x %d @ %d : %d; current time is %" G_GINT64_FORMAT "\n",
+                           current_rect.width, current_rect.height,
+                           current_rect.x - context->indicator_window_rect.x,
+                           current_rect.y - context->indicator_window_rect.y,
+                           current_time));
+#endif
+
+  return last_draw;
+}
+
+static gboolean
+redraw_indicator (gpointer user_data)
+{
+  GdkW32DragMoveResizeContext *context = user_data;
+  POINT window_position;
+  SIZE window_size;
+  BLENDFUNCTION blender;
+  HDC hdc;
+  POINT source_point = { 0, 0 };
+  gboolean last_draw;
+  gdouble indicator_opacity;
+
+  indicator_opacity = AEROSNAP_INDICATOR_OPACITY;
+
+  if (GDK_WINDOW_DESTROYED (context->window) ||
+      !ensure_snap_indicator_exists (context) ||
+      !ensure_snap_indicator_surface (context,
+                                 context->indicator_window_rect.width,
+                                 context->indicator_window_rect.height))
+    {
+      context->timer = 0;
+      return G_SOURCE_REMOVE;
+    }
+
+  last_draw = draw_indicator (context, context->draw_timestamp);
+
+  window_position.x = context->indicator_window_rect.x - _gdk_offset_x;
+  window_position.y = context->indicator_window_rect.y - _gdk_offset_y;
+  window_size.cx = context->indicator_window_rect.width;
+  window_size.cy = context->indicator_window_rect.height;
+
+  blender.BlendOp = AC_SRC_OVER;
+  blender.BlendFlags = 0;
+  blender.AlphaFormat = AC_SRC_ALPHA;
+  blender.SourceConstantAlpha = 255 * indicator_opacity;
+
+  hdc = cairo_win32_surface_get_dc (context->indicator_surface);
+
+  API_CALL (SetWindowPos, (context->shape_indicator,
+                           GDK_WINDOW_HWND (context->window),
+                           0, 0, 0, 0,
+                           SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_SHOWWINDOW | SWP_NOACTIVATE));
+
+#if defined(MORE_AEROSNAP_DEBUGGING)
+  GDK_NOTE (MISC, g_print ("Indicator window position is %ld x %ld @ %ld : %ld\n",
+                           window_size.cx, window_size.cy,
+                           window_position.x, window_position.y));
+#endif
+
+  API_CALL (UpdateLayeredWindow, (context->shape_indicator, NULL,
+                                  &window_position, &window_size,
+                                  hdc, &source_point,
+                                  0, &blender, ULW_ALPHA));
+
+  if (last_draw)
+    context->timer = 0;
+
+  return last_draw ? G_SOURCE_REMOVE : G_SOURCE_CONTINUE;
+}
+
+static GdkRectangle
+unity_of_rects (GdkRectangle a,
+                GdkRectangle b)
+{
+  GdkRectangle u = b;
+
+  if (a.x < u.x)
+    {
+      u.width += u.x - a.x;
+      u.x = a.x;
+    }
+
+  if (a.y < u.y)
+    {
+      u.height += (u.y - a.y);
+      u.y = a.y;
+    }
+
+  if (a.x + a.width > u.x + u.width)
+    u.width += (a.x + a.width) - (u.x + u.width);
+
+  if (a.y + a.height > u.y + u.height)
+    u.height += (a.y + a.height) - (u.y + u.height);
+
+#if defined(MORE_AEROSNAP_DEBUGGING)
+  GDK_NOTE (MISC, g_print ("Unified 2 rects into %d x %d @ %d : %d\n",
+                           u.width, u.height, u.x, u.y));
+#endif
+
+  return u;
+}
+
 static void
 start_indicator_drawing (GdkW32DragMoveResizeContext *context,
                          GdkRectangle                 from,
                          GdkRectangle                 to)
 {
-  (void) context;
-  (void) from;
-  (void) to;
+  GdkRectangle to_adjusted, from_adjusted, from_or_to;
+  gint64 indicator_animation_tick = AEROSNAP_INDICATOR_ANIMATION_TICK;
 
   GDK_NOTE (MISC, g_print ("Start drawing snap indicator %d x %d @ %d : %d -> %d x %d @ %d : %d\n",
                            from.width, from.height, from.x, from.y, to.width, to.height, to.x, to.y));
+
+  if (GDK_WINDOW_DESTROYED (context->window))
+    return;
+
+  if (!ensure_snap_indicator_exists (context))
+    return;
+
+  from_or_to = unity_of_rects (from, to);
+
+  if (!ensure_snap_indicator_surface (context, from_or_to.width, from_or_to.height))
+    return;
+
+  to_adjusted = to;
+  adjust_indicator_rectangle (&to_adjusted, TRUE);
+  from_adjusted = from;
+  adjust_indicator_rectangle (&from_adjusted, TRUE);
+
+  context->draw_timestamp = 0;
+  context->indicator_start = from_adjusted;
+  context->indicator_target = to_adjusted;
+  context->indicator_window_rect = from_or_to;
+  context->indicator_start_time = g_get_monotonic_time ();
+
+  if (context->timer)
+    {
+      g_source_remove (context->timer);
+      context->timer = 0;
+    }
+
+  context->timer = g_timeout_add_full (G_PRIORITY_DEFAULT,
+                                       indicator_animation_tick,
+                                       redraw_indicator,
+                                       context,
+                                       NULL);
 }
 
 static void
 update_fullup_indicator (GdkWindow                   *window,
                          GdkW32DragMoveResizeContext *context)
 {
-  (void) window;
-  (void) context;
+  SHORT maxysize;
+  GdkRectangle from, to;
+  GdkRectangle to_adjusted, from_adjusted, from_or_to;
 
   GDK_NOTE (MISC, g_print ("Update fullup indicator\n"));
+
+  if (GDK_WINDOW_DESTROYED (context->window))
+    return;
+
+  if (context->shape_indicator == NULL)
+    return;
+
+  maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN);
+  gdk_window_get_position (window, &to.x, &to.y);
+  to.width = gdk_window_get_width (window);
+  to.height = gdk_window_get_height (window);
+
+  to.y = 0;
+  to.height = maxysize;
+  from = context->indicator_target;
+
+  if (context->timer == 0)
+    {
+      from_adjusted = from;
+      adjust_indicator_rectangle (&from_adjusted, FALSE);
+
+      GDK_NOTE (MISC, g_print ("Restart fullup animation from %d x %d @ %d : %d -> %d x %d @ %d x %d\n",
+                               context->indicator_target.width, context->indicator_target.height,
+                               context->indicator_target.x, context->indicator_target.y,
+                               to.width, to.height, to.x, to.y));
+      start_indicator_drawing (context, from_adjusted, to);
+
+      return;
+    }
+
+  from_or_to = unity_of_rects (from, to);
+
+  to_adjusted = to;
+  adjust_indicator_rectangle (&to_adjusted, TRUE);
+
+  GDK_NOTE (MISC, g_print ("Retarget fullup animation %d x %d @ %d : %d -> %d x %d @ %d x %d\n",
+                           context->indicator_target.width, context->indicator_target.height,
+                           context->indicator_target.x, context->indicator_target.y,
+                           to_adjusted.width, to_adjusted.height, to_adjusted.x, to_adjusted.y));
+
+  context->indicator_target = to_adjusted;
+  context->indicator_window_rect = from_or_to;
+
+  ensure_snap_indicator_surface (context, from_or_to.width, from_or_to.height);
 }
 
 static void
@@ -3535,7 +3981,7 @@ start_indicator (GdkWindow                   *window,
   monitor = gdk_screen_get_monitor_at_point (screen, x, y);
   gdk_screen_get_monitor_workarea (screen, monitor, &workarea);
 
-  maxysize = GetSystemMetrics (SM_CYMAXTRACK);
+  maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN);
   gdk_window_get_position (window, &start_size.x, &start_size.y);
   start_size.width = gdk_window_get_width (window);
   start_size.height = gdk_window_get_height (window);
@@ -3565,6 +4011,7 @@ start_indicator (GdkWindow                   *window,
       end_size.height = workarea.height;
       break;
     case GDK_WIN32_AEROSNAP_STATE_FULLUP:
+      end_size.y = 0;
       end_size.height = maxysize;
       break;
     }
@@ -3576,10 +4023,19 @@ static void
 stop_indicator (GdkWindow                   *window,
                 GdkW32DragMoveResizeContext *context)
 {
-  (void) window;
-  (void) context;
-
   GDK_NOTE (MISC, g_print ("Stop drawing snap indicator\n"));
+
+  if (context->timer)
+    {
+      g_source_remove (context->timer);
+      context->timer = 0;
+    }
+
+  API_CALL (SetWindowPos, (context->shape_indicator,
+                           SWP_NOZORDER_SPECIFIED,
+                           0, 0, 0, 0,
+                           SWP_NOZORDER | SWP_NOMOVE |
+                           SWP_NOSIZE | SWP_NOREDRAW | SWP_HIDEWINDOW | SWP_NOACTIVATE));
 }
 
 static gint
@@ -3610,6 +4066,23 @@ handle_aerosnap_move_resize (GdkWindow                   *window,
   gint halfleft = 0;
   gint halfright = 0;
   gint fullup = 0;
+  gboolean fullup_edge = FALSE;
+
+  if (context->op == GDK_WIN32_DRAGOP_RESIZE)
+    switch (context->edge)
+      {
+      case GDK_WINDOW_EDGE_NORTH_WEST:
+      case GDK_WINDOW_EDGE_NORTH_EAST:
+      case GDK_WINDOW_EDGE_WEST:
+      case GDK_WINDOW_EDGE_EAST:
+      case GDK_WINDOW_EDGE_SOUTH_WEST:
+      case GDK_WINDOW_EDGE_SOUTH_EAST:
+        break;
+      case GDK_WINDOW_EDGE_SOUTH:
+      case GDK_WINDOW_EDGE_NORTH:
+        fullup_edge = TRUE;
+        break;
+      }
 
   for (i = 0; i < context->maximize_regions->len && maximize == 0; i++)
     {
@@ -3635,8 +4108,10 @@ handle_aerosnap_move_resize (GdkWindow                   *window,
       fullup = point_in_aerosnap_region (x, y, reg);
     }
 
+#if defined(MORE_AEROSNAP_DEBUGGING)
   GDK_NOTE (MISC, g_print ("AeroSnap: point %d : %d - max: %d, left %d, right %d, up %d\n",
                            x, y, maximize, halfleft, halfright, fullup));
+#endif
 
   if (!context->revealed)
     {
@@ -3658,7 +4133,7 @@ handle_aerosnap_move_resize (GdkWindow                   *window,
           context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT;
           start_indicator (window, context, x, y, context->current_snap);
         }
-      else if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup == 2)
+      else if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup == 2 && fullup_edge)
         {
           context->revealed = TRUE;
           context->current_snap = GDK_WIN32_AEROSNAP_STATE_FULLUP;
@@ -3738,7 +4213,7 @@ handle_aerosnap_move_resize (GdkWindow                   *window,
         }
       break;
     case GDK_WIN32_AEROSNAP_STATE_FULLUP:
-      if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup > 0)
+      if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup > 0 && fullup_edge)
         {
           update_fullup_indicator (window, context);
           break;
@@ -4018,6 +4493,7 @@ setup_drag_move_resize_context (GdkWindow                   *window,
                    context->cursor,
                    timestamp);
 
+  context->window = g_object_ref (window);
   context->op = op;
   context->edge = edge;
   context->device = device;
@@ -4064,7 +4540,28 @@ gdk_win32_window_end_move_resize_drag (GdkWindow *window)
   g_clear_object (&context->cursor);
 
   context->revealed = FALSE;
-  g_clear_object (&context->shape_indicator);
+
+  if (context->timer)
+    {
+      g_source_remove (context->timer);
+      context->timer = 0;
+    }
+
+  g_clear_object (&context->window);
+
+  if (context->indicator_surface)
+    {
+      cairo_surface_destroy (context->indicator_surface);
+      context->indicator_surface = NULL;
+    }
+
+  if (context->shape_indicator)
+    {
+      stop_indicator (window, context);
+      DestroyWindow (context->shape_indicator);
+      context->shape_indicator = NULL;
+    }
+
   g_clear_pointer (&context->halfleft_regions, g_array_unref);
   g_clear_pointer (&context->halfright_regions, g_array_unref);
   g_clear_pointer (&context->maximize_regions, g_array_unref);
diff --git a/gdk/win32/gdkwindow-win32.h b/gdk/win32/gdkwindow-win32.h
index 8c4df17..7eca54f 100644
--- a/gdk/win32/gdkwindow-win32.h
+++ b/gdk/win32/gdkwindow-win32.h
@@ -98,6 +98,9 @@ typedef enum _GdkW32WindowDragOp GdkW32WindowDragOp;
 
 struct _GdkW32DragMoveResizeContext
 {
+  /* The window that is being moved/resized */
+  GdkWindow         *window;
+
   /* The kind of drag-operation going on. */
   GdkW32WindowDragOp op;
 
@@ -141,9 +144,39 @@ struct _GdkW32DragMoveResizeContext
   /* This window looks like an outline and is drawn under the window
    * that is being dragged. It indicates the shape the dragged window
    * will take if released at a particular point.
+   * Indicator window size always matches the target indicator shape,
+   * the the actual indicator drawn on it might not, depending on
+   * how much time elapsed since the animation started.
    */
   HWND               shape_indicator;
 
+  /* Used to draw the indicator */
+  cairo_surface_t   *indicator_surface;
+  gint               indicator_surface_width;
+  gint               indicator_surface_height;
+
+  /* Size/position of shape_indicator */
+  GdkRectangle       indicator_window_rect;
+
+  /* Indicator will animate to occupy this rectangle */
+  GdkRectangle       indicator_target;
+
+  /* Indicator will start animating from this rectangle */
+  GdkRectangle       indicator_start;
+
+  /* Timestamp of the animation start */
+  gint64             indicator_start_time;
+
+  /* Timer that drives the animation */
+  guint              timer;
+
+  /* A special timestamp, if we want to draw not how
+   * the animation should look *now*, but how it should
+   * look at arbitrary moment of time.
+   * Set to 0 to tell GDK to use current time.
+   */
+  gint64             draw_timestamp;
+
   /* Indicates that a transformation was revealed:
    *
    * For drag-resize: If it's FALSE,


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