@@ -42,6 +42,9 @@ internal var log: Logger = Logger(subsystem: "org.tirania.SwiftTerm", category:
4242 * defaults, otherwise, this uses its own set of defaults colors.
4343 */
4444open class TerminalView : UIScrollView , UITextInputTraits , UIKeyInput , UIScrollViewDelegate , TerminalDelegate {
45+ public static var textInputDebugEnabled : Bool = false
46+ internal static var textInputLogCounter : Int = 0
47+
4548 struct FontSet {
4649 public let normal : UIFont
4750 let bold : UIFont
@@ -1086,14 +1089,20 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
10861089 }
10871090
10881091 public var hasText : Bool {
1089- return true
1092+ return !textInputStorage . isEmpty
10901093 }
10911094
10921095 /*
1093- Soft keyboard input. Hardware keyboard input is handled in pressesBegan.
1096+ Soft keyboard input. Hardware keyboard text input is delivered here; special keys are handled in pressesBegan.
10941097 */
10951098 open func insertText( _ text: String ) {
1096- uitiLog ( " insertText( \" \( text) \" ) textInputStorage: \" \( textInputStorage) \" " )
1099+ //uitiLog("insertText(\(text.debugDescription)) \(textInputStateDescription())")
1100+
1101+ if tryComposeKoreanFinal ( text) {
1102+ return
1103+ }
1104+
1105+ beginTextInputEdit ( )
10971106
10981107 let rangeToReplace = _markedTextRange ?? _selectedTextRange
10991108 let rangeStartIndex = rangeToReplace. startPosition. offset
@@ -1102,6 +1111,8 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11021111 let insertedPosition = TextPosition ( offset: rangeStartIndex + text. count)
11031112 _selectedTextRange = TextRange ( from: insertedPosition, to: insertedPosition)
11041113
1114+ endTextInputEdit ( )
1115+
11051116 if terminalAccessory? . controlModifier ?? false {
11061117 self . send ( applyControlToEventCharacters ( text) )
11071118 terminalAccessory? . controlModifier = false
@@ -1117,15 +1128,78 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11171128 queuePendingDisplay ( )
11181129 }
11191130
1131+ // this is necessary because something in the iOS IME seems to prevent
1132+ // the sequence "ㅇ", "ㅜ", "ㅇ" from becoming "웅", and instead
1133+ // it becomes "우" followed by "ㅇ"
1134+ private func tryComposeKoreanFinal( _ text: String ) -> Bool {
1135+ guard let language = textInputMode? . primaryLanguage, language. hasPrefix ( " ko " ) else { return false }
1136+ guard _markedTextRange == nil else { return false }
1137+ guard _selectedTextRange. isEmpty, _selectedTextRange. endPosition. offset == textInputStorage. count else { return false }
1138+ guard text. count == 1 , let jamo = text. first else { return false }
1139+ guard let finalIndex = koreanFinalIndex [ jamo] else { return false }
1140+ guard let lastChar = textInputStorage. last else { return false }
1141+ guard let composed = composeHangulSyllable ( base: lastChar, finalIndex: finalIndex) else { return false }
1142+
1143+ uitiLog ( " koreanComposeFinal base: \( lastChar) jamo: \( jamo) -> \( composed) " )
1144+
1145+ beginTextInputEdit ( )
1146+ textInputStorage. removeLast ( )
1147+ textInputStorage. append ( composed)
1148+ let newOffset = textInputStorage. count
1149+ _markedTextRange = nil
1150+ _selectedTextRange = TextRange ( from: TextPosition ( offset: newOffset) , to: TextPosition ( offset: newOffset) )
1151+ endTextInputEdit ( )
1152+
1153+ send ( [ backspaceSendsControlH ? 8 : 0x7f ] )
1154+ send ( txt: String ( composed) )
1155+ queuePendingDisplay ( )
1156+ return true
1157+ }
1158+
1159+ private let koreanFinalIndex : [ Character : Int ] = [
1160+ " ㄱ " : 1 , " ㄲ " : 2 , " ㄳ " : 3 ,
1161+ " ㄴ " : 4 , " ㄵ " : 5 , " ㄶ " : 6 ,
1162+ " ㄷ " : 7 ,
1163+ " ㄹ " : 8 , " ㄺ " : 9 , " ㄻ " : 10 , " ㄼ " : 11 , " ㄽ " : 12 , " ㄾ " : 13 , " ㄿ " : 14 , " ㅀ " : 15 ,
1164+ " ㅁ " : 16 ,
1165+ " ㅂ " : 17 , " ㅄ " : 18 ,
1166+ " ㅅ " : 19 , " ㅆ " : 20 ,
1167+ " ㅇ " : 21 ,
1168+ " ㅈ " : 22 ,
1169+ " ㅊ " : 23 ,
1170+ " ㅋ " : 24 ,
1171+ " ㅌ " : 25 ,
1172+ " ㅍ " : 26 ,
1173+ " ㅎ " : 27
1174+ ]
1175+
1176+ private func composeHangulSyllable( base: Character , finalIndex: Int ) -> Character ? {
1177+ guard finalIndex > 0 && finalIndex < 28 else { return nil }
1178+ guard let scalar = base. unicodeScalars. first, base. unicodeScalars. count == 1 else { return nil }
1179+ let scalarValue = Int ( scalar. value)
1180+ let sBase = 0xAC00
1181+ let sEnd = 0xD7A3
1182+ guard scalarValue >= sBase && scalarValue <= sEnd else { return nil }
1183+ let vCount = 21
1184+ let tCount = 28
1185+ let sIndex = scalarValue - sBase
1186+ let lIndex = sIndex / ( vCount * tCount)
1187+ let vIndex = ( sIndex % ( vCount * tCount) ) / tCount
1188+ let tIndex = sIndex % tCount
1189+ guard tIndex == 0 else { return nil }
1190+ let newScalarValue = sBase + ( lIndex * vCount + vIndex) * tCount + finalIndex
1191+ guard let newScalar = UnicodeScalar ( newScalarValue) else { return nil }
1192+ return Character ( newScalar)
1193+ }
1194+
11201195 func ensureCaretIsVisible ( )
11211196 {
11221197 let displayBuffer = terminal. displayBuffer
11231198 contentOffset = CGPoint ( x: 0 , y: CGFloat ( displayBuffer. lines. count- displayBuffer. rows) * cellDimension. height)
11241199 }
11251200
11261201 public func deleteBackward( ) {
1127- uitiLog ( " deleteBackward() textInputStorage: \" \( textInputStorage) \" markedTextRange: \" \( _markedTextRange) \" selectedTextRange: \" \( _selectedTextRange) \" " )
1128- inputDelegate? . selectionWillChange ( self )
1202+ uitiLog ( " deleteBackward() \( textInputStateDescription ( ) ) " )
11291203
11301204 // after backward deletion, marked range is always cleared, and length of selected range is always zero
11311205 let rangeToDelete = _markedTextRange ?? _selectedTextRange
@@ -1143,12 +1217,15 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11431217 return
11441218 }
11451219
1220+ beginTextInputEdit ( )
1221+
11461222 rangeStartIndex -= 1
11471223 textInputStorage. remove ( at: textInputStorage. index ( textInputStorage. startIndex, offsetBy: rangeStartIndex) )
11481224 rangeStartPosition = TextPosition ( offset: rangeStartIndex)
11491225
11501226 self . send ( [ backspaceSendsControlH ? 8 : 0x7f ] )
11511227 } else {
1228+ beginTextInputEdit ( )
11521229 // Send as many backspaces that are in the range to delete. When on auto-repeat, after a some time
11531230 // pressing the backspace, it will delete chunks of text at a time.
11541231 let oldText = textInputStorage [ rangeToDelete. fullRange ( in: textInputStorage) ]
@@ -1163,7 +1240,7 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11631240 _markedTextRange = nil
11641241 _selectedTextRange = TextRange ( from: rangeStartPosition, to: rangeStartPosition)
11651242
1166- inputDelegate ? . selectionDidChange ( self )
1243+ endTextInputEdit ( )
11671244 }
11681245
11691246 enum SendData {
@@ -1214,9 +1291,15 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
12141291
12151292 public override func pressesBegan( _ presses: Set < UIPress > , with event: UIPressesEvent ? ) {
12161293 var didHandleEvent = false
1294+
1295+ if _markedTextRange != nil {
1296+ super. pressesBegan ( presses, with: event)
1297+ return
1298+ }
12171299
12181300 for press in presses {
12191301 guard let key = press. key else { continue }
1302+ uitiLog ( " pressesBegan keyCode: \( key. keyCode) chars: \( key. characters. debugDescription) ignoring: \( key. charactersIgnoringModifiers. debugDescription) modifiers: \( key. modifierFlags) " )
12201303
12211304 var data : SendData ? = nil
12221305
@@ -1284,19 +1367,13 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
12841367 case . keyboardDeleteForward:
12851368 data = . bytes ( EscapeSequences . cmdDelKey)
12861369
1287- case . keyboardDeleteOrBackspace:
1288- data = . bytes ( [ backspaceSendsControlH ? 8 : 0x7f ] )
1289-
12901370 case . keyboardEscape:
12911371 data = . bytes ( [ 0x1b ] )
12921372
12931373 case . keyboardInsert:
12941374 print ( " .keyboardInsert ignored " )
12951375 break
12961376
1297- case . keyboardReturn:
1298- data = . bytes ( returnByteSequence)
1299-
13001377 case . keyboardTab:
13011378 data = . bytes ( [ 9 ] )
13021379
@@ -1334,16 +1411,6 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
13341411 optionAsMetaKey. toggle ( )
13351412 } else if key. modifierFlags. contains ( . alternate) && optionAsMetaKey {
13361413 data = . text( " \u{1b} \( key. charactersIgnoringModifiers) " )
1337- } else if !key. modifierFlags. contains ( . command) {
1338- if let keyboardLanguage = self . textInputMode? . primaryLanguage {
1339- // Is the keyboard language one of the multi-input languages? Chinese, Japanese, Korean and Hindi-Transliteration
1340- // If so, do not process the input yet (we'll do it later in unmarkText())
1341- if ( !keyboardLanguage. hasPrefix ( " hi " ) && !keyboardLanguage. hasPrefix ( " zh " ) && !keyboardLanguage. hasPrefix ( " ja " ) && !keyboardLanguage. hasPrefix ( " ko " ) ) {
1342- if key. characters. count > 0 {
1343- data = . text ( key. characters)
1344- }
1345- }
1346- }
13471414 }
13481415 }
13491416 if let sendableData = data {
0 commit comments