[vlc-commits] [Git][videolan/vlc][master] 17 commits: macosx: Add starter lyrics sidebar view components

Felix Paul Kühne (@fkuehne) gitlab at videolan.org
Wed May 6 06:52:19 UTC 2026



Felix Paul Kühne pushed to branch master at VideoLAN / VLC


Commits:
206556fe by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Add starter lyrics sidebar view components

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
e58239b7 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Set up lyrics sidebar view

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
160a86bc by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Add extra meta name handling in input item

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
35255a51 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Present lyrics sidebar if the current item has sylt data

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
e6b20b73 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Present lyrics in table view with current lyric highlighted

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
802726af by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: More efficiently handle standard playback case with lyric updating

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
e3f2b783 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Add explicit typing to collection types

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
540690c2 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Make clicking lyrics move playback to time of lyric

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
f730bfc4 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Fix type mismatch errors in lyrics sidebar

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
646dbf15 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Present lyrics within audio media decorative view, rather than sidebar

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
a0633f5a by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Provide info on whether track has lyrics in player controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
b5ff79a3 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Add ability to set lyrics showing in player controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
afcca128 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Add lyrics main menu entry

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
7e634f11 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Add lyrics button to main video view

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
35fde4a3 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Check for lyrics settings in player controller in audio media decorative view

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
b0bcbf18 by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Fix lyrics insets in the main video view controller

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -
fd2acc9b by Claudio Cambra at 2026-05-06T08:37:43+02:00
macosx: Fix xib crashing on older macOS

Signed-off-by: Claudio Cambra <developer at claudiocambra.com>

- - - - -


16 changed files:

- modules/gui/macosx/UI/MainMenu.xib
- modules/gui/macosx/UI/VLCClassicMainVideoView.xib
- modules/gui/macosx/UI/VLCMainVideoView.xib
- modules/gui/macosx/UI/VLCMainVideoViewAudioMediaDecorativeView.xib
- modules/gui/macosx/library/VLCInputItem.h
- modules/gui/macosx/library/VLCInputItem.m
- modules/gui/macosx/library/VLCLibraryWindowSidebarRootViewController.m
- modules/gui/macosx/menus/VLCMainMenu.h
- modules/gui/macosx/menus/VLCMainMenu.m
- modules/gui/macosx/playqueue/VLCPlayerController.h
- modules/gui/macosx/playqueue/VLCPlayerController.m
- modules/gui/macosx/windows/controlsbar/VLCMainVideoViewControlsBar.h
- modules/gui/macosx/windows/controlsbar/VLCMainVideoViewControlsBar.m
- modules/gui/macosx/windows/video/VLCMainVideoViewAudioMediaDecorativeView.h
- modules/gui/macosx/windows/video/VLCMainVideoViewAudioMediaDecorativeView.m
- modules/gui/macosx/windows/video/VLCMainVideoViewController.m


Changes:

=====================================
modules/gui/macosx/UI/MainMenu.xib
=====================================
@@ -70,6 +70,7 @@
                 <outlet property="jumpToTime" destination="5138" id="XK6-rV-lr9"/>
                 <outlet property="libraryPlayQueueMode" destination="Zo7-ru-vku" id="uNJ-jw-lG2"/>
                 <outlet property="license" destination="2834" id="0tU-nu-6dP"/>
+                <outlet property="lyrics" destination="Lyr-Ics-Mni" id="Lyr-Ics-Out"/>
                 <outlet property="mcopyItem" destination="197" id="aBx-7l-f4e"/>
                 <outlet property="messages" destination="1003" id="q4C-pt-kQo"/>
                 <outlet property="minimize" destination="5606" id="tN3-Ho-T0c"/>
@@ -431,6 +432,11 @@
                                     <action selector="goToSpecificTime:" target="-2" id="maw-9c-f5Q"/>
                                 </connections>
                             </menuItem>
+                            <menuItem title="Lyrics" id="Lyr-Ics-Mni">
+                                <connections>
+                                    <action selector="toggleLyrics:" target="-2" id="Lyr-Ics-Con"/>
+                                </connections>
+                            </menuItem>
                             <menuItem isSeparatorItem="YES" id="5155">
                                 <modifierMask key="keyEquivalentModifierMask" command="YES"/>
                             </menuItem>


=====================================
modules/gui/macosx/UI/VLCClassicMainVideoView.xib
=====================================
@@ -38,6 +38,7 @@
                 <outlet property="fullscreenButton" destination="Dka-oG-Ar0" id="nqz-qB-etE"/>
                 <outlet property="jumpBackwardButton" destination="Y6X-Qj-wAU" id="ZDb-ZR-IDF"/>
                 <outlet property="jumpForwardButton" destination="omu-it-6Lt" id="gtC-uD-Mnu"/>
+                <outlet property="lyricsButton" destination="Lyr-Ics-Btn" id="Lyr-Ics-Out"/>
                 <outlet property="muteVolumeButton" destination="x8O-Jz-KS8" id="lIH-Am-21m"/>
                 <outlet property="pipButton" destination="ha3-L8-lMU" id="nqJ-hd-8M1"/>
                 <outlet property="playButton" destination="etJ-bh-XaL" id="3LP-if-Pah"/>
@@ -299,6 +300,16 @@
                                             <stackView distribution="fillEqually" orientation="horizontal" alignment="centerY" spacing="5" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="UCW-iN-Dk1">
                                                 <rect key="frame" x="874" y="5" width="148" height="28"/>
                                                 <subviews>
