Skip to content

Commit

Permalink
Add menu bar
Browse files Browse the repository at this point in the history
  • Loading branch information
cuba committed Nov 1, 2023
1 parent d0ea8b4 commit 1f41efa
Show file tree
Hide file tree
Showing 13 changed files with 333 additions and 32 deletions.
33 changes: 33 additions & 0 deletions Whisky.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
8C2AEFCA2AED79CD00CB568F /* WhiskyKit in Frameworks */ = {isa = PBXBuildFile; productRef = 8C2AEFC92AED79CD00CB568F /* WhiskyKit */; };
8CB681E52AED7C6F0018D319 /* WhiskyKit in Resources */ = {isa = PBXBuildFile; fileRef = 8CB681E42AED7C6F0018D319 /* WhiskyKit */; };
8CB681E72AED7CD00018D319 /* WhiskyKit in Frameworks */ = {isa = PBXBuildFile; productRef = 8CB681E62AED7CD00018D319 /* WhiskyKit */; };
8CB681EA2AEDEDC20018D319 /* WhiskyBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB681E92AEDEDC20018D319 /* WhiskyBarView.swift */; };
8CB681EC2AEDEDE70018D319 /* BottleBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB681EB2AEDEDE70018D319 /* BottleBarView.swift */; };
8CB681EE2AEDEE2F0018D319 /* ProgramBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB681ED2AEDEE2F0018D319 /* ProgramBarView.swift */; };
8CB681F12AEDF9620018D319 /* Bundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB681F02AEDF9620018D319 /* Bundle+Extensions.swift */; };
AB66A8642A4195B10006D238 /* Rosetta2.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB66A8632A4195B10006D238 /* Rosetta2.swift */; };
EB58FB552A499896002DC184 /* SemanticVersion in Frameworks */ = {isa = PBXBuildFile; productRef = EB58FB542A499896002DC184 /* SemanticVersion */; };
EEA5A2462A31DD65008274AE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA5A2452A31DD65008274AE /* AppDelegate.swift */; };
Expand Down Expand Up @@ -142,6 +146,10 @@
6EFDF6532AAE2DA800EF622F /* WhiskyThumbnail.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WhiskyThumbnail.entitlements; sourceTree = "<group>"; };
6EFDF6652AAE303300EF622F /* Icons.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Icons.xcassets; sourceTree = "<group>"; };
8CB681E42AED7C6F0018D319 /* WhiskyKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = WhiskyKit; sourceTree = "<group>"; };
8CB681E92AEDEDC20018D319 /* WhiskyBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhiskyBarView.swift; sourceTree = "<group>"; };
8CB681EB2AEDEDE70018D319 /* BottleBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottleBarView.swift; sourceTree = "<group>"; };
8CB681ED2AEDEE2F0018D319 /* ProgramBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgramBarView.swift; sourceTree = "<group>"; };
8CB681F02AEDF9620018D319 /* Bundle+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Extensions.swift"; sourceTree = "<group>"; };
AB66A8632A4195B10006D238 /* Rosetta2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rosetta2.swift; sourceTree = "<group>"; };
EEA5A2452A31DD65008274AE /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -225,6 +233,7 @@
6E40495429CCA19C006E3F1B /* Whisky */ = {
isa = PBXGroup;
children = (
8CB681EF2AEDF9450018D319 /* Extensions */,
EEA5A2452A31DD65008274AE /* AppDelegate.swift */,
6E5197CF29D71FF900CF655E /* Models */,
6E5197D029D7200700CF655E /* Utils */,
Expand Down Expand Up @@ -258,6 +267,7 @@
6E5197CD29D71FCD00CF655E /* Views */ = {
isa = PBXGroup;
children = (
8CB681E82AEDED9D0018D319 /* Menu Bar */,
6E49E01F2AECB7D000009CAC /* Settings */,
63FFDE822ADEFADF00178665 /* Common */,
6E40495529CCA19C006E3F1B /* WhiskyApp.swift */,
Expand Down Expand Up @@ -343,6 +353,24 @@
path = WhiskyThumbnail;
sourceTree = "<group>";
};
8CB681E82AEDED9D0018D319 /* Menu Bar */ = {
isa = PBXGroup;
children = (
8CB681E92AEDEDC20018D319 /* WhiskyBarView.swift */,
8CB681EB2AEDEDE70018D319 /* BottleBarView.swift */,
8CB681ED2AEDEE2F0018D319 /* ProgramBarView.swift */,
);
path = "Menu Bar";
sourceTree = "<group>";
};
8CB681EF2AEDF9450018D319 /* Extensions */ = {
isa = PBXGroup;
children = (
8CB681F02AEDF9620018D319 /* Bundle+Extensions.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -533,6 +561,7 @@
buildActionMask = 2147483647;
files = (
EEA5A2462A31DD65008274AE /* AppDelegate.swift in Sources */,
8CB681EE2AEDEE2F0018D319 /* ProgramBarView.swift in Sources */,
6E70A4A12A9A280C007799E9 /* WhiskyCmd.swift in Sources */,
6E40495829CCA19C006E3F1B /* ContentView.swift in Sources */,
6EF557982A410599001A4F09 /* SetupView.swift in Sources */,
Expand All @@ -547,11 +576,14 @@
6E50D98529CDF25B008C39F6 /* BottleCreationView.swift in Sources */,
6E6C0CF42A419A7600356232 /* RosettaView.swift in Sources */,
6E6C0CF82A419A8C00356232 /* GPTKInstallView.swift in Sources */,
8CB681F12AEDF9620018D319 /* Bundle+Extensions.swift in Sources */,
6E40498329CCA91B006E3F1B /* Bottle.swift in Sources */,
6E621CEF2A5F631300C9AAB3 /* Winetricks.swift in Sources */,
8CB681EA2AEDEDC20018D319 /* WhiskyBarView.swift in Sources */,
6E064B1429DD331F00D9A2D2 /* SparkleView.swift in Sources */,
6E40495629CCA19C006E3F1B /* WhiskyApp.swift in Sources */,
6E50D98329CD6066008C39F6 /* BottleVM.swift in Sources */,
8CB681EC2AEDEDE70018D319 /* BottleBarView.swift in Sources */,
6E6915452A3265BB0085BBB7 /* Logger.swift in Sources */,
6E49E0212AECB7DB00009CAC /* SettingsView.swift in Sources */,
6E7C07C02AAF570100F6E66B /* FileOpenView.swift in Sources */,
Expand Down Expand Up @@ -732,6 +764,7 @@
INFOPLIST_KEY_NSCameraUsageDescription = "A Windows application is trying to access the camera.";
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © Whisky";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A Windows application is trying to access the microphone.";
INFOPLIST_KEY_UIStatusBarStyle = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
Expand Down
2 changes: 1 addition & 1 deletion Whisky/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
return false
}

private static var appUrl: URL? {
Expand Down
24 changes: 24 additions & 0 deletions Whisky/Extensions/Bundle+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// Bundle+Extensions.swift
// Whisky
//
// This file is part of Whisky.
//
// Whisky 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 3 of the License, or (at your option) any later version.
//
// Whisky 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 Whisky.
// If not, see https://www.gnu.org/licenses/.
//

import Foundation
extension Bundle {
static var wiskeyBundleIdentifier: String {
return "com.isaacmarovitz.Whisky"
}
}
60 changes: 60 additions & 0 deletions Whisky/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -7012,6 +7012,66 @@
}
}
},
"menubar.action.launch" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Launch"
}
}
}
},
"menubar.action.pin" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Pin"
}
}
}
},
"menubar.action.unpin" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Unpin"
}
}
}
},
"menubar.bottles" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bottles"
}
}
}
},
"menubar.morePrograms" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "More..."
}
}
}
},
"menubar.title" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Whisky"
}
}
}
},
"open.bottle" : {
"localizations" : {
"da" : {
Expand Down
35 changes: 35 additions & 0 deletions Whisky/Models/Bottle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@
import Foundation
import AppKit
import WhiskyKit
import UniformTypeIdentifiers
import os.log

extension Bottle {
static let logger = Logger(
subsystem: Bundle.main.bundleIdentifier ?? Bundle.wiskeyBundleIdentifier, category: "Bottle"
)

func openCDrive() {
NSWorkspace.shared.open(url.appending(path: "drive_c"))
}
Expand Down Expand Up @@ -178,4 +184,33 @@ extension Bottle {
func rename(newName: String) {
settings.name = newName
}

@MainActor
/// Open a panel to chose a file for running
/// - Returns: URL of the file we wish to run
public func choseFileForRun() async -> URL? {
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseDirectories = false
panel.canChooseFiles = true
panel.allowedContentTypes = [
UTType.exe, UTType(exportedAs: "com.microsoft.msi-installer"),
UTType(exportedAs: "com.microsoft.bat")
]
panel.directoryURL = url.appending(path: "drive_c")
let result = await panel.begin()
guard result == .OK else { return nil }
return panel.urls.first
}

@MainActor
/// Open an open panel, chose a file and attempt to run it
/// - Returns: true or false if a file was run
public func openFileForRun(url: URL) async throws {
if url.pathExtension == "bat" {
try await Wine.runBatchFile(url: url, bottle: self)
} else {
try await Wine.runExternalProgram(url: url, bottle: self)
}
}
}
1 change: 1 addition & 0 deletions Whisky/Models/Program.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ extension Program {
}
}

@discardableResult
func togglePinned() -> Bool {
if pinned {
bottle.settings.pins.removeAll(where: { $0.url == url })
Expand Down
37 changes: 10 additions & 27 deletions Whisky/Views/Bottle Views/BottleView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,35 +89,18 @@ struct BottleView: View {
showWinetricksSheet.toggle()
}
Button("button.run") {
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseDirectories = false
panel.canChooseFiles = true
panel.allowedContentTypes = [UTType.exe,
UTType(exportedAs: "com.microsoft.msi-installer"),
UTType(exportedAs: "com.microsoft.bat")]
panel.directoryURL = bottle.url.appending(path: "drive_c")
panel.begin { result in
programLoading = true
Task(priority: .userInitiated) {
if result == .OK {
if let url = panel.urls.first {
do {
if url.pathExtension == "bat" {
try await Wine.runBatchFile(url: url, bottle: bottle)
} else {
try await Wine.runExternalProgram(url: url, bottle: bottle)
}
} catch {
print("Failed to run external program: \(error)")
}
programLoading = false
}
} else {
programLoading = false
}
Task {
guard let fileURL = await bottle.choseFileForRun() else { return }
programLoading = false

do {
try await bottle.openFileForRun(url: fileURL)
updateStartMenu()
} catch {
Bottle.logger.error("Failed to run external program: \(error)")
}

programLoading = false
}
}
.disabled(programLoading)
Expand Down
72 changes: 72 additions & 0 deletions Whisky/Views/Menu Bar/BottleBarView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// BottleBarView.swift
// Whisky
//
// This file is part of Whisky.
//
// Whisky 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 3 of the License, or (at your option) any later version.
//
// Whisky 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 Whisky.
// If not, see https://www.gnu.org/licenses/.
//

import SwiftUI
import WhiskyKit

/// A menu for a single bottle
struct BottleBarView: View {
@ObservedObject var bottle: Bottle

var body: some View {
Group {
Button("button.run", systemImage: "play") {
Task {
guard let fileURL = await bottle.choseFileForRun() else { return }

do {
try await bottle.openFileForRun(url: fileURL)
} catch {
Bottle.logger.error("Failed to run external program: \(error)")
}
}
}

Section("tab.programs") {
let pinnedPrograms = bottle.programs.pinned
let unpinnedPrograms = bottle.programs.unpinned
let changeCallback: () -> Void = {
// Force a reload by re-setting the programs
bottle.programs = bottle.programs
}

ForEach(pinnedPrograms, id: \.url) { program in
ProgramBarView(program: program, changed: changeCallback)
}

Menu("menubar.morePrograms") {
ForEach(unpinnedPrograms, id: \.url) { program in
ProgramBarView(program: program, changed: changeCallback)
}
}.badge(unpinnedPrograms.count)
}
}
}
}

private extension Sequence where Iterator.Element == Program {
/// Filter all pinned programs
var pinned: [Program] {
return self.filter({ $0.pinned })
}

/// Filter all unpinned programs
var unpinned: [Program] {
return self.filter({ !$0.pinned })
}
}
Loading

0 comments on commit 1f41efa

Please sign in to comment.