diff --git a/modules/gui/macosx/Makefile.am b/modules/gui/macosx/Makefile.am
index 1d8de36cc0..163057daf2 100644
--- a/modules/gui/macosx/Makefile.am
+++ b/modules/gui/macosx/Makefile.am
@@ -58,10 +58,16 @@ libmacosx_plugin_la_SOURCES = \
gui/macosx/library/VLCInputItem.m \
gui/macosx/library/VLCLibraryAlbumTableCellView.h \
gui/macosx/library/VLCLibraryAlbumTableCellView.m \
+ gui/macosx/library/VLCLibraryAlbumTracksDataSource.h \
+ gui/macosx/library/VLCLibraryAlbumTracksDataSource.m \
gui/macosx/library/VLCLibraryAudioDataSource.h \
gui/macosx/library/VLCLibraryAudioDataSource.m \
+ gui/macosx/library/VLCLibraryCollectionViewFlowLayout.h \
+ gui/macosx/library/VLCLibraryCollectionViewFlowLayout.m \
gui/macosx/library/VLCLibraryCollectionViewItem.h \
gui/macosx/library/VLCLibraryCollectionViewItem.m \
+ gui/macosx/library/VLCLibraryCollectionViewAlbumSupplementaryDetailView.h \
+ gui/macosx/library/VLCLibraryCollectionViewAlbumSupplementaryDetailView.m \
gui/macosx/library/VLCLibraryCollectionViewSupplementaryElementView.h \
gui/macosx/library/VLCLibraryCollectionViewSupplementaryElementView.m \
gui/macosx/library/VLCLibraryController.h \
@@ -297,6 +303,7 @@ libmacosx_plugin_la_XIB_SOURCES = \
gui/macosx/UI/VLCLibraryTableCellView.xib \
gui/macosx/UI/VLCPlaylistTableCellView.xib \
gui/macosx/UI/VLCLibraryCollectionViewItem.xib \
+ gui/macosx/UI/VLCLibraryCollectionViewAlbumSupplementaryDetailView.xib \
gui/macosx/UI/VLCMediaSourceCollectionViewItem.xib \
gui/macosx/UI/VLCMediaSourceDeviceCollectionViewItem.xib \
gui/macosx/UI/VLCInformationWindow.xib \
diff --git a/modules/gui/macosx/UI/VLCLibraryCollectionViewAlbumSupplementaryDetailView.xib b/modules/gui/macosx/UI/VLCLibraryCollectionViewAlbumSupplementaryDetailView.xib
new file mode 100644
index 0000000000..0ea09e7c35
--- /dev/null
+++ b/modules/gui/macosx/UI/VLCLibraryCollectionViewAlbumSupplementaryDetailView.xib
@@ -0,0 +1,164 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.h b/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.h
index b5f91c44c9..5c9fbb2cc9 100644
--- a/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.h
+++ b/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.h
@@ -25,12 +25,13 @@
NS_ASSUME_NONNULL_BEGIN
@class VLCImageView;
-@class VLCLibraryTracksDataSource;
@class VLCTrackingView;
@class VLCMediaLibraryAlbum;
@interface VLCLibraryAlbumTableCellView : NSTableCellView
+extern NSString *VLCAudioLibraryCellIdentifier;
+
+ (CGFloat)defaultHeight;
+ (CGFloat)heightForAlbum:(VLCMediaLibraryAlbum *)album;
diff --git a/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m b/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m
index 62c15ec132..2ba0ed3ffa 100644
--- a/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m
+++ b/modules/gui/macosx/library/VLCLibraryAlbumTableCellView.m
@@ -29,22 +29,17 @@
#import "library/VLCLibraryController.h"
#import "library/VLCLibraryDataTypes.h"
#import "library/VLCLibraryTableCellView.h"
+#import "library/VLCLibraryAlbumTracksDataSource.h"
NSString *VLCAudioLibraryCellIdentifier = @"VLCAudioLibraryCellIdentifier";
const CGFloat VLCLibraryTracksRowHeight = 50.;
const CGFloat VLCLibraryAlbumTableCellViewDefaultHeight = 168.;
const CGFloat LayoutSpacer;
-@interface VLCLibraryTracksDataSource : NSObject
-
-@property (readwrite, retain, nonatomic, nullable) VLCMediaLibraryAlbum *representedAlbum;
-
-@end
-
@interface VLCLibraryAlbumTableCellView ()
{
VLCLibraryController *_libraryController;
- VLCLibraryTracksDataSource *_tracksDataSource;
+ VLCLibraryAlbumTracksDataSource *_tracksDataSource;
NSTableView *_tracksTableView;
}
@end
@@ -75,7 +70,7 @@ const CGFloat LayoutSpacer;
_tracksTableView.rowHeight = VLCLibraryTracksRowHeight;
[_tracksTableView addTableColumn:column];
_tracksTableView.translatesAutoresizingMaskIntoConstraints = NO;
- _tracksDataSource = [[VLCLibraryTracksDataSource alloc] init];
+ _tracksDataSource = [[VLCLibraryAlbumTracksDataSource alloc] init];
_tracksTableView.dataSource = _tracksDataSource;
_tracksTableView.delegate = _tracksDataSource;
_tracksTableView.doubleAction = @selector(tracksTableViewDoubleClickAction:);
@@ -152,68 +147,3 @@ const CGFloat LayoutSpacer;
}
@end
-
-@interface VLCLibraryTracksDataSource ()
-{
- NSArray *_tracks;
-}
-@end
-
-@implementation VLCLibraryTracksDataSource
-
-- (void)setRepresentedAlbum:(VLCMediaLibraryAlbum *)representedAlbum
-{
- _representedAlbum = representedAlbum;
- _tracks = [_representedAlbum tracksAsMediaItems];
-}
-
-- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
-{
- if (_representedAlbum != nil) {
- return _representedAlbum.numberOfTracks;
- }
-
- return 0;
-}
-
-- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
-{
- VLCLibraryTableCellView *cellView = [tableView makeViewWithIdentifier:VLCAudioLibraryCellIdentifier owner:self];
-
- if (cellView == nil) {
- /* the following code saves us an instance of NSViewController which we don't need */
- NSNib *nib = [[NSNib alloc] initWithNibNamed:@"VLCLibraryTableCellView" bundle:nil];
- NSArray *topLevelObjects;
- if (![nib instantiateWithOwner:self topLevelObjects:&topLevelObjects]) {
- NSAssert(1, @"Failed to load nib file to show audio library items");
- return nil;
- }
-
- for (id topLevelObject in topLevelObjects) {
- if ([topLevelObject isKindOfClass:[VLCLibraryTableCellView class]]) {
- cellView = topLevelObject;
- break;
- }
- }
- cellView.identifier = VLCAudioLibraryCellIdentifier;
- }
-
- VLCMediaLibraryMediaItem *mediaItem = _tracks[row];
-
- NSImage *image = mediaItem.smallArtworkImage;
- if (!image) {
- image = [NSImage imageNamed: @"noart.png"];
- }
- cellView.representedImageView.image = image;
- cellView.representedMediaItem = mediaItem;
-
- NSString *title = mediaItem.title;
- cellView.primaryTitleTextField.hidden = NO;
- cellView.secondaryTitleTextField.hidden = NO;
- cellView.primaryTitleTextField.stringValue = title;
- cellView.secondaryTitleTextField.stringValue = [NSString stringWithTime:mediaItem.duration / 1000];
-
- return cellView;
-}
-
-@end
diff --git a/modules/gui/macosx/library/VLCLibraryAlbumTracksDataSource.h b/modules/gui/macosx/library/VLCLibraryAlbumTracksDataSource.h
new file mode 100644
index 0000000000..8d7738d0f4
--- /dev/null
+++ b/modules/gui/macosx/library/VLCLibraryAlbumTracksDataSource.h
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * VLCLibraryAlbumTracksDataSource.h: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2022 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra
+ *
+ * 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
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class VLCMediaLibraryAlbum;
+
+@interface VLCLibraryAlbumTracksDataSource : NSObject
+
+@property (readwrite, retain, nonatomic, nullable) VLCMediaLibraryAlbum *representedAlbum;
+
+@end
+
+NS_ASSUME_NONNULL_END
\ No newline at end of file
diff --git a/modules/gui/macosx/library/VLCLibraryAlbumTracksDataSource.m b/modules/gui/macosx/library/VLCLibraryAlbumTracksDataSource.m
new file mode 100644
index 0000000000..9af1301570
--- /dev/null
+++ b/modules/gui/macosx/library/VLCLibraryAlbumTracksDataSource.m
@@ -0,0 +1,98 @@
+/*****************************************************************************
+ * VLCLibraryAlbumTracksDataSource.m: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * Authors: Felix Paul Kühne
+ *
+ * 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 "VLCLibraryAlbumTracksDataSource.h"
+#import "VLCLibraryAlbumTableCellView.h"
+#import "extensions/NSFont+VLCAdditions.h"
+#import "extensions/NSString+Helpers.h"
+#import "views/VLCImageView.h"
+#import "views/VLCTrackingView.h"
+#import "main/VLCMain.h"
+#import "library/VLCLibraryController.h"
+#import "library/VLCLibraryDataTypes.h"
+#import "library/VLCLibraryTableCellView.h"
+#import "library/VLCLibraryAlbumTracksDataSource.h"
+
+@interface VLCLibraryAlbumTracksDataSource ()
+{
+ NSArray *_tracks;
+}
+@end
+
+@implementation VLCLibraryAlbumTracksDataSource
+
+- (void)setRepresentedAlbum:(VLCMediaLibraryAlbum *)representedAlbum
+{
+ _representedAlbum = representedAlbum;
+ _tracks = [_representedAlbum tracksAsMediaItems];
+}
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
+{
+ if (_representedAlbum != nil) {
+ return _representedAlbum.numberOfTracks;
+ }
+
+ return 0;
+}
+
+- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
+{
+ VLCLibraryTableCellView *cellView = [tableView makeViewWithIdentifier:VLCAudioLibraryCellIdentifier owner:self];
+
+ if (cellView == nil) {
+ /* the following code saves us an instance of NSViewController which we don't need */
+ NSNib *nib = [[NSNib alloc] initWithNibNamed:@"VLCLibraryTableCellView" bundle:nil];
+ NSArray *topLevelObjects;
+ if (![nib instantiateWithOwner:self topLevelObjects:&topLevelObjects]) {
+ NSAssert(1, @"Failed to load nib file to show audio library items");
+ return nil;
+ }
+
+ for (id topLevelObject in topLevelObjects) {
+ if ([topLevelObject isKindOfClass:[VLCLibraryTableCellView class]]) {
+ cellView = topLevelObject;
+ break;
+ }
+ }
+ cellView.identifier = VLCAudioLibraryCellIdentifier;
+ }
+
+ VLCMediaLibraryMediaItem *mediaItem = _tracks[row];
+
+ NSImage *image = mediaItem.smallArtworkImage;
+ if (!image) {
+ image = [NSImage imageNamed: @"noart.png"];
+ }
+ cellView.representedImageView.image = image;
+ cellView.representedMediaItem = mediaItem;
+
+ NSString *title = mediaItem.title;
+ cellView.primaryTitleTextField.hidden = NO;
+ cellView.secondaryTitleTextField.hidden = NO;
+ cellView.primaryTitleTextField.stringValue = title;
+ cellView.secondaryTitleTextField.stringValue = [NSString stringWithTime:mediaItem.duration / 1000];
+
+ return cellView;
+}
+
+@end
\ No newline at end of file
diff --git a/modules/gui/macosx/library/VLCLibraryAudioDataSource.m b/modules/gui/macosx/library/VLCLibraryAudioDataSource.m
index 0c5a58fc14..9d02a60d52 100644
--- a/modules/gui/macosx/library/VLCLibraryAudioDataSource.m
+++ b/modules/gui/macosx/library/VLCLibraryAudioDataSource.m
@@ -30,14 +30,15 @@
#import "library/VLCLibraryTableCellView.h"
#import "library/VLCLibraryAlbumTableCellView.h"
#import "library/VLCLibraryCollectionViewItem.h"
+#import "library/VLCLibraryCollectionViewFlowLayout.h"
+#import "library/VLCLibraryCollectionViewAlbumSupplementaryDetailView.h"
#import "extensions/NSString+Helpers.h"
#import "views/VLCImageView.h"
-static NSString *VLCAudioLibraryCellIdentifier = @"VLCAudioLibraryCellIdentifier";
-
@interface VLCLibraryAudioDataSource ()
{
+ VLCLibraryCollectionViewFlowLayout *_collectionViewFlowLayout;
NSInteger _currentSelectedSegment;
NSArray *_placeholderImageNames;
NSArray *_placeholderLabelStrings;
@@ -67,12 +68,19 @@ static NSString *VLCAudioLibraryCellIdentifier = @"VLCAudioLibraryCellIdentifier
_collectionView.delegate = self;
[_collectionView registerClass:[VLCLibraryCollectionViewItem class] forItemWithIdentifier:VLCLibraryCellIdentifier];
-
- NSCollectionViewFlowLayout *flowLayout = _collectionView.collectionViewLayout;
- flowLayout.itemSize = CGSizeMake(214., 260.);
- flowLayout.sectionInset = NSEdgeInsetsMake(20., 20., 20., 20.);
- flowLayout.minimumLineSpacing = 20.;
- flowLayout.minimumInteritemSpacing = 20.;
+
+ NSNib *albumSupplementaryDetailView = [[NSNib alloc] initWithNibNamed:@"VLCLibraryCollectionViewAlbumSupplementaryDetailView" bundle:nil];
+ [_collectionView registerNib:albumSupplementaryDetailView
+ forSupplementaryViewOfKind:VLCLibraryCollectionViewAlbumSupplementaryDetailViewKind
+ withIdentifier:VLCLibraryCollectionViewAlbumSupplementaryDetailViewIdentifier];
+
+ _collectionViewFlowLayout = [[VLCLibraryCollectionViewFlowLayout alloc] init];
+ _collectionView.collectionViewLayout = _collectionViewFlowLayout;
+
+ _collectionViewFlowLayout.itemSize = CGSizeMake(214., 260.);
+ _collectionViewFlowLayout.sectionInset = NSEdgeInsetsMake(20., 20., 20., 20.);
+ _collectionViewFlowLayout.minimumLineSpacing = 20.;
+ _collectionViewFlowLayout.minimumInteritemSpacing = 20.;
_groupSelectionTableView.target = self;
_groupSelectionTableView.doubleAction = @selector(groubSelectionDoubleClickAction:);
@@ -112,6 +120,7 @@ static NSString *VLCAudioLibraryCellIdentifier = @"VLCAudioLibraryCellIdentifier
- (IBAction)segmentedControlAction:(id)sender
{
+ [_collectionViewFlowLayout resetLayout];
_currentSelectedSegment = _segmentedControl.selectedSegment;
switch (_currentSelectedSegment) {
case 0:
@@ -400,6 +409,42 @@ static NSString *VLCAudioLibraryCellIdentifier = @"VLCAudioLibraryCellIdentifier
return viewItem;
}
+- (void)collectionView:(NSCollectionView *)collectionView didSelectItemsAtIndexPaths:(NSSet *)indexPaths
+{
+ NSIndexPath *indexPath = indexPaths.anyObject;
+ if (!indexPath || _currentParentType != VLC_ML_PARENT_ALBUM) {
+ return;
+ }
+
+ [_collectionViewFlowLayout expandDetailSectionAtIndex:indexPath];
+}
+
+- (void)collectionView:(NSCollectionView *)collectionView didDeselectItemsAtIndexPaths:(NSSet *)indexPaths
+{
+ NSIndexPath *indexPath = indexPaths.anyObject;
+ if (!indexPath || _currentParentType != VLC_ML_PARENT_ALBUM) {
+ return;
+ }
+
+ [_collectionViewFlowLayout collapseDetailSectionAtIndex:indexPath];
+}
+
+- (NSView *)collectionView:(NSCollectionView *)collectionView
+viewForSupplementaryElementOfKind:(NSCollectionViewSupplementaryElementKind)kind
+ atIndexPath:(NSIndexPath *)indexPath
+{
+ if ([kind isEqualToString:VLCLibraryCollectionViewAlbumSupplementaryDetailViewKind] && _currentParentType == VLC_ML_PARENT_ALBUM) {
+ VLCLibraryCollectionViewAlbumSupplementaryDetailView* albumSupplementaryDetailView = [collectionView makeSupplementaryViewOfKind:kind withIdentifier:VLCLibraryCollectionViewAlbumSupplementaryDetailViewKind forIndexPath:indexPath];
+
+ VLCMediaLibraryAlbum *album = _displayedCollection[indexPath.item];
+ albumSupplementaryDetailView.representedAlbum = album;
+
+ return albumSupplementaryDetailView;
+ }
+
+ return nil;
+}
+
@end
@implementation VLCLibraryGroupDataSource
diff --git a/modules/gui/macosx/library/VLCLibraryCollectionViewAlbumSupplementaryDetailView.h b/modules/gui/macosx/library/VLCLibraryCollectionViewAlbumSupplementaryDetailView.h
new file mode 100644
index 0000000000..10eadc8f86
--- /dev/null
+++ b/modules/gui/macosx/library/VLCLibraryCollectionViewAlbumSupplementaryDetailView.h
@@ -0,0 +1,41 @@
+/*****************************************************************************
+ * VLCLibraryCollectionViewSupplementaryDetailView.h: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2022 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra
+ *
+ * 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
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class VLCMediaLibraryAlbum;
+
+extern NSString *const VLCLibraryCollectionViewAlbumSupplementaryDetailViewIdentifier;
+extern NSCollectionViewSupplementaryElementKind const VLCLibraryCollectionViewAlbumSupplementaryDetailViewKind;
+
+@interface VLCLibraryCollectionViewAlbumSupplementaryDetailView : NSView
+
+@property (readwrite, retain, nonatomic) VLCMediaLibraryAlbum *representedAlbum;
+@property (readwrite, weak) IBOutlet NSTextField *albumTitleTextField;
+@property (readwrite, weak) IBOutlet NSTextField *albumDetailsTextField;
+@property (readwrite, weak) IBOutlet NSImageView *albumArtworkImageView;
+@property (readwrite, weak) IBOutlet NSTableView *albumTracksTableView;
+
+@end
+
+NS_ASSUME_NONNULL_END
\ No newline at end of file
diff --git a/modules/gui/macosx/library/VLCLibraryCollectionViewAlbumSupplementaryDetailView.m b/modules/gui/macosx/library/VLCLibraryCollectionViewAlbumSupplementaryDetailView.m
new file mode 100644
index 0000000000..f1ef574ce9
--- /dev/null
+++ b/modules/gui/macosx/library/VLCLibraryCollectionViewAlbumSupplementaryDetailView.m
@@ -0,0 +1,84 @@
+/*****************************************************************************
+ * VLCLibraryCollectionViewSupplementaryDetailView.m: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * Authors: Samuel Bassaly
+ * Claudio Cambra
+ *
+ * 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 "VLCLibraryCollectionViewAlbumSupplementaryDetailView.h"
+
+#import "main/VLCMain.h"
+#import "library/VLCLibraryController.h"
+#import "library/VLCLibraryDataTypes.h"
+#import "library/VLCLibraryModel.h"
+#import "library/VLCLibraryMenuController.h"
+#import "views/VLCImageView.h"
+#import "extensions/NSString+Helpers.h"
+#import "extensions/NSFont+VLCAdditions.h"
+#import "extensions/NSColor+VLCAdditions.h"
+#import "extensions/NSView+VLCAdditions.h"
+#import "library/VLCLibraryAlbumTracksDataSource.h"
+#import "library/VLCLibraryAlbumTableCellView.h"
+
+NSString *const VLCLibraryCollectionViewAlbumSupplementaryDetailViewIdentifier = @"VLCLibraryCollectionViewAlbumSupplementaryDetailViewIdentifier";
+NSCollectionViewSupplementaryElementKind const VLCLibraryCollectionViewAlbumSupplementaryDetailViewKind = @"VLCLibraryCollectionViewAlbumSupplementaryDetailViewIdentifier";
+
+@interface VLCLibraryCollectionViewAlbumSupplementaryDetailView ()
+{
+ VLCLibraryAlbumTracksDataSource *_tracksDataSource;
+}
+@end
+
+@implementation VLCLibraryCollectionViewAlbumSupplementaryDetailView
+
+@synthesize representedAlbum = _representedAlbum;
+@synthesize albumTitleTextField = _albumTitleTextField;
+@synthesize albumDetailsTextField = _albumDetailsTextField;
+@synthesize albumArtworkImageView = _albumArtworkImageView;
+@synthesize albumTracksTableView = _albumTracksTableView;
+
+- (void)awakeFromNib
+{
+ _tracksDataSource = [[VLCLibraryAlbumTracksDataSource alloc] init];
+ _albumTracksTableView.dataSource = _tracksDataSource;
+ _albumTracksTableView.delegate = _tracksDataSource;
+}
+
+- (void)setRepresentedAlbum:(VLCMediaLibraryAlbum *)representedAlbum
+{
+ _representedAlbum = representedAlbum;
+ [self updateRepresentation];
+}
+
+- (void)updateRepresentation
+{
+ if (_representedAlbum == nil) {
+ NSAssert(1, @"no media item assigned for collection view item", nil);
+ return;
+ }
+
+ _albumTitleTextField.stringValue = _representedAlbum.displayString;
+ _albumDetailsTextField.stringValue = _representedAlbum.artistName;
+ _albumArtworkImageView.image = _representedAlbum.smallArtworkImage;
+ _tracksDataSource.representedAlbum = _representedAlbum;
+
+ [_albumTracksTableView reloadData];
+}
+
+@end
\ No newline at end of file
diff --git a/modules/gui/macosx/library/VLCLibraryCollectionViewFlowLayout.h b/modules/gui/macosx/library/VLCLibraryCollectionViewFlowLayout.h
new file mode 100644
index 0000000000..718f79c22c
--- /dev/null
+++ b/modules/gui/macosx/library/VLCLibraryCollectionViewFlowLayout.h
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * VLCLibraryCollectionViewFlowLayout.h: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2022 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra
+ *
+ * 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
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface VLCLibraryCollectionViewFlowLayout : NSCollectionViewFlowLayout
+
+- (void)expandDetailSectionAtIndex:(NSIndexPath *)indexPath;
+- (void)collapseDetailSectionAtIndex:(NSIndexPath *)indexPath;
+- (void)resetLayout;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/modules/gui/macosx/library/VLCLibraryCollectionViewFlowLayout.m b/modules/gui/macosx/library/VLCLibraryCollectionViewFlowLayout.m
new file mode 100644
index 0000000000..10c18dbf57
--- /dev/null
+++ b/modules/gui/macosx/library/VLCLibraryCollectionViewFlowLayout.m
@@ -0,0 +1,274 @@
+/*****************************************************************************
+ * VLCLibraryCollectionViewFlowLayout.m: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2022 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra
+ *
+ * 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 "VLCLibraryCollectionViewFlowLayout.h"
+#import "VLCLibraryCollectionViewAlbumSupplementaryDetailView.h"
+
+#pragma mark - Private data
+static const NSUInteger kAnimationSteps = 16;
+static const NSUInteger kWrapAroundValue = (NSUInteger)-1;
+
+static const CGFloat kDetailViewMargin = 8.;
+static const CGFloat kDetailViewCollapsedHeight = 0.;
+static const CGFloat kDetailViewExpandedHeight = 300.;
+
+static const CGFloat kAnimationHeightIncrement = kDetailViewExpandedHeight / (CGFloat)kAnimationSteps;
+
+typedef NS_ENUM(NSUInteger, VLCDetailViewAnimationType)
+{
+ VLCDetailViewAnimationTypeExpand,
+ VLCDetailViewAnimationTypeCollapse,
+};
+
+static CVReturn detailViewAnimationCallback(CVDisplayLinkRef displayLink,
+ const CVTimeStamp *inNow,
+ const CVTimeStamp *inOutputTime,
+ CVOptionFlags flagsIn,
+ CVOptionFlags *flagsOut,
+ void *displayLinkContext);
+
+#pragma mark - VLCLibraryCollectionViewFlowLayout
+@interface VLCLibraryCollectionViewFlowLayout ()
+{
+ NSUInteger _lastHeightIndex;
+ CVDisplayLinkRef _displayLinkRef;
+}
+
+@property (nonatomic, readwrite) BOOL detailViewIsAnimating;
+@property (nonatomic, readonly) BOOL animationIsCollapse;
+@property (nonatomic, readwrite) NSUInteger animationIndex;
+@property (nonatomic, readwrite, strong) NSIndexPath *selectedIndexPath;
+
+@end
+
+@implementation VLCLibraryCollectionViewFlowLayout
+
+- (instancetype)init
+{
+ self = [super init];
+ if (self == nil) {
+ return nil;
+ }
+
+ [self resetLayout];
+
+ return self;
+}
+
+#pragma mark - Public methods
+- (void)expandDetailSectionAtIndex:(NSIndexPath *)indexPath
+{
+ if([_selectedIndexPath isEqual:indexPath]) {
+ return;
+ }
+
+ _selectedIndexPath = indexPath;
+ [self animateDetailViewWithAnimation:VLCDetailViewAnimationTypeExpand];
+}
+
+- (void)collapseDetailSectionAtIndex:(NSIndexPath *)indexPath
+{
+ if(![_selectedIndexPath isEqual:indexPath]) {
+ return;
+ }
+
+ [self animateDetailViewWithAnimation:VLCDetailViewAnimationTypeCollapse];
+}
+
+- (void)resetLayout
+{
+ [self releaseDisplayLink];
+
+ _selectedIndexPath = nil;
+ _detailViewIsAnimating = NO;
+ _animationIndex = 0;
+
+ [self invalidateLayout];
+}
+
+#pragma mark - Flow Layout methods
+
+- (NSCollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
+{
+ NSCollectionViewLayoutAttributes *attributes = [super layoutAttributesForItemAtIndexPath:indexPath];
+
+ if(_selectedIndexPath == nil || indexPath == _selectedIndexPath) {
+ return attributes;
+ }
+
+ [attributes setFrame:[self frameForDisplacedAttributes:attributes]];
+ return attributes;
+}
+
+- (NSArray<__kindof NSCollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(NSRect)rect
+{
+ if (_selectedIndexPath == nil) {
+ return [super layoutAttributesForElementsInRect:rect];
+ }
+
+ NSRect selectedItemFrame = [[self layoutAttributesForItemAtIndexPath:_selectedIndexPath] frame];
+
+ // Computed attributes from parent
+ NSMutableArray<__kindof NSCollectionViewLayoutAttributes *> *layoutAttributesArray = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
+ for (int i = 0; i < layoutAttributesArray.count; i++) {
+ NSCollectionViewLayoutAttributes *attributes = layoutAttributesArray[i];
+ [attributes setFrame:[self frameForDisplacedAttributes:attributes]];
+ layoutAttributesArray[i] = attributes;
+ }
+
+ // Add detail view to the attributes set -- detail view about to be shown
+ [layoutAttributesArray addObject:[self layoutAttributesForSupplementaryViewOfKind:VLCLibraryCollectionViewAlbumSupplementaryDetailViewKind atIndexPath:self.selectedIndexPath]];
+
+ return layoutAttributesArray;
+}
+
+- (NSCollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSCollectionViewSupplementaryElementKind)elementKind
+ atIndexPath:(NSIndexPath *)indexPath
+{
+ if ([elementKind isEqualToString:VLCLibraryCollectionViewAlbumSupplementaryDetailViewKind]) {
+ NSCollectionViewLayoutAttributes *detailViewAttributes = [NSCollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:elementKind
+ withIndexPath:indexPath];
+ NSAssert1(detailViewAttributes != NULL,
+ @"Failed to create NSCollectionViewLayoutAttributes for view of kind %@.",
+ VLCLibraryCollectionViewAlbumSupplementaryDetailViewKind );
+
+ float selectedItemFrameMaxY = _selectedIndexPath == nil ? 0 : NSMaxY([[self layoutAttributesForItemAtIndexPath:_selectedIndexPath] frame]);
+ detailViewAttributes.frame = NSMakeRect(NSMinX(self.collectionView.frame),
+ selectedItemFrameMaxY + kDetailViewMargin,
+ self.collectionViewContentSize.width - 16.0,
+ (_animationIndex * kAnimationHeightIncrement));
+
+ return detailViewAttributes;
+ }
+
+ NSCollectionViewLayoutAttributes *attributes = [super layoutAttributesForSupplementaryViewOfKind:elementKind
+ atIndexPath:indexPath];
+ [attributes setFrame:[self frameForDisplacedAttributes:attributes]];
+ return attributes;
+}
+
+- (NSSet *)indexPathsToDeleteForSupplementaryViewOfKind:(NSString *)elementKind
+{
+ if ([elementKind isEqualToString:VLCLibraryCollectionViewAlbumSupplementaryDetailViewKind]) {
+ return [self.collectionView indexPathsForVisibleSupplementaryElementsOfKind:elementKind];
+ }
+ return [NSSet set];
+}
+
+# pragma mark - Calculation of displaced frame attributes
+
+- (NSRect)frameForDisplacedAttributes:(NSCollectionViewLayoutAttributes *)inAttributes {
+ NSRect attributesFrame = inAttributes.frame;
+ if (self.selectedIndexPath) {
+ NSRect selectedItemFrame = [[self layoutAttributesForItemAtIndexPath:_selectedIndexPath] frame];
+ if (NSMinY(attributesFrame) > (NSMaxY(selectedItemFrame))) {
+ attributesFrame.origin.y += (_animationIndex * kAnimationHeightIncrement) + kDetailViewMargin;
+ }
+ }
+ return attributesFrame;
+}
+
+#pragma mark - Detail view animation
+- (void)animateDetailViewWithAnimation:(VLCDetailViewAnimationType)type
+{
+ if (type == VLCDetailViewAnimationTypeExpand) {
+ _animationIsCollapse = NO;
+ _animationIndex = kWrapAroundValue;
+ _lastHeightIndex = kAnimationSteps - 1;
+ } else {
+ _animationIsCollapse = YES;
+ _animationIndex = kAnimationSteps;
+ _lastHeightIndex = 0;
+ }
+
+ _detailViewIsAnimating = YES;
+
+ if (_displayLinkRef == NULL) {
+ [self initDisplayLink];
+ }
+}
+
+- (void)initDisplayLink
+{
+ const CVReturn createResult = CVDisplayLinkCreateWithActiveCGDisplays(&_displayLinkRef);
+
+ if ((createResult != kCVReturnSuccess) || (_displayLinkRef == NULL)) {
+ _detailViewIsAnimating = NO;
+ return;
+ }
+
+ CVDisplayLinkSetOutputCallback(_displayLinkRef, detailViewAnimationCallback, (__bridge void *)self);
+ CVDisplayLinkStart(_displayLinkRef);
+}
+
+- (void)releaseDisplayLink
+{
+ if (_displayLinkRef == NULL ) {
+ return;
+ }
+
+ CVDisplayLinkStop(_displayLinkRef);
+ CVDisplayLinkRelease(_displayLinkRef);
+
+ _displayLinkRef = NULL;
+}
+
+@end
+
+static CVReturn detailViewAnimationCallback(
+ CVDisplayLinkRef displayLink,
+ const CVTimeStamp *inNow,
+ const CVTimeStamp *inOutputTime,
+ CVOptionFlags flagsIn,
+ CVOptionFlags *flagsOut,
+ void *displayLinkContext)
+{
+ VLCLibraryCollectionViewFlowLayout *bridgedSelf = (__bridge VLCLibraryCollectionViewFlowLayout *)displayLinkContext;
+ BOOL animationFinished = NO;
+
+ if(bridgedSelf.detailViewIsAnimating) {
+ if (bridgedSelf.animationIsCollapse) {
+ --bridgedSelf.animationIndex;
+ animationFinished = (bridgedSelf.animationIndex == kWrapAroundValue);
+ } else {
+ ++bridgedSelf.animationIndex;
+ animationFinished = (bridgedSelf.animationIndex == kAnimationSteps);
+ }
+ }
+
+ if (bridgedSelf.detailViewIsAnimating == NO || animationFinished) {
+ bridgedSelf.detailViewIsAnimating = NO;
+ [bridgedSelf releaseDisplayLink];
+
+ if (bridgedSelf.animationIsCollapse) {
+ bridgedSelf.selectedIndexPath = nil;
+ bridgedSelf.animationIndex = 0;
+ }
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^(void){
+ [bridgedSelf invalidateLayout];
+ });
+
+ return kCVReturnSuccess;
+}
+
diff --git a/modules/gui/macosx/library/VLCLibraryWindow.m b/modules/gui/macosx/library/VLCLibraryWindow.m
index f3efd3c9fd..2f93edde98 100644
--- a/modules/gui/macosx/library/VLCLibraryWindow.m
+++ b/modules/gui/macosx/library/VLCLibraryWindow.m
@@ -322,6 +322,9 @@ static void addShadow(NSImageView *__unsafe_unretained imageView)
_audioGroupSelectionTableView.dataSource = _libraryAudioGroupDataSource;
_audioGroupSelectionTableView.delegate = _libraryAudioGroupDataSource;
_audioGroupSelectionTableView.rowHeight = [VLCLibraryAlbumTableCellView defaultHeight];
+ _audioLibraryCollectionView.selectable = YES;
+ _audioLibraryCollectionView.allowsMultipleSelection = NO;
+ _audioLibraryCollectionView.allowsEmptySelection = YES;
_mediaSourceDataSource = [[VLCMediaSourceBaseDataSource alloc] init];
_mediaSourceDataSource.collectionView = _mediaSourceCollectionView;