command picker for drawing toolkit

This commit is contained in:
True Zhuanjia
2026-04-07 14:35:06 +08:00
parent 9cd72271ca
commit 84adf3ac95
9 changed files with 461 additions and 142 deletions
+1 -1
View File
@@ -95,7 +95,7 @@ import Foundation
|| gamepad.leftThumbstick.yAxis.value != 0)
if navigationActionTriggeredPrivate { navigationActionTriggered = true}
else {
DispatchQueue.global().asyncAfter(deadline: .now() + 0.02) {
DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) {
if !navigationActionTriggeredPrivate {
navigationActionTriggered = false
}
+1
View File
@@ -836,6 +836,7 @@ import ObjectiveC.runtime
]
)
label.attributedText = attr
label.textAlignment = .center
}
private func setupView() {
+73 -7
View File
@@ -416,12 +416,34 @@ import UIKit
doubleTapShorcuts[i] = shortcut
}
}
@available(iOS 13.0, *)
func widgetPickerViewController(_ controller: WidgetPickerViewController, didCreateWidget payload: NSDictionary) {
let params = payload.mutableCopy() as? NSMutableDictionary ?? NSMutableDictionary()
let pickerAction = ((params["pickerAction"] as? String) ?? "").lowercased()
params.removeObject(forKey: "pickerAction")
if pickerAction == "create" {
}
}
@objc static public func enterDoubleTapShortcuts(in viewController: UIViewController){
let oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
selectedProfile = oscProfileMan.getSelectedProfile()
guard let selectedProfile = selectedProfile else {return}
if #available(iOS 13.0, *) {
let pickerViewController = WidgetPickerViewController()
pickerViewController.delegate = (viewController as! any WidgetPickerViewControllerDelegate)
pickerViewController.keyboardPickerMode = .shortcutPicker
pickerViewController.tabIdentifiers = ["keyboard"]
pickerViewController.initialTabIdentifier = "keyboard"
pickerViewController.shortcutIdentifier = "eraser"
pickerViewController.shortcutPickerTipText = SwiftLocalizationHelper.localizedString(forKey: "Select eraser shortcut keys")
let nav = UINavigationController(rootViewController: pickerViewController)
nav.modalPresentationStyle = .overFullScreen
viewController.present(nav, animated: true)
return
}
/*
let alert = UIAlertController(title: SwiftLocalizationHelper.localizedString(forKey: "Eraser Shortcut"),
message: SwiftLocalizationHelper.localizedString(forKey: "Enter eraser keyboard shortcut:"),
preferredStyle: .alert)
@@ -455,10 +477,24 @@ import UIKit
viewController.present(alert, animated: true, completion: {
})
*/
}
@available(iOS 13.0, *)
@objc static public func enterBrushShortcut(in viewController: UIViewController){
let pickerViewController = WidgetPickerViewController()
pickerViewController.delegate = (viewController as! any WidgetPickerViewControllerDelegate)
pickerViewController.keyboardPickerMode = .shortcutPicker
pickerViewController.tabIdentifiers = ["keyboard"]
pickerViewController.initialTabIdentifier = "keyboard"
pickerViewController.shortcutIdentifier = "brush"
pickerViewController.shortcutPickerTipText = SwiftLocalizationHelper.localizedString(forKey: "Select brush shortcut keys")
let nav = UINavigationController(rootViewController: pickerViewController)
nav.modalPresentationStyle = .overFullScreen
viewController.present(nav, animated: true)
return
/*
let alert = UIAlertController(title: SwiftLocalizationHelper.localizedString(forKey: "Brush Shortcut"),
message: SwiftLocalizationHelper.localizedString(forKey: "Enter brush keyboard shortcut:"),
preferredStyle: .alert)
@@ -500,13 +536,28 @@ import UIKit
viewController.present(alert, animated: true, completion: {
})
*/
}
@objc static private(set) var squeezeStartShortcut:String = ""
@objc static private(set) var squeezeEndShortcut:String = ""
@available(iOS 13.0, *)
@objc static public func enterSqueezeShortcuts(in viewController: UIViewController){
let pickerViewController = WidgetPickerViewController()
pickerViewController.delegate = (viewController as! any WidgetPickerViewControllerDelegate)
pickerViewController.keyboardPickerMode = .shortcutPicker
pickerViewController.tabIdentifiers = ["keyboard"]
pickerViewController.initialTabIdentifier = "keyboard"
pickerViewController.shortcutIdentifier = "squeezePress"
pickerViewController.shortcutPickerTipText = SwiftLocalizationHelper.localizedString(forKey: "squeezePressShortcutPickerTip")
let nav = UINavigationController(rootViewController: pickerViewController)
nav.modalPresentationStyle = .overFullScreen
viewController.present(nav, animated: true)
return
/*
let oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
selectedProfile = oscProfileMan.getSelectedProfile()
guard let selectedProfile = selectedProfile else {return}
@@ -543,10 +594,24 @@ import UIKit
viewController.present(alert, animated: true, completion: {
})
*/
}
@available(iOS 13.0, *)
@objc static public func enterSqueezeEndShortcut(in viewController: UIViewController){
let pickerViewController = WidgetPickerViewController()
pickerViewController.delegate = (viewController as! any WidgetPickerViewControllerDelegate)
pickerViewController.keyboardPickerMode = .shortcutPicker
pickerViewController.tabIdentifiers = ["keyboard"]
pickerViewController.initialTabIdentifier = "keyboard"
pickerViewController.shortcutIdentifier = "squeezeRelease"
pickerViewController.shortcutPickerTipText = SwiftLocalizationHelper.localizedString(forKey: "squeezeReleaseShortcutPickerTip")
let nav = UINavigationController(rootViewController: pickerViewController)
nav.modalPresentationStyle = .overFullScreen
viewController.present(nav, animated: true)
return
/*
let alert = UIAlertController(title: SwiftLocalizationHelper.localizedString(forKey: "Squeeze Shortcut"),
message: SwiftLocalizationHelper.localizedString(forKey: "enterSqueezeReleaseShort"),
preferredStyle: .alert)
@@ -588,6 +653,7 @@ import UIKit
viewController.present(alert, animated: true, completion: {
})
*/
}
private func attachHoverLeave(normalizedLocation:CGPoint){
+127 -29
View File
@@ -2988,6 +2988,29 @@
}
}
},
"Create shortcut" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "生成快捷键"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "生成快速鍵"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "生成快速鍵"
}
}
}
},
"Create widget" : {
"extractionState" : "manual",
"localizations" : {
@@ -4192,35 +4215,6 @@
}
}
},
"enterSqueezePressShort" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Enter shortcut on squeeze Press:"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "输入捏笔时发送的快捷键:"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "輸入捏筆時傳送的快速鍵:"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "輸入捏筆時傳送的快速鍵:"
}
}
}
},
"enterSqueezeReleaseShort" : {
"extractionState" : "manual",
"localizations" : {
@@ -9997,6 +9991,29 @@
}
}
},
"Select brush shortcut keys" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "选择笔刷快捷键组合"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "選擇筆刷快速鍵組合"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "選擇筆刷快速鍵組合"
}
}
}
},
"Select Control" : {
"extractionState" : "manual",
"localizations" : {
@@ -10020,6 +10037,29 @@
}
}
},
"Select eraser shortcut keys" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "选择橡皮擦快捷键组合"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "選擇橡皮擦快速鍵組合"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "選擇橡皮擦快速鍵組合"
}
}
}
},
"Select Gyro Button" : {
"extractionState" : "manual",
"localizations" : {
@@ -10700,6 +10740,64 @@
}
}
},
"squeezePressShortcutPickerTip" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Select shortcut keys on squeeze press"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "选择捏笔按压时发送的快捷键组合"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "選擇捏筆按壓時發送的快捷鍵組合"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "選擇捏筆按壓時發送的快捷鍵組合"
}
}
}
},
"squeezeReleaseShortcutPickerTip" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Select shortcut keys on squeeze release. To keep the previously selected keys pressed while squeezing, select “Null” on top right"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "选择捏笔松开时的快捷键组合, 若要在捏笔时保持之前选择的组合键按下, 选右上方”Null”"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "選擇捏筆鬆開時的快速鍵組合,若要在捏筆時保持之前選擇的組合鍵按下,選右上方「Null」"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "選擇捏筆鬆開時的快速鍵組合,若要在捏筆時保持之前選擇的組合鍵按下,選右上方「Null」"
}
}
}
},
"squeezeShortcutStackTip" : {
"extractionState" : "manual",
"localizations" : {
@@ -17,9 +17,10 @@
@class LayoutOnScreenControlsViewController;
@class MicHandler;
@protocol MicHandlerDelegate;
@class WidgetPickerViewController;
@protocol WidgetPickerViewControllerDelegate;
@interface SettingsViewController : UIViewController <RearNavigationBarMenuDelegate, MenuSectionDelegate, MicHandlerDelegate, UITextFieldDelegate>
@interface SettingsViewController : UIViewController <RearNavigationBarMenuDelegate, MenuSectionDelegate, UITextFieldDelegate, WidgetPickerViewControllerDelegate>
@property (strong, nonatomic) IBOutlet UINavigationBar *navigationBar;
@property (strong, nonatomic) UIStackView *parentStack;
@@ -3614,6 +3614,33 @@ BOOL isCustomResolution(int resolutionSelected) {
}
}
- (void)widgetPickerViewController:(WidgetPickerViewController *)controller didCreateWidget:(NSDictionary *)payload API_AVAILABLE(ios(13.0)){
OSCProfilesManager * profileMan = [OSCProfilesManager sharedManager:CGRectZero];
OSCProfile* profile = [profileMan getSelectedProfile];
NSString* shortcutIdentifier = [payload objectForKey:@"shortcutIdentifier"];
if([shortcutIdentifier isEqualToString:@"eraser"]) {
profile.eraserShortcut = [payload objectForKey:@"cmdString"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.8 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[PencilHandler enterBrushShortcutIn:self];
});
}
if([shortcutIdentifier isEqualToString:@"brush"]) {
profile.brushShortcut = [payload objectForKey:@"cmdString"];
}
if([shortcutIdentifier isEqualToString:@"squeezePress"]) {
profile.squeezeStartShortcut = [payload objectForKey:@"cmdString"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.8 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[PencilHandler enterSqueezeEndShortcutIn:self];
});
}
if([shortcutIdentifier isEqualToString:@"squeezeRelease"]) {
profile.squeezeEndShortcut = [payload objectForKey:@"cmdString"];
}
[profileMan replaceSelectedProfileWith:profile overwriteDefault:true];
}
/*
- (void)autoHoverSwitchFlipped:(UISwitch* )sender{
if(sender.isOn && !settingsViewJustLoaded){
+145 -64
View File
@@ -59,9 +59,10 @@ enum KeyHighlightStyle {
}
@available(iOS 13.0, *)
enum VirtualKeyboardMode {
public enum VirtualKeyboardMode {
case picker
case typing
case shortcutPicker
}
@available(iOS 13.0, *)
@@ -179,12 +180,12 @@ struct VirtualKeyboardView: View {
self.selectionSyncToken = selectionSyncToken
self.externalDeselectionCommand = externalDeselectionCommand
self.externalDeselectionToken = externalDeselectionToken
_showsMouseWidgets = SwiftUI.State(initialValue: mode == .picker)
_showsMouseWidgets = SwiftUI.State(initialValue: mode != .typing)
}
var body: some View {
Group {
if mode == .picker {
if usesPickerLayout {
GeometryReader { proxy in
let isPhone = UIDevice.current.userInterfaceIdiom == .phone
let horizontalInset: CGFloat = isPhone ? 6 : 12
@@ -298,7 +299,7 @@ struct VirtualKeyboardView: View {
height: keyHeight,
keyboardType: keyboardType,
mode: mode,
usesDirectionPadBaseStyle: mode == .picker,
usesDirectionPadBaseStyle: usesDirectionPadBaseStyle,
isActive:
(key.role == .fn && fnMode)
|| (key.role == .keyboardSwitch && keyboardType == .mac),
@@ -325,7 +326,7 @@ struct VirtualKeyboardView: View {
height: height ?? keyHeight,
keyboardType: keyboardType,
mode: mode,
usesDirectionPadBaseStyle: mode == .picker,
usesDirectionPadBaseStyle: usesDirectionPadBaseStyle,
isActive: isActive,
highlightStyle: highlightStyle(for: key),
action: {
@@ -345,9 +346,9 @@ struct VirtualKeyboardView: View {
case .keyboardSwitch:
keyboardType = keyboardType == .win ? .mac : .win
case .normal:
if mode == .picker, let family = selectionFamily(for: key) {
if usesPickerSelectionBehavior, let family = selectionFamily(for: key) {
handleDirectionKeyTap(key, family: family)
} else if mode == .picker || isTypingToggleKey(key) {
} else if usesPickerSelectionBehavior || isTypingToggleKey(key) {
toggleRegularKeyHighlight(key)
}
}
@@ -399,7 +400,9 @@ struct VirtualKeyboardView: View {
if VirtualKeyboardView.selectedCmd == padCommand(for: family) {
VirtualKeyboardView.selectedCmd = ""
}
return
if mode != .shortcutPicker {
return
}
}
if directionSingleKeyLabels.contains(key.label) {
@@ -413,6 +416,29 @@ struct VirtualKeyboardView: View {
return
}
if mode == .shortcutPicker {
let command = key.cmdString(for: keyboardType) ?? key.label
if canSelectCommand?(command) == false {
return
}
let option = KeyboardWidgetOption(
command: command,
description: singleKeyDescription(for: key, family: family),
highlightedLabels: [key.label],
family: family,
selectionState: .single(key.label)
)
applySelection(option)
VirtualKeyboardView.tappedKeyLabels = [key.label]
VirtualKeyboardView.selectedCmd = command
VirtualKeyboardView.lastSelectionDisplayText = poolDisplayText(for: option)
VirtualKeyboardView.lastSelectionUsesMacLayout = keyboardType == .mac
VirtualKeyboardView.lastSelectionFromMouseWidgets = false
VirtualKeyboardView.lastSelectionUsesFnCommandDisplay = false
onCommandSelected?(command)
return
}
pendingSelectionFamily = family
pendingKeyboardOptions = keyboardOptions(for: key, family: family)
VirtualKeyboardView.tappedKeyLabels = [key.label]
@@ -590,12 +616,12 @@ struct VirtualKeyboardView: View {
}
private func highlightStyle(for key: Key) -> KeyHighlightStyle {
if mode == .picker, let family = selectionFamily(for: key), padSelections.contains(family) {
if usesPadSelectionHighlight, let family = selectionFamily(for: key), padSelections.contains(family) {
return .pad
}
if highlightedKeyIDs.contains(regularHighlightID(for: key))
|| (mode == .picker && directionSingleKeyLabels.contains(key.label)) {
|| (usesPickerSelectionBehavior && directionSingleKeyLabels.contains(key.label)) {
return isPadLikeControl(key) ? .pad : .single
}
@@ -770,7 +796,7 @@ struct VirtualKeyboardView: View {
// MARK: - 小键盘 / 鼠标控件区
var numpadBlock: some View {
VStack(spacing: spacing) {
if mode == .picker {
if usesPickerLayout {
HStack(spacing: spacing) {
KeyView(
key: Key(
@@ -813,7 +839,7 @@ struct VirtualKeyboardView: View {
}
}
if mode == .picker {
if usesPickerLayout {
if showsMouseWidgets {
mouseWidgetsBlock
} else {
@@ -980,65 +1006,93 @@ struct VirtualKeyboardView: View {
)
}
HStack(spacing: spacing) {
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Wheel\nUp"),
width: unit,
winCmdString: "WHEELDOWN",
macCmdString: "WHEELDOWN",
winKeyCode: 0xFF,
macKeyCode: 0xFF
if mode == .shortcutPicker {
HStack(spacing: spacing) {
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Wheel\nUp"),
width: unit,
winCmdString: "WHEELDOWN",
macCmdString: "WHEELDOWN",
winKeyCode: 0xFF,
macKeyCode: 0xFF
)
)
)
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Wheel\nDown"),
width: unit,
winCmdString: "WHEELUP",
macCmdString: "WHEELUP",
winKeyCode: 0xFF,
macKeyCode: 0xFF
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Wheel\nDown"),
width: unit,
winCmdString: "WHEELUP",
macCmdString: "WHEELUP",
winKeyCode: 0xFF,
macKeyCode: 0xFF
)
)
}
} else {
HStack(spacing: spacing) {
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Wheel\nUp"),
width: unit,
winCmdString: "WHEELDOWN",
macCmdString: "WHEELDOWN",
winKeyCode: 0xFF,
macKeyCode: 0xFF
)
)
)
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "WheelPad"),
width: unit * 2 + spacing,
winCmdString: "WHEEL",
macCmdString: "WHEEL",
winKeyCode: 0xFF,
macKeyCode: 0xFF
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Wheel\nDown"),
width: unit,
winCmdString: "WHEELUP",
macCmdString: "WHEELUP",
winKeyCode: 0xFF,
macKeyCode: 0xFF
)
)
)
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "WheelPad"),
width: unit * 2 + spacing,
winCmdString: "WHEEL",
macCmdString: "WHEEL",
winKeyCode: 0xFF,
macKeyCode: 0xFF
)
)
}
}
HStack(spacing: spacing) {
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "MousePad"),
width: unit * 2 + spacing,
winCmdString: "MOUSEPAD",
macCmdString: "MOUSEPAD",
winKeyCode: 0xFF,
macKeyCode: 0xFF
),
height: keyHeight * 2 + spacing
)
if mode != .shortcutPicker {
HStack(spacing: spacing) {
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "MousePad"),
width: unit * 2 + spacing,
winCmdString: "MOUSEPAD",
macCmdString: "MOUSEPAD",
winKeyCode: 0xFF,
macKeyCode: 0xFF
),
height: keyHeight * 2 + spacing
)
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Trackball"),
width: unit * 2 + spacing,
winCmdString: "TRACKBALL",
macCmdString: "TRACKBALL",
winKeyCode: 0xFF,
macKeyCode: 0xFF
),
height: keyHeight * 2 + spacing
)
keyboardKeyView(
Key(
label: SwiftLocalizationHelper.localizedString(forKey: "Trackball"),
width: unit * 2 + spacing,
winCmdString: "TRACKBALL",
macCmdString: "TRACKBALL",
winKeyCode: 0xFF,
macKeyCode: 0xFF
),
height: keyHeight * 2 + spacing
)
}
}
}
}
@@ -1210,7 +1264,7 @@ struct VirtualKeyboardView: View {
}
private var mouseWidgetKeys: [Key] {
[
var keys = [
Key(label: SwiftLocalizationHelper.localizedString(forKey: "Left Button"), width: unit * 2 + spacing, winCmdString: "MLEFT", macCmdString: "MLEFT", winKeyCode: 0xFF, macKeyCode: 0xFF),
Key(label: SwiftLocalizationHelper.localizedString(forKey: "Right Button"), width: unit * 2 + spacing, winCmdString: "MRIGHT", macCmdString: "MRIGHT", winKeyCode: 0xFF, macKeyCode: 0xFF),
Key(label: SwiftLocalizationHelper.localizedString(forKey: "Middle Button"), width: unit * 2 + spacing, winCmdString: "MMIDDLE", macCmdString: "MMIDDLE", winKeyCode: 0xFF, macKeyCode: 0xFF),
@@ -1222,6 +1276,29 @@ struct VirtualKeyboardView: View {
Key(label: SwiftLocalizationHelper.localizedString(forKey: "MousePad"), width: unit * 2 + spacing, winCmdString: "MOUSEPAD", macCmdString: "MOUSEPAD", winKeyCode: 0xFF, macKeyCode: 0xFF),
Key(label: SwiftLocalizationHelper.localizedString(forKey: "Trackball"), width: unit * 2 + spacing, winCmdString: "TRACKBALL", macCmdString: "TRACKBALL", winKeyCode: 0xFF, macKeyCode: 0xFF)
]
if mode == .shortcutPicker {
keys.removeAll { key in
let command = key.cmdString(for: keyboardType) ?? ""
return command == "WHEEL" || command == "MOUSEPAD" || command == "TRACKBALL"
}
}
return keys
}
private var usesPickerLayout: Bool {
mode != .typing
}
private var usesPickerSelectionBehavior: Bool {
mode == .picker || mode == .shortcutPicker
}
private var usesPadSelectionHighlight: Bool {
mode == .picker
}
private var usesDirectionPadBaseStyle: Bool {
mode == .picker
}
}
@@ -1528,6 +1605,10 @@ struct VirtualKeyboardView_Previews: PreviewProvider {
VirtualKeyboardView(mode: .picker)
.frame(height: 320)
.previewDisplayName("Picker")
VirtualKeyboardView(mode: .shortcutPicker)
.frame(height: 320)
.previewDisplayName("Shortcut Picker")
}
}
}
+78 -39
View File
@@ -283,6 +283,9 @@ struct WidgetPickerView: View {
private let initialShape: String?
private let availableTabs: [WidgetPickerTab]
private let preferredInitialTab: WidgetPickerTab?
private let keyboardPickerMode: VirtualKeyboardMode
private let shortcutPickerTipText: String?
private let shortcutIdentifier: String?
var onWidgetCreated: (([String: String]) -> Void)? = nil
var onCloseRequested: (() -> Void)? = nil
@@ -291,7 +294,7 @@ struct WidgetPickerView: View {
@SwiftUI.State private var selectedCmds: [String] = []
@SwiftUI.State private var poolItems: [WidgetPoolItem] = []
@SwiftUI.State private var resetToken: Int = 0
@SwiftUI.State private var tipMessage: String = SwiftLocalizationHelper.localizedString(forKey: "Tap any control to add it")
@SwiftUI.State private var tipMessage: String
@SwiftUI.State private var tipMessageType: PoolTipMessageType = .normal
@SwiftUI.State private var keyboardDeselectionCommand: String = ""
@SwiftUI.State private var keyboardDeselectionToken: Int = 0
@@ -316,6 +319,9 @@ struct WidgetPickerView: View {
initialShape: String? = nil,
availableTabs: [WidgetPickerTab] = WidgetPickerTab.allCases,
preferredInitialTab: WidgetPickerTab? = nil,
keyboardPickerMode: VirtualKeyboardMode = .picker,
shortcutPickerTipText: String? = nil,
shortcutIdentififier: String? = nil,
onWidgetCreated: (([String: String]) -> Void)? = nil,
onCloseRequested: (() -> Void)? = nil
) {
@@ -327,9 +333,17 @@ struct WidgetPickerView: View {
self.initialShape = initialShape
self.availableTabs = normalizedTabs
self.preferredInitialTab = resolvedInitialTab
self.keyboardPickerMode = keyboardPickerMode
self.shortcutPickerTipText = shortcutPickerTipText
self.shortcutIdentifier = shortcutIdentififier
self.onWidgetCreated = onWidgetCreated
self.onCloseRequested = onCloseRequested
_selectedTab = SwiftUI.State(initialValue: resolvedInitialTab ?? (normalizedTabs.contains(.keyboard) ? .keyboard : normalizedTabs[0]))
_tipMessage = SwiftUI.State(
initialValue: (keyboardPickerMode == .shortcutPicker && !(shortcutPickerTipText ?? "").isEmpty)
? (shortcutPickerTipText ?? "")
: SwiftLocalizationHelper.localizedString(forKey: "Tap any control to add it")
)
}
private let functionalButtonOptions: [FunctionalButtonOption] = [
@@ -487,7 +501,10 @@ struct WidgetPickerView: View {
]
private var defaultTipMessage: String {
SwiftLocalizationHelper.localizedString(forKey: "Tap any control to add it")
if isShortcutPickerMode, let shortcutPickerTipText, !shortcutPickerTipText.isEmpty {
return shortcutPickerTipText
}
return SwiftLocalizationHelper.localizedString(forKey: "Tap any control to add it")
}
private var tipMessageColor: Color {
@@ -500,6 +517,11 @@ struct WidgetPickerView: View {
}
private func setTipMessage(_ message: String, type: PoolTipMessageType = .normal) {
if isShortcutPickerMode, let shortcutPickerTipText, !shortcutPickerTipText.isEmpty {
tipMessage = shortcutPickerTipText
tipMessageType = .normal
return
}
tipMessage = message
tipMessageType = type
}
@@ -678,7 +700,7 @@ struct WidgetPickerView: View {
.padding(.vertical, pickerInsetGamepadVertical)
case .keyboard:
VirtualKeyboardView(
mode: .picker,
mode: keyboardPickerMode,
canSelectCommand: { canSelectCommand($0, source: .keyboard) },
isCommandSelected: { selectedCmds.contains($0) },
onCommandSelected: { appendCommand($0, source: .keyboard) },
@@ -852,44 +874,48 @@ struct WidgetPickerView: View {
.frame(maxWidth: poolGridWidth ?? .infinity, alignment: .center)
.frame(maxWidth: .infinity, alignment: .center)
if keyboardPickerMode != .shortcutPicker {
PoolActionChip(
title: SwiftLocalizationHelper.localizedString(forKey: "Gyro switch button"),
isPrimary: false,
height: poolChipHeight,
fontSize: poolChipFontSize,
cornerRadius: metrics.chipCornerRadius
) {
handleGyroButtonTap()
}
.overlay(
Group {
if selectedGyroCommand != nil {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.stroke(Color.orange.opacity(0.75), lineWidth: 1.4)
.background(
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(
LinearGradient(
gradient: Gradient(colors: [
Color(red: 1.00, green: 0.96, blue: 0.74).opacity(0.34),
Color(red: 0.95, green: 0.78, blue: 0.32).opacity(0.22)
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
)
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
.allowsHitTesting(false)
}
title: SwiftLocalizationHelper.localizedString(forKey: "Gyro switch button"),
isPrimary: false,
height: poolChipHeight,
fontSize: poolChipFontSize,
cornerRadius: metrics.chipCornerRadius
) {
handleGyroButtonTap()
}
)
.overlay(
Group {
if selectedGyroCommand != nil {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.stroke(Color.orange.opacity(0.75), lineWidth: 1.4)
.background(
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(
LinearGradient(
gradient: Gradient(colors: [
Color(red: 1.00, green: 0.96, blue: 0.74).opacity(0.34),
Color(red: 0.95, green: 0.78, blue: 0.32).opacity(0.22)
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
)
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
.allowsHitTesting(false)
}
}
)
}
HStack(spacing: metrics.isPhone ? 8 : 12) {
PoolActionChip(
title: isEditMode
? WidgetPickerSubmissionAction.create.poolActionTitle
: SwiftLocalizationHelper.localizedString(forKey: "Create widget"),
title: isShortcutPickerMode
? SwiftLocalizationHelper.localizedString(forKey: "Create shortcut")
: (isEditMode
? WidgetPickerSubmissionAction.create.poolActionTitle
: SwiftLocalizationHelper.localizedString(forKey: "Create widget")),
isPrimary: true,
height: poolChipHeight,
fontSize: poolChipFontSize,
@@ -1662,7 +1688,15 @@ struct WidgetPickerView: View {
return "{\n \"cmdString\": \"\(payload["cmdString"] ?? "")\",\n \"buttonLabel\": \"\(payload["buttonLabel"] ?? "")\",\n \"shape\": \"\(payload["shape"] ?? "")\"\n}"
}
private var isShortcutPickerMode: Bool {
keyboardPickerMode == .shortcutPicker
}
private var shouldBypassCreateWidgetSheet: Bool {
if isShortcutPickerMode {
return true
}
guard !poolItems.isEmpty else { return false }
let hasVisibleConfigurationSection =
@@ -1724,7 +1758,7 @@ struct WidgetPickerView: View {
}
private func presentCreateWidgetSheet(for submissionAction: WidgetPickerSubmissionAction) {
guard !poolItems.isEmpty else {
guard isShortcutPickerMode || !poolItems.isEmpty else {
setTipMessage(SwiftLocalizationHelper.localizedString(forKey: "Select at least one command before creating a widget"), type: .error)
return
}
@@ -1740,7 +1774,7 @@ struct WidgetPickerView: View {
}
private func submitWidgetPayload() {
let payload = makeWidgetPayload()
var payload = makeWidgetPayload()
lastCreatedWidgetPayload = payload
setTipMessage(SwiftLocalizationHelper.localizedString(forKey: "Widget config generated"))
onWidgetCreated?(payload)
@@ -1753,11 +1787,16 @@ struct WidgetPickerView: View {
"cmdString": buildCmdString(),
"buttonLabel": targetWidgetKind == .button ? widgetButtonLabel : "",
"shape": targetWidgetKind == .button ? widgetShape.rawValue : "",
"pickerAction": pendingSubmissionAction.payloadValue
"pickerAction": pendingSubmissionAction.payloadValue,
"shortcutIdentifier": shortcutIdentifier ?? ""
]
}
private func buildCmdString() -> String {
if isShortcutPickerMode {
return selectedCmds.joined(separator: "+")
}
let joiner = effectiveWidgetComboMode == .shortcut && showsComboModeControl ? "+" : "-"
var cmdString = selectedCmds.joined(separator: joiner)
@@ -17,6 +17,9 @@ public final class WidgetPickerViewController: UIViewController {
public var initialShape: String?
public var tabIdentifiers: [String] = []
public var initialTabIdentifier: String?
public var keyboardPickerMode: VirtualKeyboardMode = .picker
public var shortcutPickerTipText: String?
@objc public var shortcutIdentifier: String?
private var hostingViewController: UIHostingController<WidgetPickerView>?
@@ -32,6 +35,9 @@ public final class WidgetPickerViewController: UIViewController {
initialShape: initialShape,
availableTabs: resolvedTabs(),
preferredInitialTab: resolvedInitialTab(),
keyboardPickerMode: keyboardPickerMode,
shortcutPickerTipText: shortcutPickerTipText,
shortcutIdentififier: shortcutIdentifier,
onWidgetCreated: { [weak self] payload in
guard let self else { return }
self.delegate?.widgetPickerViewController(self, didCreateWidget: payload as NSDictionary)