@@ -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
@@ -1084,14 +1087,20 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
10841087 }
10851088
10861089 public var hasText : Bool {
1087- return true
1090+ return !textInputStorage . isEmpty
10881091 }
10891092
10901093 /*
1091- Soft keyboard input. Hardware keyboard input is handled in pressesBegan.
1094+ Soft keyboard input. Hardware keyboard text input is delivered here; special keys are handled in pressesBegan.
10921095 */
10931096 open func insertText( _ text: String ) {
1094- uitiLog ( " insertText( \" \( text) \" ) textInputStorage: \" \( textInputStorage) \" " )
1097+ //uitiLog("insertText(\(text.debugDescription)) \(textInputStateDescription())")
1098+
1099+ if tryComposeKoreanFinal ( text) {
1100+ return
1101+ }
1102+
1103+ beginTextInputEdit ( )
10951104
10961105 let rangeToReplace = _markedTextRange ?? _selectedTextRange
10971106 let rangeStartIndex = rangeToReplace. startPosition. offset
@@ -1100,6 +1109,8 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11001109 let insertedPosition = TextPosition ( offset: rangeStartIndex + text. count)
11011110 _selectedTextRange = TextRange ( from: insertedPosition, to: insertedPosition)
11021111
1112+ endTextInputEdit ( )
1113+
11031114 if terminalAccessory? . controlModifier ?? false {
11041115 self . send ( applyControlToEventCharacters ( text) )
11051116 terminalAccessory? . controlModifier = false
@@ -1115,14 +1126,77 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11151126 queuePendingDisplay ( )
11161127 }
11171128
1129+ // this is necessary because something in the iOS IME seems to prevent
1130+ // the sequence "ㅇ", "ㅜ", "ㅇ" from becoming "웅", and instead
1131+ // it becomes "우" followed by "ㅇ"
1132+ private func tryComposeKoreanFinal( _ text: String ) -> Bool {
1133+ guard let language = textInputMode? . primaryLanguage, language. hasPrefix ( " ko " ) else { return false }
1134+ guard _markedTextRange == nil else { return false }
1135+ guard _selectedTextRange. isEmpty, _selectedTextRange. endPosition. offset == textInputStorage. count else { return false }
1136+ guard text. count == 1 , let jamo = text. first else { return false }
1137+ guard let finalIndex = koreanFinalIndex [ jamo] else { return false }
1138+ guard let lastChar = textInputStorage. last else { return false }
1139+ guard let composed = composeHangulSyllable ( base: lastChar, finalIndex: finalIndex) else { return false }
1140+
1141+ uitiLog ( " koreanComposeFinal base: \( lastChar) jamo: \( jamo) -> \( composed) " )
1142+
1143+ beginTextInputEdit ( )
1144+ textInputStorage. removeLast ( )
1145+ textInputStorage. append ( composed)
1146+ let newOffset = textInputStorage. count
1147+ _markedTextRange = nil
1148+ _selectedTextRange = TextRange ( from: TextPosition ( offset: newOffset) , to: TextPosition ( offset: newOffset) )
1149+ endTextInputEdit ( )
1150+
1151+ send ( [ backspaceSendsControlH ? 8 : 0x7f ] )
1152+ send ( txt: String ( composed) )
1153+ queuePendingDisplay ( )
1154+ return true
1155+ }
1156+
1157+ private let koreanFinalIndex : [ Character : Int ] = [
1158+ " ㄱ " : 1 , " ㄲ " : 2 , " ㄳ " : 3 ,
1159+ " ㄴ " : 4 , " ㄵ " : 5 , " ㄶ " : 6 ,
1160+ " ㄷ " : 7 ,
1161+ " ㄹ " : 8 , " ㄺ " : 9 , " ㄻ " : 10 , " ㄼ " : 11 , " ㄽ " : 12 , " ㄾ " : 13 , " ㄿ " : 14 , " ㅀ " : 15 ,
1162+ " ㅁ " : 16 ,
1163+ " ㅂ " : 17 , " ㅄ " : 18 ,
1164+ " ㅅ " : 19 , " ㅆ " : 20 ,
1165+ " ㅇ " : 21 ,
1166+ " ㅈ " : 22 ,
1167+ " ㅊ " : 23 ,
1168+ " ㅋ " : 24 ,
1169+ " ㅌ " : 25 ,
1170+ " ㅍ " : 26 ,
1171+ " ㅎ " : 27
1172+ ]
1173+
1174+ private func composeHangulSyllable( base: Character , finalIndex: Int ) -> Character ? {
1175+ guard finalIndex > 0 && finalIndex < 28 else { return nil }
1176+ guard let scalar = base. unicodeScalars. first, base. unicodeScalars. count == 1 else { return nil }
1177+ let scalarValue = Int ( scalar. value)
1178+ let sBase = 0xAC00
1179+ let sEnd = 0xD7A3
1180+ guard scalarValue >= sBase && scalarValue <= sEnd else { return nil }
1181+ let vCount = 21
1182+ let tCount = 28
1183+ let sIndex = scalarValue - sBase
1184+ let lIndex = sIndex / ( vCount * tCount)
1185+ let vIndex = ( sIndex % ( vCount * tCount) ) / tCount
1186+ let tIndex = sIndex % tCount
1187+ guard tIndex == 0 else { return nil }
1188+ let newScalarValue = sBase + ( lIndex * vCount + vIndex) * tCount + finalIndex
1189+ guard let newScalar = UnicodeScalar ( newScalarValue) else { return nil }
1190+ return Character ( newScalar)
1191+ }
1192+
11181193 func ensureCaretIsVisible ( )
11191194 {
11201195 contentOffset = CGPoint ( x: 0 , y: CGFloat ( terminal. buffer. lines. count- terminal. rows) * cellDimension. height)
11211196 }
11221197
11231198 public func deleteBackward( ) {
1124- uitiLog ( " deleteBackward() textInputStorage: \" \( textInputStorage) \" markedTextRange: \" \( _markedTextRange) \" selectedTextRange: \" \( _selectedTextRange) \" " )
1125- inputDelegate? . selectionWillChange ( self )
1199+ uitiLog ( " deleteBackward() \( textInputStateDescription ( ) ) " )
11261200
11271201 // after backward deletion, marked range is always cleared, and length of selected range is always zero
11281202 let rangeToDelete = _markedTextRange ?? _selectedTextRange
@@ -1140,12 +1214,15 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11401214 return
11411215 }
11421216
1217+ beginTextInputEdit ( )
1218+
11431219 rangeStartIndex -= 1
11441220 textInputStorage. remove ( at: textInputStorage. index ( textInputStorage. startIndex, offsetBy: rangeStartIndex) )
11451221 rangeStartPosition = TextPosition ( offset: rangeStartIndex)
11461222
11471223 self . send ( [ backspaceSendsControlH ? 8 : 0x7f ] )
11481224 } else {
1225+ beginTextInputEdit ( )
11491226 // Send as many backspaces that are in the range to delete. When on auto-repeat, after a some time
11501227 // pressing the backspace, it will delete chunks of text at a time.
11511228 let oldText = textInputStorage [ rangeToDelete. fullRange ( in: textInputStorage) ]
@@ -1160,7 +1237,7 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
11601237 _markedTextRange = nil
11611238 _selectedTextRange = TextRange ( from: rangeStartPosition, to: rangeStartPosition)
11621239
1163- inputDelegate ? . selectionDidChange ( self )
1240+ endTextInputEdit ( )
11641241 }
11651242
11661243 enum SendData {
@@ -1211,9 +1288,15 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
12111288
12121289 public override func pressesBegan( _ presses: Set < UIPress > , with event: UIPressesEvent ? ) {
12131290 var didHandleEvent = false
1291+
1292+ if _markedTextRange != nil {
1293+ super. pressesBegan ( presses, with: event)
1294+ return
1295+ }
12141296
12151297 for press in presses {
12161298 guard let key = press. key else { continue }
1299+ uitiLog ( " pressesBegan keyCode: \( key. keyCode) chars: \( key. characters. debugDescription) ignoring: \( key. charactersIgnoringModifiers. debugDescription) modifiers: \( key. modifierFlags) " )
12171300
12181301 var data : SendData ? = nil
12191302
@@ -1281,19 +1364,13 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
12811364 case . keyboardDeleteForward:
12821365 data = . bytes ( EscapeSequences . cmdDelKey)
12831366
1284- case . keyboardDeleteOrBackspace:
1285- data = . bytes ( [ backspaceSendsControlH ? 8 : 0x7f ] )
1286-
12871367 case . keyboardEscape:
12881368 data = . bytes ( [ 0x1b ] )
12891369
12901370 case . keyboardInsert:
12911371 print ( " .keyboardInsert ignored " )
12921372 break
12931373
1294- case . keyboardReturn:
1295- data = . bytes ( returnByteSequence)
1296-
12971374 case . keyboardTab:
12981375 data = . bytes ( [ 9 ] )
12991376
@@ -1331,16 +1408,6 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
13311408 optionAsMetaKey. toggle ( )
13321409 } else if key. modifierFlags. contains ( . alternate) && optionAsMetaKey {
13331410 data = . text( " \u{1b} \( key. charactersIgnoringModifiers) " )
1334- } else if !key. modifierFlags. contains ( . command) {
1335- if let keyboardLanguage = self . textInputMode? . primaryLanguage {
1336- // Is the keyboard language one of the multi-input languages? Chinese, Japanese, Korean and Hindi-Transliteration
1337- // If so, do not process the input yet (we'll do it later in unmarkText())
1338- if ( !keyboardLanguage. hasPrefix ( " hi " ) && !keyboardLanguage. hasPrefix ( " zh " ) && !keyboardLanguage. hasPrefix ( " ja " ) && !keyboardLanguage. hasPrefix ( " ko " ) ) {
1339- if key. characters. count > 0 {
1340- data = . text ( key. characters)
1341- }
1342- }
1343- }
13441411 }
13451412 }
13461413 if let sendableData = data {
0 commit comments