Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion docs/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,47 @@ In this case, the preedit callback also works on X11. However, on-the-spot styl
X11 is unstable, so it is not recommended.


@subsection input_text_focus Text input focus

Text input focus describes whether the application is currently in a text input
context. Examples include a chat box, text field, search box, rename dialog or
editor. This is distinct from native window focus.

This is useful for applications that render their own user interface inside a
single native window, such as games and browsers. In these applications, the
native window may remain focused while the application switches between
gameplay, menus, chat input, search fields, editors and other UI elements. The
application is the only component that reliably knows when text input is
expected.

Use @ref glfwSetTextInputFocus to tell GLFW when the application enters or
leaves a text input context:

@code
glfwSetTextInputFocus(window, GLFW_TRUE); // Text field became active
glfwSetTextInputFocus(window, GLFW_FALSE); // Text field lost focus
@endcode

This function does not turn the IME on or off. It expresses whether GLFW should
route text input through the platform text input or IME path for this window.
It does not request an input language change, force a specific IME state or
force a specific input source.

For compatibility, GLFW preserves the previous platform text input behavior for
applications that never call @ref glfwSetTextInputFocus. Once an application
calls it for a window, that window enters explicit text input focus management.
The application is then responsible for calling it with `GLFW_TRUE` when text
input begins and with `GLFW_FALSE` when text input ends.

Applications that opt into explicit text input focus management should set the
initial state explicitly, usually to `GLFW_FALSE`, after window creation. They
should then set it to `GLFW_TRUE` only while a text input widget is active.

Individual platforms map this abstraction to their native text input
mechanisms. Some platforms may also cancel or clear active preedit text when
text input focus is set to `GLFW_FALSE`.


@subsection input_preedit Preedit input

When inputting text with IME, the text is temporarily inputted, then conversion
Expand Down Expand Up @@ -407,6 +448,16 @@ glfwSetInputMode(window, GLFW_IME, GLFW_TRUE);
glfwSetInputMode(window, GLFW_IME, GLFW_FALSE);
@endcode

This is related to but distinct from @ref glfwSetTextInputFocus. Text input
focus describes application intent: the application has entered or left a text
input context. `GLFW_IME` controls platform-specific IME state. Applications
should normally prefer @ref glfwSetTextInputFocus unless they specifically need
platform-dependent IME state control.

As a rule of thumb, if you think you need to enable or disable IME because a
chat box, text field, search field, rename dialog or editor gained or lost
focus, you probably want text input focus instead.

You can use the following function to clear the current preedit.

@code
Expand Down Expand Up @@ -1239,4 +1290,3 @@ void drop_callback(GLFWwindow* window, int count, const char** paths)
The path array and its strings are only valid until the file drop callback
returns, as they may have been generated specifically for that event. You need
to make a deep copy of the array if you want to keep the paths.

