summaryrefslogtreecommitdiffstats
path: root/src/core/render_widget_host_view_qt.cpp
diff options
context:
space:
mode:
authorPeter Varga <pvarga@inf.u-szeged.hu>2016-11-29 13:10:21 +0100
committerPeter Varga <pvarga@inf.u-szeged.hu>2017-04-12 15:02:36 +0000
commit78417958b7172ec4968088fc3a908a219ed848f6 (patch)
tree33ddd78db29b945fe7673c12b2757ac0bcc59d13 /src/core/render_widget_host_view_qt.cpp
parentab82c1d1adcc79e56d8287ea322ab039dedd8b32 (diff)
Fix text selection and input method query
Instruct the render process to change the text selection if it was requested via an input method event. Raise the selectionChanged() signal when all the corresponding input method properties are set. Moreover, add back the remaining input method widget auto tests. The updated tests are moved to the QWebEngineView tests since the corresponding APIs (inputMethodQuery() and input event handling) are now available via the QWebEngineView's focus proxy (aka RWHV). Task-number: QTBUG-55766 Change-Id: Ia0022d5f38b31dd59b084ff42e4abc2780ae90ec Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'src/core/render_widget_host_view_qt.cpp')
-rw-r--r--src/core/render_widget_host_view_qt.cpp237
1 files changed, 188 insertions, 49 deletions
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index f96329139..619577d93 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -54,9 +54,10 @@
#include "base/command_line.h"
#include "cc/output/direct_renderer.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
+#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/renderer_host/text_input_manager.h"
#include "content/common/cursors/webcursor.h"
+#include "content/common/input_messages.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_switches.h"
@@ -92,6 +93,13 @@
namespace QtWebEngineCore {
+enum ImStateFlags {
+ TextInputStateUpdated = 1 << 0,
+ TextSelectionUpdated = 1 << 1,
+ TextSelectionBoundsUpdated = 1 << 2,
+ AllFlags = TextInputStateUpdated | TextSelectionUpdated | TextSelectionBoundsUpdated
+};
+
static inline ui::LatencyInfo CreateLatencyInfo(const blink::WebInputEvent& event) {
ui::LatencyInfo latency_info;
// The latency number should only be added if the timestamp is valid.
@@ -238,13 +246,17 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost* widget
, m_needsDelegatedFrameAck(false)
, m_loadVisuallyCommittedState(NotCommitted)
, m_adapterClient(0)
- , m_currentInputType(ui::TEXT_INPUT_TYPE_NONE)
, m_imeInProgress(false)
, m_receivedEmptyImeText(false)
, m_initPending(false)
, m_beginFrameSource(nullptr)
, m_needsBeginFrames(false)
, m_addedFrameObserver(false)
+ , m_imState(0)
+ , m_anchorPositionWithinSelection(0)
+ , m_cursorPositionWithinSelection(0)
+ , m_cursorPosition(0)
+ , m_emptyPreviousSelection(true)
{
m_host->SetView(this);
#ifndef QT_NO_ACCESSIBILITY
@@ -255,6 +267,9 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost* widget
auto* task_runner = base::ThreadTaskRunnerHandle::Get().get();
m_beginFrameSource.reset(new cc::DelayBasedBeginFrameSource(
base::MakeUnique<cc::DelayBasedTimeSource>(task_runner)));
+
+ if (GetTextInputManager())
+ GetTextInputManager()->AddObserver(this);
}
RenderWidgetHostViewQt::~RenderWidgetHostViewQt()
@@ -263,6 +278,9 @@ RenderWidgetHostViewQt::~RenderWidgetHostViewQt()
#ifndef QT_NO_ACCESSIBILITY
QAccessible::removeActivationObserver(this);
#endif // QT_NO_ACCESSIBILITY
+
+ if (text_input_manager_)
+ text_input_manager_->RemoveObserver(this);
}
void RenderWidgetHostViewQt::setDelegate(RenderWidgetHostViewQtDelegate* delegate)
@@ -576,15 +594,6 @@ void RenderWidgetHostViewQt::SetIsLoading(bool)
// We use WebContentsDelegateQt::LoadingStateChanged to notify about loading state.
}
-void RenderWidgetHostViewQt::TextInputStateChanged(const content::TextInputState &params)
-{
- m_currentInputType = params.type;
- m_delegate->inputMethodStateChanged(params.type != ui::TEXT_INPUT_TYPE_NONE);
- m_delegate->setInputMethodHints(toQtInputMethodHints(params.type));
-
- m_surroundingText = QString::fromStdString(params.value);
-}
-
void RenderWidgetHostViewQt::ImeCancelComposition()
{
qApp->inputMethod()->reset();
@@ -707,16 +716,114 @@ void RenderWidgetHostViewQt::ClearCompositorFrame()
{
}
-void RenderWidgetHostViewQt::SelectionChanged(const base::string16 &text, size_t offset, const gfx::Range &range)
+void RenderWidgetHostViewQt::OnUpdateTextInputStateCalled(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view, bool did_update_state)
{
- content::RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
- m_adapterClient->selectionChanged();
+ Q_UNUSED(text_input_manager);
+ Q_UNUSED(updated_view);
+ Q_UNUSED(did_update_state);
+
+ ui::TextInputType type = getTextInputType();
+ m_delegate->inputMethodStateChanged(type != ui::TEXT_INPUT_TYPE_NONE);
+ m_delegate->setInputMethodHints(toQtInputMethodHints(type));
+
+ const content::TextInputState *state = text_input_manager_->GetTextInputState();
+ if (!state)
+ return;
+
+ if (GetSelectedText().empty())
+ m_cursorPosition = state->selection_start;
+
+ m_surroundingText = QString::fromStdString(state->value);
+
+ // Remove IME composition text from the surrounding text
+ if (state->composition_start != -1 && state->composition_end != -1)
+ m_surroundingText.remove(state->composition_start, state->composition_end - state->composition_start);
+
+ if (m_imState & ImStateFlags::TextInputStateUpdated) {
+ m_imState = ImStateFlags::TextInputStateUpdated;
+ return;
+ }
+
+ // Ignore selection change triggered by ime composition unless it clears an actual text selection
+ if (state->composition_start != -1 && m_emptyPreviousSelection) {
+ m_imState = 0;
+ return;
+ }
+
+ m_imState |= ImStateFlags::TextInputStateUpdated;
+ if (m_imState == ImStateFlags::AllFlags)
+ selectionChanged();
+}
+
+void RenderWidgetHostViewQt::OnSelectionBoundsChanged(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view)
+{
+ Q_UNUSED(text_input_manager);
+ Q_UNUSED(updated_view);
+
+ m_imState |= ImStateFlags::TextSelectionBoundsUpdated;
+ if (m_imState == ImStateFlags::AllFlags)
+ selectionChanged();
+}
+
+void RenderWidgetHostViewQt::OnTextSelectionChanged(content::TextInputManager *text_input_manager, RenderWidgetHostViewBase *updated_view)
+{
+ Q_UNUSED(text_input_manager);
+ Q_UNUSED(updated_view);
#if defined(USE_X11)
- // Set the CLIPBOARD_TYPE_SELECTION to the ui::Clipboard.
- ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_SELECTION);
- clipboard_writer.WriteText(text);
-#endif
+ if (!GetSelectedText().empty()) {
+ // Set the CLIPBOARD_TYPE_SELECTION to the ui::Clipboard.
+ ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_SELECTION);
+ clipboard_writer.WriteText(GetSelectedText());
+ }
+#endif // defined(USE_X11)
+
+ m_imState |= ImStateFlags::TextSelectionUpdated;
+ if (m_imState == ImStateFlags::AllFlags)
+ selectionChanged();
+}
+
+void RenderWidgetHostViewQt::selectionChanged()
+{
+ // Reset input manager state
+ m_imState = 0;
+
+ const content::TextInputManager::TextSelection *selection = text_input_manager_->GetTextSelection();
+ if (!selection)
+ return;
+
+ if (!selection->range.IsValid())
+ return;
+
+ // Avoid duplicate empty selectionChanged() signals
+ if (GetSelectedText().empty() && m_emptyPreviousSelection) {
+ m_anchorPositionWithinSelection = m_cursorPosition;
+ m_cursorPositionWithinSelection = m_cursorPosition;
+ return;
+ }
+
+ uint newAnchorPositionWithinSelection = 0;
+ uint newCursorPositionWithinSelection = 0;
+
+ if (text_input_manager_->GetSelectionRegion()->anchor.type() == gfx::SelectionBound::RIGHT) {
+ newAnchorPositionWithinSelection = selection->range.GetMax() - selection->offset;
+ newCursorPositionWithinSelection = selection->range.GetMin() - selection->offset;
+ } else {
+ newAnchorPositionWithinSelection = selection->range.GetMin() - selection->offset;
+ newCursorPositionWithinSelection = selection->range.GetMax() - selection->offset;
+ }
+
+ if (m_anchorPositionWithinSelection == newAnchorPositionWithinSelection && m_cursorPositionWithinSelection == newCursorPositionWithinSelection)
+ return;
+
+ m_anchorPositionWithinSelection = newAnchorPositionWithinSelection;
+ m_cursorPositionWithinSelection = newCursorPositionWithinSelection;
+
+ if (!GetSelectedText().empty())
+ m_cursorPosition = newCursorPositionWithinSelection;
+
+ m_emptyPreviousSelection = GetSelectedText().empty();
+ m_adapterClient->selectionChanged();
}
void RenderWidgetHostViewQt::OnGestureEvent(const ui::GestureEventData& gesture)
@@ -820,34 +927,27 @@ QVariant RenderWidgetHostViewQt::inputMethodQuery(Qt::InputMethodQuery query)
{
switch (query) {
case Qt::ImEnabled:
- return QVariant(m_currentInputType != ui::TEXT_INPUT_TYPE_NONE);
+ return QVariant(getTextInputType() != ui::TEXT_INPUT_TYPE_NONE);
case Qt::ImFont:
+ // TODO: Implement this
return QVariant();
case Qt::ImCursorRectangle:
- // QIBusPlatformInputContext might query ImCursorRectangle before the
- // RenderWidgetHostView is created. Without an available view GetSelectionRange()
- // returns nullptr.
- if (!GetTextInputManager()->GetSelectionRegion())
+ if (!text_input_manager_ || !text_input_manager_->GetActiveWidget())
return QVariant();
- return toQt(GetTextInputManager()->GetSelectionRegion()->caret_rect);
+ return toQt(text_input_manager_->GetSelectionRegion()->caret_rect);
case Qt::ImCursorPosition:
- Q_ASSERT(GetTextInputManager()->GetSelectionRegion());
- return toQt(GetTextInputManager()->GetSelectionRegion()->focus.edge_top_rounded().x());
+ return m_cursorPosition;
case Qt::ImAnchorPosition:
- Q_ASSERT(GetTextInputManager()->GetSelectionRegion());
- return toQt(GetTextInputManager()->GetSelectionRegion()->anchor.edge_top_rounded().x());
+ return GetSelectedText().empty() ? m_cursorPosition : m_anchorPositionWithinSelection;
case Qt::ImSurroundingText:
return m_surroundingText;
- case Qt::ImCurrentSelection: {
- Q_ASSERT(GetTextInputManager()->GetTextSelection());
- base::string16 text;
- GetTextInputManager()->GetTextSelection()->GetSelectedText(&text);
- return toQt(text);
- }
+ case Qt::ImCurrentSelection:
+ return toQt(GetSelectedText());
case Qt::ImMaximumTextLength:
+ // TODO: Implement this
return QVariant(); // No limit.
case Qt::ImHints:
- return int(toQtInputMethodHints(m_currentInputType));
+ return int(toQtInputMethodHints(getTextInputType()));
default:
return QVariant();
}
@@ -1005,6 +1105,9 @@ void RenderWidgetHostViewQt::handleKeyEvent(QKeyEvent *ev)
void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
{
+ // Reset input manager state
+ m_imState = 0;
+
if (!m_host)
return;
@@ -1016,19 +1119,9 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
const QList<QInputMethodEvent::Attribute> &attributes = ev->attributes();
std::vector<blink::WebCompositionUnderline> underlines;
- auto ensureValidSelectionRange = [&]() {
- if (!selectionRange.IsValid()) {
- // We did not receive a valid selection range, hence the range is going to mark the
- // cursor position.
- int newCursorPosition =
- (cursorPositionInPreeditString < 0) ? preeditString.length()
- : cursorPositionInPreeditString;
- selectionRange.set_start(newCursorPosition);
- selectionRange.set_end(newCursorPosition);
- }
- };
+ bool hasSelection = false;
- Q_FOREACH (const QInputMethodEvent::Attribute &attribute, attributes) {
+ for (const auto &attribute : attributes) {
switch (attribute.type) {
case QInputMethodEvent::TextFormat: {
if (preeditString.isEmpty())
@@ -1065,6 +1158,15 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
cursorPositionInPreeditString = attribute.start;
break;
case QInputMethodEvent::Selection:
+ hasSelection = true;
+
+ // Cancel IME composition
+ if (preeditString.isEmpty() && attribute.start + attribute.length == 0) {
+ selectionRange.set_start(0);
+ selectionRange.set_end(0);
+ break;
+ }
+
selectionRange.set_start(qMin(attribute.start, (attribute.start + attribute.length)));
selectionRange.set_end(qMax(attribute.start, (attribute.start + attribute.length)));
break;
@@ -1073,6 +1175,22 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
}
}
+ if (!selectionRange.IsValid()) {
+ // We did not receive a valid selection range, hence the range is going to mark the
+ // cursor position.
+ int newCursorPosition =
+ (cursorPositionInPreeditString < 0) ? preeditString.length()
+ : cursorPositionInPreeditString;
+ selectionRange.set_start(newCursorPosition);
+ selectionRange.set_end(newCursorPosition);
+ }
+
+ if (hasSelection) {
+ content::RenderFrameHost *frameHost = getFocusedFrameHost();
+ if (frameHost)
+ frameHost->Send(new InputMsg_SetEditableSelectionOffsets(frameHost->GetRoutingID(), selectionRange.start(), selectionRange.end()));
+ }
+
int replacementLength = ev->replacementLength();
gfx::Range replacementRange = gfx::Range::InvalidRange();
@@ -1089,7 +1207,6 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
}
auto setCompositionString = [&](const QString &compositionString){
- ensureValidSelectionRange();
m_host->ImeSetComposition(toString16(compositionString),
underlines,
replacementRange,
@@ -1126,7 +1243,7 @@ void RenderWidgetHostViewQt::handleInputMethodEvent(QInputMethodEvent *ev)
// flickering in the textarea (or any other element).
// Instead we postpone the processing of the empty QInputMethodEvent by posting it
// to the same focused object, and cancelling the composition on the next event loop tick.
- if (!m_receivedEmptyImeText && m_imeInProgress) {
+ if (!m_receivedEmptyImeText && m_imeInProgress && !hasSelection) {
m_receivedEmptyImeText = true;
m_imeInProgress = false;
QInputMethodEvent *eventCopy = new QInputMethodEvent(*ev);
@@ -1304,4 +1421,26 @@ void RenderWidgetHostViewQt::OnBeginFrameSourcePausedChanged(bool paused)
// begin frames.
}
+content::RenderFrameHost *RenderWidgetHostViewQt::getFocusedFrameHost()
+{
+ content::RenderViewHostImpl *viewHost = content::RenderViewHostImpl::From(m_host);
+ if (!viewHost)
+ return nullptr;
+
+ content::FrameTreeNode *focusedFrame = viewHost->GetDelegate()->GetFrameTree()->GetFocusedFrame();
+ if (!focusedFrame)
+ return nullptr;
+
+ return focusedFrame->current_frame_host();
+}
+
+ui::TextInputType RenderWidgetHostViewQt::getTextInputType() const
+{
+ if (text_input_manager_ && text_input_manager_->GetTextInputState())
+ return text_input_manager_->GetTextInputState()->type;
+
+ return ui::TEXT_INPUT_TYPE_NONE;
+}
+
+
} // namespace QtWebEngineCore