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;