[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