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;
