Skip to content

Commit 757b951

Browse files
Nick LefeverSaadnajmi
authored andcommitted
[fabric] Add drag and drop support to View component
Summary: Add support for drag and drop to the `ViewComponent` for files and images. The implementation converts the dropped pasteboard items the same way as in Paper. The `DataTransferItem` resulting from the pasteboard conversion of the dragged items is used to build the JS payload for the dragEnter/dragLeave/drop events. Test Plan: * Run Zeratul with Fabric enabled * Drag and drop an image on the messages view * Send the attachment https://pxl.cl/4ldB7 Reviewers: shawndempsey, #rn-desktop Reviewed By: shawndempsey Differential Revision: https://phabricator.intern.facebook.com/D53674742
1 parent 6f83f8f commit 757b951

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed

packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,6 +1691,158 @@ - (void)keyUp:(NSEvent *)event {
16911691
}
16921692

16931693

1694+
#pragma mark - Drag and Drop Events
1695+
1696+
enum DragEventType {
1697+
DragEnter,
1698+
DragLeave,
1699+
Drop,
1700+
};
1701+
1702+
- (void)buildDataTransferItems:(std::vector<DataTransferItem> &)dataTransferItems forPasteboard:(NSPasteboard *)pasteboard {
1703+
NSArray *fileNames = [pasteboard propertyListForType:NSFilenamesPboardType] ?: @[];
1704+
for (NSString *file in fileNames) {
1705+
NSURL *fileURL = [NSURL fileURLWithPath:file];
1706+
BOOL isDir = NO;
1707+
BOOL isValid = (![[NSFileManager defaultManager] fileExistsAtPath:fileURL.path isDirectory:&isDir] || isDir) ? NO : YES;
1708+
if (isValid) {
1709+
1710+
NSString *MIMETypeString = nil;
1711+
if (fileURL.pathExtension) {
1712+
CFStringRef fileExtension = (__bridge CFStringRef)fileURL.pathExtension;
1713+
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, NULL);
1714+
if (UTI != NULL) {
1715+
CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType);
1716+
CFRelease(UTI);
1717+
MIMETypeString = (__bridge_transfer NSString *)MIMEType;
1718+
}
1719+
}
1720+
1721+
NSNumber *fileSizeValue = nil;
1722+
NSError *fileSizeError = nil;
1723+
BOOL success = [fileURL getResourceValue:&fileSizeValue
1724+
forKey:NSURLFileSizeKey
1725+
error:&fileSizeError];
1726+
1727+
NSNumber *width = nil;
1728+
NSNumber *height = nil;
1729+
if ([MIMETypeString hasPrefix:@"image/"]) {
1730+
NSImage *image = [[NSImage alloc] initWithContentsOfURL:fileURL];
1731+
width = @(image.size.width);
1732+
height = @(image.size.height);
1733+
}
1734+
1735+
DataTransferItem transferItem = {
1736+
.name = fileURL.lastPathComponent.UTF8String,
1737+
.kind = "file",
1738+
.type = MIMETypeString.UTF8String,
1739+
.uri = fileURL.path.UTF8String,
1740+
};
1741+
1742+
if (success) {
1743+
transferItem.size = fileSizeValue.intValue;
1744+
}
1745+
1746+
if (width != nil) {
1747+
transferItem.width = width.intValue;
1748+
}
1749+
1750+
if (height != nil) {
1751+
transferItem.height = height.intValue;
1752+
}
1753+
1754+
dataTransferItems.push_back(transferItem);
1755+
}
1756+
}
1757+
1758+
NSPasteboardType imageType = [pasteboard availableTypeFromArray:@[NSPasteboardTypePNG, NSPasteboardTypeTIFF]];
1759+
if (imageType && fileNames.count == 0) {
1760+
NSString *MIMETypeString = imageType == NSPasteboardTypePNG ? @"image/png" : @"image/tiff";
1761+
NSData *imageData = [pasteboard dataForType:imageType];
1762+
NSImage *image = [[NSImage alloc] initWithData:imageData];
1763+
1764+
DataTransferItem transferItem = {
1765+
.kind = "image",
1766+
.type = MIMETypeString.UTF8String,
1767+
.uri = RCTDataURL(MIMETypeString, imageData).absoluteString.UTF8String,
1768+
.size = imageData.length,
1769+
.width = image.size.width,
1770+
.height = image.size.height,
1771+
};
1772+
1773+
dataTransferItems.push_back(transferItem);
1774+
}
1775+
}
1776+
1777+
- (void)sendDragEvent:(DragEventType)eventType withLocation:(NSPoint)locationInWindow pasteboard:(NSPasteboard *)pasteboard {
1778+
if (!_eventEmitter) {
1779+
return;
1780+
}
1781+
1782+
std::vector<DataTransferItem> dataTransferItems{};
1783+
[self buildDataTransferItems:dataTransferItems forPasteboard:pasteboard];
1784+
1785+
NSPoint locationInView = [self convertPoint:locationInWindow fromView:nil];
1786+
NSEventModifierFlags modifierFlags = self.window.currentEvent.modifierFlags;
1787+
1788+
DragEvent dragEvent = {
1789+
{
1790+
.clientX = locationInView.x,
1791+
.clientY = locationInView.y,
1792+
.screenX = locationInWindow.x,
1793+
.screenY = locationInWindow.y,
1794+
.altKey = static_cast<bool>(modifierFlags & NSEventModifierFlagOption),
1795+
.ctrlKey = static_cast<bool>(modifierFlags & NSEventModifierFlagControl),
1796+
.shiftKey = static_cast<bool>(modifierFlags & NSEventModifierFlagShift),
1797+
.metaKey = static_cast<bool>(modifierFlags & NSEventModifierFlagCommand),
1798+
},
1799+
.dataTransferItems = dataTransferItems,
1800+
};
1801+
1802+
switch (eventType) {
1803+
case DragEnter:
1804+
_eventEmitter->onDragEnter(dragEvent);
1805+
break;
1806+
1807+
case DragLeave:
1808+
_eventEmitter->onDragLeave(dragEvent);
1809+
break;
1810+
1811+
case Drop:
1812+
_eventEmitter->onDrop(dragEvent);
1813+
break;
1814+
}
1815+
}
1816+
1817+
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
1818+
{
1819+
NSPasteboard *pboard = sender.draggingPasteboard;
1820+
NSDragOperation sourceDragMask = sender.draggingSourceOperationMask;
1821+
1822+
[self sendDragEvent:DragEnter withLocation:sender.draggingLocation pasteboard:pboard];
1823+
1824+
if ([pboard availableTypeFromArray:self.registeredDraggedTypes]) {
1825+
if (sourceDragMask & NSDragOperationLink) {
1826+
return NSDragOperationLink;
1827+
} else if (sourceDragMask & NSDragOperationCopy) {
1828+
return NSDragOperationCopy;
1829+
}
1830+
}
1831+
return NSDragOperationNone;
1832+
}
1833+
1834+
- (void)draggingExited:(id<NSDraggingInfo>)sender
1835+
{
1836+
[self sendDragEvent:DragLeave withLocation:sender.draggingLocation pasteboard:sender.draggingPasteboard];
1837+
}
1838+
1839+
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
1840+
{
1841+
[self sendDragEvent:Drop withLocation:sender.draggingLocation pasteboard:sender.draggingPasteboard];
1842+
return YES;
1843+
}
1844+
1845+
16941846
#pragma mark - Mouse Events
16951847

16961848
- (void)emitMouseEvent {

0 commit comments

Comments
 (0)