diff --git a/Swift Shift/src/Constants.swift b/Swift Shift/src/Constants.swift index 9430994..f582a2f 100644 --- a/Swift Shift/src/Constants.swift +++ b/Swift Shift/src/Constants.swift @@ -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 diff --git a/Swift Shift/src/View/ShortcutView.swift b/Swift Shift/src/View/ShortcutView.swift index 2f43da5..72b89db 100644 --- a/Swift Shift/src/View/ShortcutView.swift +++ b/Swift Shift/src/View/ShortcutView.swift @@ -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", @@ -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() @@ -362,7 +362,6 @@ 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) } @@ -370,7 +369,7 @@ struct ShortcutView: View { } private var mouseRow: some View { - HStack(spacing: 8) { + LazyVGrid(columns: shortcutRowColumns, spacing: 0) { TriggerToggleButton( title: "Mouse", icon: "computermouse", @@ -379,10 +378,8 @@ 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) @@ -390,6 +387,13 @@ struct ShortcutView: View { .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") @@ -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)) @@ -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) { @@ -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 diff --git a/www/src/images/mouse-dark.jpg b/www/src/images/mouse-dark.jpg index c4c510f..521febc 100644 Binary files a/www/src/images/mouse-dark.jpg and b/www/src/images/mouse-dark.jpg differ diff --git a/www/src/images/mouse-light.jpg b/www/src/images/mouse-light.jpg index aad9ea0..6290852 100644 Binary files a/www/src/images/mouse-light.jpg and b/www/src/images/mouse-light.jpg differ diff --git a/www/src/images/screenshot-dark.jpg b/www/src/images/screenshot-dark.jpg index b5b2db9..d9bd5ef 100644 Binary files a/www/src/images/screenshot-dark.jpg and b/www/src/images/screenshot-dark.jpg differ diff --git a/www/src/images/screenshot-light.jpg b/www/src/images/screenshot-light.jpg index 0c53442..35c1d3d 100644 Binary files a/www/src/images/screenshot-light.jpg and b/www/src/images/screenshot-light.jpg differ