fix the strange behavior of Korean ime composition window.

Because Hangul is a combination character, WM_IME_STARTCOMPOSITION is only called once when the IME input is started, so the position of the composition window is strange.

Therefore, I solved the problem by controlling the state of characters combined using WM_IME_COMPOSITION and WM_IME_CHAR.

We have also improved to support other IME languages such as Chinese and Japanese.
This commit is contained in:
이지한 2019-08-01 15:24:46 +09:00
parent de9043e223
commit 129b83e68f
13 changed files with 152 additions and 12 deletions

View File

@ -85,6 +85,7 @@ namespace nana
virtual void mouse_dropfiles(graph_reference, const arg_dropfiles&);
virtual void focus(graph_reference, const arg_focus&);
virtual void key_ime(graph_reference, const arg_ime&);
virtual void key_press(graph_reference, const arg_keyboard&);
virtual void key_char(graph_reference, const arg_keyboard&);
virtual void key_release(graph_reference, const arg_keyboard&);
@ -140,6 +141,7 @@ namespace nana
void resized(const arg_resized&, const bool);
void move(const arg_move&, const bool);
void focus(const arg_focus&, const bool);
void key_ime(const arg_ime& arg, const bool bForce__EmitInternal);
void key_press(const arg_keyboard&, const bool);
void key_char(const arg_keyboard&, const bool);
void key_release(const arg_keyboard&, const bool);

View File

@ -34,6 +34,7 @@ namespace nana
unload, ///< A form is closed by clicking the X button, only works for root widget.
destroy, ///< A widget is about to be destroyed.
focus, ///< A widget's focus is changed.
key_ime,
key_press, ///< A keyboard is pressed on a focus widget.
key_char, ///< The focus widget received a character.
key_release, ///< A keyboard is released on a focus widget.

View File

@ -466,6 +466,19 @@ namespace nana
reason focus_reason; ///< determines how the widget receives keyboard focus, it is ignored when 'getting' is equal to false
};
struct arg_ime: public event_arg
{
enum class reason
{
composition,
result
};
::nana::window window_handle; ///< A handle to the event window
reason ime_reason;
std::wstring composition_string;
};
struct arg_keyboard : public event_arg
{
event_code evt_code; ///< it is event_code::key_press in current event

View File

@ -65,6 +65,7 @@ namespace nana
void mouse_up(graph_reference, const arg_mouse&) override;
void mouse_move(graph_reference, const arg_mouse&) override;
void mouse_wheel(graph_reference, const arg_wheel&) override;
void key_ime(graph_reference, const arg_ime&) override;
void key_press(graph_reference, const arg_keyboard&) override;
void key_char(graph_reference, const arg_keyboard&) override;
private:

View File

@ -98,6 +98,7 @@ namespace nana{ namespace widgets
void set_accept(std::function<bool(char_type)>);
void set_accept(accepts);
bool respond_ime(const arg_ime& arg);
bool respond_char(const arg_keyboard& arg);
bool respond_key(const arg_keyboard& arg);
@ -328,6 +329,8 @@ namespace nana{ namespace widgets
nana::upoint caret; //position of caret by text, it specifies the position of a new character
nana::upoint shift_begin_caret;
}points_;
size_t composition_size_ { 0 };
};
}//end namespace skeletons
}//end namespace widgets

View File

@ -66,6 +66,7 @@ namespace nana
void mouse_move(graph_reference, const arg_mouse&) override;
void mouse_up(graph_reference, const arg_mouse& arg) override;
void mouse_leave(graph_reference, const arg_mouse&) override;
void key_ime(graph_reference, const arg_ime& arg) override;
void key_press(graph_reference, const arg_keyboard&) override;
void key_char(graph_reference, const arg_keyboard&) override;
void resized(graph_reference, const arg_resized&) override;

View File

