Skip to content
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
2 changes: 1 addition & 1 deletion Swift Shift/src/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ let DEFAULT_IGNORED_APP_BUNDLE_ID = [
]

let IGNORE_APP_BUNDLE_ID = SYSTEM_IGNORED_APP_BUNDLE_ID + DEFAULT_IGNORED_APP_BUNDLE_ID
let MAIN_WINDOW_WIDTH = 360.0
let MAIN_WINDOW_WIDTH = 340.0
107 changes: 90 additions & 17 deletions Swift Shift/src/View/ShortcutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ struct ShortcutView: View {
}

private var keyboardRow: some View {
HStack(spacing: 8) {
LazyVGrid(columns: shortcutRowColumns, spacing: 0) {
TriggerToggleButton(
title: "Keyboard",
icon: "keyboard",
Expand All @@ -340,10 +340,10 @@ struct ShortcutView: View {
isFlashingError: keyboardTriggerErrorPulse,
onToggle: setKeyboardEnabled
)
.frame(width: 116)

HStack(spacing: 4) {
ShortcutRecorderView(shortcut: $shortcut.keyboardShortcut)
.frame(maxWidth: .infinity)
.onChange(of: shortcut.keyboardShortcut) { newValue in
shortcut.shortcut = newValue?.shortcutRecorderShortcut
saveShortcut()
Expand All @@ -362,15 +362,14 @@ struct ShortcutView: View {
.buttonStyle(.plain)
.disabled(!shortcut.keyboardEnabled)
}
.frame(maxWidth: .infinity)
.opacity(shortcut.keyboardEnabled ? 1 : 0.25)
.animation(.easeInOut(duration: 0.18), value: shortcut.keyboardEnabled)
}
.animation(.easeInOut(duration: 0.18), value: shortcut.keyboardEnabled)
}

private var mouseRow: some View {
HStack(spacing: 8) {
LazyVGrid(columns: shortcutRowColumns, spacing: 0) {
TriggerToggleButton(
title: "Mouse",
icon: "computermouse",
Expand All @@ -379,17 +378,22 @@ struct ShortcutView: View {
isFlashingError: mouseTriggerErrorPulse,
onToggle: setMouseEnabled
)
.frame(width: 116)

mouseButtonPicker
.frame(maxWidth: .infinity)
.disabled(!shortcut.mouseEnabled)
.opacity(shortcut.mouseEnabled ? 1 : 0.25)
.animation(.easeInOut(duration: 0.18), value: shortcut.mouseEnabled)
}
.animation(.easeInOut(duration: 0.18), value: shortcut.mouseEnabled)
}

private var shortcutRowColumns: [GridItem] {
[
GridItem(.flexible(), spacing: 8),
GridItem(.flexible(), spacing: 0)
]
}

private var errorToast: some View {
HStack(spacing: 6) {
Image(systemName: "exclamationmark.triangle.fill")
Expand All @@ -407,14 +411,29 @@ struct ShortcutView: View {
}

private var mouseButtonPicker: some View {
HStack(spacing: 4) {
ForEach([MouseButton.left, .right, .both], id: \.self) { mouseButton in
let isAvailable = shortcut.keyboardEnabled || mouseButton == .both
let selected = mouseButton == shortcut.mouseButton || (!shortcut.keyboardEnabled && mouseButton == .both)
LazyVGrid(
columns: [
GridItem(.flexible(), spacing: 4),
GridItem(.flexible(), spacing: 4)
],
spacing: 4
) {
ForEach([MouseButton.left, .right], id: \.self) { mouseButton in
let selected = isMouseButtonSelected(mouseButton)
Button {
guard isAvailable else { return }
shortcut.mouseButton = mouseButton
saveShortcut()
var leftSelected = isMouseButtonSelected(.left)
var rightSelected = isMouseButtonSelected(.right)

switch mouseButton {
case .left:
leftSelected.toggle()
case .right:
rightSelected.toggle()
case .both, .none:
break
}

setMouseButtonSelection(left: leftSelected, right: rightSelected)
} label: {
HStack(spacing: 3) {
Image(systemName: clickIcon(mouseButton))
Expand All @@ -431,12 +450,62 @@ struct ShortcutView: View {
.clipShape(Capsule())
}
.buttonStyle(.plain)
.frame(maxWidth: .infinity)
.foregroundStyle(selected ? .teal : .secondary)
.opacity(isAvailable ? 1 : 0.35)
.animation(.easeInOut(duration: 0.18), value: isAvailable)
.disabled(!isAvailable)
.accessibilityLabel("\(mouseButton.rawValue) mouse button")
.accessibilityValue(selected ? "On" : "Off")
.accessibilityAddTraits(selected ? .isSelected : [])
.help(mouseButton.rawValue)
}
}
}

private func isMouseButtonSelected(_ mouseButton: MouseButton) -> Bool {
if !shortcut.keyboardEnabled && shortcut.mouseEnabled {
return mouseButton == .left || mouseButton == .right || mouseButton == .both
}

switch mouseButton {
case .left:
return shortcut.mouseButton == .left || shortcut.mouseButton == .both
case .right:
return shortcut.mouseButton == .right || shortcut.mouseButton == .both
case .both:
return shortcut.mouseButton == .both
case .none:
return shortcut.mouseButton == .none
}
}

private func setMouseButtonSelection(left: Bool, right: Bool) {
if !shortcut.keyboardEnabled {
guard left && right else {
showTriggerError("Mouse-only shortcuts require both mouse buttons", on: .mouse)
return
}

shortcut.mouseEnabled = true
shortcut.mouseButton = .both
saveShortcut()
return
}

switch (left, right) {
case (true, true):
shortcut.mouseEnabled = true
shortcut.mouseButton = .both
case (true, false):
shortcut.mouseEnabled = true
shortcut.mouseButton = .left
case (false, true):
shortcut.mouseEnabled = true
shortcut.mouseButton = .right
case (false, false):
shortcut.mouseEnabled = false
shortcut.mouseButton = .none
}

saveShortcut()
}

private func setKeyboardEnabled(_ enabled: Bool) {
Expand Down Expand Up @@ -483,7 +552,11 @@ struct ShortcutView: View {
}

private func showTriggerConflict(on trigger: TriggerKind) {
errorToastMessage = "You cannot use the same trigger for both actions"
showTriggerError("You cannot use the same trigger for both actions", on: trigger)
}

private func showTriggerError(_ message: String, on trigger: TriggerKind) {
errorToastMessage = message

withAnimation(.easeIn(duration: 0.08)) {
errorToastVisible = true
Expand Down
Binary file modified www/src/images/mouse-dark.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified www/src/images/mouse-light.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified www/src/images/screenshot-dark.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified www/src/images/screenshot-light.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading