Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/ghost')
-rw-r--r--intern/ghost/CMakeLists.txt3
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.h33
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.mm23
-rw-r--r--intern/ghost/intern/GHOST_WindowViewCocoa.h265
4 files changed, 319 insertions, 5 deletions
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 77ec307e604..e98faf522e6 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -155,6 +155,9 @@ if(WITH_HEADLESS OR WITH_GHOST_SDL)
endif()
elseif(APPLE AND NOT WITH_GHOST_X11)
+ if(WITH_INPUT_IME)
+ add_definitions(-DWITH_INPUT_IME)
+ endif()
list(APPEND SRC
intern/GHOST_DisplayManagerCocoa.mm
intern/GHOST_SystemCocoa.mm
diff --git a/intern/ghost/intern/GHOST_WindowCocoa.h b/intern/ghost/intern/GHOST_WindowCocoa.h
index 3cfe46a080b..fdc806e2167 100644
--- a/intern/ghost/intern/GHOST_WindowCocoa.h
+++ b/intern/ghost/intern/GHOST_WindowCocoa.h
@@ -29,6 +29,9 @@
#endif // __APPLE__
#include "GHOST_Window.h"
+#ifdef WITH_INPUT_IME
+# include "GHOST_Event.h"
+#endif
@class CAMetalLayer;
@class CocoaMetalView;
@@ -263,6 +266,11 @@ class GHOST_WindowCocoa : public GHOST_Window {
return m_immediateDraw;
}
+#ifdef WITH_INPUT_IME
+ void beginIME(GHOST_TInt32 x, GHOST_TInt32 y, GHOST_TInt32 w, GHOST_TInt32 h, int completed);
+ void endIME();
+#endif /* WITH_INPUT_IME */
+
protected:
/**
* \param type: The type of rendering context create.
@@ -326,3 +334,28 @@ class GHOST_WindowCocoa : public GHOST_Window {
bool m_debug_context; // for debug messages during context setup
bool m_is_dialog;
};
+
+#ifdef WITH_INPUT_IME
+class GHOST_EventIME : public GHOST_Event {
+ public:
+ /**
+ * Constructor.
+ * \param msec: The time this event was generated.
+ * \param type: The type of key event.
+ * \param key: The key code of the key.
+ */
+ GHOST_EventIME(GHOST_TUns64 msec, GHOST_TEventType type, GHOST_IWindow *window, void *customdata)
+ : GHOST_Event(msec, type, window)
+ {
+ this->m_data = customdata;
+ }
+};
+
+typedef int GHOST_ImeStateFlagCocoa;
+enum {
+ GHOST_IME_INPUT_FOCUSED = (1 << 0),
+ GHOST_IME_ENABLED = (1 << 1),
+ GHOST_IME_COMPOSING = (1 << 2),
+ GHOST_IME_KEY_CONTROL_CHAR = (1 << 3)
+};
+#endif /* WITH_INPUT_IME */
diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm
index d082fa99ad8..cea2969739c 100644
--- a/intern/ghost/intern/GHOST_WindowCocoa.mm
+++ b/intern/ghost/intern/GHOST_WindowCocoa.mm
@@ -1221,3 +1221,26 @@ GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap
[pool drain];
return GHOST_kSuccess;
}
+
+#ifdef WITH_INPUT_IME
+void GHOST_WindowCocoa::beginIME(
+ GHOST_TInt32 x, GHOST_TInt32 y, GHOST_TInt32 w, GHOST_TInt32 h, int completed)
+{
+ if (m_openGLView) {
+ [m_openGLView beginIME:x y:y w:w h:h completed:(bool)completed];
+ }
+ else {
+ [m_metalView beginIME:x y:y w:w h:h completed:(bool)completed];
+ }
+}
+
+void GHOST_WindowCocoa::endIME()
+{
+ if (m_openGLView) {
+ [m_openGLView endIME];
+ }
+ else {
+ [m_metalView endIME];
+ }
+}
+#endif /* WITH_INPUT_IME */
diff --git a/intern/ghost/intern/GHOST_WindowViewCocoa.h b/intern/ghost/intern/GHOST_WindowViewCocoa.h
index af84297626b..42f0fcc6ac6 100644
--- a/intern/ghost/intern/GHOST_WindowViewCocoa.h
+++ b/intern/ghost/intern/GHOST_WindowViewCocoa.h
@@ -17,6 +17,11 @@
* All rights reserved.
*/
+/* The Carbon API is still needed to check if the Input Source (Input Method or IME) is valid. */
+#ifdef WITH_INPUT_IME
+# import <Carbon/Carbon.h>
+#endif
+
/* NSView subclass for drawing and handling input.
*
* COCOA_VIEW_BASE_CLASS will be either NSView or NSOpenGLView depending if
@@ -33,10 +38,30 @@
bool composing;
NSString *composing_text;
- bool immediate_draw;
+#ifdef WITH_INPUT_IME
+ struct {
+ GHOST_ImeStateFlagCocoa state_flag;
+ NSRect candidate_window_position;
+
+ /* Event data. */
+ GHOST_TEventImeData event;
+ std::string result;
+ std::string composite;
+ } ime;
+#endif
}
- (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa
windowCocoa:(GHOST_WindowCocoa *)winCocoa;
+
+#ifdef WITH_INPUT_IME
+- (void)beginIME:(GHOST_TInt32)x
+ y:(GHOST_TInt32)y
+ w:(GHOST_TInt32)w
+ h:(GHOST_TInt32)h
+ completed:(bool)completed;
+
+- (void)endIME;
+#endif
@end
@implementation COCOA_VIEW_CLASS
@@ -50,7 +75,21 @@
composing = false;
composing_text = nil;
- immediate_draw = false;
+#ifdef WITH_INPUT_IME
+ ime.state_flag = 0;
+ ime.candidate_window_position = NSZeroRect;
+ ime.event.cursor_position = -1;
+ ime.event.target_start = -1;
+ ime.event.target_end = -1;
+
+ /* Register a function to be executed when Input Method is changed using
+ * 'Control + Space' or language-specific keys (such as 'Eisu / Kana' key for Japanese).*/
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self
+ selector:@selector(ImeDidChangeCallback:)
+ name:NSTextInputContextKeyboardSelectionDidChangeNotification
+ object:nil];
+#endif
}
- (BOOL)acceptsFirstResponder
@@ -66,11 +105,20 @@
// The trick to prevent Cocoa from complaining (beeping)
- (void)keyDown:(NSEvent *)event
{
- systemCocoa->handleKeyEvent(event);
+#ifdef WITH_INPUT_IME
+ [self checkKeyCodeIsControlChar:event];
+ const bool ime_process = [self isProcessedByIme];
+#else
+ const bool ime_process = false;
+#endif
+
+ if (!ime_process) {
+ systemCocoa->handleKeyEvent(event);
+ }
/* Start or continue composing? */
if ([[event characters] length] == 0 || [[event charactersIgnoringModifiers] length] == 0 ||
- composing) {
+ composing || ime_process) {
composing = YES;
// interpret event to call insertText
@@ -202,26 +250,72 @@
}
}
+// Processes the Result String sent from the Input Method.
- (void)insertText:(id)chars replacementRange:(NSRange)replacementRange
{
[self composing_free];
+
+#ifdef WITH_INPUT_IME
+ if (ime.state_flag & GHOST_IME_ENABLED) {
+ if (!(ime.state_flag & GHOST_IME_COMPOSING)) {
+ [self processImeEvent:GHOST_kEventImeCompositionStart];
+ }
+
+ [self setImeResult:[self convertNSString:chars]];
+
+ [self processImeEvent:GHOST_kEventImeComposition];
+ [self processImeEvent:GHOST_kEventImeCompositionEnd];
+ ime.state_flag &= ~GHOST_IME_COMPOSING;
+ }
+#endif
}
+// Processes the Composition String sent from the Input Method.
- (void)setMarkedText:(id)chars
selectedRange:(NSRange)range
replacementRange:(NSRange)replacementRange
{
[self composing_free];
- if ([chars length] == 0)
+
+ if ([chars length] == 0) {
+#ifdef WITH_INPUT_IME
+ // Processes when the last Composition String is deleted.
+ if (ime.state_flag & GHOST_IME_COMPOSING) {
+ [self setImeResult:std::string()];
+ [self processImeEvent:GHOST_kEventImeComposition];
+ [self processImeEvent:GHOST_kEventImeCompositionEnd];
+ ime.state_flag &= ~GHOST_IME_COMPOSING;
+ }
+#endif
+
return;
+ }
// start composing
composing = YES;
composing_text = [chars copy];
+ // chars of markedText by Input Method is an instance of NSAttributedString
+ if ([chars isKindOfClass:[NSAttributedString class]]) {
+ composing_text = [[chars string] copy];
+ }
+
// if empty, cancel
if ([composing_text length] == 0)
[self composing_free];
+
+#ifdef WITH_INPUT_IME
+ if (ime.state_flag & GHOST_IME_ENABLED) {
+ if (!(ime.state_flag & GHOST_IME_COMPOSING)) {
+ ime.state_flag |= GHOST_IME_COMPOSING;
+ [self processImeEvent:GHOST_kEventImeCompositionStart];
+ }
+
+ [self setImeComposition:composing_text selectedRange:range];
+
+ [self processImeEvent:GHOST_kEventImeComposition];
+ }
+#endif
}
- (void)unmarkText
@@ -265,8 +359,14 @@
return NSMakeRange(0, length);
}
+// Specify the position where the Chinese and Japanese candidate windows are displayed.
- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
+#ifdef WITH_INPUT_IME
+ if (ime.state_flag & GHOST_IME_ENABLED) {
+ return ime.candidate_window_position;
+ }
+#endif
return NSZeroRect;
}
@@ -280,4 +380,159 @@
return [NSArray array];
}
+#ifdef WITH_INPUT_IME
+- (void)checkImeEnabled
+{
+ ime.state_flag &= ~GHOST_IME_ENABLED;
+
+ if (ime.state_flag & GHOST_IME_INPUT_FOCUSED) {
+ /* Since there are no functions in Cocoa API,
+ * we will use the functions in the Carbon API. */
+ TISInputSourceRef currentKeyboardInputSource = TISCopyCurrentKeyboardInputSource();
+ bool ime_enabled = !CFBooleanGetValue((CFBooleanRef)TISGetInputSourceProperty(
+ currentKeyboardInputSource, kTISPropertyInputSourceIsASCIICapable));
+ CFRelease(currentKeyboardInputSource);
+
+ if (ime_enabled) {
+ ime.state_flag |= GHOST_IME_ENABLED;
+ return;
+ }
+ }
+ return;
+}
+
+- (void)ImeDidChangeCallback:(NSNotification *)notification
+{
+ [self checkImeEnabled];
+}
+
+- (void)setImeCandidateWinPos:(GHOST_TInt32)x y:(GHOST_TInt32)y w:(GHOST_TInt32)w h:(GHOST_TInt32)h
+{
+ GHOST_TInt32 outX, outY;
+ associatedWindow->clientToScreen(x, y, outX, outY);
+ ime.candidate_window_position = NSMakeRect((CGFloat)outX, (CGFloat)outY, (CGFloat)w, (CGFloat)h);
+}
+
+- (void)beginIME:(GHOST_TInt32)x
+ y:(GHOST_TInt32)y
+ w:(GHOST_TInt32)w
+ h:(GHOST_TInt32)h
+ completed:(bool)completed
+{
+ ime.state_flag |= GHOST_IME_INPUT_FOCUSED;
+ [self checkImeEnabled];
+ [self setImeCandidateWinPos:x y:y w:w h:h];
+}
+
+- (void)endIME
+{
+ ime.state_flag = 0;
+ ime.result.clear();
+ ime.composite.clear();
+
+ [self unmarkText];
+ [[NSTextInputContext currentInputContext] discardMarkedText];
+}
+
+- (void)processImeEvent:(GHOST_TEventType)imeEventType
+{
+ ime.event.result_len = (GHOST_TUserDataPtr)ime.result.size();
+ ime.event.result = (GHOST_TUserDataPtr)ime.result.c_str();
+ ime.event.composite_len = (GHOST_TUserDataPtr)ime.composite.size();
+ ime.event.composite = (GHOST_TUserDataPtr)ime.composite.c_str();
+
+ GHOST_Event *event = new GHOST_EventIME(
+ systemCocoa->getMilliSeconds(), imeEventType, associatedWindow, &ime.event);
+ systemCocoa->pushEvent(event);
+}
+
+- (std::string)convertNSString:(NSString *)inString
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ std::string str([inString UTF8String]);
+ [pool drain];
+ return str;
+}
+
+- (void)setImeComposition:(NSString *)inString selectedRange:(NSRange)range
+{
+ ime.composite = [self convertNSString:inString];
+ 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. */
+ char *front_string = (char *)[[inString substringWithRange:NSMakeRange(0, range.location)]
+ UTF8String];
+ char *selected_string = (char *)[[inString substringWithRange:range] UTF8String];
+ ime.event.cursor_position = strlen(front_string);
+ ime.event.target_start = ime.event.cursor_position;
+ ime.event.target_end = ime.event.target_start + strlen(selected_string);
+}
+
+- (void)setImeResult:(std::string)result
+{
+ ime.result = result;
+ ime.composite.clear();
+ ime.event.cursor_position = -1;
+ ime.event.target_start = -1;
+ ime.event.target_end = -1;
+}
+
+- (void)checkKeyCodeIsControlChar:(NSEvent *)event
+{
+ ime.state_flag &= ~GHOST_IME_KEY_CONTROL_CHAR;
+ switch ([event keyCode]) {
+ case kVK_ANSI_KeypadEnter:
+ case kVK_ANSI_KeypadClear:
+ case kVK_F1:
+ case kVK_F2:
+ case kVK_F3:
+ case kVK_F4:
+ case kVK_F5:
+ case kVK_F6:
+ case kVK_F7:
+ case kVK_F8:
+ case kVK_F9:
+ case kVK_F10:
+ case kVK_F11:
+ case kVK_F12:
+ case kVK_F13:
+ case kVK_F14:
+ case kVK_F15:
+ case kVK_F16:
+ case kVK_F17:
+ case kVK_F18:
+ case kVK_F19:
+ case kVK_F20:
+ case kVK_UpArrow:
+ case kVK_DownArrow:
+ case kVK_LeftArrow:
+ case kVK_RightArrow:
+ case kVK_Return:
+ case kVK_Delete:
+ case kVK_ForwardDelete:
+ case kVK_Escape:
+ case kVK_Tab:
+ case kVK_Home:
+ case kVK_End:
+ case kVK_PageUp:
+ case kVK_PageDown:
+ case kVK_VolumeUp:
+ case kVK_VolumeDown:
+ case kVK_Mute:
+ ime.state_flag |= GHOST_IME_KEY_CONTROL_CHAR;
+ return;
+ }
+}
+
+/* Even if IME is enabled, when not composing, control characters
+ * (such as arrow, enter, delete) are handled by handleKeyEvent. */
+- (bool)isProcessedByIme
+{
+ return (
+ (ime.state_flag & GHOST_IME_ENABLED) &&
+ ((ime.state_flag & GHOST_IME_COMPOSING) || !(ime.state_flag & GHOST_IME_KEY_CONTROL_CHAR)));
+}
+#endif /* WITH_INPUT_IME */
+
@end