macos: Support dragging from GdkMacosWindow
authorArjan Molenaar <gaphor@gmail.com>
Mon, 9 Jan 2023 22:01:12 +0000 (23:01 +0100)
committerArjan Molenaar <gaphor@gmail.com>
Sun, 15 Jan 2023 09:01:56 +0000 (10:01 +0100)
The handling is done similar to drag targets.
Note that dragging is a modal action on macos: no events
are sent to the main window. This could cause trouble when
we finish the drag, and not finish the gesture in GTK.

gdk/macos/GdkMacosWindow.c
gdk/macos/GdkMacosWindow.h
gdk/macos/gdkmacosdisplay-private.h
gdk/macos/gdkmacosdisplay.c
gdk/macos/gdkmacosdrag-private.h
gdk/macos/gdkmacosdrag.c
gdk/macos/gdkmacossurface.c

index 7680e6d8b013953042d3462d2c4be04fa76b39dc..79e2ec8001bad323d525dcef4ce332fd264da1ea 100644 (file)
@@ -28,6 +28,7 @@
 #import "GdkMacosWindow.h"
 
 #include "gdkmacosdisplay-private.h"
+#include "gdkmacosdrag-private.h"
 #include "gdkmacosdrop-private.h"
 #include "gdkmacosmonitor-private.h"
 #include "gdkmacospasteboard-private.h"
@@ -668,7 +669,56 @@ typedef NSString *CALayerContentsGravity;
 }
 
 // NSDraggingSource protocol
-// ...
+
+- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
+{
+  NSInteger sequence_number = [session draggingSequenceNumber];
+  GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface));
+  GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number);
+  GdkModifierType state = _gdk_macos_display_get_current_keyboard_modifiers (GDK_MACOS_DISPLAY (display));
+
+  _gdk_macos_drag_set_actions (GDK_MACOS_DRAG (drag), state);
+
+  return _gdk_macos_drag_operation (GDK_MACOS_DRAG (drag));
+}
+
+- (void)draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint
+{
+  NSInteger sequence_number = [session draggingSequenceNumber];
+  GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface));
+  GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number);
+  int x, y;
+
+  _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y);
+  _gdk_macos_drag_set_start_position (GDK_MACOS_DRAG (drag), x, y);
+  _gdk_macos_drag_surface_move (GDK_MACOS_DRAG (drag), x, y);
+}
+
+- (void)draggingSession:(NSDraggingSession *)session movedToPoint:(NSPoint)screenPoint
+{
+  NSInteger sequence_number = [session draggingSequenceNumber];
+  GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface));
+  GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number);
+  int x, y;
+
+  _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y);
+  _gdk_macos_drag_surface_move (GDK_MACOS_DRAG (drag), x, y);
+}
+
+- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
+{
+  NSInteger sequence_number = [session draggingSequenceNumber];
+  GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface));
+  GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number);
+
+  if (gdk_drag_get_selected_action (drag) != 0)
+    g_signal_emit_by_name (drag, "drop-performed");
+  else
+    gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
+
+  _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (display), [session draggingSequenceNumber], NULL);
+}
+
 // end
 
 -(void)setStyleMask:(NSWindowStyleMask)styleMask
index 1cf9ec805c0c22f3afd2fb1c7f12800a40d20189..66fcd45717a1b8e1e068344cadc9ed3a8972c3e6 100644 (file)
@@ -32,7 +32,7 @@
 
 #define GDK_IS_MACOS_WINDOW(obj) ([obj isKindOfClass:[GdkMacosWindow class]])
 
-@interface GdkMacosWindow : NSWindow {
+@interface GdkMacosWindow : NSWindow <NSDraggingSource, NSDraggingDestination> {
   GdkMacosSurface *gdk_surface;
 
   BOOL             inMove;
index 83ae435e49db2c1f0131d31377c194fdd0874da9..122d004cec6ee7f9d769b9858755edd724574ced 100644 (file)
@@ -161,6 +161,7 @@ void             _gdk_macos_display_warp_pointer                   (GdkMacosDisp
                                                                     int              x,
                                                                     int              y);
 NSEvent         *_gdk_macos_display_get_nsevent                    (GdkEvent        *event);
+NSEvent         *_gdk_macos_display_get_last_nsevent               (void);
 GdkDrag         *_gdk_macos_display_find_drag                      (GdkMacosDisplay *self,
                                                                     NSInteger        sequence_number);
 GdkDrop         *_gdk_macos_display_find_drop                      (GdkMacosDisplay *self,
index 0e5a9b8eb62980dd5ad213639306e4dcb594e14a..b9869fae7cccf5eae32535948117ee85067a9258 100644 (file)
@@ -1024,6 +1024,16 @@ _gdk_macos_display_get_nsevent (GdkEvent *event)
   return NULL;
 }
 
+NSEvent *
+_gdk_macos_display_get_last_nsevent ()
+{
+  const GdkToNSEventMap *map = g_queue_peek_tail (&event_map);
+  if (map)
+    return map->nsevent;
+
+  return NULL;
+}
+
 GdkDrag *
 _gdk_macos_display_find_drag (GdkMacosDisplay *self,
                               NSInteger        sequence_number)
index 98075f27ef51dbc41f63fa913336570f6aba17b4..fbc525d2477e0f75da3530e9461fe13f3ab8c703 100644 (file)
@@ -62,8 +62,20 @@ struct _GdkMacosDragClass
   GdkDragClass parent_class;
 };
 
-GType    gdk_macos_drag_get_type (void) G_GNUC_CONST;
-gboolean _gdk_macos_drag_begin   (GdkMacosDrag *self);
+GType           gdk_macos_drag_get_type     (void) G_GNUC_CONST;
+gboolean        _gdk_macos_drag_begin       (GdkMacosDrag       *self,
+                                             GdkContentProvider *content,
+                                             GdkMacosWindow     *window);
+NSDragOperation _gdk_macos_drag_operation   (GdkMacosDrag       *self);
+void     _gdk_macos_drag_surface_move       (GdkMacosDrag   *self,
+                                             int             x_root,
+                                             int             y_root);
+void     _gdk_macos_drag_set_start_position (GdkMacosDrag   *self,
+                                             int             start_x,
+                                             int             start_y);
+void     _gdk_macos_drag_set_actions        (GdkMacosDrag   *self,
+                                             GdkModifierType mods);
+
 
 G_END_DECLS
 
index 10a756998db9a28eeb896dca52b4e8c15bbab959..50c1ac814df996303093f6b578d549cd6f9569eb 100644 (file)
@@ -25,6 +25,7 @@
 #include "gdkmacoscursor-private.h"
 #include "gdkmacosdisplay-private.h"
 #include "gdkmacosdragsurface-private.h"
+#include "gdkmacospasteboard-private.h"
 
 #include "gdk/gdkdeviceprivate.h"
 #include "gdk/gdkeventsprivate.h"
@@ -624,11 +625,101 @@ gdk_macos_drag_init (GdkMacosDrag *self)
 }
 
 gboolean