+                                                    <button wantsLayer="YES" horizontalHuggingPriority="1000" verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Lyr-Ics-Btn" customClass="VLCImageButton">
+                                                        <rect key="frame" x="0.0" y="0.0" width="48" height="28"/>
+                                                        <buttonCell key="cell" type="push" bezelStyle="rounded" image="music.note.list" catalog="system" imagePosition="only" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Lyr-Ics-Cel">
+                                                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
+                                                            <font key="font" metaFont="system"/>
+                                                        </buttonCell>
+                                                        <connections>
+                                                            <action selector="toggleLyrics:" target="3" id="Lyr-Ics-Con"/>
+                                                        </connections>
+                                                    </button>
                                                     <button wantsLayer="YES" horizontalHuggingPriority="1000" verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="ha3-L8-lMU" customClass="VLCImageButton">
                                                         <rect key="frame" x="0.0" y="0.0" width="48" height="28"/>
                                                         <buttonCell key="cell" type="push" bezelStyle="rounded" image="pip.enter" catalog="system" imagePosition="only" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="a9e-eU-lIl">
@@ -334,11 +345,13 @@
                                                     <integer value="1000"/>
                                                     <integer value="1000"/>
                                                     <integer value="1000"/>
+                                                    <integer value="1000"/>
                                                 </visibilityPriorities>
                                                 <customSpacing>
                                                     <real value="3.4028234663852886e+38"/>
                                                     <real value="3.4028234663852886e+38"/>
                                                     <real value="3.4028234663852886e+38"/>
+                                                    <real value="3.4028234663852886e+38"/>
                                                 </customSpacing>
                                             </stackView>
                                         </subviews>


=====================================
modules/gui/macosx/UI/VLCMainVideoView.xib
=====================================
@@ -48,6 +48,7 @@
                 <outlet property="fullscreenButton" destination="dYZ-ri-Kra" id="Cw2-BS-QG9"/>
                 <outlet property="jumpBackwardButton" destination="AXA-01-AU8" id="gfa-Yt-ezI"/>
                 <outlet property="jumpForwardButton" destination="aAq-uE-mLW" id="M7n-mW-tNe"/>
+                <outlet property="lyricsButton" destination="Lyr-Ics-Btn" id="Lyr-Ics-Out"/>
                 <outlet property="muteVolumeButton" destination="afi-4d-rQk" id="y6R-6o-2OM"/>
                 <outlet property="pipButton" destination="yEi-SZ-SIS" id="vFi-Ln-9lT"/>
                 <outlet property="playButton" destination="PCC-8a-sVF" id="ddT-ZM-Jhz"/>
@@ -197,6 +198,16 @@
                                                             <action selector="openSubtitlesMenu:" target="3" id="X6e-aG-mVF"/>
                                                         </connections>
                                                     </button>
+                                                    <button wantsLayer="YES" horizontalHuggingPriority="1000" verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Lyr-Ics-Btn" customClass="VLCImageButton">
+                                                        <rect key="frame" x="204" y="0.0" width="45" height="28"/>
+                                                        <buttonCell key="cell" type="recessed" bezelStyle="recessed" image="music.note.list" catalog="system" imagePosition="only" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Lyr-Ics-Cel">
+                                                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
+                                                            <font key="font" metaFont="system"/>
+                                                        </buttonCell>
+                                                        <connections>
+                                                            <action selector="toggleLyrics:" target="3" id="Lyr-Ics-Con"/>
+                                                        </connections>
+                                                    </button>
                                                     <button wantsLayer="YES" horizontalHuggingPriority="1000" verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="4tZ-52-1q9" customClass="VLCImageButton">
                                                         <rect key="frame" x="204" y="0.0" width="42" height="28"/>
                                                         <buttonCell key="cell" type="recessed" bezelStyle="recessed" image="bookmark.fill" catalog="system" imagePosition="only" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bGc-fn-jgQ">
@@ -256,6 +267,7 @@
                                                     <integer value="1000"/>
                                                     <integer value="1000"/>
                                                     <integer value="1000"/>
+                                                    <integer value="1000"/>
                                                 </visibilityPriorities>
                                                 <customSpacing>
                                                     <real value="3.4028234663852886e+38"/>
@@ -266,6 +278,7 @@
                                                     <real value="3.4028234663852886e+38"/>
                                                     <real value="3.4028234663852886e+38"/>
                                                     <real value="3.4028234663852886e+38"/>
+                                                    <real value="3.4028234663852886e+38"/>
                                                 </customSpacing>
                                             </stackView>
                                             <stackView distribution="fill" orientation="horizontal" alignment="centerY" spacing="5" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zyp-45-IgR">


=====================================
modules/gui/macosx/UI/VLCMainVideoViewAudioMediaDecorativeView.xib
=====================================
@@ -30,6 +30,73 @@
                     </shadow>
                     <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="noart" id="m7g-tH-8dl"/>
                 </imageView>
