From 2be143ebb5246bb2f9b674bb09d23df5b2b6c504 Mon Sep 17 00:00:00 2001 From: Sami Merila Date: Thu, 20 Oct 2011 16:46:12 +0300 Subject: Accepting predicted text using hardware keyboard replaces unwanted part Current implementation of Symbian input context assumes that predicted word replacement happens so that the original typed text is at the end of the surrounded text. The logic fails, if to-be-predicted text is in the middle of, or in the beginning of another, already accepted word. As a fix, input context need to store the original cursor position, when reset() was called (this happens when word selection list appears). Input context is already storing a copy of a preedit string in this situation. Then, when word replacement happens, this stored cursor position is used instead of current cursor position (the native side might temporarily move the cursor to the end when word selection list opens or closes) to replace the typed word with one selected from suggested word list. Stored cursor position is dismissed immediately after used, or if cached preedit string is dismissed. Task-number: QTBUG-22147 Reviewed-by: Miikka Heikkinen --- src/gui/inputmethod/qcoefepinputcontext_p.h | 1 + src/gui/inputmethod/qcoefepinputcontext_s60.cpp | 37 +++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/gui/inputmethod/qcoefepinputcontext_p.h b/src/gui/inputmethod/qcoefepinputcontext_p.h index 8c30838..cefae5e 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_p.h +++ b/src/gui/inputmethod/qcoefepinputcontext_p.h @@ -162,6 +162,7 @@ private: QBasicTimer m_tempPreeditStringTimeout; bool m_hasTempPreeditString; QString m_cachedPreeditString; + int m_cachedCursorAndAnchorPosition; int m_splitViewResizeBy; Qt::WindowStates m_splitViewPreviousWindowStates; diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index 79005ce..66ab4c8 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -115,6 +115,7 @@ QCoeFepInputContext::QCoeFepInputContext(QObject *parent) m_formatRetriever(0), m_pointerHandler(0), m_hasTempPreeditString(false), + m_cachedCursorAndAnchorPosition(-1), m_splitViewResizeBy(0), m_splitViewPreviousWindowStates(Qt::WindowNoState) { @@ -158,9 +159,18 @@ void QCoeFepInputContext::reset() } // Store a copy of preedit text, if prediction is active and input context is reseted. // This is to ensure that we can replace preedit string after losing focus to FEP manager's - // internal sub-windows. - if (m_cachedPreeditString.isEmpty() && !(currentHints & Qt::ImhNoPredictiveText)) + // internal sub-windows. Additionally, store the cursor position if there is no selected text. + // This allows input context to replace preedit strings if they are not at the end of current + // text. + if (m_cachedPreeditString.isEmpty() && !(currentHints & Qt::ImhNoPredictiveText)) { m_cachedPreeditString = m_preeditString; + if (focusWidget()) { + int cursor = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); + int anchor = focusWidget()->inputMethodQuery(Qt::ImAnchorPosition).toInt(); + if (cursor == anchor) + m_cachedCursorAndAnchorPosition = cursor; + } + } commitCurrentString(true); } @@ -196,6 +206,7 @@ void QCoeFepInputContext::setFocusWidget(QWidget *w) void QCoeFepInputContext::widgetDestroyed(QWidget *w) { m_cachedPreeditString.clear(); + m_cachedCursorAndAnchorPosition = -1; // Make sure that the input capabilities of whatever new widget got focused are queried. CCoeControl *ctrl = w->effectiveWinId(); @@ -981,6 +992,7 @@ void QCoeFepInputContext::StartFepInlineEditL(const TDesC& aInitialInlineText, return; m_cachedPreeditString.clear(); + m_cachedCursorAndAnchorPosition = -1; commitTemporaryPreeditString(); @@ -1039,8 +1051,16 @@ void QCoeFepInputContext::UpdateFepInlineTextL(const TDesC& aNewInlineText, QString newPreeditString = qt_TDesC2QString(aNewInlineText); QInputMethodEvent event(newPreeditString, attributes); if (!m_cachedPreeditString.isEmpty()) { - event.setCommitString(QLatin1String(""), -m_cachedPreeditString.length(), m_cachedPreeditString.length()); + int cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt(); + // Predicted word is either replaced from the end of the word (normal case), + // or from stored location, if the predicted word is either in the beginning of, + // or in the middle of already committed word. + int diff = cursorPos - m_cachedCursorAndAnchorPosition; + int replaceLocation = (diff != m_cachedPreeditString.length()) ? diff : m_cachedPreeditString.length(); + + event.setCommitString(QLatin1String(""), -replaceLocation, m_cachedPreeditString.length()); m_cachedPreeditString.clear(); + m_cachedCursorAndAnchorPosition = -1; } else if (newPreeditString.isEmpty() && m_preeditString.isEmpty()) { // In Symbian world this means "erase last character". event.setCommitString(QLatin1String(""), -1, 1); @@ -1138,6 +1158,10 @@ void QCoeFepInputContext::SetCursorSelectionForFepL(const TCursorSelection& aCur int pos = aCursorSelection.iAnchorPos; int length = aCursorSelection.iCursorPos - pos; + if (m_cachedCursorAndAnchorPosition != -1) { + pos = m_cachedCursorAndAnchorPosition; + length = 0; + } QList attributes; attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos, length, QVariant()); @@ -1155,6 +1179,13 @@ void QCoeFepInputContext::GetCursorSelectionForFep(TCursorSelection& aCursorSele int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt() + m_preeditString.size(); int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt() + m_preeditString.size(); + + // If the position is stored, use that value, so that word replacement from proposed word + // lists are added to the correct position. + if (m_cachedCursorAndAnchorPosition != -1) { + cursor = m_cachedCursorAndAnchorPosition; + anchor = m_cachedCursorAndAnchorPosition; + } QString text = w->inputMethodQuery(Qt::ImSurroundingText).value(); int combinedSize = text.size() + m_preeditString.size(); if (combinedSize < anchor || combinedSize < cursor) { -- cgit v0.12 From 5195c26208e9e2d80509c8308d590da2ef8a029b Mon Sep 17 00:00:00 2001 From: Sami Merila Date: Fri, 21 Oct 2011 14:40:28 +0300 Subject: Regression caused by 2be143ebb5246bb2f9b674bb09d23df5b2b6c504 After 2be143ebb5246bb2f9b674bb09d23df5b2b6c504, if user opts to spell a word him/herself instead of using the suggested word list, the result is incorrect. The existing preedit string is committed, then cursor is moved to the beginning and user written word is added. E.g. user writes 'tadaa' then selects to spell it again and writes 'radar', in editor there is 'radartadaa'. Regression is caused due to storing the cursor pointer even in cases where there is no stored preedit string. Task-number: QTBUG-22147 Reviewed-by: Miikka Heikkinen --- src/gui/inputmethod/qcoefepinputcontext_s60.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index 66ab4c8..eeec04b 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -164,7 +164,7 @@ void QCoeFepInputContext::reset() // text. if (m_cachedPreeditString.isEmpty() && !(currentHints & Qt::ImhNoPredictiveText)) { m_cachedPreeditString = m_preeditString; - if (focusWidget()) { + if (focusWidget() && !m_cachedPreeditString.isEmpty()) { int cursor = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); int anchor = focusWidget()->inputMethodQuery(Qt::ImAnchorPosition).toInt(); if (cursor == anchor) -- cgit v0.12 From 657b33557df8a997d7d440f33fd9fa34e97d1e0a Mon Sep 17 00:00:00 2001 From: Sergio Ahumada Date: Fri, 21 Oct 2011 23:15:05 +0200 Subject: Doc: Fix example code Task-number: QTWEBSITE-281 --- doc/src/getting-started/gettingstartedqt.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/getting-started/gettingstartedqt.qdoc b/doc/src/getting-started/gettingstartedqt.qdoc index fc9d799..eda5ee1 100644 --- a/doc/src/getting-started/gettingstartedqt.qdoc +++ b/doc/src/getting-started/gettingstartedqt.qdoc @@ -374,7 +374,7 @@ \code 25 Notepad::Notepad() 26 { -27 saveAction = new QAction(tr("&Open"), this); +27 openAction = new QAction(tr("&Open"), this); 28 saveAction = new QAction(tr("&Save"), this); 29 exitAction = new QAction(tr("E&xit"), this); 30 -- cgit v0.12 From cc084a1a05c53035bf401aed2e08e3c30a75e509 Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Thu, 27 Oct 2011 15:22:45 +1000 Subject: Adding items to a view with no delegate crashes. If there is no delegate then clear state and return. Change-Id: I786b9bc4018706797056fbd1ad25d25663102707 Task-number: QTBUG-22379 Reviewed-by: Andrew den Exter --- src/declarative/graphicsitems/qdeclarativegridview.cpp | 10 ++++++++++ src/declarative/graphicsitems/qdeclarativelistview.cpp | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/declarative/graphicsitems/qdeclarativegridview.cpp b/src/declarative/graphicsitems/qdeclarativegridview.cpp index 29c714d..feca6b5 100644 --- a/src/declarative/graphicsitems/qdeclarativegridview.cpp +++ b/src/declarative/graphicsitems/qdeclarativegridview.cpp @@ -2852,6 +2852,11 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) addedVisible = true; } FxGridItem *item = d->createItem(modelIndex + i); + if (!item) { + // broken or no delegate + d->clear(); + return; + } d->visibleItems.insert(index, item); item->setPosition(colPos, rowPos); added.append(item); @@ -3042,6 +3047,11 @@ void QDeclarativeGridView::itemsMoved(int from, int to, int count) FxGridItem *movedItem = moved.take(item->index); if (!movedItem) movedItem = d->createItem(item->index); + if (!movedItem) { + // broken or no delegate + d->clear(); + return; + } it = d->visibleItems.insert(it, movedItem); if (it == d->visibleItems.begin() && firstItem) movedItem->setPosition(firstItem->colPos(), firstItem->rowPos()); diff --git a/src/declarative/graphicsitems/qdeclarativelistview.cpp b/src/declarative/graphicsitems/qdeclarativelistview.cpp index 920b6ae..37c34dc 100644 --- a/src/declarative/graphicsitems/qdeclarativelistview.cpp +++ b/src/declarative/graphicsitems/qdeclarativelistview.cpp @@ -3283,6 +3283,11 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) addedVisible = true; } FxListItem *item = d->createItem(modelIndex + i); + if (!item) { + // broken or no delegate + d->clear(); + return; + } d->visibleItems.insert(insertionIdx, item); pos -= item->size() + d->spacing; item->setPosition(pos); @@ -3313,6 +3318,11 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) addedVisible = true; } FxListItem *item = d->createItem(modelIndex + i); + if (!item) { + // broken or no delegate + d->clear(); + return; + } d->visibleItems.insert(index, item); item->setPosition(pos); added.append(item); @@ -3516,6 +3526,11 @@ void QDeclarativeListView::itemsMoved(int from, int to, int count) FxListItem *movedItem = moved.take(item->index); if (!movedItem) movedItem = d->createItem(item->index); + if (!movedItem) { + // broken or no delegate + d->clear(); + return; + } if (item->index <= firstVisible->index) moveBy -= movedItem->size(); it = d->visibleItems.insert(it, movedItem); -- cgit v0.12