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;