+                <scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4vZ-4x-fwW">
+                    <rect key="frame" x="0.0" y="0.0" width="316" height="336"/>
+                    <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="H6H-Cp-iPh">
+                        <rect key="frame" x="0.0" y="0.0" width="316" height="336"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="firstColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" rowSizeStyle="automatic" viewBased="YES" id="5mv-Qc-JwM" customClass="VLCPlayQueueTableView">
+                                <rect key="frame" x="0.0" y="0.0" width="316" height="336"/>
+                                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                <size key="intercellSpacing" width="3" height="2"/>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                                <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
+                                <tableColumns>
+                                    <tableColumn identifier="VLCMainVideoViewLyricsTableViewColumnIdentifier" editable="NO" width="284" minWidth="100" maxWidth="2000" id="9yW-DI-c08">
+                                        <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Lyric">
+                                            <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+                                            <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
+                                        </tableHeaderCell>
+                                        <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="JOU-jI-2dC">
+                                            <font key="font" metaFont="message"/>
+                                            <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                            <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                        </textFieldCell>
+                                        <tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/>
+                                        <prototypeCellViews>
+                                            <tableCellView identifier="LyricCellIdentifier" id="ZTp-fs-5fE">
+                                                <rect key="frame" x="11" y="1" width="293" height="44"/>
+                                                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                                <subviews>
+                                                    <textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Wdd-df-zUi">
+                                                        <rect key="frame" x="0.0" y="0.0" width="293" height="44"/>
+                                                        <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="Lyric Line" id="deP-dG-o27">
+                                                            <font key="font" metaFont="message"/>
+                                                            <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                                                        </textFieldCell>
+                                                    </textField>
+                                                </subviews>
+                                                <constraints>
+                                                    <constraint firstItem="Wdd-df-zUi" firstAttribute="leading" secondItem="ZTp-fs-5fE" secondAttribute="leading" id="l-lead"/>
+                                                    <constraint firstItem="Wdd-df-zUi" firstAttribute="trailing" secondItem="ZTp-fs-5fE" secondAttribute="trailing" id="l-trail"/>
+                                                    <constraint firstItem="Wdd-df-zUi" firstAttribute="top" secondItem="ZTp-fs-5fE" secondAttribute="top" id="l-top"/>
+                                                    <constraint firstItem="Wdd-df-zUi" firstAttribute="bottom" secondItem="ZTp-fs-5fE" secondAttribute="bottom" id="l-bot"/>
+                                                </constraints>
+                                                <connections>
+                                                    <outlet property="textField" destination="Wdd-df-zUi" id="61g-Ze-nvR"/>
+                                                </connections>
+                                            </tableCellView>
+                                        </prototypeCellViews>
+                                    </tableColumn>
+                                </tableColumns>
+                                <connections>
+                                    <action selector="tableViewAction:" target="WRu-Ic-lQK" id="conn-action-001"/>
+                                </connections>
+                            </tableView>
+                        </subviews>
+                        <nil key="backgroundColor"/>
+                    </clipView>
+                    <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="L1S-02-OQF">
+                        <rect key="frame" x="-100" y="-100" width="0.0" height="16"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </scroller>
+                    <scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="IJr-bf-Dsz">
+                        <rect key="frame" x="300" y="0.0" width="16" height="336"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </scroller>
+                </scrollView>
             </subviews>
             <constraints>
                 <constraint firstItem="40P-sL-Mzq" firstAttribute="centerX" secondItem="WRu-Ic-lQK" secondAttribute="centerX" id="4Ib-c0-egD"/>
@@ -45,12 +112,18 @@
                 <constraint firstAttribute="bottom" secondItem="D1e-fS-bwO" secondAttribute="bottom" id="toU-5j-lin"/>
                 <constraint firstAttribute="trailing" secondItem="cDv-dn-65H" secondAttribute="trailing" id="vEl-zP-vj9"/>
                 <constraint firstItem="40P-sL-Mzq" firstAttribute="centerY" secondItem="WRu-Ic-lQK" secondAttribute="centerY" priority="250" id="yFl-gc-ITT"/>
+                <constraint firstAttribute="trailing" secondItem="4vZ-4x-fwW" secondAttribute="trailing" id="Vo4-sT-MAu"/>
+                <constraint firstAttribute="bottom" secondItem="4vZ-4x-fwW" secondAttribute="bottom" id="aRK-vR-wkV"/>
+                <constraint firstItem="4vZ-4x-fwW" firstAttribute="top" secondItem="WRu-Ic-lQK" secondAttribute="top" id="ran-Af-QyV"/>
+                <constraint firstItem="4vZ-4x-fwW" firstAttribute="leading" secondItem="WRu-Ic-lQK" secondAttribute="leading" id="ran-Af-QyW"/>
             </constraints>
             <connections>
                 <outlet property="backgroundCoverArtView" destination="cDv-dn-65H" id="bw2-8i-Xg6"/>
                 <outlet property="backgroundVisualEffectView" destination="D1e-fS-bwO" id="Jkh-ep-Wyn"/>
                 <outlet property="foregroundCoverArtView" destination="40P-sL-Mzq" id="nVl-ga-BoM"/>
                 <outlet property="foregroundViewTopConstraint" destination="TVt-Xt-aoo" id="OFh-5C-XZF"/>
+                <outlet property="lyricsScrollView" destination="4vZ-4x-fwW" id="lyr-scroll-conn"/>
+                <outlet property="lyricsTableView" destination="5mv-Qc-JwM" id="lyr-table-conn"/>
             </connections>
             <point key="canvasLocation" x="66" y="-232"/>
         </customView>


=====================================
modules/gui/macosx/library/VLCInputItem.h
=====================================
@@ -77,6 +77,9 @@ extern NSString * const VLCInputItemCommonDataDifferingFlagString;
 @property (readonly) BOOL preparsed;
 @property (readonly) BOOL isStream;
 @property (readonly, nullable) NSArray<NSString *> *options;
+ at property (readonly) NSArray<NSString *> *extraMetaNames;
+
+- (nullable NSString *)extraMetaForKey:(NSString *)key;
 
 - (void)parseInputItem;
 - (void)cancelParsing;


=====================================
modules/gui/macosx/library/VLCInputItem.m
=====================================
@@ -372,6 +372,32 @@ static const struct input_item_parser_cbs_t parserCallbacks =
     return @"";
 }
 
+- (NSArray<NSString *> *)extraMetaNames
+{
+    char **ppsz_names;
+    const unsigned i_count = input_item_GetMetaExtraNames(_vlcInputItem, &ppsz_names);
+    if (i_count == 0) {
+        return @[];
+    }
+
+    NSMutableArray * const names = [NSMutableArray arrayWithCapacity:i_count];
+    for (unsigned i = 0; i < i_count; i++) {
+        [names addObject:toNSStr(ppsz_names[i])];
+        free(ppsz_names[i]);
+    }
+    free(ppsz_names);
+
+    return [names copy];
+}
+
+- (nullable NSString *)extraMetaForKey:(NSString *)key
+{
+    char * const psz_value = input_item_GetMetaExtra(_vlcInputItem, [key UTF8String]);
+    NSString * const returnValue = toNSStr(psz_value);
+    free(psz_value);
+    return returnValue;
+}
+
 - (vlc_tick_t)duration
 {
     return _vlcInputItem->i_duration;


=====================================
modules/gui/macosx/library/VLCLibraryWindowSidebarRootViewController.m
=====================================
@@ -32,6 +32,7 @@
 
 #import "library/VLCLibraryUIUnits.h"
 #import "library/VLCLibraryWindow.h"
+#import "library/VLCInputItem.h"
 #import "library/VLCLibraryWindowChaptersSidebarViewController.h"
 #import "library/VLCLibraryWindowPlayQueueSidebarViewController.h"
 #import "library/VLCLibraryWindowSidebarChildViewController.h"
@@ -89,6 +90,10 @@
                            selector:@selector(titleListChanged:)
                                name:VLCPlayerTitleListChanged
                              object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(mediaItemChanged:)
+                               name:VLCPlayerCurrentMediaItemChanged
+                             object:nil];
 }
 
 - (void)setupPlayQueueTitle
