[vlc-commits] [Git][videolan/vlc][master] macosx: support drag and drop for input items into play queue
Steve Lhomme (@robUx4)
gitlab at videolan.org
Tue Aug 18 13:15:51 UTC 2026
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
b81c96e4 by Serhii Bykov at 2026-08-18T13:00:18+00:00
macosx: support drag and drop for input items into play queue
- - - - -
8 changed files:
- modules/gui/macosx/UI/VLCLibraryWindow.xib
- modules/gui/macosx/extensions/NSPasteboardItem+VLCAdditions.h
- modules/gui/macosx/extensions/NSPasteboardItem+VLCAdditions.m
- modules/gui/macosx/library/media-source/VLCMediaSourceBaseDataSource.h
- modules/gui/macosx/library/media-source/VLCMediaSourceBaseDataSource.m
- modules/gui/macosx/library/media-source/VLCMediaSourceDataSource.m
- modules/gui/macosx/playqueue/VLCPlayQueueDataSource.m
- modules/gui/macosx/views/VLCFileDragRecognisingView.m
Changes:
=====================================
modules/gui/macosx/UI/VLCLibraryWindow.xib
=====================================
@@ -1028,7 +1028,7 @@
<rect key="frame" x="0.0" y="0.0" width="528" height="394"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
- <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="firstColumnOnly" columnReordering="NO" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" headerView="4cS-pi-5KZ" viewBased="YES" id="vpJ-Oz-Ebz" customClass="VLCLibraryTableView">
+ <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="firstColumnOnly" columnReordering="NO" columnSelection="YES" multipleSelection="YES" autosaveColumns="NO" headerView="4cS-pi-5KZ" viewBased="YES" id="vpJ-Oz-Ebz" customClass="VLCLibraryTableView">
<rect key="frame" x="0.0" y="0.0" width="718" height="366"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
=====================================
modules/gui/macosx/extensions/NSPasteboardItem+VLCAdditions.h
=====================================
@@ -25,10 +25,12 @@
NS_ASSUME_NONNULL_BEGIN
@protocol VLCMediaLibraryItemProtocol;
+ at class VLCInputItem;
@interface NSPasteboardItem (VLCAdditions)
+ (instancetype)pasteboardItemWithLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem;
++ (nullable instancetype)pasteboardItemWithInputItem:(VLCInputItem *)inputItem;
@end
=====================================
modules/gui/macosx/extensions/NSPasteboardItem+VLCAdditions.m
=====================================
@@ -22,6 +22,7 @@
#import "NSPasteboardItem+VLCAdditions.h"
+#import "library/VLCInputItem.h"
#import "library/VLCLibraryDataTypes.h"
@implementation NSPasteboardItem (VLCAdditions)
@@ -47,4 +48,30 @@
return pboardItem;
}
++ (nullable instancetype)pasteboardItemWithInputItem:(VLCInputItem *)inputItem
+{
+ if (inputItem == nil || inputItem.MRL.length == 0) {
+ return nil;
+ }
+
+ NSURL * const itemURL = [NSURL URLWithString:inputItem.MRL];
+ if (itemURL == nil) {
+ return nil;
+ }
+
+ NSPasteboardItem * const pboardItem = [[NSPasteboardItem alloc] init];
+ [pboardItem setString:itemURL.absoluteString forType:NSPasteboardTypeString];
+ [pboardItem setString:itemURL.absoluteString
+ forType:itemURL.isFileURL ? NSPasteboardTypeFileURL : NSPasteboardTypeURL];
+
+ if (itemURL.isFileURL) {
+ NSString * const localPath = itemURL.path;
+ if (localPath.length > 0) {
+ [pboardItem setPropertyList:@[localPath] forType:NSFilenamesPboardType];
+ }
+ }
+
+ return pboardItem;
+}
+
@end
=====================================
modules/gui/macosx/library/media-source/VLCMediaSourceBaseDataSource.h
=====================================
@@ -32,6 +32,7 @@ typedef NS_ENUM(NSInteger, VLCMediaSourceMode) {
};
@class VLCInputNodePathControl;
+ at class VLCInputItem;
@class VLCLibraryMediaSourceViewNavigationStack;
@class VLCMediaSourceDataSource;
@@ -64,6 +65,7 @@ extern NSString * const VLCMediaSourceBaseDataSourceNodeChanged;
- (void)pathControlAction:(id)sender;
- (void)browseFolderByMrl:(NSString *)mrl;
+- (nullable id<NSPasteboardWriting>)pasteboardWriterForInputItem:(nullable VLCInputItem *)inputItem;
@end
=====================================
modules/gui/macosx/library/media-source/VLCMediaSourceBaseDataSource.m
=====================================
@@ -30,6 +30,7 @@
#import "VLCMediaSourceProvider.h"
#import "extensions/NSImage+VLCAdditions.h"
+#import "extensions/NSPasteboardItem+VLCAdditions.h"
#import "extensions/NSString+Helpers.h"
#import "extensions/NSTableCellView+VLCAdditions.h"
#import "extensions/NSWindow+VLCAdditions.h"
@@ -47,6 +48,7 @@
#import "main/VLCMain.h"
+#import "views/VLCFileDragRecognisingView.h"
#import "views/VLCImageView.h"
#import "views/VLCUIUnits.h"
@@ -154,6 +156,9 @@ NSString * const VLCMediaSourceTableTagsColumnIdentifier = @"VLCMediaSourceTable
self.tableView.dataSource = self;
self.tableView.delegate = self;
+ [self.tableView registerForDraggedTypes:@[NSFilenamesPboardType]];
+ [self.tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
+ [self.tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:YES];
NSNib * const tableCellViewNib = [[NSNib alloc] initWithNibNamed:NSStringFromClass(VLCLibraryTableCellView.class) bundle:nil];
[self.tableView registerNib:tableCellViewNib forIdentifier:VLCLibraryTableCellViewIdentifier];
@@ -537,6 +542,46 @@ referenceSizeForHeaderInSection:(NSInteger)section
[self reloadData];
}
+- (id<NSPasteboardWriting>)tableView:(NSTableView *)tableView pasteboardWriterForRow:(NSInteger)row
+{
+ VLCInputItem * const inputItem = _mediaSourceMode == VLCMediaSourceModeLAN
+ ? _lanDeviceSnapshot[row].inputNode.inputItem
+ : _mediaSources[row].rootNode.inputItem;
+ return [self pasteboardWriterForInputItem:inputItem];
+}
+
+- (NSDragOperation)tableView:(NSTableView *)tableView
+ validateDrop:(id<NSDraggingInfo>)info
+ proposedRow:(NSInteger)row
+ proposedDropOperation:(NSTableViewDropOperation)dropOperation
+{
+ const id propertyList = [info.draggingPasteboard propertyListForType:NSFilenamesPboardType];
+ if (propertyList == nil) {
+ return NSDragOperationNone;
+ }
+
+ [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
+ return NSDragOperationCopy;
+}
+
+- (BOOL)tableView:(NSTableView *)tableView
+ acceptDrop:(id<NSDraggingInfo>)info
+ row:(NSInteger)row
+ dropOperation:(NSTableViewDropOperation)dropOperation
+{
+ return [VLCFileDragRecognisingView
+ handlePasteboardFromDragSessionAsPlayQueueItems:info.draggingPasteboard];
+}
+
+- (nullable id<NSPasteboardWriting>)pasteboardWriterForInputItem:(nullable VLCInputItem *)inputItem
+{
+ if (inputItem == nil || inputItem.inputType == ITEM_TYPE_DIRECTORY) {
+ return nil;
+ }
+
+ return [NSPasteboardItem pasteboardItemWithInputItem:inputItem];
+}
+
#pragma mark - LAN device snapshot
- (NSArray<VLCLANDeviceRecord *> *)buildMediaSourceSnapshot
=====================================
modules/gui/macosx/library/media-source/VLCMediaSourceDataSource.m
=====================================
@@ -26,6 +26,7 @@
#import "VLCMediaSource.h"
#import "VLCMediaSourceBaseDataSource.h"
+#import "extensions/NSPasteboardItem+VLCAdditions.h"
#import "extensions/NSString+Helpers.h"
#import "extensions/NSTableCellView+VLCAdditions.h"
@@ -42,6 +43,7 @@
#import "playqueue/VLCPlayQueueController.h"
+#import "views/VLCFileDragRecognisingView.h"
#import "views/VLCImageView.h"
#import "views/VLCUIUnits.h"
@@ -249,6 +251,9 @@ static NSValue * _Nullable inputItemIdentifier(VLCInputItem * _Nullable const in
{
[self.tableView setDoubleAction:@selector(tableViewAction:)];
[self.tableView setTarget:self];
+ [self.tableView registerForDraggedTypes:@[NSFilenamesPboardType]];
+ [self.tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
+ [self.tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:YES];
}
- (nullable VLCInputNode *)inputNodeForIndexPath:(NSIndexPath *)indexPath
@@ -516,6 +521,35 @@ static NSValue * _Nullable inputItemIdentifier(VLCInputItem * _Nullable const in
}
}
+- (id<NSPasteboardWriting>)tableView:(NSTableView *)tableView pasteboardWriterForRow:(NSInteger)row
+{
+ VLCInputItem * const inputItem = [self mediaSourceInputItemAtRow:row];
+ return [self.parentBaseDataSource pasteboardWriterForInputItem:inputItem];
+}
+
+- (NSDragOperation)tableView:(NSTableView *)tableView
+ validateDrop:(id<NSDraggingInfo>)info
+ proposedRow:(NSInteger)row
+ proposedDropOperation:(NSTableViewDropOperation)dropOperation
+{
+ const id propertyList = [info.draggingPasteboard propertyListForType:NSFilenamesPboardType];
+ if (propertyList == nil) {
+ return NSDragOperationNone;
+ }
+
+ [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
+ return NSDragOperationCopy;
+}
+
+- (BOOL)tableView:(NSTableView *)tableView
+ acceptDrop:(id<NSDraggingInfo>)info
+ row:(NSInteger)row
+ dropOperation:(NSTableViewDropOperation)dropOperation
+{
+ return [VLCFileDragRecognisingView
+ handlePasteboardFromDragSessionAsPlayQueueItems:info.draggingPasteboard];
+}
+
- (nullable VLCInputNode *)mediaSourceInputNodeAtRow:(NSInteger)tableViewRow
{
if (_nodeToDisplay == nil) {
=====================================
modules/gui/macosx/playqueue/VLCPlayQueueDataSource.m
=====================================
@@ -29,10 +29,8 @@
#import "playqueue/VLCPlayQueueTableCellView.h"
#import "playqueue/VLCPlayQueueItem.h"
#import "playqueue/VLCPlayQueueModel.h"
-#import "views/VLCDragDropView.h"
#import "library/VLCLibraryDataTypes.h"
-#import "library/VLCInputItem.h"
-#import "windows/VLCOpenInputMetadata.h"
+#import "views/VLCDragDropView.h"
static NSString *VLCPlayQueueCellIdentifier = @"VLCPlayQueueCellIdentifier";
@@ -59,7 +57,15 @@ static NSString *VLCPlayQueueCellIdentifier = @"VLCPlayQueueCellIdentifier";
- (void)prepareForUse
{
- [_tableView registerForDraggedTypes:@[VLCMediaLibraryMediaItemPasteboardType, VLCMediaLibraryMediaItemUTI, VLCPlaylistItemPasteboardType, NSFilenamesPboardType]];
+ [_tableView registerForDraggedTypes:@[
+ VLCMediaLibraryMediaItemPasteboardType,
+ VLCMediaLibraryMediaItemUTI,
+ VLCPlaylistItemPasteboardType,
+ NSPasteboardTypeURL,
+ NSPasteboardTypeFileURL,
+ NSPasteboardTypeString,
+ NSFilenamesPboardType
+ ]];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
@@ -140,73 +146,8 @@ static NSString *VLCPlayQueueCellIdentifier = @"VLCPlayQueueCellIdentifier";
[_playQueueController moveItemWithID:uniqueID toPosition:row];
return YES;
}
-
- /* Collect library media items from all pasteboard items.
- * Table view drags create one NSPasteboardItem per selected row, so we
- * must iterate all of them to capture every dragged item. */
- NSMutableArray<VLCMediaLibraryMediaItem *> * const allMediaItems = [NSMutableArray array];
-
- for (NSPasteboardItem * const pboardItem in info.draggingPasteboard.pasteboardItems) {
- NSData *itemData = [pboardItem dataForType:VLCMediaLibraryMediaItemPasteboardType];
- if (!itemData) {
- itemData = [pboardItem dataForType:VLCMediaLibraryMediaItemUTI];
- }
- if (!itemData) {
- continue;
- }
-
- /* It is a media library item, so unarchive it and add it to the playlist */
- NSError *unarchiveError = nil;
- NSArray<VLCMediaLibraryMediaItem *> * const items =
- [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithObjects:[NSArray class], [VLCMediaLibraryMediaItem class], nil]
- fromData:itemData
- error:&unarchiveError];
- if (unarchiveError != nil) {
- msg_Err(getIntf(), "Failed to unarchive MediaLibrary Item: %s",
- unarchiveError.localizedDescription.UTF8String);
- continue;
- }
-
- if (items) {
- [allMediaItems addObjectsFromArray:items];
- }
- }
-
- if (allMediaItems.count > 0) {
- NSInteger insertionIndex = (NSInteger)row;
- for (VLCMediaLibraryMediaItem * const mediaItem in allMediaItems) {
- [_playQueueController addInputItem:mediaItem.inputItem.vlcInputItem
- atPosition:insertionIndex
- startPlayback:NO];
- insertionIndex++;
- }
- return YES;
- }
-
- /* Not a library item — check if it is a file handle from the Finder */
- const id propertyList = [info.draggingPasteboard propertyListForType:NSFilenamesPboardType];
- if (propertyList == nil) {
- return NO;
- }
-
- const NSUInteger mediaCount = [propertyList count];
- if (mediaCount > 0) {
- NSMutableArray * const metadataArray = [NSMutableArray arrayWithCapacity:mediaCount];
- for (NSString * const mediaPath in propertyList) {
- VLCOpenInputMetadata *inputMetadata;
- NSURL * const url = [NSURL fileURLWithPath:mediaPath isDirectory:NO];
- if (!url) {
- continue;
- }
- inputMetadata = [[VLCOpenInputMetadata alloc] init];
- inputMetadata.MRLString = url.absoluteString;
- [metadataArray addObject:inputMetadata];
- }
- [_playQueueController addPlayQueueItems:metadataArray];
-
- return YES;
- }
- return NO;
+ return [VLCFileDragRecognisingView
+ handlePasteboardFromDragSessionAsPlayQueueItems:info.draggingPasteboard];
}
- (NSArray<NSTableViewRowAction *> *)tableView:(NSTableView *)tableView
=====================================
modules/gui/macosx/views/VLCFileDragRecognisingView.m
=====================================
@@ -22,6 +22,8 @@
#import "VLCFileDragRecognisingView.h"
+#import "library/VLCInputItem.h"
+#import "library/VLCLibraryDataTypes.h"
#import "main/VLCMain.h"
#import "playqueue/VLCPlayQueueController.h"
#import "windows/VLCOpenInputMetadata.h"
@@ -30,12 +32,66 @@
+ (BOOL)handlePasteboardFromDragSessionAsPlayQueueItems:(NSPasteboard *)pasteboard
{
+ NSMutableArray<VLCMediaLibraryMediaItem *> * const allMediaItems = [NSMutableArray array];
+ for (NSPasteboardItem * const pboardItem in pasteboard.pasteboardItems) {
+ NSData *itemData = [pboardItem dataForType:VLCMediaLibraryMediaItemPasteboardType];
+ if (!itemData) {
+ itemData = [pboardItem dataForType:VLCMediaLibraryMediaItemUTI];
+ }
+ if (!itemData) {
+ continue;
+ }
+
+ NSError *unarchiveError = nil;
+ NSArray<VLCMediaLibraryMediaItem *> * const items =
+ [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithObjects:[NSArray class], [VLCMediaLibraryMediaItem class], nil]
+ fromData:itemData
+ error:&unarchiveError];
+ if (unarchiveError != nil) {
+ msg_Err(getIntf(), "Failed to unarchive MediaLibrary Item: %s",
+ unarchiveError.localizedDescription.UTF8String);
+ continue;
+ }
+
+ if (items) {
+ [allMediaItems addObjectsFromArray:items];
+ }
+ }
+
+ if (allMediaItems.count > 0) {
+ for (VLCMediaLibraryMediaItem * const mediaItem in allMediaItems) {
+ [VLCMain.sharedInstance.playQueueController addInputItem:mediaItem.inputItem.vlcInputItem
+ atPosition:-1
+ startPlayback:NO];
+ }
+ return YES;
+ }
+
+ NSMutableArray<VLCOpenInputMetadata *> * const URLMetadataArray = [NSMutableArray array];
+ for (NSPasteboardItem * const pboardItem in pasteboard.pasteboardItems) {
+ NSString * const URLString = [pboardItem stringForType:NSPasteboardTypeURL] ?:
+ [pboardItem stringForType:NSPasteboardTypeFileURL] ?:
+ [pboardItem stringForType:NSPasteboardTypeString];
+ if (URLString.length <= 0) {
+ continue;
+ }
+
+ VLCOpenInputMetadata * const inputMetadata = [[VLCOpenInputMetadata alloc] init];
+ inputMetadata.MRLString = URLString;
+ [URLMetadataArray addObject:inputMetadata];
+ }
+
+ if (URLMetadataArray.count > 0) {
+ [VLCMain.sharedInstance.playQueueController addPlayQueueItems:URLMetadataArray];
+ return YES;
+ }
+
const id propertyList = [pasteboard propertyListForType:NSFilenamesPboardType];
if (propertyList == nil) {
return NO;
}
- NSArray * const values = [propertyList sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
+ NSArray * const values = propertyList;
const NSUInteger valueCount = values.count;
if (valueCount <= 0) {
return NO;
@@ -93,12 +149,21 @@
- (void)setupDragRecognition
{
- [self registerForDraggedTypes:@[NSFilenamesPboardType]];
+ [self registerForDraggedTypes:@[
+ VLCMediaLibraryMediaItemPasteboardType,
+ VLCMediaLibraryMediaItemUTI,
+ NSPasteboardTypeURL,
+ NSPasteboardTypeFileURL,
+ NSPasteboardTypeString,
+ NSFilenamesPboardType
+ ]];
}
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
- if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric) {
+ const NSDragOperation sourceOperationMask = [sender draggingSourceOperationMask];
+ if ((sourceOperationMask & NSDragOperationCopy) == NSDragOperationCopy ||
+ (sourceOperationMask & NSDragOperationGeneric) == NSDragOperationGeneric) {
return NSDragOperationCopy;
}
return NSDragOperationNone;
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/b81c96e4f8abd8e57802944c7dee62d8e079c4e2
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/b81c96e4f8abd8e57802944c7dee62d8e079c4e2
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help
More information about the vlc-commits
mailing list