vcl/inc/qt5/QtWidget.hxx |    2 ++
 vcl/qt5/QtWidget.cxx     |   39 +++++++++++++++++++++++++++++++++++++--
 2 files changed, 39 insertions(+), 2 deletions(-)

New commits:
commit 217ca9c79d75912df3fb735def4b64b0a7284e30
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Sat May 28 10:51:53 2022 +0200
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Sat May 28 16:09:00 2022 +0200

    tdf#149255 qt: Implement deletion/"swallowing" for IM replacement
    
    This implements deletion of the text specified by the
    `replacementStart()` and `replacementLength()` of the
    `QInputMethodEvent*` received in `QtWidget::inputMethodEvent`.
    
    Quoting from the `QInputMethodEvent` doc [1]:
    
    > When receiving an input method event, the text widget has to performs
    > the following steps:
    >
    > 1. If the widget has selected text, the selected text should
    >    get removed.
    > 2. Remove the text starting at replacementStart() with length
    >    replacementLength() and replace it by the commitString().
    > [...]
    
    This implementation is sufficient for the scenario described in tdf#149255,
    but I didn't test any more complex scenarios, like one where text is 
selected.
    (My current knowledge of input methods is too limited to be able to do
    more extensive testing without first spending time to get deeper into
    the topic.)
    
    The gtk3 implementation in
    `GtkSalFrame::IMHandler::signalIMDeleteSurrounding`
    was very helpful to get an impression of what needs to be done.
    
    Since the documentation for `QInputMethodEvent::replacementLength()`
    talks about "number of characters", I suspect that conversion
    to UTF-16 code units is needed just the same way as it is for the
    gtk3 case and this therefore calls
    `SalFrame::CalcDeleteSurroundingSelection` the same way.
    However, this part is untested, since it is not relevant
    for the tested scenario (where each of the characters is represented
    in a single UTF-16 code unit).
    
    [1] https://doc.qt.io/qt-5/qinputmethodevent.html
    
    Change-Id: I2a34e58067e253c39dbd87905943134bdfa4ea27
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/134855
    Tested-by: Jenkins
    Reviewed-by: Jan-Marek Glogowski <glo...@fbihome.de>
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/vcl/inc/qt5/QtWidget.hxx b/vcl/inc/qt5/QtWidget.hxx
index 8f7f6cc319e1..e644e7f70cd9 100644
--- a/vcl/inc/qt5/QtWidget.hxx
+++ b/vcl/inc/qt5/QtWidget.hxx
@@ -49,6 +49,8 @@ class QtWidget : public QWidget
     };
 
     static void commitText(QtFrame&, const QString& aText);
+    static void deleteReplacementText(QtFrame& rFrame, int nReplacementStart,
+                                      int nReplacementLength);
     static bool handleKeyEvent(QtFrame&, const QWidget&, QKeyEvent*, const 
ButtonKeyState);
     static void handleMouseButtonEvent(const QtFrame&, const QMouseEvent*, 
const ButtonKeyState);
     static void handleMouseEnterLeaveEvents(const QtFrame&, QEvent*);
diff --git a/vcl/qt5/QtWidget.cxx b/vcl/qt5/QtWidget.cxx
index a9484f0b2531..74adcc4e974c 100644
--- a/vcl/qt5/QtWidget.cxx
+++ b/vcl/qt5/QtWidget.cxx
@@ -493,6 +493,33 @@ void QtWidget::commitText(QtFrame& rFrame, const QString& 
aText)
         rFrame.CallCallback(SalEvent::EndExtTextInput, nullptr);
 }
 
+void QtWidget::deleteReplacementText(QtFrame& rFrame, int nReplacementStart, 
int nReplacementLength)
+{
+    // get the surrounding text
+    SolarMutexGuard aGuard;
+    SalSurroundingTextRequestEvent aSurroundingTextEvt;
+    aSurroundingTextEvt.maText.clear();
+    aSurroundingTextEvt.mnStart = aSurroundingTextEvt.mnEnd = 0;
+    rFrame.CallCallback(SalEvent::SurroundingTextRequest, 
&aSurroundingTextEvt);
+
+    // Turn nReplacementStart, nReplacementLength into a UTF-16 selection
+    const Selection aSelection = SalFrame::CalcDeleteSurroundingSelection(
+        aSurroundingTextEvt.maText, aSurroundingTextEvt.mnStart, 
nReplacementStart,
+        nReplacementLength);
+
+    const Selection aInvalid(SAL_MAX_UINT32, SAL_MAX_UINT32);
+    if (aSelection == aInvalid)
+    {
+        SAL_WARN("vcl.qt", "Invalid selection when deleting IM replacement 
text");
+        return;
+    }
+
+    SalSurroundingTextSelectionChangeEvent aEvt;
+    aEvt.mnStart = aSelection.Min();
+    aEvt.mnEnd = aSelection.Max();
+    rFrame.CallCallback(SalEvent::DeleteSurroundingTextRequest, &aEvt);
+}
+
 bool QtWidget::handleKeyEvent(QtFrame& rFrame, const QWidget& rWidget, 
QKeyEvent* pEvent,
                               const ButtonKeyState eState)
 {
@@ -724,8 +751,16 @@ static ExtTextInputAttr 
lcl_MapUnderlineStyle(QTextCharFormat::UnderlineStyle us
 
 void QtWidget::inputMethodEvent(QInputMethodEvent* pEvent)
 {
-    if (!pEvent->commitString().isEmpty())
-        commitText(m_rFrame, pEvent->commitString());
+    const bool bHasCommitText = !pEvent->commitString().isEmpty();
+    const int nReplacementLength = pEvent->replacementLength();
+
+    if (nReplacementLength > 0 || bHasCommitText)
+    {
+        if (nReplacementLength > 0)
+            deleteReplacementText(m_rFrame, pEvent->replacementStart(), 
nReplacementLength);
+        if (bHasCommitText)
+            commitText(m_rFrame, pEvent->commitString());
+    }
     else
     {
         SalExtTextInputEvent aInputEvent;

Reply via email to