Felix Paul Kühne pushed to branch master at VideoLAN / VLC
Commits: c6f924db by Claudio Cambra at 2026-04-14T21:31:57+02:00 macosx: Add VLCLibrarySectionedTableViewDataSource Signed-off-by: Claudio Cambra <[email protected]> - - - - - 8faee9ad by Claudio Cambra at 2026-04-14T21:31:57+02:00 macosx: Add flattened row type to video data source Signed-off-by: Claudio Cambra <[email protected]> - - - - - 446e0f50 by Claudio Cambra at 2026-04-14T21:31:57+02:00 macosx: Replace master/detail handling in video table view delegate with sectioned table handling Signed-off-by: Claudio Cambra <[email protected]> - - - - - 3e546b35 by Claudio Cambra at 2026-04-14T21:31:57+02:00 macosx: Refactor video view controller to set up views as sectioned table view instead of master/detail view Signed-off-by: Claudio Cambra <[email protected]> - - - - - 3dae7f48 by Claudio Cambra at 2026-04-14T21:31:57+02:00 macosx: Adapt video data source to flattened table view approach Signed-off-by: Claudio Cambra <[email protected]> - - - - - 5bc09a7f by Claudio Cambra at 2026-04-14T21:31:57+02:00 macosx: Keep master/detail view for shows Signed-off-by: Claudio Cambra <[email protected]> - - - - - e0dcad55 by Claudio Cambra at 2026-04-14T21:31:57+02:00 macosx: Make headers for sectioned table view functional Signed-off-by: Claudio Cambra <[email protected]> - - - - - 6e32711b by Claudio Cambra at 2026-04-14T21:31:57+02:00 macosx: Remove background from table header view Signed-off-by: Claudio Cambra <[email protected]> - - - - - 1579d239 by Claudio Cambra at 2026-04-14T21:31:57+02:00 macosx: Cache array counts in flattened table view data source before iterating Signed-off-by: Claudio Cambra <[email protected]> - - - - - 9 changed files: - modules/gui/macosx/Makefile.am - + modules/gui/macosx/library/VLCLibrarySectionedTableViewDataSource.h - modules/gui/macosx/library/audio-library/VLCLibraryAudioGroupTableHeaderView.m - modules/gui/macosx/library/video-library/VLCLibraryVideoDataSource.h - modules/gui/macosx/library/video-library/VLCLibraryVideoDataSource.m - modules/gui/macosx/library/video-library/VLCLibraryVideoTableViewDelegate.h - modules/gui/macosx/library/video-library/VLCLibraryVideoTableViewDelegate.m - modules/gui/macosx/library/video-library/VLCLibraryVideoViewController.h - modules/gui/macosx/library/video-library/VLCLibraryVideoViewController.m Changes: ===================================== modules/gui/macosx/Makefile.am ===================================== @@ -200,6 +200,7 @@ libmacosx_plugin_la_SOURCES = \ gui/macosx/library/VLCLibraryTableView.h \ gui/macosx/library/VLCLibraryTableView.m \ gui/macosx/library/VLCLibraryTableViewDataSource.h \ + gui/macosx/library/VLCLibrarySectionedTableViewDataSource.h \ gui/macosx/library/VLCLibraryTableViewDelegate.h \ gui/macosx/library/VLCLibraryTableViewDelegate.m \ gui/macosx/library/VLCLibraryTableCellView.h \ ===================================== modules/gui/macosx/library/VLCLibrarySectionedTableViewDataSource.h ===================================== @@ -0,0 +1,41 @@ +/***************************************************************************** + * VLCLibrarySectionedTableViewDataSource.h: MacOS X interface module + ***************************************************************************** + * Copyright (C) 2026 VLC authors and VideoLAN + * + * Authors: Claudio Cambra <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#import <Cocoa/Cocoa.h> + +#import "library/VLCLibraryTableViewDataSource.h" + +@class VLCLibraryRepresentedItem; + +NS_ASSUME_NONNULL_BEGIN + +@protocol VLCLibrarySectionedTableViewDataSource <VLCLibraryTableViewDataSource> + +- (BOOL)isHeaderRow:(NSInteger)row; +- (NSString *)titleForRow:(NSInteger)row; + +@optional +- (nullable VLCLibraryRepresentedItem *)representedItemForHeaderRow:(NSInteger)row; + +@end + +NS_ASSUME_NONNULL_END ===================================== modules/gui/macosx/library/audio-library/VLCLibraryAudioGroupTableHeaderView.m ===================================== @@ -185,6 +185,7 @@ NSString * const VLCLibraryAudioGroupTableHeaderViewIdentifier = @"VLCLibraryAud self.layer = [CALayer new]; self.layer.backgroundColor = NSColor.clearColor.CGColor; + self.layer.masksToBounds = NO; if (@available(macOS 26.0, *)) { } else { ===================================== modules/gui/macosx/library/video-library/VLCLibraryVideoDataSource.h ===================================== @@ -23,20 +23,19 @@ #import <Cocoa/Cocoa.h> #import "library/VLCLibraryCollectionViewDataSource.h" -#import "library/VLCLibraryMasterDetailViewTableViewDataSource.h" +#import "library/VLCLibrarySectionedTableViewDataSource.h" NS_ASSUME_NONNULL_BEGIN @class VLCLibraryModel; -@interface VLCLibraryVideoDataSource : NSObject <VLCLibraryMasterDetailViewTableViewDataSource, VLCLibraryCollectionViewDataSource> +@interface VLCLibraryVideoDataSource : NSObject <VLCLibrarySectionedTableViewDataSource, VLCLibraryCollectionViewDataSource> extern NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification; @property (readwrite, weak) VLCLibraryModel *libraryModel; @property (readwrite, weak) NSCollectionView *collectionView; -@property (readwrite, weak) NSTableView *masterTableView; -@property (readwrite, weak) NSTableView *detailTableView; +@property (readwrite, weak) NSTableView *tableView; - (void)reloadData; ===================================== modules/gui/macosx/library/video-library/VLCLibraryVideoDataSource.m ===================================== @@ -42,12 +42,47 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification = @"VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification"; +/** + * Represents one row in the flattened table view model. + * A row is either a section header or a media item within a section. + */ +@interface VLCLibraryVideoFlattenedRow : NSObject +@property (readonly) BOOL isHeader; +@property (readonly) VLCMediaLibraryParentGroupType parentType; +@property (readonly) NSInteger itemIndex; // -1 for header rows ++ (instancetype)headerForGroup:(VLCMediaLibraryParentGroupType)group; ++ (instancetype)itemAtIndex:(NSInteger)index inGroup:(VLCMediaLibraryParentGroupType)group; +@end + +@implementation VLCLibraryVideoFlattenedRow + ++ (instancetype)headerForGroup:(VLCMediaLibraryParentGroupType)group +{ + VLCLibraryVideoFlattenedRow * const row = [VLCLibraryVideoFlattenedRow new]; + row->_isHeader = YES; + row->_parentType = group; + row->_itemIndex = -1; + return row; +} + ++ (instancetype)itemAtIndex:(NSInteger)index inGroup:(VLCMediaLibraryParentGroupType)group +{ + VLCLibraryVideoFlattenedRow * const row = [VLCLibraryVideoFlattenedRow new]; + row->_isHeader = NO; + row->_parentType = group; + row->_itemIndex = index; + return row; +} + +@end + @interface VLCLibraryVideoDataSource () { NSMutableArray *_recentsArray; NSMutableArray *_libraryArray; VLCLibraryCollectionViewFlowLayout *_collectionViewFlowLayout; NSUInteger _priorNumVideoSections; + NSArray<VLCLibraryVideoFlattenedRow *> *_flattenedRows; } @end @@ -58,6 +93,7 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification { self = [super init]; if(self) { + _flattenedRows = @[]; [self connect]; } return self; @@ -98,7 +134,6 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification - (void)libraryModelRecentsListReset:(NSNotification * const)aNotification { - [self checkRecentsSection]; [self reloadData]; } @@ -114,8 +149,6 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification - (void)libraryModelRecentsItemDeleted:(NSNotification * const)aNotification { - [self checkRecentsSection]; - NSParameterAssert(aNotification); VLCMediaLibraryMediaItem * const notificationMediaItem = aNotification.object; NSAssert(notificationMediaItem != nil, @"Media item deleted notification should carry valid media item"); @@ -162,6 +195,45 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification [NSNotificationCenter.defaultCenter removeObserver:self]; } +#pragma mark - Flattened row model + +- (NSArray *)arrayForGroup:(VLCMediaLibraryParentGroupType)group +{ + switch (group) { + case VLCMediaLibraryParentGroupTypeRecentVideos: + return _recentsArray; + case VLCMediaLibraryParentGroupTypeVideoLibrary: + return _libraryArray; + default: + return @[]; + } +} + +- (void)rebuildFlattenedRows +{ + NSMutableArray<VLCLibraryVideoFlattenedRow *> * const rows = [NSMutableArray array]; + + const NSUInteger recentsCount = _recentsArray.count; + if (recentsCount > 0) { + [rows addObject:[VLCLibraryVideoFlattenedRow headerForGroup:VLCMediaLibraryParentGroupTypeRecentVideos]]; + for (NSUInteger i = 0; i < recentsCount; i++) { + [rows addObject:[VLCLibraryVideoFlattenedRow itemAtIndex:i + inGroup:VLCMediaLibraryParentGroupTypeRecentVideos]]; + } + } + + const NSUInteger libraryCount = _libraryArray.count; + if (libraryCount > 0) { + [rows addObject:[VLCLibraryVideoFlattenedRow headerForGroup:VLCMediaLibraryParentGroupTypeVideoLibrary]]; + for (NSUInteger i = 0; i < libraryCount; i++) { + [rows addObject:[VLCLibraryVideoFlattenedRow itemAtIndex:i + inGroup:VLCMediaLibraryParentGroupTypeVideoLibrary]]; + } + } + + _flattenedRows = [rows copy]; +} + - (void)reloadData { if(!_libraryModel) { @@ -173,11 +245,10 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification self->_recentsArray = [[self.libraryModel listOfRecentMedia] mutableCopy]; self->_libraryArray = [[self.libraryModel listOfVideoMedia] mutableCopy]; - if (self.masterTableView.dataSource == self) { - [self.masterTableView reloadData]; - } - if (self.detailTableView.dataSource == self) { - [self.detailTableView reloadData]; + [self rebuildFlattenedRows]; + + if (self.tableView.dataSource == self) { + [self.tableView reloadData]; } if (self.collectionView.dataSource == self) { [self.collectionView reloadData]; @@ -187,21 +258,29 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification userInfo:nil]; } +- (NSInteger)flattenedRowIndexForItemIndex:(NSUInteger)itemIndex + inGroup:(VLCMediaLibraryParentGroupType)group +{ + const NSUInteger rowCount = _flattenedRows.count; + for (NSUInteger i = 0; i < rowCount; i++) { + VLCLibraryVideoFlattenedRow * const flatRow = _flattenedRows[i]; + if (!flatRow.isHeader && + flatRow.parentType == group && + flatRow.itemIndex == (NSInteger)itemIndex) { + return i; + } + } + return NSNotFound; +} + - (void)changeDataForSpecificMediaItem:(VLCMediaLibraryMediaItem * const)mediaItem inVideoGroup:(const VLCMediaLibraryParentGroupType)group arrayOperation:(void(^)(const NSMutableArray*, const NSUInteger))arrayOperation completionHandler:(void(^)(const NSIndexSet*))completionHandler { - NSMutableArray *groupArray; - switch(group) { - case VLCMediaLibraryParentGroupTypeVideoLibrary: - groupArray = _libraryArray; - break; - case VLCMediaLibraryParentGroupTypeRecentVideos: - groupArray = _recentsArray; - break; - default: - return; + NSMutableArray *groupArray = (NSMutableArray *)[self arrayForGroup:group]; + if (groupArray == nil) { + return; } const NSUInteger mediaItemIndex = [self indexOfMediaItem:mediaItem.libraryID inArray:groupArray]; @@ -209,10 +288,22 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification return; } + // Find the flattened row index BEFORE mutating the arrays + const NSInteger flatRowIndex = [self flattenedRowIndexForItemIndex:mediaItemIndex + inGroup:group]; + arrayOperation(groupArray, mediaItemIndex); + [self rebuildFlattenedRows]; NSIndexSet * const rowIndexSet = [NSIndexSet indexSetWithIndex:mediaItemIndex]; completionHandler(rowIndexSet); + + // Targeted table view update using flattened row index + if (flatRowIndex != NSNotFound && self.tableView.dataSource == self) { + [self.tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:flatRowIndex] + columnIndexes:[NSIndexSet indexSetWithIndex:0]]; + } + [NSNotificationCenter.defaultCenter postNotificationName:VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification object:self userInfo:nil]; @@ -235,18 +326,6 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification [rowIndexSet indexPathSetWithSection:section]; [self.collectionView reloadItemsAtIndexPaths:indexPathSet]; } - - if (self.detailTableView.dataSource == self && - [self rowToVideoGroup:self.masterTableView.selectedRow] == group) { - // Don't regenerate the groups by index as these do not change according to the - // notification, stick to the selection table view - const NSRange columnRange = NSMakeRange(0, self.masterTableView.numberOfColumns); - NSIndexSet * const columnIndexSet = - [NSIndexSet indexSetWithIndexesInRange:columnRange]; - [self.detailTableView reloadDataForRowIndexes:rowIndexSet columnIndexes:columnIndexSet]; - } - - // Don't bother with the groups table view as we always show "recents" and "videos" there }]; } @@ -267,34 +346,60 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification [rowIndexSet indexPathSetWithSection:section]; [self.collectionView deleteItemsAtIndexPaths:indexPathSet]; } - - if (self.detailTableView.dataSource == self && - [self rowToVideoGroup:self.masterTableView.selectedRow] == group) { - // Don't regenerate the groups by index as these do not change according to the - // notification, stick to the selection table view - [self.detailTableView removeRowsAtIndexes:rowIndexSet - withAnimation:NSTableViewAnimationSlideUp]; - } }]; } -#pragma mark - table view data source and delegation +#pragma mark - Public query methods -- (BOOL)recentItemsPresent +- (BOOL)isHeaderRow:(NSInteger)row { - return self.libraryModel.numberOfRecentMedia > 0; + if (row < 0 || (NSUInteger)row >= _flattenedRows.count) { + return NO; + } + return _flattenedRows[row].isHeader; +} + +- (VLCMediaLibraryParentGroupType)parentTypeForRow:(NSInteger)row +{ + if (row < 0 || (NSUInteger)row >= _flattenedRows.count) { + return VLCMediaLibraryParentGroupTypeUnknown; + } + return _flattenedRows[row].parentType; } -- (BOOL)recentsSectionPresent +- (NSString *)titleForRow:(NSInteger)row { - // We display Recents and/or Library. This will need to change if we add more sections. - return _priorNumVideoSections == 2; + return [self titleForVideoGroup:[self parentTypeForRow:row]]; +} + +- (VLCLibraryRepresentedItem *)representedItemForHeaderRow:(NSInteger)row +{ + if (![self isHeaderRow:row]) { + return nil; + } + + const VLCMediaLibraryParentGroupType parentType = [self parentTypeForRow:row]; + NSArray * const groupArray = [self arrayForGroup:parentType]; + if (groupArray.count == 0) { + return nil; + } + + NSString * const title = [self titleForVideoGroup:parentType]; + VLCMediaLibraryDummyItem * const groupItem = + [[VLCMediaLibraryDummyItem alloc] initWithDisplayString:title + withMediaItems:groupArray]; + return [[VLCLibraryRepresentedItem alloc] initWithItem:groupItem parentType:parentType]; +} + +#pragma mark - Table view data source (sectioned flat table) + +- (BOOL)recentItemsPresent +{ + return self.libraryModel.numberOfRecentMedia > 0; } - (NSUInteger)rowToVideoGroupAdjustment { - // We need to adjust the selected row value to match the backing enum. - // Additionally, we hide recents when there are no recent media items. static const VLCMediaLibraryParentGroupType firstEntry = VLCMediaLibraryParentGroupTypeRecentVideos; const BOOL anyRecents = [self recentItemsPresent]; return anyRecents ? firstEntry : firstEntry + 1; @@ -310,41 +415,16 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification return videoGroup - [self rowToVideoGroupAdjustment]; } -- (void)checkRecentsSection -{ - const BOOL recentsPresent = [self recentItemsPresent]; - const BOOL recentsVisible = [self recentsSectionPresent]; - - if (recentsPresent == recentsVisible) { - return; - } - - [self.masterTableView reloadData]; - [self reloadData]; -} - - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { - if (tableView == self.masterTableView) { - _priorNumVideoSections = [self recentItemsPresent] ? 2 : 1; - return _priorNumVideoSections; - } else if (tableView == self.detailTableView && self.masterTableView.selectedRow > -1) { - switch([self rowToVideoGroup:self.masterTableView.selectedRow]) { - case VLCMediaLibraryParentGroupTypeRecentVideos: - return _recentsArray.count; - case VLCMediaLibraryParentGroupTypeVideoLibrary: - return _libraryArray.count; - default: - NSAssert(NO, @"Reached unreachable case for video library section"); - break; - } - } - - return 0; + return _flattenedRows.count; } - (id<NSPasteboardWriting>)tableView:(NSTableView *)tableView pasteboardWriterForRow:(NSInteger)row { + if ([self isHeaderRow:row]) { + return nil; + } const id<VLCMediaLibraryItemProtocol> libraryItem = [self libraryItemAtRow:row forTableView:tableView]; return [NSPasteboardItem pasteboardItemWithLibraryItem:libraryItem]; } @@ -352,16 +432,19 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification - (id<VLCMediaLibraryItemProtocol>)libraryItemAtRow:(NSInteger)row forTableView:(NSTableView *)tableView { - if (tableView == self.detailTableView && self.masterTableView.selectedRow > -1) { - switch([self rowToVideoGroup:self.masterTableView.selectedRow]) { - case VLCMediaLibraryParentGroupTypeRecentVideos: - return _recentsArray[row]; - case VLCMediaLibraryParentGroupTypeVideoLibrary: - return _libraryArray[row]; - default: - NSAssert(NO, @"Reached unreachable case for video library section"); - break; - } + if (row < 0 || (NSUInteger)row >= _flattenedRows.count) { + return nil; + } + + VLCLibraryVideoFlattenedRow * const flatRow = _flattenedRows[row]; + + if (flatRow.isHeader) { + return nil; + } + + NSArray * const groupArray = [self arrayForGroup:flatRow.parentType]; + if (flatRow.itemIndex >= 0 && (NSUInteger)flatRow.itemIndex < groupArray.count) { + return groupArray[flatRow.itemIndex]; } return nil; @@ -372,7 +455,24 @@ NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification if (libraryItem == nil) { return NSNotFound; } - return [self indexOfMediaItem:libraryItem.libraryID inArray:_libraryArray]; + + const NSUInteger rowCount = _flattenedRows.count; + for (NSUInteger i = 0; i < rowCount; i++) { + VLCLibraryVideoFlattenedRow * const flatRow = _flattenedRows[i]; + if (flatRow.isHeader) { + continue; + } + + NSArray * const groupArray = [self arrayForGroup:flatRow.parentType]; + if (flatRow.itemIndex >= 0 && (NSUInteger)flatRow.itemIndex < groupArray.count) { + id<VLCMediaLibraryItemProtocol> const item = groupArray[flatRow.itemIndex]; + if (item.libraryID == libraryItem.libraryID) { + return i; + } + } + } + + return NSNotFound; } - (VLCMediaLibraryParentGroupType)currentParentType ===================================== modules/gui/macosx/library/video-library/VLCLibraryVideoTableViewDelegate.h ===================================== @@ -22,11 +22,11 @@ #import <Cocoa/Cocoa.h> -#import "library/VLCLibraryMasterDetailViewTableViewDelegate.h" +#import "library/VLCLibraryTableViewDelegate.h" NS_ASSUME_NONNULL_BEGIN -@interface VLCLibraryVideoTableViewDelegate : VLCLibraryMasterDetailViewTableViewDelegate +@interface VLCLibraryVideoTableViewDelegate : VLCLibraryTableViewDelegate @end ===================================== modules/gui/macosx/library/video-library/VLCLibraryVideoTableViewDelegate.m ===================================== @@ -22,13 +22,17 @@ #import "VLCLibraryVideoTableViewDelegate.h" -#import "VLCLibraryVideoDataSource.h" - #import "library/VLCLibraryDataTypes.h" +#import "library/VLCLibraryRepresentedItem.h" +#import "library/VLCLibrarySectionedTableViewDataSource.h" #import "library/VLCLibraryTableCellView.h" #import "library/VLCLibraryTableView.h" +#import "library/VLCLibraryUIUnits.h" + +#import "library/audio-library/VLCLibraryAudioGroupTableHeaderView.h" -#import "library/groups-library/VLCLibraryGroupsDataSource.h" +@interface VLCLibraryVideoHeaderRowView : NSTableRowView +@end @implementation VLCLibraryVideoTableViewDelegate @@ -42,26 +46,100 @@ return self; } -- (NSView *)tableView:(NSTableView *)tableView - viewForTableColumn:(NSTableColumn *)tableColumn - row:(NSInteger)row +#pragma mark - NSTableViewDelegate + +- (NSView *)tableView:(NSTableView *)tableView + viewForTableColumn:(NSTableColumn *)tableColumn + row:(NSInteger)row +{ + if (![tableView.dataSource conformsToProtocol:@protocol(VLCLibrarySectionedTableViewDataSource)]) { + return [super tableView:tableView viewForTableColumn:tableColumn row:row]; + } + + NSObject<VLCLibrarySectionedTableViewDataSource> * const sectionedDataSource = + (NSObject<VLCLibrarySectionedTableViewDataSource> *)tableView.dataSource; + + if ([sectionedDataSource isHeaderRow:row]) { + VLCLibraryAudioGroupTableHeaderView *headerView = + (VLCLibraryAudioGroupTableHeaderView *)[tableView makeViewWithIdentifier:VLCLibraryAudioGroupTableHeaderViewIdentifier + owner:self]; + if (headerView == nil) { + headerView = [[VLCLibraryAudioGroupTableHeaderView alloc] initWithFrame:NSZeroRect]; + headerView.identifier = VLCLibraryAudioGroupTableHeaderViewIdentifier; + } + + NSString * const title = [sectionedDataSource titleForRow:row]; + VLCLibraryRepresentedItem *representedItem = nil; + if ([sectionedDataSource respondsToSelector:@selector(representedItemForHeaderRow:)]) { + representedItem = [sectionedDataSource representedItemForHeaderRow:row]; + } + [headerView updateWithRepresentedItem:representedItem + fallbackTitle:title + fallbackDetail:nil]; + return headerView; + } + + return [super tableView:tableView viewForTableColumn:tableColumn row:row]; +} + +- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row { - VLCLibraryTableCellView * const cellView = - (VLCLibraryTableCellView *)[super tableView:tableView - viewForTableColumn:tableColumn - row:row]; - NSParameterAssert(cellView != nil); - - if ([tableView.dataSource isKindOfClass:[VLCLibraryVideoDataSource class]]) { - VLCLibraryVideoDataSource * const videoTableViewDataSource = - (VLCLibraryVideoDataSource *)tableView.dataSource; - NSParameterAssert(videoTableViewDataSource != nil); - if (tableView == videoTableViewDataSource.masterTableView) { - cellView.representedVideoLibrarySection = row; + if ([tableView.dataSource conformsToProtocol:@protocol(VLCLibrarySectionedTableViewDataSource)]) { + NSObject<VLCLibrarySectionedTableViewDataSource> * const sectionedDataSource = + (NSObject<VLCLibrarySectionedTableViewDataSource> *)tableView.dataSource; + if ([sectionedDataSource isHeaderRow:row]) { + return VLCLibraryAudioGroupTableHeaderViewHeight; } } - return cellView; + return VLCLibraryUIUnits.mediumTableViewRowHeight; +} + +- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row +{ + if ([tableView.dataSource conformsToProtocol:@protocol(VLCLibrarySectionedTableViewDataSource)]) { + NSObject<VLCLibrarySectionedTableViewDataSource> * const sectionedDataSource = + (NSObject<VLCLibrarySectionedTableViewDataSource> *)tableView.dataSource; + return ![sectionedDataSource isHeaderRow:row]; + } + return YES; +} + +- (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row +{ + if ([tableView.dataSource conformsToProtocol:@protocol(VLCLibrarySectionedTableViewDataSource)]) { + NSObject<VLCLibrarySectionedTableViewDataSource> * const sectionedDataSource = + (NSObject<VLCLibrarySectionedTableViewDataSource> *)tableView.dataSource; + return [sectionedDataSource isHeaderRow:row]; + } + return NO; +} + +- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row +{ + if ([tableView.dataSource conformsToProtocol:@protocol(VLCLibrarySectionedTableViewDataSource)]) { + NSObject<VLCLibrarySectionedTableViewDataSource> * const sectionedDataSource = + (NSObject<VLCLibrarySectionedTableViewDataSource> *)tableView.dataSource; + if ([sectionedDataSource isHeaderRow:row]) { + VLCLibraryVideoHeaderRowView * const rowView = [[VLCLibraryVideoHeaderRowView alloc] init]; + return rowView; + } + } + return nil; +} + +@end + +@implementation VLCLibraryVideoHeaderRowView + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.wantsLayer = YES; + self.layer.masksToBounds = NO; + } + return self; } @end ===================================== modules/gui/macosx/library/video-library/VLCLibraryVideoViewController.h ===================================== @@ -39,13 +39,13 @@ NS_ASSUME_NONNULL_BEGIN @interface VLCLibraryVideoViewController : VLCLibraryAbstractMediaLibrarySegmentViewController<VLCLibraryItemPresentingCapable> @property (readonly, weak) NSView *videoLibraryView; -@property (readonly, weak) NSSplitView *videoLibrarySplitView; @property (readonly, weak) NSScrollView *videoLibraryCollectionViewScrollView; @property (readonly, weak) VLCLibraryCollectionView *videoLibraryCollectionView; -@property (readonly, weak) NSScrollView *videoLibraryGroupSelectionTableViewScrollView; -@property (readonly, weak) NSTableView *videoLibraryGroupSelectionTableView; +@property (readonly, weak) NSSplitView *videoLibrarySplitView; @property (readonly, weak) NSScrollView *videoLibraryGroupsTableViewScrollView; @property (readonly, weak) NSTableView *videoLibraryGroupsTableView; +@property (readonly, weak) NSScrollView *videoLibraryGroupSelectionTableViewScrollView; +@property (readonly, weak) NSTableView *videoLibraryGroupSelectionTableView; @property (readonly, nullable) VLCLibraryVideoDataSource *libraryVideoDataSource; @property (readonly, nullable) VLCLibraryShowsDataSource *libraryShowsDataSource; ===================================== modules/gui/macosx/library/video-library/VLCLibraryVideoViewController.m ===================================== @@ -34,7 +34,6 @@ #import "library/VLCLibraryModel.h" #import "library/VLCLibrarySegment.h" #import "library/VLCLibraryTableCellView.h" -#import "library/VLCLibraryTwoPaneSplitViewDelegate.h" #import "library/VLCLibraryUIUnits.h" #import "library/VLCLibraryWindow.h" #import "library/VLCLibraryWindowPersistentPreferences.h" @@ -58,7 +57,6 @@ @interface VLCLibraryVideoViewController () { VLCLibraryVideoTableViewDelegate *_videoLibraryTableViewDelegate; - VLCLibraryTwoPaneSplitViewDelegate *_splitViewDelegate; VLCLibraryCollectionViewDelegate *_collectionViewDelegate; VLCLibraryCollectionViewFlowLayout *_collectionViewLayout; @@ -76,10 +74,9 @@ if(self) { _videoLibraryTableViewDelegate = [[VLCLibraryVideoTableViewDelegate alloc] init]; - _splitViewDelegate = [[VLCLibraryTwoPaneSplitViewDelegate alloc] init]; [self setupPropertiesFromLibraryWindow:libraryWindow]; - [self setupTableViews]; + [self setupTableView]; [self setupCollectionView]; [self setupVideoPlaceholderView]; [self setupVideoLibraryViews]; @@ -124,35 +121,31 @@ { NSParameterAssert(libraryWindow); _videoLibraryView = libraryWindow.videoLibraryView; - _videoLibrarySplitView = libraryWindow.videoLibrarySplitView; _videoLibraryCollectionViewScrollView = libraryWindow.videoLibraryCollectionViewScrollView; _videoLibraryCollectionView = libraryWindow.videoLibraryCollectionView; - _videoLibraryGroupSelectionTableViewScrollView = libraryWindow.videoLibraryGroupSelectionTableViewScrollView; - _videoLibraryGroupSelectionTableView = libraryWindow.videoLibraryGroupSelectionTableView; + _videoLibrarySplitView = libraryWindow.videoLibrarySplitView; _videoLibraryGroupsTableViewScrollView = libraryWindow.videoLibraryGroupsTableViewScrollView; _videoLibraryGroupsTableView = libraryWindow.videoLibraryGroupsTableView; + _videoLibraryGroupSelectionTableViewScrollView = libraryWindow.videoLibraryGroupSelectionTableViewScrollView; + _videoLibraryGroupSelectionTableView = libraryWindow.videoLibraryGroupSelectionTableView; } -- (void)setupTableViews +- (void)setupTableView { - // Split view with table views - self.videoLibrarySplitView.delegate = _splitViewDelegate; - NSNib * const tableCellViewNib = [[NSNib alloc] initWithNibNamed:NSStringFromClass(VLCLibraryTableCellView.class) bundle:nil]; - [self.videoLibraryGroupsTableView registerNib:tableCellViewNib - forIdentifier:@"VLCVideoLibraryTableViewCellIdentifier"]; - [self.videoLibraryGroupSelectionTableView registerNib:tableCellViewNib - forIdentifier:@"VLCVideoLibraryTableViewCellIdentifier"]; + [self.videoLibraryGroupSelectionTableView registerNib:tableCellViewNib + forIdentifier:@"VLCVideoLibraryTableViewCellIdentifier"]; + + self.videoLibraryGroupSelectionTableView.floatsGroupRows = NO; } - (void)setupVideoDataSource { _libraryVideoDataSource = [[VLCLibraryVideoDataSource alloc] init]; self.libraryVideoDataSource.libraryModel = VLCMain.sharedInstance.libraryController.libraryModel; - self.libraryVideoDataSource.masterTableView = self.videoLibraryGroupsTableView; - self.libraryVideoDataSource.detailTableView = self.videoLibraryGroupSelectionTableView; + self.libraryVideoDataSource.tableView = self.videoLibraryGroupSelectionTableView; self.libraryVideoDataSource.collectionView = self.videoLibraryCollectionView; } @@ -233,7 +226,6 @@ - (void)setupVideoLibraryViews { - _videoLibraryGroupsTableView.rowHeight = VLCLibraryUIUnits.mediumTableViewRowHeight; _videoLibraryGroupSelectionTableView.rowHeight = VLCLibraryUIUnits.mediumTableViewRowHeight; const NSEdgeInsets defaultInsets = VLCLibraryUIUnits.libraryViewScrollViewContentInsets; @@ -243,9 +235,6 @@ _videoLibraryCollectionViewScrollView.contentInsets = defaultInsets; _videoLibraryCollectionViewScrollView.scrollerInsets = scrollerInsets; - _videoLibraryGroupsTableViewScrollView.automaticallyAdjustsContentInsets = NO; - _videoLibraryGroupsTableViewScrollView.contentInsets = defaultInsets; - _videoLibraryGroupsTableViewScrollView.scrollerInsets = scrollerInsets; _videoLibraryGroupSelectionTableViewScrollView.automaticallyAdjustsContentInsets = NO; _videoLibraryGroupSelectionTableViewScrollView.contentInsets = defaultInsets; _videoLibraryGroupSelectionTableViewScrollView.scrollerInsets = scrollerInsets; @@ -279,11 +268,6 @@ [self setupVideoDataSource]; self.videoLibraryCollectionView.dataSource = self.libraryVideoDataSource; - self.videoLibraryGroupsTableView.dataSource = self.libraryVideoDataSource; - self.videoLibraryGroupsTableView.target = self.libraryVideoDataSource; - self.videoLibraryGroupsTableView.delegate = _videoLibraryTableViewDelegate; - self.videoLibraryGroupsTableViewScrollView.hidden = NO; - self.videoLibraryGroupSelectionTableView.dataSource = self.libraryVideoDataSource; self.videoLibraryGroupSelectionTableView.target = self.libraryVideoDataSource; self.videoLibraryGroupSelectionTableView.delegate = _videoLibraryTableViewDelegate; @@ -308,14 +292,8 @@ [self setupShowsDataSource]; self.videoLibraryCollectionView.dataSource = self.libraryShowsDataSource; - self.videoLibraryGroupsTableView.dataSource = self.libraryShowsDataSource; - self.videoLibraryGroupsTableView.target = self.libraryShowsDataSource; - self.videoLibraryGroupsTableView.delegate = _videoLibraryTableViewDelegate; - self.videoLibraryGroupsTableViewScrollView.hidden = NO; - - self.videoLibraryGroupSelectionTableView.dataSource = self.libraryShowsDataSource; - self.videoLibraryGroupSelectionTableView.target = self.libraryShowsDataSource; - self.videoLibraryGroupSelectionTableView.delegate = _videoLibraryTableViewDelegate; + // Shows uses the master/detail split view, not the single sectioned table. + // The master and detail table views are wired in setupShowsDataSource. [self.libraryShowsDataSource reloadData]; @@ -342,11 +320,6 @@ [self setupMoviesDataSource]; self.videoLibraryCollectionView.dataSource = self.libraryMoviesDataSource; - self.videoLibraryGroupsTableView.dataSource = nil; - self.videoLibraryGroupsTableView.target = nil; - self.videoLibraryGroupsTableView.delegate = nil; - self.videoLibraryGroupsTableViewScrollView.hidden = YES; - self.videoLibraryGroupSelectionTableView.dataSource = self.libraryMoviesDataSource; self.videoLibraryGroupSelectionTableView.target = self.libraryMoviesDataSource; self.videoLibraryGroupSelectionTableView.delegate = _videoLibraryTableViewDelegate; @@ -383,14 +356,25 @@ - (void)presentVideoLibraryView:(VLCLibraryViewModeSegment)viewModeSegment { + const NSInteger librarySegmentType = self.libraryWindow.librarySegmentType; + const BOOL isShowsSegment = (librarySegmentType == VLCLibraryShowsVideoSubSegmentType); + [self.libraryWindow displayLibraryView:self.videoLibraryView]; + if (viewModeSegment == VLCLibraryGridViewModeSegment) { self.videoLibrarySplitView.hidden = YES; self.videoLibraryCollectionViewScrollView.hidden = NO; } else if (viewModeSegment == VLCLibraryListViewModeSegment) { - self.videoLibrarySplitView.hidden = NO; self.videoLibraryCollectionViewScrollView.hidden = YES; - [_splitViewDelegate resetDefaultSplitForSplitView:self.videoLibrarySplitView]; + if (isShowsSegment) { + // Shows uses the master/detail split view + self.videoLibrarySplitView.hidden = NO; + self.videoLibraryGroupsTableViewScrollView.hidden = NO; + } else { + // Videos and Movies use the single sectioned table + self.videoLibrarySplitView.hidden = NO; + self.videoLibraryGroupsTableViewScrollView.hidden = YES; + } } else { NSAssert(false, @"View mode must be grid or list mode"); } @@ -449,8 +433,8 @@ const NSInteger rowForLibraryItem = [self.libraryVideoDataSource rowForLibraryItem:_awaitingPresentingLibraryItem]; if (rowForLibraryItem != NSNotFound) { NSIndexSet * const indexSet = [NSIndexSet indexSetWithIndex:rowForLibraryItem]; - [self.videoLibraryGroupsTableView selectRowIndexes:indexSet byExtendingSelection:NO]; - [self.videoLibraryGroupsTableView scrollRowToVisible:rowForLibraryItem]; + [self.videoLibraryGroupSelectionTableView selectRowIndexes:indexSet byExtendingSelection:NO]; + [self.videoLibraryGroupSelectionTableView scrollRowToVisible:rowForLibraryItem]; } _awaitingPresentingLibraryItem = nil; View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7ac24e43efbe7b0b839f41e68e1cf760c5986a52...1579d23920b4e5ba61321fd8b2e64c5b6bb408da -- View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7ac24e43efbe7b0b839f41e68e1cf760c5986a52...1579d23920b4e5ba61321fd8b2e64c5b6bb408da You're receiving this email because of your account on code.videolan.org.
_______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