@ -83,6 +83,7 @@ namespace nana
void mouse_enter(graph_reference, const arg_mouse&) override;
void mouse_leave(graph_reference, const arg_mouse&) override;
void dbl_click(graph_reference, const arg_mouse&) override;
void key_ime(graph_reference, const arg_ime&) override;
void key_press(graph_reference, const arg_keyboard&)override;
void key_char(graph_reference, const arg_keyboard&) override;
void mouse_wheel(graph_reference, const arg_wheel&) override;

View File

@ -77,6 +77,9 @@ namespace detail
typedef BOOL (__stdcall* imm_set_composition_window_type)(HIMC, LPCOMPOSITIONFORM);
imm_set_composition_window_type imm_set_composition_window;
typedef LONG(__stdcall* imm_get_composition_string_type)(HIMC, DWORD, LPVOID, DWORD);
imm_get_composition_string_type imm_get_composition_string;
}
#pragma pack(1)
//Decoder of WPARAM and LPARAM
@ -210,6 +213,9 @@ namespace detail
restrict::imm_set_composition_window = reinterpret_cast<restrict::imm_set_composition_window_type>(
::GetProcAddress(imm32, "ImmSetCompositionWindow"));
restrict::imm_get_composition_string = reinterpret_cast<restrict::imm_get_composition_string_type>(
::GetProcAddress(imm32, "ImmGetCompositionStringW"));
}
bedrock::~bedrock()
@ -639,6 +645,8 @@ namespace detail
case WM_NCRBUTTONDOWN:
case WM_NCMBUTTONDOWN:
case WM_IME_STARTCOMPOSITION:
case WM_IME_COMPOSITION:
case WM_IME_CHAR:
case WM_DROPFILES:
case WM_MOUSELEAVE:
case WM_MOUSEWHEEL: //The WM_MOUSELAST may not include the WM_MOUSEWHEEL/WM_MOUSEHWHEEL when the version of SDK is low.
@ -786,25 +794,67 @@ namespace detail
}
break;
case WM_IME_STARTCOMPOSITION:
if (msgwnd->other.attribute.root->ime_enabled)
break;
case WM_IME_COMPOSITION:
if (lParam & (GCS_COMPSTR | GCS_RESULTSTR))
{
auto native_font = msgwnd->drawer.graphics.typeface().handle();
LOGFONTW logfont;
::GetObjectW(reinterpret_cast<HFONT>(native_font), sizeof logfont, &logfont);
msgwnd = brock.focus();
if (msgwnd && msgwnd->flags.enabled)
{
auto & wd_manager = brock.wd_manager();
auto composition_index = static_cast<DWORD>(lParam & (GCS_COMPSTR | GCS_RESULTSTR));
arg_ime arg;
arg.window_handle = msgwnd;
if (lParam & GCS_COMPSTR)
{
arg.ime_reason = arg_ime::reason::composition;
}
if (lParam & GCS_RESULTSTR)
{
arg.ime_reason = arg_ime::reason::result;
}
HIMC imc = restrict::imm_get_context(root_window);
restrict::imm_set_composition_font(imc, &logfont);
POINT pos;
::GetCaretPos(&pos);
COMPOSITIONFORM cf = { CFS_POINT };
cf.ptCurrentPos = pos;
restrict::imm_set_composition_window(imc, &cf);
auto size = restrict::imm_get_composition_string(imc, composition_index, nullptr, 0);
if (size > 0)
{
wchar_t * buffer = new wchar_t[100 / sizeof(wchar_t) + 1];
size = restrict::imm_get_composition_string(imc, composition_index, buffer, size + sizeof(wchar_t));
buffer[size / sizeof(wchar_t)] = '\0';
arg.composition_string = std::move(buffer);
delete[] buffer;
}
restrict::imm_release_context(root_window, imc);
if (wd_manager.available(msgwnd))
draw_invoker(&drawer::key_ime, msgwnd, arg, &context);
wd_manager.do_lazy_refresh(msgwnd, false);
}
}
def_window_proc = true;
break;
case WM_IME_CHAR:
msgwnd = brock.focus();
if (msgwnd && msgwnd->flags.enabled)
{
auto & wd_manager = brock.wd_manager();
arg_keyboard arg;
arg.evt_code = event_code::key_char;
arg.window_handle = msgwnd;
arg.key = static_cast<wchar_t>(wParam);
brock.get_key_state(arg);
arg.ignore = false;
msgwnd->annex.events_ptr->key_char.emit(arg, msgwnd);
if ((false == arg.ignore) && wd_manager.available(msgwnd))
draw_invoker(&drawer::key_char, msgwnd, arg, &context);
wd_manager.do_lazy_refresh(msgwnd, false);
}
break;
case WM_GETMINMAXINFO:
{
bool take_over = false;

View File

@ -96,6 +96,11 @@ namespace nana
overridden_ &= ~(1 << static_cast<int>(event_code::focus));
}
void drawer_trigger::key_ime(graph_reference, const arg_ime&)
{
overridden_ &= ~(1 << static_cast<int>(event_code::key_ime));
}
void drawer_trigger::key_press(graph_reference, const arg_keyboard&)
{
overridden_ &= ~(1 << static_cast<int>(event_code::key_press));
@ -318,6 +323,11 @@ namespace nana
_m_emit(event_code::focus, arg, &drawer_trigger::focus, bForce__EmitInternal);
}
void drawer::key_ime(const arg_ime& arg, const bool bForce__EmitInternal)
{
_m_emit(event_code::key_ime, arg, &drawer_trigger::key_ime, bForce__EmitInternal);
}
void drawer::key_press(const arg_keyboard& arg, const bool bForce__EmitInternal)
{
_m_emit(event_code::key_press, arg, &drawer_trigger::key_press, bForce__EmitInternal);

View File

@ -773,6 +773,13 @@ namespace nana
API::dev::lazy_refresh();
}
void trigger::key_ime(graph_reference, const arg_ime& arg)
{
drawer_->editor()->respond_ime(arg);
if (drawer_->editor()->try_refresh())
API::dev::lazy_refresh();
}
void trigger::key_char(graph_reference, const arg_keyboard& arg)
{
drawer_->editor()->respond_char(arg);

View File

@ -1163,6 +1163,43 @@ namespace nana {
impl_->capacities.acceptive = acceptive;
}
bool text_editor::respond_ime(const arg_ime& arg)
{
if (!API::window_enabled(window_))
return false;
if (select_.a != select_.b)
{
points_.caret = _m_erase_select(false);
}
for (size_t i = 0; i < composition_size_; ++i)
{
backspace(false, false);
}
if (arg.ime_reason == arg_ime::reason::composition)
{
composition_size_ = arg.composition_string.length();
points_.caret = _m_put(std::move(arg.composition_string), false);
_m_reset_content_size(true);
textbase().text_changed();
if (this->_m_adjust_view())
impl_->cview->sync(false);
reset_caret();
impl_->try_refresh = sync_graph::refresh;
}
else
{
composition_size_ = 0;
}
return true;
}
bool text_editor::respond_char(const arg_keyboard& arg) //key is a character of ASCII code
{
if (!API::window_enabled(window_))

View File

@ -621,6 +621,13 @@ namespace nana
API::dev::lazy_refresh();
}
void drawer::key_ime(graph_reference, const arg_ime& arg)
{
impl_->editor()->respond_ime(arg);
if (impl_->editor()->try_refresh())
API::dev::lazy_refresh();
}
void drawer::key_press(graph_reference, const arg_keyboard& arg)
{
if (impl_->editor()->respond_key(arg))

View File

@ -152,6 +152,13 @@ namespace drawerbase {
API::dev::lazy_refresh();
}
void drawer::key_ime(graph_reference, const arg_ime& arg)
{
editor_->respond_ime(arg);
if (editor_->try_refresh())
API::dev::lazy_refresh();
}
void drawer::key_press(graph_reference, const arg_keyboard& arg)
{
editor_->respond_key(arg);