// Engine/Source/Runtime/WebBrowser/Private/CEF/CEFImeHandler.cpp #include "CEF/CEFInterfaceImeHandler.h" #if WITH_CEF3 && !PLATFORM_LINUX #include "CEFInterfaceTextInputMethodContext.h" FCEFInterfaceImeHandler::FCEFInterfaceImeHandler(CefRefPtr Browser) : InternalCefBrowser(Browser) , TextInputMethodSystem(nullptr) { } bool FCEFInterfaceImeHandler::OnProcessMessageReceived(CefRefPtr Browser, CefProcessId SourceProcess, CefRefPtr Message) { bool Result = false; FString MessageName = WCHAR_TO_TCHAR(Message->GetName().ToWString().c_str()); if (MessageName == TEXT("UE::IME::FocusChanged")) { Result = HandleFocusChangedMessage(Message->GetArgumentList()); } return Result; } void FCEFInterfaceImeHandler::SendProcessMessage(CefRefPtr Message) { if (IsValid() && InternalCefBrowser->GetMainFrame()) { InternalCefBrowser->GetMainFrame()->SendProcessMessage(PID_RENDERER, Message); } } void FCEFInterfaceImeHandler::BindInputMethodSystem(ITextInputMethodSystem* InTextInputMethodSystem) { TextInputMethodSystem = InTextInputMethodSystem; if (TextInputMethodSystem && TextInputMethodContext.IsValid()) { TextInputMethodChangeNotifier = TextInputMethodSystem->RegisterContext(TextInputMethodContext.ToSharedRef()); } } void FCEFInterfaceImeHandler::UnbindInputMethodSystem() { if (TextInputMethodContext.IsValid()) { DestroyContext(); } TextInputMethodSystem = nullptr; } void FCEFInterfaceImeHandler::InitContext() { if (!TextInputMethodSystem) { return; } // Clean up any existing context DestroyContext(); TextInputMethodContext = FCEFInterfaceTextInputMethodContext::Create(SharedThis(this)); if (TextInputMethodSystem) { TextInputMethodChangeNotifier = TextInputMethodSystem->RegisterContext(TextInputMethodContext.ToSharedRef()); } if (TextInputMethodChangeNotifier.IsValid()) { TextInputMethodChangeNotifier->NotifyLayoutChanged(ITextInputMethodChangeNotifier::ELayoutChangeType::Created); } TextInputMethodSystem->ActivateContext(TextInputMethodContext.ToSharedRef()); } void FCEFInterfaceImeHandler::DeactivateContext() { if (!TextInputMethodSystem || !TextInputMethodContext.IsValid()) { return; } TSharedRef TextInputMethodContextRef = TextInputMethodContext.ToSharedRef(); if (TextInputMethodSystem->IsActiveContext(TextInputMethodContextRef)) { // Make sure that the composition is aborted to avoid any IME calls to EndComposition from trying to mutate our dying owner widget if (TextInputMethodContextRef->IsComposing()) { TextInputMethodContextRef->AbortComposition(); if (TextInputMethodChangeNotifier.IsValid()) { TextInputMethodChangeNotifier->CancelComposition(); } } TextInputMethodSystem->DeactivateContext(TextInputMethodContextRef); } } void FCEFInterfaceImeHandler::DestroyContext() { if (!TextInputMethodContext.IsValid()) { return; } if (TextInputMethodSystem) { DeactivateContext(); TextInputMethodSystem->UnregisterContext(TextInputMethodContext.ToSharedRef()); } TextInputMethodChangeNotifier.Reset(); TextInputMethodContext.Reset(); } bool FCEFInterfaceImeHandler::HandleFocusChangedMessage(CefRefPtr MessageArguments) { if (MessageArguments->GetSize() == 1 && MessageArguments->GetType(0) == VTYPE_STRING) { if (TextInputMethodContext.IsValid()) { DestroyContext(); } return true; } else if (MessageArguments->GetSize() == 8 && MessageArguments->GetType(0) == VTYPE_STRING && MessageArguments->GetType(1) == VTYPE_STRING && MessageArguments->GetType(2) == VTYPE_BOOL && MessageArguments->GetType(3) == VTYPE_STRING && MessageArguments->GetType(4) == VTYPE_INT && MessageArguments->GetType(5) == VTYPE_INT && MessageArguments->GetType(6) == VTYPE_INT && MessageArguments->GetType(7) == VTYPE_INT) { FString Type = WCHAR_TO_TCHAR(MessageArguments->GetString(0).ToWString().c_str()); FString Name = WCHAR_TO_TCHAR(MessageArguments->GetString(1).ToWString().c_str()); bool bIsEditable = MessageArguments->GetBool(2); if (Type == TEXT("DOM_NODE_TYPE_ELEMENT") && (Name == TEXT("INPUT") || Name == TEXT("TEXTAREA")) && bIsEditable) { // @todo: Make use of the bounds of the text box here to act as a fallback for the initial composition window pos. InitContext(); } return true; } return false; } void FCEFInterfaceImeHandler::UnbindCefBrowser() { if (TextInputMethodContext.IsValid()) { DestroyContext(); } InternalCefBrowser = nullptr; } void FCEFInterfaceImeHandler::CacheBrowserSlateInfo(const TSharedRef& Widget) { InternalBrowserSlateWidget = Widget; } void FCEFInterfaceImeHandler::SetFocus(bool bHasFocus) { if (!TextInputMethodSystem || !TextInputMethodContext.IsValid()) { return; } if (bHasFocus) { TextInputMethodSystem->ActivateContext(TextInputMethodContext.ToSharedRef()); } else { DeactivateContext(); } } void FCEFInterfaceImeHandler::UpdateCachedGeometry(const FGeometry& AllottedGeometry) { if (TextInputMethodContext.IsValid() && TextInputMethodContext->UpdateCachedGeometry(AllottedGeometry)) { if (TextInputMethodChangeNotifier.IsValid()) { TextInputMethodChangeNotifier->NotifyLayoutChanged(ITextInputMethodChangeNotifier::ELayoutChangeType::Changed); } } } void FCEFInterfaceImeHandler::CEFCompositionRangeChanged(const CefRange& SelectionRange, const CefRenderHandler::RectList& CharacterBounds) { if (TextInputMethodContext.IsValid() && TextInputMethodContext->CEFCompositionRangeChanged(SelectionRange, CharacterBounds)) { if (TextInputMethodChangeNotifier.IsValid()) { TextInputMethodChangeNotifier->NotifyLayoutChanged(ITextInputMethodChangeNotifier::ELayoutChangeType::Changed); } } } #endif