[vlc-commits] [Git][videolan/vlc][master] 10 commits: macosx: Add visual effect view with glass/v-e fallback
Felix Paul Kühne (@fkuehne)
gitlab at videolan.org
Sun Oct 18 09:40:42 UTC 2026
Felix Paul Kühne pushed to branch master at VideoLAN / VLC
Commits:
15e34582 by Claudio Cambra at 2026-10-18T11:24:09+02:00
macosx: Add visual effect view with glass/v-e fallback
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
45764433 by Claudio Cambra at 2026-10-18T11:24:09+02:00
macosx: Use convenience visual effect view in library header view
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
acfab115 by Claudio Cambra at 2026-10-18T11:24:09+02:00
macosx: Add home view section header view
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
412e025d by Claudio Cambra at 2026-10-18T11:24:09+02:00
macosx: Use new header view for carousel containers
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
3ccaa4f5 by Claudio Cambra at 2026-10-18T11:24:09+02:00
macosx: Rely on containers supplying their own headers
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
59523601 by Claudio Cambra at 2026-10-18T11:24:09+02:00
macosx: Use new header view in grid container
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
99f4a085 by Claudio Cambra at 2026-10-18T11:24:09+02:00
macosx: Add section header view test
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
869dbba6 by Claudio Cambra at 2026-10-18T11:24:09+02:00
macosx: Add VLCVisualEffectView test
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
2829c50f by Claudio Cambra at 2026-10-18T11:24:09+02:00
macosx: Fix crash on show all button for recent music
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
f53db707 by Claudio Cambra at 2026-10-18T11:24:09+02:00
macosx: Fix build of visual effect view on older macOS versions
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
19 changed files:
- extras/package/macosx/VLC.xcodeproj/project.pbxproj
- modules/gui/macosx/Makefile.am
- modules/gui/macosx/library/VLCLibraryHeaderView.m
- modules/gui/macosx/library/VLCLibraryWindowNavigationSidebarViewController.m
- modules/gui/macosx/library/home-library/VLCLibraryHomeViewAudioCarouselContainerView.m
- modules/gui/macosx/library/home-library/VLCLibraryHomeViewBaseCarouselContainerView.h
- modules/gui/macosx/library/home-library/VLCLibraryHomeViewBaseCarouselContainerView.m
- + modules/gui/macosx/library/home-library/VLCLibraryHomeViewSectionHeaderView.h
- + modules/gui/macosx/library/home-library/VLCLibraryHomeViewSectionHeaderView.m
- modules/gui/macosx/library/home-library/VLCLibraryHomeViewVideoCarouselContainerView.m
- modules/gui/macosx/library/home-library/VLCLibraryHomeViewVideoContainerViewDataSource.m
- modules/gui/macosx/library/home-library/VLCLibraryHomeViewVideoGridContainerView.h
- modules/gui/macosx/library/home-library/VLCLibraryHomeViewVideoGridContainerView.m
- modules/gui/macosx/tests/Makefile.am
- + modules/gui/macosx/tests/VLCLibraryHomeViewSectionHeaderViewTest.m
- + modules/gui/macosx/tests/VLCVisualEffectViewTest.m
- + modules/gui/macosx/views/VLCVisualEffectView.h
- + modules/gui/macosx/views/VLCVisualEffectView.m
- po/POTFILES.in
Changes:
=====================================
extras/package/macosx/VLC.xcodeproj/project.pbxproj
=====================================
@@ -109,6 +109,7 @@
534E73E229D2EDB1009982DE /* VLCBookmarksTableViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 534E73E129D2EDB1009982DE /* VLCBookmarksTableViewDelegate.m */; };
534E73E529D2FF07009982DE /* VLCBookmark.m in Sources */ = {isa = PBXBuildFile; fileRef = 534E73E429D2FF07009982DE /* VLCBookmark.m */; };
534E8E3A29A06325009503F8 /* VLCMainVideoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 534E8E3929A06325009503F8 /* VLCMainVideoViewController.m */; };
+ 7A6EFFEC2026101100000003 /* VLCVisualEffectView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A6EFFEC2026101100000002 /* VLCVisualEffectView.m */; };
535053EE2B610C3400D7EAF2 /* VLCLoadingOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 535053ED2B610C3400D7EAF2 /* VLCLoadingOverlayView.m */; };
5350E4EE2B1B21A700F276CB /* VLCLibraryHomeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5350E4ED2B1B21A700F276CB /* VLCLibraryHomeViewController.m */; };
5352B37329DF29BF0011CE03 /* VLCMainVideoViewControlsBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 5352B37229DF29BF0011CE03 /* VLCMainVideoViewControlsBar.m */; };
@@ -116,6 +117,7 @@
535F1BBA2B47ACCE00C78D98 /* VLCLibraryHomeViewVideoContainerViewDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 535F1BB92B47ACCD00C78D98 /* VLCLibraryHomeViewVideoContainerViewDataSource.m */; };
535F1BC02B4892A200C78D98 /* VLCLibraryHomeViewAudioCarouselContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 535F1BBF2B4892A200C78D98 /* VLCLibraryHomeViewAudioCarouselContainerView.m */; };
535F1BC32B4979D300C78D98 /* VLCLibraryHomeViewBaseCarouselContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 535F1BC22B4979D300C78D98 /* VLCLibraryHomeViewBaseCarouselContainerView.m */; };
+ 7A72163B05C94DBB9B8B5CE3 /* VLCLibraryHomeViewSectionHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A72163B05C94DBB9B8B5CE2 /* VLCLibraryHomeViewSectionHeaderView.m */; };
5360E9A52C5BE5550046BA8B /* VLCLibraryMasterDetailViewTableViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5360E9A42C5BE5550046BA8B /* VLCLibraryMasterDetailViewTableViewDelegate.m */; };
5360E9A82C5CF6270046BA8B /* VLCLibraryAbstractGroupingDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 5360E9A72C5CF6270046BA8B /* VLCLibraryAbstractGroupingDataSource.m */; };
5362550D293FD639005D64FA /* VLCLibraryWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5362550C293FD639005D64FA /* VLCLibraryWindowController.m */; };
@@ -406,6 +408,8 @@
534E8E3729A04F95009503F8 /* VLCMainVideoView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = VLCMainVideoView.xib; sourceTree = "<group>"; };
534E8E3829A06325009503F8 /* VLCMainVideoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCMainVideoViewController.h; sourceTree = "<group>"; };
534E8E3929A06325009503F8 /* VLCMainVideoViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCMainVideoViewController.m; sourceTree = "<group>"; };
+ 7A6EFFEC2026101100000001 /* VLCVisualEffectView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCVisualEffectView.h; sourceTree = "<group>"; };
+ 7A6EFFEC2026101100000002 /* VLCVisualEffectView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCVisualEffectView.m; sourceTree = "<group>"; };
535053EC2B610C3400D7EAF2 /* VLCLoadingOverlayView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLoadingOverlayView.h; sourceTree = "<group>"; };
535053ED2B610C3400D7EAF2 /* VLCLoadingOverlayView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLoadingOverlayView.m; sourceTree = "<group>"; };
5350E4EC2B1B21A700F276CB /* VLCLibraryHomeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCLibraryHomeViewController.h; sourceTree = "<group>"; };
@@ -420,6 +424,8 @@
535F1BBF2B4892A200C78D98 /* VLCLibraryHomeViewAudioCarouselContainerView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryHomeViewAudioCarouselContainerView.m; sourceTree = "<group>"; };
535F1BC12B4979D300C78D98 /* VLCLibraryHomeViewBaseCarouselContainerView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryHomeViewBaseCarouselContainerView.h; sourceTree = "<group>"; };
535F1BC22B4979D300C78D98 /* VLCLibraryHomeViewBaseCarouselContainerView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryHomeViewBaseCarouselContainerView.m; sourceTree = "<group>"; };
+ 7A72163B05C94DBB9B8B5CE1 /* VLCLibraryHomeViewSectionHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryHomeViewSectionHeaderView.h; sourceTree = "<group>"; };
+ 7A72163B05C94DBB9B8B5CE2 /* VLCLibraryHomeViewSectionHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryHomeViewSectionHeaderView.m; sourceTree = "<group>"; };
5360E99F2C5BE01F0046BA8B /* VLCLibraryMasterDetailViewTableViewDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryMasterDetailViewTableViewDataSource.h; sourceTree = "<group>"; };
5360E9A32C5BE5550046BA8B /* VLCLibraryMasterDetailViewTableViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryMasterDetailViewTableViewDelegate.h; sourceTree = "<group>"; };
5360E9A42C5BE5550046BA8B /* VLCLibraryMasterDetailViewTableViewDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryMasterDetailViewTableViewDelegate.m; sourceTree = "<group>"; };
@@ -1250,6 +1256,8 @@
7DFBDCB6226CDFD600B700A5 /* VLCImageView.m */,
7D2FFA3E227B8A5B0085D649 /* VLCLinearProgressIndicator.h */,
7D2FFA3F227B8A5B0085D649 /* VLCLinearProgressIndicator.m */,
+ 7A6EFFEC2026101100000001 /* VLCVisualEffectView.h */,
+ 7A6EFFEC2026101100000002 /* VLCVisualEffectView.m */,
535053EC2B610C3400D7EAF2 /* VLCLoadingOverlayView.h */,
535053ED2B610C3400D7EAF2 /* VLCLoadingOverlayView.m */,
32543BFA9EFD8D9FE43BE6A4 /* VLCMediaItemCollectionViewItem.h */,
@@ -1786,6 +1794,8 @@
535F1BBF2B4892A200C78D98 /* VLCLibraryHomeViewAudioCarouselContainerView.m */,
535F1BC12B4979D300C78D98 /* VLCLibraryHomeViewBaseCarouselContainerView.h */,
535F1BC22B4979D300C78D98 /* VLCLibraryHomeViewBaseCarouselContainerView.m */,
+ 7A72163B05C94DBB9B8B5CE1 /* VLCLibraryHomeViewSectionHeaderView.h */,
+ 7A72163B05C94DBB9B8B5CE2 /* VLCLibraryHomeViewSectionHeaderView.m */,
53C1EF842B466B13001AEEF5 /* VLCLibraryHomeViewContainerView.h */,
5350E4EC2B1B21A700F276CB /* VLCLibraryHomeViewController.h */,
5350E4ED2B1B21A700F276CB /* VLCLibraryHomeViewController.m */,
@@ -2405,6 +2415,7 @@
1CCC88F02078A3D500E5626F /* About.xib in Sources */,
7DFBDCA82269E77500B700A5 /* VLCLibraryController.m in Sources */,
535F1BC32B4979D300C78D98 /* VLCLibraryHomeViewBaseCarouselContainerView.m in Sources */,
+ 7A72163B05C94DBB9B8B5CE3 /* VLCLibraryHomeViewSectionHeaderView.m in Sources */,
1CCC88F12078A3D500E5626F /* AddonManager.xib in Sources */,
7DFBDCC1226DC16200B700A5 /* VLCInputItem.m in Sources */,
53F187A82DB8F42A00BB1F21 /* NSIndexSet+VLCAdditions.m in Sources */,
@@ -2560,6 +2571,7 @@
7DFBDCBB226CED6300B700A5 /* VLCMediaSourceProvider.m in Sources */,
F905D8ED29CD4DF0B8A46B24 /* VLCMediaSourceTableViewSorting.m in Sources */,
7D66D4362200BC340040D04A /* VLCClickerManager.m in Sources */,
+ 7A6EFFEC2026101100000003 /* VLCVisualEffectView.m in Sources */,
535053EE2B610C3400D7EAF2 /* VLCLoadingOverlayView.m in Sources */,
53B447F9293BB47B00857588 /* VLCLibraryVideoDataSource.m in Sources */,
53F020A12A91115D00E79705 /* VLCLibraryWindowSplitViewController.m in Sources */,
=====================================
modules/gui/macosx/Makefile.am
=====================================
@@ -270,6 +270,8 @@ libmacosx_plugin_la_SOURCES = \
gui/macosx/library/home-library/VLCLibraryHomeViewAudioCarouselContainerView.m \
gui/macosx/library/home-library/VLCLibraryHomeViewBaseCarouselContainerView.h \
gui/macosx/library/home-library/VLCLibraryHomeViewBaseCarouselContainerView.m \
+ gui/macosx/library/home-library/VLCLibraryHomeViewSectionHeaderView.h \
+ gui/macosx/library/home-library/VLCLibraryHomeViewSectionHeaderView.m \
gui/macosx/library/home-library/VLCLibraryHomeViewStackViewController.h \
gui/macosx/library/home-library/VLCLibraryHomeViewStackViewController.m \
gui/macosx/library/home-library/VLCLibraryHomeViewContainerView.h \
@@ -521,6 +523,8 @@ libmacosx_plugin_la_SOURCES = \
gui/macosx/views/VLCTrackingView.m \
gui/macosx/views/VLCUIUnits.h \
gui/macosx/views/VLCUIUnits.m \
+ gui/macosx/views/VLCVisualEffectView.h \
+ gui/macosx/views/VLCVisualEffectView.m \
gui/macosx/views/VLCVolumeSlider.h \
gui/macosx/views/VLCVolumeSlider.m \
gui/macosx/views/VLCVolumeSliderCell.h \
=====================================
modules/gui/macosx/library/VLCLibraryHeaderView.m
=====================================
@@ -31,6 +31,7 @@
#import "library/VLCLibraryRepresentedItem.h"
#import "views/VLCUIUnits.h"
+#import "views/VLCVisualEffectView.h"
const CGFloat VLCLibraryHeaderViewHeight = 66.f;
NSString * const VLCLibraryHeaderViewIdentifier = @"VLCLibraryHeaderView";
@@ -113,26 +114,14 @@ NSString * const VLCLibraryHeaderViewIdentifier = @"VLCLibraryHeaderView";
self.backgroundTopInset = VLCUIUnits.largeSpacing + VLCUIUnits.mediumSpacing;
self.backgroundBottomInset = 0.f;
-#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 260000
- if (@available(macOS 26.0, *)) {
- NSGlassEffectView * const glassView = [[NSGlassEffectView alloc] initWithFrame:self.bounds];
- glassView.translatesAutoresizingMaskIntoConstraints = NO;
- NSView * const glassContentView = [[NSView alloc] initWithFrame:glassView.bounds];
- glassContentView.translatesAutoresizingMaskIntoConstraints = NO;
- glassView.contentView = glassContentView;
- self.backgroundView = glassView;
- contentHostView = glassContentView;
- self.backgroundBottomInset = VLCUIUnits.largeSpacing + VLCUIUnits.mediumSpacing + VLCUIUnits.smallSpacing;
- } else
-#endif
if (@available(macOS 10.14, *)) {
- NSVisualEffectView * const visualEffectView = [[NSVisualEffectView alloc] initWithFrame:self.bounds];
+ VLCVisualEffectView * const visualEffectView = [[VLCVisualEffectView alloc] initWithFrame:self.bounds];
visualEffectView.translatesAutoresizingMaskIntoConstraints = NO;
- visualEffectView.wantsLayer = YES;
- visualEffectView.material = NSVisualEffectMaterialPopover;
- visualEffectView.blendingMode = NSVisualEffectBlendingModeWithinWindow;
self.backgroundView = visualEffectView;
- contentHostView = visualEffectView;
+ contentHostView = visualEffectView.contentView;
+ if (visualEffectView.usesGlassEffect) {
+ self.backgroundBottomInset = VLCUIUnits.largeSpacing + VLCUIUnits.mediumSpacing + VLCUIUnits.smallSpacing;
+ }
} else {
NSView * const fallbackBackgroundView = [[NSView alloc] initWithFrame:self.bounds];
fallbackBackgroundView.translatesAutoresizingMaskIntoConstraints = NO;
=====================================
modules/gui/macosx/library/VLCLibraryWindowNavigationSidebarViewController.m
=====================================
@@ -318,6 +318,11 @@ static NSString * const VLCLibrarySegmentCellIdentifier = @"VLCLibrarySegmentCel
return;
}
+ // Music is a grouping node. Match sidebar clicks by presenting Artists.
+ if (segmentType == VLCLibraryMusicSegmentType) {
+ segmentType = VLCLibraryArtistsMusicSubSegmentType;
+ }
+
VLCLibrarySegment * const segment = [VLCLibrarySegment segmentWithSegmentType:segmentType];
if (segment.mediaLibraryRequired &&
VLCMain.sharedInstance.libraryController.libraryModel == nil) {
@@ -333,10 +338,6 @@ static NSString * const VLCLibrarySegmentCellIdentifier = @"VLCLibrarySegmentCel
self.libraryWindow.librarySegmentType = segmentType;
- if (segmentType == VLCLibraryMusicSegmentType) {
- [self.outlineView expandItem:[self nodeForSegmentType:VLCLibraryMusicSegmentType]];
- }
-
[self expandParentsOfNode:targetNode];
const NSInteger segmentIndex = [self.outlineView rowForItem:targetNode];
[self.outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:segmentIndex]
=====================================
modules/gui/macosx/library/home-library/VLCLibraryHomeViewAudioCarouselContainerView.m
=====================================
@@ -28,6 +28,10 @@
#import "library/VLCLibraryCarouselCollectionViewItem.h"
#import "library/VLCLibraryCollectionView.h"
#import "library/VLCLibraryModel.h"
+#import "library/VLCLibrarySegment.h"
+#import "library/VLCLibraryWindow.h"
+#import "library/VLCLibraryWindowSplitViewController.h"
+#import "library/VLCLibraryWindowNavigationSidebarViewController.h"
#import "library/audio-library/VLCLibraryAudioDataSource.h"
@@ -37,10 +41,9 @@
@synthesize dataSource = _dataSource;
-- (void)setup
+- (NSNotificationName)displayedCollectionChangedNotification
{
- [super setup];
- [self setupDataSource];
+ return VLCLibraryAudioDataSourceDisplayedCollectionChangedNotification;
}
- (void)setupDataSource
@@ -59,6 +62,60 @@
return ((VLCLibraryAudioDataSource *)self.dataSource).audioLibrarySegment;
}
+- (void)connect
+{
+ [super connect];
+ NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter;
+ for (NSString * const name in @[VLCLibraryModelRecentAudioMediaListReset,
+ VLCLibraryModelRecentAudioMediaItemDeleted,
+ VLCLibraryModelAudioMediaItemDeleted]) {
+ [notificationCenter addObserver:self selector:@selector(reloadRecentMusic:) name:name object:nil];
+ }
+}
+
+- (void)disconnect
+{
+ [super disconnect];
+ NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter;
+ for (NSString * const name in @[VLCLibraryModelRecentAudioMediaListReset,
+ VLCLibraryModelRecentAudioMediaItemDeleted,
+ VLCLibraryModelAudioMediaItemDeleted]) {
+ [notificationCenter removeObserver:self name:name object:nil];
+ }
+}
+
+- (void)reloadRecentMusic:(NSNotification *)notification
+{
+ if (self.audioLibrarySegment == VLCAudioLibraryRecentsSegment) {
+ [self.dataSource reloadData];
+ }
+}
+
+- (void)openLibrary:(id)sender
+{
+ VLCLibrarySegmentType segmentType;
+ switch (self.audioLibrarySegment) {
+ case VLCAudioLibraryRecentsSegment:
+ segmentType = VLCLibraryMusicSegmentType;
+ break;
+ case VLCAudioLibraryArtistsSegment:
+ segmentType = VLCLibraryArtistsMusicSubSegmentType;
+ break;
+ case VLCAudioLibraryAlbumsSegment:
+ segmentType = VLCLibraryAlbumsMusicSubSegmentType;
+ break;
+ case VLCAudioLibrarySongsSegment:
+ segmentType = VLCLibrarySongsMusicSubSegmentType;
+ break;
+ case VLCAudioLibraryGenresSegment:
+ segmentType = VLCLibraryGenresMusicSubSegmentType;
+ break;
+ default:
+ return;
+ }
+ [VLCMain.sharedInstance.libraryWindow.splitViewController.navSidebarViewController selectSegment:segmentType];
+}
+
- (void)setAudioLibrarySegment:(VLCAudioLibrarySegment)audioLibrarySegment
{
if (audioLibrarySegment == self.audioLibrarySegment) {
@@ -67,27 +124,28 @@
switch (audioLibrarySegment) {
case VLCAudioLibraryRecentsSegment:
- self.titleView.stringValue = _NS("Recent music");
+ self.headerView.title = _NS("Recent music");
break;
case VLCAudioLibraryArtistsSegment:
- self.titleView.stringValue = _NS("Artists");
+ self.headerView.title = _NS("Artists");
break;
case VLCAudioLibraryAlbumsSegment:
- self.titleView.stringValue = _NS("Albums");
+ self.headerView.title = _NS("Albums");
break;
case VLCAudioLibrarySongsSegment:
- self.titleView.stringValue = _NS("Songs");
+ self.headerView.title = _NS("Songs");
break;
case VLCAudioLibraryGenresSegment:
- self.titleView.stringValue = _NS("Genres");
+ self.headerView.title = _NS("Genres");
break;
case VLCAudioLibraryUnknownSegment:
default:
- self.titleView.stringValue = _NS("Unknown");
+ self.headerView.title = _NS("Unknown");
break;
}
((VLCLibraryAudioDataSource *)self.dataSource).audioLibrarySegment = audioLibrarySegment;
+ self.headerView.libraryButton.enabled = audioLibrarySegment != VLCAudioLibraryUnknownSegment;
[self.collectionView reloadData];
}
=====================================
modules/gui/macosx/library/home-library/VLCLibraryHomeViewBaseCarouselContainerView.h
=====================================
@@ -23,6 +23,7 @@
#import <Cocoa/Cocoa.h>
#import "VLCLibraryHomeViewContainerView.h"
+#import "VLCLibraryHomeViewSectionHeaderView.h"
#import "library/VLCLibraryCarouselView.h"
#import "library/VLCLibraryCollectionViewDataSource.h"
@@ -34,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
@class VLCLibraryCollectionViewFlowLayout;
@interface VLCLibraryHomeViewBaseCarouselContainerView : NSView<VLCLibraryHomeViewContainerView>
- at property (readonly) NSTextField *titleView;
+ at property (readonly) VLCLibraryHomeViewSectionHeaderView *headerView;
@property (readonly) VLCSubScrollView *scrollView;
@property (readonly) VLCLibraryCarouselView *collectionView;
@property (readonly) VLCLibraryCollectionViewDelegate *collectionViewDelegate;
@@ -42,9 +43,13 @@ NS_ASSUME_NONNULL_BEGIN
@property (readonly) NSButton *leftButton;
@property (readonly) NSButton *rightButton;
@property (readonly) NSObject<VLCLibraryCollectionViewDataSource> *dataSource;
+ at property (readonly, nullable) NSNotificationName displayedCollectionChangedNotification;
@property (readwrite, nonatomic) CGFloat itemHeight;
- (void)setup;
+// Subclasses provide their data source and route the shared header button.
+- (void)setupDataSource;
+- (void)openLibrary:(id)sender;
@end
=====================================
modules/gui/macosx/library/home-library/VLCLibraryHomeViewBaseCarouselContainerView.m
=====================================
@@ -22,11 +22,6 @@
#import "VLCLibraryHomeViewBaseCarouselContainerView.h"
-#import "extensions/NSFont+VLCAdditions.h"
-#import "extensions/NSString+Helpers.h"
-#import "extensions/NSTextField+VLCAdditions.h"
-#import "extensions/NSView+VLCAdditions.h"
-
#import "library/VLCLibraryCollectionView.h"
#import "library/VLCLibraryCarouselView.h"
#import "library/VLCLibraryCarouselCollectionViewItem.h"
@@ -89,19 +84,46 @@
- (void)setup
{
[self setupView];
+ if (self.dataSource == nil) {
+ [self setupDataSource];
+ }
+ [self setupHeader];
[self connect];
}
+- (void)setupDataSource
+{
+}
+
+- (void)setupHeader
+{
+ self.headerView.libraryButton.target = self;
+ self.headerView.libraryButton.action = @selector(openLibrary:);
+}
+
+- (void)openLibrary:(id)sender
+{
+ [self doesNotRecognizeSelector:_cmd];
+}
+
+- (NSNotificationName)displayedCollectionChangedNotification
+{
+ return nil;
+}
+
- (void)setupView
{
- _titleView = [NSTextField defaultLabelWithString:@""];
- self.titleView.font = NSFont.VLClibrarySectionHeaderFont;
- self.titleView.textColor = NSColor.headerTextColor;
- [self addSubview:self.titleView];
+ if (self.headerView != nil) {
+ return;
+ }
+
+ _headerView = [[VLCLibraryHomeViewSectionHeaderView alloc] initWithFrame:NSZeroRect];
+ self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self addSubview:self.headerView];
[NSLayoutConstraint activateConstraints:@[
- [self.leadingAnchor constraintEqualToAnchor:self.titleView.leadingAnchor],
- [self.trailingAnchor constraintEqualToAnchor:self.titleView.trailingAnchor],
- [self.topAnchor constraintEqualToAnchor:self.titleView.topAnchor],
+ [self.headerView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
+ [self.headerView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
+ [self.headerView.topAnchor constraintEqualToAnchor:self.topAnchor],
]];
_collectionViewLayout = [[VLCLibraryCollectionViewFlowLayout alloc] init];
@@ -133,7 +155,7 @@
[NSLayoutConstraint activateConstraints:@[
[self.leadingAnchor constraintEqualToAnchor:self.scrollView.leadingAnchor constant:VLCUIUnits.mediumSpacing],
[self.trailingAnchor constraintEqualToAnchor:self.scrollView.trailingAnchor constant:-VLCUIUnits.mediumSpacing],
- [self.titleView.bottomAnchor constraintEqualToAnchor:self.scrollView.topAnchor constant:-VLCUIUnits.mediumSpacing],
+ [self.scrollView.topAnchor constraintEqualToAnchor:self.headerView.bottomAnchor constant:VLCUIUnits.mediumSpacing],
[self.bottomAnchor constraintEqualToAnchor:self.scrollView.bottomAnchor]
]];
@@ -202,14 +224,39 @@
- (void)connect
{
+ if (self.dataSource == nil) {
+ return;
+ }
+
+ [self disconnect];
+ NSNotificationName const notificationName = self.displayedCollectionChangedNotification;
+ if (notificationName != nil) {
+ [NSNotificationCenter.defaultCenter addObserver:self
+ selector:@selector(displayedCollectionChanged:)
+ name:notificationName
+ object:self.dataSource];
+ }
[self.dataSource connect];
+ [self displayedCollectionChanged:nil];
}
- (void)disconnect
{
+ NSNotificationName const notificationName = self.displayedCollectionChangedNotification;
+ if (notificationName != nil && self.dataSource != nil) {
+ [NSNotificationCenter.defaultCenter removeObserver:self
+ name:notificationName
+ object:self.dataSource];
+ }
[self.dataSource disconnect];
}
+- (void)displayedCollectionChanged:(NSNotification *)notification
+{
+ self.headerView.itemCount = [self.dataSource collectionView:self.collectionView numberOfItemsInSection:0];
+ [self updateCarouselButtonVisibility];
+}
+
- (void)updateCarouselViewHeight
{
if (self.heightConstraint == nil) {
=====================================
modules/gui/macosx/library/home-library/VLCLibraryHomeViewSectionHeaderView.h
=====================================
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * VLCLibraryHomeViewSectionHeaderView.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>
+
+NS_ASSUME_NONNULL_BEGIN
+
+ at interface VLCLibraryHomeViewSectionHeaderView : NSView
+
+ at property (readwrite, copy, nonatomic) NSString *title;
+ at property (readwrite, nonatomic) NSUInteger itemCount;
+ at property (readonly) NSButton *libraryButton;
+
+ at end
+
+NS_ASSUME_NONNULL_END
=====================================
modules/gui/macosx/library/home-library/VLCLibraryHomeViewSectionHeaderView.m
=====================================
@@ -0,0 +1,132 @@
+/*****************************************************************************
+ * VLCLibraryHomeViewSectionHeaderView.m: 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 "VLCLibraryHomeViewSectionHeaderView.h"
+
+#import "extensions/NSFont+VLCAdditions.h"
+#import "extensions/NSString+Helpers.h"
+#import "extensions/NSView+VLCAdditions.h"
+
+#import "views/VLCUIUnits.h"
+#import "views/VLCVisualEffectView.h"
+
+ at interface VLCLibraryHomeViewSectionHeaderView ()
+
+ at property (readonly) NSTextField *titleField;
+ at property (readonly) NSTextField *countField;
+
+ at end
+
+ at implementation VLCLibraryHomeViewSectionHeaderView
+
+- (instancetype)initWithFrame:(NSRect)frameRect
+{
+ self = [super initWithFrame:frameRect];
+ if (self) {
+ [self setupView];
+ }
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+ self = [super initWithCoder:coder];
+ if (self) {
+ [self setupView];
+ }
+ return self;
+}
+
+- (void)setupView
+{
+ VLCVisualEffectView * const backgroundView = [[VLCVisualEffectView alloc] initWithFrame:self.bounds];
+ if (!backgroundView.usesGlassEffect) {
+ backgroundView.cornerRadius = VLCUIUnits.cornerRadius;
+ }
+ backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self addSubview:backgroundView];
+ [backgroundView applyConstraintsToFillSuperview];
+ NSView * const contentView = backgroundView.contentView;
+
+ _titleField = [self labelWithFont:NSFont.VLClibrarySectionHeaderFont textColor:NSColor.labelColor];
+ _countField = [self labelWithFont:NSFont.VLCLibrarySubsectionSubheaderFont textColor:NSColor.secondaryLabelColor];
+ _libraryButton = [NSButton buttonWithTitle:_NS("Show all") target:nil action:nil];
+ self.libraryButton.translatesAutoresizingMaskIntoConstraints = NO;
+ self.libraryButton.bezelStyle = NSBezelStyleRounded;
+ [self.libraryButton setContentHuggingPriority:NSLayoutPriorityRequired
+ forOrientation:NSLayoutConstraintOrientationHorizontal];
+ [self.libraryButton setContentCompressionResistancePriority:NSLayoutPriorityRequired
+ forOrientation:NSLayoutConstraintOrientationHorizontal];
+
+ NSStackView * const labelsView = [NSStackView stackViewWithViews:@[self.titleField, self.countField]];
+ labelsView.translatesAutoresizingMaskIntoConstraints = NO;
+ labelsView.orientation = NSUserInterfaceLayoutOrientationVertical;
+ labelsView.alignment = NSLayoutAttributeLeading;
+ labelsView.spacing = VLCUIUnits.smallSpacing;
+ [labelsView setContentHuggingPriority:NSLayoutPriorityDefaultLow
+ forOrientation:NSLayoutConstraintOrientationHorizontal];
+ [labelsView setContentCompressionResistancePriority:NSLayoutPriorityDefaultLow
+ forOrientation:NSLayoutConstraintOrientationHorizontal];
+ [contentView addSubview:labelsView];
+ [contentView addSubview:self.libraryButton];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [labelsView.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:VLCUIUnits.mediumSpacing],
+ [labelsView.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:VLCUIUnits.smallSpacing],
+ [labelsView.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor constant:-VLCUIUnits.smallSpacing],
+ [self.libraryButton.leadingAnchor constraintGreaterThanOrEqualToAnchor:labelsView.trailingAnchor constant:VLCUIUnits.largeSpacing],
+ [self.libraryButton.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor constant:-VLCUIUnits.mediumSpacing],
+ [self.libraryButton.centerYAnchor constraintEqualToAnchor:contentView.centerYAnchor],
+ [self.libraryButton.topAnchor constraintGreaterThanOrEqualToAnchor:contentView.topAnchor constant:VLCUIUnits.smallSpacing],
+ [self.libraryButton.bottomAnchor constraintLessThanOrEqualToAnchor:contentView.bottomAnchor constant:-VLCUIUnits.smallSpacing],
+ ]];
+}
+
+- (NSTextField *)labelWithFont:(NSFont *)font textColor:(NSColor *)textColor
+{
+ NSTextField * const label = [NSTextField labelWithString:@""];
+ label.translatesAutoresizingMaskIntoConstraints = NO;
+ label.font = font;
+ label.textColor = textColor;
+ label.maximumNumberOfLines = 1;
+ label.lineBreakMode = NSLineBreakByTruncatingTail;
+ [label setContentCompressionResistancePriority:NSLayoutPriorityDefaultLow
+ forOrientation:NSLayoutConstraintOrientationHorizontal];
+ return label;
+}
+
+- (void)setTitle:(NSString *)title
+{
+ _title = [title copy];
+ self.titleField.stringValue = title;
+ self.titleField.toolTip = title;
+ self.libraryButton.accessibilityLabel = [NSString stringWithFormat:_NS("Open %@ in the library"), title];
+ self.libraryButton.toolTip = self.libraryButton.accessibilityLabel;
+}
+
+- (void)setItemCount:(NSUInteger)itemCount
+{
+ _itemCount = itemCount;
+ self.countField.stringValue = _NPS("%lu item", "%lu items", (unsigned long)itemCount);
+}
+
+ at end
=====================================
modules/gui/macosx/library/home-library/VLCLibraryHomeViewVideoCarouselContainerView.m
=====================================
@@ -28,10 +28,16 @@
#import "library/VLCLibraryCollectionView.h"
#import "library/VLCLibraryCollectionViewDelegate.h"
#import "library/VLCLibraryCarouselCollectionViewItem.h"
+#import "library/VLCLibraryDataTypes.h"
+#import "library/VLCLibraryWindow.h"
+#import "library/VLCLibraryWindowSplitViewController.h"
+#import "library/VLCLibrarySegment.h"
+#import "library/VLCLibraryWindowNavigationSidebarViewController.h"
#import "library/home-library/VLCLibraryHomeViewVideoContainerViewDataSource.h"
#import "views/VLCUIUnits.h"
+#import "main/VLCMain.h"
#include "library/video-library/VLCLibraryVideoGroupDescriptor.h"
@@ -41,18 +47,17 @@
@synthesize videoGroup = _videoGroup;
@synthesize dataSource = _dataSource;
-- (void)setup
+- (NSNotificationName)displayedCollectionChangedNotification
{
- [super setup];
- self.collectionViewDelegate.itemsAspectRatio = VLCLibraryCollectionViewItemAspectRatioVideoItem;
- self.collectionViewDelegate.staticItemSize = NSMakeSize(VLCUIUnits.carouselViewVideoItemViewWidth,
- VLCUIUnits.carouselViewItemViewHeight);
- self.itemHeight = VLCUIUnits.carouselViewItemViewHeight;
- [self setupDataSource];
+ return VLCLibraryVideoCollectionViewDataSourceDisplayedCollectionChangedNotification;
}
- (void)setupDataSource
{
+ self.collectionViewDelegate.itemsAspectRatio = VLCLibraryCollectionViewItemAspectRatioVideoItem;
+ self.collectionViewDelegate.staticItemSize = NSMakeSize(VLCUIUnits.carouselViewVideoItemViewWidth,
+ VLCUIUnits.carouselViewItemViewHeight);
+ self.itemHeight = VLCUIUnits.carouselViewItemViewHeight;
_dataSource = [[VLCLibraryHomeViewVideoContainerViewDataSource alloc] init];
_dataSource.collectionViewItemIdentifier = VLCLibraryCarouselCollectionViewItemIdentifier;
self.dataSource.collectionView = self.collectionView;
@@ -65,11 +70,17 @@
_videoGroup = groupDescriptor.group;
self.dataSource.groupDescriptor = groupDescriptor;
if (groupDescriptor.group == VLCMediaLibraryParentGroupTypeRecentVideos) {
- self.titleView.stringValue = _NS("Recent videos");
+ self.headerView.title = _NS("Recent videos");
} else {
- self.titleView.stringValue = groupDescriptor.name;
+ self.headerView.title = groupDescriptor.name;
}
- }
+}
+
+- (void)openLibrary:(id)sender
+{
+ [VLCMain.sharedInstance.libraryWindow.splitViewController.navSidebarViewController
+ selectSegment:VLCLibraryVideoSegmentType];
+}
- (void)setVideoGroup:(VLCMediaLibraryParentGroupType)group
{
=====================================
modules/gui/macosx/library/home-library/VLCLibraryHomeViewVideoContainerViewDataSource.m
=====================================
@@ -27,7 +27,6 @@
#import "library/VLCLibraryCollectionViewFlowLayout.h"
#import "library/VLCLibraryCollectionViewItem.h"
#import "library/VLCLibraryCollectionViewMediaItemSupplementaryDetailView.h"
-#import "library/VLCLibraryCollectionViewSupplementaryElementView.h"
#import "library/VLCLibraryController.h"
#import "library/VLCLibraryDataTypes.h"
#import "library/VLCLibraryImageCache.h"
@@ -267,10 +266,6 @@ NSString * const VLCLibraryVideoCollectionViewDataSourceDisplayedCollectionChang
[self.collectionView registerClass:VLCLibraryCollectionViewItem.class
forItemWithIdentifier:VLCLibraryCollectionViewItemIdentifier];
- [self.collectionView registerClass:[VLCLibraryCollectionViewSupplementaryElementView class]
- forSupplementaryViewOfKind:NSCollectionElementKindSectionHeader
- withIdentifier:VLCLibrarySupplementaryElementViewIdentifier];
-
NSNib * const mediaItemSupplementaryDetailView = [[NSNib alloc] initWithNibNamed:@"VLCLibraryCollectionViewMediaItemSupplementaryDetailView" bundle:nil];
[self.collectionView registerNib:mediaItemSupplementaryDetailView
forSupplementaryViewOfKind:VLCLibraryCollectionViewMediaItemSupplementaryDetailViewKind
@@ -310,15 +305,7 @@ NSString * const VLCLibraryVideoCollectionViewDataSourceDisplayedCollectionChang
viewForSupplementaryElementOfKind:(NSCollectionViewSupplementaryElementKind)kind
atIndexPath:(NSIndexPath *)indexPath
{
- if([kind isEqualToString:NSCollectionElementKindSectionHeader]) {
- VLCLibraryCollectionViewSupplementaryElementView *sectionHeadingView = [collectionView makeSupplementaryViewOfKind:kind
- withIdentifier:VLCLibrarySupplementaryElementViewIdentifier
- forIndexPath:indexPath];
-
- sectionHeadingView.stringValue = _groupDescriptor.name;
- return sectionHeadingView;
-
- } else if ([kind isEqualToString:VLCLibraryCollectionViewMediaItemSupplementaryDetailViewKind]) {
+ if ([kind isEqualToString:VLCLibraryCollectionViewMediaItemSupplementaryDetailViewKind]) {
VLCLibraryCollectionViewMediaItemSupplementaryDetailView * const mediaItemSupplementaryDetailView = [collectionView makeSupplementaryViewOfKind:kind withIdentifier:VLCLibraryCollectionViewMediaItemSupplementaryDetailViewKind forIndexPath:indexPath];
const id<VLCMediaLibraryItemProtocol> item = self.collectionArray[indexPath.item];
=====================================
modules/gui/macosx/library/home-library/VLCLibraryHomeViewVideoGridContainerView.h
=====================================
@@ -30,6 +30,7 @@
@class VLCLibraryCollectionView;
@class VLCLibraryCollectionViewDelegate;
@class VLCLibraryCollectionViewFlowLayout;
+ at class VLCLibraryHomeViewSectionHeaderView;
@class VLCLibraryVideoCollectionViewGroupDescriptor;
NS_ASSUME_NONNULL_BEGIN
@@ -40,6 +41,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (readonly) VLCLibraryCollectionViewDelegate *collectionViewDelegate;
@property (readonly) VLCLibraryCollectionViewFlowLayout *collectionViewLayout;
@property (readonly) VLCSubScrollView *scrollView;
+ at property (readonly) VLCLibraryHomeViewSectionHeaderView *headerView;
@end
=====================================
modules/gui/macosx/library/home-library/VLCLibraryHomeViewVideoGridContainerView.m
=====================================
@@ -22,19 +22,23 @@
#import "VLCLibraryHomeViewVideoGridContainerView.h"
-#import "extensions/NSView+VLCAdditions.h"
-
#import "library/VLCLibraryCollectionView.h"
#import "library/VLCLibraryCollectionViewDelegate.h"
#import "library/VLCLibraryCollectionViewFlowLayout.h"
#import "library/VLCLibraryCollectionViewItem.h"
-#import "library/VLCLibraryCollectionViewSupplementaryElementView.h"
#import "library/VLCLibraryDataTypes.h"
+#import "library/VLCLibrarySegment.h"
+#import "library/VLCLibraryWindow.h"
+#import "library/VLCLibraryWindowNavigationSidebarViewController.h"
+#import "library/VLCLibraryWindowSplitViewController.h"
+#import "library/home-library/VLCLibraryHomeViewSectionHeaderView.h"
#import "library/home-library/VLCLibraryHomeViewVideoContainerViewDataSource.h"
#import "library/video-library/VLCLibraryVideoGroupDescriptor.h"
+#import "main/VLCMain.h"
+
#import "views/VLCSubScrollView.h"
#import "views/VLCUIUnits.h"
@@ -64,8 +68,23 @@
[self setupCollectionView];
[self setupScrollView];
+ _headerView = [[VLCLibraryHomeViewSectionHeaderView alloc] initWithFrame:NSZeroRect];
+ _headerView.translatesAutoresizingMaskIntoConstraints = NO;
+ _headerView.libraryButton.target = self;
+ _headerView.libraryButton.action = @selector(openLibrary:);
+
+ [self addSubview:_headerView];
[self addSubview:_scrollView];
- [self.scrollView applyConstraintsToFillSuperview];
+ [NSLayoutConstraint activateConstraints:@[
+ [_headerView.topAnchor constraintEqualToAnchor:self.topAnchor],
+ [_headerView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
+ [_headerView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
+ [_scrollView.topAnchor constraintEqualToAnchor:_headerView.bottomAnchor
+ constant:VLCUIUnits.mediumSpacing],
+ [_scrollView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
+ [_scrollView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
+ [_scrollView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor]
+ ]];
[self setContentHuggingPriority:NSLayoutPriorityDefaultLow
forOrientation:NSLayoutConstraintOrientationHorizontal];
@@ -81,7 +100,7 @@
- (void)setupCollectionView
{
_collectionViewLayout = VLCLibraryCollectionViewFlowLayout.standardLayout;
- _collectionViewLayout.headerReferenceSize = VLCLibraryCollectionViewSupplementaryElementView.defaultHeaderSize;
+ _collectionViewLayout.headerReferenceSize = NSZeroSize;
_collectionView = [[VLCLibraryCollectionView alloc] initWithFrame:NSZeroRect];
_collectionView.postsFrameChangedNotifications = YES;
@@ -124,19 +143,51 @@
- (void)connect
{
+ NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter;
+ [notificationCenter removeObserver:self
+ name:VLCLibraryVideoCollectionViewDataSourceDisplayedCollectionChangedNotification
+ object:self.dataSource];
+ [notificationCenter addObserver:self
+ selector:@selector(displayedCollectionChanged:)
+ name:VLCLibraryVideoCollectionViewDataSourceDisplayedCollectionChangedNotification
+ object:self.dataSource];
+ [self.dataSource disconnect];
[self.dataSource connect];
+ [self updateHeader];
}
- (void)disconnect
{
+ [NSNotificationCenter.defaultCenter removeObserver:self
+ name:VLCLibraryVideoCollectionViewDataSourceDisplayedCollectionChangedNotification
+ object:self.dataSource];
[self.dataSource disconnect];
}
+- (void)displayedCollectionChanged:(NSNotification *)notification
+{
+ [self updateHeader];
+ [self invalidateIntrinsicContentSize];
+}
+
+- (void)updateHeader
+{
+ _headerView.title = _groupDescriptor.name ?: @"";
+ _headerView.itemCount = [self.dataSource collectionView:_collectionView numberOfItemsInSection:0];
+}
+
+- (void)openLibrary:(id)sender
+{
+ [VLCMain.sharedInstance.libraryWindow.splitViewController.navSidebarViewController
+ selectSegment:VLCLibraryVideoSegmentType];
+}
+
- (void)setGroupDescriptor:(VLCLibraryVideoCollectionViewGroupDescriptor *)groupDescriptor
{
_groupDescriptor = groupDescriptor;
_videoGroup = groupDescriptor.group;
_dataSource.groupDescriptor = groupDescriptor;
+ [self updateHeader];
_collectionViewLayout.scrollDirection = _groupDescriptor.isHorizontalBarCollectionView ?
NSCollectionViewScrollDirectionHorizontal :
@@ -178,6 +229,7 @@
collectionViewLayoutInset.top +
collectionViewLayoutInset.bottom;
const CGFloat itemHeight = _collectionViewDelegate.staticItemSize.height;
+ const CGFloat headerHeight = _headerView.fittingSize.height + VLCUIUnits.mediumSpacing;
const CGFloat width = scrollViewInsets.left +
scrollViewInsets.right +
collectionViewLayoutInset.left +
@@ -188,12 +240,12 @@
// If we don't return a size larger than 0 then we run into issues with the collection
// view layout not trying to properly calculate its size. So let's return something
NSLog(@"Unable to provide accurate height for container -- providing rough size");
- const CGFloat roughValue = itemHeight + insetsHeight;
+ const CGFloat roughValue = itemHeight + insetsHeight + headerHeight;
return NSMakeSize(width, roughValue);
}
if (_groupDescriptor.isHorizontalBarCollectionView) {
- const CGFloat viewHeight = itemHeight + insetsHeight + VLCUIUnits.smallSpacing;
+ const CGFloat viewHeight = itemHeight + insetsHeight + VLCUIUnits.smallSpacing + headerHeight;
return NSMakeSize(width, viewHeight);
}
@@ -202,7 +254,7 @@
// at all. By reducing the height of the container to below the height of the
// collection view contents we can eliminate this, so we reduce the height
// just enough to not be noticeable but enough for the bug to not manifest
- return NSMakeSize(width, collectionViewContentSize.height - 15);
+ return NSMakeSize(width, collectionViewContentSize.height - 15 + headerHeight);
}
- (void)presentLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem
=====================================
modules/gui/macosx/tests/Makefile.am
=====================================
@@ -22,15 +22,23 @@ VLCMacOSXTests_SOURCES = \
gui/macosx/tests/VLCLogMessageTest.m \
gui/macosx/tests/VLCPositionFormatterTest.m \
gui/macosx/tests/VLCTimeFormatterTest.m \
+ gui/macosx/tests/VLCLibraryHomeViewSectionHeaderViewTest.m \
+ gui/macosx/tests/VLCVisualEffectViewTest.m \
gui/macosx/extensions/VLCHexNumberFormatter.m \
gui/macosx/extensions/VLCPositionFormatter.m \
gui/macosx/extensions/NSString+Helpers.m \
+ gui/macosx/extensions/NSAppearance+VLCAdditions.m \
+ gui/macosx/extensions/NSFont+VLCAdditions.m \
+ gui/macosx/extensions/NSView+VLCAdditions.m \
+ gui/macosx/library/home-library/VLCLibraryHomeViewSectionHeaderView.m \
gui/macosx/library/VLCLibraryDataTypes.m \
gui/macosx/library/VLCInputItem.m \
gui/macosx/library/VLCInputNode.m \
gui/macosx/library/media-source/VLCMediaSourceTableViewSorting.m \
gui/macosx/windows/logging/VLCLogMessage.m \
- gui/macosx/views/VLCTimeFormatter.m
+ gui/macosx/views/VLCTimeFormatter.m \
+ gui/macosx/views/VLCUIUnits.m \
+ gui/macosx/views/VLCVisualEffectView.m
noinst_HEADERS += \
gui/macosx/tests/VLCInputItemTestSupport.h \
=====================================
modules/gui/macosx/tests/VLCLibraryHomeViewSectionHeaderViewTest.m
=====================================
@@ -0,0 +1,165 @@
+/****************************************************************************
+ * VLCLibraryHomeViewSectionHeaderViewTest.m: Home header native tests
+ ****************************************************************************
+ * 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, or (at your option) any later
+ * version.
+ *****************************************************************************/
+
+#import <XCTest/XCTest.h>
+
+#import "extensions/NSString+Helpers.h"
+#import "library/home-library/VLCLibraryHomeViewSectionHeaderView.h"
+
+ at interface VLCLibraryHomeViewSectionHeaderView (TestAccess)
+ at property (readonly) NSTextField *titleField;
+ at property (readonly) NSTextField *countField;
+ at end
+
+ at interface VLCLibraryHomeViewSectionHeaderViewTest : XCTestCase
+ at property NSWindow *window;
+ at property VLCLibraryHomeViewSectionHeaderView *header;
+ at property NSLayoutConstraint *widthConstraint;
+ at property NSUInteger actionCount;
+ at property (weak) id actionSender;
+ at end
+
+ at implementation VLCLibraryHomeViewSectionHeaderViewTest
+
+- (void)setUp
+{
+ [super setUp];
+ XCTAssertTrue(NSThread.isMainThread);
+ [NSApplication sharedApplication];
+ self.window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 840, 300)
+ styleMask:NSWindowStyleMaskBorderless
+ backing:NSBackingStoreBuffered defer:NO];
+ self.window.releasedWhenClosed = NO;
+ self.header = [[VLCLibraryHomeViewSectionHeaderView alloc] initWithFrame:NSZeroRect];
+ self.header.translatesAutoresizingMaskIntoConstraints = NO;
+ self.header.title = @"Recent videos";
+ self.header.itemCount = 123;
+ [self.window.contentView addSubview:self.header];
+ self.widthConstraint = [self.header.widthAnchor constraintEqualToConstant:800];
+ [NSLayoutConstraint activateConstraints:@[
+ [self.header.leadingAnchor constraintEqualToAnchor:self.window.contentView.leadingAnchor constant:20],
+ [self.header.topAnchor constraintEqualToAnchor:self.window.contentView.topAnchor constant:20],
+ self.widthConstraint,
+ ]];
+ [self.window.contentView layoutSubtreeIfNeeded];
+}
+
+- (void)tearDown
+{
+ [self.window close];
+ self.header = nil;
+ self.widthConstraint = nil;
+ self.window = nil;
+ [super tearDown];
+}
+
+- (void)openLibrary:(id)sender
+{
+ self.actionCount++;
+ self.actionSender = sender;
+}
+
+- (void)assertContentFitsHeader
+{
+ for (NSView *view in @[self.header.titleField, self.header.countField, self.header.libraryButton]) {
+ const NSRect contentRect = [view convertRect:view.bounds toView:self.header];
+ XCTAssertTrue(NSContainsRect(self.header.bounds, contentRect));
+ XCTAssertFalse(view.hasAmbiguousLayout);
+ XCTAssertGreaterThanOrEqual(view.frame.size.height, view.intrinsicContentSize.height);
+ }
+ XCTAssertFalse(self.header.hasAmbiguousLayout);
+}
+
+- (void)testTitleIsCopiedAndUpdatesLabelAndAccessibility
+{
+ NSMutableString * const title = [@"Recent music" mutableCopy];
+ self.header.title = title;
+ [title appendString:@" changed after assignment"];
+ XCTAssertEqualObjects(self.header.title, @"Recent music");
+ XCTAssertEqualObjects(self.header.titleField.stringValue, @"Recent music");
+ XCTAssertEqualObjects(self.header.titleField.toolTip, @"Recent music");
+ NSString * const accessibilityLabel =
+ [NSString stringWithFormat:_NS("Open %@ in the library"), @"Recent music"];
+ XCTAssertEqualObjects(self.header.libraryButton.accessibilityLabel, accessibilityLabel);
+ XCTAssertEqualObjects(self.header.libraryButton.toolTip, accessibilityLabel);
+
+ self.header.title = @"Library";
+ XCTAssertEqualObjects(self.header.titleField.stringValue, @"Library");
+ NSString * const libraryAccessibilityLabel =
+ [NSString stringWithFormat:_NS("Open %@ in the library"), @"Library"];
+ XCTAssertEqualObjects(self.header.libraryButton.accessibilityLabel, libraryAccessibilityLabel);
+}
+
+- (void)testCountUpdatesSubtitleIncludingZeroAndSingular
+{
+ for (NSNumber *count in @[@0, @1, @2, @123, @1, @0]) {
+ const NSUInteger itemCount = count.unsignedIntegerValue;
+ self.header.itemCount = itemCount;
+ XCTAssertEqual(self.header.itemCount, itemCount);
+ NSString * const subtitle = _NPS("%lu item", "%lu items", (unsigned long)itemCount);
+ XCTAssertEqualObjects(self.header.countField.stringValue, subtitle);
+ }
+}
+
+- (void)testLibraryButtonDispatchesItsAction
+{
+ self.header.libraryButton.target = self;
+ self.header.libraryButton.action = @selector(openLibrary:);
+ [self.header.libraryButton performClick:nil];
+ XCTAssertEqual(self.actionCount, (NSUInteger)1);
+ XCTAssertEqual(self.actionSender, self.header.libraryButton);
+}
+
+- (void)testNarrowHeaderTruncatesTitleWithoutOverlappingButton
+{
+ self.header.title = @"A very long recent video collection section title";
+ self.widthConstraint.constant = 320;
+ [self.window.contentView layoutSubtreeIfNeeded];
+ [self assertContentFitsHeader];
+
+ const NSRect buttonRect = [self.header.libraryButton convertRect:self.header.libraryButton.bounds
+ toView:self.header];
+ for (NSTextField *label in @[self.header.titleField, self.header.countField]) {
+ const NSRect labelRect = [label convertRect:label.bounds toView:self.header];
+ XCTAssertLessThanOrEqual(NSMaxX(labelRect), NSMinX(buttonRect));
+ }
+ XCTAssertLessThan(self.header.titleField.frame.size.width,
+ self.header.titleField.intrinsicContentSize.width);
+ XCTAssertEqual(self.header.titleField.lineBreakMode, NSLineBreakByTruncatingTail);
+ XCTAssertGreaterThanOrEqual(self.header.libraryButton.frame.size.width,
+ self.header.libraryButton.intrinsicContentSize.width);
+
+ self.widthConstraint.constant = 800;
+ [self.window.contentView layoutSubtreeIfNeeded];
+ [self assertContentFitsHeader];
+}
+
+- (void)testHeightFollowsContentAndLargerFonts
+{
+ const CGFloat originalHeight = self.header.frame.size.height;
+ XCTAssertGreaterThan(originalHeight, 0);
+ XCTAssertEqualWithAccuracy(originalHeight, self.header.fittingSize.height, 0.5);
+
+ NSFont * const originalFont = self.header.titleField.font;
+ self.header.titleField.font = [NSFont boldSystemFontOfSize:originalFont.pointSize + 12];
+ [self.window.contentView layoutSubtreeIfNeeded];
+ XCTAssertGreaterThan(self.header.frame.size.height, originalHeight);
+ XCTAssertEqualWithAccuracy(self.header.frame.size.height, self.header.fittingSize.height, 0.5);
+ [self assertContentFitsHeader];
+
+ self.header.titleField.font = originalFont;
+ [self.window.contentView layoutSubtreeIfNeeded];
+ XCTAssertEqualWithAccuracy(self.header.frame.size.height, originalHeight, 0.5);
+}
+
+ at end
=====================================
modules/gui/macosx/tests/VLCVisualEffectViewTest.m
=====================================
@@ -0,0 +1,139 @@
+/****************************************************************************
+ * VLCVisualEffectViewTest.m: Visual effect native tests
+ ****************************************************************************
+ * 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, or (at your option) any later
+ * version.
+ *****************************************************************************/
+
+#import <XCTest/XCTest.h>
+
+#import "extensions/NSView+VLCAdditions.h"
+#import "views/VLCVisualEffectView.h"
+
+ at interface VLCVisualEffectViewTest : XCTestCase
+ at property NSWindow *window;
+ at end
+
+ at implementation VLCVisualEffectViewTest
+
+- (void)setUp
+{
+ [super setUp];
+ XCTAssertTrue(NSThread.isMainThread);
+ [NSApplication sharedApplication];
+}
+
+- (void)tearDown
+{
+ [self.window close];
+ self.window = nil;
+ [super tearDown];
+}
+
+- (void)testSelectsPlatformEffectAndHostsContent
+{
+ VLCVisualEffectView * const view = [[VLCVisualEffectView alloc] initWithFrame:NSZeroRect];
+ NSView * const background = view.subviews.firstObject;
+ XCTAssertNotNil(view.contentView);
+ XCTAssertTrue([view.contentView isDescendantOf:view]);
+ XCTAssertFalse(view.contentView.translatesAutoresizingMaskIntoConstraints);
+ XCTAssertEqual(view.blendingMode, NSVisualEffectBlendingModeWithinWindow);
+ if (@available(macOS 10.14, *)) {
+ XCTAssertEqual(view.material, NSVisualEffectMaterialPopover);
+ } else {
+ XCTAssertEqual(view.material, NSVisualEffectMaterialSidebar);
+ }
+
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 260000
+ if (@available(macOS 26.0, *)) {
+ XCTAssertTrue(view.usesGlassEffect);
+ XCTAssertTrue([background isKindOfClass:NSGlassEffectView.class]);
+ XCTAssertEqual(((NSGlassEffectView *)background).contentView, view.contentView);
+ XCTAssertEqual(view.cornerRadius, ((NSGlassEffectView *)background).cornerRadius);
+ } else
+#endif
+ {
+ XCTAssertFalse(view.usesGlassEffect);
+ XCTAssertTrue([background isKindOfClass:NSVisualEffectView.class]);
+ XCTAssertEqual(view.contentView.superview, background);
+ XCTAssertEqual(((NSVisualEffectView *)background).material, view.material);
+ XCTAssertEqual(((NSVisualEffectView *)background).blendingMode, view.blendingMode);
+ }
+}
+
+- (void)testContentFillsViewAfterResizingFromZeroFrame
+{
+ VLCVisualEffectView * const view = [[VLCVisualEffectView alloc] initWithFrame:NSZeroRect];
+ self.window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 640, 240)
+ styleMask:NSWindowStyleMaskBorderless
+ backing:NSBackingStoreBuffered defer:NO];
+ self.window.releasedWhenClosed = NO;
+ self.window.contentView = view;
+
+ for (NSValue *sizeValue in @[[NSValue valueWithSize:NSMakeSize(640, 240)],
+ [NSValue valueWithSize:NSMakeSize(320, 80)]]) {
+ [self.window setContentSize:sizeValue.sizeValue];
+ [view layoutSubtreeIfNeeded];
+ XCTAssertTrue(NSEqualSizes(view.bounds.size, sizeValue.sizeValue));
+ XCTAssertTrue(NSEqualRects(view.subviews.firstObject.frame, view.bounds));
+ XCTAssertTrue(NSEqualRects([view.contentView convertRect:view.contentView.bounds toView:view],
+ view.bounds));
+ XCTAssertFalse(view.contentView.hasAmbiguousLayout);
+ }
+}
+
+- (void)testFittingSizeFollowsContent
+{
+ VLCVisualEffectView * const view = [[VLCVisualEffectView alloc] initWithFrame:NSZeroRect];
+ NSTextField * const label = [NSTextField labelWithString:@"A content-sized effect view"];
+ label.translatesAutoresizingMaskIntoConstraints = NO;
+ label.font = [NSFont systemFontOfSize:18];
+ [view.contentView addSubview:label];
+ [label applyConstraintsToFillSuperview];
+
+ const NSSize originalSize = view.fittingSize;
+ XCTAssertEqualWithAccuracy(originalSize.height, label.intrinsicContentSize.height, 0.5);
+ XCTAssertEqualWithAccuracy(originalSize.width, label.intrinsicContentSize.width, 0.5);
+ label.font = [NSFont systemFontOfSize:36];
+ XCTAssertGreaterThan(view.fittingSize.height, originalSize.height);
+ XCTAssertGreaterThan(view.fittingSize.width, originalSize.width);
+ XCTAssertEqualWithAccuracy(view.fittingSize.height, label.intrinsicContentSize.height, 0.5);
+}
+
+- (void)testConfigurationReachesBackingEffect
+{
+ VLCVisualEffectView * const view = [[VLCVisualEffectView alloc] initWithFrame:NSZeroRect];
+ const NSVisualEffectMaterial material = NSVisualEffectMaterialSidebar;
+ view.material = material;
+ view.blendingMode = NSVisualEffectBlendingModeBehindWindow;
+ view.cornerRadius = 12;
+ XCTAssertEqual(view.material, material);
+ XCTAssertEqual(view.blendingMode, NSVisualEffectBlendingModeBehindWindow);
+ XCTAssertEqual(view.cornerRadius, 12);
+ NSView * const background = view.subviews.firstObject;
+
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 260000
+ if (@available(macOS 26.0, *)) {
+ XCTAssertEqual(((NSGlassEffectView *)background).cornerRadius, 12);
+ view.cornerRadius = 0;
+ XCTAssertEqual(((NSGlassEffectView *)background).cornerRadius, 0);
+ } else
+#endif
+ {
+ NSVisualEffectView * const visualEffect = (NSVisualEffectView *)background;
+ XCTAssertEqual(visualEffect.material, material);
+ XCTAssertEqual(visualEffect.blendingMode, NSVisualEffectBlendingModeBehindWindow);
+ XCTAssertEqual(visualEffect.layer.cornerRadius, 12);
+ XCTAssertTrue(visualEffect.layer.masksToBounds);
+ view.cornerRadius = 0;
+ XCTAssertEqual(visualEffect.layer.cornerRadius, 0);
+ }
+}
+
+ at end
=====================================
modules/gui/macosx/views/VLCVisualEffectView.h
=====================================
@@ -0,0 +1,41 @@
+/*****************************************************************************
+ * VLCVisualEffectView.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>
+
+NS_ASSUME_NONNULL_BEGIN
+
+// A glass background on macOS 26 and later, with a visual-effect fallback.
+// Add controls to contentView; its constraints determine this view's fitting size.
+ at interface VLCVisualEffectView : NSView
+
+ at property (readonly) NSView *contentView;
+ at property (readonly) BOOL usesGlassEffect;
+// These configure the visual-effect fallback; glass uses the system material.
+ at property (nonatomic) NSVisualEffectMaterial material;
+ at property (nonatomic) NSVisualEffectBlendingMode blendingMode;
+
+ at property (nonatomic) CGFloat cornerRadius;
+
+ at end
+
+NS_ASSUME_NONNULL_END
=====================================
modules/gui/macosx/views/VLCVisualEffectView.m
=====================================
@@ -0,0 +1,122 @@
+/*****************************************************************************
+ * VLCVisualEffectView.m: 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 "VLCVisualEffectView.h"
+
+#import "extensions/NSView+VLCAdditions.h"
+#import "main/CompatibilityFixes.h"
+
+ at interface VLCVisualEffectView ()
+
+ at property (readonly) NSView *effectView;
+ at property (readonly, nullable) NSVisualEffectView *visualEffectView;
+
+ at end
+
+ at implementation VLCVisualEffectView
+
+- (instancetype)initWithFrame:(NSRect)frameRect
+{
+ self = [super initWithFrame:frameRect];
+ if (self) {
+ [self setupView];
+ }
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+ self = [super initWithCoder:coder];
+ if (self) {
+ [self setupView];
+ }
+ return self;
+}
+
+- (void)setupView
+{
+ _contentView = [[NSView alloc] initWithFrame:self.bounds];
+ self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
+ self.wantsLayer = YES;
+ self.layer.masksToBounds = NO;
+ if (@available(macOS 14.0, *)) {
+ self.clipsToBounds = NO;
+ }
+
+ if (@available(macOS 10.14, *)) {
+ _material = NSVisualEffectMaterialPopover;
+ } else {
+ _material = NSVisualEffectMaterialSidebar;
+ }
+ _blendingMode = NSVisualEffectBlendingModeWithinWindow;
+
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 260000
+ if (@available(macOS 26.0, *)) {
+ NSGlassEffectView * const glassView = [[NSGlassEffectView alloc] initWithFrame:self.bounds];
+ glassView.contentView = self.contentView;
+ _cornerRadius = glassView.cornerRadius;
+ _effectView = glassView;
+ _usesGlassEffect = YES;
+ } else
+#endif
+ {
+ _visualEffectView = [[NSVisualEffectView alloc] initWithFrame:self.bounds];
+ self.visualEffectView.material = self.material;
+ self.visualEffectView.blendingMode = self.blendingMode;
+ self.visualEffectView.wantsLayer = YES;
+ [self.visualEffectView addSubview:self.contentView];
+ [self.contentView applyConstraintsToFillSuperview];
+ _effectView = self.visualEffectView;
+ }
+
+ self.effectView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self addSubview:self.effectView];
+ [self.effectView applyConstraintsToFillSuperview];
+}
+
+- (void)setMaterial:(NSVisualEffectMaterial)material
+{
+ _material = material;
+ self.visualEffectView.material = material;
+}
+
+- (void)setBlendingMode:(NSVisualEffectBlendingMode)blendingMode
+{
+ _blendingMode = blendingMode;
+ self.visualEffectView.blendingMode = blendingMode;
+}
+
+- (void)setCornerRadius:(CGFloat)cornerRadius
+{
+ _cornerRadius = cornerRadius;
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 260000
+ if (@available(macOS 26.0, *)) {
+ ((NSGlassEffectView *)self.effectView).cornerRadius = cornerRadius;
+ return;
+ }
+#endif
+ self.visualEffectView.layer.cornerRadius = cornerRadius;
+ self.visualEffectView.layer.masksToBounds = YES;
+ self.layer.cornerRadius = cornerRadius;
+}
+
+ at end
=====================================
po/POTFILES.in
=====================================
@@ -474,6 +474,7 @@ modules/gui/macosx/library/audio-library/VLCLibraryAlbumTableCellView.h
modules/gui/macosx/library/audio-library/VLCLibraryAlbumTableCellView.m
modules/gui/macosx/library/audio-library/VLCLibraryAudioDataSource.h
modules/gui/macosx/library/audio-library/VLCLibraryAudioDataSource.m
+modules/gui/macosx/library/home-library/VLCLibraryHomeViewSectionHeaderView.m
modules/gui/macosx/library/media-source/VLCMediaSource.h
modules/gui/macosx/library/media-source/VLCMediaSource.m
modules/gui/macosx/library/media-source/VLCMediaSourceBaseDataSource.h
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7ee44ffc93821fee267b5d35fa2b1719bb790880...f53db707da181e3f0958d3ef0a3241c940538b9e
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7ee44ffc93821fee267b5d35fa2b1719bb790880...f53db707da181e3f0958d3ef0a3241c940538b9e
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help
More information about the vlc-commits
mailing list