Skip to content

Commit

Permalink
Key Presses with Custom String input
Browse files Browse the repository at this point in the history
Implements #1888
  • Loading branch information
Carlos Cabanero committed Nov 2, 2023
1 parent 5b02941 commit 77d3cec
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 37 deletions.
2 changes: 1 addition & 1 deletion Blink/SpaceController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ extension SpaceController {

// input.reportStateReset()
switch cmd.bindingAction {
case .hex(let hex, comment: _):
case .hex(let hex, stringInput: _, comment: _):
input.reportHex(hex)
case .press(let keyCode, mods: let mods):
input.reportPress(UIKeyModifierFlags(rawValue: mods), keyId: keyCode.id)
Expand Down
55 changes: 33 additions & 22 deletions KB/Native/Model/KeyBindingAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,14 @@ enum Command: String, Codable, CaseIterable {
}

enum KeyBindingAction: Codable, Identifiable {
case hex(String, comment: String?)
case hex(String, stringInput: String?, comment: String?)
case press(KeyCode, mods: Int)
case command(Command)
case none

var id: String {
switch self {
case .hex(let str, _): return "hex-\(str)"
case .hex(let str, _, _): return "hex-\(str)"
case .press(let keyCode, let mods): return "press-\(keyCode.id)-\(mods)"
case .command(let cmd): return "cmd-\(cmd)"
case .none: return "none"
Expand All @@ -133,12 +133,20 @@ enum KeyBindingAction: Codable, Identifiable {

var isCustomHEX: Bool {
switch self {
case .hex(_, comment: let comment):
case .hex(_, _, comment: let comment):
return comment == nil
default: return false
}
}

var isHexStringInput: Bool {
switch self {
case .hex(_, stringInput: let stringInput, comment: let comment):
return comment == nil && stringInput != nil
default: return false
}
}

static func press(_ keyCode: KeyCode, _ mods: UIKeyModifierFlags) -> KeyBindingAction {
KeyBindingAction.press(keyCode, mods: mods.rawValue)
}
Expand All @@ -164,15 +172,15 @@ enum KeyBindingAction: Codable, Identifiable {
.press(.f10, []),
.press(.f11, []),
.press(.f12, []),
.hex("03", comment: "Press ^C"),
.hex("16", comment: "Press ^V"),
.hex("3C", comment: "Press <"),
.hex("3E", comment: "Press >"),
.hex("A7", comment: "Press §"),
.hex("B1", comment: "Press ±"),
.hex("7E", comment: "Press ~"),
.hex("7C", comment: "Press |"),
.hex("5C", comment: "Press \\"),
.hex("03", stringInput: nil, comment: "Press ^C"),
.hex("16", stringInput: nil, comment: "Press ^V"),
.hex("3C", stringInput: nil, comment: "Press <"),
.hex("3E", stringInput: nil, comment: "Press >"),
.hex("A7", stringInput: nil, comment: "Press §"),
.hex("B1", stringInput: nil, comment: "Press ±"),
.hex("7E", stringInput: nil, comment: "Press ~"),
.hex("7C", stringInput: nil, comment: "Press |"),
.hex("5C", stringInput: nil, comment: "Press \\"),
.press(.w, [.command]),
.press(.t, [.command]),
// .press(.left, [.shift, .command]),
Expand All @@ -186,8 +194,8 @@ enum KeyBindingAction: Codable, Identifiable {

var title: String {
switch self {
case .hex(let str, comment: let comment):
return comment ?? "Hex: \(str)"
case .hex(let str, stringInput: let stringInput, comment: let comment):
return comment ?? (stringInput != nil ? "String: \(stringInput!)" : "Hex: \(str)")
case .press(let keyCode, let mods):
var sym = UIKeyModifierFlags(rawValue: mods).toSymbols()
sym += keyCode.symbol
Expand All @@ -199,17 +207,17 @@ enum KeyBindingAction: Codable, Identifiable {

var titleWithoutValue: String {
switch self {
case .hex(_, comment: let comment):
return comment ?? "Send Hex Code"
case .hex(_, stringInput: let stringInput, comment: let comment):
return comment ?? (stringInput != nil ? "Send Input" : "Send Hex Code")
default: return title
}
}

var hexValue: String {
var hexValues : (String, String?) {
switch self {
case .hex(let str, comment: _):
return str
default: return ""
case .hex(let str, stringInput: let stringInput, comment: _):
return (str, stringInput)
default: return ("", nil)
}
}

Expand All @@ -219,6 +227,7 @@ enum KeyBindingAction: Codable, Identifiable {
case type
case hex
case value
case stringInput
case key
case press
case mods
Expand All @@ -230,9 +239,10 @@ enum KeyBindingAction: Codable, Identifiable {
func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: Keys.self)
switch self {
case .hex(let str, comment: let comment):
case .hex(let str, stringInput: let stringInput, comment: let comment):
try c.encode(Keys.hex.stringValue, forKey: .type)
try c.encode(str, forKey: .value)
try c.encodeIfPresent(stringInput, forKey: .stringInput)
try c.encodeIfPresent(comment, forKey: .comment)
case .press(let keyCode, let mods):
try c.encode(Keys.press.stringValue, forKey: .type)
Expand All @@ -254,8 +264,9 @@ enum KeyBindingAction: Codable, Identifiable {
switch k {
case .hex:
let hex = try c.decode(String.self, forKey: .value)
let stringInput = try c.decodeIfPresent(String.self, forKey: .stringInput)
let comment = try c.decodeIfPresent(String.self, forKey: .comment)
self = .hex(hex, comment: comment)
self = .hex(hex, stringInput: stringInput, comment: comment)
case .press:
let keyCode = try c.decode(KeyCode.self, forKey: .key)
let mods = try c.decode(Int.self, forKey: .mods)
Expand Down
109 changes: 95 additions & 14 deletions KB/Native/Views/ShortcutsConfigView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ struct ActionsList: View {
} else {
Section(header: Text("Send")) {
self._rowHex(action: self.action)
self._rowCustomInput(action: self.action)
}
Section(header: Text("Press")) {
ForEach(pressList, id: \.id) { ka in
Expand All @@ -66,7 +67,8 @@ struct ActionsList: View {
private func _rowHex(action: KeyBindingAction) -> some View {
var checked = false
var value = ""
if case .hex(let val, let comment) = action, comment == nil {

if case .hex(let val, let input, let comment) = action, input == nil, comment == nil {
checked = true
value = val
}
Expand All @@ -76,7 +78,30 @@ struct ActionsList: View {
Checkmark(checked: checked)
}.overlay(
Button(action: {
self.action = .hex(value, comment: nil)
self.action = .hex(value, stringInput: nil, comment: nil)
self.updatedAt = Date()
}, label: { EmptyView() }
)
)
}

private func _rowCustomInput(action: KeyBindingAction) -> some View {
var checked = false
var value = ""
var stringInput = ""

if case .hex(let val, let input, let comment) = action, input != nil, comment == nil {
checked = true
value = val
stringInput = input!
}
return HStack {
Text("Custom String")
Spacer()
Checkmark(checked: checked)
}.overlay(
Button(action: {
self.action = .hex(value, stringInput: stringInput, comment: nil)
self.updatedAt = Date()
}, label: { EmptyView() }
)
Expand Down Expand Up @@ -132,22 +157,77 @@ class HexFormatter: Formatter {
.prefix(1000)
)
}

func stringToHexString(_ input: String) -> String {
var result = ""
var currentIndex = input.startIndex

while currentIndex < input.endIndex {
let currentCharacter = input[currentIndex]

if currentCharacter == "\\" && input.index(currentIndex, offsetBy: 1) < input.endIndex && input[input.index(currentIndex, offsetBy: 1)] == "x" {
// Skip "\x"
currentIndex = input.index(currentIndex, offsetBy: 2)
var hexSubstring = ""
for _ in 0..<2 {
if currentIndex < input.endIndex, "0123456789ABCDEFabcdef".contains(input[currentIndex]) {
hexSubstring.append(input[currentIndex])
currentIndex = input.index(after: currentIndex)
} else {
break
}
}
// Ensure is double digit.
if hexSubstring.count == 1 {
hexSubstring = "0" + hexSubstring
}
result.append(hexSubstring)
} else {
let hexValue = String(format: "%02X", currentCharacter.unicodeScalars.first?.value ?? 0)
result.append(hexValue)
currentIndex = input.index(after: currentIndex)
}
}

return result
}
}

struct HexEditorView: View {
@ObservedObject var shortcut: KeyShortcut
@State var value: String = ""
@State var input: String = ""
var value: String { shortcut.action.hexValues.0 }
var stringInput: String? { shortcut.action.hexValues.1 }
private let _formatter = HexFormatter()

var body: some View {
TextField("HEX", text: $value, onEditingChanged: { _ in
// Whenever the view is first shown, enter pressed, tap back on Navigation Link & TextField selected
// Update the HEX code using the HexFormatter to only accept valid HEX encoded Strings
value = _formatter.hexString(str: value)
shortcut.action = .hex(value, comment: nil)
})
.disableAutocorrection(true)
.keyboardType(.asciiCapable)
_editor()
.onAppear {
if let stringInput = stringInput {
self.input = stringInput
} else {
self.input = value
}
}
.disableAutocorrection(true)
.keyboardType(.asciiCapable)
}

private func _editor() -> some View {
if self.stringInput != nil {
return TextField("Custom String", text: $input,
onEditingChanged: { _ in
let value = _formatter.stringToHexString(input)
shortcut.action = .hex(value, stringInput: input, comment: nil)
})
} else {
return TextField("HEX", text: $input, onEditingChanged: { _ in
// Whenever the view is first shown, enter pressed, tap back on Navigation Link & TextField selected
// Update the HEX code using the HexFormatter to only accept valid HEX encoded Strings
let value = _formatter.hexString(str: input)
shortcut.action = .hex(value, stringInput: nil, comment: nil)
})
}
}
}

Expand All @@ -171,15 +251,16 @@ struct ShortcutConfigView: View {
}
Section(
header: Text("Action"),
footer: Text(self.shortcut.action.isCustomHEX ? "Use hex encoded sequence" : ""))
footer: Text(self.shortcut.action.isCustomHEX ? (self.shortcut.action.isHexStringInput ?
"Use string sequence, with \\x for escape characters." :
"Use hex encoded sequence") : ""))
{
DefaultRow(title: shortcut.action.titleWithoutValue) {
ActionsList(action: self.$shortcut.action, commandsMode: self.commandsMode)
}
if self.shortcut.action.isCustomHEX {
HexEditorView(
shortcut: self.shortcut,
value: self.shortcut.action.hexValue
shortcut: self.shortcut
)
}
}
Expand Down

0 comments on commit 77d3cec

Please sign in to comment.