From 0ef794b5534ab28901cbb18d69ba0e6642439ba9 Mon Sep 17 00:00:00 2001 From: Yuki Hashimoto Date: Mon, 5 Jul 2021 13:17:33 +0200 Subject: macOS: support Chinese and Korean input for text buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch extends D11695 to provide full support for Chinese and Korean input on macOS. Chinese input notes: You can input symbolic characters (such as '! , '$') during Chinese input. The difference from Japanese input is that multiple `insertText` may be called with a single key down (`keyDown` method). This happens when you input a symbolic character (such as '! , '$') during conversion. The conversion is confirmed (`insertText`) and the symbolic character is entered (`insertText`). To solve this problem, I have `result_text` to concatenate the strings and store them in `result`. Korean input notes: Korean does not display a conversion suggestion window. Like Chinese, Korean input may call multiple `insertText` methods. Also, in Korean, the previous confirmation (`setMarkedText` and `insertText`) and the next conversion is processed (`setMarkedText`) may be called simultaneously with a single key down (`keyDown` method). For example: 1. press g ㅎ (`setMarkedText`) 2. press k 하 (`setMarkedText`) 3. press t 앗 (`setMarkedText`) 4. press k 하세 (`setMarkedText`, `insertText`, `setMarkedText`) Fixed so that the `insertText` and the last `setMarkedText` are processed. Also, if a control character (such as Arrow, Enter) is input during Korean input, the conversion will be confirmed (`setMarkedText`, `insertText`) and the original control character will be processed. In other words, if you press the left arrow key while typing in Korean, the cursor will move to the left after the character is confirmed. Therefore, I modified the `keyDown` method so that the `handleKeyEvent` is called again after the `insertText` is processed in the `interpretKeyEvents` method. Differential Revision: https://developer.blender.org/D11699 --- intern/ghost/intern/GHOST_WindowCocoa.h | 4 ++- intern/ghost/intern/GHOST_WindowViewCocoa.h | 51 ++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 5 deletions(-) (limited to 'intern') diff --git a/intern/ghost/intern/GHOST_WindowCocoa.h b/intern/ghost/intern/GHOST_WindowCocoa.h index fdc806e2167..8ac1a78b32a 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.h +++ b/intern/ghost/intern/GHOST_WindowCocoa.h @@ -356,6 +356,8 @@ enum { GHOST_IME_INPUT_FOCUSED = (1 << 0), GHOST_IME_ENABLED = (1 << 1), GHOST_IME_COMPOSING = (1 << 2), - GHOST_IME_KEY_CONTROL_CHAR = (1 << 3) + GHOST_IME_KEY_CONTROL_CHAR = (1 << 3), + GHOST_IME_COMPOSITION_EVENT = (1 << 4), // For Korean input + GHOST_IME_RESULT_EVENT = (1 << 5) // For Korean input }; #endif /* WITH_INPUT_IME */ diff --git a/intern/ghost/intern/GHOST_WindowViewCocoa.h b/intern/ghost/intern/GHOST_WindowViewCocoa.h index 42f0fcc6ac6..2aaf1d56116 100644 --- a/intern/ghost/intern/GHOST_WindowViewCocoa.h +++ b/intern/ghost/intern/GHOST_WindowViewCocoa.h @@ -47,6 +47,7 @@ GHOST_TEventImeData event; std::string result; std::string composite; + std::string combined_result; } ime; #endif } @@ -123,6 +124,20 @@ // interpret event to call insertText [self interpretKeyEvents:[NSArray arrayWithObject:event]]; // calls insertText + +#ifdef WITH_INPUT_IME + // For Korean input, control characters are also processed by handleKeyEvent. + const int controlCharForKorean = (GHOST_IME_COMPOSITION_EVENT | GHOST_IME_RESULT_EVENT | + GHOST_IME_KEY_CONTROL_CHAR); + if (((ime.state_flag & controlCharForKorean) == controlCharForKorean)) { + systemCocoa->handleKeyEvent(event); + } + + ime.state_flag &= ~(GHOST_IME_COMPOSITION_EVENT | GHOST_IME_RESULT_EVENT); + + ime.combined_result.clear(); +#endif + return; } } @@ -261,9 +276,23 @@ [self processImeEvent:GHOST_kEventImeCompositionStart]; } - [self setImeResult:[self convertNSString:chars]]; + // For Chinese and Korean input, insertText may be executed twice with a single keyDown. + if (ime.state_flag & GHOST_IME_RESULT_EVENT) { + ime.combined_result += [self convertNSString:chars]; + } + else { + ime.combined_result = [self convertNSString:chars]; + } + + [self setImeResult:ime.combined_result]; + + /* For Korean input, both "Result Event" and "Composition Event" + * can occur in a single keyDown. */ + if (![self ime_did_composition]) { + [self processImeEvent:GHOST_kEventImeComposition]; + } + ime.state_flag |= GHOST_IME_RESULT_EVENT; - [self processImeEvent:GHOST_kEventImeComposition]; [self processImeEvent:GHOST_kEventImeCompositionEnd]; ime.state_flag &= ~GHOST_IME_COMPOSING; } @@ -313,7 +342,11 @@ [self setImeComposition:composing_text selectedRange:range]; - [self processImeEvent:GHOST_kEventImeComposition]; + // For Korean input, setMarkedText may be executed twice with a single keyDown. + if (![self ime_did_composition]) { + ime.state_flag |= GHOST_IME_COMPOSITION_EVENT; + [self processImeEvent:GHOST_kEventImeComposition]; + } } #endif } @@ -457,7 +490,11 @@ - (void)setImeComposition:(NSString *)inString selectedRange:(NSRange)range { ime.composite = [self convertNSString:inString]; - ime.result.clear(); + + // For Korean input, both "Result Event" and "Composition Event" can occur in a single keyDown. + if (!(ime.state_flag & GHOST_IME_RESULT_EVENT)) { + ime.result.clear(); + } /* The target string is equivalent to the string in selectedRange of setMarkedText. * The cursor is displayed at the beginning of the target string. */ @@ -525,6 +562,12 @@ } } +- (bool)ime_did_composition +{ + return (ime.state_flag & GHOST_IME_COMPOSITION_EVENT) || + (ime.state_flag & GHOST_IME_RESULT_EVENT); +} + /* Even if IME is enabled, when not composing, control characters * (such as arrow, enter, delete) are handled by handleKeyEvent. */ - (bool)isProcessedByIme -- cgit v1.2.3