Compare commits
78 Commits
main
...
input_meth
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1254e98a48 | ||
![]() |
151f2467a4 | ||
![]() |
6919ea1726 | ||
![]() |
b64b18f8ac | ||
![]() |
36fc98cb0b | ||
![]() |
36652982dd | ||
![]() |
a54d863a93 | ||
![]() |
cce89d04ee | ||
![]() |
d399927749 | ||
![]() |
80496abbf4 | ||
![]() |
b2a5b96ff4 | ||
![]() |
4fc691f619 | ||
![]() |
316bae9fe8 | ||
![]() |
3e5962656a | ||
![]() |
1087b9236f | ||
![]() |
a677da8638 | ||
![]() |
b5cf3cd415 | ||
![]() |
f65879fd51 | ||
![]() |
ff27b12904 | ||
![]() |
30654e186f | ||
![]() |
60a035c52a | ||
fde03ec578 | |||
0f159b7306 | |||
98beae0c11 | |||
![]() |
b68166355a | ||
2bf7215130 | |||
14502a3557 | |||
e0283f8b6c | |||
fffc35c34a | |||
8299602f1f | |||
3d6b912293 | |||
4a55637a0d | |||
4135458c3d | |||
c3eab0e2f4 | |||
9901eed588 | |||
48b3a09c41 | |||
ea671a6e87 | |||
a36778ab8e | |||
b4337e870e | |||
ed01da08fd | |||
e2855b81a4 | |||
6650436d4a | |||
e2e69f6011 | |||
6147ad3596 | |||
b0ea6aecb3 | |||
a7536dfac5 | |||
d472651713 | |||
808c6e383b | |||
70dee80a8b | |||
77447f0401 | |||
157e6ef22d | |||
78b91eb23f | |||
c6b2422db0 | |||
d6931a6ce6 | |||
d5e0613d6e | |||
6a6336ce77 | |||
e0b960a77e | |||
7bd15aa347 | |||
bbe68ddf7e | |||
339147666a | |||
716642379f | |||
b6192bfa3b | |||
15a212ee06 | |||
d813f70382 | |||
3e5de9dc3f | |||
656b868db9 | |||
aee3d19082 | |||
56d4aed17d | |||
c6acfdf2a0 | |||
![]() |
baccf32076 | ||
![]() |
e42f974ae6 | ||
![]() |
122ad8ac6d | ||
![]() |
f086c5a1ea | ||
![]() |
c4b7440bc0 | ||
![]() |
7d281401fd | ||
![]() |
23c7716bbb | ||
![]() |
98f0333110 | ||
![]() |
7907014ed7 |
@@ -318,6 +318,9 @@ mark_as_advanced(WITH_LIBMV_SCHUR_SPECIALIZATIONS)
|
||||
option(WITH_FREESTYLE "Enable Freestyle (advanced edges rendering)" ON)
|
||||
|
||||
# Misc
|
||||
if (WIN32)
|
||||
option(WITH_INPUT_IME "Enable Input Method Editor (IME)" ON)
|
||||
endif()
|
||||
option(WITH_INPUT_NDOF "Enable NDOF input devices (SpaceNavigator and friends)" ${_init_INPUT_NDOF})
|
||||
option(WITH_RAYOPTIMIZATION "Enable use of SIMD (SSE) optimizations for the raytracer" ON)
|
||||
option(WITH_OPENNL "Enable use of Open Numerical Library" ON)
|
||||
@@ -1149,7 +1152,7 @@ elseif(WIN32)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO")
|
||||
|
||||
list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 advapi32 shfolder shell32 ole32 oleaut32 uuid psapi)
|
||||
list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 advapi32 shfolder shell32 ole32 oleaut32 uuid psapi imm32)
|
||||
|
||||
add_definitions(
|
||||
-D_CRT_NONSTDC_NO_DEPRECATE
|
||||
@@ -1505,7 +1508,7 @@ elseif(WIN32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(APPEND PLATFORM_LINKLIBS -lshell32 -lshfolder -lgdi32 -lmsvcrt -lwinmm -lmingw32 -lm -lws2_32 -lz -lstdc++ -lole32 -luuid -lwsock32 -lpsapi)
|
||||
list(APPEND PLATFORM_LINKLIBS -lshell32 -lshfolder -lgdi32 -lmsvcrt -lwinmm -lmingw32 -lm -lws2_32 -lz -lstdc++ -lole32 -luuid -lwsock32 -lpsapi -limm32)
|
||||
set(PLATFORM_CFLAGS "-pipe -funsigned-char -fno-strict-aliasing")
|
||||
|
||||
if(WITH_MINGW64)
|
||||
|
@@ -196,7 +196,7 @@ C_WARN = ['-Wno-char-subscripts', '-Wdeclaration-after-statement', '-Wstrict-pro
|
||||
|
||||
CC_WARN = [ '-Wall' ]
|
||||
|
||||
LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi']
|
||||
LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi', '-limm32']
|
||||
|
||||
PLATFORM_LINKFLAGS = ['-Xlinker', '--stack=2097152']
|
||||
|
||||
|
@@ -235,7 +235,7 @@ C_WARN = []
|
||||
CC_WARN = []
|
||||
CXX_WARN = []
|
||||
|
||||
LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi']
|
||||
LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi', 'imm32']
|
||||
|
||||
PLATFORM_LINKFLAGS = ['/SUBSYSTEM:CONSOLE','/MACHINE:IX86','/STACK:2097152','/INCREMENTAL:NO', '/LARGEADDRESSAWARE', '/NODEFAULTLIB:msvcrt.lib', '/NODEFAULTLIB:msvcmrt.lib', '/NODEFAULTLIB:msvcurt.lib', '/NODEFAULTLIB:msvcrtd.lib']
|
||||
|
||||
|
@@ -190,7 +190,7 @@ C_WARN = ['-Wno-char-subscripts', '-Wdeclaration-after-statement', '-Wstrict-pro
|
||||
|
||||
CC_WARN = [ '-Wall' ]
|
||||
|
||||
LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi', '-lpthread']
|
||||
LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi', '-lpthread', '-limm32']
|
||||
|
||||
PLATFORM_LINKFLAGS = ['-Xlinker', '--stack=2097152']
|
||||
|
||||
|
@@ -242,7 +242,7 @@ C_WARN = []
|
||||
CC_WARN = []
|
||||
CXX_WARN = []
|
||||
|
||||
LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi']
|
||||
LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi', 'imm32']
|
||||
|
||||
PLATFORM_LINKFLAGS = ['/SUBSYSTEM:CONSOLE','/MACHINE:X64','/STACK:2097152','/OPT:NOREF','/INCREMENTAL:NO', '/NODEFAULTLIB:msvcrt.lib', '/NODEFAULTLIB:msvcmrt.lib', '/NODEFAULTLIB:msvcurt.lib', '/NODEFAULTLIB:msvcrtd.lib']
|
||||
|
||||
|
@@ -280,6 +280,16 @@ elseif(WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_INPUT_IME)
|
||||
add_definitions(-DWITH_INPUT_IME)
|
||||
|
||||
list(APPEND SRC
|
||||
intern/GHOST_ImeWin32.cpp
|
||||
|
||||
intern/GHOST_ImeWin32.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_INPUT_NDOF)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_NDOFManagerWin32.cpp
|
||||
|
@@ -897,6 +897,30 @@ extern int GHOST_UseNativePixels(void);
|
||||
*/
|
||||
extern float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle);
|
||||
|
||||
/**
|
||||
* Enable IME attached to the given window, i.e. allows user-input
|
||||
* events to be dispatched to the IME.
|
||||
* \param windowhandle Window handle of the caller
|
||||
* \param x Requested x-coordinate of the rectangle
|
||||
* \param y Requested y-coordinate of the rectangle
|
||||
* \param w Requested width of the rectangle
|
||||
* \param h Requested height of the rectangle
|
||||
* \param complete Whether or not to complete the ongoing composition
|
||||
* true: Start a new composition
|
||||
* false: Move the IME windows to the given position without finishing it.
|
||||
*/
|
||||
extern void GHOST_BeginIME(GHOST_WindowHandle windowhandle,
|
||||
GHOST_TInt32 x,
|
||||
GHOST_TInt32 y,
|
||||
GHOST_TInt32 w,
|
||||
GHOST_TInt32 h,
|
||||
int complete);
|
||||
/**
|
||||
* Disable the IME attached to the given window, i.e. prohibits any user-input
|
||||
* events from being dispatched to the IME.
|
||||
* \param windowhandle The window handle of the caller
|
||||
*/
|
||||
extern void GHOST_EndIME(GHOST_WindowHandle windowhandle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -331,6 +331,30 @@ public:
|
||||
|
||||
virtual float getNativePixelSize(void) = 0;
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/**
|
||||
* Enable IME attached to the given window, i.e. allows user-input
|
||||
* events to be dispatched to the IME.
|
||||
* \param x Requested x-coordinate of the rectangle
|
||||
* \param y Requested y-coordinate of the rectangle
|
||||
* \param w Requested width of the rectangle
|
||||
* \param h Requested height of the rectangle
|
||||
* \param complete Whether or not to complete the ongoing composition
|
||||
* true: Start a new composition
|
||||
* false: Move the IME windows to the given position without finishing it.
|
||||
*/
|
||||
virtual void beginIME(GHOST_TInt32 x,
|
||||
GHOST_TInt32 y,
|
||||
GHOST_TInt32 w,
|
||||
GHOST_TInt32 h,
|
||||
int completed) = 0;
|
||||
|
||||
/**
|
||||
* Disable the IME attached to the given window, i.e. prohibits any user-input
|
||||
* events from being dispatched to the IME.
|
||||
*/
|
||||
virtual void endIME() = 0;
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_IWindow")
|
||||
|
401
intern/ghost/GHOST_ImeWin32.h
Normal file
401
intern/ghost/GHOST_ImeWin32.h
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: some of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file ghost/intern/GHOST_ImeWin32.h
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#ifndef __GHOST_IME_H__
|
||||
#define __GHOST_IME_H__
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "GHOST_Event.h"
|
||||
#include "GHOST_Rect.h"
|
||||
#include <vector>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This header file defines a struct and a class used for encapsulating IMM32
|
||||
* APIs, controls IMEs attached to a window, and enables the 'on-the-spot'
|
||||
* input without deep knowledge about the APIs, i.e. knowledge about the
|
||||
* language-specific and IME-specific behaviors.
|
||||
* The following items enumerates the simplest steps for an (window)
|
||||
* application to control its IMEs with the struct and the class defined
|
||||
* this file.
|
||||
* 1. Add an instance of the GHOST_ImeWin32 class to its window class.
|
||||
* (The GHOST_ImeWin32 class needs a window handle.)
|
||||
* 2. Add messages handlers listed in the following subsections, follow the
|
||||
* instructions written in each subsection, and use the GHOST_ImeWin32 class.
|
||||
* 2.1. WM_IME_SETCONTEXT (0x0281)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::CreateImeWindow();
|
||||
* - GHOST_ImeWin32::CleanupComposition(), and;
|
||||
* - GHOST_ImeWin32::SetImeWindowStyle().
|
||||
* An application MUST prevent from calling ::DefWindowProc().
|
||||
* 2.2. WM_IME_STARTCOMPOSITION (0x010D)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::CreateImeWindow(), and;
|
||||
* - GHOST_ImeWin32::ResetComposition().
|
||||
* An application MUST prevent from calling ::DefWindowProc().
|
||||
* 2.3. WM_IME_COMPOSITION (0x010F)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::UpdateImeWindow();
|
||||
* - GHOST_ImeWin32::GetResult();
|
||||
* - GHOST_ImeWin32::GetComposition(), and;
|
||||
* - GHOST_ImeWin32::ResetComposition() (optional).
|
||||
* An application MUST prevent from calling ::DefWindowProc().
|
||||
* 2.4. WM_IME_ENDCOMPOSITION (0x010E)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::ResetComposition(), and;
|
||||
* - GHOST_ImeWin32::DestroyImeWindow().
|
||||
* An application CAN call ::DefWindowProc().
|
||||
* 2.5. WM_INPUTLANGCHANGE (0x0051)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::SetInputLanguage().
|
||||
* An application CAN call ::DefWindowProc().
|
||||
*/
|
||||
|
||||
/* This struct represents the status of an ongoing composition. */
|
||||
struct ImeComposition {
|
||||
/* Represents the cursor position in the IME composition. */
|
||||
int cursor_position;
|
||||
|
||||
/* Represents the position of the beginning of the selection */
|
||||
int target_start;
|
||||
|
||||
/* Represents the position of the end of the selection */
|
||||
int target_end;
|
||||
|
||||
/**
|
||||
* Represents the type of the string in the 'ime_string' parameter.
|
||||
* Its possible values and description are listed bwlow:
|
||||
* Value Description
|
||||
* 0 The parameter is not used.
|
||||
* GCS_RESULTSTR The parameter represents a result string.
|
||||
* GCS_COMPSTR The parameter represents a composition string.
|
||||
*/
|
||||
int string_type;
|
||||
|
||||
/* Represents the string retrieved from IME (Input Method Editor) */
|
||||
std::wstring ime_string;
|
||||
std::vector<char> utf8_buf;
|
||||
std::vector<unsigned char> format;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class controls the IMM (Input Method Manager) through IMM32 APIs and
|
||||
* enables it to retrieve the string being controled by the IMM. (I wrote
|
||||
* a note to describe the reason why I do not use 'IME' but 'IMM' below.)
|
||||
* NOTE(hbono):
|
||||
* Fortunately or unfortunately, TSF (Text Service Framework) and
|
||||
* CUAS (Cicero Unaware Application Support) allows IMM32 APIs for
|
||||
* retrieving not only the inputs from IMEs (Input Method Editors), used
|
||||
* only for inputting East-Asian language texts, but also the ones from
|
||||
* tablets (on Windows XP Tablet PC Edition and Windows Vista), voice
|
||||
* recognizers (e.g. ViaVoice and Microsoft Office), etc.
|
||||
* We can disable TSF and CUAS in Windows XP Tablet PC Edition. On the other
|
||||
* hand, we can NEVER disable either TSF or CUAS in Windows Vista, i.e.
|
||||
* THIS CLASS IS NOT ONLY USED ON THE INPUT CONTEXTS OF EAST-ASIAN
|
||||
* LANGUAGES BUT ALSO USED ON THE INPUT CONTEXTS OF ALL LANGUAGES.
|
||||
*/
|
||||
class GHOST_ImeWin32 {
|
||||
public:
|
||||
GHOST_ImeWin32();
|
||||
~GHOST_ImeWin32();
|
||||
|
||||
/* Retrieves whether or not there is an ongoing composition. */
|
||||
bool is_composing() const { return is_composing_; }
|
||||
|
||||
/**
|
||||
* Retrieves the input language from Windows and update it.
|
||||
* Return values
|
||||
* * true
|
||||
* The given input language has IMEs.
|
||||
* * false
|
||||
* The given input language does not have IMEs.
|
||||
*/
|
||||
bool SetInputLanguage();
|
||||
|
||||
/**
|
||||
* Create the IME windows, and allocate required resources for them.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void CreateImeWindow(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Update the style of the IME windows.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * message [in] (UINT)
|
||||
* * wparam [in] (WPARAM)
|
||||
* * lparam [in] (LPARAM)
|
||||
* Represent the windows message of the caller.
|
||||
* These parameters are used for verifying if this function is called
|
||||
* in a handler function for WM_IME_SETCONTEXT messages because this
|
||||
* function uses ::DefWindowProc() to update the style.
|
||||
* A caller just has to pass the input parameters for the handler
|
||||
* function without modifications.
|
||||
* * handled [out] (BOOL*)
|
||||
* Returns ::DefWindowProc() is really called in this function.
|
||||
* PLEASE DO NOT CALL ::DefWindowProc() IF THIS VALUE IS TRUE!
|
||||
* All the window styles set in this function are over-written when
|
||||
* calling ::DefWindowProc() after returning this function.
|
||||
*/
|
||||
void SetImeWindowStyle(HWND window_handle, UINT message,
|
||||
WPARAM wparam, LPARAM lparam, BOOL* handled);
|
||||
|
||||
/**
|
||||
* Destroy the IME windows and all the resources attached to them.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void DestroyImeWindow(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Update the position of the IME windows.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void UpdateImeWindow(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Clean up the all resources attached to the given GHOST_ImeWin32 object, and
|
||||
* reset its composition status.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void CleanupComposition(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Reset the composition status.
|
||||
* Cancel the ongoing composition if it exists.
|
||||
* NOTE(hbono): This method does not release the allocated resources.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void ResetComposition(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Retrieve a composition result of the ongoing composition if it exists.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * lparam [in] (LPARAM)
|
||||
* Specifies the updated members of the ongoing composition, and must be
|
||||
* the same parameter of a WM_IME_COMPOSITION message handler.
|
||||
* This parameter is used for checking if the ongoing composition has
|
||||
* its result string,
|
||||
* * composition [out] (ImeComposition)
|
||||
* Represents the struct contains the composition result.
|
||||
* Return values
|
||||
* * true
|
||||
* The ongoing composition has a composition result.
|
||||
* * false
|
||||
* The ongoing composition does not have composition results.
|
||||
* Remarks
|
||||
* This function is designed for being called from WM_IME_COMPOSITION
|
||||
* message handlers.
|
||||
*/
|
||||
bool GetResult(HWND window_handle, LPARAM lparam,
|
||||
ImeComposition* composition);
|
||||
|
||||
/**
|
||||
* Retrieve the current composition status of the ongoing composition.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * lparam [in] (LPARAM)
|
||||
* Specifies the updated members of the ongoing composition, and must be
|
||||
* the same parameter of a WM_IME_COMPOSITION message handler.
|
||||
* This parameter is used for checking if the ongoing composition has
|
||||
* its result string,
|
||||
* * composition [out] (ImeComposition)
|
||||
* Represents the struct contains the composition status.
|
||||
* Return values
|
||||
* * true
|
||||
* The status of the ongoing composition is updated.
|
||||
* * false
|
||||
* The status of the ongoing composition is not updated.
|
||||
* Remarks
|
||||
* This function is designed for being called from WM_IME_COMPOSITION
|
||||
* message handlers.
|
||||
*/
|
||||
bool GetComposition(HWND window_handle, LPARAM lparam,
|
||||
ImeComposition* composition);
|
||||
|
||||
/**
|
||||
* Enable the IME attached to the given window, i.e. allows user-input
|
||||
* events to be dispatched to the IME.
|
||||
* In Chrome, this function is used when:
|
||||
* * a renderer process moves its input focus to another edit control, or;
|
||||
* * a renrerer process moves the position of the focused edit control.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * caret_rect [in] (const gfx::Rect&)
|
||||
* Represent the rectangle of the input caret.
|
||||
* This rectangle is used for controlling the positions of IME windows.
|
||||
* * complete [in] (bool)
|
||||
* Represents whether or not to complete the ongoing composition.
|
||||
* + true
|
||||
* After finishing the ongoing composition and close its IME windows,
|
||||
* start another composition and display its IME windows to the given
|
||||
* position.
|
||||
* + false
|
||||
* Just move the IME windows of the ongoing composition to the given
|
||||
* position without finishing it.
|
||||
*/
|
||||
void EnableIME(HWND window_handle,
|
||||
const GHOST_Rect& caret_rect,
|
||||
bool complete);
|
||||
|
||||
/**
|
||||
* Disable the IME attached to the given window, i.e. prohibits any user-input
|
||||
* events from being dispatched to the IME.
|
||||
* In Chrome, this function is used when:
|
||||
* * a renreder process sets its input focus to a password input.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void DisableIME(HWND window_handle);
|
||||
|
||||
/* Updatg resultInfo and compInfo */
|
||||
void UpdateInfo(HWND window_handle);
|
||||
|
||||
/* disable ime when start up */
|
||||
void CheckFirst(HWND window_handle);
|
||||
|
||||
ImeComposition resultInfo, compInfo;
|
||||
GHOST_TEventImeData eventImeData;
|
||||
|
||||
protected:
|
||||
/* Determines whether or not the given attribute represents a target (a.k.a. a selection). */
|
||||
bool IsTargetAttribute(char attribute) const {
|
||||
return (attribute == ATTR_TARGET_CONVERTED ||
|
||||
attribute == ATTR_TARGET_NOTCONVERTED);
|
||||
}
|
||||
|
||||
/* Retrieve the target area. */
|
||||
void GetCaret(HIMC imm_context, LPARAM lparam,
|
||||
ImeComposition* composition);
|
||||
|
||||
/* Update the position of the IME windows. */
|
||||
void MoveImeWindow(HWND window_handle, HIMC imm_context);
|
||||
|
||||
/* Complete the ongoing composition if it exists. */
|
||||
void CompleteComposition(HWND window_handle, HIMC imm_context);
|
||||
|
||||
/* Retrieve a string from the IMM. */
|
||||
bool GetString(HIMC imm_context, WPARAM lparam, int type,
|
||||
ImeComposition* composition);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Represents whether or not there is an ongoing composition in a browser
|
||||
* process, i.e. whether or not a browser process is composing a text.
|
||||
*/
|
||||
bool is_composing_;
|
||||
|
||||
/**
|
||||
* This value represents whether or not the current input context has IMEs.
|
||||
* The following table shows the list of IME status:
|
||||
* Value Description
|
||||
* false The current input language does not have IMEs.
|
||||
* true The current input language has IMEs.
|
||||
*/
|
||||
bool ime_status_;
|
||||
|
||||
/**
|
||||
* The current input Language ID retrieved from Windows, which consists of:
|
||||
* * Primary Language ID (bit 0 to bit 9), which shows a natunal language
|
||||
* (English, Korean, Chinese, Japanese, etc.) and;
|
||||
* * Sub-Language ID (bit 10 to bit 15), which shows a geometrical region
|
||||
* the language is spoken (For English, United States, United Kingdom,
|
||||
* Australia, Canada, etc.)
|
||||
* The following list enumerates some examples for the Language ID:
|
||||
* * "en-US" (0x0409)
|
||||
* MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
|
||||
* * "ko-KR" (0x0412)
|
||||
* MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN);
|
||||
* * "zh-TW" (0x0404)
|
||||
* MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL);
|
||||
* * "zh-CN" (0x0804)
|
||||
* MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
|
||||
* * "ja-JP" (0x0411)
|
||||
* MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), etc.
|
||||
* (See <winnt.h> for other available values.)
|
||||
* This Language ID is used for processing language-specific operations in
|
||||
* IME functions.
|
||||
*/
|
||||
LANGID input_language_id_;
|
||||
|
||||
/**
|
||||
* Represents whether or not the current input context has created a system
|
||||
* caret to set the position of its IME candidate window.
|
||||
* * true: it creates a system caret.
|
||||
* * false: it does not create a system caret.
|
||||
*/
|
||||
bool system_caret_;
|
||||
|
||||
/* The rectangle of the input caret retrieved from a renderer process. */
|
||||
GHOST_Rect caret_rect_;
|
||||
|
||||
/* used for disable ime when start up */
|
||||
bool is_first, is_enable;
|
||||
};
|
||||
|
||||
#endif * __GHOST_IME_H__
|
@@ -190,6 +190,10 @@ typedef enum {
|
||||
|
||||
GHOST_kEventTimer,
|
||||
|
||||
GHOST_kEventImeCompositionStart,
|
||||
GHOST_kEventImeComposition,
|
||||
GHOST_kEventImeCompositionEnd,
|
||||
|
||||
GHOST_kNumEventTypes
|
||||
} GHOST_TEventType;
|
||||
|
||||
@@ -436,6 +440,22 @@ typedef struct {
|
||||
GHOST_TEventDataPtr data;
|
||||
} GHOST_TEventDragnDropData;
|
||||
|
||||
/** similar to wmImeData */
|
||||
typedef struct {
|
||||
/** size_t */
|
||||
GHOST_TUserDataPtr result_len, composite_len;
|
||||
/** char * utf8 encoding */
|
||||
GHOST_TUserDataPtr result, composite;
|
||||
/** Cursor position in the IME composition. */
|
||||
int cursor_position;
|
||||
/** Represents the position of the beginning of the selection */
|
||||
int target_start;
|
||||
/** Represents the position of the end of the selection */
|
||||
int target_end;
|
||||
/** custom temporal data */
|
||||
GHOST_TUserDataPtr tmp;
|
||||
} GHOST_TEventImeData;
|
||||
|
||||
typedef struct {
|
||||
int count;
|
||||
GHOST_TUns8 **strings;
|
||||
|
@@ -915,3 +915,23 @@ float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle)
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
|
||||
void GHOST_BeginIME(GHOST_WindowHandle windowhandle,
|
||||
GHOST_TInt32 x,
|
||||
GHOST_TInt32 y,
|
||||
GHOST_TInt32 w,
|
||||
GHOST_TInt32 h,
|
||||
int complete)
|
||||
{
|
||||
GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
|
||||
window->beginIME(x, y, w, h, complete);
|
||||
}
|
||||
|
||||
void GHOST_EndIME(GHOST_WindowHandle windowhandle)
|
||||
{
|
||||
GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
|
||||
window->endIME();
|
||||
}
|
||||
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
517
intern/ghost/intern/GHOST_ImeWin32.cpp
Normal file
517
intern/ghost/intern/GHOST_ImeWin32.cpp
Normal file
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: some of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
|
||||
/** \file ghost/intern/GHOST_ImeWin32.cpp
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
|
||||
#include "GHOST_C-api.h"
|
||||
#include "GHOST_ImeWin32.h"
|
||||
#include "GHOST_WindowWin32.h"
|
||||
#include "utfconv.h"
|
||||
|
||||
|
||||
GHOST_ImeWin32::GHOST_ImeWin32()
|
||||
: ime_status_(false),
|
||||
input_language_id_(LANG_USER_DEFAULT),
|
||||
is_composing_(false),
|
||||
system_caret_(false),
|
||||
caret_rect_(-1, -1, 0, 0),
|
||||
is_first(true),
|
||||
is_enable(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
GHOST_ImeWin32::~GHOST_ImeWin32()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool GHOST_ImeWin32::SetInputLanguage()
|
||||
{
|
||||
/**
|
||||
* Retrieve the current keyboard layout from Windows and determine whether
|
||||
* or not the current input context has IMEs.
|
||||
* Also save its input language for language-specific operations required
|
||||
* while composing a text.
|
||||
*/
|
||||
HKL keyboard_layout = ::GetKeyboardLayout(0);
|
||||
input_language_id_ = reinterpret_cast<LANGID>(keyboard_layout);
|
||||
ime_status_ = ::ImmIsIME(keyboard_layout);
|
||||
return ime_status_;
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::CreateImeWindow(HWND window_handle)
|
||||
{
|
||||
/**
|
||||
* When a user disables TSF (Text Service Framework) and CUAS (Cicero
|
||||
* Unaware Application Support), Chinese IMEs somehow ignore function calls
|
||||
* to ::ImmSetCandidateWindow(), i.e. they do not move their candidate
|
||||
* window to the position given as its parameters, and use the position
|
||||
* of the current system caret instead, i.e. it uses ::GetCaretPos() to
|
||||
* retrieve the position of their IME candidate window.
|
||||
* Therefore, we create a temporary system caret for Chinese IMEs and use
|
||||
* it during this input context.
|
||||
* Since some third-party Japanese IME also uses ::GetCaretPos() to determine
|
||||
* their window position, we also create a caret for Japanese IMEs.
|
||||
*/
|
||||
if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE ||
|
||||
PRIMARYLANGID(input_language_id_) == LANG_JAPANESE) {
|
||||
if (!system_caret_) {
|
||||
if (::CreateCaret(window_handle, NULL, 1, 1)) {
|
||||
system_caret_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Restore the positions of the IME windows. */
|
||||
UpdateImeWindow(window_handle);
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::SetImeWindowStyle(HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam, BOOL *handled)
|
||||
{
|
||||
/**
|
||||
* To prevent the IMM (Input Method Manager) from displaying the IME
|
||||
* composition window, Update the styles of the IME windows and EXPLICITLY
|
||||
* call ::DefWindowProc() here.
|
||||
* NOTE(hbono): We can NEVER let WTL call ::DefWindowProc() when we update
|
||||
* the styles of IME windows because the 'lparam' variable is a local one
|
||||
* and all its updates disappear in returning from this function, i.e. WTL
|
||||
* does not call ::DefWindowProc() with our updated 'lparam' value but call
|
||||
* the function with its original value and over-writes our window styles.
|
||||
*/
|
||||
*handled = TRUE;
|
||||
lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
|
||||
::DefWindowProc(window_handle, message, wparam, lparam);
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::DestroyImeWindow(HWND window_handle)
|
||||
{
|
||||
/* Destroy the system caret if we have created for this IME input context. */
|
||||
if (system_caret_) {
|
||||
::DestroyCaret();
|
||||
system_caret_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::MoveImeWindow(HWND window_handle, HIMC imm_context)
|
||||
{
|
||||
int x = caret_rect_.m_l;
|
||||
int y = caret_rect_.m_t;
|
||||
const int kCaretMargin = 1;
|
||||
/**
|
||||
* As written in a comment in GHOST_ImeWin32::CreateImeWindow(),
|
||||
* Chinese IMEs ignore function calls to ::ImmSetCandidateWindow()
|
||||
* when a user disables TSF (Text Service Framework) and CUAS (Cicero
|
||||
* Unaware Application Support).
|
||||
* On the other hand, when a user enables TSF and CUAS, Chinese IMEs
|
||||
* ignore the position of the current system caret and uses the
|
||||
* parameters given to ::ImmSetCandidateWindow() with its 'dwStyle'
|
||||
* parameter CFS_CANDIDATEPOS.
|
||||
* Therefore, we do not only call ::ImmSetCandidateWindow() but also
|
||||
* set the positions of the temporary system caret if it exists.
|
||||
*/
|
||||
CANDIDATEFORM candidate_position = { 0, CFS_CANDIDATEPOS, { x, y },
|
||||
{ 0, 0, 0, 0 } };
|
||||
::ImmSetCandidateWindow(imm_context, &candidate_position);
|
||||
if (system_caret_) {
|
||||
switch (PRIMARYLANGID(input_language_id_)) {
|
||||
case LANG_JAPANESE:
|
||||
::SetCaretPos(x, y + caret_rect_.getHeight());
|
||||
break;
|
||||
default:
|
||||
::SetCaretPos(x, y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (PRIMARYLANGID(input_language_id_) == LANG_KOREAN) {
|
||||
/**
|
||||
* Chinese IMEs and Japanese IMEs require the upper-left corner of
|
||||
* the caret to move the position of their candidate windows.
|
||||
* On the other hand, Korean IMEs require the lower-left corner of the
|
||||
* caret to move their candidate windows.
|
||||
*/
|
||||
y += kCaretMargin;
|
||||
}
|
||||
/**
|
||||
* Japanese IMEs and Korean IMEs also use the rectangle given to
|
||||
* ::ImmSetCandidateWindow() with its 'dwStyle' parameter CFS_EXCLUDE
|
||||
* to move their candidate windows when a user disables TSF and CUAS.
|
||||
* Therefore, we also set this parameter here.
|
||||
*/
|
||||
CANDIDATEFORM exclude_rectangle = { 0, CFS_EXCLUDE, { x, y },
|
||||
{ x, y, x + caret_rect_.getWidth(), y + caret_rect_.getHeight() } };
|
||||
::ImmSetCandidateWindow(imm_context, &exclude_rectangle);
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::UpdateImeWindow(HWND window_handle)
|
||||
{
|
||||
/* Just move the IME window attached to the given window. */
|
||||
if (caret_rect_.m_l >= 0 && caret_rect_.m_t >= 0) {
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
MoveImeWindow(window_handle, imm_context);
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::CleanupComposition(HWND window_handle)
|
||||
{
|
||||
/**
|
||||
* Notify the IMM attached to the given window to complete the ongoing
|
||||
* composition, (this case happens when the given window is de-activated
|
||||
* while composing a text and re-activated), and reset the omposition status.
|
||||
*/
|
||||
if (is_composing_) {
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
ResetComposition(window_handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::CheckFirst(HWND window_handle)
|
||||
{
|
||||
if (is_first) {
|
||||
this->EndIME(window_handle);
|
||||
is_first = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::ResetComposition(HWND window_handle)
|
||||
{
|
||||
/* Currently, just reset the composition status. */
|
||||
is_composing_ = false;
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::CompleteComposition(HWND window_handle, HIMC imm_context)
|
||||
{
|
||||
/**
|
||||
* We have to confirm there is an ongoing composition before completing it.
|
||||
* This is for preventing some IMEs from getting confused while completing an
|
||||
* ongoing composition even if they do not have any ongoing compositions.)
|
||||
*/
|
||||
if (is_composing_) {
|
||||
::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
|
||||
ResetComposition(window_handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::GetCaret(HIMC imm_context, LPARAM lparam, ImeComposition *composition)
|
||||
{
|
||||
/**
|
||||
* This operation is optional and language-dependent because the caret
|
||||
* style is depended on the language, e.g.:
|
||||
* * Korean IMEs: the caret is a blinking block,
|
||||
* (It contains only one hangul character);
|
||||
* * Chinese IMEs: the caret is a blinking line,
|
||||
* (i.e. they do not need to retrieve the target selection);
|
||||
* * Japanese IMEs: the caret is a selection (or underlined) block,
|
||||
* (which can contain one or more Japanese characters).
|
||||
*/
|
||||
int target_start = -1;
|
||||
int target_end = -1;
|
||||
switch (PRIMARYLANGID(input_language_id_)) {
|
||||
case LANG_KOREAN:
|
||||
if (lparam & CS_NOMOVECARET) {
|
||||
target_start = 0;
|
||||
target_end = 1;
|
||||
}
|
||||
break;
|
||||
case LANG_CHINESE:
|
||||
{
|
||||
int clause_size = ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE, NULL, 0);
|
||||
if (clause_size) {
|
||||
static std::vector<unsigned long> clauses;
|
||||
clause_size = clause_size / sizeof(clauses[0]);
|
||||
clauses.resize(clause_size);
|
||||
ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE, &clauses[0],
|
||||
sizeof(clauses[0]) *clause_size);
|
||||
if (composition->cursor_position == composition->ime_string.size()) {
|
||||
target_start = clauses[clause_size - 2];
|
||||
target_end = clauses[clause_size - 1];
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < clause_size - 1; i++) {
|
||||
if (clauses[i] == composition->cursor_position) {
|
||||
target_start = clauses[i];
|
||||
target_end = clauses[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (composition->cursor_position != -1) {
|
||||
target_start = composition->cursor_position;
|
||||
target_end = composition->ime_string.size();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LANG_JAPANESE:
|
||||
|
||||
/**
|
||||
* For Japanese IMEs, the robustest way to retrieve the caret
|
||||
* is scanning the attribute of the latest composition string and
|
||||
* retrieving the begining and the end of the target clause, i.e.
|
||||
* a clause being converted.
|
||||
*/
|
||||
if (lparam & GCS_COMPATTR) {
|
||||
int attribute_size = ::ImmGetCompositionStringW(imm_context,
|
||||
GCS_COMPATTR,
|
||||
NULL, 0);
|
||||
if (attribute_size > 0) {
|
||||
char *attribute_data = new char[attribute_size];
|
||||
if (attribute_data) {
|
||||
::ImmGetCompositionStringW(imm_context, GCS_COMPATTR,
|
||||
attribute_data, attribute_size);
|
||||
for (target_start = 0; target_start < attribute_size;
|
||||
++target_start) {
|
||||
if (IsTargetAttribute(attribute_data[target_start]))
|
||||
break;
|
||||
}
|
||||
for (target_end = target_start; target_end < attribute_size;
|
||||
++target_end) {
|
||||
if (!IsTargetAttribute(attribute_data[target_end]))
|
||||
break;
|
||||
}
|
||||
if (target_start == attribute_size) {
|
||||
/**
|
||||
* This composition clause does not contain any target clauses,
|
||||
* i.e. this clauses is an input clause.
|
||||
* We treat whole this clause as a target clause.
|
||||
*/
|
||||
target_end = target_start;
|
||||
target_start = 0;
|
||||
}
|
||||
if (target_start != -1 && target_start < attribute_size &&
|
||||
attribute_data[target_start] == ATTR_TARGET_NOTCONVERTED)
|
||||
{
|
||||
composition->cursor_position = target_start;
|
||||
}
|
||||
}
|
||||
delete[] attribute_data;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
composition->target_start = target_start;
|
||||
composition->target_end = target_end;
|
||||
}
|
||||
|
||||
|
||||
bool GHOST_ImeWin32::GetString(HIMC imm_context, WPARAM lparam, int type, ImeComposition *composition)
|
||||
{
|
||||
bool result = false;
|
||||
if (lparam & type) {
|
||||
int string_size = ::ImmGetCompositionStringW(imm_context, type, NULL, 0);
|
||||
if (string_size > 0) {
|
||||
int string_length = string_size / sizeof(wchar_t);
|
||||
wchar_t *string_data = new wchar_t[string_length + 1];
|
||||
string_data[string_length] = '\0';
|
||||
if (string_data) {
|
||||
/* Fill the given ImeComposition object. */
|
||||
::ImmGetCompositionStringW(imm_context, type,
|
||||
string_data, string_size);
|
||||
composition->string_type = type;
|
||||
composition->ime_string = string_data;
|
||||
result = true;
|
||||
}
|
||||
delete[] string_data;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool GHOST_ImeWin32::GetResult(HWND window_handle, LPARAM lparam, ImeComposition *composition)
|
||||
{
|
||||
bool result = false;
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
/* Copy the result string to the ImeComposition object. */
|
||||
result = GetString(imm_context, lparam, GCS_RESULTSTR, composition);
|
||||
/**
|
||||
* Reset all the other parameters because a result string does not
|
||||
* have composition attributes.
|
||||
*/
|
||||
composition->cursor_position = -1;
|
||||
composition->target_start = -1;
|
||||
composition->target_end = -1;
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool GHOST_ImeWin32::GetComposition(HWND window_handle, LPARAM lparam, ImeComposition *composition)
|
||||
{
|
||||
bool result = false;
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
/* Copy the composition string to the ImeComposition object. */
|
||||
result = GetString(imm_context, lparam, GCS_COMPSTR, composition);
|
||||
|
||||
/* Retrieve the cursor position in the IME composition. */
|
||||
int cursor_position = ::ImmGetCompositionStringW(imm_context, GCS_CURSORPOS, NULL, 0);
|
||||
composition->cursor_position = cursor_position;
|
||||
composition->target_start = -1;
|
||||
composition->target_end = -1;
|
||||
|
||||
/* Retrieve the target selection and Update the ImeComposition object. */
|
||||
GetCaret(imm_context, lparam, composition);
|
||||
|
||||
/* Mark that there is an ongoing composition. */
|
||||
is_composing_ = true;
|
||||
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::EndIME(HWND window_handle)
|
||||
{
|
||||
/**
|
||||
* A renderer process have moved its input focus to a password input
|
||||
* when there is an ongoing composition, e.g. a user has clicked a
|
||||
* mouse button and selected a password input while composing a text.
|
||||
* For this case, we have to complete the ongoing composition and
|
||||
* clean up the resources attached to this object BEFORE DISABLING THE IME.
|
||||
*/
|
||||
if (!is_enable) return;
|
||||
is_enable = false;
|
||||
CleanupComposition(window_handle);
|
||||
::ImmAssociateContextEx(window_handle, NULL, 0);
|
||||
eventImeData.composite_len = 0;
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::BeginIME(HWND window_handle, const GHOST_Rect &caret_rect, bool complete)
|
||||
{
|
||||
if (is_enable && complete) return;
|
||||
is_enable = true;
|
||||
/**
|
||||
* Load the default IME context.
|
||||
* NOTE(hbono)
|
||||
* IMM ignores this call if the IME context is loaded. Therefore, we do
|
||||
* not have to check whether or not the IME context is loaded.
|
||||
*/
|
||||
::ImmAssociateContextEx(window_handle, NULL, IACE_DEFAULT);
|
||||
/* Complete the ongoing composition and move the IME windows. */
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
if (complete) {
|
||||
/**
|
||||
* A renderer process have moved its input focus to another edit
|
||||
* control when there is an ongoing composition, e.g. a user has
|
||||
* clicked a mouse button and selected another edit control while
|
||||
* composing a text.
|
||||
* For this case, we have to complete the ongoing composition and
|
||||
* hide the IME windows BEFORE MOVING THEM.
|
||||
*/
|
||||
CompleteComposition(window_handle, imm_context);
|
||||
}
|
||||
/**
|
||||
* Save the caret position, and Update the position of the IME window.
|
||||
* This update is used for moving an IME window when a renderer process
|
||||
* resize/moves the input caret.
|
||||
*/
|
||||
if (caret_rect.m_l >= 0 && caret_rect.m_t >= 0) {
|
||||
caret_rect_ = caret_rect;
|
||||
MoveImeWindow(window_handle, imm_context);
|
||||
}
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void convert_utf16_to_utf8_len(std::wstring s, int &len)
|
||||
{
|
||||
if (len >= 0 && len <= s.size())
|
||||
len = count_utf_8_from_16(s.substr(0, len).c_str()) - 1;
|
||||
else
|
||||
len = -1;
|
||||
}
|
||||
|
||||
|
||||
static size_t updateUtf8Buf(ImeComposition &info)
|
||||
{
|
||||
size_t len = count_utf_8_from_16(info.ime_string.c_str());
|
||||
info.utf8_buf.resize(len);
|
||||
conv_utf_16_to_8(info.ime_string.c_str(), &info.utf8_buf[0], len);
|
||||
convert_utf16_to_utf8_len(info.ime_string, info.cursor_position);
|
||||
convert_utf16_to_utf8_len(info.ime_string, info.target_start);
|
||||
convert_utf16_to_utf8_len(info.ime_string, info.target_end);
|
||||
return len - 1;
|
||||
}
|
||||
|
||||
|
||||
void GHOST_ImeWin32::UpdateInfo(HWND window_handle)
|
||||
{
|
||||
int res = this->GetResult(window_handle, GCS_RESULTSTR, &resultInfo);
|
||||
int comp = this->GetComposition(window_handle, GCS_COMPSTR | GCS_COMPATTR, &compInfo);
|
||||
/* convert wchar to utf8 */
|
||||
if (res) {
|
||||
eventImeData.result_len = (GHOST_TUserDataPtr)updateUtf8Buf(resultInfo);
|
||||
eventImeData.result = &resultInfo.utf8_buf[0];
|
||||
}
|
||||
else {
|
||||
eventImeData.result = 0;
|
||||
eventImeData.result_len = 0;
|
||||
}
|
||||
if (comp) {
|
||||
eventImeData.composite_len = (GHOST_TUserDataPtr)updateUtf8Buf(compInfo);
|
||||
eventImeData.composite = &compInfo.utf8_buf[0];
|
||||
eventImeData.cursor_position = compInfo.cursor_position;
|
||||
eventImeData.target_start = compInfo.target_start;
|
||||
eventImeData.target_end = compInfo.target_end;
|
||||
}
|
||||
else {
|
||||
eventImeData.composite = 0;
|
||||
eventImeData.composite_len = 0;
|
||||
eventImeData.cursor_position = -1;
|
||||
eventImeData.target_start = -1;
|
||||
eventImeData.target_end = -1;
|
||||
}
|
||||
}
|
401
intern/ghost/intern/GHOST_ImeWin32.h
Normal file
401
intern/ghost/intern/GHOST_ImeWin32.h
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: some of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file ghost/intern/GHOST_ImeWin32.h
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#ifndef __GHOST_IME_H__
|
||||
#define __GHOST_IME_H__
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "GHOST_Event.h"
|
||||
#include "GHOST_Rect.h"
|
||||
#include <vector>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This header file defines a struct and a class used for encapsulating IMM32
|
||||
* APIs, controls IMEs attached to a window, and enables the 'on-the-spot'
|
||||
* input without deep knowledge about the APIs, i.e. knowledge about the
|
||||
* language-specific and IME-specific behaviors.
|
||||
* The following items enumerates the simplest steps for an (window)
|
||||
* application to control its IMEs with the struct and the class defined
|
||||
* this file.
|
||||
* 1. Add an instance of the GHOST_ImeWin32 class to its window class.
|
||||
* (The GHOST_ImeWin32 class needs a window handle.)
|
||||
* 2. Add messages handlers listed in the following subsections, follow the
|
||||
* instructions written in each subsection, and use the GHOST_ImeWin32 class.
|
||||
* 2.1. WM_IME_SETCONTEXT (0x0281)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::CreateImeWindow();
|
||||
* - GHOST_ImeWin32::CleanupComposition(), and;
|
||||
* - GHOST_ImeWin32::SetImeWindowStyle().
|
||||
* An application MUST prevent from calling ::DefWindowProc().
|
||||
* 2.2. WM_IME_STARTCOMPOSITION (0x010D)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::CreateImeWindow(), and;
|
||||
* - GHOST_ImeWin32::ResetComposition().
|
||||
* An application MUST prevent from calling ::DefWindowProc().
|
||||
* 2.3. WM_IME_COMPOSITION (0x010F)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::UpdateImeWindow();
|
||||
* - GHOST_ImeWin32::GetResult();
|
||||
* - GHOST_ImeWin32::GetComposition(), and;
|
||||
* - GHOST_ImeWin32::ResetComposition() (optional).
|
||||
* An application MUST prevent from calling ::DefWindowProc().
|
||||
* 2.4. WM_IME_ENDCOMPOSITION (0x010E)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::ResetComposition(), and;
|
||||
* - GHOST_ImeWin32::DestroyImeWindow().
|
||||
* An application CAN call ::DefWindowProc().
|
||||
* 2.5. WM_INPUTLANGCHANGE (0x0051)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::SetInputLanguage().
|
||||
* An application CAN call ::DefWindowProc().
|
||||
*/
|
||||
|
||||
/* This struct represents the status of an ongoing composition. */
|
||||
struct ImeComposition {
|
||||
/* Represents the cursor position in the IME composition. */
|
||||
int cursor_position;
|
||||
|
||||
/* Represents the position of the beginning of the selection */
|
||||
int target_start;
|
||||
|
||||
/* Represents the position of the end of the selection */
|
||||
int target_end;
|
||||
|
||||
/**
|
||||
* Represents the type of the string in the 'ime_string' parameter.
|
||||
* Its possible values and description are listed bwlow:
|
||||
* Value Description
|
||||
* 0 The parameter is not used.
|
||||
* GCS_RESULTSTR The parameter represents a result string.
|
||||
* GCS_COMPSTR The parameter represents a composition string.
|
||||
*/
|
||||
int string_type;
|
||||
|
||||
/* Represents the string retrieved from IME (Input Method Editor) */
|
||||
std::wstring ime_string;
|
||||
std::vector<char> utf8_buf;
|
||||
std::vector<unsigned char> format;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class controls the IMM (Input Method Manager) through IMM32 APIs and
|
||||
* enables it to retrieve the string being controled by the IMM. (I wrote
|
||||
* a note to describe the reason why I do not use 'IME' but 'IMM' below.)
|
||||
* NOTE(hbono):
|
||||
* Fortunately or unfortunately, TSF (Text Service Framework) and
|
||||
* CUAS (Cicero Unaware Application Support) allows IMM32 APIs for
|
||||
* retrieving not only the inputs from IMEs (Input Method Editors), used
|
||||
* only for inputting East-Asian language texts, but also the ones from
|
||||
* tablets (on Windows XP Tablet PC Edition and Windows Vista), voice
|
||||
* recognizers (e.g. ViaVoice and Microsoft Office), etc.
|
||||
* We can disable TSF and CUAS in Windows XP Tablet PC Edition. On the other
|
||||
* hand, we can NEVER disable either TSF or CUAS in Windows Vista, i.e.
|
||||
* THIS CLASS IS NOT ONLY USED ON THE INPUT CONTEXTS OF EAST-ASIAN
|
||||
* LANGUAGES BUT ALSO USED ON THE INPUT CONTEXTS OF ALL LANGUAGES.
|
||||
*/
|
||||
class GHOST_ImeWin32 {
|
||||
public:
|
||||
GHOST_ImeWin32();
|
||||
~GHOST_ImeWin32();
|
||||
|
||||
/* Retrieves whether or not there is an ongoing composition. */
|
||||
bool is_composing() const {return is_composing_;}
|
||||
|
||||
/**
|
||||
* Retrieves the input language from Windows and update it.
|
||||
* Return values
|
||||
* * true
|
||||
* The given input language has IMEs.
|
||||
* * false
|
||||
* The given input language does not have IMEs.
|
||||
*/
|
||||
bool SetInputLanguage();
|
||||
|
||||
/**
|
||||
* Create the IME windows, and allocate required resources for them.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void CreateImeWindow(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Update the style of the IME windows.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * message [in] (UINT)
|
||||
* * wparam [in] (WPARAM)
|
||||
* * lparam [in] (LPARAM)
|
||||
* Represent the windows message of the caller.
|
||||
* These parameters are used for verifying if this function is called
|
||||
* in a handler function for WM_IME_SETCONTEXT messages because this
|
||||
* function uses ::DefWindowProc() to update the style.
|
||||
* A caller just has to pass the input parameters for the handler
|
||||
* function without modifications.
|
||||
* * handled [out] (BOOL*)
|
||||
* Returns ::DefWindowProc() is really called in this function.
|
||||
* PLEASE DO NOT CALL ::DefWindowProc() IF THIS VALUE IS TRUE!
|
||||
* All the window styles set in this function are over-written when
|
||||
* calling ::DefWindowProc() after returning this function.
|
||||
*/
|
||||
void SetImeWindowStyle(HWND window_handle, UINT message,
|
||||
WPARAM wparam, LPARAM lparam, BOOL* handled);
|
||||
|
||||
/**
|
||||
* Destroy the IME windows and all the resources attached to them.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void DestroyImeWindow(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Update the position of the IME windows.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void UpdateImeWindow(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Clean up the all resources attached to the given GHOST_ImeWin32 object, and
|
||||
* reset its composition status.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void CleanupComposition(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Reset the composition status.
|
||||
* Cancel the ongoing composition if it exists.
|
||||
* NOTE(hbono): This method does not release the allocated resources.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void ResetComposition(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Retrieve a composition result of the ongoing composition if it exists.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * lparam [in] (LPARAM)
|
||||
* Specifies the updated members of the ongoing composition, and must be
|
||||
* the same parameter of a WM_IME_COMPOSITION message handler.
|
||||
* This parameter is used for checking if the ongoing composition has
|
||||
* its result string,
|
||||
* * composition [out] (ImeComposition)
|
||||
* Represents the struct contains the composition result.
|
||||
* Return values
|
||||
* * true
|
||||
* The ongoing composition has a composition result.
|
||||
* * false
|
||||
* The ongoing composition does not have composition results.
|
||||
* Remarks
|
||||
* This function is designed for being called from WM_IME_COMPOSITION
|
||||
* message handlers.
|
||||
*/
|
||||
bool GetResult(HWND window_handle, LPARAM lparam,
|
||||
ImeComposition* composition);
|
||||
|
||||
/**
|
||||
* Retrieve the current composition status of the ongoing composition.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * lparam [in] (LPARAM)
|
||||
* Specifies the updated members of the ongoing composition, and must be
|
||||
* the same parameter of a WM_IME_COMPOSITION message handler.
|
||||
* This parameter is used for checking if the ongoing composition has
|
||||
* its result string,
|
||||
* * composition [out] (ImeComposition)
|
||||
* Represents the struct contains the composition status.
|
||||
* Return values
|
||||
* * true
|
||||
* The status of the ongoing composition is updated.
|
||||
* * false
|
||||
* The status of the ongoing composition is not updated.
|
||||
* Remarks
|
||||
* This function is designed for being called from WM_IME_COMPOSITION
|
||||
* message handlers.
|
||||
*/
|
||||
bool GetComposition(HWND window_handle, LPARAM lparam,
|
||||
ImeComposition* composition);
|
||||
|
||||
/**
|
||||
* Enable the IME attached to the given window, i.e. allows user-input
|
||||
* events to be dispatched to the IME.
|
||||
* In Chrome, this function is used when:
|
||||
* * a renderer process moves its input focus to another edit control, or;
|
||||
* * a renrerer process moves the position of the focused edit control.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * caret_rect [in] (const gfx::Rect&)
|
||||
* Represent the rectangle of the input caret.
|
||||
* This rectangle is used for controlling the positions of IME windows.
|
||||
* * complete [in] (bool)
|
||||
* Represents whether or not to complete the ongoing composition.
|
||||
* + true
|
||||
* After finishing the ongoing composition and close its IME windows,
|
||||
* start another composition and display its IME windows to the given
|
||||
* position.
|
||||
* + false
|
||||
* Just move the IME windows of the ongoing composition to the given
|
||||
* position without finishing it.
|
||||
*/
|
||||
void BeginIME(HWND window_handle,
|
||||
const GHOST_Rect& caret_rect,
|
||||
bool complete);
|
||||
|
||||
/**
|
||||
* Disable the IME attached to the given window, i.e. prohibits any user-input
|
||||
* events from being dispatched to the IME.
|
||||
* In Chrome, this function is used when:
|
||||
* * a renreder process sets its input focus to a password input.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void EndIME(HWND window_handle);
|
||||
|
||||
/* Updatg resultInfo and compInfo */
|
||||
void UpdateInfo(HWND window_handle);
|
||||
|
||||
/* disable ime when start up */
|
||||
void CheckFirst(HWND window_handle);
|
||||
|
||||
ImeComposition resultInfo, compInfo;
|
||||
GHOST_TEventImeData eventImeData;
|
||||
|
||||
protected:
|
||||
/* Determines whether or not the given attribute represents a target (a.k.a. a selection). */
|
||||
bool IsTargetAttribute(char attribute) const {
|
||||
return (attribute == ATTR_TARGET_CONVERTED ||
|
||||
attribute == ATTR_TARGET_NOTCONVERTED);
|
||||
}
|
||||
|
||||
/* Retrieve the target area. */
|
||||
void GetCaret(HIMC imm_context, LPARAM lparam,
|
||||
ImeComposition* composition);
|
||||
|
||||
/* Update the position of the IME windows. */
|
||||
void MoveImeWindow(HWND window_handle, HIMC imm_context);
|
||||
|
||||
/* Complete the ongoing composition if it exists. */
|
||||
void CompleteComposition(HWND window_handle, HIMC imm_context);
|
||||
|
||||
/* Retrieve a string from the IMM. */
|
||||
bool GetString(HIMC imm_context, WPARAM lparam, int type,
|
||||
ImeComposition* composition);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Represents whether or not there is an ongoing composition in a browser
|
||||
* process, i.e. whether or not a browser process is composing a text.
|
||||
*/
|
||||
bool is_composing_;
|
||||
|
||||
/**
|
||||
* This value represents whether or not the current input context has IMEs.
|
||||
* The following table shows the list of IME status:
|
||||
* Value Description
|
||||
* false The current input language does not have IMEs.
|
||||
* true The current input language has IMEs.
|
||||
*/
|
||||
bool ime_status_;
|
||||
|
||||
/**
|
||||
* The current input Language ID retrieved from Windows, which consists of:
|
||||
* * Primary Language ID (bit 0 to bit 9), which shows a natunal language
|
||||
* (English, Korean, Chinese, Japanese, etc.) and;
|
||||
* * Sub-Language ID (bit 10 to bit 15), which shows a geometrical region
|
||||
* the language is spoken (For English, United States, United Kingdom,
|
||||
* Australia, Canada, etc.)
|
||||
* The following list enumerates some examples for the Language ID:
|
||||
* * "en-US" (0x0409)
|
||||
* MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
|
||||
* * "ko-KR" (0x0412)
|
||||
* MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN);
|
||||
* * "zh-TW" (0x0404)
|
||||
* MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL);
|
||||
* * "zh-CN" (0x0804)
|
||||
* MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
|
||||
* * "ja-JP" (0x0411)
|
||||
* MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), etc.
|
||||
* (See <winnt.h> for other available values.)
|
||||
* This Language ID is used for processing language-specific operations in
|
||||
* IME functions.
|
||||
*/
|
||||
LANGID input_language_id_;
|
||||
|
||||
/**
|
||||
* Represents whether or not the current input context has created a system
|
||||
* caret to set the position of its IME candidate window.
|
||||
* * true: it creates a system caret.
|
||||
* * false: it does not create a system caret.
|
||||
*/
|
||||
bool system_caret_;
|
||||
|
||||
/* The rectangle of the input caret retrieved from a renderer process. */
|
||||
GHOST_Rect caret_rect_;
|
||||
|
||||
/* used for disable ime when start up */
|
||||
bool is_first, is_enable;
|
||||
};
|
||||
|
||||
#endif * __GHOST_IME_H__
|
@@ -792,6 +792,15 @@ GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_
|
||||
return new GHOST_Event(system->getMilliSeconds(), type, window);
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
GHOST_Event *GHOST_SystemWin32::processImeEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TEventImeData *data)
|
||||
{
|
||||
GHOST_System *system = (GHOST_System *)getSystem();
|
||||
return new GHOST_EventIME(system->getMilliSeconds(), type, window, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(
|
||||
GHOST_TEventType eventType,
|
||||
GHOST_TDragnDropTypes draggedObjectType,
|
||||
@@ -904,6 +913,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
|
||||
LRESULT lResult = 0;
|
||||
GHOST_SystemWin32 *system = ((GHOST_SystemWin32 *)getSystem());
|
||||
GHOST_EventManager *eventManager = system->getEventManager();
|
||||
GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized");
|
||||
|
||||
if (hwnd) {
|
||||
@@ -912,8 +922,13 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
switch (msg) {
|
||||
// we need to check if new key layout has AltGr
|
||||
case WM_INPUTLANGCHANGE:
|
||||
{
|
||||
system->handleKeyboardChange();
|
||||
#ifdef WITH_INPUT_IME
|
||||
window->getImeInput()->SetInputLanguage();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Keyboard events, processed
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
@@ -949,6 +964,56 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef WITH_INPUT_IME
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// IME events, processed, read more in GHOST_IME.h
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
case WM_IME_SETCONTEXT:
|
||||
{
|
||||
window->getImeInput()->SetInputLanguage();
|
||||
window->getImeInput()->CreateImeWindow(window->getHWND());
|
||||
window->getImeInput()->CleanupComposition(window->getHWND());
|
||||
window->getImeInput()->CheckFirst(window->getHWND());
|
||||
break;
|
||||
}
|
||||
case WM_IME_STARTCOMPOSITION:
|
||||
{
|
||||
eventHandled = true;
|
||||
/* remove input event before start comp event, avoid redundant input */
|
||||
eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
|
||||
window->getImeInput()->CreateImeWindow(window->getHWND());
|
||||
window->getImeInput()->ResetComposition(window->getHWND());
|
||||
event = processImeEvent(
|
||||
GHOST_kEventImeCompositionStart,
|
||||
window,
|
||||
&window->getImeInput()->eventImeData);
|
||||
break;
|
||||
}
|
||||
case WM_IME_COMPOSITION:
|
||||
{
|
||||
eventHandled = true;
|
||||
window->getImeInput()->UpdateImeWindow(window->getHWND());
|
||||
window->getImeInput()->UpdateInfo(window->getHWND());
|
||||
event = processImeEvent(
|
||||
GHOST_kEventImeComposition,
|
||||
window,
|
||||
&window->getImeInput()->eventImeData);
|
||||
break;
|
||||
}
|
||||
case WM_IME_ENDCOMPOSITION:
|
||||
{
|
||||
eventHandled = true;
|
||||
/* remove input event after end comp event, avoid redundant input */
|
||||
eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
|
||||
window->getImeInput()->ResetComposition(window->getHWND());
|
||||
window->getImeInput()->DestroyImeWindow(window->getHWND());
|
||||
event = processImeEvent(
|
||||
GHOST_kEventImeCompositionEnd,
|
||||
window,
|
||||
&window->getImeInput()->eventImeData);
|
||||
break;
|
||||
}
|
||||
#endif /* WITH_INPUT_IME */
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Keyboard events, ignored
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
@@ -306,6 +306,15 @@ protected:
|
||||
*/
|
||||
static GHOST_Event *processWindowEvent(GHOST_TEventType type, GHOST_IWindow *window);
|
||||
|
||||
/**
|
||||
* Creates a IME event.
|
||||
* \param type The type of event to create.
|
||||
* \param window The window receiving the event (the active window).
|
||||
* \param data IME data.
|
||||
* \return The event created.
|
||||
*/
|
||||
static GHOST_Event *processImeEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TEventImeData *data);
|
||||
|
||||
/**
|
||||
* Handles minimum window size.
|
||||
* \param minmax The MINMAXINFO structure.
|
||||
|
@@ -295,6 +295,22 @@ public:
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
virtual void beginIME(GHOST_TInt32 x,
|
||||
GHOST_TInt32 y,
|
||||
GHOST_TInt32 w,
|
||||
GHOST_TInt32 h,
|
||||
int completed)
|
||||
{
|
||||
/* do nothing temporarily if not in windows */
|
||||
}
|
||||
|
||||
virtual void endIME()
|
||||
{
|
||||
/* do nothing temporarily if not in windows */
|
||||
}
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Tries to install a rendering context in this window.
|
||||
|
@@ -1050,3 +1050,16 @@ GHOST_TSuccess GHOST_WindowWin32::endProgressBar()
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
void GHOST_WindowWin32::beginIME(GHOST_TInt32 x, GHOST_TInt32 y, GHOST_TInt32 w, GHOST_TInt32 h, int completed)
|
||||
{
|
||||
this->getImeInput()->BeginIME(this->getHWND(), GHOST_Rect(x, y - h , x, y), (bool)completed);
|
||||
}
|
||||
|
||||
|
||||
void GHOST_WindowWin32::endIME()
|
||||
{
|
||||
this->getImeInput()->EndIME(this->getHWND());
|
||||
}
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
@@ -39,6 +39,9 @@
|
||||
|
||||
#include "GHOST_Window.h"
|
||||
#include "GHOST_TaskbarWin32.h"
|
||||
#ifdef WITH_INPUT_IME
|
||||
#include "GHOST_ImeWin32.h"
|
||||
#endif
|
||||
|
||||
#include <wintab.h>
|
||||
#define PACKETDATA (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR)
|
||||
@@ -253,6 +256,18 @@ public:
|
||||
/** if the window currently resizing */
|
||||
bool m_inLiveResize;
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
GHOST_ImeWin32 *getImeInput() {return &m_imeImput;}
|
||||
|
||||
virtual void beginIME(GHOST_TInt32 x,
|
||||
GHOST_TInt32 y,
|
||||
GHOST_TInt32 w,
|
||||
GHOST_TInt32 h,
|
||||
int completed);
|
||||
|
||||
virtual void endIME();
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
@@ -339,6 +354,11 @@ private:
|
||||
|
||||
/** Hwnd to parent window */
|
||||
GHOST_TEmbedderWindowID m_parentWindowHwnd;
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/** Handle input method editors event */
|
||||
GHOST_ImeWin32 m_imeImput;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // __GHOST_WINDOWWIN32_H__
|
||||
|
@@ -313,6 +313,7 @@ void UI_draw_box_shadow(unsigned char alpha, float minx, float miny, float maxx,
|
||||
void UI_draw_roundbox_gl_mode(int mode, float minx, float miny, float maxx, float maxy, float rad);
|
||||
void UI_draw_roundbox_shade_x(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadetop, float shadedown);
|
||||
void UI_draw_roundbox_shade_y(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadeLeft, float shadeRight);
|
||||
void UI_text_draw_underline(int pos_x, int pos_y, int len, int height);
|
||||
|
||||
/* state for scrolldrawing */
|
||||
#define UI_SCROLL_PRESSED (1 << 0)
|
||||
@@ -1009,4 +1010,6 @@ void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p);
|
||||
|
||||
int UI_calc_float_precision(int prec, double value);
|
||||
|
||||
/* Utility */
|
||||
void ui_region_to_window(const struct ARegion *ar, int *x, int *y);
|
||||
#endif /* __UI_INTERFACE_H__ */
|
||||
|
@@ -71,6 +71,12 @@ if(WITH_PYTHON)
|
||||
add_definitions(-DWITH_PYTHON)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(WITH_INPUT_IME)
|
||||
add_definitions(-DWITH_INPUT_IME)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_definitions(${GL_DEFINITIONS})
|
||||
|
||||
blender_add_lib(bf_editor_interface "${SRC}" "${INC}" "${INC_SYS}")
|
||||
|
@@ -209,6 +209,11 @@ void ui_window_to_region(const ARegion *ar, int *x, int *y)
|
||||
*y -= ar->winrct.ymin;
|
||||
}
|
||||
|
||||
void ui_region_to_window(const ARegion *ar, int *x, int *y)
|
||||
{
|
||||
*x += ar->winrct.xmin;
|
||||
*y += ar->winrct.ymin;
|
||||
}
|
||||
/* ******************* block calc ************************* */
|
||||
|
||||
void ui_block_translate(uiBlock *block, int x, int y)
|
||||
|
@@ -395,6 +395,11 @@ void UI_draw_roundbox(float minx, float miny, float maxx, float maxy, float rad)
|
||||
ui_draw_anti_roundbox(GL_POLYGON, minx, miny, maxx, maxy, rad, roundboxtype & UI_RB_ALPHA);
|
||||
}
|
||||
|
||||
void UI_text_draw_underline(int pos_x, int pos_y, int len, int height) {
|
||||
short ofs_y = 4 * U.pixelsize;
|
||||
glRecti(pos_x, pos_y - ofs_y, pos_x + len, pos_y - ofs_y + (height * U.pixelsize));
|
||||
}
|
||||
|
||||
/* ************** SPECIAL BUTTON DRAWING FUNCTIONS ************* */
|
||||
|
||||
void ui_draw_but_IMAGE(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *rect)
|
||||
|
@@ -86,6 +86,7 @@
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
#include "wm_window.h"
|
||||
|
||||
/* place the mouse at the scaled down location when un-grabbing */
|
||||
#define USE_CONT_MOUSE_CORRECT
|
||||
@@ -2425,8 +2426,48 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in
|
||||
return changed;
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* enable ime, and set up uibut ime data */
|
||||
static void ui_textedit_ime_begin(wmWindow *win, uiBut *UNUSED(but))
|
||||
{
|
||||
/* XXX Is this really needed? */
|
||||
int x, y;
|
||||
|
||||
/* enable IME and position to cursor, it's a trick */
|
||||
x = win->eventstate->x;
|
||||
/* flip y and move down a bit, prevent the IME panel cover the edit button */
|
||||
y = win->eventstate->y - 12;
|
||||
|
||||
wm_window_IME_begin(win, x, y, 0, 0, true);
|
||||
}
|
||||
|
||||
/* disable ime, and clear uibut ime data */
|
||||
static void ui_textedit_ime_end(wmWindow *win, uiBut *UNUSED(but))
|
||||
{
|
||||
wm_window_IME_end(win);
|
||||
}
|
||||
|
||||
void ui_but_ime_reposition(uiBut *but, int x, int y, int complete)
|
||||
{
|
||||
BLI_assert(but->active);
|
||||
|
||||
ui_region_to_window(but->active->region, &x, &y);
|
||||
wm_window_IME_begin(but->active->window, x, y - 4, 0, 0, complete);
|
||||
}
|
||||
|
||||
/* should be ui_but_ime_data_get */
|
||||
wmImeData *ui_but_get_ime_data(uiBut *but)
|
||||
{
|
||||
if (but->active && but->active->window)
|
||||
return but->active->window->ime_data;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
int len;
|
||||
|
||||
if (data->str) {
|
||||
@@ -2482,12 +2523,18 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
|
||||
but->flag &= ~UI_BUT_REDALERT;
|
||||
|
||||
ui_but_update(but);
|
||||
|
||||
WM_cursor_modal_set(CTX_wm_window(C), BC_TEXTEDITCURSOR);
|
||||
|
||||
WM_cursor_modal_set(win, BC_TEXTEDITCURSOR);
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
ui_textedit_ime_begin(win, but);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
if (but) {
|
||||
if (ui_but_is_utf8(but)) {
|
||||
int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr));
|
||||
@@ -2518,7 +2565,11 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
|
||||
but->pos = -1;
|
||||
}
|
||||
|
||||
WM_cursor_modal_restore(CTX_wm_window(C));
|
||||
WM_cursor_modal_restore(win);
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
ui_textedit_ime_end(win, but);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
|
||||
@@ -2583,6 +2634,10 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
|
||||
int retval = WM_UI_HANDLER_CONTINUE;
|
||||
bool changed = false, inbox = false, update = false;
|
||||
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
wmImeData *ime_data = win->ime_data;
|
||||
bool is_ime_composing = ime_data && ime_data->is_ime_composite;
|
||||
|
||||
switch (event->type) {
|
||||
case MOUSEMOVE:
|
||||
case MOUSEPAN:
|
||||
@@ -2603,6 +2658,12 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
|
||||
case RIGHTMOUSE:
|
||||
case ESCKEY:
|
||||
if (event->val == KM_PRESS) {
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* skips button handling since it is not wanted */
|
||||
if (is_ime_composing) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
data->cancel = true;
|
||||
data->escapecancel = true;
|
||||
button_activate_state(C, but, BUTTON_STATE_EXIT);
|
||||
@@ -2660,7 +2721,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
|
||||
}
|
||||
}
|
||||
|
||||
if (event->val == KM_PRESS) {
|
||||
if (event->val == KM_PRESS && !is_ime_composing) {
|
||||
switch (event->type) {
|
||||
case VKEY:
|
||||
case XKEY:
|
||||
@@ -2776,7 +2837,14 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
|
||||
break;
|
||||
}
|
||||
|
||||
if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)) {
|
||||
if ((event->ascii || event->utf8_buf[0]) &&
|
||||
(retval == WM_UI_HANDLER_CONTINUE)
|
||||
#ifdef WITH_INPUT_IME
|
||||
&& !is_ime_composing &&
|
||||
!WM_event_is_ime_switch(event)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
char ascii = event->ascii;
|
||||
const char *utf8_buf = event->utf8_buf;
|
||||
|
||||
@@ -2811,6 +2879,22 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
|
||||
update = true;
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
if (event->type == WM_IME_COMPOSITE_START || event->type == WM_IME_COMPOSITE_EVENT) {
|
||||
changed = true;
|
||||
|
||||
if (event->type == WM_IME_COMPOSITE_START && but->selend > but->selsta)
|
||||
ui_textedit_delete_selection(but, data);
|
||||
if (event->type == WM_IME_COMPOSITE_EVENT && ime_data->result_len)
|
||||
ui_textedit_type_buf(but, data,
|
||||
ime_data->result,
|
||||
ime_data->result_len);
|
||||
}
|
||||
else if (event->type == WM_IME_COMPOSITE_END) {
|
||||
changed = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (changed) {
|
||||
/* only update when typing for TAB key */
|
||||
if (update && data->interactive) {
|
||||
|
@@ -627,6 +627,9 @@ void ui_panel_menu(struct bContext *C, ARegion *ar, Panel *pa);
|
||||
uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new);
|
||||
uiBut *ui_but_find_new(uiBlock *block_old, const uiBut *but_new);
|
||||
|
||||
void ui_but_ime_reposition(uiBut *but, int x, int y, int complete);
|
||||
struct wmImeData *ui_but_get_ime_data(uiBut *but);
|
||||
|
||||
/* interface_widgets.c */
|
||||
void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3);
|
||||
void ui_draw_anti_roundbox(int mode, float minx, float miny, float maxx, float maxy, float rad, bool use_alpha);
|
||||
|
@@ -33,6 +33,8 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
@@ -59,6 +61,8 @@
|
||||
|
||||
#include "interface_intern.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
|
||||
/* icons are 80% of height of button (16 pixels inside 20 height) */
|
||||
#define ICON_SIZE_FROM_BUTRECT(rect) (0.8f * BLI_rcti_size_y(rect))
|
||||
|
||||
@@ -1232,12 +1236,56 @@ static void ui_text_clip_right_label(uiFontStyle *fstyle, uiBut *but, const rcti
|
||||
BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
static void widget_draw_text_ime_underline(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect, wmImeData *ime_data, char *drawstr)
|
||||
{
|
||||
int ofs_x, width;
|
||||
int rect_x = BLI_rcti_size_x(rect);
|
||||
int target_start = ime_data->target_start, target_end = ime_data->target_end;
|
||||
|
||||
if (drawstr[0] != 0) {
|
||||
if (but->pos >= but->ofs) {
|
||||
ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->pos - but->ofs);
|
||||
}
|
||||
else {
|
||||
ofs_x = 0;
|
||||
}
|
||||
|
||||
width = BLF_width(fstyle->uifont_id, drawstr + but->ofs,
|
||||
ime_data->composite_len + but->pos - but->ofs);
|
||||
|
||||
glColor4ubv((unsigned char *)wcol->text);
|
||||
UI_text_draw_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 1);
|
||||
|
||||
/* draw the thick line */
|
||||
if (target_start != -1 && target_end != -1) {
|
||||
target_end -= target_start;
|
||||
target_start += but->pos;
|
||||
|
||||
if (target_start >= but->ofs) {
|
||||
ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, target_start - but->ofs);
|
||||
}
|
||||
else {
|
||||
ofs_x = 0;
|
||||
}
|
||||
|
||||
width = BLF_width(fstyle->uifont_id, drawstr + but->ofs,
|
||||
target_end + target_start - but->ofs);
|
||||
|
||||
UI_text_draw_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect)
|
||||
{
|
||||
int drawstr_left_len = UI_MAX_DRAW_STR;
|
||||
const char *drawstr = but->drawstr;
|
||||
char *drawstr = but->drawstr;
|
||||
const char *drawstr_right = NULL;
|
||||
char *drawstr_edit = NULL;
|
||||
bool use_right_only = false;
|
||||
wmImeData *ime_data;
|
||||
|
||||
UI_fontstyle_set(fstyle);
|
||||
|
||||
@@ -1266,13 +1314,26 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
|
||||
/* max length isn't used in this case,
|
||||
* we rely on string being NULL terminated. */
|
||||
drawstr_left_len = INT_MAX;
|
||||
drawstr = but->editstr;
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
ime_data = ui_but_get_ime_data(but);
|
||||
|
||||
if (ime_data && ime_data->composite_len) {
|
||||
/* insert composite string into cursor pos */
|
||||
BLI_snprintf(drawstr, UI_MAX_DRAW_STR, "%s%s%s",
|
||||
but->editstr, ime_data->composite,
|
||||
but->editstr + but->pos);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
drawstr = but->editstr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* text button selection and cursor */
|
||||
/* text button selection, cursor, composite underline */
|
||||
if (but->editstr && but->pos != -1) {
|
||||
int vpos;
|
||||
int tx, ty;
|
||||
|
||||
/* text button selection */
|
||||
if ((but->selend - but->selsta) > 0) {
|
||||
@@ -1297,19 +1358,48 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* text cursor */
|
||||
vpos = but->pos;
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* if is ime compositing, move the cursor */
|
||||
if (ime_data && ime_data->composite_len && ime_data->cursor_position != -1) {
|
||||
vpos += ime_data->cursor_position;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (but->pos >= but->ofs) {
|
||||
int t;
|
||||
if (drawstr[0] != 0) {
|
||||
t = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->pos - but->ofs);
|
||||
t = BLF_width(fstyle->uifont_id, drawstr + but->ofs, vpos - but->ofs);
|
||||
}
|
||||
else {
|
||||
t = 0;
|
||||
}
|
||||
|
||||
glColor3f(0.20, 0.6, 0.9);
|
||||
glRecti(rect->xmin + t, rect->ymin + 2, rect->xmin + t + 2, rect->ymax - 2);
|
||||
|
||||
tx = rect->xmin + t + 2;
|
||||
ty = rect->ymin + 2;
|
||||
|
||||
/* draw cursor */
|
||||
glRecti(rect->xmin + t, ty, tx, rect->ymax - 2);
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
if (ime_data && ime_data->composite_len) {
|
||||
/* ime cursor following */
|
||||
if (but->pos >= but->ofs) {
|
||||
ui_but_ime_reposition(but, tx + 5, ty + 3, false);
|
||||
}
|
||||
|
||||
/* composite underline */
|
||||
widget_draw_text_ime_underline(fstyle, wcol, but, rect, ime_data, drawstr);
|
||||
}
|
||||
#else
|
||||
(void)ime_data;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (fstyle->kerning == 1)
|
||||
|
@@ -52,6 +52,12 @@ if(WITH_INTERNATIONAL)
|
||||
add_definitions(-DWITH_INTERNATIONAL)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(WITH_INPUT_IME)
|
||||
add_definitions(-DWITH_INPUT_IME)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_definitions(${GL_DEFINITIONS})
|
||||
|
||||
blender_add_lib(bf_editor_screen "${SRC}" "${INC}" "${INC_SYS}")
|
||||
|
@@ -54,6 +54,7 @@
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
#include "wm_window.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
#include "ED_screen.h"
|
||||
@@ -1434,6 +1435,11 @@ void ED_screen_set_subwinactive(bContext *C, wmEvent *event)
|
||||
else {
|
||||
/* notifier invokes freeing the buttons... causing a bit too much redraws */
|
||||
if (oldswin != scr->subwinactive) {
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* when cursor leaves a region, disable IME */
|
||||
wm_window_IME_end(win);
|
||||
#endif
|
||||
|
||||
region_cursor_set(win, scr->subwinactive, true);
|
||||
|
||||
/* this used to be a notifier, but needs to be done immediate
|
||||
|
@@ -47,6 +47,12 @@ if(WITH_PYTHON)
|
||||
add_definitions(-DWITH_PYTHON)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(WITH_INPUT_IME)
|
||||
add_definitions(-DWITH_INPUT_IME)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_definitions(${GL_DEFINITIONS})
|
||||
|
||||
blender_add_lib(bf_editor_space_console "${SRC}" "${INC}" "${INC_SYS}")
|
||||
|
@@ -47,6 +47,7 @@
|
||||
|
||||
#include "console_intern.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "../space_info/textview.h"
|
||||
|
||||
@@ -161,7 +162,7 @@ static void console_cursor_wrap_offset(const char *str, int width, int *row, int
|
||||
return;
|
||||
}
|
||||
|
||||
static int console_textview_line_color(struct TextViewContext *tvc, unsigned char fg[3], unsigned char UNUSED(bg[3]))
|
||||
static int console_textview_line_color(struct TextViewContext *tvc, wmImeData *ime_data, unsigned char fg[3], unsigned char UNUSED(bg[3]))
|
||||
{
|
||||
ConsoleLine *cl_iter = (ConsoleLine *)tvc->iter;
|
||||
|
||||
@@ -176,9 +177,28 @@ static int console_textview_line_color(struct TextViewContext *tvc, unsigned cha
|
||||
|
||||
console_cursor_wrap_offset(sc->prompt, tvc->console_width, &offl, &offc, NULL);
|
||||
console_cursor_wrap_offset(cl->line, tvc->console_width, &offl, &offc, cl->line + cl->cursor);
|
||||
|
||||
pen[0] = tvc->cwidth * offc;
|
||||
pen[1] = -2 - tvc->lheight * offl;
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* consider the effect of composition string */
|
||||
if (ime_data && ime_data->composite_len) {
|
||||
char *end = NULL;
|
||||
if (ime_data->cursor_position != -1)
|
||||
end = ime_data->composite + ime_data->cursor_position;
|
||||
|
||||
console_cursor_wrap_offset(ime_data->composite, tvc->console_width, &offl, &offc, end);
|
||||
|
||||
/* cursor inside the composition string */
|
||||
pen[0] = tvc->cwidth * offc;
|
||||
pen[1] = -2 - tvc->lheight * offl;
|
||||
|
||||
if (end != NULL)
|
||||
console_cursor_wrap_offset(end, tvc->console_width, &offl, &offc, NULL);
|
||||
}
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
console_cursor_wrap_offset(cl->line + cl->cursor, tvc->console_width, &offl, &offc, NULL);
|
||||
pen[1] += tvc->lheight * offl;
|
||||
|
||||
@@ -190,7 +210,16 @@ static int console_textview_line_color(struct TextViewContext *tvc, unsigned cha
|
||||
(xy[1] + pen[1]),
|
||||
(xy[0] + pen[0]) + 1,
|
||||
(xy[1] + pen[1] + tvc->lheight)
|
||||
);
|
||||
);
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* cursor following */
|
||||
if (ime_data && ime_data->composite_len) {
|
||||
ime_data->cursor_xy[0] = (xy[0] + pen[0]) + 1;
|
||||
ime_data->cursor_xy[1] = (xy[1] + pen[1]) - 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
console_line_color(fg, cl_iter->type);
|
||||
@@ -203,8 +232,8 @@ static void console_textview_const_colors(TextViewContext *UNUSED(tvc), unsigned
|
||||
UI_GetThemeColor4ubv(TH_CONSOLE_SELECT, bg_sel);
|
||||
}
|
||||
|
||||
static int console_textview_main__internal(struct SpaceConsole *sc, ARegion *ar, int draw,
|
||||
int mval[2], void **mouse_pick, int *pos_pick)
|
||||
static int console_textview_main__internal(struct SpaceConsole *sc, ARegion *ar, wmImeData *ime_data,
|
||||
int draw, int mval[2], void **mouse_pick, int *pos_pick)
|
||||
{
|
||||
ConsoleLine cl_dummy = {NULL};
|
||||
int ret = 0;
|
||||
@@ -232,27 +261,33 @@ static int console_textview_main__internal(struct SpaceConsole *sc, ARegion *ar,
|
||||
tvc.ymax = v2d->cur.ymax;
|
||||
tvc.winx = ar->winx - V2D_SCROLL_WIDTH;
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
if (!draw) {
|
||||
ime_data = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
console_scrollback_prompt_begin(sc, &cl_dummy);
|
||||
ret = textview_draw(&tvc, draw, mval, mouse_pick, pos_pick);
|
||||
ret = textview_draw(&tvc, ime_data, draw, mval, mouse_pick, pos_pick);
|
||||
console_scrollback_prompt_end(sc, &cl_dummy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void console_textview_main(struct SpaceConsole *sc, ARegion *ar)
|
||||
void console_textview_main(struct SpaceConsole *sc, ARegion *ar, wmImeData *ime_data)
|
||||
{
|
||||
int mval[2] = {INT_MAX, INT_MAX};
|
||||
console_textview_main__internal(sc, ar, 1, mval, NULL, NULL);
|
||||
console_textview_main__internal(sc, ar, ime_data, 1, mval, NULL, NULL);
|
||||
}
|
||||
|
||||
int console_textview_height(struct SpaceConsole *sc, ARegion *ar)
|
||||
{
|
||||
int mval[2] = {INT_MAX, INT_MAX};
|
||||
return console_textview_main__internal(sc, ar, 0, mval, NULL, NULL);
|
||||
return console_textview_main__internal(sc, ar, NULL, 0, mval, NULL, NULL);
|
||||
}
|
||||
|
||||
int console_char_pick(struct SpaceConsole *sc, ARegion *ar, const int mval[2])
|
||||
int console_char_pick(struct SpaceConsole *sc, ARegion *ar, wmImeData *ime_data, const int mval[2])
|
||||
{
|
||||
int pos_pick = 0;
|
||||
void *mouse_pick = NULL;
|
||||
@@ -261,6 +296,6 @@ int console_char_pick(struct SpaceConsole *sc, ARegion *ar, const int mval[2])
|
||||
mval_clamp[0] = CLAMPIS(mval[0], CONSOLE_DRAW_MARGIN, ar->winx - CONSOLE_DRAW_MARGIN);
|
||||
mval_clamp[1] = CLAMPIS(mval[1], CONSOLE_DRAW_MARGIN, ar->winy - CONSOLE_DRAW_MARGIN);
|
||||
|
||||
console_textview_main__internal(sc, ar, 0, mval_clamp, &mouse_pick, &pos_pick);
|
||||
console_textview_main__internal(sc, ar, ime_data, 0, mval_clamp, &mouse_pick, &pos_pick);
|
||||
return pos_pick;
|
||||
}
|
||||
|
@@ -35,9 +35,9 @@ struct ReportList;
|
||||
struct bContext;
|
||||
|
||||
/* console_draw.c */
|
||||
void console_textview_main(struct SpaceConsole *sc, struct ARegion *ar);
|
||||
void console_textview_main(struct SpaceConsole *sc, struct ARegion *ar, struct wmImeData *ime_data);
|
||||
int console_textview_height(struct SpaceConsole *sc, struct ARegion *ar); /* needed to calculate the scrollbar */
|
||||
int console_char_pick(struct SpaceConsole *sc, struct ARegion *ar, const int mval[2]);
|
||||
int console_char_pick(struct SpaceConsole *sc, struct ARegion *ar, struct wmImeData *ime_data, const int mval[2]);
|
||||
|
||||
void console_scrollback_prompt_begin(struct SpaceConsole *sc, ConsoleLine *cl_dummy);
|
||||
void console_scrollback_prompt_end(struct SpaceConsole *sc, ConsoleLine *cl_dummy);
|
||||
|
@@ -394,6 +394,37 @@ static int console_insert_exec(bContext *C, wmOperator *op)
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int console_insert_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
#ifdef WITH_INPUT_IME
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
wmImeData *ime = win->ime_data;
|
||||
ARegion *ar = CTX_wm_region(C);
|
||||
|
||||
if (event->type == WM_IME_COMPOSITE_EVENT) {
|
||||
ED_area_tag_redraw(CTX_wm_area(C));
|
||||
console_scroll_bottom(ar);
|
||||
}
|
||||
|
||||
/* composition complete
|
||||
* some Japanese IMEs return result without WM_IME_COMPOSITE_END event
|
||||
*/
|
||||
if (event->type == WM_IME_COMPOSITE_EVENT && ime->result_len) {
|
||||
char *str = "";
|
||||
if (ime->result) str = ime->result;
|
||||
RNA_string_set(op->ptr, "text", str);
|
||||
console_insert_exec(C, op);
|
||||
}
|
||||
|
||||
if (event->type == WM_IME_COMPOSITE_END)
|
||||
return OPERATOR_FINISHED;
|
||||
#else
|
||||
(void)C; (void)op; (void)event;
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
// if (!RNA_struct_property_is_set(op->ptr, "text")) { /* always set from keymap XXX */
|
||||
@@ -405,6 +436,19 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve
|
||||
if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) {
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
/* most IME shortcut for switch IME, fullwidth/halfwidth and so on */
|
||||
if (WM_event_is_ime_switch(event))
|
||||
{
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
/* composition begin and enter modal */
|
||||
else if (event->type == WM_IME_COMPOSITE_START) {
|
||||
SpaceConsole *sc = CTX_wm_space_console(C);
|
||||
WM_event_add_modal_handler(C, op);
|
||||
/* clear seletion */
|
||||
sc->sel_end = sc->sel_start;
|
||||
return console_insert_modal(C, op, event);
|
||||
}
|
||||
else {
|
||||
char str[BLI_UTF8_MAX + 1];
|
||||
size_t len;
|
||||
@@ -436,6 +480,7 @@ void CONSOLE_OT_insert(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = console_insert_exec;
|
||||
ot->invoke = console_insert_invoke;
|
||||
ot->modal = console_insert_modal;
|
||||
ot->poll = ED_operator_console_active;
|
||||
|
||||
/* properties */
|
||||
@@ -1017,10 +1062,10 @@ typedef struct SetConsoleCursor {
|
||||
} SetConsoleCursor;
|
||||
|
||||
// TODO, cursor placement without selection
|
||||
static void console_cursor_set_to_pos(SpaceConsole *sc, ARegion *ar, SetConsoleCursor *scu, int mval[2], int UNUSED(sel))
|
||||
static void console_cursor_set_to_pos(SpaceConsole *sc, ARegion *ar, SetConsoleCursor *scu, wmImeData *ime_data, int mval[2], int UNUSED(sel))
|
||||
{
|
||||
int pos;
|
||||
pos = console_char_pick(sc, ar, mval);
|
||||
pos = console_char_pick(sc, ar, ime_data, mval);
|
||||
|
||||
if (scu->sel_init == INT_MAX) {
|
||||
scu->sel_init = pos;
|
||||
@@ -1043,6 +1088,7 @@ static void console_cursor_set_to_pos(SpaceConsole *sc, ARegion *ar, SetConsoleC
|
||||
|
||||
static void console_modal_select_apply(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
SpaceConsole *sc = CTX_wm_space_console(C);
|
||||
ARegion *ar = CTX_wm_region(C);
|
||||
SetConsoleCursor *scu = op->customdata;
|
||||
@@ -1055,7 +1101,7 @@ static void console_modal_select_apply(bContext *C, wmOperator *op, const wmEven
|
||||
sel_prev[0] = sc->sel_start;
|
||||
sel_prev[1] = sc->sel_end;
|
||||
|
||||
console_cursor_set_to_pos(sc, ar, scu, mval, true);
|
||||
console_cursor_set_to_pos(sc, ar, scu, win->ime_data, mval, true);
|
||||
|
||||
/* only redraw if the selection changed */
|
||||
if (sel_prev[0] != sc->sel_start || sel_prev[1] != sc->sel_end) {
|
||||
|
@@ -48,7 +48,9 @@
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
#include "wm_window.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
#include "UI_view2d.h"
|
||||
|
||||
@@ -167,6 +169,12 @@ static void console_cursor(wmWindow *win, ScrArea *sa, ARegion *ar)
|
||||
if (st->text && BLI_rcti_isect_pt(&st->txtbar, win->eventstate->x - ar->winrct.xmin, st->txtbar.ymin)) {
|
||||
wmcursor = CURSOR_STD;
|
||||
}
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* XXX find a better place */
|
||||
else {
|
||||
wm_window_IME_begin(win, ar->winrct.xmin, ar->winrct.ymin, 0, 0, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
WM_cursor_set(win, wmcursor);
|
||||
}
|
||||
@@ -225,6 +233,13 @@ static void console_main_area_draw(const bContext *C, ARegion *ar)
|
||||
SpaceConsole *sc = CTX_wm_space_console(C);
|
||||
View2D *v2d = &ar->v2d;
|
||||
View2DScrollers *scrollers;
|
||||
#ifdef WITH_INPUT_IME
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
wmImeData *ime_data = win->ime_data;
|
||||
bool is_ime_active = ime_data &&
|
||||
ime_data->composite_len &&
|
||||
BLI_rcti_isect_pt_v(&ar->winrct, &win->eventstate->x);
|
||||
#endif
|
||||
|
||||
if (BLI_listbase_is_empty(&sc->scrollback))
|
||||
WM_operator_name_call((bContext *)C, "CONSOLE_OT_banner", WM_OP_EXEC_DEFAULT, NULL);
|
||||
@@ -239,7 +254,33 @@ static void console_main_area_draw(const bContext *C, ARegion *ar)
|
||||
/* data... */
|
||||
|
||||
console_history_verify(C); /* make sure we have some command line */
|
||||
console_textview_main(sc, ar);
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
if (is_ime_active) {
|
||||
/* get cursor position from console_textview_main and repositon ime window */
|
||||
ConsoleLine *line = (ConsoleLine *)sc->history.last;
|
||||
ime_data->cursor_pos_text = line->cursor + strlen(sc->prompt);
|
||||
}
|
||||
else {
|
||||
/* delete ImeData if it didn't exist previously */
|
||||
ime_data = NULL;
|
||||
}
|
||||
|
||||
console_textview_main(sc, ar, ime_data);
|
||||
|
||||
if (ime_data && is_ime_active) {
|
||||
int x = ime_data->cursor_xy[0];
|
||||
int y = ime_data->cursor_xy[1];
|
||||
|
||||
ui_region_to_window(ar, &x, &y);
|
||||
|
||||
wm_window_IME_begin(win, x + 5, y, 0, 0, false);
|
||||
|
||||
ime_data->cursor_xy[0] = ime_data->cursor_xy[1] = 0;
|
||||
}
|
||||
#else
|
||||
console_textview_main(sc, ar, NULL);
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
/* reset view matrix */
|
||||
UI_view2d_view_restore(C);
|
||||
@@ -354,6 +395,8 @@ static void console_keymap(struct wmKeyConfig *keyconf)
|
||||
WM_keymap_add_item(keymap, "CONSOLE_OT_indent", TABKEY, KM_PRESS, 0, 0);
|
||||
WM_keymap_add_item(keymap, "CONSOLE_OT_unindent", TABKEY, KM_PRESS, KM_SHIFT, 0);
|
||||
|
||||
WM_keymap_add_item(keymap, "CONSOLE_OT_insert", WM_IME_COMPOSITE_START, KM_ANY, KM_ANY, 0);
|
||||
|
||||
WM_keymap_add_item(keymap, "CONSOLE_OT_insert", KM_TEXTINPUT, KM_ANY, KM_ANY, 0); // last!
|
||||
}
|
||||
|
||||
|
@@ -54,6 +54,12 @@ if(WITH_INTERNATIONAL)
|
||||
add_definitions(-DWITH_INTERNATIONAL)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(WITH_INPUT_IME)
|
||||
add_definitions(-DWITH_INPUT_IME)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_definitions(${GL_DEFINITIONS})
|
||||
|
||||
blender_add_lib(bf_editor_space_info "${SRC}" "${INC}" "${INC_SYS}")
|
||||
|
@@ -191,7 +191,7 @@ static int report_textview_line_get(struct TextViewContext *tvc, const char **li
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int report_textview_line_color(struct TextViewContext *tvc, unsigned char fg[3], unsigned char bg[3])
|
||||
static int report_textview_line_color(struct TextViewContext *tvc, struct wmImeData *ime_data, unsigned char fg[3], unsigned char bg[3])
|
||||
{
|
||||
Report *report = (Report *)tvc->iter;
|
||||
info_report_color(fg, bg, report, tvc->iter_tmp % 2);
|
||||
@@ -232,7 +232,7 @@ static int report_textview_line_color(struct TextViewContext *tvc, unsigned char
|
||||
|
||||
#undef USE_INFO_NEWLINE
|
||||
|
||||
static int info_textview_main__internal(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports,
|
||||
static int info_textview_main__internal(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports, struct wmImeData *ime_data,
|
||||
int draw, int mval[2], void **mouse_pick, int *pos_pick)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -259,12 +259,12 @@ static int info_textview_main__internal(struct SpaceInfo *sinfo, ARegion *ar, Re
|
||||
tvc.ymax = v2d->cur.ymax;
|
||||
tvc.winx = ar->winx - V2D_SCROLL_WIDTH;
|
||||
|
||||
ret = textview_draw(&tvc, draw, mval, mouse_pick, pos_pick);
|
||||
ret = textview_draw(&tvc, ime_data, draw, mval, mouse_pick, pos_pick);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *info_text_pick(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports, int mouse_y)
|
||||
void *info_text_pick(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports, struct wmImeData *ime_data, int mouse_y)
|
||||
{
|
||||
void *mouse_pick = NULL;
|
||||
int mval[2];
|
||||
@@ -272,19 +272,19 @@ void *info_text_pick(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports,
|
||||
mval[0] = 0;
|
||||
mval[1] = mouse_y;
|
||||
|
||||
info_textview_main__internal(sinfo, ar, reports, 0, mval, &mouse_pick, NULL);
|
||||
info_textview_main__internal(sinfo, ar, reports, ime_data, 0, mval, &mouse_pick, NULL);
|
||||
return (void *)mouse_pick;
|
||||
}
|
||||
|
||||
|
||||
int info_textview_height(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports)
|
||||
int info_textview_height(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports, struct wmImeData *ime_data)
|
||||
{
|
||||
int mval[2] = {INT_MAX, INT_MAX};
|
||||
return info_textview_main__internal(sinfo, ar, reports, 0, mval, NULL, NULL);
|
||||
return info_textview_main__internal(sinfo, ar, reports, ime_data, 0, mval, NULL, NULL);
|
||||
}
|
||||
|
||||
void info_textview_main(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports)
|
||||
void info_textview_main(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports, struct wmImeData *ime_data)
|
||||
{
|
||||
int mval[2] = {INT_MAX, INT_MAX};
|
||||
info_textview_main__internal(sinfo, ar, reports, 1, mval, NULL, NULL);
|
||||
info_textview_main__internal(sinfo, ar, reports, ime_data, 1, mval, NULL, NULL);
|
||||
}
|
||||
|
@@ -53,9 +53,9 @@ void FILE_OT_find_missing_files(struct wmOperatorType *ot);
|
||||
void INFO_OT_reports_display_update(struct wmOperatorType *ot);
|
||||
|
||||
/* info_draw.c */
|
||||
void *info_text_pick(struct SpaceInfo *sinfo, struct ARegion *ar, ReportList *reports, int mouse_y);
|
||||
int info_textview_height(struct SpaceInfo *sinfo, struct ARegion *ar, struct ReportList *reports);
|
||||
void info_textview_main(struct SpaceInfo *sinfo, struct ARegion *ar, struct ReportList *reports);
|
||||
void *info_text_pick(struct SpaceInfo *sinfo, struct ARegion *ar, ReportList *reports, struct wmImeData *ime_data, int mouse_y);
|
||||
int info_textview_height(struct SpaceInfo *sinfo, struct ARegion *ar, struct ReportList *reports, struct wmImeData *ime_data);
|
||||
void info_textview_main(struct SpaceInfo *sinfo, struct ARegion *ar, struct ReportList *reports, struct wmImeData *ime_data);
|
||||
|
||||
/* info_report.c */
|
||||
int info_report_mask(struct SpaceInfo *sinfo);
|
||||
|
@@ -128,15 +128,16 @@ static int select_report_pick_exec(bContext *C, wmOperator *op)
|
||||
|
||||
static int select_report_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
SpaceInfo *sinfo = CTX_wm_space_info(C);
|
||||
ARegion *ar = CTX_wm_region(C);
|
||||
ReportList *reports = CTX_wm_reports(C);
|
||||
Report *report;
|
||||
|
||||
/* uses opengl */
|
||||
wmSubWindowSet(CTX_wm_window(C), ar->swinid);
|
||||
wmSubWindowSet(win, ar->swinid);
|
||||
|
||||
report = info_text_pick(sinfo, ar, reports, event->mval[1]);
|
||||
report = info_text_pick(sinfo, ar, reports, win->ime_data, event->mval[1]);
|
||||
|
||||
RNA_int_set(op->ptr, "report_index", BLI_findindex(&reports->list, report));
|
||||
|
||||
@@ -218,6 +219,7 @@ void INFO_OT_select_all_toggle(wmOperatorType *ot)
|
||||
/* borderselect operator */
|
||||
static int borderselect_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
SpaceInfo *sinfo = CTX_wm_space_info(C);
|
||||
ARegion *ar = CTX_wm_region(C);
|
||||
ReportList *reports = CTX_wm_reports(C);
|
||||
@@ -245,8 +247,8 @@ static int borderselect_exec(bContext *C, wmOperator *op)
|
||||
}
|
||||
}
|
||||
|
||||
report_min = info_text_pick(sinfo, ar, reports, rect.ymax);
|
||||
report_max = info_text_pick(sinfo, ar, reports, rect.ymin);
|
||||
report_min = info_text_pick(sinfo, ar, reports, win->ime_data, rect.ymax);
|
||||
report_max = info_text_pick(sinfo, ar, reports, win->ime_data, rect.ymin);
|
||||
|
||||
/* get the first report if none found */
|
||||
if (report_min == NULL) {
|
||||
|
@@ -139,10 +139,11 @@ static void info_main_area_init(wmWindowManager *wm, ARegion *ar)
|
||||
|
||||
static void info_textview_update_rect(const bContext *C, ARegion *ar)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
SpaceInfo *sinfo = CTX_wm_space_info(C);
|
||||
View2D *v2d = &ar->v2d;
|
||||
|
||||
UI_view2d_totRect_set(v2d, ar->winx - 1, info_textview_height(sinfo, ar, CTX_wm_reports(C)));
|
||||
UI_view2d_totRect_set(v2d, ar->winx - 1, info_textview_height(sinfo, ar, CTX_wm_reports(C), win->ime_data));
|
||||
}
|
||||
|
||||
static void info_main_area_draw(const bContext *C, ARegion *ar)
|
||||
@@ -151,6 +152,7 @@ static void info_main_area_draw(const bContext *C, ARegion *ar)
|
||||
SpaceInfo *sinfo = CTX_wm_space_info(C);
|
||||
View2D *v2d = &ar->v2d;
|
||||
View2DScrollers *scrollers;
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
/* clear and setup matrix */
|
||||
UI_ThemeClearColor(TH_BACK);
|
||||
@@ -165,7 +167,7 @@ static void info_main_area_draw(const bContext *C, ARegion *ar)
|
||||
/* worlks best with no view2d matrix set */
|
||||
UI_view2d_view_ortho(v2d);
|
||||
|
||||
info_textview_main(sinfo, ar, CTX_wm_reports(C));
|
||||
info_textview_main(sinfo, ar, CTX_wm_reports(C), win->ime_data);
|
||||
|
||||
/* reset view matrix */
|
||||
UI_view2d_view_restore(C);
|
||||
|
@@ -37,6 +37,7 @@
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "BIF_gl.h"
|
||||
@@ -45,6 +46,10 @@
|
||||
|
||||
#include "textview.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
|
||||
static void console_font_begin(TextViewContext *sc)
|
||||
{
|
||||
/* 0.875 is based on: 16 pixels lines get 14 pixel text */
|
||||
@@ -88,6 +93,25 @@ static void console_draw_sel(const char *str, const int sel[2], const int xy[2],
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
static void console_draw_underline(const char *str, const int sel[2], const int xy[2], const int str_len_draw,
|
||||
const int cwidth, const int height, const unsigned char bg_sel[4])
|
||||
{
|
||||
if (sel[0] <= str_len_draw && sel[1] >= 0) {
|
||||
const int col_xmin = txt_utf8_offset_to_column(str, max_ii(sel[0], 0));
|
||||
const int col_xmax = txt_utf8_offset_to_column(str, min_ii(sel[1], str_len_draw));
|
||||
|
||||
const int x = xy[0] + (cwidth * col_xmin);
|
||||
const int y = xy[1];
|
||||
const int width = (xy[0] + (cwidth * col_xmax)) - x;
|
||||
|
||||
glColor3ubv(bg_sel);
|
||||
|
||||
UI_text_draw_underline(x, y, width, height);
|
||||
}
|
||||
}
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
/* warning: allocated memory for 'offsets' must be freed by caller */
|
||||
static int console_wrap_offsets(const char *str, int len, int width, int *lines, int **offsets)
|
||||
{
|
||||
@@ -117,13 +141,33 @@ static int console_wrap_offsets(const char *str, int len, int width, int *lines,
|
||||
/* return 0 if the last line is off the screen
|
||||
* should be able to use this for any string type */
|
||||
|
||||
static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str_len,
|
||||
static int console_draw_string(ConsoleDrawContext *cdc, wmImeData *ime_data, const char *str, int str_len,
|
||||
const unsigned char fg[3], const unsigned char bg[3], const unsigned char bg_sel[4])
|
||||
{
|
||||
int tot_lines; /* total number of lines for wrapping */
|
||||
int *offsets; /* offsets of line beginnings for wrapping */
|
||||
int y_next;
|
||||
const int mono = blf_mono_font;
|
||||
const int mono = blf_mono_font;;
|
||||
int cursor;
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
if (ime_data && !(ime_data->cursor_pos_text || ime_data->cursor_xy)) {
|
||||
ime_data = NULL;
|
||||
}
|
||||
|
||||
if (ime_data && ime_data->cursor_xy && ime_data->composite_len) {
|
||||
size_t slen;
|
||||
char buf[1024]; /* should be enough */
|
||||
cursor = ime_data->cursor_pos_text;
|
||||
|
||||
/* insert composite str */
|
||||
slen = ime_data->composite_len;
|
||||
BLI_snprintf(buf, slen + str_len + 1, "%s%s%s", str, ime_data->composite, str + cursor);
|
||||
|
||||
str = buf;
|
||||
str_len += slen;
|
||||
}
|
||||
#endif
|
||||
|
||||
str_len = console_wrap_offsets(str, str_len, cdc->console_width, &tot_lines, &offsets);
|
||||
y_next = cdc->xy[1] + cdc->lheight * tot_lines;
|
||||
@@ -173,6 +217,7 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str
|
||||
size_t len = str_len - initial_offset;
|
||||
const char *s = str + initial_offset;
|
||||
int i;
|
||||
int cursors[2];
|
||||
|
||||
int sel_orig[2];
|
||||
copy_v2_v2_int(sel_orig, cdc->sel);
|
||||
@@ -199,6 +244,25 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str
|
||||
glColor3ubv(fg);
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
cursors[0] = cursor;
|
||||
|
||||
/* draw IME composite underline */
|
||||
if (ime_data && ime_data->composite_len) {
|
||||
cursors[0] -= initial_offset;
|
||||
cursors[1] = cursors[0] + ime_data->composite_len;
|
||||
console_draw_underline(s, cursors, cdc->xy, len, cdc->cwidth, 1, fg);
|
||||
|
||||
/* draw the thicker line */
|
||||
if (ime_data->target_start != -1 && ime_data->target_end != -1) {
|
||||
int isel[2];
|
||||
isel[0] = cursors[0] + ime_data->target_start;
|
||||
isel[1] = cursors[0] + ime_data->target_end;
|
||||
console_draw_underline(s, isel, cdc->xy, len, cdc->cwidth, 2, fg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
cdc->xy[1] += cdc->lheight;
|
||||
|
||||
for (i = tot_lines - 1; i > 0; i--) {
|
||||
@@ -215,6 +279,25 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str
|
||||
glColor3ubv(fg);
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* draw IME composite underline */
|
||||
if (ime_data &&ime_data->composite_len) {
|
||||
cursors[0] += len;
|
||||
cursors[1] = cursors[0] + ime_data->composite_len;
|
||||
console_draw_underline(s, cursors, cdc->xy, len, cdc->cwidth, 1, fg);
|
||||
|
||||
/* draw the thicker line */
|
||||
if (ime_data->target_start != -1 && ime_data->target_end != -1) {
|
||||
int isel[2];
|
||||
isel[0] = cursors[0] + ime_data->target_start;
|
||||
isel[1] = cursors[0] + ime_data->target_end;
|
||||
console_draw_underline(s, isel, cdc->xy, len, cdc->cwidth, 2, fg);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)cursor; (void)cursors;
|
||||
#endif
|
||||
|
||||
cdc->xy[1] += cdc->lheight;
|
||||
|
||||
/* check if were out of view bounds */
|
||||
@@ -250,6 +333,24 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str
|
||||
console_step_sel(cdc, -(str_len + 1));
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* draw IME composite underline */
|
||||
if (ime_data &&ime_data->composite_len) {
|
||||
int isel[2];
|
||||
|
||||
isel[0] = cursor;
|
||||
isel[1] = cursor + ime_data->composite_len;
|
||||
console_draw_underline(str, isel, cdc->xy, str_len, cdc->cwidth, 1, fg);
|
||||
|
||||
/* draw the thicker line */
|
||||
if (ime_data->target_start != -1 && ime_data->target_end != -1) {
|
||||
isel[0] = cursor + ime_data->target_start;
|
||||
isel[1] = cursor + ime_data->target_end;
|
||||
console_draw_underline(str, isel, cdc->xy, str_len, cdc->cwidth, 2, fg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
cdc->xy[1] += cdc->lheight;
|
||||
|
||||
if (cdc->xy[1] > cdc->ymax) {
|
||||
@@ -264,7 +365,7 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str
|
||||
|
||||
#define CONSOLE_DRAW_MARGIN 4
|
||||
|
||||
int textview_draw(TextViewContext *tvc, const int draw, int mval[2], void **mouse_pick, int *pos_pick)
|
||||
int textview_draw(TextViewContext *tvc, wmImeData *ime_data, const int draw, int mval[2], void **mouse_pick, int *pos_pick)
|
||||
{
|
||||
ConsoleDrawContext cdc = {0};
|
||||
|
||||
@@ -308,6 +409,7 @@ int textview_draw(TextViewContext *tvc, const int draw, int mval[2], void **mous
|
||||
tvc->console_width = cdc.console_width;
|
||||
tvc->iter_index = 0;
|
||||
|
||||
|
||||
if (tvc->sel_start != tvc->sel_end) {
|
||||
sel[0] = tvc->sel_start;
|
||||
sel[1] = tvc->sel_end;
|
||||
@@ -328,11 +430,17 @@ int textview_draw(TextViewContext *tvc, const int draw, int mval[2], void **mous
|
||||
y_prev = xy[1];
|
||||
|
||||
if (draw)
|
||||
color_flag = tvc->line_color(tvc, fg, bg);
|
||||
color_flag = tvc->line_color(tvc, ime_data, fg, bg);
|
||||
|
||||
tvc->line_get(tvc, &ext_line, &ext_len);
|
||||
|
||||
if (!console_draw_string(&cdc, ext_line, ext_len,
|
||||
#ifdef WITH_INPUT_IME
|
||||
if (ime_data && tvc->iter_index) {
|
||||
ime_data = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!console_draw_string(&cdc, ime_data, ext_line, ext_len,
|
||||
(color_flag & TVC_LINE_FG) ? fg : NULL,
|
||||
(color_flag & TVC_LINE_BG) ? bg : NULL,
|
||||
bg_sel))
|
||||
|
@@ -47,17 +47,16 @@ typedef struct TextViewContext {
|
||||
/* iterator */
|
||||
int (*step)(struct TextViewContext *tvc);
|
||||
int (*line_get)(struct TextViewContext *tvc, const char **, int *);
|
||||
int (*line_color)(struct TextViewContext *tvc, unsigned char fg[3], unsigned char bg[3]);
|
||||
int (*line_color)(struct TextViewContext *tvc, struct wmImeData *ime_data, unsigned char fg[3], unsigned char bg[3]);
|
||||
void (*const_colors)(struct TextViewContext *tvc, unsigned char bg_sel[4]); /* constant theme colors */
|
||||
void *iter;
|
||||
int iter_index;
|
||||
int iter_char; /* char intex, used for multi-line report display */
|
||||
int iter_char_next; /* same as above, next \n */
|
||||
int iter_tmp; /* internal iterator use */
|
||||
|
||||
} TextViewContext;
|
||||
|
||||
int textview_draw(struct TextViewContext *tvc, const int draw, int mval[2], void **mouse_pick, int *pos_pick);
|
||||
int textview_draw(struct TextViewContext *tvc, struct wmImeData *ime_data, const int draw, int mval[2], void **mouse_pick, int *pos_pick);
|
||||
|
||||
#define TVC_LINE_FG (1<<0)
|
||||
#define TVC_LINE_BG (1<<1)
|
||||
|
@@ -63,4 +63,10 @@ if(WITH_INTERNATIONAL)
|
||||
add_definitions(-DWITH_INTERNATIONAL)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
if(WITH_INPUT_IME)
|
||||
add_definitions(-DWITH_INPUT_IME)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_editor_space_text "${SRC}" "${INC}" "${INC_SYS}")
|
||||
|
@@ -48,6 +48,7 @@
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
#include "wm_window.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
@@ -380,6 +381,8 @@ static void text_keymap(struct wmKeyConfig *keyconf)
|
||||
WM_keymap_add_item(keymap, "TEXT_OT_autocomplete", SPACEKEY, KM_PRESS, KM_CTRL, 0);
|
||||
|
||||
WM_keymap_add_item(keymap, "TEXT_OT_line_number", KM_TEXTINPUT, KM_ANY, KM_ANY, 0);
|
||||
|
||||
WM_keymap_add_item(keymap, "TEXT_OT_insert", WM_IME_COMPOSITE_START, KM_ANY, KM_ANY, 0);
|
||||
WM_keymap_add_item(keymap, "TEXT_OT_insert", KM_TEXTINPUT, KM_ANY, KM_ANY, 0); // last!
|
||||
}
|
||||
|
||||
@@ -427,6 +430,13 @@ static void text_main_area_draw(const bContext *C, ARegion *ar)
|
||||
{
|
||||
/* draw entirely, view changes should be handled here */
|
||||
SpaceText *st = CTX_wm_space_text(C);
|
||||
#ifdef WITH_INPUT_IME
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
wmImeData *ime_data = win->ime_data;
|
||||
bool is_ime_active = ime_data &&
|
||||
ime_data->composite_len &&
|
||||
BLI_rcti_isect_pt_v(&ar->winrct, &win->eventstate->x);
|
||||
#endif
|
||||
//View2D *v2d = &ar->v2d;
|
||||
|
||||
/* clear and setup matrix */
|
||||
@@ -436,8 +446,28 @@ static void text_main_area_draw(const bContext *C, ARegion *ar)
|
||||
// UI_view2d_view_ortho(v2d);
|
||||
|
||||
/* data... */
|
||||
draw_text_main(st, ar);
|
||||
|
||||
/* get cursor position from draw_text_main and repositon ime window */
|
||||
#ifdef WITH_INPUT_IME
|
||||
if (!is_ime_active) {
|
||||
ime_data = NULL;
|
||||
}
|
||||
draw_text_main(win, st, ar);
|
||||
|
||||
if (ime_data && is_ime_active) {
|
||||
int x = ime_data->cursor_xy[0];
|
||||
int y = ime_data->cursor_xy[1];
|
||||
|
||||
ui_region_to_window(ar, &x, &y);
|
||||
|
||||
wm_window_IME_begin(win, x + 5, y, 0, 0, false);
|
||||
|
||||
ime_data->cursor_xy[0] = ime_data->cursor_xy[1] = 0;
|
||||
}
|
||||
#else
|
||||
draw_text_main(win, st, ar);
|
||||
#endif
|
||||
|
||||
/* reset view matrix */
|
||||
// UI_view2d_view_restore(C);
|
||||
|
||||
@@ -453,6 +483,13 @@ static void text_cursor(wmWindow *win, ScrArea *sa, ARegion *ar)
|
||||
wmcursor = CURSOR_STD;
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* enable IME if the text region has text which can be edited */
|
||||
if (st->text && !st->text->id.lib) {
|
||||
wm_window_IME_begin(win, ar->winrct.xmin, ar->winrct.ymin, 0, 0, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
WM_cursor_set(win, wmcursor);
|
||||
}
|
||||
|
||||
|
@@ -34,10 +34,12 @@
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "DNA_text_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_suggestions.h"
|
||||
@@ -53,6 +55,8 @@
|
||||
#include "text_intern.h"
|
||||
#include "text_format.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
|
||||
/******************** text font drawing ******************/
|
||||
// XXX, fixme
|
||||
#define mono blf_mono_font
|
||||
@@ -412,10 +416,21 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w
|
||||
|
||||
/* Draw the visible portion of text on the overshot line */
|
||||
for (a = fstart, ma = mstart; ma < mend; a++, ma += BLI_str_utf8_size_safe(str + ma)) {
|
||||
int len;
|
||||
if (use_syntax) {
|
||||
if (fmt_prev != format[a]) format_draw_color(fmt_prev = format[a]);
|
||||
}
|
||||
x += text_font_draw_character_utf8(st, x, y, str + ma);
|
||||
len = text_font_draw_character_utf8(st, x, y, str + ma);
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* draw underline */
|
||||
if (format && format[a] == FMT_TYPE_ULINE)
|
||||
UI_text_draw_underline(x, y, len, 1);
|
||||
else
|
||||
if (format && format[a] == FMT_TYPE_TULINE)
|
||||
UI_text_draw_underline(x, y, len, 2);
|
||||
x += len;
|
||||
#endif
|
||||
fpos++;
|
||||
}
|
||||
y -= st->lheight_dpi + TXT_LINE_SPACING;
|
||||
@@ -435,11 +450,22 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w
|
||||
|
||||
/* Draw the remaining text */
|
||||
for (a = fstart, ma = mstart; str[ma] && y > clip_min_y; a++, ma += BLI_str_utf8_size_safe(str + ma)) {
|
||||
int len;
|
||||
if (use_syntax) {
|
||||
if (fmt_prev != format[a]) format_draw_color(fmt_prev = format[a]);
|
||||
}
|
||||
|
||||
x += text_font_draw_character_utf8(st, x, y, str + ma);
|
||||
len = text_font_draw_character_utf8(st, x, y, str + ma);
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* draw underline */
|
||||
if (format && format[a] == FMT_TYPE_ULINE)
|
||||
UI_text_draw_underline(x, y, len, 1);
|
||||
else
|
||||
if (format && format[a] == FMT_TYPE_TULINE)
|
||||
UI_text_draw_underline(x, y, len, 2);
|
||||
x += len;
|
||||
#endif
|
||||
}
|
||||
|
||||
flatten_string_free(&fs);
|
||||
@@ -447,7 +473,7 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w
|
||||
return lines;
|
||||
}
|
||||
|
||||
static void text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int x, int y, const char *format)
|
||||
static void text_draw(SpaceText *st, wmImeData *ime_data, char *str, int cshift, int maxwidth, int x, int y, const char *format)
|
||||
{
|
||||
const bool use_syntax = (st->showsyntax && format);
|
||||
FlattenString fs;
|
||||
@@ -482,13 +508,20 @@ static void text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int x,
|
||||
|
||||
x += st->cwidth * padding;
|
||||
|
||||
if (use_syntax) {
|
||||
int a, str_shift = 0;
|
||||
if ((use_syntax || (ime_data && ime_data->composite_len)) && format) {
|
||||
int a, str_shift = 0, len;
|
||||
char fmt_prev = 0xff;
|
||||
|
||||
for (a = 0; a < amount; a++) {
|
||||
if (format[a] != fmt_prev) format_draw_color(fmt_prev = format[a]);
|
||||
x += text_font_draw_character_utf8(st, x, y, in + str_shift);
|
||||
len = text_font_draw_character_utf8(st, x, y, in + str_shift);
|
||||
/* draw underline */
|
||||
if (fmt_prev == FMT_TYPE_ULINE)
|
||||
UI_text_draw_underline(x, y, len, 1);
|
||||
else
|
||||
if (fmt_prev == FMT_TYPE_TULINE)
|
||||
UI_text_draw_underline(x, y, len, 2);
|
||||
x += len;
|
||||
str_shift += BLI_str_utf8_size_safe(in + str_shift);
|
||||
}
|
||||
}
|
||||
@@ -908,7 +941,7 @@ static void draw_textscroll(SpaceText *st, rcti *scroll, rcti *back)
|
||||
|
||||
/*********************** draw documentation *******************************/
|
||||
|
||||
static void draw_documentation(SpaceText *st, ARegion *ar)
|
||||
static void draw_documentation(SpaceText *st, ARegion *ar, wmImeData *ime_data)
|
||||
{
|
||||
TextLine *tmp;
|
||||
char *docs, buf[DOC_WIDTH + 1], *p;
|
||||
@@ -971,7 +1004,7 @@ static void draw_documentation(SpaceText *st, ARegion *ar)
|
||||
buf[i] = '\0';
|
||||
if (lines >= 0) {
|
||||
y -= st->lheight_dpi;
|
||||
text_draw(st, buf, 0, 0, x + 4, y - 3, NULL);
|
||||
text_draw(st, ime_data, buf, 0, 0, x + 4, y - 3, NULL);
|
||||
}
|
||||
i = 0; br = DOC_WIDTH; lines++;
|
||||
}
|
||||
@@ -980,7 +1013,7 @@ static void draw_documentation(SpaceText *st, ARegion *ar)
|
||||
buf[br] = '\0';
|
||||
if (lines >= 0) {
|
||||
y -= st->lheight_dpi;
|
||||
text_draw(st, buf, 0, 0, x + 4, y - 3, NULL);
|
||||
text_draw(st, ime_data, buf, 0, 0, x + 4, y - 3, NULL);
|
||||
}
|
||||
p -= i - br - 1; /* Rewind pointer to last break */
|
||||
i = 0; br = DOC_WIDTH; lines++;
|
||||
@@ -990,13 +1023,13 @@ static void draw_documentation(SpaceText *st, ARegion *ar)
|
||||
|
||||
if (0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) {
|
||||
// XXX doc_scroll--;
|
||||
draw_documentation(st, ar);
|
||||
draw_documentation(st, ar, ime_data);
|
||||
}
|
||||
}
|
||||
|
||||
/*********************** draw suggestion list *******************************/
|
||||
|
||||
static void draw_suggestion_list(SpaceText *st, ARegion *ar)
|
||||
static void draw_suggestion_list(SpaceText *st, ARegion *ar, wmImeData *ime_data)
|
||||
{
|
||||
SuggItem *item, *first, *last, *sel;
|
||||
char str[SUGG_LIST_WIDTH * BLI_UTF8_MAX + 1];
|
||||
@@ -1061,7 +1094,7 @@ static void draw_suggestion_list(SpaceText *st, ARegion *ar)
|
||||
}
|
||||
|
||||
format_draw_color(item->type);
|
||||
text_draw(st, str, 0, 0, x + margin_x, y - 1, NULL);
|
||||
text_draw(st, ime_data, str, 0, 0, x + margin_x, y - 1, NULL);
|
||||
|
||||
if (item == last) break;
|
||||
}
|
||||
@@ -1069,7 +1102,7 @@ static void draw_suggestion_list(SpaceText *st, ARegion *ar)
|
||||
|
||||
/*********************** draw cursor ************************/
|
||||
|
||||
static void draw_cursor(SpaceText *st, ARegion *ar)
|
||||
static void draw_cursor(SpaceText *st, ARegion *ar, wmImeData *ime_data)
|
||||
{
|
||||
Text *text = st->text;
|
||||
int vcurl, vcurc, vsell, vselc, hidden = 0;
|
||||
@@ -1182,6 +1215,13 @@ static void draw_cursor(SpaceText *st, ARegion *ar)
|
||||
else {
|
||||
UI_ThemeColor(TH_HILITE);
|
||||
glRecti(x - 1, y, x + 1, y - lheight);
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
if (ime_data && ime_data->composite_len && text->curl) {
|
||||
ime_data->cursor_xy[0] = x + 1;
|
||||
ime_data->cursor_xy[1] = y - lheight - 3;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1315,7 +1355,7 @@ static void draw_brackets(SpaceText *st, ARegion *ar)
|
||||
|
||||
/*********************** main area drawing *************************/
|
||||
|
||||
void draw_text_main(SpaceText *st, ARegion *ar)
|
||||
void draw_text_main(wmWindow *win, SpaceText *st, ARegion *ar)
|
||||
{
|
||||
Text *text = st->text;
|
||||
TextFormatType *tft;
|
||||
@@ -1327,6 +1367,14 @@ void draw_text_main(SpaceText *st, ARegion *ar)
|
||||
int margin_column_x;
|
||||
/* don't draw lines below this */
|
||||
const int clip_min_y = -(int)(st->lheight_dpi - 1);
|
||||
#ifdef WITH_INPUT_IME
|
||||
TextLine bak = {0};
|
||||
wmImeData *ime_data = win->ime_data;
|
||||
bool is_ime_active = ime_data &&
|
||||
ime_data->cursor_xy &&
|
||||
ime_data->composite_len &&
|
||||
BLI_rcti_isect_pt_v(&ar->winrct, &win->eventstate->x);
|
||||
#endif
|
||||
|
||||
/* if no text, nothing to do */
|
||||
if (!text)
|
||||
@@ -1335,6 +1383,57 @@ void draw_text_main(SpaceText *st, ARegion *ar)
|
||||
/* dpi controlled line height and font size */
|
||||
st->lheight_dpi = (U.widget_unit * st->lheight) / 20;
|
||||
st->viewlines = (st->lheight_dpi) ? (int)(ar->winy - clip_min_y) / (st->lheight_dpi + TXT_LINE_SPACING) : 0;
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* if is composing, backup and insert composition string */
|
||||
if (is_ime_active && text->curl) {
|
||||
int clen = ime_data->composite_len;
|
||||
int tlen = text->curl->len;
|
||||
int clen_utf8 = BLI_strnlen_utf8(ime_data->composite, clen);
|
||||
int tlen_utf8 = BLI_strnlen_utf8(text->curl->line, tlen);
|
||||
|
||||
int max_size = tlen + clen + 1;
|
||||
int max_size_utf8 = tlen_utf8 + clen_utf8 + 1;
|
||||
|
||||
int i = text->curc;
|
||||
|
||||
bak = *text->curl;
|
||||
tmp = text->curl;
|
||||
|
||||
/* XXX - would be good if we could avoid this as it mallocs every redraw
|
||||
* keep in mind we need to ensure virtually endless lines, so a limited array isn't a solution */
|
||||
tmp->line = MEM_mallocN(max_size, "text ime backup");
|
||||
tmp->format = MEM_mallocN(max_size_utf8, "text ime backup");
|
||||
|
||||
BLI_snprintf(tmp->line, max_size, "%s%s%s", bak.line, ime_data->composite, bak.line + i);
|
||||
|
||||
tmp->len += clen;
|
||||
|
||||
i = BLI_strnlen_utf8(bak.line, text->curc);
|
||||
if (bak.format) {
|
||||
memset(tmp->format + i, FMT_TYPE_ULINE, clen_utf8);
|
||||
BLI_snprintf(tmp->format, tlen_utf8 - i, "%s%s%s", bak.format, tmp->format + i, bak.format + i);
|
||||
}
|
||||
else {
|
||||
memset(tmp->format, FMT_TYPE_DEFAULT, clen_utf8 + tlen_utf8);
|
||||
memset(tmp->format + i, FMT_TYPE_ULINE, clen_utf8);
|
||||
}
|
||||
|
||||
/* set thicker line format */
|
||||
if (ime_data->target_start != -1 && ime_data->target_end != -1) {
|
||||
int target_start = BLI_strnlen_utf8(ime_data->composite, ime_data->target_start);
|
||||
int target_end = BLI_strnlen_utf8(ime_data->composite, ime_data->target_end);
|
||||
target_end -= target_start;
|
||||
memset(tmp->format + i + target_start, FMT_TYPE_TULINE, target_end);
|
||||
}
|
||||
|
||||
tmp->format[clen_utf8 + tlen_utf8] = '\0';
|
||||
|
||||
/* move the cursor */
|
||||
text->curc += ime_data->cursor_position;
|
||||
text->selc += ime_data->cursor_position;
|
||||
}
|
||||
#endif
|
||||
|
||||
text_update_drawcache(st, ar);
|
||||
|
||||
@@ -1393,7 +1492,7 @@ void draw_text_main(SpaceText *st, ARegion *ar)
|
||||
winx = ar->winx - TXT_SCROLL_WIDTH;
|
||||
|
||||
/* draw cursor */
|
||||
draw_cursor(st, ar);
|
||||
draw_cursor(st, ar, ime_data);
|
||||
|
||||
/* draw the text */
|
||||
UI_ThemeColor(TH_TEXT);
|
||||
@@ -1423,7 +1522,7 @@ void draw_text_main(SpaceText *st, ARegion *ar)
|
||||
}
|
||||
else {
|
||||
/* draw unwrapped text */
|
||||
text_draw(st, tmp->line, st->left, ar->winx / st->cwidth, x, y, tmp->format);
|
||||
text_draw(st, ime_data, tmp->line, st->left, ar->winx / st->cwidth, x, y, tmp->format);
|
||||
y -= st->lheight_dpi + TXT_LINE_SPACING;
|
||||
}
|
||||
|
||||
@@ -1447,10 +1546,21 @@ void draw_text_main(SpaceText *st, ARegion *ar)
|
||||
draw_brackets(st, ar);
|
||||
glTranslatef(GLA_PIXEL_OFS, GLA_PIXEL_OFS, 0.0f); /* XXX scroll requires exact pixel space */
|
||||
draw_textscroll(st, &scroll, &back);
|
||||
draw_documentation(st, ar);
|
||||
draw_suggestion_list(st, ar);
|
||||
draw_documentation(st, ar, ime_data);
|
||||
draw_suggestion_list(st, ar, ime_data);
|
||||
|
||||
text_font_end(st);
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
if (bak.line) {
|
||||
MEM_freeN(text->curl->line);
|
||||
MEM_freeN(text->curl->format);
|
||||
*text->curl = bak;
|
||||
/* recover the cursor */
|
||||
text->curc -= ime_data->cursor_position;
|
||||
text->selc -= ime_data->cursor_position;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/************************** update ***************************/
|
||||
|
@@ -93,6 +93,8 @@ enum {
|
||||
FMT_TYPE_RESERVED = 'r', /* Reserved keywords currently not in use, but still prohibited (OSL -> switch e.g.) */
|
||||
FMT_TYPE_KEYWORD = 'b', /* Built-in names (return, for, etc.) */
|
||||
FMT_TYPE_DEFAULT = 'q', /* Regular text (identifiers, etc.) */
|
||||
FMT_TYPE_ULINE = 'u', /* Regular text with underline, be used for composition string */
|
||||
FMT_TYPE_TULINE = 't', /* Regular text with thicker underline, be used for target string */
|
||||
};
|
||||
|
||||
TextFormatType *ED_text_format_get(Text *text);
|
||||
|
@@ -45,7 +45,7 @@ struct wmOperatorType;
|
||||
struct wmWindowManager;
|
||||
|
||||
/* text_draw.c */
|
||||
void draw_text_main(struct SpaceText *st, struct ARegion *ar);
|
||||
void draw_text_main(struct wmWindow *win, struct SpaceText *st, struct ARegion *ar);
|
||||
|
||||
void text_update_line_edited(struct TextLine *line);
|
||||
void text_update_edited(struct Text *text);
|
||||
|
@@ -2914,6 +2914,41 @@ static int text_insert_exec(bContext *C, wmOperator *op)
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int text_insert_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
#ifdef WITH_INPUT_IME
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
wmImeData *ime_data = win->ime_data;
|
||||
Text *text = CTX_data_edit_text(C);
|
||||
|
||||
/* composition complete
|
||||
* some Japanese IMEs return result without WM_IME_COMPOSITE_END event
|
||||
*/
|
||||
if (event->type == WM_IME_COMPOSITE_EVENT && ime_data->result_len) {
|
||||
const char *str = "";
|
||||
int ret;
|
||||
|
||||
if (ime_data->result) str = ime_data->result;
|
||||
RNA_string_set(op->ptr, "text", str);
|
||||
ret = text_insert_exec(C, op);
|
||||
if (ret == OPERATOR_CANCELLED)
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_TEXT, text);
|
||||
}
|
||||
|
||||
if (event->type == WM_IME_COMPOSITE_END)
|
||||
return OPERATOR_FINISHED;
|
||||
|
||||
if (event->type == WM_IME_COMPOSITE_EVENT) {
|
||||
/* only redraw, don't clean away format */
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_TEXT, text);
|
||||
}
|
||||
#else
|
||||
(void)C; (void)op; (void)event;
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
int ret;
|
||||
@@ -2927,6 +2962,20 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) {
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* most IME shortcut for switch IME, fullwidth/halfwidth and so on */
|
||||
if (WM_event_is_ime_switch(event))
|
||||
{
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
/* composition begin and enter modal */
|
||||
else if (event->type == WM_IME_COMPOSITE_START) {
|
||||
Text *text = CTX_data_edit_text(C);
|
||||
WM_event_add_modal_handler(C, op);
|
||||
txt_delete_selected(text);
|
||||
return text_insert_modal(C, op, event);
|
||||
}
|
||||
#endif /* WITH_INPUT_IME */
|
||||
else {
|
||||
char str[BLI_UTF8_MAX + 1];
|
||||
size_t len;
|
||||
@@ -2965,6 +3014,7 @@ void TEXT_OT_insert(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = text_insert_exec;
|
||||
ot->invoke = text_insert_invoke;
|
||||
ot->modal = text_insert_modal;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* properties */
|
||||
|
@@ -191,6 +191,8 @@ typedef struct wmWindow {
|
||||
short last_pie_event; /* exception to the above rule for nested pies, store last pie event for operators
|
||||
* that spawn a new pie right after destruction of last pie */
|
||||
|
||||
struct wmImeData *ime_data; /* Input Method Editor data - complex character input (esp. for asian character input) */
|
||||
|
||||
struct wmEvent *eventstate; /* storage for event system */
|
||||
|
||||
struct wmSubWindow *curswin; /* internal for wm_subwindow.c only */
|
||||
|
@@ -130,10 +130,14 @@ if(WITH_BUILDINFO)
|
||||
add_definitions(-DWITH_BUILDINFO)
|
||||
endif()
|
||||
|
||||
if(WIN322)
|
||||
if(WIN32)
|
||||
list(APPEND INC
|
||||
../../../intern/utfconv
|
||||
)
|
||||
|
||||
if(WITH_INPUT_IME)
|
||||
add_definitions(-DWITH_INPUT_IME)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_COMPOSITOR)
|
||||
|
@@ -462,6 +462,8 @@ void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4
|
||||
float WM_event_tablet_data(const struct wmEvent *event, int *pen_flip, float tilt[2]);
|
||||
bool WM_event_is_tablet(const struct wmEvent *event);
|
||||
|
||||
bool WM_event_is_ime_switch(const struct wmEvent *event);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -470,6 +470,23 @@ typedef struct wmTabletData {
|
||||
float Ytilt; /* as above */
|
||||
} wmTabletData;
|
||||
|
||||
/* similar to GHOST_TEventImeData */
|
||||
/* XXX - names, comments */
|
||||
typedef struct wmImeData {
|
||||
size_t result_len, composite_len;
|
||||
|
||||
char *result; /* utf8 encoding */
|
||||
char *composite; /* utf8 encoding */
|
||||
|
||||
int cursor_position; /* cursor position in the IME composition. */
|
||||
int target_start; /* position of the beginning of the selection */
|
||||
int target_end; /* position of the end of the selection */
|
||||
int cursor_xy[2]; /* text cursor position */
|
||||
int cursor_pos_text; /* cursor pos in text (console space only) */
|
||||
|
||||
bool is_ime_composite; /* for uiBut only */
|
||||
} wmImeData;
|
||||
|
||||
typedef enum { /* motion progress, for modal handlers */
|
||||
P_NOT_STARTED,
|
||||
P_STARTING, /* <-- */
|
||||
|
@@ -3373,6 +3373,33 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
case GHOST_kEventImeCompositionStart:
|
||||
{
|
||||
event.val = KM_PRESS;
|
||||
win->ime_data = customdata;
|
||||
win->ime_data->is_ime_composite = true;
|
||||
event.type = WM_IME_COMPOSITE_START;
|
||||
wm_event_add(win, &event);
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventImeComposition:
|
||||
{
|
||||
event.val = KM_PRESS;
|
||||
event.type = WM_IME_COMPOSITE_EVENT;
|
||||
wm_event_add(win, &event);
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventImeCompositionEnd:
|
||||
{
|
||||
event.val = KM_PRESS;
|
||||
win->ime_data->is_ime_composite = false;
|
||||
event.type = WM_IME_COMPOSITE_END;
|
||||
wm_event_add(win, &event);
|
||||
break;
|
||||
}
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -3479,5 +3506,12 @@ bool WM_event_is_tablet(const struct wmEvent *event)
|
||||
return (event->tablet_data) ? true : false;
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/* most os using ctrl/oskey + space to switch ime, avoid added space */
|
||||
bool WM_event_is_ime_switch(const struct wmEvent *event) {
|
||||
return event->val == KM_PRESS && event->type == SPACEKEY &&
|
||||
(event->ctrl || event->oskey || event->shift || event->alt);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
@@ -1524,3 +1524,20 @@ bool WM_window_is_fullscreen(wmWindow *win)
|
||||
return win->windowstate == GHOST_kWindowStateFullScreen;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
void wm_window_IME_begin(wmWindow *win, int x, int y, int w, int h, bool complete)
|
||||
{
|
||||
BLI_assert(win);
|
||||
|
||||
GHOST_BeginIME(win->ghostwin, x, win->sizey - y, w, h, complete);
|
||||
}
|
||||
|
||||
void wm_window_IME_end(wmWindow *win)
|
||||
{
|
||||
BLI_assert(win && win->ime_data);
|
||||
|
||||
GHOST_EndIME(win->ghostwin);
|
||||
win->ime_data = NULL;
|
||||
}
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
@@ -86,6 +86,13 @@ enum {
|
||||
* paint and drawing tools however will want to handle these. */
|
||||
INBETWEEN_MOUSEMOVE = 0x0011,
|
||||
|
||||
/* IME event, GHOST_kEventImeCompositionStart in ghost */
|
||||
WM_IME_COMPOSITE_START = 0x0014,
|
||||
/* IME event, GHOST_kEventImeComposition in ghost */
|
||||
WM_IME_COMPOSITE_EVENT = 0x0015,
|
||||
/* IME event, GHOST_kEventImeCompositionEnd in ghost */
|
||||
WM_IME_COMPOSITE_END = 0x0016,
|
||||
|
||||
/* *** Start of keyboard codes. *** */
|
||||
|
||||
/* standard keyboard.
|
||||
|
@@ -69,6 +69,10 @@ wmWindow *wm_window_copy (bContext *C, wmWindow *winorig);
|
||||
|
||||
void wm_window_testbreak (void);
|
||||
|
||||
/* *************** window IME api ************** */
|
||||
void wm_window_IME_begin (wmWindow *win, int x, int y, int w, int h, bool complete);
|
||||
void wm_window_IME_end (wmWindow *win);
|
||||
|
||||
/* *************** window operators ************** */
|
||||
int wm_window_duplicate_exec(bContext *C, struct wmOperator *op);
|
||||
int wm_window_fullscreen_toggle_exec(bContext *C, struct wmOperator *op);
|
||||
|
Reference in New Issue
Block a user