diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index e5051dafde00..8185ca608cbb 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -5811,6 +5811,9 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) if (mDragService) { // set the dragend point from the current mouse location nsDragService* dragService = static_cast(mDragService); + FlipCocoaScreenCoordinate(aPoint); + dragService->SetDragEndPoint(gfx::IntPoint::Round(aPoint.x, aPoint.y)); + NSPoint pnt = [NSEvent mouseLocation]; FlipCocoaScreenCoordinate(pnt); dragService->SetDragEndPoint(gfx::IntPoint::Round(pnt.x, pnt.y)); @@ -5856,11 +5859,8 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) } if (dragService) { - NSPoint pnt = [NSEvent mouseLocation]; - FlipCocoaScreenCoordinate(pnt); - - LayoutDeviceIntPoint devPoint = mGeckoChild->CocoaPointsToDevPixels(pnt); - dragService->DragMoved(devPoint.x, devPoint.y); + nsDragService* ds = static_cast(dragService.get()); + ds->DragMovedWithView(aSession, aPoint); } NS_OBJC_END_TRY_ABORT_BLOCK; diff --git a/widget/cocoa/nsDragService.h b/widget/cocoa/nsDragService.h index 88cf7581917e..404df4057f2e 100644 --- a/widget/cocoa/nsDragService.h +++ b/widget/cocoa/nsDragService.h @@ -28,20 +28,34 @@ public: uint32_t aActionType); // nsIDragService NS_IMETHOD EndDragSession(bool aDoneDrag); + NS_IMETHOD UpdateDragImage(nsIDOMNode* aImage, int32_t aImageX, int32_t aImageY); // nsIDragSession NS_IMETHOD GetData(nsITransferable * aTransferable, uint32_t aItemIndex); NS_IMETHOD IsDataFlavorSupported(const char *aDataFlavor, bool *_retval); NS_IMETHOD GetNumDropItems(uint32_t * aNumItems); + void DragMovedWithView(NSDraggingSession* aSession, NSPoint aPoint); + protected: virtual ~nsDragService(); private: + // Creates and returns the drag image for a drag. aImagePoint will be set to + // the origin of the drag relative to mNativeDragView. NSImage* ConstructDragImage(nsIDOMNode* aDOMNode, - mozilla::LayoutDeviceIntRect* aDragRect, - nsIScriptableRegion* aRegion); + nsIScriptableRegion* aRegion, + NSPoint* aImagePoint); + + // Creates and returns the drag image for a drag. aPoint should be the origin + // of the drag, for example the mouse coordinate of the mousedown event. + // aDragRect will be set the area of the drag relative to this. + NSImage* ConstructDragImage(nsIDOMNode* aDOMNode, + nsIScriptableRegion* aRegion, + mozilla::CSSIntPoint aPoint, + mozilla::LayoutDeviceIntRect* aDragRect); + bool IsValidType(NSString* availableType, bool allowFileURL); NSString* GetStringForType(NSPasteboardItem* item, const NSString* type, bool allowFileURL = false); @@ -51,6 +65,8 @@ private: nsCOMPtr mDataItems; // only valid for a drag started within gecko ChildView* mNativeDragView; NSEvent* mNativeDragEvent; + + bool mDragImageChanged; }; #endif // nsDragService_h_ diff --git a/widget/cocoa/nsDragService.mm b/widget/cocoa/nsDragService.mm index 0439891c8d11..0d5aed3f9be9 100644 --- a/widget/cocoa/nsDragService.mm +++ b/widget/cocoa/nsDragService.mm @@ -57,10 +57,8 @@ NSString* const kUTTypeURLName = @"public.url-name"; NSString* const kCustomTypesPboardType = @"org.mozilla.custom-clipdata"; nsDragService::nsDragService() + : mNativeDragView(nil), mNativeDragEvent(nil), mDragImageChanged(false) { - mNativeDragView = nil; - mNativeDragEvent = nil; - EnsureLogInitialized(); } @@ -70,22 +68,65 @@ nsDragService::~nsDragService() NSImage* nsDragService::ConstructDragImage(nsIDOMNode* aDOMNode, - LayoutDeviceIntRect* aDragRect, - nsIScriptableRegion* aRegion) + nsIScriptableRegion* aRegion, + NSPoint* aDragPoint) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView); + CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mNativeDragView); + + LayoutDeviceIntRect dragRect(0, 0, 20, 20); + NSImage* image = ConstructDragImage(mSourceNode, aRegion, mScreenPosition, &dragRect); + if (!image) { + // if no image was returned, just draw a rectangle + NSSize size; + size.width = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.width, scaleFactor); + size.height = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.height, scaleFactor); + image = [[NSImage alloc] initWithSize:size]; + [image lockFocus]; + [[NSColor grayColor] set]; + NSBezierPath* path = [NSBezierPath bezierPath]; + [path setLineWidth:2.0]; + [path moveToPoint:NSMakePoint(0, 0)]; + [path lineToPoint:NSMakePoint(0, size.height)]; + [path lineToPoint:NSMakePoint(size.width, size.height)]; + [path lineToPoint:NSMakePoint(size.width, 0)]; + [path lineToPoint:NSMakePoint(0, 0)]; + [path stroke]; + [image unlockFocus]; + } + + LayoutDeviceIntPoint pt(dragRect.x, dragRect.YMost()); + NSPoint point = nsCocoaUtils::DevPixelsToCocoaPoints(pt, scaleFactor); + point.y = nsCocoaUtils::FlippedScreenY(point.y); + + point = nsCocoaUtils::ConvertPointFromScreen([mNativeDragView window], point); + *aDragPoint = [mNativeDragView convertPoint:point fromView:nil]; + + return image; + + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; +} + +NSImage* +nsDragService::ConstructDragImage(nsIDOMNode* aDOMNode, + nsIScriptableRegion* aRegion, + CSSIntPoint aPoint, + LayoutDeviceIntRect* aDragRect) + { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; + + CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mNativeDragView); RefPtr surface; nsPresContext* pc; - nsresult rv = DrawDrag(aDOMNode, aRegion, mScreenPosition, + nsresult rv = DrawDrag(aDOMNode, aRegion, aPoint, aDragRect, &surface, &pc); if (pc && (!aDragRect->width || !aDragRect->height)) { // just use some suitable defaults int32_t size = nsCocoaUtils::CocoaPointsToDevPixels(20, scaleFactor); - aDragRect->SetRect(pc->CSSPixelsToDevPixels(mScreenPosition.x), - pc->CSSPixelsToDevPixels(mScreenPosition.y), size, size); + aDragRect->SetRect(pc->CSSPixelsToDevPixels(aPoint.x), + pc->CSSPixelsToDevPixels(aPoint.y), size, size); } if (NS_FAILED(rv) || !surface) @@ -293,36 +334,12 @@ nsDragService::InvokeDragSessionImpl(nsIArray* aTransferableArray, CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView); - LayoutDeviceIntRect dragRect(0, 0, 20, 20); - NSImage* image = ConstructDragImage(mSourceNode, &dragRect, aDragRgn); - if (!image) { - // if no image was returned, just draw a rectangle - NSSize size; - size.width = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.width, scaleFactor); - size.height = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.height, scaleFactor); - image = [[NSImage alloc] initWithSize:size]; - [image lockFocus]; - [[NSColor grayColor] set]; - NSBezierPath* path = [NSBezierPath bezierPath]; - [path setLineWidth:2.0]; - [path moveToPoint:NSMakePoint(0, 0)]; - [path lineToPoint:NSMakePoint(0, size.height)]; - [path lineToPoint:NSMakePoint(size.width, size.height)]; - [path lineToPoint:NSMakePoint(size.width, 0)]; - [path lineToPoint:NSMakePoint(0, 0)]; - [path stroke]; - [image unlockFocus]; - } + NSPoint draggingPoint; + NSImage* image = ConstructDragImage(mSourceNode, aDragRgn, &draggingPoint); - // Make drag image appear in the right place under the cursor. - LayoutDeviceIntPoint pt(dragRect.x, dragRect.YMost()); - NSPoint point = nsCocoaUtils::DevPixelsToCocoaPoints(pt, scaleFactor); - point.y = nsCocoaUtils::FlippedScreenY(point.y); - point = nsCocoaUtils::ConvertPointFromScreen([gLastDragView window], point); - NSPoint localPoint = [gLastDragView convertPoint:point fromView:nil]; NSRect localDragRect = image.alignmentRect; - localDragRect.origin.x = localPoint.x; - localDragRect.origin.y = localPoint.y - localDragRect.size.height; + localDragRect.origin.x = draggingPoint.x; + localDragRect.origin.y = draggingPoint.y - localDragRect.size.height; NSDraggingItem* dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:pbItem]; @@ -621,6 +638,71 @@ nsDragService::GetNumDropItems(uint32_t* aNumItems) NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } +NS_IMETHODIMP +nsDragService::UpdateDragImage(nsIDOMNode* aImage, int32_t aImageX, int32_t aImageY) +{ + nsBaseDragService::UpdateDragImage(aImage, aImageX, aImageY); + mDragImageChanged = true; + return NS_OK; +} + +void +nsDragService::DragMovedWithView(NSDraggingSession* aSession, NSPoint aPoint) +{ + aPoint.y = nsCocoaUtils::FlippedScreenY(aPoint.y); + + // XXX It feels like we should be using the backing scale factor at aPoint + // rather than the initial drag view, but I've seen no ill effects of this. + CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mNativeDragView); + LayoutDeviceIntPoint devPoint = nsCocoaUtils::CocoaPointsToDevPixels(aPoint, scaleFactor); + + // If the image has changed, call enumerateDraggingItemsWithOptions to get + // the item being dragged and update its image. + if (mDragImageChanged && mNativeDragView) { + mDragImageChanged = false; + + nsPresContext* pc = nullptr; + nsCOMPtr content = do_QueryInterface(mImage); + if (content) { + nsCOMPtr document = content->OwnerDoc(); + if (document) { + nsIPresShell* shell = document->GetShell(); + pc = shell ? shell->GetPresContext() : nullptr; + } + } + + if (pc) { + void (^changeImageBlock) (NSDraggingItem*, NSInteger, BOOL*) = + ^(NSDraggingItem* draggingItem, NSInteger idx, BOOL* stop) { + // We never add more than one item right now, but check just in case. + if (idx > 0) { + return; + } + + CSSIntPoint screenPoint = + CSSIntPoint(pc->DevPixelsToIntCSSPixels(devPoint.x), + pc->DevPixelsToIntCSSPixels(devPoint.y)); + + // Create a new image; if one isn't returned don't change the current one. + LayoutDeviceIntRect newRect; + NSImage* image = ConstructDragImage(mSourceNode, nullptr, screenPoint, &newRect); + if (image) { + NSRect draggingRect = nsCocoaUtils::GeckoRectToCocoaRectDevPix(newRect, scaleFactor); + [draggingItem setDraggingFrame:draggingRect contents:image]; + } + }; + + [aSession enumerateDraggingItemsWithOptions:NSDraggingItemEnumerationConcurrent + forView:nil + classes:[NSArray arrayWithObject:[NSPasteboardItem class]] + searchOptions:nil + usingBlock:changeImageBlock]; + } + } + + DragMoved(devPoint.x, devPoint.y); +} + NS_IMETHODIMP nsDragService::EndDragSession(bool aDoneDrag) {