@@ -517,19 +517,29 @@ - (void) keyDown:(NSEvent *)theEvent
517517 {
518518 ch = [str_no_modifiers characterAtIndex: 0 ];
519519 }
520+
521+ // Korean input fix: Improved input source detection
522+ NSString *inputSource = [[NSTextInputContext currentInputContext ] selectedKeyboardInputSource ];
523+ BOOL isKoreanInput = [inputSource containsString: @" Korean" ] ||
524+ [inputSource containsString: @" Hangul" ] ||
525+ [inputSource containsString: @" 2-Set Korean" ] ||
526+ [inputSource containsString: @" 390 Hangul" ];
527+
520528 bool acceptsText = mHasMarkedText ? false : callKeyDown (&eventData, keycode, mModifiers , ch);
521529
522530 if (acceptsText &&
523531 !mMarkedTextAllowed &&
524532 !(mModifiers & (NSControlKeyMask | NSCommandKeyMask )) && // commands don't invoke InputWindow
525- ![(LLAppDelegate*)[NSApp delegate ] romanScript ] &&
533+ (isKoreanInput || ![(LLAppDelegate*)[NSApp delegate ] romanScript ]) && // Korean input condition improved
526534 ch > ' ' &&
527535 ch != NSDeleteCharacter &&
528536 (ch < 0xF700 || ch > 0xF8FF )) // 0xF700-0xF8FF: reserved for function keys on the keyboard(from NSEvent.h)
529537 {
538+ // Korean input mode - activate Input Method handling
530539 [(LLAppDelegate*)[NSApp delegate ] showInputWindow: true withEvent: theEvent];
531540 } else
532541 {
542+ // Handle through Input Context for IME processing
533543 [[self inputContext ] handleEvent: theEvent];
534544 }
535545}
@@ -628,21 +638,49 @@ - (BOOL) performDragOperation:(id<NSDraggingInfo>)sender
628638
629639- (BOOL )hasMarkedText
630640{
641+ // Korean input fix: Also check NSTextInputContext state
642+ NSTextInputContext *context = [self inputContext ];
643+ if (context && [context respondsToSelector: @selector (hasMarkedText )])
644+ {
645+ return mHasMarkedText || [context hasMarkedText ];
646+ }
631647 return mHasMarkedText ;
632648}
633649
634650- (NSRange )markedRange
635651{
652+ // Korean input fix: Improved range handling
653+ if (!mHasMarkedText )
654+ {
655+ return NSMakeRange (NSNotFound , 0 );
656+ }
657+
636658 int range[2 ];
637659 getPreeditMarkedRange (&range[0 ], &range[1 ]);
638- return NSMakeRange (range[0 ], range[1 ]);
660+
661+ // Validate range before returning
662+ if (range[0 ] >= 0 && range[1 ] > 0 )
663+ {
664+ return NSMakeRange (range[0 ], range[1 ]);
665+ }
666+
667+ return NSMakeRange (NSNotFound , 0 );
639668}
640669
641670- (NSRange )selectedRange
642671{
672+ // Korean input fix: Improved selection range handling
643673 int range[2 ];
644674 getPreeditSelectionRange (&range[0 ], &range[1 ]);
645- return NSMakeRange (range[0 ], range[1 ]);
675+
676+ // Validate range before returning
677+ if (range[0 ] >= 0 )
678+ {
679+ return NSMakeRange (range[0 ], range[1 ] > 0 ? range[1 ] : 0 );
680+ }
681+
682+ // Default to current cursor position
683+ return NSMakeRange (0 , 0 );
646684}
647685
648686- (void )setMarkedText : (id )aString selectedRange : (NSRange )selectedRange replacementRange : (NSRange )replacementRange
@@ -653,49 +691,72 @@ - (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replaceme
653691 // Apple also says when aString is an NSString object,
654692 // the receiver is expected to render the marked text with distinguishing appearance.
655693 // So I tried to make attributedStringInfo, but it won't be used... (Pell Smit)
656-
694+
695+ // Korean input fix: Improved composition text handling based on Chrome's approach
657696 if (mMarkedTextAllowed )
658697 {
659- unsigned int selected[2 ] = {
660- unsigned (selectedRange.location ),
661- unsigned (selectedRange.length )
662- };
698+ // Check if we have composition text
699+ BOOL hasComposition = [aString length ] > 0 ;
663700
664- unsigned int replacement[2 ] = {
665- unsigned (replacementRange.location ),
666- unsigned (replacementRange.length )
667- };
668-
669- int string_length = [aString length ];
670- unichar text[string_length];
671- attributedStringInfo segments;
672- // I used 'respondsToSelector:@selector(string)'
673- // to judge aString is an attributed string or not.
674- if ([aString respondsToSelector: @selector (string )])
675- {
676- // aString is attibuted
677- [[aString string ] getCharacters: text range: NSMakeRange (0 , string_length)];
678- segments = getSegments ((NSAttributedString *)aString);
679- }
680- else
701+ // If we have existing marked text and a replacement range is specified, handle it first
702+ if (mHasMarkedText && replacementRange.location != NSNotFound && replacementRange.length > 0 )
681703 {
682- // aString is not attributed
683- [aString getCharacters: text range: NSMakeRange (0 , string_length)];
684- segments.seg_lengths .push_back (string_length);
685- segments.seg_standouts .push_back (true );
704+ // Delete the replacement range text first
705+ callDeleteRange ((int )replacementRange.location , (int )replacementRange.length );
686706 }
687- setMarkedText (text, selected, replacement, string_length, segments);
688- if (string_length > 0 )
707+
708+ if (hasComposition )
689709 {
710+ unsigned int selected[2 ] = {
711+ unsigned (selectedRange.location ),
712+ unsigned (selectedRange.length )
713+ };
714+
715+ unsigned int replacement[2 ] = {
716+ unsigned (replacementRange.location != NSNotFound ? replacementRange.location : 0 ),
717+ unsigned (replacementRange.length )
718+ };
719+
720+ int string_length = [aString length ];
721+ unichar text[string_length];
722+ attributedStringInfo segments;
723+
724+ // I used 'respondsToSelector:@selector(string)'
725+ // to judge aString is an attributed string or not.
726+ if ([aString respondsToSelector: @selector (string )])
727+ {
728+ // aString is attributed
729+ [[aString string ] getCharacters: text range: NSMakeRange (0 , string_length)];
730+ segments = getSegments ((NSAttributedString *)aString);
731+ }
732+ else
733+ {
734+ // aString is not attributed
735+ [aString getCharacters: text range: NSMakeRange (0 , string_length)];
736+ segments.seg_lengths .push_back (string_length);
737+ segments.seg_standouts .push_back (true );
738+ }
739+
740+ // Set marked text with improved handling for Korean composition
741+ setMarkedText (text, selected, replacement, string_length, segments);
690742 mHasMarkedText = TRUE ;
691743 mMarkedTextLength = string_length;
744+
745+ // Notify the composition text update
746+ const wchar_t * wtext = reinterpret_cast <const wchar_t *>(text);
747+ std::vector<int > seg_lengths_vec (segments.seg_lengths .begin (), segments.seg_lengths .end ());
748+ std::vector<bool > standouts_vec (segments.seg_standouts .begin (), segments.seg_standouts .end ());
749+ callCompositionTextUpdate (wtext, string_length, (int )selectedRange.location ,
750+ seg_lengths_vec.data (), (int )seg_lengths_vec.size (),
751+ standouts_vec.data ());
692752 }
693753 else
694754 {
695- // we must clear the marked text when aString is null.
755+ // Composition completed or cancelled
696756 [self unmarkText ];
697757 }
698758 } else {
759+ // Marked text not allowed, clear any existing marked text
699760 if (mHasMarkedText )
700761 {
701762 [self unmarkText ];
@@ -705,20 +766,40 @@ - (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replaceme
705766
706767- (void )commitCurrentPreedit
707768{
769+ // Korean input fix: Improved commit process
708770 if (mHasMarkedText )
709771 {
710- if ([[self inputContext ] respondsToSelector: @selector (commitEditing )])
772+ NSTextInputContext *context = [self inputContext ];
773+ if ([context respondsToSelector: @selector (commitEditing )])
711774 {
712- [[ self inputContext ] commitEditing ];
775+ [context commitEditing ];
713776 }
777+
778+ // Reset internal state after commit
779+ mHasMarkedText = FALSE ;
780+ mMarkedTextLength = 0 ;
781+
782+ // Notify composition commit
783+ callCompositionTextCommit ();
714784 }
715785}
716786
717787- (void )unmarkText
718788{
719- [[self inputContext ] discardMarkedText ];
720- resetPreedit ();
721- mHasMarkedText = FALSE ;
789+ // Korean input fix: Improved unmarking process
790+ if (mHasMarkedText )
791+ {
792+ // Notify input context
793+ [[self inputContext ] discardMarkedText ];
794+
795+ // Reset internal state
796+ resetPreedit ();
797+ mHasMarkedText = FALSE ;
798+ mMarkedTextLength = 0 ;
799+
800+ // Clear composition state
801+ clearCompositionText ();
802+ }
722803}
723804
724805// We don't support attributed strings.
@@ -743,6 +824,22 @@ - (void)insertText:(id)insertString
743824
744825- (void )insertText : (id )aString replacementRange : (NSRange )replacementRange
745826{
827+ if (aString == nil ) return ;
828+
829+ // Korean input fix: Clear any existing marked text first
830+ if (mHasMarkedText )
831+ {
832+ resetPreedit ();
833+ mHasMarkedText = FALSE ;
834+ callCompositionTextCommit ();
835+ }
836+
837+ // Handle replacement range if specified
838+ if (replacementRange.location != NSNotFound && replacementRange.length > 0 )
839+ {
840+ callDeleteRange ((int )replacementRange.location , (int )replacementRange.length );
841+ }
842+
746843 // SL-19801 Special workaround for system emoji picker
747844 if ([aString length ] == 2 )
748845 {
@@ -767,27 +864,27 @@ - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
767864
768865 @try
769866 {
770- if (!mHasMarkedText )
867+ // Korean input fix: Process each character with improved handling
868+ for (NSInteger i = 0 ; i < [aString length ]; i++)
771869 {
772- for (NSInteger i = 0 ; i < [aString length ]; i++)
870+ unichar character = [aString characterAtIndex: i];
871+
872+ // Check if this is a Korean completed character (Hangul syllables)
873+ if (character >= 0xAC00 && character <= 0xD7A3 )
773874 {
774- callUnicodeCallback ([aString characterAtIndex: i], mModifiers );
875+ // Korean completed character - handle as final composed character
876+ callUnicodeCallback (character, mModifiers );
775877 }
776- } else {
777- resetPreedit ();
778- // We may never get this point since unmarkText may be called before insertText ever gets called once we submit our text.
779- // But just in case...
780-
781- for (NSInteger i = 0 ; i < [aString length ]; i++)
878+ else
782879 {
783- handleUnicodeCharacter ([aString characterAtIndex: i]);
880+ // Other characters (including Korean Jamo if any)
881+ callUnicodeCallback (character, mModifiers );
784882 }
785- mHasMarkedText = FALSE ;
786883 }
787884 }
788885 @catch (NSException * e)
789886 {
790- NSLog (@" Failed to process an attributed string . Exception: %@ String: %@ " , e.name, aString);
887+ NSLog (@" Failed to process text input . Exception: %@ String: %@ " , e.name, aString);
791888 }
792889}
793890
@@ -830,7 +927,33 @@ - (BOOL)drawsVerticallyForCharacterAtIndex:(NSUInteger)charIndex
830927
831928- (void ) allowMarkedTextInput : (bool )allowed
832929{
930+ // Korean input fix: Improved marked text control
833931 mMarkedTextAllowed = allowed;
932+
933+ // Clear existing marked text if not allowed
934+ if (!allowed && mHasMarkedText )
935+ {
936+ [self unmarkText ];
937+ }
938+ }
939+
940+ // Korean input fix: New utility methods
941+ - (BOOL )isKoreanInputActive
942+ {
943+ NSString *inputSource = [[NSTextInputContext currentInputContext ] selectedKeyboardInputSource ];
944+ return [inputSource containsString: @" Korean" ] ||
945+ [inputSource containsString: @" Hangul" ] ||
946+ [inputSource containsString: @" 2-Set Korean" ] ||
947+ [inputSource containsString: @" 390 Hangul" ];
948+ }
949+
950+ - (void )resetIMEState
951+ {
952+ if (mHasMarkedText )
953+ {
954+ [self unmarkText ];
955+ }
956+ resetIMEState ();
834957}
835958
836959@end
0 commit comments