51 changes: 50 additions & 1 deletion include/GLFW/glfw3.h
Original file line number Diff line number Diff line change
Expand Up @@ -5310,6 +5310,56 @@ GLFWAPI void glfwSetPreeditCursorRectangle(GLFWwindow* window, int x, int y, int
*/
GLFWAPI void glfwResetPreeditText(GLFWwindow* window);

/*! @brief Sets whether the application has text input focus.
*
* This function informs GLFW that the specified window has entered or left
* a text input context, such as a chat box, text field, search box, rename
* dialog or editor. Text input focus is separate from native window focus.
* A window may remain focused while the application switches between
* gameplay, menus, chat input, search fields, editors and other UI elements.
* The application is the only component that reliably knows when text input
* is expected.
*
* Pass `GLFW_TRUE` when the application enters a text input context and
* `GLFW_FALSE` when it leaves that context.
*
* This function does not turn the IME on or off, switch input languages or
* force a specific platform input source. It expresses whether GLFW should
* route key and text input through the platform text input or IME path for
* this window. Individual platforms may map this abstraction differently.
*
* For compatibility, applications that never call this function keep the same
* platform text input behavior as before this API was introduced. Once this
* function is called for a window, that window enters explicit text input
* focus management and the application is responsible for notifying GLFW when
* text input begins and ends.
* Applications that opt into explicit text input focus management should set
* the initial state explicitly after window creation.
*
* This function is related to but distinct from
* `glfwSetInputMode(window, GLFW_IME, value)`. Text input focus describes
* application intent, while `GLFW_IME` controls platform-specific IME state.
* Applications should normally prefer this function unless they specifically
* need platform-dependent IME state control.
*
* @param[in] window The window whose text input focus state to set.
* @param[in] focused `GLFW_TRUE` to enter text input focus, or `GLFW_FALSE`
* to leave it.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref ime_support
* @sa @ref glfwSetInputMode
*
* @since Added in GLFW 3.X.
*
* @ingroup input
*/
GLFWAPI void glfwSetTextInputFocus(GLFWwindow* window, int focused);

/*! @brief Returns the preedit candidate.
*
* This function returns the text and the text-count of the preedit candidate.
Expand Down Expand Up @@ -6862,4 +6912,3 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window
#endif

#endif /* _glfw3_h_ */

2 changes: 1 addition & 1 deletion src/cocoa_init.m
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform)
.getClipboardString = _glfwGetClipboardStringCocoa,
.updatePreeditCursorRectangle = _glfwUpdatePreeditCursorRectangleCocoa,
.resetPreeditText = _glfwResetPreeditTextCocoa,
.setTextInputFocus = _glfwSetTextInputFocusCocoa,
.setIMEStatus = _glfwSetIMEStatusCocoa,
.getIMEStatus = _glfwGetIMEStatusCocoa,
.initJoysticks = _glfwInitJoysticksCocoa,
Expand Down Expand Up @@ -723,4 +724,3 @@ void _glfwTerminateCocoa(void)
}

#endif // _GLFW_COCOA

2 changes: 1 addition & 1 deletion src/cocoa_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ const char* _glfwGetClipboardStringCocoa(void);

void _glfwUpdatePreeditCursorRectangleCocoa(_GLFWwindow* window);
void _glfwResetPreeditTextCocoa(_GLFWwindow* window);
void _glfwSetTextInputFocusCocoa(_GLFWwindow* window, GLFWbool focused);
void _glfwSetIMEStatusCocoa(_GLFWwindow* window, int active);
int _glfwGetIMEStatusCocoa(_GLFWwindow* window);

Expand Down Expand Up @@ -332,4 +333,3 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig);
void _glfwDestroyContextNSGL(_GLFWwindow* window);

16 changes: 14 additions & 2 deletions src/cocoa_window.m
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,14 @@ - (void)keyDown:(NSEvent *)event
if (![self hasMarkedText])
_glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods);

[self interpretKeyEvents:@[event]];
if (!window->textInputFocusExplicit || window->textInputFocus)
[self interpretKeyEvents:@[event]];
else
{
NSString* characters = [event characters];
if (characters)
[self insertText:characters replacementRange:[self selectedRange]];
}
}

- (void)flagsChanged:(NSEvent *)event
Expand Down Expand Up @@ -2037,6 +2044,12 @@ void _glfwResetPreeditTextCocoa(_GLFWwindow* window)
} // autoreleasepool
}

void _glfwSetTextInputFocusCocoa(_GLFWwindow* window, GLFWbool focused)
{
if (!focused)
_glfwResetPreeditTextCocoa(window);
}

void _glfwSetIMEStatusCocoa(_GLFWwindow* window, int active)
{
@autoreleasepool {
Expand Down Expand Up @@ -2311,4 +2324,3 @@ GLFWAPI id glfwGetCocoaView(GLFWwindow* handle)
}

#endif // _GLFW_COCOA

14 changes: 13 additions & 1 deletion src/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,19 @@ GLFWAPI void glfwResetPreeditText(GLFWwindow* handle)
_glfw.platform.resetPreeditText(window);
}

GLFWAPI void glfwSetTextInputFocus(GLFWwindow* handle, int focused)
{
_GLFW_REQUIRE_INIT();

_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);

focused = focused ? GLFW_TRUE : GLFW_FALSE;
window->textInputFocusExplicit = GLFW_TRUE;
window->textInputFocus = focused;
_glfw.platform.setTextInputFocus(window, focused);
}

GLFWAPI unsigned int* glfwGetPreeditCandidate(GLFWwindow* handle, int index, int* textCount)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
Expand Down Expand Up @@ -1647,4 +1660,3 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void)
_GLFW_REQUIRE_INIT_OR_RETURN(0);
return _glfwPlatformGetTimerFrequency();
}