@@ -114,10 +119,11 @@
 {
     self.viewSelector.segmentCount = 1;
     [self.viewSelector setLabel:self.playQueueSidebarViewController.title
-                     forSegment:self.viewSelector.segmentCount - 1];
+                     forSegment:0];
 
     VLCPlayQueueController * const playQueueController = VLCMain.sharedInstance.playQueueController;
     VLCPlayerController * const playerController = playQueueController.playerController;
+
     if (playerController.numberOfTitlesOfCurrentMedia > 0) {
         self.viewSelector.segmentCount++; 
         [self.viewSelector setLabel:self.titlesSidebarViewController.title
@@ -160,37 +166,44 @@
     [self updateViewSelectorState];
 }
 
+- (void)mediaItemChanged:(NSNotification *)notification
+{
+    [self updateViewSelectorState];
+}
+
 - (void)updateViewSelectorState
 {
     [self setupViewSelectorSegments];
 
-    VLCPlayQueueController * const playQueueController = VLCMain.sharedInstance.playQueueController;
-    VLCPlayerController * const playerController = playQueueController.playerController;
-    const BOOL titlesEnabled = playerController.numberOfTitlesOfCurrentMedia > 0;
-    const BOOL chaptersEnabled = playerController.numberOfChaptersForCurrentTitle > 0;
-    
-    self.viewSelector.hidden = !chaptersEnabled && !titlesEnabled;
-    self.topInternalConstraint.active = !self.viewSelector.hidden;
+    const BOOL showSelector = self.viewSelector.segmentCount > 1;
+    self.viewSelector.hidden = !showSelector;
+    self.topInternalConstraint.active = showSelector;
 
+    self.playQueueHeaderLabel.hidden = showSelector;
     const NSLayoutPriority playQueueCompressionPriority =
-        self.viewSelector.hidden ? NSLayoutPriorityDefaultHigh : NSLayoutPriorityRequired;
-    self.playQueueHeaderLabel.hidden = chaptersEnabled;
+        showSelector ? NSLayoutPriorityRequired : NSLayoutPriorityDefaultHigh;
     [self.playQueueHeaderLabel setContentCompressionResistancePriority:playQueueCompressionPriority
                                                         forOrientation:NSLayoutConstraintOrientationVertical];
-    self.playQueueHeaderTopConstraint.active = !self.playQueueHeaderLabel.hidden;
-
-    NSLayoutConstraint * const counterLabelConstraintToActivate = self.viewSelector.hidden
-        ? self.counterLabelInHeaderConstraint
-        : self.counterLabelInChildViewConstraint;
-    NSLayoutConstraint * const counterLabelConstraintToDeactivate = self.viewSelector.hidden
-        ? self.counterLabelInChildViewConstraint
-        : self.counterLabelInHeaderConstraint;
-    counterLabelConstraintToActivate.active = YES;
-    counterLabelConstraintToDeactivate.active = NO;
-    
+    self.playQueueHeaderTopConstraint.active = !showSelector;
+
+    if (showSelector) {
+        self.counterLabelInHeaderConstraint.active = NO;
+        self.counterLabelInChildViewConstraint.active = YES;
+    } else {
+        self.counterLabelInHeaderConstraint.active = YES;
+        self.counterLabelInChildViewConstraint.active = NO;
+    }
+
     NSString * const selectedSegmentLabel = [self.viewSelector labelForSegment:self.viewSelector.selectedSegment];
-    if ((!chaptersEnabled && [selectedSegmentLabel isEqualToString:self.chaptersSidebarViewController.title]) ||
-        (!titlesEnabled && [selectedSegmentLabel isEqualToString:self.titlesSidebarViewController.title])) {
+    BOOL selectedSegmentValid = NO;
+    for (NSInteger i = 0; i < self.viewSelector.segmentCount; i++) {
+        if ([[self.viewSelector labelForSegment:i] isEqualToString:selectedSegmentLabel]) {
+            selectedSegmentValid = YES;
+            break;
+        }
+    }
+
+    if (!selectedSegmentValid) {
         self.viewSelector.selectedSegment = 0;
         [self viewSelectorAction:self.viewSelector];
     }


=====================================
modules/gui/macosx/menus/VLCMainMenu.h
=====================================
@@ -89,6 +89,7 @@
 @property (readwrite, weak) IBOutlet NSMenuItem *random;
 @property (readwrite, weak) IBOutlet NSMenuItem *repeat;
 @property (readwrite, weak) IBOutlet NSMenuItem *AtoBloop;
+ at property (readwrite, weak) IBOutlet NSMenuItem *lyrics;
 @property (readwrite, weak) IBOutlet NSMenuItem *libraryPlayQueueMode;
 @property (readwrite, weak) IBOutlet NSMenuItem *sortPlayQueue;
 @property (readwrite, weak) IBOutlet NSMenuItem *quitAfterPB;


=====================================
modules/gui/macosx/menus/VLCMainMenu.m
=====================================
@@ -385,6 +385,7 @@ typedef NS_ENUM(NSInteger, VLCObjectType) {
     [_random setTitle: _NS("Random")];
     [_repeat setTitle: _NS("Repeat")];
     [_AtoBloop setTitle: _NS("A→B Loop")];
+    [_lyrics setTitle: _NS("Lyrics")];
     [_libraryPlayQueueMode setTitle: _NS("Library Play Queue Mode")];
     [_sortPlayQueue setTitle: _NS("Sort Play Queue")];
     [_quitAfterPB setTitle: _NS("Quit after Playback")];
@@ -884,6 +885,11 @@ typedef NS_ENUM(NSInteger, VLCObjectType) {
     [_playerController setABLoop];
 }
 
+- (IBAction)toggleLyrics:(id)sender
+{
+    _playerController.showLyrics = !_playerController.showLyrics;
+}
+
 - (IBAction)toggleLibraryPlayQueueMode:(id)sender
 {
     _playQueueController.libraryPlayQueueMode = !_playQueueController.libraryPlayQueueMode;
@@ -1944,6 +1950,9 @@ typedef NS_ENUM(NSInteger, VLCObjectType) {
         mi.state = state ? NSOnState : NSOffState;
     } else if (mi == self.fwd || mi == self.bwd || mi == self.jumpToTime) {
         return _playerController.currentMedia != nil && _playerController.seekable;
+    } else if (mi == self.lyrics) {
+        mi.state = _playerController.showLyrics ? NSOnState : NSOffState;
+        return _playerController.lyricsAvailable;
     } else if (mi == self.mute || mi == self.dockMenumute || mi == self.voutMenumute) {
         mi.state = _playerController.mute ? NSOnState : NSOffState;
         [self refreshAudioDeviceList];


=====================================
modules/gui/macosx/playqueue/VLCPlayerController.h
=====================================
@@ -242,6 +242,18 @@ extern NSString *VLCPlayerVolumeChanged;
  */
 extern NSString *VLCPlayerMuteChanged;
 
+/**
+ * Listen to VLCPlayerLyricsAvailableChanged to be notified if the current media has synchronized lyrics available
+ * @note the affected player object will be the object of the notification
+ */
+extern NSString * const VLCPlayerLyricsAvailableChanged;
+
+/**
+ * Listen to VLCPlayerShowLyricsChanged to be notified if the show lyrics user preference changes
+ * @note the affected player object will be the object of the notification
+ */
+extern NSString * const VLCPlayerShowLyricsChanged;
+
 extern const CGFloat VLCVolumeMaximum;
 extern const CGFloat VLCVolumeDefault;
 
@@ -346,6 +358,16 @@ extern const CGFloat VLCVolumeDefault;
 
 @property (readonly) BOOL currentMediaIsAudioOnly;
 
+/**
+ * user preference to show synchronized lyrics instead of artwork during audio playback
+ */
+ at property (readwrite, nonatomic) BOOL showLyrics;
+
+/**
+ * indicates if the currently playing media has synchronized lyrics available
+ */
+ at property (readonly) BOOL lyricsAvailable;
+
 /**
  * the current player state
  * @return a value according to the vlc_player_state enum


=====================================
modules/gui/macosx/playqueue/VLCPlayerController.m
=====================================
@@ -82,6 +82,10 @@ NSString *VLCPlayerWallpaperModeChanged = @"VLCPlayerWallpaperModeChanged";
 NSString *VLCPlayerListOfVideoOutputThreadsChanged = @"VLCPlayerListOfVideoOutputThreadsChanged";
 NSString *VLCPlayerVolumeChanged = @"VLCPlayerVolumeChanged";
 NSString *VLCPlayerMuteChanged = @"VLCPlayerMuteChanged";
+NSString * const VLCPlayerLyricsAvailableChanged = @"VLCPlayerLyricsAvailableChanged";
+NSString * const VLCPlayerShowLyricsChanged = @"VLCPlayerShowLyricsChanged";
+
+NSString * const VLCPlayerShowLyricsKey = @"VLCPlayerShowLyricsKey";
 
 const CGFloat VLCVolumeMaximum = 2.;
 const CGFloat VLCVolumeDefault = 1.;
@@ -673,6 +677,8 @@ static int BossCallback(vlc_object_t *p_this,
 
         _playbackRate = 1.0;
 
+        _showLyrics = [NSUserDefaults.standardUserDefaults boolForKey:VLCPlayerShowLyricsKey];
+
         libvlc_int_t *libvlc = vlc_object_instance(getIntf());
         var_AddCallback(libvlc, "intf-boss", BossCallback, (__bridge void *)self);
     }
@@ -737,6 +743,19 @@ static int BossCallback(vlc_object_t *p_this,
     [_defaultNotificationCenter removeObserver:self];
 }
 
+- (void)setShowLyrics:(BOOL)showLyrics
+{
+    if (_showLyrics == showLyrics) {
+        return;
+    }
+
+    _showLyrics = showLyrics;
+    [NSUserDefaults.standardUserDefaults setBool:showLyrics forKey:VLCPlayerShowLyricsKey];
+
+    [_defaultNotificationCenter postNotificationName:VLCPlayerShowLyricsChanged
+                                              object:self];
+}
+
 - (VLCInputItem *)currentMedia
 {
     if (_currentMedia)
@@ -818,6 +837,18 @@ static int BossCallback(vlc_object_t *p_this,
 
 - (void)metaDataChangedForInput:(input_item_t *)inputItem
 {
+    NSString *syltData = nil;
+    if (_currentMedia) {
+        syltData = [_currentMedia extraMetaForKey:@"sylt-data"];
+    }
+
+    const BOOL lyricsAvailable = (syltData && syltData.length > 0);
+    if (_lyricsAvailable != lyricsAvailable) {
+        _lyricsAvailable = lyricsAvailable;
+        [_defaultNotificationCenter postNotificationName:VLCPlayerLyricsAvailableChanged
+                                                  object:self];
+    }
+
     [_defaultNotificationCenter postNotificationName:VLCPlayerMetadataChangedForCurrentMedia
                                               object:self];
 }
@@ -834,6 +865,19 @@ static int BossCallback(vlc_object_t *p_this,
 - (void)currentMediaItemChanged:(input_item_t *)newMediaItem
 {
     _currentMedia = [[VLCInputItem alloc] initWithInputItem:newMediaItem];
+
+    NSString *syltData = nil;
+    if (_currentMedia) {
+        syltData = [_currentMedia extraMetaForKey:@"sylt-data"];
+    }
+
+    const BOOL lyricsAvailable = (syltData && syltData.length > 0);
+    if (_lyricsAvailable != lyricsAvailable) {
+        _lyricsAvailable = lyricsAvailable;
+        [_defaultNotificationCenter postNotificationName:VLCPlayerLyricsAvailableChanged
+                                                  object:self];
+    }
+
     [_defaultNotificationCenter postNotificationName:VLCPlayerCurrentMediaItemChanged object:self];
 }
 


=====================================
modules/gui/macosx/windows/controlsbar/VLCMainVideoViewControlsBar.h
=====================================
@@ -30,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN
 @property (readwrite, weak) IBOutlet NSButton *subtitlesButton;
 @property (readwrite, weak) IBOutlet NSButton *audioButton;
 @property (readwrite, weak) IBOutlet NSButton *videoButton;
+ at property (readwrite, weak) IBOutlet NSButton *lyricsButton;
 @property (readwrite, weak) IBOutlet NSButton *floatOnTopButton;
 @property (readwrite, weak) IBOutlet NSButton *playbackRateButton;
 
@@ -39,6 +40,7 @@ NS_ASSUME_NONNULL_BEGIN
 - (IBAction)openAudioMenu:(id)sender;
 - (IBAction)openVideoMenu:(id)sender;
 - (IBAction)toggleFloatOnTop:(id)sender;
+- (IBAction)toggleLyrics:(id)sender;
 
 @end
 


=====================================
modules/gui/macosx/windows/controlsbar/VLCMainVideoViewControlsBar.m
=====================================
@@ -68,6 +68,9 @@
     self.videoButton.toolTip = _NS("Video");
     self.videoButton.accessibilityLabel = self.videoButton.toolTip;
 
+    self.lyricsButton.toolTip = _NS("Lyrics");
+    self.lyricsButton.accessibilityLabel = self.lyricsButton.toolTip;
+
     self.playbackRateButton.toolTip = _NS("Playback Rate");
     self.playbackRateButton.accessibilityLabel = self.playbackRateButton.toolTip;
 
@@ -91,6 +94,7 @@
         [buttons addPointer:(__bridge void *)self.subtitlesButton];
         [buttons addPointer:(__bridge void *)self.audioButton];
         [buttons addPointer:(__bridge void *)self.videoButton];
+        [buttons addPointer:(__bridge void *)self.lyricsButton];
         [buttons addPointer:(__bridge void *)self.fullscreenButton];
         [buttons addPointer:(__bridge void *)self.floatOnTopButton];
         [buttons addPointer:(__bridge void *)self.playbackRateButton];
@@ -133,6 +137,14 @@
                            selector:@selector(updateAvailableButtons:)
                                name:VLCPlayerCurrentMediaItemChanged
                              object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(updateLyricsButton:)
+                               name:VLCPlayerShowLyricsChanged
+                             object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(updateLyricsButton:)
+                               name:VLCPlayerLyricsAvailableChanged
+                             object:nil];
 
     [self update];
 }
@@ -142,6 +154,7 @@
     [super update];
     [self updateFloatOnTopButton];
     [self updatePlaybackRateButton];
+    [self updateLyricsButton:nil];
 }
 
 - (void)floatOnTopChanged:(NSNotification *)notification
@@ -149,6 +162,21 @@
     [self updateFloatOnTopButton];
 }
 
+- (void)updateLyricsButton:(NSNotification *)notification
+{
+    const BOOL lyricsAvailable = _playerController.lyricsAvailable;
+    self.lyricsButton.enabled = lyricsAvailable;
+    self.lyricsButton.state = _playerController.showLyrics ? NSControlStateValueOn : NSControlStateValueOff;
+
+    if (@available(macOS 26.0, *)) {
+        self.lyricsButton.bezelColor =
+            _playerController.showLyrics ? NSColor.controlAccentColor : nil;
+    } else if (@available(macOS 10.14, *)) {
+        self.lyricsButton.contentTintColor =
+            _playerController.showLyrics ? NSColor.controlAccentColor : nil;
+    }
+}
+
 - (vout_thread_t *)windowVoutThread
 {
     NSWindow * const window = self.floatOnTopButton.window;
@@ -277,6 +305,11 @@
     vout_Release(p_vout);
 }
 
+- (IBAction)toggleLyrics:(id)sender
+{
+    _playerController.showLyrics = !_playerController.showLyrics;
+}
+
 - (void)updateAvailableButtons:(id)sender
 {
     const BOOL currentItemIsAudio = _playerController.currentMediaIsAudioOnly;


=====================================
modules/gui/macosx/windows/video/VLCMainVideoViewAudioMediaDecorativeView.h
=====================================
@@ -33,6 +33,9 @@ NS_ASSUME_NONNULL_BEGIN
 @property (readwrite, strong) IBOutlet NSVisualEffectView *backgroundVisualEffectView;
 @property (readwrite, strong) IBOutlet NSLayoutConstraint *foregroundViewTopConstraint;
 
+ at property (readwrite, weak) IBOutlet NSScrollView *lyricsScrollView;
+ at property (readwrite, weak) IBOutlet NSTableView *lyricsTableView;
+
 @end
 
 NS_ASSUME_NONNULL_END


=====================================
modules/gui/macosx/windows/video/VLCMainVideoViewAudioMediaDecorativeView.m
=====================================
@@ -22,16 +22,26 @@
 
 #import "VLCMainVideoViewAudioMediaDecorativeView.h"
 
+#import "extensions/NSColor+VLCAdditions.h"
+#import "extensions/NSFont+VLCAdditions.h"
 #import "extensions/NSView+VLCAdditions.h"
 
 #import "main/VLCMain.h"
 
 #import "library/VLCLibraryDataTypes.h"
 #import "library/VLCLibraryImageCache.h"
+#import "library/VLCInputItem.h"
 
 #import "playqueue/VLCPlayQueueController.h"
 #import "playqueue/VLCPlayerController.h"
 
+ at interface VLCMainVideoViewAudioMediaDecorativeView () <NSTableViewDataSource, NSTableViewDelegate>
+
+ at property (readwrite) NSArray<NSDictionary<NSString *, id> *> *lyricsEntries;
+ at property (readwrite) NSInteger currentLyricIndex;
+
+ at end
+
 @implementation VLCMainVideoViewAudioMediaDecorativeView
 
 + (instancetype)fromNibWithOwner:(id)owner
@@ -43,17 +53,51 @@
 
 - (void)awakeFromNib
 {
+    _lyricsEntries = @[];
+    _currentLyricIndex = -1;
+
+    self.lyricsTableView.dataSource = self;
+    self.lyricsTableView.delegate = self;
+    self.lyricsTableView.headerView = nil;
+    self.lyricsTableView.backgroundColor = [NSColor clearColor];
+    self.lyricsTableView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
+
     NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter;
     [notificationCenter addObserver:self
-                           selector:@selector(playerCurrentMediaItemChanged:)
+                           selector:@selector(playerMetadataChanged:)
+                               name:VLCPlayerMetadataChangedForCurrentMedia
+                             object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(playerMetadataChanged:)
                                name:VLCPlayerCurrentMediaItemChanged
                              object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(playerTimeChanged:)
+                               name:VLCPlayerTimeAndPositionChanged
+                             object:nil];
+    [notificationCenter addObserver:self
+                           selector:@selector(playerLyricsPreferenceChanged:)
+                               name:VLCPlayerShowLyricsChanged
+                             object:nil];
+
     [self updateCoverArt];
+    [self updateLyricsData];
 }
 
-- (void)playerCurrentMediaItemChanged:(NSNotification *)notification
+- (void)playerMetadataChanged:(NSNotification *)notification
 {
     [self updateCoverArt];
+    [self updateLyricsData];
+}
+
+- (void)playerLyricsPreferenceChanged:(NSNotification *)notification
+{
+    [self updateLyricsData];
+}
+
+- (void)playerTimeChanged:(NSNotification *)notification
+{
+    [self updateCurrentLyric];
 }
 
 - (void)updateCoverArt
@@ -79,4 +123,183 @@
     _foregroundCoverArtView.image = coverArtImage;
 }
 
+- (void)updateLyricsData
+{
+    VLCPlayerController * const playerController =
+        VLCMain.sharedInstance.playQueueController.playerController;
+
+    NSString *syltData = nil;
+    if (playerController.showLyrics && playerController.lyricsAvailable) {
+        syltData = [playerController.currentMedia extraMetaForKey:@"sylt-data"];
+    }
+
+    if (!syltData || syltData.length == 0) {
+        self.lyricsEntries = @[];
+        self.currentLyricIndex = -1;
+        [self.lyricsTableView reloadData];
+        self.lyricsScrollView.hidden = YES;
+        self.foregroundCoverArtView.hidden = NO;
+        return;
+    }
+
+    NSMutableArray * const entries = [NSMutableArray array];
+    NSArray<NSString *> * const rawEntries = [syltData componentsSeparatedByString:@"\x1E"];
+    for (NSString * const rawEntry in rawEntries) {
+        if (rawEntry.length == 0) {
+            continue;
+        }
+        NSArray<NSString *> * const parts = [rawEntry componentsSeparatedByString:@"\x1F"];
+        if (parts.count >= 2) {
+            NSDictionary<NSString *, id> * const entry = @{
+                @"time": @([parts[0] longLongValue]),
+                @"text": parts[1]
+            };
+            [entries addObject:entry];
+        }
+    }
+
+    self.lyricsEntries = [entries copy];
+    self.currentLyricIndex = -1;
+    [self.lyricsTableView reloadData];
+
+    const BOOL hasLyrics = self.lyricsEntries.count > 0;
+    self.lyricsScrollView.hidden = !hasLyrics;
+    self.foregroundCoverArtView.hidden = hasLyrics;
+
+    [self updateCurrentLyric];
+}
+
+- (void)updateCurrentLyric
+{
+    const NSInteger lyricCount = (NSInteger)self.lyricsEntries.count;
+    if (lyricCount == 0) {
+        return;
+    }
+
+    VLCPlayerController * const playerController =
+        VLCMain.sharedInstance.playQueueController.playerController;
+    const vlc_tick_t currentTime = playerController.time;
+    const long long currentTimeMs = MS_FROM_VLC_TICK(currentTime);
+
+    // 1. Check if we are still on the same lyric (most frequent case)
+    if (self.currentLyricIndex >= 0 && self.currentLyricIndex < lyricCount) {
+        const long long startTime =
+            [self.lyricsEntries[self.currentLyricIndex][@"time"] longLongValue];
+        const BOOL isLast = (self.currentLyricIndex == lyricCount - 1);
+        const long long nextStartTime = isLast
+            ? LLONG_MAX
+            : [self.lyricsEntries[self.currentLyricIndex + 1][@"time"] longLongValue];
+
+        if (currentTimeMs >= startTime && currentTimeMs < nextStartTime) {
+            return;
+        }
+
+        // 2. Check if it's simply the next one (normal linear playback transition)
+        if (!isLast && currentTimeMs >= nextStartTime) {
+            const NSInteger nextIndex = self.currentLyricIndex + 1;
+            const BOOL isNextLast = (nextIndex == lyricCount - 1);
+            const long long nextNextStartTime = isNextLast
+                ? LLONG_MAX
+                : [self.lyricsEntries[nextIndex + 1][@"time"] longLongValue];
+
+            if (currentTimeMs < nextNextStartTime) {
+                [self updateToLyricIndex:nextIndex];
+                return;
+            }
+        }
+    }
+
+    // 3. Fallback to search (for seeks, jumps, or initial playback)
+    NSInteger foundIndex = -1;
+    for (NSInteger i = 0; i < lyricCount; i++) {
+        if ([self.lyricsEntries[i][@"time"] longLongValue] <= currentTimeMs) {
+            foundIndex = i;
+        } else {
+            break;
+        }
+    }
+
+    if (foundIndex != self.currentLyricIndex) {
+        [self updateToLyricIndex:foundIndex];
+    }
+}
+
+- (void)updateToLyricIndex:(NSInteger)newIndex
+{
+    const NSInteger lyricCount = (NSInteger)self.lyricsEntries.count;
+    NSMutableIndexSet * const indicesToUpdate = [NSMutableIndexSet indexSet];
+    if (self.currentLyricIndex != -1 && self.currentLyricIndex < lyricCount) {
+        [indicesToUpdate addIndex:self.currentLyricIndex];
+    }
+
+    self.currentLyricIndex = newIndex;
+
+    if (self.currentLyricIndex != -1 && self.currentLyricIndex < lyricCount) {
+        [indicesToUpdate addIndex:self.currentLyricIndex];
+    };
+
+    if (indicesToUpdate.count > 0) {
+        [self.lyricsTableView noteHeightOfRowsWithIndexesChanged:indicesToUpdate];
+        [self.lyricsTableView reloadDataForRowIndexes:indicesToUpdate
+                                         columnIndexes:[NSIndexSet indexSetWithIndex:0]];
+    }
+
+    if (self.currentLyricIndex != -1) {
+        [self.lyricsTableView scrollRowToVisible:self.currentLyricIndex];
+    }
+}
+
+#pragma mark - Table View Data Source
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
+{
+    return (NSInteger)self.lyricsEntries.count;
+}
+
+#pragma mark - Table View Delegate
+
+- (NSView *)tableView:(NSTableView *)tableView
+   viewForTableColumn:(NSTableColumn *)tableColumn 
+                  row:(NSInteger)row
+{
+    NSTableCellView * const cellView =
+        [tableView makeViewWithIdentifier:@"LyricCellIdentifier" owner:self];
+    if (row < 0 || row >= (NSInteger)self.lyricsEntries.count) {
+        return cellView;
+    }
+
+    NSString * const text = self.lyricsEntries[row][@"text"];
+    cellView.textField.stringValue = text;
+
+    if (row == self.currentLyricIndex) {
+        cellView.textField.font = [NSFont boldSystemFontOfSize:22.0];
+        cellView.textField.textColor = [NSColor labelColor];
+    } else {
+        cellView.textField.font = [NSFont systemFontOfSize:16.0];
+        cellView.textField.textColor = [NSColor secondaryLabelColor];
+    }
+
+    return cellView;
+}
+
+- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
+{
+    return (row == self.currentLyricIndex) ? 60.0 : 40.0;
+}
+
+- (IBAction)tableViewAction:(id)sender
+{
+    const NSInteger clickedRow = self.lyricsTableView.clickedRow;
+    if (clickedRow < 0 || clickedRow >= (NSInteger)self.lyricsEntries.count) {
+        return;
+    }
+
+    const long long lyricTimeMs = [self.lyricsEntries[clickedRow][@"time"] longLongValue];
+    const vlc_tick_t vlcTime = VLC_TICK_FROM_MS(lyricTimeMs);
+
+    VLCPlayerController * const playerController =
+        VLCMain.sharedInstance.playQueueController.playerController;
+    [playerController setTimePrecise:vlcTime];
+}
+
 @end


=====================================
modules/gui/macosx/windows/video/VLCMainVideoViewController.m
=====================================
@@ -172,6 +172,7 @@ NSString * const VLCUseClassicVideoPlayerLayoutKey = @"VLCUseClassicVideoPlayerL
         // Only set up button constraints for standard layout
         _bottomButtonStackViewConstraint =
             [self.bottomBarView.topAnchor constraintEqualToAnchor:self.centralControlsStackView.bottomAnchor];
+        [self updateLyricsInsets];
     }
 
     VLCPlayerController * const controller =
@@ -219,6 +220,7 @@ NSString * const VLCUseClassicVideoPlayerLayoutKey = @"VLCUseClassicVideoPlayerL
     [self setupAudioDecorativeView];
     [self.controlsBar update];
     [self updateFloatOnTopIndicator];
+    [self updateLyricsInsets];
     
     // For classic layout, create constraint for video view to fill main view when controls are hidden
     if (self.classic) {
@@ -245,6 +247,17 @@ NSString * const VLCUseClassicVideoPlayerLayoutKey = @"VLCUseClassicVideoPlayerL
     }
 }
 
+- (void)updateLyricsInsets
+{
+    const CGFloat topInset = self.fakeTitleBar.frame.size.height + VLCLibraryUIUnits.largeSpacing;
+    const CGFloat bottomInset = self.bottomBarView.frame.size.height + VLCLibraryUIUnits.largeSpacing;
+
+    NSScrollView * const scrollView = self.audioDecorativeView.lyricsScrollView;
+    scrollView.automaticallyAdjustsContentInsets = NO;
+    scrollView.contentInsets = NSEdgeInsetsMake(topInset, 0, bottomInset, 0);
+    scrollView.scrollerInsets = NSEdgeInsetsMake(topInset, 0, 0, 0);
+}
+
 - (BOOL)isVisualizationActive
 {
     BOOL isVisualActive;



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d0a89f94fb0003aa41b23b6b259946242f8f0476...fd2acc9bfc113600f6ba6447c02507eb4f193d13

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d0a89f94fb0003aa41b23b6b259946242f8f0476...fd2acc9bfc113600f6ba6447c02507eb4f193d13
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list