diff --git a/Submariner.xcodeproj/project.pbxproj b/Submariner.xcodeproj/project.pbxproj index 6b6277d..c2010dc 100644 --- a/Submariner.xcodeproj/project.pbxproj +++ b/Submariner.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ 3E53DBBC27E41B6400DB4C84 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E53DBBB27E41B6400DB4C84 /* CoreMedia.framework */; }; 3E5C42E029846E25009B9699 /* SBOnboardingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E5C42DE29846E25009B9699 /* SBOnboardingController.swift */; }; 3E69A2FF28B02D86009800D8 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E69A2FD28B02D86009800D8 /* UserNotifications.framework */; }; + 3E6DF3D72C3DEA6800055A52 /* SBPlayRateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E6DF3D62C3DEA6800055A52 /* SBPlayRateController.swift */; }; 3E702DE32A3E8E1F005F7184 /* SBAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E702DE22A3E8E1F005F7184 /* SBAppDelegate.swift */; }; 3E702DE72A428A1B005F7184 /* Synchronized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E702DE62A428A1B005F7184 /* Synchronized.swift */; }; 3E702DE92A428CF6005F7184 /* SBSubsonicParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E702DE82A428CF6005F7184 /* SBSubsonicParsingOperation.swift */; }; @@ -206,6 +207,7 @@ 3E5C42DE29846E25009B9699 /* SBOnboardingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SBOnboardingController.swift; sourceTree = ""; }; 3E6126EB2AD7363100B2A1E2 /* Submariner v4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Submariner v4.xcdatamodel"; sourceTree = ""; }; 3E69A2FD28B02D86009800D8 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; + 3E6DF3D62C3DEA6800055A52 /* SBPlayRateController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SBPlayRateController.swift; sourceTree = ""; }; 3E702DE22A3E8E1F005F7184 /* SBAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SBAppDelegate.swift; sourceTree = ""; }; 3E702DE62A428A1B005F7184 /* Synchronized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Synchronized.swift; sourceTree = ""; }; 3E702DE82A428CF6005F7184 /* SBSubsonicParsingOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SBSubsonicParsingOperation.swift; sourceTree = ""; }; @@ -718,6 +720,7 @@ 3E04F5EC2B70487200E24E56 /* SBAddServerPlaylistController.swift */, 3E04F5EA2B7043F600E24E56 /* SBMergeArtistsController.swift */, 3EA755512C37749D00852938 /* SBJumpToTimestampController.swift */, + 3E6DF3D62C3DEA6800055A52 /* SBPlayRateController.swift */, ); name = "Window Controllers"; sourceTree = ""; @@ -862,6 +865,7 @@ 3E32BE522B8D9FDE00E77CF0 /* SBLibraryItemPasteboardWriter.swift in Sources */, 3E1B785E2ACE5039008927C6 /* SBInspectorController.swift in Sources */, 3EF978022BC3C4E300C986E9 /* SBMessageTextView.swift in Sources */, + 3E6DF3D72C3DEA6800055A52 /* SBPlayRateController.swift in Sources */, 3E70B2DF2A2BDC55002C0B93 /* SBApplication.swift in Sources */, 3E702DE72A428A1B005F7184 /* Synchronized.swift in Sources */, 3EC03B4029F4F2E0001FDE50 /* SBDownloads.swift in Sources */, diff --git a/Submariner/SBDatabaseController.h b/Submariner/SBDatabaseController.h index 6acb8c2..6a0e5ff 100644 --- a/Submariner/SBDatabaseController.h +++ b/Submariner/SBDatabaseController.h @@ -45,6 +45,7 @@ @class SBEditServerController; @class SBAddServerPlaylistController; @class SBJumpToTimestampController; +@class SBPlayRateController; @class SBMusicController; @class SBMusicSearchController; @class SBServerSearchController; @@ -79,6 +80,7 @@ IBOutlet NSTreeController *resourcesController; IBOutlet SBEditServerController *editServerController; SBJumpToTimestampController *jumpToTimestampController; + SBPlayRateController *playRateController; SBAddServerPlaylistController *addServerPlaylistController; IBOutlet NSProgressIndicator *progressIndicator; IBOutlet NSButton *toggleButton; diff --git a/Submariner/SBDatabaseController.m b/Submariner/SBDatabaseController.m index ce3d117..da15a5e 100644 --- a/Submariner/SBDatabaseController.m +++ b/Submariner/SBDatabaseController.m @@ -131,6 +131,8 @@ - (id)initWithManagedObjectContext:(NSManagedObjectContext *)context { // init sheet controllers not managed by IB jumpToTimestampController = [[SBJumpToTimestampController alloc] init]; jumpToTimestampController.parentWindow = self.window; + playRateController = [[SBPlayRateController alloc] init]; + playRateController.parentWindow = self.window; [onboardingController setDatabaseController:self]; [musicController setDatabaseController:self]; @@ -975,6 +977,11 @@ - (IBAction)jumpToTimestamp:(id)sender { [jumpToTimestampController openSheet: sender]; } + +- (IBAction)showPlayRate:(id)sender { + [playRateController openSheet: sender]; +} + #pragma mark - #pragma mark NSTimer diff --git a/Submariner/SBPlayRateController.swift b/Submariner/SBPlayRateController.swift new file mode 100644 index 0000000..7356a91 --- /dev/null +++ b/Submariner/SBPlayRateController.swift @@ -0,0 +1,75 @@ +// +// SBPlayRateController.swift +// Submariner +// +// Created by Calvin Buckley on 2024-07-09. +// +// Copyright (c) 2024 Calvin Buckley +// SPDX-License-Identifier: BSD-3-Clause +// + +import Cocoa +import SwiftUI + +@objc class SBPlayRateController: SBSheetController { + var newTimestampString: String? + + override func openSheet(_ sender: Any!) { + let timestampString = SBPlayer.sharedInstance().currentTimeString + let viewController = NSHostingController(rootView: PlayRateControllerView(playRateController: self)) + let sheet = NSWindow(contentViewController: viewController) + sheet.hasShadow = true + sheet.isReleasedWhenClosed = true + // This is pretty gross and based on vibes, but not sure how to get the content size; + // preferredContentSize seems to be small as possible. + sheet.setContentSize(NSSize(width: 300, height: 100)) + self.sheet = sheet + + super.openSheet(sender) + } + + override func closeSheet(_ sender: Any!) { + // TODO: Put some effort into parsing? + if let newTimestampString = self.newTimestampString { + SBPlayer.sharedInstance().seek(to: newTimestampString.toTimeInterval()) + } + + super.closeSheet(sender) + } + + struct PlayRateControllerView: View { + weak var playRateController: SBPlayRateController! + + @AppStorage("playRate") private var playRate: Double = 1.0 + + var body: some View { + VStack { + Form { + Slider(value: $playRate, in: 0.5...2, step: 0.25) { + Text("Playback Speed") + } minimumValueLabel: { + Text("0.5") + } maximumValueLabel: { + Text("2") + } + } + HStack { + Button { + playRate = 1.0 + } label: { + Text("Reset") + } + Spacer() + Button { + playRateController.closeSheet(playRateController) + } label: { + Text("OK") + } + .keyboardShortcut(.defaultAction) + } + } + .padding(20) + } + } +} + diff --git a/Submariner/en.lproj/MainMenu.xib b/Submariner/en.lproj/MainMenu.xib index ceddaa6..8111af9 100644 --- a/Submariner/en.lproj/MainMenu.xib +++ b/Submariner/en.lproj/MainMenu.xib @@ -518,6 +518,13 @@ DQ + + + + + + +