14 changes: 13 additions & 1 deletion src/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,18 @@ struct _GLFWwindow
GLFWbool stickyMouseButtons;
GLFWbool lockKeyMods;
GLFWbool disableMouseButtonLimit;

// Requested application text input focus state. This is meaningful only
// after textInputFocusExplicit has been set by glfwSetTextInputFocus.
// It describes whether an application-drawn text input context, such as a
// chat box or text field, currently has focus inside this native window.
GLFWbool textInputFocus;
// Whether the application has opted into explicit text input focus
// management for this window. This exists so that the zero-initialized
// textInputFocus value does not disable or bypass existing platform text
// input behavior for applications that never call glfwSetTextInputFocus.
GLFWbool textInputFocusExplicit;

int cursorMode;
char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1];
char keys[GLFW_KEY_LAST + 1];
Expand Down Expand Up @@ -744,6 +756,7 @@ struct _GLFWplatform
const char* (*getClipboardString)(void);
void (*updatePreeditCursorRectangle)(_GLFWwindow*);
void (*resetPreeditText)(_GLFWwindow*);
void (*setTextInputFocus)(_GLFWwindow*,GLFWbool);
void (*setIMEStatus)(_GLFWwindow*,int);
int (*getIMEStatus)(_GLFWwindow*);
GLFWbool (*initJoysticks)(void);
Expand Down Expand Up @@ -1070,4 +1083,3 @@ int _glfw_max(int a, int b);
void* _glfw_calloc(size_t count, size_t size);
void* _glfw_realloc(void* pointer, size_t size);
void _glfw_free(void* pointer);

2 changes: 1 addition & 1 deletion src/null_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform)
.getClipboardString = _glfwGetClipboardStringNull,
.updatePreeditCursorRectangle = _glfwUpdatePreeditCursorRectangleNull,
.resetPreeditText = _glfwResetPreeditTextNull,
.setTextInputFocus = _glfwSetTextInputFocusNull,
.setIMEStatus = _glfwSetIMEStatusNull,
.getIMEStatus = _glfwGetIMEStatusNull,
.initJoysticks = _glfwInitJoysticksNull,
Expand Down Expand Up @@ -266,4 +267,3 @@ void _glfwTerminateNull(void)
_glfwTerminateEGL();
memset(&_glfw.null, 0, sizeof(_glfw.null));
}

2 changes: 1 addition & 1 deletion src/null_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ int _glfwGetKeyScancodeNull(int key);

void _glfwUpdatePreeditCursorRectangleNull(_GLFWwindow* window);
void _glfwResetPreeditTextNull(_GLFWwindow* window);
void _glfwSetTextInputFocusNull(_GLFWwindow* window, GLFWbool focused);
void _glfwSetIMEStatusNull(_GLFWwindow* window, int active);
int _glfwGetIMEStatusNull(_GLFWwindow* window);

Expand All @@ -284,4 +285,3 @@ GLFWbool _glfwGetPhysicalDevicePresentationSupportNull(VkInstance instance, VkPh
VkResult _glfwCreateWindowSurfaceNull(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);

void _glfwPollMonitorsNull(void);

5 changes: 4 additions & 1 deletion src/null_window.c
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,10 @@ void _glfwResetPreeditTextNull(_GLFWwindow* window)
{
}

void _glfwSetTextInputFocusNull(_GLFWwindow* window, GLFWbool focused)
{
}

void _glfwSetIMEStatusNull(_GLFWwindow* window, int active)
{
}
Expand Down Expand Up @@ -763,4 +767,3 @@ VkResult _glfwCreateWindowSurfaceNull(VkInstance instance,

return err;
}

4 changes: 3 additions & 1 deletion src/win32_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ static GLFWbool loadLibraries(void)
_glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmGetCompositionStringW");
_glfw.win32.imm32.ImmGetContext_ = (PFN_ImmGetContext)
_glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmGetContext");
_glfw.win32.imm32.ImmAssociateContext_ = (PFN_ImmAssociateContext)
_glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmAssociateContext");
_glfw.win32.imm32.ImmGetConversionStatus_ = (PFN_ImmGetConversionStatus)
_glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmGetConversionStatus");
_glfw.win32.imm32.ImmGetDescriptionW_ = (PFN_ImmGetDescriptionW)
Expand Down Expand Up @@ -621,6 +623,7 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform)
.getClipboardString = _glfwGetClipboardStringWin32,
.updatePreeditCursorRectangle = _glfwUpdatePreeditCursorRectangleWin32,
.resetPreeditText = _glfwResetPreeditTextWin32,
.setTextInputFocus = _glfwSetTextInputFocusWin32,
.setIMEStatus = _glfwSetIMEStatusWin32,
.getIMEStatus = _glfwGetIMEStatusWin32,
.initJoysticks = _glfwInitJoysticksWin32,
Expand Down Expand Up @@ -741,4 +744,3 @@ void _glfwTerminateWin32(void)
}

