先说明几个名词:
composition window
: 指上图中红色区域candicate window
: 指上图中绿色区域当用户输入中文时,每输入一个字符,都会触发
CefRenderWidgetHostViewOSR::ImeCompositionRangeChanged
回调,用以通知用户合成词宽度。但是这个函数有个问题,必须要输入字符之后,才会回调,所以导致用户输入第一个字符时输入法是错位的,输入第二个字符才会纠正。Qt侧通过
inputMethodEvent
和inputMethodQuery
两个回调函数来控制输入法位置,QWidget获取焦点时,inputMethodQuery
会被调用,用以确认输入法相关前置信息。QVariant QtWebctrlWidget::inputMethodQuery(Qt::InputMethodQuery query) const { if (Qt::ImCursorRectangle == query) { return QVariant(ime_rect_); } return QWidget::inputMethodQuery(query); }
Qt::ImCursorRectangle
用以查询光标位置,不可返回0,否则输入法会出现在窗口右下角。未处于composition状态下,初始光标位置通过
TextInputManager::Observer::OnSelectionBoundsChanged
回调函数获取。void CefRenderWidgetHostViewOSR::OnSelectionBoundsChanged( content::TextInputManager* text_input_manager, RenderWidgetHostViewBase* updated_view) { CefRefPtr<CefRenderHandler> handler = browser_impl_->GetClient()->GetRenderHandler(); CHECK(handler); // from RenderWidgetHostViewAura::GetCaretBounds auto selection_region = text_input_manager->GetSelectionRegion(updated_view); if (selection_region) { gfx::Rect caret_rect = gfx::RectBetweenSelectionBounds(selection_region->anchor, selection_region->focus); if (caret_rect.width() < 1) { caret_rect.set_width(1); } if (caret_rect.height() < 1) { caret_rect.set_height(1); } if (caret_rect.x() < 0) { caret_rect.set_x(0); } if (caret_rect.y() < 0) { caret_rect.set_y(0); } handler->OnSelectionBoundsChanged( browser_impl_->GetBrowser(), CefRect(caret_rect.x(), caret_rect.y(), caret_rect.width(), caret_rect.height())); } }
在cef层增加
CefRenderWidgetHostViewOSR::OnSelectionBoundsChanged
回调,用以获取光标初始位置,增加CefRenderHandler::OnSelectionBoundsChanged
函数用以通知到业务层/// /// Called when the selection bounds has changed. |bounding_box| is the /// bounds of selection region in view coordinates. /// /*--cef()--*/ virtual void OnSelectionBoundsChanged(CefRefPtr<CefBrowser> browser, const CefRect& bounding_box) {}
这样就可以实时更新光标位置。
但是,带来一个新问题,合成词窗口位置会随着用户输入而动态改变,预期是当用户正在输入时,输入法位置不会动态更新。所以需要在
inputMethodEvent
中检测,如果event->preeditString()
不为空,说明处于输入合成词状态,此时要以CefRenderWidgetHostViewOSR::ImeCompositionRangeChanged
的值来更新输入法窗口位置;如果event->commitString()
不为空,说明是提交操作,之后就要以CefRenderHandler::OnSelectionBoundsChanged
的值来更新输入法窗口位置。