mirror of
https://github.com/glfw/glfw.git
synced 2024-11-26 20:11:58 +00:00
macOS: Support IME
This commit re-organizes 31b12b7f79a5aa8bd8f8eb1488a050ab894ca289. * Use dynamic load for TIS functions and stop using Carbon. * Generalize platform-specific features to _GLFWplatform. * Add caret-position info to preedit-callback. * Handle UTF16 data correctly. * Implement `firstRectForCharacterRange:actualRange:` to display preedit candidate window correctly. * Suppress _glfwInputKey during preediting. * Ensure preedit cleared after committed. * Fix wrong length of markedRange. * Improve IME status APIs. * Refactor code shapes and variable names. Co-authored-by: Takuro Ashie <ashie@clear-code.com> Co-authored-by: xfangfang <2553041586@qq.com>
This commit is contained in:
parent
e947cb7b52
commit
2368112f98
@ -150,12 +150,11 @@ endif()
|
|||||||
|
|
||||||
if (GLFW_BUILD_COCOA)
|
if (GLFW_BUILD_COCOA)
|
||||||
target_link_libraries(glfw PRIVATE "-framework Cocoa"
|
target_link_libraries(glfw PRIVATE "-framework Cocoa"
|
||||||
"-framework Carbon"
|
|
||||||
"-framework IOKit"
|
"-framework IOKit"
|
||||||
"-framework CoreFoundation")
|
"-framework CoreFoundation")
|
||||||
|
|
||||||
set(glfw_PKG_DEPS "")
|
set(glfw_PKG_DEPS "")
|
||||||
set(glfw_PKG_LIBS "-framework Cocoa -framework Carbon -framework IOKit -framework CoreFoundation")
|
set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (GLFW_BUILD_WAYLAND)
|
if (GLFW_BUILD_WAYLAND)
|
||||||
|
@ -349,22 +349,70 @@ static GLFWbool initializeTIS(void)
|
|||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CFStringRef* kCategoryKeyboardInputSource =
|
||||||
|
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("kTISCategoryKeyboardInputSource"));
|
||||||
|
CFStringRef* kPropertyInputSourceCategory =
|
||||||
|
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("kTISPropertyInputSourceCategory"));
|
||||||
|
CFStringRef* kPropertyInputSourceID =
|
||||||
|
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("kTISPropertyInputSourceID"));
|
||||||
|
CFStringRef* kPropertyInputSourceIsSelectCapable =
|
||||||
|
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("kTISPropertyInputSourceIsSelectCapable"));
|
||||||
|
CFStringRef* kPropertyInputSourceType =
|
||||||
|
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("kTISPropertyInputSourceType"));
|
||||||
CFStringRef* kPropertyUnicodeKeyLayoutData =
|
CFStringRef* kPropertyUnicodeKeyLayoutData =
|
||||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||||
CFSTR("kTISPropertyUnicodeKeyLayoutData"));
|
CFSTR("kTISPropertyUnicodeKeyLayoutData"));
|
||||||
|
CFStringRef* kTypeKeyboardInputMethodModeEnabled =
|
||||||
|
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("kTISTypeKeyboardInputMethodModeEnabled"));
|
||||||
|
_glfw.ns.tis.CopyCurrentASCIICapableKeyboardInputSource =
|
||||||
|
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("TISCopyCurrentASCIICapableKeyboardInputSource"));
|
||||||
|
_glfw.ns.tis.CopyCurrentKeyboardInputSource =
|
||||||
|
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("TISCopyCurrentKeyboardInputSource"));
|
||||||
_glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource =
|
_glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource =
|
||||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||||
CFSTR("TISCopyCurrentKeyboardLayoutInputSource"));
|
CFSTR("TISCopyCurrentKeyboardLayoutInputSource"));
|
||||||
|
_glfw.ns.tis.CopyInputSourceForLanguage =
|
||||||
|
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("TISCopyInputSourceForLanguage"));
|
||||||
|
_glfw.ns.tis.CreateASCIICapableInputSourceList =
|
||||||
|
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("TISCreateASCIICapableInputSourceList"));
|
||||||
|
_glfw.ns.tis.CreateInputSourceList =
|
||||||
|
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("TISCreateInputSourceList"));
|
||||||
_glfw.ns.tis.GetInputSourceProperty =
|
_glfw.ns.tis.GetInputSourceProperty =
|
||||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||||
CFSTR("TISGetInputSourceProperty"));
|
CFSTR("TISGetInputSourceProperty"));
|
||||||
|
_glfw.ns.tis.SelectInputSource =
|
||||||
|
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||||
|
CFSTR("TISSelectInputSource"));
|
||||||
_glfw.ns.tis.GetKbdType =
|
_glfw.ns.tis.GetKbdType =
|
||||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||||
CFSTR("LMGetKbdType"));
|
CFSTR("LMGetKbdType"));
|
||||||
|
|
||||||
if (!kPropertyUnicodeKeyLayoutData ||
|
if (!kCategoryKeyboardInputSource||
|
||||||
|
!kPropertyInputSourceCategory ||
|
||||||
|
!kPropertyInputSourceID ||
|
||||||
|
!kPropertyInputSourceIsSelectCapable||
|
||||||
|
!kPropertyInputSourceType||
|
||||||
|
!kPropertyUnicodeKeyLayoutData ||
|
||||||
|
!kTypeKeyboardInputMethodModeEnabled ||
|
||||||
|
!TISCopyCurrentASCIICapableKeyboardInputSource ||
|
||||||
|
!TISCopyCurrentKeyboardInputSource ||
|
||||||
!TISCopyCurrentKeyboardLayoutInputSource ||
|
!TISCopyCurrentKeyboardLayoutInputSource ||
|
||||||
|
!TISCopyInputSourceForLanguage ||
|
||||||
|
!TISCreateASCIICapableInputSourceList ||
|
||||||
|
!TISCreateInputSourceList ||
|
||||||
!TISGetInputSourceProperty ||
|
!TISGetInputSourceProperty ||
|
||||||
|
!TISSelectInputSource ||
|
||||||
!LMGetKbdType)
|
!LMGetKbdType)
|
||||||
{
|
{
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
@ -372,8 +420,20 @@ static GLFWbool initializeTIS(void)
|
|||||||
return GLFW_FALSE;
|
return GLFW_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_glfw.ns.tis.kCategoryKeyboardInputSource =
|
||||||
|
*kCategoryKeyboardInputSource;
|
||||||
|
_glfw.ns.tis.kPropertyInputSourceCategory =
|
||||||
|
*kPropertyInputSourceCategory;
|
||||||
|
_glfw.ns.tis.kPropertyInputSourceID =
|
||||||
|
*kPropertyInputSourceID;
|
||||||
|
_glfw.ns.tis.kPropertyInputSourceIsSelectCapable =
|
||||||
|
*kPropertyInputSourceIsSelectCapable;
|
||||||
|
_glfw.ns.tis.kPropertyInputSourceType =
|
||||||
|
*kPropertyInputSourceType;
|
||||||
_glfw.ns.tis.kPropertyUnicodeKeyLayoutData =
|
_glfw.ns.tis.kPropertyUnicodeKeyLayoutData =
|
||||||
*kPropertyUnicodeKeyLayoutData;
|
*kPropertyUnicodeKeyLayoutData;
|
||||||
|
_glfw.ns.tis.kTypeKeyboardInputMethodModeEnabled =
|
||||||
|
*kTypeKeyboardInputMethodModeEnabled;
|
||||||
|
|
||||||
return updateUnicodeData();
|
return updateUnicodeData();
|
||||||
}
|
}
|
||||||
|
@ -109,11 +109,29 @@ typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMeta
|
|||||||
#define GLFW_NSGL_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl;
|
#define GLFW_NSGL_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl;
|
||||||
|
|
||||||
// HIToolbox.framework pointer typedefs
|
// HIToolbox.framework pointer typedefs
|
||||||
|
#define kTISCategoryKeyboardInputSource _glfw.ns.tis.kCategoryKeyboardInputSource
|
||||||
|
#define kTISPropertyInputSourceCategory _glfw.ns.tis.kPropertyInputSourceCategory
|
||||||
|
#define kTISPropertyInputSourceID _glfw.ns.tis.kPropertyInputSourceID
|
||||||
|
#define kTISPropertyInputSourceIsSelectCapable _glfw.ns.tis.kPropertyInputSourceIsSelectCapable
|
||||||
|
#define kTISPropertyInputSourceType _glfw.ns.tis.kPropertyInputSourceType
|
||||||
#define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData
|
#define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData
|
||||||
|
#define kTISTypeKeyboardInputMethodModeEnabled _glfw.ns.tis.kTypeKeyboardInputMethodModeEnabled
|
||||||
|
typedef TISInputSourceRef (*PFN_TISCopyCurrentASCIICapableKeyboardInputSource)(void);
|
||||||
|
#define TISCopyCurrentASCIICapableKeyboardInputSource _glfw.ns.tis.CopyCurrentASCIICapableKeyboardInputSource
|
||||||
|
typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardInputSource)(void);
|
||||||
|
#define TISCopyCurrentKeyboardInputSource _glfw.ns.tis.CopyCurrentKeyboardInputSource
|
||||||
typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void);
|
typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void);
|
||||||
#define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource
|
#define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource
|
||||||
|
typedef TISInputSourceRef (*PFN_TISCopyInputSourceForLanguage)(CFStringRef);
|
||||||
|
#define TISCopyInputSourceForLanguage _glfw.ns.tis.CopyInputSourceForLanguage
|
||||||
|
typedef CFArrayRef (*PFN_TISCreateASCIICapableInputSourceList)(void);
|
||||||
|
#define TISCreateASCIICapableInputSourceList _glfw.ns.tis.CreateASCIICapableInputSourceList
|
||||||
|
typedef CFArrayRef (*PEN_TISCreateInputSourceList)(CFDictionaryRef,Boolean);
|
||||||
|
#define TISCreateInputSourceList _glfw.ns.tis.CreateInputSourceList
|
||||||
typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef);
|
typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef);
|
||||||
#define TISGetInputSourceProperty _glfw.ns.tis.GetInputSourceProperty
|
#define TISGetInputSourceProperty _glfw.ns.tis.GetInputSourceProperty
|
||||||
|
typedef OSStatus (*PFN_TISSelectInputSource)(TISInputSourceRef);
|
||||||
|
#define TISSelectInputSource _glfw.ns.tis.SelectInputSource
|
||||||
typedef UInt8 (*PFN_LMGetKbdType)(void);
|
typedef UInt8 (*PFN_LMGetKbdType)(void);
|
||||||
#define LMGetKbdType _glfw.ns.tis.GetKbdType
|
#define LMGetKbdType _glfw.ns.tis.GetKbdType
|
||||||
|
|
||||||
@ -184,10 +202,22 @@ typedef struct _GLFWlibraryNS
|
|||||||
|
|
||||||
struct {
|
struct {
|
||||||
CFBundleRef bundle;
|
CFBundleRef bundle;
|
||||||
|
PFN_TISCopyCurrentASCIICapableKeyboardInputSource CopyCurrentASCIICapableKeyboardInputSource;
|
||||||
|
PFN_TISCopyCurrentKeyboardInputSource CopyCurrentKeyboardInputSource;
|
||||||
PFN_TISCopyCurrentKeyboardLayoutInputSource CopyCurrentKeyboardLayoutInputSource;
|
PFN_TISCopyCurrentKeyboardLayoutInputSource CopyCurrentKeyboardLayoutInputSource;
|
||||||
|
PFN_TISCopyInputSourceForLanguage CopyInputSourceForLanguage;
|
||||||
|
PFN_TISCreateASCIICapableInputSourceList CreateASCIICapableInputSourceList;
|
||||||
|
PEN_TISCreateInputSourceList CreateInputSourceList;
|
||||||
PFN_TISGetInputSourceProperty GetInputSourceProperty;
|
PFN_TISGetInputSourceProperty GetInputSourceProperty;
|
||||||
|
PFN_TISSelectInputSource SelectInputSource;
|
||||||
PFN_LMGetKbdType GetKbdType;
|
PFN_LMGetKbdType GetKbdType;
|
||||||
|
CFStringRef kCategoryKeyboardInputSource;
|
||||||
|
CFStringRef kPropertyInputSourceCategory;
|
||||||
|
CFStringRef kPropertyInputSourceID;
|
||||||
|
CFStringRef kPropertyInputSourceIsSelectCapable;
|
||||||
|
CFStringRef kPropertyInputSourceType;
|
||||||
CFStringRef kPropertyUnicodeKeyLayoutData;
|
CFStringRef kPropertyUnicodeKeyLayoutData;
|
||||||
|
CFStringRef kTypeKeyboardInputMethodModeEnabled;
|
||||||
} tis;
|
} tis;
|
||||||
} _GLFWlibraryNS;
|
} _GLFWlibraryNS;
|
||||||
|
|
||||||
|
@ -321,7 +321,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)imeStatusChangeNotified:(NSNotification *)notification {
|
- (void)imeStatusChangeNotified:(NSNotification *)notification
|
||||||
|
{
|
||||||
_glfwInputIMEStatus(window);
|
_glfwInputIMEStatus(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,7 +570,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
const int key = translateKey([event keyCode]);
|
const int key = translateKey([event keyCode]);
|
||||||
const int mods = translateFlags([event modifierFlags]);
|
const int mods = translateFlags([event modifierFlags]);
|
||||||
|
|
||||||
_glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods);
|
if (![self hasMarkedText])
|
||||||
|
_glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods);
|
||||||
|
|
||||||
[self interpretKeyEvents:@[event]];
|
[self interpretKeyEvents:@[event]];
|
||||||
}
|
}
|
||||||
@ -662,7 +664,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
- (NSRange)markedRange
|
- (NSRange)markedRange
|
||||||
{
|
{
|
||||||
if ([markedText length] > 0)
|
if ([markedText length] > 0)
|
||||||
return NSMakeRange(0, [markedText length] - 1);
|
return NSMakeRange(0, [markedText length]);
|
||||||
else
|
else
|
||||||
return kEmptyRange;
|
return kEmptyRange;
|
||||||
}
|
}
|
||||||
@ -684,59 +686,92 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
|
|
||||||
NSString* markedTextString = markedText.string;
|
NSString* markedTextString = markedText.string;
|
||||||
|
|
||||||
NSUInteger i, length = [markedTextString length];
|
NSUInteger textLen = [markedTextString length];
|
||||||
int ctext = window->ctext;
|
_GLFWpreedit* preedit = &window->preedit;
|
||||||
while (ctext < length+1) {
|
int textBufferCount = preedit->textBufferCount;
|
||||||
ctext = (ctext == 0) ? 1 : ctext*2;
|
while (textBufferCount < textLen + 1)
|
||||||
}
|
textBufferCount = textBufferCount == 0 ? 1 : textBufferCount * 2;
|
||||||
if (ctext != window->ctext) {
|
if (textBufferCount != preedit->textBufferCount)
|
||||||
unsigned int* preeditText = realloc(window->preeditText, sizeof(unsigned int)*ctext);
|
|
||||||
if (preeditText == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window->preeditText = preeditText;
|
|
||||||
window->ctext = ctext;
|
|
||||||
}
|
|
||||||
window->ntext = length;
|
|
||||||
window->preeditText[length] = 0;
|
|
||||||
for (i = 0; i < length; i++)
|
|
||||||
{
|
{
|
||||||
const unichar codepoint = [markedTextString characterAtIndex:i];
|
unsigned int* preeditText = _glfw_realloc(preedit->text,
|
||||||
window->preeditText[i] = codepoint;
|
sizeof(unsigned int) * textBufferCount);
|
||||||
|
if (preeditText == NULL)
|
||||||
|
return;
|
||||||
|
preedit->text = preeditText;
|
||||||
|
preedit->textBufferCount = textBufferCount;
|
||||||
}
|
}
|
||||||
int focusedBlock = 0;
|
|
||||||
NSInteger offset = 0;
|
|
||||||
window->nblocks = 0;
|
|
||||||
while (offset < length) {
|
|
||||||
NSRange effectiveRange;
|
|
||||||
NSDictionary *attributes = [markedText attributesAtIndex:offset effectiveRange:&effectiveRange];
|
|
||||||
|
|
||||||
if (window->nblocks == window->cblocks) {
|
// NSString handles text data in UTF16 by default, so we have to convert them
|
||||||
int cblocks = window->cblocks * 2;
|
// to UTF32. Not only the encoding, but also the number of characters and
|
||||||
int* blocks = realloc(window->preeditAttributeBlocks, sizeof(int)*cblocks);
|
// the position of each block.
|
||||||
if (blocks == NULL) {
|
int currentBlockIndex = 0;
|
||||||
|
int currentBlockLength = 0;
|
||||||
|
int currentBlockLocation = 0;
|
||||||
|
int focusedBlockIndex = 0;
|
||||||
|
NSInteger preeditTextLength = 0;
|
||||||
|
NSRange range = NSMakeRange(0, textLen);
|
||||||
|
while (range.length)
|
||||||
|
{
|
||||||
|
uint32_t codepoint = 0;
|
||||||
|
NSRange currentBlockRange;
|
||||||
|
[markedText attributesAtIndex:range.location
|
||||||
|
effectiveRange:¤tBlockRange];
|
||||||
|
|
||||||
|
if (preedit->blockSizesBufferCount < 1 + currentBlockIndex)
|
||||||
|
{
|
||||||
|
int blockBufferCount = (preedit->blockSizesBufferCount == 0)
|
||||||
|
? 1 : preedit->blockSizesBufferCount * 2;
|
||||||
|
int* blocks = _glfw_realloc(preedit->blockSizes,
|
||||||
|
sizeof(int) * blockBufferCount);
|
||||||
|
if (blocks == NULL)
|
||||||
return;
|
return;
|
||||||
}
|
preedit->blockSizes = blocks;
|
||||||
window->preeditAttributeBlocks = blocks;
|
preedit->blockSizesBufferCount = blockBufferCount;
|
||||||
window->cblocks = cblocks;
|
|
||||||
}
|
}
|
||||||
window->preeditAttributeBlocks[window->nblocks] = effectiveRange.length;
|
|
||||||
offset += effectiveRange.length;
|
if (currentBlockLocation != currentBlockRange.location)
|
||||||
if (effectiveRange.length == 0) {
|
{
|
||||||
break;
|
currentBlockLocation = currentBlockRange.location;
|
||||||
|
preedit->blockSizes[currentBlockIndex++] = currentBlockLength;
|
||||||
|
currentBlockLength = 0;
|
||||||
|
if (selectedRange.location == currentBlockRange.location)
|
||||||
|
focusedBlockIndex = currentBlockIndex;
|
||||||
}
|
}
|
||||||
NSNumber* underline = (NSNumber*) [attributes objectForKey:@"NSUnderline"];
|
|
||||||
if ([underline intValue] != 1) {
|
if ([markedTextString getBytes:&codepoint
|
||||||
focusedBlock = window->nblocks;
|
maxLength:sizeof(codepoint)
|
||||||
|
usedLength:NULL
|
||||||
|
encoding:NSUTF32StringEncoding
|
||||||
|
options:0
|
||||||
|
range:range
|
||||||
|
remainingRange:&range])
|
||||||
|
{
|
||||||
|
if (codepoint >= 0xf700 && codepoint <= 0xf7ff)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
preedit->text[preeditTextLength++] = codepoint;
|
||||||
|
currentBlockLength++;
|
||||||
}
|
}
|
||||||
window->nblocks++;
|
|
||||||
}
|
}
|
||||||
_glfwInputPreedit(window, focusedBlock);
|
preedit->blockSizes[currentBlockIndex] = currentBlockLength;
|
||||||
|
preedit->blockSizesCount = 1 + currentBlockIndex;
|
||||||
|
preedit->textCount = preeditTextLength;
|
||||||
|
preedit->text[preeditTextLength] = 0;
|
||||||
|
preedit->focusedBlockIndex = focusedBlockIndex;
|
||||||
|
// The caret is always at the last of preedit in macOS.
|
||||||
|
preedit->caretIndex = preeditTextLength;
|
||||||
|
|
||||||
|
_glfwInputPreedit(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)unmarkText
|
- (void)unmarkText
|
||||||
{
|
{
|
||||||
[[markedText mutableString] setString:@""];
|
[[markedText mutableString] setString:@""];
|
||||||
|
window->preedit.blockSizesCount = 0;
|
||||||
|
window->preedit.textCount = 0;
|
||||||
|
window->preedit.focusedBlockIndex = 0;
|
||||||
|
window->preedit.caretIndex = 0;
|
||||||
|
_glfwInputPreedit(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray*)validAttributesForMarkedText
|
- (NSArray*)validAttributesForMarkedText
|
||||||
@ -758,8 +793,19 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
- (NSRect)firstRectForCharacterRange:(NSRange)range
|
- (NSRect)firstRectForCharacterRange:(NSRange)range
|
||||||
actualRange:(NSRangePointer)actualRange
|
actualRange:(NSRangePointer)actualRange
|
||||||
{
|
{
|
||||||
const NSRect frame = [window->ns.view frame];
|
int x = window->preedit.cursorPosX;
|
||||||
return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0);
|
int y = window->preedit.cursorPosY;
|
||||||
|
int w = window->preedit.cursorWidth;
|
||||||
|
int h = window->preedit.cursorHeight;
|
||||||
|
|
||||||
|
const NSRect frame =
|
||||||
|
[window->ns.object contentRectForFrameRect:[window->ns.object frame]];
|
||||||
|
|
||||||
|
return NSMakeRect(frame.origin.x + x,
|
||||||
|
// The y-axis is upward on macOS, so this conversion is needed.
|
||||||
|
frame.origin.y + frame.size.height - y - h,
|
||||||
|
w,
|
||||||
|
h);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
||||||
@ -793,6 +839,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
_glfwInputChar(window, codepoint, mods, plain);
|
_glfwInputChar(window, codepoint, mods, plain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[self unmarkText];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)doCommandBySelector:(SEL)selector
|
- (void)doCommandBySelector:(SEL)selector
|
||||||
@ -1038,10 +1086,10 @@ GLFWbool _glfwCreateWindowCocoa(_GLFWwindow* window,
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter]
|
[[NSNotificationCenter defaultCenter]
|
||||||
addObserver: window->ns.delegate
|
addObserver:window->ns.delegate
|
||||||
selector:@selector(imeStatusChangeNotified:)
|
selector:@selector(imeStatusChangeNotified:)
|
||||||
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||||
object: nil];
|
object:nil];
|
||||||
|
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
|
|
||||||
@ -1055,7 +1103,7 @@ void _glfwDestroyWindowCocoa(_GLFWwindow* window)
|
|||||||
if (_glfw.ns.disabledCursorWindow == window)
|
if (_glfw.ns.disabledCursorWindow == window)
|
||||||
_glfw.ns.disabledCursorWindow = NULL;
|
_glfw.ns.disabledCursorWindow = NULL;
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver: window->ns.delegate];
|
[[NSNotificationCenter defaultCenter] removeObserver:window->ns.delegate];
|
||||||
|
|
||||||
[window->ns.object orderOut:nil];
|
[window->ns.object orderOut:nil];
|
||||||
|
|
||||||
@ -1946,19 +1994,114 @@ const char* _glfwGetClipboardStringCocoa(void)
|
|||||||
|
|
||||||
void _glfwUpdatePreeditCursorRectangleCocoa(_GLFWwindow* window)
|
void _glfwUpdatePreeditCursorRectangleCocoa(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
|
// Do nothing. Instead, implement `firstRectForCharacterRange` callback
|
||||||
|
// to update the position.
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwResetPreeditTextCocoa(_GLFWwindow* window)
|
void _glfwResetPreeditTextCocoa(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
|
@autoreleasepool {
|
||||||
|
|
||||||
|
NSTextInputContext* context = [NSTextInputContext currentInputContext];
|
||||||
|
[context discardMarkedText];
|
||||||
|
[window->ns.view unmarkText];
|
||||||
|
|
||||||
|
} // autoreleasepool
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwSetIMEStatusCocoa(_GLFWwindow* window, int active)
|
void _glfwSetIMEStatusCocoa(_GLFWwindow* window, int active)
|
||||||
{
|
{
|
||||||
|
@autoreleasepool {
|
||||||
|
|
||||||
|
if (active)
|
||||||
|
{
|
||||||
|
NSArray* locales = CFBridgingRelease(CFLocaleCopyPreferredLanguages());
|
||||||
|
// Select the most preferred locale.
|
||||||
|
CFStringRef locale = (__bridge CFStringRef) [locales firstObject];
|
||||||
|
if (locale)
|
||||||
|
{
|
||||||
|
TISInputSourceRef source = TISCopyInputSourceForLanguage(locale);
|
||||||
|
if (source)
|
||||||
|
{
|
||||||
|
CFStringRef sourceType = TISGetInputSourceProperty(source,
|
||||||
|
kTISPropertyInputSourceType);
|
||||||
|
|
||||||
|
if (sourceType != kTISTypeKeyboardInputMethodModeEnabled)
|
||||||
|
TISSelectInputSource(source);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Some IMEs return a input-method that has input-method-modes for `TISCopyInputSourceForLanguage()`.
|
||||||
|
// We can't select these input-methods directly, but need to find
|
||||||
|
// a input-method-mode of the input-method.
|
||||||
|
// Example:
|
||||||
|
// - Input Method: com.apple.inputmethod.SCIM
|
||||||
|
// - Input Mode: com.apple.inputmethod.SCIM.ITABC
|
||||||
|
NSString* sourceID =
|
||||||
|
(__bridge NSString *) TISGetInputSourceProperty(source, kTISPropertyInputSourceID);
|
||||||
|
NSDictionary* properties = @{
|
||||||
|
(__bridge NSString *) kTISPropertyInputSourceCategory: (__bridge NSString *) kTISCategoryKeyboardInputSource,
|
||||||
|
(__bridge NSString *) kTISPropertyInputSourceIsSelectCapable: @YES,
|
||||||
|
};
|
||||||
|
NSArray* selectableSources =
|
||||||
|
CFBridgingRelease(TISCreateInputSourceList((__bridge CFDictionaryRef) properties, NO));
|
||||||
|
for (id sourceCandidate in selectableSources)
|
||||||
|
{
|
||||||
|
TISInputSourceRef sourceCandidateRef = (__bridge TISInputSourceRef) sourceCandidate;
|
||||||
|
NSString* sourceCandidateID =
|
||||||
|
(__bridge NSString *) TISGetInputSourceProperty(sourceCandidateRef, kTISPropertyInputSourceID);
|
||||||
|
if ([sourceCandidateID hasPrefix:sourceID])
|
||||||
|
{
|
||||||
|
TISSelectInputSource(sourceCandidateRef);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CFRelease(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TISInputSourceRef source = TISCopyCurrentASCIICapableKeyboardInputSource();
|
||||||
|
TISSelectInputSource(source);
|
||||||
|
CFRelease(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// `NSTextInputContextKeyboardSelectionDidChangeNotification` is sometimes
|
||||||
|
// not called immediately after this, so call the callback here.
|
||||||
|
_glfwInputIMEStatus(window);
|
||||||
|
|
||||||
|
} // autoreleasepool
|
||||||
}
|
}
|
||||||
|
|
||||||
int _glfwGetIMEStatusCocoa(_GLFWwindow* window)
|
int _glfwGetIMEStatusCocoa(_GLFWwindow* window)
|
||||||
{
|
{
|
||||||
return GLFW_FALSE;
|
@autoreleasepool {
|
||||||
|
|
||||||
|
NSArray* asciiInputSources =
|
||||||
|
CFBridgingRelease(TISCreateASCIICapableInputSourceList());
|
||||||
|
|
||||||
|
TISInputSourceRef currentSource = TISCopyCurrentKeyboardInputSource();
|
||||||
|
NSString* currentSourceID =
|
||||||
|
(__bridge NSString *) TISGetInputSourceProperty(currentSource,
|
||||||
|
kTISPropertyInputSourceID);
|
||||||
|
CFRelease(currentSource);
|
||||||
|
|
||||||
|
for (int i = 0; i < [asciiInputSources count]; i++)
|
||||||
|
{
|
||||||
|
TISInputSourceRef asciiSource =
|
||||||
|
(__bridge TISInputSourceRef) [asciiInputSources objectAtIndex:i];
|
||||||
|
NSString* asciiSourceID =
|
||||||
|
(__bridge NSString *) TISGetInputSourceProperty(asciiSource,
|
||||||
|
kTISPropertyInputSourceID);
|
||||||
|
if ([asciiSourceID compare:currentSourceID] == NSOrderedSame)
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GLFW_TRUE;
|
||||||
|
|
||||||
|
} // autoreleasepool
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLenum _glfwGetEGLPlatformCocoa(EGLint** attribs)
|
EGLenum _glfwGetEGLPlatformCocoa(EGLint** attribs)
|
||||||
@ -2114,49 +2257,6 @@ VkResult _glfwCreateWindowSurfaceCocoa(VkInstance instance,
|
|||||||
} // autoreleasepool
|
} // autoreleasepool
|
||||||
}
|
}
|
||||||
|
|
||||||
void _glfwPlatformResetPreeditText(_GLFWwindow* window)
|
|
||||||
{
|
|
||||||
NSTextInputContext *context = [NSTextInputContext currentInputContext];
|
|
||||||
[context discardMarkedText];
|
|
||||||
[window->ns.view unmarkText];
|
|
||||||
}
|
|
||||||
|
|
||||||
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active)
|
|
||||||
{
|
|
||||||
// Mac OS has several input sources.
|
|
||||||
// this code assumes input methods not in ascii capable inputs using IME.
|
|
||||||
NSArray* asciiInputSources = CFBridgingRelease(TISCreateASCIICapableInputSourceList());
|
|
||||||
TISInputSourceRef asciiSource = (__bridge TISInputSourceRef)([asciiInputSources firstObject]);
|
|
||||||
if (active) {
|
|
||||||
NSArray* allInputSources = CFBridgingRelease(TISCreateInputSourceList(NULL, false));
|
|
||||||
NSString* asciiSourceID = (__bridge NSString *)(TISGetInputSourceProperty(asciiSource, kTISPropertyInputSourceID));
|
|
||||||
int i;
|
|
||||||
int count = [allInputSources count];
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
TISInputSourceRef source = (__bridge TISInputSourceRef)([allInputSources objectAtIndex: i]);
|
|
||||||
NSString* sourceID = (__bridge NSString *)(TISGetInputSourceProperty(source, kTISPropertyInputSourceID));
|
|
||||||
if ([asciiSourceID compare: sourceID] != NSOrderedSame) {
|
|
||||||
TISSelectInputSource(source);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (asciiSource) {
|
|
||||||
TISSelectInputSource(asciiSource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int _glfwPlatformGetIMEStatus(_GLFWwindow* window)
|
|
||||||
{
|
|
||||||
TISInputSourceRef currentSource = TISCopyCurrentKeyboardInputSource();
|
|
||||||
NSString* currentSourceID = (__bridge NSString *)(TISGetInputSourceProperty(currentSource, kTISPropertyInputSourceID));
|
|
||||||
NSArray* asciiInputSources = CFBridgingRelease(TISCreateASCIICapableInputSourceList());
|
|
||||||
TISInputSourceRef asciiSource = (__bridge TISInputSourceRef)([asciiInputSources firstObject]);
|
|
||||||
if (asciiSource) {
|
|
||||||
NSString* asciiSourceID = (__bridge NSString *)(TISGetInputSourceProperty(asciiSource, kTISPropertyInputSourceID));
|
|
||||||
return ([asciiSourceID compare: currentSourceID] == NSOrderedSame) ? GLFW_FALSE : GLFW_TRUE;
|
|
||||||
}
|
|
||||||
return GLFW_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW native API //////
|
////// GLFW native API //////
|
||||||
|
Loading…
Reference in New Issue
Block a user