Skip to content

Commit 1055d4c

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 7a4f466 commit 1055d4c

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
@@ -1693,6 +1693,158 @@ - (void)keyUp:(NSEvent *)event {
16931693
}
16941694

16951695

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

16981850
- (void)emitMouseEvent {

0 commit comments

Comments
 (0)