[vlc-commits] [Git][videolan/vlc][master] 9 commits: macosx: Add VLCLibrarySectionedTableViewDataSource
Felix Paul Kühne (@fkuehne)
gitlab at videolan.org
Tue Apr 14 19:51:01 UTC 2026
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 <developer at claudiocambra.com>
- - - - -
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 <developer at claudiocambra.com>
- - - - -
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 <developer at claudiocambra.com>
- - - - -
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 <developer at claudiocambra.com>
- - - - -
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 <developer at claudiocambra.com>
- - - - -
5bc09a7f by Claudio Cambra at 2026-04-14T21:31:57+02:00
macosx: Keep master/detail view for shows
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
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 <developer at claudiocambra.com>
- - - - -
6e32711b by Claudio Cambra at 2026-04-14T21:31:57+02:00
macosx: Remove background from table header view
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
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 <developer at claudiocambra.com>
- - - - -
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 <developer at claudiocambra.com>
+ *
+ * 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"
+
+ at class VLCLibraryRepresentedItem;
+
+NS_ASSUME_NONNULL_BEGIN
+
+ at protocol VLCLibrarySectionedTableViewDataSource <VLCLibraryTableViewDataSource>
+
+- (BOOL)isHeaderRow:(NSInteger)row;
+- (NSString *)titleForRow:(NSInteger)row;
+
+ at optional
+- (nullable VLCLibraryRepresentedItem *)representedItemForHeaderRow:(NSInteger)row;
+
+ at 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;
- at interface VLCLibraryVideoDataSource : NSObject <VLCLibraryMasterDetailViewTableViewDataSource, VLCLibraryCollectionViewDataSource>
+ at interface VLCLibraryVideoDataSource : NSObject <VLCLibrarySectionedTableViewDataSource, VLCLibraryCollectionViewDataSource>
extern NSString * const VLCLibraryVideoDataSourceDisplayedCollectionChangedNotification;
@property (readwrite, weak) VLCLibraryModel *libraryModel;
@property (readwrite, weak) NSCollectionView *collectionView;
- at property (readwrite, weak) NSTableView *masterTableView;
- at property (readwrite, weak) NSTableView *detailTableView;
+ at 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.
+ */
+ at interface VLCLibraryVideoFlattenedRow : NSObject
+ at property (readonly) BOOL isHeader;
+ at property (readonly) VLCMediaLibraryParentGroupType parentType;
+ at property (readonly) NSInteger itemIndex; // -1 for header rows
++ (instancetype)headerForGroup:(VLCMediaLibraryParentGroupType)group;
++ (instancetype)itemAtIndex:(NSInteger)index inGroup:(VLCMediaLibraryParentGroupType)group;
+ at end
+
+ at 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;
+}
+
+ at 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
- at interface VLCLibraryVideoTableViewDelegate : VLCLibraryMasterDetailViewTableViewDelegate
+ at 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"
+ at interface VLCLibraryVideoHeaderRowView : NSTableRowView
+ at 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;
+}
+
+ at end
+
+ at 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;
- at property (readonly, weak) NSSplitView *videoLibrarySplitView;
@property (readonly, weak) NSScrollView *videoLibraryCollectionViewScrollView;
@property (readonly, weak) VLCLibraryCollectionView *videoLibraryCollectionView;
- at property (readonly, weak) NSScrollView *videoLibraryGroupSelectionTableViewScrollView;
- at property (readonly, weak) NSTableView *videoLibraryGroupSelectionTableView;
+ at property (readonly, weak) NSSplitView *videoLibrarySplitView;
@property (readonly, weak) NSScrollView *videoLibraryGroupsTableViewScrollView;
@property (readonly, weak) NSTableView *videoLibraryGroupsTableView;
+ at property (readonly, weak) NSScrollView *videoLibraryGroupSelectionTableViewScrollView;
+ at 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.
More information about the vlc-commits
mailing list