mirror of
https://github.com/glfw/glfw.git
synced 2024-11-25 22:14:34 +00:00
Win32: Support preedit candidate feature
You can use this feature when you need to manage the drawing of the preedit candidates on the application side.
This commit is contained in:
parent
79fe47e970
commit
4a2883b4e2
@ -140,10 +140,17 @@ information on what to include when reporting a bug.
|
||||
area (#2130)
|
||||
- Added `glfwResetPreeditText` function to reset preedit of input method
|
||||
(#2130)
|
||||
- Added `glfwSetPreeditCandidateCallback` function and
|
||||
`GLFWpreeditcandidatefun` type for preedit candidates (#2130)
|
||||
- Added `glfwGetPreeditCandidate` function to get a preeidt candidate text
|
||||
(#2130)
|
||||
- Added `GLFW_IME` input mode for `glfwGetInputMode` and `glfwSetInputMode`
|
||||
(#2130)
|
||||
- Added `GLFW_X11_ONTHESPOT` init hint for using on-the-spot input method
|
||||
style on X11 (#2130)
|
||||
- Added `GLFW_MANAGE_PREEDIT_CANDIDATE` init hint for displaying preedit
|
||||
candidates on the application side (supported only on Windows currently)
|
||||
(#2130)
|
||||
|
||||
|
||||
## Contact
|
||||
|
@ -414,6 +414,80 @@ glfwResetPreeditText(window);
|
||||
@endcode
|
||||
|
||||
|
||||
@subsection manage_preedit_candidate Manage preedit candidate
|
||||
|
||||
By default, the IME manages the drawing of the preedit candidates, but
|
||||
sometimes you need to do that on the application side for some reason. In such
|
||||
a case, you can use
|
||||
[GLFW_MANAGE_PREEDIT_CANDIDATE](@ref GLFW_MANAGE_PREEDIT_CANDIDATE_hint) init hint.
|
||||
By setting this to `GLFW_TRUE`, the IME stops managing the drawing of the
|
||||
candidates and the application needs to manage it by using the following
|
||||
functions.
|
||||
|
||||
@note
|
||||
@win32 Only the OS currently supports this hint.
|
||||
|
||||
You can register the candidate callback as follows.
|
||||
|
||||
@code
|
||||
glfwSetPreeditCandidateCallback(window, candidate_callback);
|
||||
@endcode
|
||||
|
||||
The callback receives the following information.
|
||||
|
||||
@code
|
||||
void candidate_callback(GLFWwindow* window,
|
||||
int candidates_count,
|
||||
int selected_index,
|
||||
int page_start,
|
||||
int page_size)
|
||||
{
|
||||
}
|
||||
@endcode
|
||||
|
||||
`candidates_count` is the number of total candidates. `selected_index` is the
|
||||
index of the currently selected candidate. Normally all candidates should not
|
||||
be displayed at once, but divided into pages. You can use `page_start` and
|
||||
`page_size` to manage the pages. `page_start` is the index of the first
|
||||
candidate on the current page. `page_size` is the number of the candidates on
|
||||
the current page.
|
||||
|
||||
You can get the text of the candidate on the specific index as follows. Each
|
||||
character of the returned text is a native endian UTF-32.
|
||||
|
||||
@code
|
||||
int text_count;
|
||||
unsigned int* text = glfwGetPreeditCandidate(window, index, &text_count);
|
||||
@endcode
|
||||
|
||||
A sample code to get all candidate texts on the current page is as follows.
|
||||
|
||||
@code
|
||||
void candidate_callback(GLFWwindow* window, int candidates_count,
|
||||
int selected_index, int page_start, int page_size)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < page_size; ++i)
|
||||
{
|
||||
int index = i + page_start;
|
||||
int text_count;
|
||||
unsigned int* text = glfwGetPreeditCandidate(window, index, &text_count);
|
||||
if (index == selected_index)
|
||||
printf("> ");
|
||||
for (j = 0; j < text_count; ++j)
|
||||
{
|
||||
char encoded[5] = "";
|
||||
encode_utf8(encoded, text[j]); // Some kind of encoding process
|
||||
printf("%s", encoded);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
glfwSetPreeditCandidateCallback(window, candidate_callback);
|
||||
@endcode
|
||||
|
||||
|
||||
## Mouse input {#input_mouse}
|
||||
|
||||
Mouse input comes in many forms, including mouse motion, button presses and
|
||||
|
@ -118,6 +118,16 @@ The ANGLE platform type is specified via the `EGL_ANGLE_platform_angle`
|
||||
extension. This extension is not used if this hint is
|
||||
`GLFW_ANGLE_PLATFORM_TYPE_NONE`, which is the default value.
|
||||
|
||||
@anchor GLFW_MANAGE_PREEDIT_CANDIDATE_hint
|
||||
__GLFW_MANAGE_PREEDIT_CANDIDATE__ specifies whether to manage the preedit
|
||||
candidates on the application side. Possible values are `GLFW_TRUE` and
|
||||
`GLFW_FALSE`. The default is `GLFW_FALSE` and there is no need to manage
|
||||
the candidates on the application side. When you need to do that on the
|
||||
application side for some reason, you can enable this hint. Please see
|
||||
@ref ime_support for more information about IME support.
|
||||
|
||||
@win32 Only the OS currently supports this hint.
|
||||
|
||||
|
||||
#### macOS specific init hints {#init_hints_osx}
|
||||
|
||||
@ -164,16 +174,17 @@ it is recommended not to use this hint in normal cases. Possible values are
|
||||
|
||||
#### Supported and default values {#init_hints_values}
|
||||
|
||||
Initialization hint | Default value | Supported values
|
||||
-------------------------------- | ------------------------------- | ----------------
|
||||
@ref GLFW_PLATFORM | `GLFW_ANY_PLATFORM` | `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`, `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` or `GLFW_PLATFORM_NULL`
|
||||
@ref GLFW_JOYSTICK_HAT_BUTTONS | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
|
||||
@ref GLFW_ANGLE_PLATFORM_TYPE | `GLFW_ANGLE_PLATFORM_TYPE_NONE` | `GLFW_ANGLE_PLATFORM_TYPE_NONE`, `GLFW_ANGLE_PLATFORM_TYPE_OPENGL`, `GLFW_ANGLE_PLATFORM_TYPE_OPENGLES`, `GLFW_ANGLE_PLATFORM_TYPE_D3D9`, `GLFW_ANGLE_PLATFORM_TYPE_D3D11`, `GLFW_ANGLE_PLATFORM_TYPE_VULKAN` or `GLFW_ANGLE_PLATFORM_TYPE_METAL`
|
||||
@ref GLFW_COCOA_CHDIR_RESOURCES | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
|
||||
@ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
|
||||
@ref GLFW_WAYLAND_LIBDECOR | `GLFW_WAYLAND_PREFER_LIBDECOR` | `GLFW_WAYLAND_PREFER_LIBDECOR` or `GLFW_WAYLAND_DISABLE_LIBDECOR`
|
||||
@ref GLFW_X11_XCB_VULKAN_SURFACE | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
|
||||
@ref GLFW_X11_ONTHESPOT | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE`
|
||||
Initialization hint | Default value | Supported values
|
||||
---------------------------------- | ------------------------------- | ----------------
|
||||
@ref GLFW_PLATFORM | `GLFW_ANY_PLATFORM` | `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`, `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` or `GLFW_PLATFORM_NULL`
|
||||
@ref GLFW_JOYSTICK_HAT_BUTTONS | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
|
||||
@ref GLFW_ANGLE_PLATFORM_TYPE | `GLFW_ANGLE_PLATFORM_TYPE_NONE` | `GLFW_ANGLE_PLATFORM_TYPE_NONE`, `GLFW_ANGLE_PLATFORM_TYPE_OPENGL`, `GLFW_ANGLE_PLATFORM_TYPE_OPENGLES`, `GLFW_ANGLE_PLATFORM_TYPE_D3D9`, `GLFW_ANGLE_PLATFORM_TYPE_D3D11`, `GLFW_ANGLE_PLATFORM_TYPE_VULKAN` or `GLFW_ANGLE_PLATFORM_TYPE_METAL`
|
||||
@ref GLFW_MANAGE_PREEDIT_CANDIDATE | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE`
|
||||
@ref GLFW_COCOA_CHDIR_RESOURCES | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
|
||||
@ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
|
||||
@ref GLFW_WAYLAND_LIBDECOR | `GLFW_WAYLAND_PREFER_LIBDECOR` | `GLFW_WAYLAND_PREFER_LIBDECOR` or `GLFW_WAYLAND_DISABLE_LIBDECOR`
|
||||
@ref GLFW_X11_XCB_VULKAN_SURFACE | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
|
||||
@ref GLFW_X11_ONTHESPOT | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE`
|
||||
|
||||
|
||||
### Runtime platform selection {#platform}
|
||||
|
@ -1309,6 +1309,11 @@ extern "C" {
|
||||
* Platform selection [init hint](@ref GLFW_PLATFORM).
|
||||
*/
|
||||
#define GLFW_PLATFORM 0x00050003
|
||||
/*! @brief Preedit candidate init hint.
|
||||
*
|
||||
* Preedit candidate [init hint](@ref GLFW_MANAGE_PREEDIT_CANDIDATE_hint).
|
||||
*/
|
||||
#define GLFW_MANAGE_PREEDIT_CANDIDATE 0x00050004
|
||||
/*! @brief macOS specific init hint.
|
||||
*
|
||||
* macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint).
|
||||
@ -1989,6 +1994,29 @@ typedef void (* GLFWpreeditfun)(GLFWwindow* window,
|
||||
*/
|
||||
typedef void (* GLFWimestatusfun)(GLFWwindow* window);
|
||||
|
||||
/*! @brief The function pointer type for preedit candidate callbacks.
|
||||
*
|
||||
* This is the function pointer type for preedit candidate callback functions.
|
||||
* Use @ref glfwGetPreeditCandidate to get the candidate text for a specific index.
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] candidates_count Candidates count.
|
||||
* @param[in] selected_index.Index of selected candidate.
|
||||
* @param[in] page_start Start index of candidate currently displayed.
|
||||
* @param[in] page_size Count of candidates currently displayed.
|
||||
*
|
||||
* @sa @ref ime_support
|
||||
* @sa @ref glfwSetPreeditCandidateCallback
|
||||
* @sa @ref glfwGetPreeditCandidate
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef void (* GLFWpreeditcandidatefun)(GLFWwindow* window,
|
||||
int candidates_count,
|
||||
int selected_index,
|
||||
int page_start,
|
||||
int page_size);
|
||||
|
||||
/*! @brief The function pointer type for path drop callbacks.
|
||||
*
|
||||
* This is the function pointer type for path drop callbacks. A path drop
|
||||
@ -5270,6 +5298,34 @@ GLFWAPI void glfwSetPreeditCursorRectangle(GLFWwindow* window, int x, int y, int
|
||||
*/
|
||||
GLFWAPI void glfwResetPreeditText(GLFWwindow* window);
|
||||
|
||||
/*! @brief Returns the preedit candidate.
|
||||
*
|
||||
* This function returns the text and the text-count of the preedit candidate.
|
||||
*
|
||||
* By default, the IME manages the preedit candidates, so there is no need to
|
||||
* use this function. See @ref glfwSetPreeditCandidateCallback and
|
||||
* [GLFW_MANAGE_PREEDIT_CANDIDATE](@ref GLFW_MANAGE_PREEDIT_CANDIDATE_hint) for details.
|
||||
*
|
||||
* @param[in] window The window.
|
||||
* @param[in] index The index of the candidate.
|
||||
* @param[out] textCount The text-count of the candidate.
|
||||
* @return The text of the candidate as Unicode code points.
|
||||
*
|
||||
* @remark @macos @x11 @wayland Don't support this function.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* This function may only be called from the main thread.
|
||||
*
|
||||
* @sa @ref ime_support
|
||||
* @sa @ref glfwSetPreeditCandidateCallback
|
||||
* @sa [GLFW_MANAGE_PREEDIT_CANDIDATE](@ref GLFW_MANAGE_PREEDIT_CANDIDATE_hint)
|
||||
*
|
||||
* @since Added in GLFW 3.X.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI unsigned int* glfwGetPreeditCandidate(GLFWwindow* window, int index, int* textCount);
|
||||
|
||||
/*! @brief Sets the key callback.
|
||||
*
|
||||
* This function sets the key callback of the specified window, which is called
|
||||
@ -5478,6 +5534,51 @@ GLFWAPI GLFWpreeditfun glfwSetPreeditCallback(GLFWwindow* window, GLFWpreeditfun
|
||||
*/
|
||||
GLFWAPI GLFWimestatusfun glfwSetIMEStatusCallback(GLFWwindow* window, GLFWimestatusfun cbfun);
|
||||
|
||||
/*! @brief Sets the preedit candidate change callback.
|
||||
*
|
||||
* This function sets the preedit candidate callback of the specified
|
||||
* window, which is called when the candidates are updated and can be used
|
||||
* to display them by the application side.
|
||||
*
|
||||
* By default, this callback is not called because the IME displays the
|
||||
* candidates and there is nothing to do on the application side. Only when
|
||||
* the application side needs to use this to manage the displaying of
|
||||
* IME candidates, you can set
|
||||
* [GLFW_MANAGE_PREEDIT_CANDIDATE](@ref GLFW_MANAGE_PREEDIT_CANDIDATE_hint) init hint
|
||||
* and stop the IME from managing it.
|
||||
*
|
||||
* @param[in] window The window whose callback to set.
|
||||
* @param[in] cbfun The new callback, or `NULL` to remove the currently set
|
||||
* callback.
|
||||
* @return The previously set callback, or `NULL` if no callback was set or an
|
||||
* error occurred.
|
||||
*
|
||||
* @callback_signature
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window,
|
||||
int candidates_count,
|
||||
int selected_index,
|
||||
int page_start,
|
||||
int page_size)
|
||||
* @endcode
|
||||
* For more information about the callback parameters, see the
|
||||
* [function pointer type](@ref GLFWpreeditcandidatefun).
|
||||
*
|
||||
* @remark @macos @x11 @wayland Don't support this function. The callback is
|
||||
* not called.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* This function may only be called from the main thread.
|
||||
*
|
||||
* @sa @ref ime_support
|
||||
* @sa [GLFW_MANAGE_PREEDIT_CANDIDATE](@ref GLFW_MANAGE_PREEDIT_CANDIDATE_hint)
|
||||
*
|
||||
* @since Added in GLFW 3.X
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI GLFWpreeditcandidatefun glfwSetPreeditCandidateCallback(GLFWwindow* window, GLFWpreeditcandidatefun cbfun);
|
||||
|
||||
/*! @brief Sets the mouse button callback.
|
||||
*
|
||||
* This function sets the mouse button callback of the specified window, which
|
||||
|
@ -52,6 +52,7 @@ static _GLFWinitconfig _glfwInitHints =
|
||||
.hatButtons = GLFW_TRUE,
|
||||
.angleType = GLFW_ANGLE_PLATFORM_TYPE_NONE,
|
||||
.platformID = GLFW_ANY_PLATFORM,
|
||||
.managePreeditCandidate = GLFW_FALSE,
|
||||
.vulkanLoader = NULL,
|
||||
.ns =
|
||||
{
|
||||
@ -474,6 +475,9 @@ GLFWAPI void glfwInitHint(int hint, int value)
|
||||
case GLFW_PLATFORM:
|
||||
_glfwInitHints.platformID = value;
|
||||
return;
|
||||
case GLFW_MANAGE_PREEDIT_CANDIDATE:
|
||||
_glfwInitHints.managePreeditCandidate = value;
|
||||
return;
|
||||
case GLFW_COCOA_CHDIR_RESOURCES:
|
||||
_glfwInitHints.ns.chdir = value;
|
||||
return;
|
||||
|
39
src/input.c
39
src/input.c
@ -355,6 +355,21 @@ void _glfwInputIMEStatus(_GLFWwindow* window)
|
||||
}
|
||||
}
|
||||
|
||||
// Notifies shared code of a preedit candidate event
|
||||
//
|
||||
void _glfwInputPreeditCandidate(_GLFWwindow* window)
|
||||
{
|
||||
if (window->callbacks.preeditCandidate)
|
||||
{
|
||||
_GLFWpreedit* preedit = &window->preedit;
|
||||
window->callbacks.preeditCandidate((GLFWwindow*) window,
|
||||
preedit->candidateCount,
|
||||
preedit->candidateSelection,
|
||||
preedit->candidatePageStart,
|
||||
preedit->candidatePageSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Notifies shared code of a scroll event
|
||||
//
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset)
|
||||
@ -1029,6 +1044,21 @@ GLFWAPI void glfwResetPreeditText(GLFWwindow* handle)
|
||||
_glfw.platform.resetPreeditText(window);
|
||||
}
|
||||
|
||||
GLFWAPI unsigned int* glfwGetPreeditCandidate(GLFWwindow* handle, int index, int* textCount)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_GLFWpreedit* preedit = &window->preedit;
|
||||
|
||||
if (preedit->candidateCount <= index)
|
||||
return NULL;
|
||||
|
||||
if (textCount)
|
||||
*textCount = preedit->candidates[index].textCount;
|
||||
|
||||
|
||||
return preedit->candidates[index].text;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
@ -1078,6 +1108,15 @@ GLFWAPI GLFWimestatusfun glfwSetIMEStatusCallback(GLFWwindow* handle, GLFWimesta
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWpreeditcandidatefun glfwSetPreeditCandidateCallback(GLFWwindow* handle,
|
||||
GLFWpreeditcandidatefun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP(GLFWpreeditcandidatefun, window->callbacks.preeditCandidate, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle,
|
||||
GLFWmousebuttonfun cbfun)
|
||||
{
|
||||
|
@ -63,23 +63,24 @@
|
||||
typedef int GLFWbool;
|
||||
typedef void (*GLFWproc)(void);
|
||||
|
||||
typedef struct _GLFWerror _GLFWerror;
|
||||
typedef struct _GLFWinitconfig _GLFWinitconfig;
|
||||
typedef struct _GLFWwndconfig _GLFWwndconfig;
|
||||
typedef struct _GLFWctxconfig _GLFWctxconfig;
|
||||
typedef struct _GLFWfbconfig _GLFWfbconfig;
|
||||
typedef struct _GLFWcontext _GLFWcontext;
|
||||
typedef struct _GLFWpreedit _GLFWpreedit;
|
||||
typedef struct _GLFWwindow _GLFWwindow;
|
||||
typedef struct _GLFWplatform _GLFWplatform;
|
||||
typedef struct _GLFWlibrary _GLFWlibrary;
|
||||
typedef struct _GLFWmonitor _GLFWmonitor;
|
||||
typedef struct _GLFWcursor _GLFWcursor;
|
||||
typedef struct _GLFWmapelement _GLFWmapelement;
|
||||
typedef struct _GLFWmapping _GLFWmapping;
|
||||
typedef struct _GLFWjoystick _GLFWjoystick;
|
||||
typedef struct _GLFWtls _GLFWtls;
|
||||
typedef struct _GLFWmutex _GLFWmutex;
|
||||
typedef struct _GLFWerror _GLFWerror;
|
||||
typedef struct _GLFWinitconfig _GLFWinitconfig;
|
||||
typedef struct _GLFWwndconfig _GLFWwndconfig;
|
||||
typedef struct _GLFWctxconfig _GLFWctxconfig;
|
||||
typedef struct _GLFWfbconfig _GLFWfbconfig;
|
||||
typedef struct _GLFWcontext _GLFWcontext;
|
||||
typedef struct _GLFWpreedit _GLFWpreedit;
|
||||
typedef struct _GLFWpreeditcandidate _GLFWpreeditcandidate;
|
||||
typedef struct _GLFWwindow _GLFWwindow;
|
||||
typedef struct _GLFWplatform _GLFWplatform;
|
||||
typedef struct _GLFWlibrary _GLFWlibrary;
|
||||
typedef struct _GLFWmonitor _GLFWmonitor;
|
||||
typedef struct _GLFWcursor _GLFWcursor;
|
||||
typedef struct _GLFWmapelement _GLFWmapelement;
|
||||
typedef struct _GLFWmapping _GLFWmapping;
|
||||
typedef struct _GLFWjoystick _GLFWjoystick;
|
||||
typedef struct _GLFWtls _GLFWtls;
|
||||
typedef struct _GLFWmutex _GLFWmutex;
|
||||
|
||||
#define GL_VERSION 0x1f02
|
||||
#define GL_NONE 0
|
||||
@ -378,6 +379,7 @@ struct _GLFWinitconfig
|
||||
GLFWbool hatButtons;
|
||||
int angleType;
|
||||
int platformID;
|
||||
GLFWbool managePreeditCandidate;
|
||||
PFN_vkGetInstanceProcAddr vulkanLoader;
|
||||
struct {
|
||||
GLFWbool menubar;
|
||||
@ -530,16 +532,34 @@ struct _GLFWcontext
|
||||
// Preedit structure for Input Method Editor/Engine
|
||||
//
|
||||
struct _GLFWpreedit
|
||||
{
|
||||
unsigned int* text;
|
||||
int textCount;
|
||||
int textBufferCount;
|
||||
int* blockSizes;
|
||||
int blockSizesCount;
|
||||
int blockSizesBufferCount;
|
||||
int focusedBlockIndex;
|
||||
int caretIndex;
|
||||
int cursorPosX, cursorPosY, cursorWidth, cursorHeight;
|
||||
|
||||
// Used only when apps display candidates by themselves.
|
||||
// Usually, OS displays them, so apps don't need to do it.
|
||||
_GLFWpreeditcandidate* candidates;
|
||||
int candidateCount;
|
||||
int candidateBufferCount;
|
||||
int candidateSelection;
|
||||
int candidatePageStart;
|
||||
int candidatePageSize;
|
||||
};
|
||||
|
||||
// Preedit candidate structure
|
||||
//
|
||||
struct _GLFWpreeditcandidate
|
||||
{
|
||||
unsigned int* text;
|
||||
int textCount;
|
||||
int textBufferCount;
|
||||
int* blockSizes;
|
||||
int blockSizesCount;
|
||||
int blockSizesBufferCount;
|
||||
int focusedBlockIndex;
|
||||
int caretIndex;
|
||||
int cursorPosX, cursorPosY, cursorWidth, cursorHeight;
|
||||
};
|
||||
|
||||
// Window and context structure
|
||||
@ -601,6 +621,7 @@ struct _GLFWwindow
|
||||
GLFWcharmodsfun charmods;
|
||||
GLFWpreeditfun preedit;
|
||||
GLFWimestatusfun imestatus;
|
||||
GLFWpreeditcandidatefun preeditCandidate;
|
||||
GLFWdropfun drop;
|
||||
} callbacks;
|
||||
|
||||
@ -961,6 +982,7 @@ void _glfwInputChar(_GLFWwindow* window,
|
||||
uint32_t codepoint, int mods, GLFWbool plain);
|
||||
void _glfwInputPreedit(_GLFWwindow* window);
|
||||
void _glfwInputIMEStatus(_GLFWwindow* window);
|
||||
void _glfwInputPreeditCandidate(_GLFWwindow* window);
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset);
|
||||
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
||||
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||
|
@ -170,6 +170,8 @@ static GLFWbool loadLibraries(void)
|
||||
_glfw.win32.imm32.instance = _glfwPlatformLoadModule("imm32.dll");
|
||||
if (_glfw.win32.imm32.instance)
|
||||
{
|
||||
_glfw.win32.imm32.ImmGetCandidateListW_ = (PFN_ImmGetCandidateListW)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmGetCandidateListW");
|
||||
_glfw.win32.imm32.ImmGetCompositionStringW_ = (PFN_ImmGetCompositionStringW)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.win32.imm32.instance, "ImmGetCompositionStringW");
|
||||
_glfw.win32.imm32.ImmGetContext_ = (PFN_ImmGetContext)
|
||||
|
@ -318,6 +318,7 @@ typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLO
|
||||
#define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_
|
||||
|
||||
// imm32 function pointer typedefs
|
||||
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 BOOL (WINAPI * PFN_ImmGetConversionStatus)(HIMC,LPDWORD,LPDWORD);
|
||||
@ -327,6 +328,7 @@ typedef BOOL (WINAPI * PFN_ImmNotifyIME)(HIMC,DWORD,DWORD,DWORD);
|
||||
typedef BOOL (WINAPI * PFN_ImmReleaseContext)(HWND,HIMC);
|
||||
typedef BOOL (WINAPI * PFN_ImmSetCandidateWindow)(HIMC,LPCANDIDATEFORM);
|
||||
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 ImmGetConversionStatus _glfw.win32.imm32.ImmGetConversionStatus_
|
||||
@ -526,6 +528,7 @@ typedef struct _GLFWlibraryWin32
|
||||
|
||||
struct {
|
||||
HINSTANCE instance;
|
||||
PFN_ImmGetCandidateListW ImmGetCandidateListW_;
|
||||
PFN_ImmGetCompositionStringW ImmGetCompositionStringW_;
|
||||
PFN_ImmGetContext ImmGetContext_;
|
||||
PFN_ImmGetConversionStatus ImmGetConversionStatus_;
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <assert.h>
|
||||
#include <windowsx.h>
|
||||
#include <shellapi.h>
|
||||
#include <wchar.h>
|
||||
|
||||
// Converts utf16 units to Unicode code points (UTF32).
|
||||
// Returns GLFW_TRUE when the converting completes and the result is assigned to
|
||||
@ -564,6 +565,117 @@ static void maximizeWindowManually(_GLFWwindow* window)
|
||||
SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
}
|
||||
|
||||
// Store candidate text from the buffer data
|
||||
//
|
||||
static void setCandidate(_GLFWpreeditcandidate* candidate, LPWSTR buffer)
|
||||
{
|
||||
size_t bufferCount = wcslen(buffer);
|
||||
int textBufferCount = candidate->textBufferCount;
|
||||
uint32_t codepoint;
|
||||
WCHAR highSurrogate = 0;
|
||||
int convertedLength = 0;
|
||||
int i;
|
||||
|
||||
while ((size_t) textBufferCount < bufferCount + 1)
|
||||
textBufferCount = (textBufferCount == 0) ? 1 : textBufferCount * 2;
|
||||
if (textBufferCount != candidate->textBufferCount)
|
||||
{
|
||||
unsigned int* text =
|
||||
_glfw_realloc(candidate->text,
|
||||
sizeof(unsigned int) * textBufferCount);
|
||||
if (text == NULL)
|
||||
return;
|
||||
candidate->text = text;
|
||||
candidate->textBufferCount = textBufferCount;
|
||||
}
|
||||
|
||||
for (i = 0; (size_t) i < bufferCount; ++i)
|
||||
{
|
||||
if (convertToUTF32FromUTF16(buffer[i],
|
||||
&highSurrogate,
|
||||
&codepoint))
|
||||
candidate->text[convertedLength++] = codepoint;
|
||||
}
|
||||
|
||||
candidate->textCount = convertedLength;
|
||||
}
|
||||
|
||||
// Get preedit candidates of Imm32 and pass them to candidate-callback
|
||||
//
|
||||
static void getImmCandidates(_GLFWwindow* window)
|
||||
{
|
||||
_GLFWpreedit* preedit = &window->preedit;
|
||||
HIMC hIMC = ImmGetContext(window->win32.handle);
|
||||
DWORD candidateListBytes = ImmGetCandidateListW(hIMC, 0, NULL, 0);
|
||||
|
||||
if (candidateListBytes == 0)
|
||||
{
|
||||
ImmReleaseContext(window->win32.handle, hIMC);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
int i;
|
||||
int bufferCount = preedit->candidateBufferCount;
|
||||
LPCANDIDATELIST candidateList = _glfw_calloc(candidateListBytes, 1);
|
||||
if (candidateList == NULL)
|
||||
{
|
||||
ImmReleaseContext(window->win32.handle, hIMC);
|
||||
return;
|
||||
}
|
||||
ImmGetCandidateListW(hIMC, 0, candidateList, candidateListBytes);
|
||||
ImmReleaseContext(window->win32.handle, hIMC);
|
||||
|
||||
while ((DWORD) bufferCount < candidateList->dwCount + 1)
|
||||
bufferCount = (bufferCount == 0) ? 1 : bufferCount * 2;
|
||||
if (bufferCount != preedit->candidateBufferCount)
|
||||
{
|
||||
_GLFWpreeditcandidate* candidates =
|
||||
_glfw_realloc(preedit->candidates,
|
||||
sizeof(_GLFWpreeditcandidate) * bufferCount);
|
||||
if (candidates == NULL)
|
||||
{
|
||||
_glfw_free(candidateList);
|
||||
return;
|
||||
}
|
||||
// `realloc` does not initialize the increased area with 0.
|
||||
// This logic should be moved to a more appropriate place to share
|
||||
// when other platforms support this feature.
|
||||
for (i = preedit->candidateBufferCount; i < bufferCount; ++i)
|
||||
{
|
||||
candidates[i].text = NULL;
|
||||
candidates[i].textCount = 0;
|
||||
candidates[i].textBufferCount = 0;
|
||||
}
|
||||
preedit->candidates = candidates;
|
||||
preedit->candidateBufferCount = bufferCount;
|
||||
}
|
||||
|
||||
for (i = 0; (DWORD) i < candidateList->dwCount; ++i)
|
||||
setCandidate(&preedit->candidates[i],
|
||||
(LPWSTR)((char*) candidateList + candidateList->dwOffset[i]));
|
||||
|
||||
preedit->candidateCount = candidateList->dwCount;
|
||||
preedit->candidateSelection = candidateList->dwSelection;
|
||||
preedit->candidatePageStart = candidateList->dwPageStart;
|
||||
preedit->candidatePageSize = candidateList->dwPageSize;
|
||||
|
||||
_glfw_free(candidateList);
|
||||
}
|
||||
|
||||
_glfwInputPreeditCandidate(window);
|
||||
}
|
||||
|
||||
// Clear preedit candidates
|
||||
static void clearImmCandidate(_GLFWwindow* window)
|
||||
{
|
||||
window->preedit.candidateCount = 0;
|
||||
window->preedit.candidateSelection = 0;
|
||||
window->preedit.candidatePageStart = 0;
|
||||
window->preedit.candidatePageSize = 0;
|
||||
_glfwInputPreeditCandidate(window);
|
||||
}
|
||||
|
||||
// Get preedit texts of Imm32 and pass them to preedit-callback
|
||||
//
|
||||
static GLFWbool getImmPreedit(_GLFWwindow* window)
|
||||
@ -798,6 +910,12 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
|
||||
// To draw preedit text by an application side
|
||||
if (lParam & ISC_SHOWUICOMPOSITIONWINDOW)
|
||||
lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
|
||||
|
||||
if (_glfw.hints.init.managePreeditCandidate &&
|
||||
(lParam & ISC_SHOWUICANDIDATEWINDOW))
|
||||
{
|
||||
lParam &= ~ISC_SHOWUICANDIDATEWINDOW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1044,15 +1162,34 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
|
||||
case WM_IME_ENDCOMPOSITION:
|
||||
{
|
||||
clearImmPreedit(window);
|
||||
// Usually clearing candidates in IMN_CLOSECANDIDATE is sufficient.
|
||||
// However, some IME need it here, e.g. Google Japanese Input.
|
||||
clearImmCandidate(window);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
case WM_IME_NOTIFY:
|
||||
{
|
||||
if (wParam == IMN_SETOPENSTATUS)
|
||||
switch (wParam)
|
||||
{
|
||||
_glfwInputIMEStatus(window);
|
||||
return TRUE;
|
||||
case IMN_SETOPENSTATUS:
|
||||
{
|
||||
_glfwInputIMEStatus(window);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
case IMN_OPENCANDIDATE:
|
||||
case IMN_CHANGECANDIDATE:
|
||||
{
|
||||
getImmCandidates(window);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
case IMN_CLOSECANDIDATE:
|
||||
{
|
||||
clearImmCandidate(window);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -119,11 +119,16 @@ static int currentIMEStatus = GLFW_FALSE;
|
||||
#define MAX_PREEDIT_LEN 128
|
||||
static char preeditBuf[MAX_PREEDIT_LEN] = "";
|
||||
|
||||
// Assuming that the page-size is 10 at most.
|
||||
static char candidateBuf[9][MAX_PREEDIT_LEN];
|
||||
static int candidatePageSize = 0;
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
printf("Usage: input_text [-h] [-s]\n");
|
||||
printf("Usage: input_text [-h] [-s] [-c]\n");
|
||||
printf("Options:\n");
|
||||
printf(" -s Use on-the-spot sytle on X11. This is ignored on other platforms.\n");
|
||||
printf(" -c Use manage-preedit-candidate on Win32. This is ignored on other platforms.\n");
|
||||
printf(" -h Show this help\n");
|
||||
}
|
||||
|
||||
@ -566,11 +571,32 @@ static void set_preedit_labels(GLFWwindow* window, struct nk_context* nk, int he
|
||||
nk_layout_row_end(nk);
|
||||
}
|
||||
|
||||
static void set_candidate_labels(GLFWwindow* window, struct nk_context* nk)
|
||||
{
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
nk_layout_row_begin(nk, NK_DYNAMIC, 30, 3);
|
||||
nk_layout_row_push(nk, 1.f / 3.f);
|
||||
if (i == 0)
|
||||
nk_label(nk, "Candidates:", NK_TEXT_LEFT);
|
||||
else
|
||||
nk_label(nk, "", NK_TEXT_LEFT);
|
||||
nk_layout_row_push(nk, 1.f / 3.f);
|
||||
if (candidatePageSize > i)
|
||||
nk_label(nk, (const char*) candidateBuf[i], NK_TEXT_LEFT);
|
||||
nk_layout_row_push(nk, 1.f / 3.f);
|
||||
if (candidatePageSize > i + 5)
|
||||
nk_label(nk, (const char*) candidateBuf[i + 5], NK_TEXT_LEFT);
|
||||
nk_layout_row_end(nk);
|
||||
}
|
||||
}
|
||||
|
||||
// If it is possible to take the text-cursor position calculated in `nk_do_edit` function in `deps/nuklear.h`,
|
||||
// we can set preedit-cursor position more easily.
|
||||
// However, there doesn't seem to be a way to do that, so this does a simplified calculation only for the end
|
||||
// of the text. (Can not trace the cursor movement)
|
||||
static void update_cursor_pos(GLFWwindow* window, struct nk_context* nk, struct nk_user_font* f, char* boxBuffer, int boxLen)
|
||||
static void update_cursor_pos(GLFWwindow* window, struct nk_context* nk, struct nk_user_font* f,
|
||||
char* boxBuffer, int boxLen, int boxX, int boxY)
|
||||
{
|
||||
float lineWidth = 0;
|
||||
int totalLines = 1;
|
||||
@ -606,14 +632,10 @@ static void update_cursor_pos(GLFWwindow* window, struct nk_context* nk, struct
|
||||
}
|
||||
|
||||
{
|
||||
// I don't know how to get these info.
|
||||
int widgetLayoutX = 10;
|
||||
int widgetLayoutY = 220;
|
||||
|
||||
int lineHeight = f->height + nk->style.edit.row_padding;
|
||||
|
||||
int cursorPosX = widgetLayoutX + lineWidth;
|
||||
int cursorPosY = widgetLayoutY + lineHeight * (totalLines - 1);
|
||||
int cursorPosX = boxX + lineWidth;
|
||||
int cursorPosY = boxY + lineHeight * (totalLines - 1);
|
||||
int cursorHeight = lineHeight;
|
||||
int cursorWidth;
|
||||
|
||||
@ -686,6 +708,30 @@ static void preedit_callback(GLFWwindow* window, int preeditCount,
|
||||
}
|
||||
}
|
||||
|
||||
static void candidate_callback(GLFWwindow* window, int candidates_count,
|
||||
int selected_index, int page_start, int page_size)
|
||||
{
|
||||
int i, j;
|
||||
candidatePageSize = page_size;
|
||||
for (i = 0; i < page_size; ++i)
|
||||
{
|
||||
int index = i + page_start;
|
||||
int textCount;
|
||||
unsigned int* text = glfwGetPreeditCandidate(window, index, &textCount);
|
||||
if (index == selected_index)
|
||||
strcpy(candidateBuf[i], "> ");
|
||||
else
|
||||
strcpy(candidateBuf[i], "");
|
||||
for (j = 0; j < textCount; ++j)
|
||||
{
|
||||
char encoded[5] = "";
|
||||
encode_utf8(encoded, text[j]);
|
||||
if (strlen(candidateBuf[i]) + strlen(encoded) < MAX_PREEDIT_LEN)
|
||||
strcat(candidateBuf[i], encoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
GLFWwindow* window;
|
||||
@ -694,9 +740,10 @@ int main(int argc, char** argv)
|
||||
char boxBuffer[MAX_BUFFER_LEN] = "Input text here.";
|
||||
int boxLen = strlen(boxBuffer);
|
||||
int isAutoUpdatingCursorPosEnabled = GLFW_TRUE;
|
||||
int managePreeditCandidate = GLFW_FALSE;
|
||||
int ch;
|
||||
|
||||
while ((ch = getopt(argc, argv, "hs")) != -1)
|
||||
while ((ch = getopt(argc, argv, "hsc")) != -1)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
@ -707,6 +754,11 @@ int main(int argc, char** argv)
|
||||
case 's':
|
||||
glfwInitHint(GLFW_X11_ONTHESPOT, GLFW_TRUE);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
glfwInitHint(GLFW_MANAGE_PREEDIT_CANDIDATE, GLFW_TRUE);
|
||||
managePreeditCandidate = GLFW_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -729,6 +781,7 @@ int main(int argc, char** argv)
|
||||
glfwSetPreeditCursorRectangle(window, 0, 0, 1, 1);
|
||||
glfwSetIMEStatusCallback(window, ime_callback);
|
||||
glfwSetPreeditCallback(window, preedit_callback);
|
||||
glfwSetPreeditCandidateCallback(window, candidate_callback);
|
||||
|
||||
glfwMakeContextCurrent(window);
|
||||
gladLoadGL(glfwGetProcAddress);
|
||||
@ -757,6 +810,8 @@ int main(int argc, char** argv)
|
||||
set_preedit_cursor_edit(window, nk, 30, &isAutoUpdatingCursorPosEnabled);
|
||||
set_ime_stauts_labels(window, nk, 30);
|
||||
set_preedit_labels(window, nk, 30);
|
||||
if (managePreeditCandidate)
|
||||
set_candidate_labels(window, nk);
|
||||
|
||||
nk_layout_row_dynamic(nk, height - 250, 1);
|
||||
nk_edit_string(nk, NK_EDIT_BOX, boxBuffer, &boxLen, MAX_BUFFER_LEN, nk_filter_default);
|
||||
@ -768,7 +823,9 @@ int main(int argc, char** argv)
|
||||
glfwSwapBuffers(window);
|
||||
|
||||
if (isAutoUpdatingCursorPosEnabled)
|
||||
update_cursor_pos(window, nk, ¤tFont->handle, boxBuffer, boxLen);
|
||||
// I don't know how to get the layout info of `nk_edit_string`.
|
||||
update_cursor_pos(window, nk, ¤tFont->handle, boxBuffer, boxLen, 10,
|
||||
managePreeditCandidate ? 385 : 220);
|
||||
|
||||
glfwWaitEvents();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user