forms/source/component/FormComponent.cxx |    6 +-
 include/sfx2/lokhelper.hxx               |    2 
 include/sfx2/viewsh.hxx                  |    3 +
 include/vcl/IDialogRenderable.hxx        |    3 +
 include/vcl/svapp.hxx                    |    3 -
 include/vcl/window.hxx                   |    2 
 sfx2/source/view/lokhelper.cxx           |   10 +++
 sfx2/source/view/viewsh.cxx              |    5 +
 vcl/inc/window.h                         |    1 
 vcl/source/app/svapp.cxx                 |    4 +
 vcl/source/edit/vclmedit.cxx             |    9 +++
 vcl/source/window/cursor.cxx             |   88 +++++++++++++++++++++----------
 vcl/source/window/paint.cxx              |    6 ++
 vcl/source/window/window2.cxx            |   11 +++
 14 files changed, 123 insertions(+), 30 deletions(-)

New commits:
commit 6b3e1e3bd53d062cf6a128f8c041c0b42bb5126f
Author:     Gökay Şatır <gokaysa...@gmail.com>
AuthorDate: Mon Aug 18 17:26:50 2025 +0300
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Tue Aug 26 16:42:27 2025 +0200

    cool#12608: Fix form controls invalidation.
    
    On Online, Writer doesn't get the invalidateTiles event. Instead, it gets 
window invalidation event.
    But form controls are rendered on the document, not on a separate window. 
So we need to send invalidateTiles event.
    invalidateTiles event is indeed sent for simple text boxes.
    Rich text boxes and date selection boxes don't get the invalidations. Seems 
that these controls are under an extra parent window.
    
    This commit adds a check for form controls that are under an extra parent 
window. So the Online side gets the invalidation.
    
    Signed-off-by: Gökay Şatır <gokaysa...@gmail.com>
    Change-Id: I8ab9506df6d9d06247f19210c959771aeddae00d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189878
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>

diff --git a/vcl/source/edit/vclmedit.cxx b/vcl/source/edit/vclmedit.cxx
index 159695e5cd7a..6df7c34917cd 100644
--- a/vcl/source/edit/vclmedit.cxx
+++ b/vcl/source/edit/vclmedit.cxx
@@ -44,6 +44,8 @@
 #include <strings.hrc>
 #include <svdata.hxx>
 
+#include <comphelper/lok.hxx>
+
 class ImpVclMEdit : public SfxListener
 {
 private:
@@ -743,6 +745,9 @@ void TextWindow::KeyInput( const KeyEvent& rKEvent )
 
     if ( !bDone )
         Window::KeyInput( rKEvent );
+
+    if (comphelper::LibreOfficeKit::isActive())
+        LogicInvalidate(nullptr);
 }
 
 void TextWindow::Paint(vcl::RenderContext& rRenderContext, const 
tools::Rectangle& rRect)
@@ -852,6 +857,10 @@ void TextWindow::Command( const CommandEvent& rCEvt )
     {
         mpExtTextView->Command( rCEvt );
     }
+
+    if (comphelper::LibreOfficeKit::isActive())
+        LogicInvalidate(nullptr);
+
     Window::Command( rCEvt );
 }
 
diff --git a/vcl/source/window/paint.cxx b/vcl/source/window/paint.cxx
index b024374a13f4..60025b44da2d 100644
--- a/vcl/source/window/paint.cxx
+++ b/vcl/source/window/paint.cxx
@@ -1227,6 +1227,12 @@ void Window::PixelInvalidate(const tools::Rectangle* 
pRectangle)
 
         pNotifier->notifyWindow(GetLOKWindowId(), u"invalidate"_ustr, 
aPayload);
     }
