Skip to content

Commit 6d5570c

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 388ce43 commit 6d5570c

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
@@ -1702,6 +1702,158 @@ - (void)keyUp:(NSEvent *)event {
17021702
}
17031703

17041704

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

17071859
- (void)emitMouseEvent {

0 commit comments

Comments
 (0)