-_gdk_macos_drag_begin (GdkMacosDrag *self)
+_gdk_macos_drag_begin (GdkMacosDrag       *self,
+                       GdkContentProvider *content,
+                       GdkMacosWindow     *window)
 {
+  NSArray<NSDraggingItem *> *items;
+  NSDraggingSession *session;
+  NSPasteboardItem *item;
+  NSEvent *nsevent;
+
   g_return_val_if_fail (GDK_IS_MACOS_DRAG (self), FALSE);
+  g_return_val_if_fail (GDK_IS_MACOS_WINDOW (window), FALSE);
+
+  GDK_BEGIN_MACOS_ALLOC_POOL;
+
+  item = [[GdkMacosPasteboardItem alloc] initForDrag:GDK_DRAG (self) withContentProvider:content];
+  items = [NSArray arrayWithObject:item];
+  nsevent = _gdk_macos_display_get_last_nsevent ();
+
+  session = [[window contentView] beginDraggingSessionWithItems:items
+                                                          event:nsevent
+                                                         source:window];
+
+  GDK_END_MACOS_ALLOC_POOL;
+
+  _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (gdk_drag_get_display (GDK_DRAG (self))),
+                               [session draggingSequenceNumber],
+                               GDK_DRAG (self));
+
+  return TRUE;
+}
+
+NSDragOperation
+_gdk_macos_drag_operation (GdkMacosDrag *self)
+{
+  NSDragOperation operation = NSDragOperationNone;
+  GdkDragAction actions;
 
-  _gdk_macos_surface_show (GDK_MACOS_SURFACE (self->drag_surface));
+  g_return_val_if_fail (GDK_IS_MACOS_DRAG (self), NSDragOperationNone);
+
+  actions = gdk_drag_get_actions (GDK_DRAG (self));
+
+  if (actions & GDK_ACTION_LINK)
+    operation |= NSDragOperationLink;
+
+  if (actions & GDK_ACTION_MOVE)
+    operation |= NSDragOperationMove;
+
+  if (actions & GDK_ACTION_COPY)
+    operation |= NSDragOperationCopy;
+
+  return operation;
+}
+
+void
+_gdk_macos_drag_surface_move (GdkMacosDrag *self,
+                              int           x_root,
+                              int           y_root)
+{
+  g_return_if_fail (GDK_IS_MACOS_DRAG (self));
+
+  self->last_x = x_root;
+  self->last_y = y_root;
+
+  if (GDK_IS_MACOS_SURFACE (self->drag_surface))
+    _gdk_macos_surface_move (GDK_MACOS_SURFACE (self->drag_surface),
+                             x_root - self->hot_x,
+                             y_root - self->hot_y);
+}
+
+void
+_gdk_macos_drag_set_start_position (GdkMacosDrag *self,
+                                    int           start_x,
+                                    int           start_y)
+{
+  g_return_if_fail (GDK_IS_MACOS_DRAG (self));
+
+  self->start_x = start_x;
+  self->start_y = start_y;
+}
+
+void
+_gdk_macos_drag_set_actions (GdkMacosDrag    *self,
+                             GdkModifierType  mods)
+{
+  GdkDragAction suggested_action;
+  GdkDragAction possible_actions;
+
+  g_assert (GDK_IS_MACOS_DRAG (self));
+
+  gdk_drag_get_current_actions (mods,
+                                GDK_BUTTON_PRIMARY,
+                                gdk_drag_get_actions (GDK_DRAG (self)),
+                                &suggested_action,
+                                &possible_actions);
 
-  return drag_grab (self);
+  gdk_drag_set_selected_action (GDK_DRAG (self), suggested_action);
+  gdk_drag_set_actions (GDK_DRAG (self), possible_actions);
 }
index 37400d18927081511260845f3c84199a98bb2c7e..a002dc7db325a774ed0e1aa443c8b0c5e4c36247 100644 (file)
@@ -446,7 +446,7 @@ gdk_macos_surface_drag_begin (GdkSurface         *surface,
                                 gdk_drag_get_selected_action (GDK_DRAG (drag)));
   gdk_drag_set_cursor (GDK_DRAG (drag), cursor);
 
-  if (!_gdk_macos_drag_begin (drag))
+  if (!_gdk_macos_drag_begin (drag, content, self->window))
     {
       g_object_unref (drag);
       return NULL;