+    else if (GetParent() && GetParent()->IsFormControl())
+    {
+        const VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier();
+        if (pParent)
+            pParent->GetLOKNotifier()->notifyInvalidation(pRectangle);
+    }
     // Added for dialog items. Pass invalidation to the parent window.
     else if (VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier())
     {
commit 2e56e9f7cb96c0a931158e5de1fc668fc2e60b00
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Fri Aug 8 10:34:52 2025 +0200
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Tue Aug 26 16:42:20 2025 +0200

    vcl: cursor invalidation for form controls
    
    Form controls need to send cursor invalidation when, to make it
    possible to show the cursor in online, when we are editing an edit
    box in the document.
    
    Change-Id: I17c74b8183ba8caf06898019908bf0c2879d49d0
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189178
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/forms/source/component/FormComponent.cxx 
b/forms/source/component/FormComponent.cxx
index b36329283262..557f914e5d5d 100644
--- a/forms/source/component/FormComponent.cxx
+++ b/forms/source/component/FormComponent.cxx
@@ -53,7 +53,8 @@
 #include <sal/log.hxx>
 
 #include <algorithm>
-
+#include <vcl/window.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
 namespace frm
 {
 using namespace ::com::sun::star::uno;
@@ -271,6 +272,9 @@ void SAL_CALL OControl::createPeer(const 
Reference<XToolkit>& _rxToolkit, const
     {
         m_xControl->createPeer( _rxToolkit, _rxParent );
         impl_resetStateGuard_nothrow();
+
+        VclPtr<vcl::Window> pVclPeer = VCLUnoHelper::GetWindow(getPeer());
+        pVclPeer->SetFormControl(true);
     }
 }
 
diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx
index 08b481d4d60b..0ebd4e1d1dd2 100644
--- a/include/sfx2/lokhelper.hxx
+++ b/include/sfx2/lokhelper.hxx
@@ -187,6 +187,8 @@ public:
     static void notifyDocumentSizeChangedAllViews(vcl::ITiledRenderable* pDoc, 
bool bInvalidateAll = true);
     /// Emits a LOK_CALLBACK_DOCUMENT_SIZE_CHANGED for all views of the same 
document with the same part
     static void notifyPartSizeChangedAllViews(vcl::ITiledRenderable* pDoc, int 
nPart);
+    /// Emits a LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
+    static void notifyCursorInvalidation(SfxViewShell const* pThisView, 
tools::Rectangle const * pRect, bool bControlEvent);
     /// Emits a LOK_CALLBACK_INVALIDATE_TILES, but tweaks it according to 
setOptionalFeatures() if needed.
     static void notifyInvalidation(SfxViewShell const* pThisView, int nPart, 
tools::Rectangle const *);
     /// Emits a LOK_CALLBACK_INVALIDATE_TILES, but tweaks it according to 
setOptionalFeatures() if needed
diff --git a/include/sfx2/viewsh.hxx b/include/sfx2/viewsh.hxx
index 1e55b1594493..dc2f5f525ee0 100644
--- a/include/sfx2/viewsh.hxx
+++ b/include/sfx2/viewsh.hxx
@@ -438,6 +438,9 @@ public:
 
     /// ILibreOfficeKitNotifier. Emits a LOK_CALLBACK_INVALIDATE_TILES.
     virtual void notifyInvalidation(tools::Rectangle const *) const override;
+    /// ILibreOfficeKitNotifier.
+    virtual void notifyCursorInvalidation(tools::Rectangle const *, bool 
bControlEvent) const override;
+
     /// See OutlinerViewShell::NotifyOtherViews().
     void NotifyOtherViews(int nType, const OString& rKey, const OString& 
rPayload) override;
     /// See OutlinerViewShell::NotifyOtherView().
diff --git a/include/vcl/IDialogRenderable.hxx 
b/include/vcl/IDialogRenderable.hxx
index d92eb88ed25c..baaf6358d372 100644
--- a/include/vcl/IDialogRenderable.hxx
+++ b/include/vcl/IDialogRenderable.hxx
@@ -42,6 +42,9 @@ public:
     /// Emits a LOK_CALLBACK_INVALIDATE_TILES.
     virtual void notifyInvalidation(tools::Rectangle const *) const = 0;
 
+    /// Emits a LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR.
+    virtual void notifyCursorInvalidation(tools::Rectangle const *, bool 
bControlEvent) const = 0;
+
     /// Debugging
     virtual OString dumpNotifyState() const = 0;
 };
diff --git a/include/vcl/svapp.hxx b/include/vcl/svapp.hxx
index 6708ada07e98..b5dab8ab07ef 100644
--- a/include/vcl/svapp.hxx
+++ b/include/vcl/svapp.hxx
@@ -1328,7 +1328,8 @@ public:
                               const OUString& rAction,
                               const std::vector<vcl::LOKPayloadItem>& rPayload 
= std::vector<vcl::LOKPayloadItem>()) const override;
     virtual void libreOfficeKitViewCallback(int nType, const OString& 
pPayload) const override;
-    virtual void notifyInvalidation(tools::Rectangle const *) const override;
+    virtual void notifyInvalidation(tools::Rectangle const* pRect) const 
override;
+    virtual void notifyCursorInvalidation(tools::Rectangle const* pRect, bool 
bControlEvent) const override;
     virtual OString dumpNotifyState() const override;
 
 private:
