-
Notifications
You must be signed in to change notification settings - Fork 79
/
KeyboardKeys.swift
421 lines (383 loc) · 15.5 KB
/
KeyboardKeys.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
/**
* Classes and variables that define keys for Scribe keyboards.
*
* Copyright (C) 2024 Scribe
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import UIKit
// The keys collection as well as one for the padding for placements.
var keyboardKeys: [UIButton] = []
var paddingViews: [UIButton] = []
/// Class of UIButton that allows the tap area to be increased so that edges between keys can still receive user input.
class KeyboardKey: UIButton {
// Properties for the touch area - passing negative values will expand the touch area.
var topShift = CGFloat(0)
var leftShift = CGFloat(0)
var bottomShift = CGFloat(0)
var rightShift = CGFloat(0)
/// Allows the bounds of the key to be expanded.
override func point(inside point: CGPoint, with _: UIEvent?) -> Bool {
return bounds.inset(by: UIEdgeInsets(
top: topShift,
left: leftShift,
bottom: bottomShift,
right: rightShift
)
).contains(point)
}
var row: Int!
var idx: Int!
var key: String!
/// Styles the key with a color, corner radius and shadow.
func style() {
backgroundColor = keyColor
layer.cornerRadius = keyCornerRadius
layer.shadowColor = keyShadowColor
layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
layer.shadowOpacity = 1.0
layer.shadowRadius = 0.0
layer.masksToBounds = false
}
/// Sets the character of the key and defines its capitalized state.
func setChar() {
key = keyboard[row][idx]
if key == "space" {
key = showKeyboardLanguage ? languageTextForSpaceBar : spaceBar
layer.setValue(true, forKey: "isSpecial")
}
var capsKey = ""
if key != "ß"
&& key != "´"
&& key != spaceBar
&& key != languageTextForSpaceBar
&& key != "ABC"
&& key != "АБВ" {
capsKey = keyboard[row][idx].capitalized
} else {
capsKey = key
}
let keyToDisplay = shiftButtonState == .shift || capsLockButtonState == .locked ? capsKey : key
setTitleColor(keyCharColor, for: .normal)
layer.setValue(key, forKey: "original")
layer.setValue(keyToDisplay, forKey: "keyToDisplay")
layer.setValue(false, forKey: "isSpecial")
setTitle(keyToDisplay, for: .normal) // set button character
if showKeyboardLanguage && key == languageTextForSpaceBar {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.layer.setValue(spaceBar, forKey: "original")
self.layer.setValue(spaceBar, forKey: "keyToDisplay")
self.setTitle(spaceBar, for: .normal)
showKeyboardLanguage = false
}
}
}
/// Sets the character size of a capital key if the device is an iPhone given the orientation.
func setPhoneCapCharSize() {
if isLandscapeView {
if key == "#+="
|| key == "ABC"
|| key == "АБВ"
|| key == "123" {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 3.5)
} else if key == spaceBar || key == languageTextForSpaceBar {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 4)
} else {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 2.9)
}
} else {
if key == "#+="
|| key == "ABC"
|| key == "АБВ"
|| key == "123" {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 1.75)
} else if key == spaceBar || key == languageTextForSpaceBar {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 2)
} else {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 1.5)
}
}
}
/// Checks if the character is a lower case letter and adjusts it if so.
func checkSetPhoneLowerCharSize() {
guard let isSpecial = layer.value(forKey: "isSpecial") as? Bool else { return }
if keyboardState == .letters
&& !isSpecial
&& !["123", "´", spaceBar, languageTextForSpaceBar].contains(key)
&& shiftButtonState == .normal {
if isLandscapeView {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 2.4)
} else {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 1.35)
}
}
}
/// Sets the character size of a key if the device is an iPhone.
func setPhoneCharSize() {
setPhoneCapCharSize()
checkSetPhoneLowerCharSize()
}
/// Sets the character size of a key if the device is an iPad given the orientation.
func setPadCapCharSize() {
if isLandscapeView {
if key == "#+="
|| key == "ABC"
|| key == "АБВ"
|| key == "hideKeyboard" {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 3.75)
} else if key == spaceBar || key == languageTextForSpaceBar {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 4.25)
} else if key == ".?123" {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 4.5)
} else {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 3.75)
}
} else {
if key == "#+="
|| key == "ABC"
|| key == "АБВ"
|| key == "hideKeyboard" {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 3.25)
} else if key == spaceBar || key == languageTextForSpaceBar {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 3.5)
} else if key == ".?123" {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 4)
} else {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 3)
}
}
}
/// Sets the character size of a key if the device is an iPad given the orientation.
func checkSetPadLowerCharSize() {
guard let isSpecial = layer.value(forKey: "isSpecial") as? Bool else { return }
if keyboardState == .letters
&& !isSpecial
&& ![".?123", spaceBar, languageTextForSpaceBar, "ß", "´", ",", ".", "'", "-"].contains(key)
&& shiftButtonState == .normal {
if isLandscapeView {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 3.35)
} else {
titleLabel?.font = .systemFont(ofSize: letterKeyWidth / 2.75)
}
}
}
/// Sets the character size of a key if the device is an iPad.
func setPadCharSize() {
setPadCapCharSize()
checkSetPadLowerCharSize()
}
/// Sets the key character sizes depending on device type and orientation.
func setCharSize() {
if DeviceType.isPhone {
setPhoneCharSize()
} else if DeviceType.isPad {
setPadCharSize()
}
}
/// Adjusts the width of a key if it's one of the special characters on the iPhone keyboard.
func adjustPhoneKeyWidth() {
if ["ABC", "АБВ"].contains(key) {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 2).isActive = true
} else if ["delete", "#+=", "shift", "selectKeyboard"].contains(key) {
if keyboardState == .letters
&& (
(
commandState != .translate
&& controllerLanguage == "Russian"
) || (
commandState == .translate
&& getControllerTranslateLangCode() == "ru"
)) {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 1).isActive = true
} else {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 1.5).isActive = true
}
} else if ["123", ".?123", "return", "hideKeyboard"].contains(key) {
if row == 2 {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 1.5).isActive = true
} else if row != 2 {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 2).isActive = true
}
} else if (keyboardState == .numbers || keyboardState == .symbols)
&& row == 2 {
// Make second row number and symbol keys wider for iPhones.
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 1.4).isActive = true
} else if key != spaceBar && key != languageTextForSpaceBar {
widthAnchor.constraint(equalToConstant: keyWidth).isActive = true
}
}
/// Adjusts the width of a key if it's one of the special characters on the iPad keyboard.
func adjustPadKeyWidth() {
if usingExpandedKeyboard {
scalarCapsLockKeyWidth = 1.3
scalarDeleteKeyWidth = 1.65
scalarReturnKeyWidth = 1.3
scalarShiftKeyWidth = 1.5
scalarSpecialKeysWidth = 1.0
if ["ABC", "АБВ"].contains(key) {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 1).isActive = true
} else if ["#+=", "selectKeyboard"].contains(key) {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * scalarSpecialKeysWidth).isActive = true
} else if ["delete"].contains(key) {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * scalarDeleteKeyWidth).isActive = true
} else if [SpecialKeys.capsLock].contains(key) {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * scalarCapsLockKeyWidth).isActive = true
} else if [SpecialKeys.indent].contains(key) {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * scalarIndentKeyWidth).isActive = true
} else if ["shift"].contains(key) && idx == 0 {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * scalarShiftKeyWidth).isActive = true
} else if ["shift"].contains(key) && (idx > 0) {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * scalarRightShiftKeyWidth).isActive = true
} else if ["return"].contains(key) {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * scalarReturnKeyWidth).isActive = true
} else if ["123", ".?123", "return", "hideKeyboard"].contains(key) {
if DeviceType.isPad
&& key == "return"
&& (
(
commandState != .translate
&& ["English", "Portuguese", "Italian"].contains(controllerLanguage)
) || (
commandState == .translate
&& ["en", "pt", "it"].contains(getControllerTranslateLangCode())
)
)
&& row == 1 {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * scalarReturnKeyWidth).isActive = true
} else {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 1).isActive = true
}
} else if key != spaceBar && key != languageTextForSpaceBar {
widthAnchor.constraint(equalToConstant: keyWidth).isActive = true
}
} else {
if ["ABC", "АБВ"].contains(key) {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 1).isActive = true
} else if ["delete", "#+=", "shift", "selectKeyboard", SpecialKeys.indent, SpecialKeys.capsLock].contains(key) {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 1).isActive = true
} else if ["123", ".?123", "return", "hideKeyboard"].contains(key) {
if DeviceType.isPad
&& key == "return"
&& (
(
commandState != .translate
&& ["English", "Portuguese", "Italian"].contains(controllerLanguage)
) || (
commandState == .translate
&& ["en", "pt", "it"].contains(getControllerTranslateLangCode())
)
)
&& row == 1 {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 1.5).isActive = true
} else {
layer.setValue(true, forKey: "isSpecial")
widthAnchor.constraint(equalToConstant: numSymKeyWidth * 1).isActive = true
}
} else if key != spaceBar && key != languageTextForSpaceBar {
widthAnchor.constraint(equalToConstant: keyWidth).isActive = true
}
}
}
/// Adjusts the width of a key if it's one of the special characters on the keyboard.
func adjustKeyWidth() {
if DeviceType.isPhone {
adjustPhoneKeyWidth()
} else if DeviceType.isPad {
adjustPadKeyWidth()
}
}
/// Adjusts the style of the button based on different states.
func adjustButtonStyle() {
guard let isSpecial = layer.value(forKey: "isSpecial") as? Bool else { return }
switch key {
case SpecialKeys.indent:
backgroundColor = specialKeyColor
case SpecialKeys.capsLock:
switch capsLockButtonState {
case .normal:
backgroundColor = specialKeyColor
styleIconBtn(btn: self, color: UIColor.label, iconName: "capslock")
case .locked:
backgroundColor = keyPressedColor
styleIconBtn(btn: self, color: UIColor.label, iconName: "capslock.fill")
}
case "shift":
if shiftButtonState == .shift {
backgroundColor = keyPressedColor
styleIconBtn(btn: self, color: UIColor.label, iconName: "shift.fill")
} else if DeviceType.isPhone && capsLockButtonState == .locked {
// We need to style the SHIFT button instead of the CAPSLOCK since the keyboard is smaller.
backgroundColor = keyPressedColor
styleIconBtn(btn: self, color: UIColor.label, iconName: "capslock.fill")
} else {
backgroundColor = specialKeyColor
}
case "return":
if [.translate, .conjugate, .plural].contains(commandState) {
// Color the return key depending on if it's being used as enter for commands.
backgroundColor = commandKeyColor
} else {
backgroundColor = specialKeyColor
}
default:
if isSpecial {
backgroundColor = specialKeyColor
}
}
}
}
/// Sets a button's values that are displayed and inserted into the proxy as well as assigning a color.
///
/// - Parameters
/// - btn: the button to be set up.
/// - color: the color to assign to the background.
/// - name: the name of the value for the key.
/// - canBeCapitalized: whether the key receives a capitalized character for the shift state.
/// - isSpecial: whether the btn should be marked as special to be colored accordingly.
func setBtn(btn: UIButton, color: UIColor, name: String, canBeCapitalized: Bool, isSpecial: Bool) {
btn.backgroundColor = color
btn.layer.setValue(name, forKey: "original")
let charsWithoutShiftState = ["ß"]
var capsKey = ""
if canBeCapitalized {
if !charsWithoutShiftState.contains(name) {
capsKey = name.capitalized
} else {
capsKey = name
}
let shiftChar = shiftButtonState == .normal ? name : capsKey
btn.layer.setValue(shiftChar, forKey: "keyToDisplay")
} else {
btn.layer.setValue(name, forKey: "keyToDisplay")
}
btn.layer.setValue(isSpecial, forKey: "isSpecial")
}