#endif // _GLFW_WIN32

9 changes: 8 additions & 1 deletion src/win32_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLO
typedef DWORD (WINAPI * PFN_ImmGetCandidateListW)(HIMC,DWORD,LPCANDIDATELIST,DWORD);
typedef LONG (WINAPI * PFN_ImmGetCompositionStringW)(HIMC,DWORD,LPVOID,DWORD);
typedef HIMC (WINAPI * PFN_ImmGetContext)(HWND);
typedef HIMC (WINAPI * PFN_ImmAssociateContext)(HWND,HIMC);
typedef BOOL (WINAPI * PFN_ImmGetConversionStatus)(HIMC,LPDWORD,LPDWORD);
typedef UINT (WINAPI * PFN_ImmGetDescriptionW)(HKL,LPWSTR,UINT);
typedef BOOL (WINAPI * PFN_ImmGetOpenStatus)(HIMC);
Expand All @@ -274,6 +275,7 @@ typedef BOOL (WINAPI * PFN_ImmSetOpenStatus)(HIMC,BOOL);
#define ImmGetCandidateListW _glfw.win32.imm32.ImmGetCandidateListW_
#define ImmGetCompositionStringW _glfw.win32.imm32.ImmGetCompositionStringW_
#define ImmGetContext _glfw.win32.imm32.ImmGetContext_
#define ImmAssociateContext _glfw.win32.imm32.ImmAssociateContext_
#define ImmGetConversionStatus _glfw.win32.imm32.ImmGetConversionStatus_
#define ImmGetDescriptionW _glfw.win32.imm32.ImmGetDescriptionW_
#define ImmGetOpenStatus _glfw.win32.imm32.ImmGetOpenStatus_
Expand Down Expand Up @@ -380,6 +382,10 @@ typedef struct _GLFWlibraryWGL
typedef struct _GLFWwindowWin32
{
HWND handle;
// Saved HIMC while explicit text input focus is out. A NULL HIMC is
// associated with the HWND during that period to keep IMM from routing key
// input through composition and candidate UI.
HIMC textInputContext;
HICON bigIcon;
HICON smallIcon;

Expand Down Expand Up @@ -473,6 +479,7 @@ typedef struct _GLFWlibraryWin32
PFN_ImmGetCandidateListW ImmGetCandidateListW_;
PFN_ImmGetCompositionStringW ImmGetCompositionStringW_;
PFN_ImmGetContext ImmGetContext_;
PFN_ImmAssociateContext ImmAssociateContext_;
PFN_ImmGetConversionStatus ImmGetConversionStatus_;
PFN_ImmGetDescriptionW ImmGetDescriptionW_;
PFN_ImmGetOpenStatus ImmGetOpenStatus_;
Expand Down Expand Up @@ -578,6 +585,7 @@ const char* _glfwGetClipboardStringWin32(void);

void _glfwUpdatePreeditCursorRectangleWin32(_GLFWwindow* window);
void _glfwResetPreeditTextWin32(_GLFWwindow* window);
void _glfwSetTextInputFocusWin32(_GLFWwindow* window, GLFWbool focused);
void _glfwSetIMEStatusWin32(_GLFWwindow* window, int active);
int _glfwGetIMEStatusWin32(_GLFWwindow* window);

Expand Down Expand Up @@ -609,4 +617,3 @@ void _glfwTerminateWGL(void);
GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
const _GLFWctxconfig* ctxconfig,
const _GLFWfbconfig* fbconfig);

Loading
Loading