diff --git a/include/vcl/window.hxx b/include/vcl/window.hxx
index a9a6ac6eee3a..b8e28ff92901 100644
--- a/include/vcl/window.hxx
+++ b/include/vcl/window.hxx
@@ -775,6 +775,8 @@ public:
     WindowExtendedStyle                 GetExtendedStyle() const;
     void                                SetType( WindowType nType );
     WindowType                          GetType() const;
+    bool IsFormControl() const;
+    void SetFormControl(bool bFormControl);
     bool                                IsSystemWindow() const;
     SAL_DLLPRIVATE bool                 IsDockingWindow() const;
     bool                                IsDialog() const;
diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx
index 0c8dc2ef992e..bdcd49d196ed 100644
--- a/sfx2/source/view/lokhelper.cxx
+++ b/sfx2/source/view/lokhelper.cxx
@@ -740,6 +740,16 @@ void SfxLokHelper::notifyWindow(const SfxViewShell* 
pThisView,
     pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_WINDOW, s);
 }
 
+void SfxLokHelper::notifyCursorInvalidation(SfxViewShell const* pThisView, 
tools::Rectangle const* pRect, bool bControlEvent)
+{
+    int nViewId = SfxLokHelper::getView(*pThisView);
+    OString sPayload = OString::Concat("{ \"viewId\": \"") + 
OString::number(nViewId) + "\", \"rectangle\": \"" + pRect->toString();
+    if (bControlEvent)
+        sPayload += "\", \"controlEvent\": true";
+    sPayload += " }";
+    
pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, 
sPayload);
+}
+
 void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, 
tools::Rectangle const* pRect)
 {
     // -1 means all parts
diff --git a/sfx2/source/view/viewsh.cxx b/sfx2/source/view/viewsh.cxx
index 2fa20798113e..9e29bde9e9b8 100644
--- a/sfx2/source/view/viewsh.cxx
+++ b/sfx2/source/view/viewsh.cxx
@@ -3560,6 +3560,11 @@ void SfxViewShell::notifyInvalidation(tools::Rectangle 
const* pRect) const
     SfxLokHelper::notifyInvalidation(this, pRect);
 }
 
