diff --git a/include/nana/gui/detail/drawer.hpp b/include/nana/gui/detail/drawer.hpp index 46796573..ce7ade4f 100644 --- a/include/nana/gui/detail/drawer.hpp +++ b/include/nana/gui/detail/drawer.hpp @@ -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); diff --git a/include/nana/gui/detail/event_code.hpp b/include/nana/gui/detail/event_code.hpp index 2c63cb4c..a9802e4a 100644 --- a/include/nana/gui/detail/event_code.hpp +++ b/include/nana/gui/detail/event_code.hpp @@ -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. diff --git a/include/nana/gui/detail/general_events.hpp b/include/nana/gui/detail/general_events.hpp index f58f9680..1bcc5be1 100644 --- a/include/nana/gui/detail/general_events.hpp +++ b/include/nana/gui/detail/general_events.hpp @@ -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 diff --git a/include/nana/gui/widgets/combox.hpp b/include/nana/gui/widgets/combox.hpp index f6ba12df..3d52de05 100644 --- a/include/nana/gui/widgets/combox.hpp +++ b/include/nana/gui/widgets/combox.hpp @@ -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: diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 5f691543..5657af7c 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -98,6 +98,7 @@ namespace nana{ namespace widgets void set_accept(std::function); 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 diff --git a/include/nana/gui/widgets/spinbox.hpp b/include/nana/gui/widgets/spinbox.hpp index 2f489f1d..ee99e149 100644 --- a/include/nana/gui/widgets/spinbox.hpp +++ b/include/nana/gui/widgets/spinbox.hpp @@ -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; diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index 8557c68a..58c60df7 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -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; diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 829c909a..d05a6119 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -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( ::GetProcAddress(imm32, "ImmSetCompositionWindow")); + + restrict::imm_get_composition_string = reinterpret_cast( + ::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(native_font), sizeof logfont, &logfont); + msgwnd = brock.focus(); + if (msgwnd && msgwnd->flags.enabled) + { + auto & wd_manager = brock.wd_manager(); + auto composition_index = static_cast(lParam & (GCS_COMPSTR | GCS_RESULTSTR)); - HIMC imc = restrict::imm_get_context(root_window); - restrict::imm_set_composition_font(imc, &logfont); + 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; + } - POINT pos; - ::GetCaretPos(&pos); + HIMC imc = restrict::imm_get_context(root_window); + 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); - COMPOSITIONFORM cf = { CFS_POINT }; - cf.ptCurrentPos = pos; - restrict::imm_set_composition_window(imc, &cf); - 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(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; diff --git a/source/gui/detail/drawer.cpp b/source/gui/detail/drawer.cpp index 5b21161b..6baa2626 100644 --- a/source/gui/detail/drawer.cpp +++ b/source/gui/detail/drawer.cpp @@ -96,6 +96,11 @@ namespace nana overridden_ &= ~(1 << static_cast(event_code::focus)); } + void drawer_trigger::key_ime(graph_reference, const arg_ime&) + { + overridden_ &= ~(1 << static_cast(event_code::key_ime)); + } + void drawer_trigger::key_press(graph_reference, const arg_keyboard&) { overridden_ &= ~(1 << static_cast(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); diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index 816d4ee9..d66019fc 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -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); diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 9049165f..7d8c4475 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -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_)) diff --git a/source/gui/widgets/spinbox.cpp b/source/gui/widgets/spinbox.cpp index 74169085..1b200100 100644 --- a/source/gui/widgets/spinbox.cpp +++ b/source/gui/widgets/spinbox.cpp @@ -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)) diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 100a832d..87dc761c 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -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);