[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