+void SfxViewShell::notifyCursorInvalidation(tools::Rectangle const* pRect, 
bool bControlEvent) const
+{
+    SfxLokHelper::notifyCursorInvalidation(this, pRect, bControlEvent);
+}
+
 void SfxViewShell::NotifyOtherViews(int nType, const OString& rKey, const 
OString& rPayload)
 {
     SfxLokHelper::notifyOtherViews(this, nType, rKey, rPayload);
diff --git a/vcl/inc/window.h b/vcl/inc/window.h
index 72d07bc85c01..26aecbf6b7ee 100644
--- a/vcl/inc/window.h
+++ b/vcl/inc/window.h
@@ -393,6 +393,7 @@ public:
                         mbSecondary:1,
                         mbNonHomogeneous:1,
                         mbDoubleBufferingRequested:1;
+    bool mbIsFormControl : 1 = false;
 
     rtl::Reference< DNDListenerContainer > mxDNDListenerContainer;
 
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index 8fab026e8673..fa258d7f0613 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -341,6 +341,10 @@ void Application::notifyInvalidation(tools::Rectangle 
const* /*pRect*/) const
 {
 }
 
+void Application::notifyCursorInvalidation(tools::Rectangle const* /*pRect*/, 
bool /*bControlEvent*/) const
+{
+}
+
 void Application::Execute()
 {
     ImplSVData* pSVData = ImplGetSVData();
diff --git a/vcl/source/window/cursor.cxx b/vcl/source/window/cursor.cxx
index 6c9d7577aa41..2437fc321348 100644
--- a/vcl/source/window/cursor.cxx
+++ b/vcl/source/window/cursor.cxx
@@ -241,7 +241,38 @@ void vcl::Cursor::ImplDoShow( bool bDrawDirect, bool 
bRestore )
     }
 }
 
