Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update UI: BottleCreationView & PinCreationView #659

Merged
merged 4 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 64 additions & 65 deletions Whisky/Views/Bottle/BottleCreationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,90 +20,89 @@ import SwiftUI
import WhiskyKit

struct BottleCreationView: View {
@State var newBottleName: String = ""
@State var newBottleVersion: WinVersion = .win10
@State var newBottleURL: URL = BottleData.defaultBottleDir
@State var bottlePath: String = ""
@State var nameValid: Bool = false
@Binding var newlyCreatedBottleURL: URL?
@Environment(\.dismiss) var dismiss

@State private var newBottleName: String = ""
@State private var newBottleVersion: WinVersion = .win10
@State private var newBottleURL: URL = BottleData.defaultBottleDir
@State private var bottlePath: String = ""
@State private var nameValid: Bool = false

@Environment(\.dismiss) private var dismiss

var body: some View {
VStack {
HStack {
Text("create.title")
.bold()
Spacer()
}
Divider()
HStack(alignment: .top) {
Text("create.name")
Spacer()
TextField(String(), text: $newBottleName)
.frame(width: 180)
NavigationStack {
Form {
TextField("create.name", text: $newBottleName)
.onChange(of: newBottleName) { _, name in
nameValid = !name.isEmpty
}
}
HStack {
Text("create.win")
Spacer()
Picker(String(), selection: $newBottleVersion) {

Picker("create.win", selection: $newBottleVersion) {
ForEach(WinVersion.allCases.reversed(), id: \.self) {
Text($0.pretty())
}
}
.labelsHidden()
.frame(width: 180)
}
HStack {
Text("create.path")
Spacer()
Text(bottlePath)
.truncationMode(.middle)
.lineLimit(2)
.help(bottlePath)
Button("create.browse") {
let panel = NSOpenPanel()
panel.canChooseFiles = false
panel.canChooseDirectories = true
panel.allowsMultipleSelection = false
panel.canCreateDirectories = true
panel.directoryURL = BottleData.containerDir
panel.begin { result in
if result == .OK {
if let url = panel.urls.first {
newBottleURL = url

HStack {
VStack(alignment: .leading) {
Text("create.path")
.foregroundStyle(.primary)
Text(bottlePath)
.foregroundStyle(.secondary)
.truncationMode(.middle)
.lineLimit(2)
.help(bottlePath)
}

Spacer()
Button {
let panel = NSOpenPanel()
panel.canChooseFiles = false
panel.canChooseDirectories = true
panel.allowsMultipleSelection = false
panel.canCreateDirectories = true
panel.directoryURL = BottleData.containerDir
panel.begin { result in
if result == .OK {
if let url = panel.urls.first {
newBottleURL = url
}
}
}
} label: {
Text("create.browse")
}
}
}
Spacer()
HStack {
Spacer()
Button("create.cancel") {
dismiss()
.formStyle(.grouped)
.navigationTitle("create.title")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("create.cancel") {
dismiss()
}
.keyboardShortcut(.cancelAction)
}
.keyboardShortcut(.cancelAction)
Button("create.create") {
newlyCreatedBottleURL = BottleVM.shared.createNewBottle(bottleName: newBottleName,
winVersion: newBottleVersion,
bottleURL: newBottleURL)
dismiss()
ToolbarItem(placement: .primaryAction) {
Button("create.create") {
newlyCreatedBottleURL = BottleVM.shared.createNewBottle(bottleName: newBottleName,
winVersion: newBottleVersion,
bottleURL: newBottleURL)
dismiss()
}
.keyboardShortcut(.defaultAction)
.disabled(!nameValid)
}
.keyboardShortcut(.defaultAction)
.disabled(!nameValid)
}
.onChange(of: newBottleURL) {
bottlePath = newBottleURL.prettyPath()
}
.onAppear {
bottlePath = newBottleURL.prettyPath()
}
}
.padding()
.onChange(of: newBottleURL) {
bottlePath = newBottleURL.prettyPath()
}
.onAppear {
bottlePath = newBottleURL.prettyPath()
}
.frame(width: 400, height: 180)
.frame(minWidth: 400, minHeight: 210)
}
}

Expand Down
146 changes: 74 additions & 72 deletions Whisky/Views/Bottle/Pins/PinCreationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,98 +22,100 @@ import WhiskyKit

struct PinCreationView: View {
let bottle: Bottle
@State var newPinURL: URL?
@State var pinPath: String = ""

@State private var newPinURL: URL?
@State private var pinPath: String = ""
@State private var newPinName: String = ""
@State private var isDuplicate: Bool = false

@Environment(\.dismiss) private var dismiss

var body: some View {
VStack {
HStack {
Text("pin.title")
.bold()
Spacer()
}
Divider()
HStack(alignment: .top) {
Text("pin.name")
Spacer()
TextField(String(), text: $newPinName)
.frame(width: 180)
}
HStack {
Text("pin.path")
Spacer()
Text(pinPath)
.truncationMode(.middle)
.lineLimit(2)
.help(pinPath)
Button("create.browse") {
let panel = NSOpenPanel()
panel.canChooseFiles = true
panel.allowedContentTypes = [UTType.exe,
UTType(exportedAs: "com.microsoft.msi-installer"),
UTType(exportedAs: "com.microsoft.bat")]
panel.directoryURL = newPinURL ?? bottle.url.appending(path: "drive_c")
panel.canChooseDirectories = false
panel.allowsMultipleSelection = false
panel.canCreateDirectories = false
panel.begin { result in
if result == .OK {
if let url = panel.urls.first {
newPinURL = url
NavigationStack {
Form {
TextField("pin.name", text: $newPinName)

HStack {
VStack(alignment: .leading) {
Text("pin.path")
.foregroundStyle(.primary)
Text(pinPath)
.foregroundStyle(.secondary)
.truncationMode(.middle)
.lineLimit(2)
.help(pinPath)
}

Spacer()

Button("create.browse") {
let panel = NSOpenPanel()
panel.canChooseFiles = true
panel.allowedContentTypes = [UTType.exe,
UTType(exportedAs: "com.microsoft.msi-installer"),
UTType(exportedAs: "com.microsoft.bat")]
panel.directoryURL = newPinURL ?? bottle.url.appending(path: "drive_c")
panel.canChooseDirectories = false
panel.allowsMultipleSelection = false
panel.canCreateDirectories = false
panel.begin { result in
if result == .OK {
if let url = panel.urls.first {
newPinURL = url
}
}
}
}
}
}
HStack {
Spacer()
Button("create.cancel") {
dismiss()
.formStyle(.grouped)
.navigationTitle("pin.title")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("create.cancel") {
dismiss()
}
.keyboardShortcut(.cancelAction)
}
.keyboardShortcut(.cancelAction)
Button("pin.create") {
guard let newPinURL else { return }
ToolbarItem(placement: .primaryAction) {
Button("pin.create") {
guard let newPinURL else { return }

// Ensure this pin doesn't already exist
guard !bottle.settings.pins.contains(where: { $0.url == newPinURL })
else {
isDuplicate = true
return
}
// Ensure this pin doesn't already exist
guard !bottle.settings.pins.contains(where: { $0.url == newPinURL })
else {
isDuplicate = true
return
}

bottle.settings.pins.append(PinnedProgram(name: newPinName, url: newPinURL))
bottle.settings.pins.append(PinnedProgram(name: newPinName, url: newPinURL))

// Trigger a reload
bottle.updateInstalledPrograms()
dismiss()
}
.keyboardShortcut(.defaultAction)
.disabled(newPinName.isEmpty || newPinURL == nil)
.alert("pin.error.title",
isPresented: $isDuplicate
) {}
message: {
Text("pin.error.duplicate.\(newPinURL?.lastPathComponent ?? "unknown")")
// Trigger a reload
bottle.updateInstalledPrograms()
dismiss()
}
.keyboardShortcut(.defaultAction)
.disabled(newPinName.isEmpty || newPinURL == nil)
.alert("pin.error.title", isPresented: $isDuplicate) {
} message: {
Text("pin.error.duplicate.\(newPinURL?.lastPathComponent ?? "unknown")")
}
}
}
}
.onChange(of: newPinURL, initial: true) { oldValue, newValue in
guard let newValue = newValue else { return }
.onChange(of: newPinURL, initial: true) { oldValue, newValue in
guard let newValue = newValue else { return }

// Only reset newPinName if the textbox hasn't been modified
if newPinName.isEmpty ||
newPinName == oldValue?.deletingPathExtension().lastPathComponent {
// Only reset newPinName if the textbox hasn't been modified
if newPinName.isEmpty ||
newPinName == oldValue?.deletingPathExtension().lastPathComponent {

newPinName = newValue.deletingPathExtension().lastPathComponent
}
newPinName = newValue.deletingPathExtension().lastPathComponent
}

pinPath = newValue.prettyPath()
pinPath = newValue.prettyPath()
}
}
.padding()
.frame(width: 400)
.frame(minWidth: 400, minHeight: 170)
}
}

Expand Down
31 changes: 17 additions & 14 deletions Whisky/Views/Bottle/WinetricksView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,28 @@ struct WinetricksView: View {
}
}
}
HStack {
Spacer()
Button("create.cancel") {
dismiss()
}
Button("button.run") {
guard let selectedTrick = selectedTrick else {
return
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("create.cancel") {
dismiss()
}
}
ToolbarItem(placement: .primaryAction) {
Button("button.run") {
guard let selectedTrick = selectedTrick else {
return
}

let trick = winetricks.flatMap { $0.verbs }.first(where: { $0.id == selectedTrick })
if let trickName = trick?.name {
Task.detached {
await Winetricks.runCommand(command: trickName, bottle: bottle)
let trick = winetricks.flatMap { $0.verbs }.first(where: { $0.id == selectedTrick })
if let trickName = trick?.name {
Task.detached {
await Winetricks.runCommand(command: trickName, bottle: bottle)
}
}
dismiss()
}
dismiss()
.buttonStyle(.borderedProminent)
}
.buttonStyle(.borderedProminent)
}
} else {
Spacer()
Expand Down