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 4d3112348822ff6eb1f7bc8b998075ad1c137d81
Author:     Gökay Şatır <[email protected]>
AuthorDate: Mon Aug 18 17:26:50 2025 +0300
Commit:     Gökay ŞATIR <[email protected]>
CommitDate: Thu Sep 11 15:43:32 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 <[email protected]>
    Change-Id: I8ab9506df6d9d06247f19210c959771aeddae00d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189878
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Tomaž Vajngerl <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190798
    Tested-by: Jenkins
    Reviewed-by: Gökay ŞATIR <[email protected]>

diff --git a/vcl/source/edit/vclmedit.cxx b/vcl/source/edit/vclmedit.cxx
index 56232b32b58e..7475b7770d70 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 9ecd35cf39c3..e01a1cc0ddfd 100644
--- a/vcl/source/window/paint.cxx
+++ b/vcl/source/window/paint.cxx
@@ -1228,6 +1228,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 0b18630a5153589df6fa07d3921ad9e95bdba7a1
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Fri Aug 8 10:34:52 2025 +0200
Commit:     Gökay ŞATIR <[email protected]>
CommitDate: Thu Sep 11 15:43:17 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 <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190804
    Tested-by: Jenkins
    Reviewed-by: Gökay ŞATIR <[email protected]>

diff --git a/forms/source/component/FormComponent.cxx 
b/forms/source/component/FormComponent.cxx
index 97ac1280bd91..0492afa4817f 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;
@@ -270,6 +271,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 b32f775db14b..bee8f3178ae7 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 ee35c7195278..1748644dff75 100644
--- a/include/sfx2/viewsh.hxx
+++ b/include/sfx2/viewsh.hxx
@@ -434,6 +434,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 79ed78765212..aaa17cd7991c 100644
--- a/include/vcl/IDialogRenderable.hxx
+++ b/include/vcl/IDialogRenderable.hxx
@@ -40,6 +40,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 058402d4b5e1..ef908f61931b 100644
--- a/include/vcl/svapp.hxx
+++ b/include/vcl/svapp.hxx
@@ -1325,7 +1325,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 2176d4e7b992..8369693dd05f 100644
--- a/include/vcl/window.hxx
+++ b/include/vcl/window.hxx
@@ -768,6 +768,8 @@ public:
     void                                SetType( WindowType eType );
     WindowType                          GetType() const;
     std::string_view GetTypeName() 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 d27e0cbca0d9..495eefd13029 100644
--- a/sfx2/source/view/lokhelper.cxx
+++ b/sfx2/source/view/lokhelper.cxx
@@ -679,6 +679,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 98d3ee3a237d..a8e1e6237579 100644
--- a/sfx2/source/view/viewsh.cxx
+++ b/sfx2/source/view/viewsh.cxx
@@ -3565,6 +3565,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 36ccb9341457..1fd18186ac71 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 124dc4289f29..d60d5fa59aea 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 7b1946360a47..93a81bb259a6 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