-void vcl::Cursor::LOKNotify( vcl::Window* pWindow, const OUString& rAction )
+namespace
+{
+
+tools::Rectangle calcualteCursorRect(Point const& rPosition, Size const rSize, 
vcl::Window* pWindow, vcl::Window* pParent)
+{
+    Point aPositionPixel = pWindow->LogicToPixel(rPosition);
+    const tools::Long nX = pWindow->GetOutOffXPixel() + aPositionPixel.X() - 
pParent->GetOutOffXPixel();
+    const tools::Long nY = pWindow->GetOutOffYPixel() + aPositionPixel.Y() - 
pParent->GetOutOffYPixel();
+
+    Size aSizePixel = pWindow->LogicToPixel(rSize);
+    if (!aSizePixel.Width())
+        aSizePixel.setWidth( 
pWindow->GetSettings().GetStyleSettings().GetCursorSize() );
+
+    Point aPosition(nX, nY);
+
+    if (pWindow->IsRTLEnabled() && pWindow->GetOutDev() && pParent->GetOutDev()
+        && !pWindow->GetOutDev()->ImplIsAntiparallel())
+        pParent->GetOutDev()->ReMirror(aPosition);
+
+    if (!pWindow->IsRTLEnabled() && pWindow->GetOutDev() && 
pParent->GetOutDev()
+        && pWindow->GetOutDev()->ImplIsAntiparallel())
+    {
+        pWindow->GetOutDev()->ReMirror(aPosition);
+        pParent->GetOutDev()->ReMirror(aPosition);
+    }
+
+    return tools::Rectangle(aPosition, aSizePixel);
+}
+
+} // end anonymous namespace
+
+void vcl::Cursor::LOKNotify(vcl::Window* pWindow, const OUString& rAction)
 {
     VclPtr<vcl::Window> pParent = pWindow->GetParentWithLOKNotifier();
     if (!pParent)
@@ -251,39 +282,40 @@ void vcl::Cursor::LOKNotify( vcl::Window* pWindow, const 
OUString& rAction )
     assert(mpData && "Require ImplCursorData");
     assert(comphelper::LibreOfficeKit::isActive());
 
-    if (comphelper::LibreOfficeKit::isDialogPainting())
-        return;
-
     const vcl::ILibreOfficeKitNotifier* pNotifier = pParent->GetLOKNotifier();
-    std::vector<vcl::LOKPayloadItem> aItems;
-    if (rAction == "cursor_visible")
-        aItems.emplace_back("visible", mpData->mbCurVisible ? "true" : 
"false");
-    else if (rAction == "cursor_invalidate")
-    {
-        const tools::Long nX = pWindow->GetOutOffXPixel() + 
pWindow->LogicToPixel(GetPos()).X() - pParent->GetOutOffXPixel();
-        const tools::Long nY = pWindow->GetOutOffYPixel() + 
pWindow->LogicToPixel(GetPos()).Y() - pParent->GetOutOffYPixel();
-        Size aSize = pWindow->LogicToPixel(GetSize());
-        if (!aSize.Width())
-            aSize.setWidth( 
pWindow->GetSettings().GetStyleSettings().GetCursorSize() );
-
-        Point aPos(nX, nY);
 
-        if (pWindow->IsRTLEnabled() && pWindow->GetOutDev() && 
pParent->GetOutDev()
-            && !pWindow->GetOutDev()->ImplIsAntiparallel())
-            pParent->GetOutDev()->ReMirror(aPos);
-
-        if (!pWindow->IsRTLEnabled() && pWindow->GetOutDev() && 
pParent->GetOutDev()
-            && pWindow->GetOutDev()->ImplIsAntiparallel())
+    if (pWindow->IsFormControl() || (pWindow->GetParent() && 
pWindow->GetParent()->IsFormControl()))
+    {
+        if (rAction == "cursor_invalidate")
         {
-            pWindow->GetOutDev()->ReMirror(aPos);
-            pParent->GetOutDev()->ReMirror(aPos);
+            tools::Rectangle aRect;
+            if (pWindow->IsFormControl())
+                aRect = calcualteCursorRect(GetPos(), GetSize(), pWindow, 
pWindow->GetParent());
+            else
+                aRect = calcualteCursorRect(GetPos(), GetSize(), pWindow, 
pWindow->GetParent()->GetParent());
+
+            OutputDevice* pDevice = mpData->mpWindow->GetOutDev();
+            const tools::Rectangle aRectTwip = pDevice->PixelToLogic(aRect, 
MapMode(MapUnit::MapTwip));
+            pNotifier->notifyCursorInvalidation(&aRectTwip, true);
         }
-
-        const tools::Rectangle aRect(aPos, aSize);
-        aItems.emplace_back("rectangle", aRect.toString());
     }
+    else
+    {
+        if (comphelper::LibreOfficeKit::isDialogPainting())
+            return;
 
-    pNotifier->notifyWindow(pParent->GetLOKWindowId(), rAction, aItems);
+        std::vector<vcl::LOKPayloadItem> aItems;
+        if (rAction == "cursor_visible")
+        {
+            aItems.emplace_back("visible", mpData->mbCurVisible ? "true" : 
"false");
+        }
+        else if (rAction == "cursor_invalidate")
+        {
+            const tools::Rectangle aRect = calcualteCursorRect(GetPos(), 
GetSize(), pWindow, pParent);
+            aItems.emplace_back("rectangle", aRect.toString());
+        }
+        pNotifier->notifyWindow(pParent->GetLOKWindowId(), rAction, aItems);
+    }
 }
 
 bool vcl::Cursor::ImplDoHide( bool bSuspend )
diff --git a/vcl/source/window/window2.cxx b/vcl/source/window/window2.cxx
index 173edb8932e0..f4e9830eade1 100644
--- a/vcl/source/window/window2.cxx
+++ b/vcl/source/window/window2.cxx
@@ -1045,6 +1045,17 @@ WindowType Window::GetType() const
         return WindowType::NONE;
 }
 
+bool Window::IsFormControl() const
+{
+    return mpWindowImpl ? mpWindowImpl->mbIsFormControl : false;
+}
+
+void Window::SetFormControl(bool bFormControl)
+{
+    if (mpWindowImpl)
+        mpWindowImpl->mbIsFormControl = bFormControl;
+}
+
 Dialog* Window::GetParentDialog() const
 {
     const vcl::Window *pWindow = this;

Reply via email to