diff --git a/Aural.xcodeproj/project.pbxproj b/Aural.xcodeproj/project.pbxproj index cdc0c968f..945948d41 100644 --- a/Aural.xcodeproj/project.pbxproj +++ b/Aural.xcodeproj/project.pbxproj @@ -838,6 +838,9 @@ 3E9CF7F526A7D36D0068A21B /* ChangeHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9CF7F426A7D36D0068A21B /* ChangeHistory.swift */; }; 3E9D706F27C1DE2F00CAC4F1 /* Devices.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3E9D706E27C1DE2F00CAC4F1 /* Devices.xib */; }; 3E9D707127C1E30100CAC4F1 /* DevicesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9D707027C1E30100CAC4F1 /* DevicesViewController.swift */; }; + 3E9DA1952D34291B0094D6DE /* TextButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9DA1942D34291B0094D6DE /* TextButtonCell.swift */; }; + 3E9DA1972D342B080094D6DE /* TextAndImageButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9DA1962D342B080094D6DE /* TextAndImageButtonCell.swift */; }; + 3E9DA1992D342F6E0094D6DE /* LyricsViewController+Theming.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9DA1982D342F6E0094D6DE /* LyricsViewController+Theming.swift */; }; 3E9E590E25DB6B150064EB5F /* MenuBarAppModeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9E58FF25DB6B150064EB5F /* MenuBarAppModeController.swift */; }; 3E9E590F25DB6B150064EB5F /* AppModeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9E590025DB6B150064EB5F /* AppModeManager.swift */; }; 3E9E591025DB6B150064EB5F /* ModularAppModeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9E590125DB6B150064EB5F /* ModularAppModeController.swift */; }; @@ -1963,6 +1966,9 @@ 3E9CF7F426A7D36D0068A21B /* ChangeHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeHistory.swift; sourceTree = ""; }; 3E9D706E27C1DE2F00CAC4F1 /* Devices.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Devices.xib; sourceTree = ""; }; 3E9D707027C1E30100CAC4F1 /* DevicesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DevicesViewController.swift; sourceTree = ""; }; + 3E9DA1942D34291B0094D6DE /* TextButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextButtonCell.swift; sourceTree = ""; }; + 3E9DA1962D342B080094D6DE /* TextAndImageButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextAndImageButtonCell.swift; sourceTree = ""; }; + 3E9DA1982D342F6E0094D6DE /* LyricsViewController+Theming.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LyricsViewController+Theming.swift"; sourceTree = ""; }; 3E9E58FF25DB6B150064EB5F /* MenuBarAppModeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuBarAppModeController.swift; sourceTree = ""; }; 3E9E590025DB6B150064EB5F /* AppModeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppModeManager.swift; sourceTree = ""; }; 3E9E590125DB6B150064EB5F /* ModularAppModeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModularAppModeController.swift; sourceTree = ""; }; @@ -4015,6 +4021,8 @@ 3EAFB616267FF4C200F0DC96 /* EffectsSliderPreviewCell.swift */, 3EF172F426A989BA00BB4556 /* EffectsUnitLabels.swift */, 3EBF293626869A9300D87021 /* EffectsUnitPopupMenuCell.swift */, + 3E9DA1942D34291B0094D6DE /* TextButtonCell.swift */, + 3E9DA1962D342B080094D6DE /* TextAndImageButtonCell.swift */, ); path = Views; sourceTree = ""; @@ -5098,6 +5106,7 @@ 3EAF727A2D25B24700E24245 /* LyricsViewController.swift */, 3E57443C2D262104004B5AE7 /* LyricsViewController+Static.swift */, 3E57443A2D261E28004B5AE7 /* LyricsViewController+Timed.swift */, + 3E9DA1982D342F6E0094D6DE /* LyricsViewController+Theming.swift */, 3EA21A452D2763B3008B5B21 /* LyricsFileDropView.swift */, 3E719D8C2D28A105000A33B9 /* SheetView */, 3E719D8D2D28A12D000A33B9 /* Utils */, @@ -6120,6 +6129,7 @@ 3E6C12EF25CEBE8100BF0D07 /* DelayUnitView.swift in Sources */, 3E0218942C23490E00865AC2 /* LegacyHistoryPersistentState.swift in Sources */, 3E02182A2C23490E00865AC2 /* FavoriteTrack.swift in Sources */, + 3E9DA1952D34291B0094D6DE /* TextButtonCell.swift in Sources */, 3E6C12F025CEBE8100BF0D07 /* DelayUnitViewController.swift in Sources */, 3E02198C2C23490E00865AC2 /* OrderedSetExtensions.swift in Sources */, 3E0218AD2C23490E00865AC2 /* LibraryPersistentState.swift in Sources */, @@ -6158,6 +6168,7 @@ 3E0218392C23490E00865AC2 /* FFmpegAudioCodec.swift in Sources */, 3E02195E2C23490E00865AC2 /* SearchType.swift in Sources */, 3E0218D52C23490E00865AC2 /* PlaybackContextProtocol.swift in Sources */, + 3E9DA1972D342B080094D6DE /* TextAndImageButtonCell.swift in Sources */, 3E7E9F4E2C66B1AF0011DE8E /* WaveformWindowController.swift in Sources */, 3E02189A2C23490E00865AC2 /* MenuBarPlayerUIPersistentState.swift in Sources */, 3E02195C2C23490E00865AC2 /* SearchResults.swift in Sources */, @@ -6295,6 +6306,7 @@ 3ECF71A52B74EDE000FDB69B /* CompactPlayQueueContainer.swift in Sources */, 3E0218262C23490E00865AC2 /* Favorite.swift in Sources */, 3EB3A62126A773350060487C /* FontSizeStepper.swift in Sources */, + 3E9DA1992D342F6E0094D6DE /* LyricsViewController+Theming.swift in Sources */, 3ECFA87F2AE1EB29001096DC /* EffectsContainerViewController.swift in Sources */, 3E91932C25E0D96A00F30F81 /* FontSchemeFontsViewController.swift in Sources */, 3E3579D026835A6A009522A0 /* GenericPresetPopupMenuController.swift in Sources */, diff --git a/Source/Core/TrackIO/TrackReader+Lyrics.swift b/Source/Core/TrackIO/TrackReader+Lyrics.swift index fa10b214b..49f8cd213 100644 --- a/Source/Core/TrackIO/TrackReader+Lyrics.swift +++ b/Source/Core/TrackIO/TrackReader+Lyrics.swift @@ -17,9 +17,9 @@ import LyricsUI extension TrackReader { - func loadExternalLyrics(for track: Track) { + func loadExternalLyrics(for track: Track, immediate: Bool) { - guard track.externalTimedLyrics == nil else {return} + guard track.externalOrEmbeddedTimedLyrics == nil else {return} // Load lyrics from previously assigned external file if let externalLyricsFile = track.metadata.externalLyricsFile, externalLyricsFile.exists, @@ -45,7 +45,7 @@ extension TrackReader { // Online search guard onlineSearchEnabled else {return} - Task.detached(priority: .userInitiated) { + Task.detached(priority: immediate ? .userInitiated : .utility) { guard let bestLyrics = await LyricsSearchService().searchLyrics(for: track) else {return} @@ -112,8 +112,6 @@ extension TrackReader { func searchForLyricsOnline(for track: Track, uiUpdateBlock: @escaping (TimedLyrics) -> Void) async { - guard onlineSearchEnabled else {return} - Task.detached(priority: .userInitiated) { guard let bestLyrics = await LyricsSearchService().searchLyrics(for: track) else {return} diff --git a/Source/Core/TrackIO/TrackReader.swift b/Source/Core/TrackIO/TrackReader.swift index 1c65a6dff..be85826ef 100644 --- a/Source/Core/TrackIO/TrackReader.swift +++ b/Source/Core/TrackIO/TrackReader.swift @@ -211,7 +211,7 @@ class TrackReader { DispatchQueue.global(qos: immediate ? .userInteractive : .utility).async { self.loadArt(for: track) - self.loadExternalLyrics(for: track) + self.loadExternalLyrics(for: track, immediate: immediate) } } diff --git a/Source/UI/Effects/Views/TextAndImageButtonCell.swift b/Source/UI/Effects/Views/TextAndImageButtonCell.swift new file mode 100644 index 000000000..845521af5 --- /dev/null +++ b/Source/UI/Effects/Views/TextAndImageButtonCell.swift @@ -0,0 +1,44 @@ +// +// TextAndImageButtonCell.swift +// Aural +// +// Copyright © 2025 Kartik Venugopal. All rights reserved. +// +// This software is licensed under the MIT software license. +// See the file "LICENSE" in the project root directory for license terms. +// + +import AppKit + +@IBDesignable +class TextAndImageButtonCell: NSButtonCell { + + var rectRadius: CGFloat {4} + + @IBInspectable var imgWidth: Int = 14 + @IBInspectable var imgHeight: Int = 14 + + var borderColor: NSColor {systemColorScheme.buttonColor} + var titleFont: NSFont {systemFontScheme.normalFont} + var titleColor: NSColor {systemColorScheme.primaryTextColor} + + override func draw(withFrame cellFrame: NSRect, in controlView: NSView) { + + NSBezierPath.strokeRoundedRect(cellFrame.insetBy(dx: 0.5, dy: 0.5), radius: rectRadius, withColor: borderColor) + + title.drawCentered(in: cellFrame, + withFont: titleFont, andColor: titleColor, yOffset: 1) + + let imgWidth = CGFloat(self.imgWidth) + let imgHeight = CGFloat(self.imgHeight) + + let imgX = cellFrame.maxX - imgWidth - 5 + let imgY = cellFrame.maxY - imgHeight - ((cellFrame.height - imgHeight) / 2) + + let imgRect = NSMakeRect(imgX, imgY, imgWidth, imgHeight) + + if let image = self.image?.tintedWithColor(titleColor) { + image.draw(in: imgRect) + } + } +} diff --git a/Source/UI/Effects/Views/TextButtonCell.swift b/Source/UI/Effects/Views/TextButtonCell.swift new file mode 100644 index 000000000..f35274ac1 --- /dev/null +++ b/Source/UI/Effects/Views/TextButtonCell.swift @@ -0,0 +1,28 @@ +// +// TextButtonCell.swift +// Aural +// +// Copyright © 2025 Kartik Venugopal. All rights reserved. +// +// This software is licensed under the MIT software license. +// See the file "LICENSE" in the project root directory for license terms. +// + +import AppKit + +class TextButtonCell: NSButtonCell { + + var rectRadius: CGFloat {4} + + var borderColor: NSColor {systemColorScheme.buttonColor} + var titleFont: NSFont {systemFontScheme.normalFont} + var titleColor: NSColor {systemColorScheme.primaryTextColor} + + override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) { + + NSBezierPath.strokeRoundedRect(cellFrame.insetBy(dx: 0.5, dy: 0.5), radius: rectRadius, withColor: borderColor) + + title.drawCentered(in: cellFrame, + withFont: titleFont, andColor: titleColor, yOffset: 1) + } +} diff --git a/Source/UI/Library/BuildProgress/LibraryBuildProgress.xib b/Source/UI/Library/BuildProgress/LibraryBuildProgress.xib index 8f91b6722..fa4d6fb7b 100644 --- a/Source/UI/Library/BuildProgress/LibraryBuildProgress.xib +++ b/Source/UI/Library/BuildProgress/LibraryBuildProgress.xib @@ -1,8 +1,7 @@ - + - - + @@ -18,7 +17,7 @@ - + diff --git a/Source/UI/Lyrics/Lyrics.xib b/Source/UI/Lyrics/Lyrics.xib index 8bd622962..f763c4c40 100644 --- a/Source/UI/Lyrics/Lyrics.xib +++ b/Source/UI/Lyrics/Lyrics.xib @@ -8,7 +8,13 @@ + + + + + + @@ -187,7 +193,7 @@ - + @@ -198,11 +204,19 @@ - - - + - + - + - + @@ -251,9 +276,9 @@ - + - + @@ -265,6 +290,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/UI/Lyrics/LyricsViewController+Static.swift b/Source/UI/Lyrics/LyricsViewController+Static.swift index 99b6303fb..21c1b1f94 100644 --- a/Source/UI/Lyrics/LyricsViewController+Static.swift +++ b/Source/UI/Lyrics/LyricsViewController+Static.swift @@ -23,6 +23,8 @@ extension LyricsViewController { color: systemColorScheme.secondaryTextColor, lineSpacing: 15)) } + + searchSpinner.stopAnimation(nil) } func dismissStaticLyricsText() { diff --git a/Source/UI/Lyrics/LyricsViewController+Theming.swift b/Source/UI/Lyrics/LyricsViewController+Theming.swift new file mode 100644 index 000000000..5bebe71cb --- /dev/null +++ b/Source/UI/Lyrics/LyricsViewController+Theming.swift @@ -0,0 +1,86 @@ +// +// LyricsViewController+Theming.swift +// Aural +// +// Copyright © 2025 Kartik Venugopal. All rights reserved. +// +// This software is licensed under the MIT software license. +// See the file "LICENSE" in the project root directory for license terms. +// + +import AppKit + +extension LyricsViewController: ThemeInitialization { + + func initTheme() { + + imgLyrics.contentTintColor = systemColorScheme.buttonColor + + lblCaption.font = systemFontScheme.captionFont + lblCaption.textColor = systemColorScheme.captionTextColor + + lblDragDrop.font = systemFontScheme.prominentFont + lblDragDrop.textColor = systemColorScheme.primaryTextColor + + lblSearching.font = systemFontScheme.prominentFont + lblSearching.textColor = systemColorScheme.primaryTextColor + + view.layer?.backgroundColor = systemColorScheme.backgroundColor.cgColor + textView.backgroundColor = systemColorScheme.backgroundColor + tableView.setBackgroundColor(systemColorScheme.backgroundColor) + + textVertScroller.redraw() + tableVertScroller.redraw() + + if showingTimedLyrics { + updateTimedLyricsText() + } else { + updateStaticLyricsText() + } + } +} + +extension LyricsViewController: FontSchemeObserver { + + func fontSchemeChanged() { + + lblCaption.font = systemFontScheme.captionFont + lblDragDrop.font = systemFontScheme.prominentFont + lblSearching.font = systemFontScheme.prominentFont + + if showingTimedLyrics { + updateTimedLyricsText() + } else { + updateStaticLyricsText() + } + + [btnChooseFile, btnSearchOnline].forEach {$0?.redraw()} + } +} + +extension LyricsViewController: ColorSchemeObserver { + + func colorSchemeChanged() { + + imgLyrics.contentTintColor = systemColorScheme.buttonColor + + lblCaption.textColor = systemColorScheme.captionTextColor + lblDragDrop.textColor = systemColorScheme.primaryTextColor + lblSearching.textColor = systemColorScheme.primaryTextColor + + view.layer?.backgroundColor = systemColorScheme.backgroundColor.cgColor + textView.backgroundColor = systemColorScheme.backgroundColor + tableView.setBackgroundColor(systemColorScheme.backgroundColor) + + textVertScroller.redraw() + tableVertScroller.redraw() + + [btnChooseFile, btnSearchOnline].forEach {$0?.redraw()} + + if showingStaticLyrics { + updateStaticLyricsText() + } else if showingTimedLyrics { + updateTimedLyricsText() + } + } +} diff --git a/Source/UI/Lyrics/LyricsViewController+Timed.swift b/Source/UI/Lyrics/LyricsViewController+Timed.swift index c6390793a..654e0e0f4 100644 --- a/Source/UI/Lyrics/LyricsViewController+Timed.swift +++ b/Source/UI/Lyrics/LyricsViewController+Timed.swift @@ -33,6 +33,8 @@ extension LyricsViewController { messenger.subscribeAsync(to: .Player.playbackStateChanged, handler: playbackStateChanged) messenger.subscribeAsync(to: .Player.seekPerformed, handler: seekPerformed) messenger.subscribeAsync(to: .Lyrics.karaokeModePreferenceUpdated, handler: karaokeModePreferenceUpdated) + + searchSpinner.stopAnimation(nil) } func dismissTimedLyricsView() { @@ -54,7 +56,23 @@ extension LyricsViewController { @IBAction func searchForLyricsOnlineButtonAction(_ sender: NSButton) { - // TODO: + guard let track else {return} + + tabView.selectTabViewItem(at: 4) + searchSpinner.startAnimation(nil) + + let uiUpdateBlock = {(timedLyrics: TimedLyrics) in + + if playbackInfoDelegate.playingTrack == track { + + self.timedLyrics = timedLyrics + self.doUpdate() + } + } + + Task.detached(priority: .userInitiated) { + await trackReader.searchForLyricsOnline(for: track, uiUpdateBlock: uiUpdateBlock) + } } func loadLyrics(fromFile lyricsFile: URL) { @@ -152,22 +170,6 @@ extension LyricsViewController { preferences.metadataPreferences.lyrics.enableOnlineSearch.value } - private func searchForLyricsOnline(for track: Track) { - - let uiUpdateBlock = {(timedLyrics: TimedLyrics) in - - if playbackInfoDelegate.playingTrack == track { - - self.timedLyrics = timedLyrics - self.doUpdate() - } - } - - Task.detached(priority: .userInitiated) { - await trackReader.searchForLyricsOnline(for: track, uiUpdateBlock: uiUpdateBlock) - } - } - func lyricsLoaded(notif: TrackInfoUpdatedNotification) { if playbackInfoDelegate.playingTrack == notif.updatedTrack, appModeManager.isShowingLyrics { diff --git a/Source/UI/Lyrics/LyricsViewController.swift b/Source/UI/Lyrics/LyricsViewController.swift index 1da41c3e1..4bb46e4b8 100644 --- a/Source/UI/Lyrics/LyricsViewController.swift +++ b/Source/UI/Lyrics/LyricsViewController.swift @@ -16,6 +16,7 @@ class LyricsViewController: NSViewController { override var nibName: NSNib.Name? {"Lyrics"} + @IBOutlet weak var imgLyrics: NSImageView! @IBOutlet weak var lblCaption: NSTextField! @IBOutlet weak var tabView: NSTabView! @@ -26,6 +27,13 @@ class LyricsViewController: NSViewController { @IBOutlet weak var tableView: NSTableView! @IBOutlet weak var tableVertScroller: PrettyVerticalScroller! + @IBOutlet weak var lblDragDrop: NSTextField! + @IBOutlet weak var btnChooseFile: NSButton! + @IBOutlet weak var btnSearchOnline: NSButton! + + @IBOutlet weak var lblSearching: NSTextField! + @IBOutlet weak var searchSpinner: NSProgressIndicator! + var track: Track? var staticLyrics: String? @@ -50,6 +58,8 @@ class LyricsViewController: NSViewController { fontSchemesManager.registerObserver(self) colorSchemesManager.registerSchemeObservers(self) + colorSchemesManager.registerPropertyObserver(self, forProperty: \.buttonColor, changeReceivers: [imgLyrics, btnChooseFile, btnSearchOnline]) + colorSchemesManager.registerPropertyObserver(self, forProperty: \.primaryTextColor, changeReceivers: [lblDragDrop, btnChooseFile, btnSearchOnline]) messenger.subscribeAsync(to: .Player.trackTransitioned, handler: trackTransitioned(_:)) @@ -76,6 +86,10 @@ class LyricsViewController: NSViewController { dismissTimedLyricsView() } + var showingStaticLyrics: Bool { + tabView.selectedIndex == 0 + } + var showingTimedLyrics: Bool { tabView.selectedIndex == 1 } @@ -106,6 +120,7 @@ class LyricsViewController: NSViewController { let wasShowingTimedLyrics = tabView.selectedIndex == 1 tabView.selectTabViewItem(at: track == nil ? 3 : 2) + searchSpinner.stopAnimation(nil) dismissStaticLyricsText() @@ -126,60 +141,3 @@ class LyricsViewController: NSViewController { view.layer?.cornerRadius = radius } } - -extension LyricsViewController: ThemeInitialization { - - func initTheme() { - - lblCaption.font = systemFontScheme.captionFont - lblCaption.textColor = systemColorScheme.captionTextColor - - view.layer?.backgroundColor = systemColorScheme.backgroundColor.cgColor - textView.backgroundColor = systemColorScheme.backgroundColor - tableView.setBackgroundColor(systemColorScheme.backgroundColor) - - textVertScroller.redraw() - tableVertScroller.redraw() - - if showingTimedLyrics { - updateTimedLyricsText() - } else { - updateStaticLyricsText() - } - } -} - -extension LyricsViewController: FontSchemeObserver { - - func fontSchemeChanged() { - - lblCaption.font = systemFontScheme.captionFont - - if showingTimedLyrics { - updateTimedLyricsText() - } else { - updateStaticLyricsText() - } - } -} - -extension LyricsViewController: ColorSchemeObserver { - - func colorSchemeChanged() { - - lblCaption.textColor = systemColorScheme.captionTextColor - - view.layer?.backgroundColor = systemColorScheme.backgroundColor.cgColor - textView.backgroundColor = systemColorScheme.backgroundColor - tableView.setBackgroundColor(systemColorScheme.backgroundColor) - - textVertScroller.redraw() - tableVertScroller.redraw() - - if showingTimedLyrics { - updateTimedLyricsText() - } else { - updateStaticLyricsText() - } - } -} diff --git a/Source/UI/Search/Search.xib b/Source/UI/Search/Search.xib index 1471d7784..3e02b9529 100644 --- a/Source/UI/Search/Search.xib +++ b/Source/UI/Search/Search.xib @@ -1,12 +1,14 @@ - + - + + + @@ -26,7 +28,7 @@ - + @@ -104,7 +106,7 @@ - + @@ -135,7 +137,7 @@ Gw - + @@ -159,7 +161,7 @@ Gw - + @@ -191,7 +193,7 @@ Gw - + diff --git a/Source/UI/Search/SearchViewController+Theming.swift b/Source/UI/Search/SearchViewController+Theming.swift index ddc858ded..1007ffc1d 100644 --- a/Source/UI/Search/SearchViewController+Theming.swift +++ b/Source/UI/Search/SearchViewController+Theming.swift @@ -36,6 +36,7 @@ extension SearchViewController: FontSchemeObserver { lblSummary.font = systemFontScheme.normalFont resultsTable.reloadDataMaintainingSelection() + btnDone.redraw() } } @@ -49,5 +50,6 @@ extension SearchViewController: ColorSchemeObserver { lblSummary.textColor = systemColorScheme.tertiaryTextColor resultsTable.reloadDataMaintainingSelection() + btnDone.redraw() } } diff --git a/Source/UI/Search/SearchViewController.swift b/Source/UI/Search/SearchViewController.swift index 62aa621de..1fda66dff 100644 --- a/Source/UI/Search/SearchViewController.swift +++ b/Source/UI/Search/SearchViewController.swift @@ -20,6 +20,8 @@ class SearchViewController: NSViewController { @IBOutlet weak var lblSummary: NSTextField! @IBOutlet weak var resultsTable: CompactPlayQueueSearchResultsTableView! + @IBOutlet weak var btnDone: NSButton! + var searchQuery: SearchQuery = SearchQuery() // Current search results @@ -38,6 +40,7 @@ class SearchViewController: NSViewController { fontSchemesManager.registerObserver(self) colorSchemesManager.registerSchemeObserver(self) colorSchemesManager.registerPropertyObserver(self, forProperty: \.captionTextColor, changeReceiver: lblCaption) + colorSchemesManager.registerPropertyObserver(self, forProperty: \.buttonColor, changeReceiver: btnDone) // Offset the caption label a bit to the right. if appModeManager.currentMode == .modular,