desktop/source/lib/init.cxx | 28 +++++------ include/LibreOfficeKit/LibreOfficeKitEnums.h | 14 +++++ libreofficekit/source/gtk/lokdocview.cxx | 1 sfx2/source/view/lokhelper.cxx | 3 + sw/qa/extras/tiledrendering/tiledrendering.cxx | 45 ++++++++++++++++++ sw/source/uibase/docvw/edtwin2.cxx | 61 ++++++++++++++++--------- 6 files changed, 117 insertions(+), 35 deletions(-)
New commits: commit 35beee45d3e0fada4796ae69b1df5adfe344602d Author: Mike Kaganski <[email protected]> AuthorDate: Tue Dec 26 14:08:07 2023 +0600 Commit: Miklos Vajna <[email protected]> CommitDate: Mon Jan 8 08:56:25 2024 +0100 Send tooltip text to LOK Call vcl::Window::RequestHelp from LOKPostAsyncEvent for mouse movement. Introduce LOK_CALLBACK_TOOLTIP callback type, and send it from SwEditWin::RequestHelp. Intention is, that the tooltip is shown by client at the current mouse pointer position, which is hopefully not far away from the point that generated the mouse event. On the next movement, the client starts a timer to hide the tooltip. If the next tooltip message arrives, the tooltip would be updated in the new place. Alternatively, the payload could contain the coordinates from the HelpEvent. Change-Id: I8e96eb6e6983ad8d13b4c5d7be4d51ff3fd11893 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161302 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161621 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 3491dd6323f5..60618c6b9dc1 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -1864,6 +1864,7 @@ void CallbackFlushHandler::queue(const int type, CallbackData& aCallbackData) case LOK_CALLBACK_GRAPHIC_SELECTION: case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: case LOK_CALLBACK_INVALIDATE_TILES: + case LOK_CALLBACK_TOOLTIP: if (removeAll(type)) SAL_INFO("lok", "Removed dups of [" << type << "]: [" << aCallbackData.getPayload() << "]."); break; @@ -1891,6 +1892,7 @@ void CallbackFlushHandler::queue(const int type, CallbackData& aCallbackData) case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED: case LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED: case LOK_CALLBACK_COLOR_PALETTES: + case LOK_CALLBACK_TOOLTIP: { if (removeAll(type)) SAL_INFO("lok", "Removed dups of [" << type << "]: [" << aCallbackData.getPayload() << "]."); @@ -1900,28 +1902,26 @@ void CallbackFlushHandler::queue(const int type, CallbackData& aCallbackData) // These are safe to use the latest state and ignore previous // ones (if any) since the last overrides previous ones, // but only if the view is the same. + case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: + // deleting the duplicate of visible cursor message can cause hyperlink popup not to show up on second/or more click on the same place. + // If the hyperlink is not empty we can bypass that to show the popup + if (aCallbackData.getPayload().find("\"hyperlink\":\"\"") == std::string::npos + && aCallbackData.getPayload().find("\"hyperlink\": {}") == std::string::npos) + break; + [[fallthrough]]; case LOK_CALLBACK_CELL_VIEW_CURSOR: case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION: case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR: - case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: case LOK_CALLBACK_TEXT_VIEW_SELECTION: case LOK_CALLBACK_VIEW_CURSOR_VISIBLE: case LOK_CALLBACK_CALC_FUNCTION_LIST: case LOK_CALLBACK_FORM_FIELD_BUTTON: { - // deleting the duplicate of visible cursor message can cause hyperlink popup not to show up on second/or more click on the same place. - // If the hyperlink is not empty we can bypass that to show the popup - const bool hyperLinkException = type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR && - aCallbackData.getPayload().find("\"hyperlink\":\"\"") == std::string::npos && - aCallbackData.getPayload().find("\"hyperlink\": {}") == std::string::npos; - if(!hyperLinkException) - { - const int nViewId = aCallbackData.getViewId(); - removeAll(type, [nViewId] (const CallbackData& elemData) { - return (nViewId == elemData.getViewId()); - } - ); - } + const int nViewId = aCallbackData.getViewId(); + removeAll(type, [nViewId] (const CallbackData& elemData) { + return (nViewId == elemData.getViewId()); + } + ); } break; diff --git a/include/LibreOfficeKit/LibreOfficeKitEnums.h b/include/LibreOfficeKit/LibreOfficeKitEnums.h index 45f41a142563..a894bd473ad9 100644 --- a/include/LibreOfficeKit/LibreOfficeKitEnums.h +++ b/include/LibreOfficeKit/LibreOfficeKitEnums.h @@ -1033,7 +1033,17 @@ typedef enum * * Payload is the log to be sent */ - LOK_CALLBACK_CORE_LOG = 70 + LOK_CALLBACK_CORE_LOG = 70, + + /** + * Tooltips shown in the documents, like redline author and date. + * + * { + * "text": "text of tooltip", + * "rectangle": "x, y, width, height" + * } + */ + LOK_CALLBACK_TOOLTIP = 71, } LibreOfficeKitCallbackType; @@ -1205,6 +1215,8 @@ static inline const char* lokCallbackTypeToString(int nType) return "LOK_CALLBACK_A11Y_SELECTION_CHANGED"; case LOK_CALLBACK_CORE_LOG: return "LOK_CALLBACK_CORE_LOG"; + case LOK_CALLBACK_TOOLTIP: + return "LOK_CALLBACK_TOOLTIP"; } assert(!"Unknown LibreOfficeKitCallbackType type."); diff --git a/libreofficekit/source/gtk/lokdocview.cxx b/libreofficekit/source/gtk/lokdocview.cxx index bd2cec88f071..f025cf99a140 100644 --- a/libreofficekit/source/gtk/lokdocview.cxx +++ b/libreofficekit/source/gtk/lokdocview.cxx @@ -1497,6 +1497,7 @@ callback (gpointer pData) case LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE: case LOK_CALLBACK_A11Y_SELECTION_CHANGED: case LOK_CALLBACK_CORE_LOG: + case LOK_CALLBACK_TOOLTIP: { // TODO: Implement me break; diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx index 7a8c1c7454dc..1ee8f262906c 100644 --- a/sfx2/source/view/lokhelper.cxx +++ b/sfx2/source/view/lokhelper.cxx @@ -912,6 +912,9 @@ namespace case VclEventId::WindowMouseMove: pLOKEv->mpWindow->SetLastMousePos(pLOKEv->maMouseEvent.GetPosPixel()); pLOKEv->mpWindow->MouseMove(pLOKEv->maMouseEvent); + pLOKEv->mpWindow->RequestHelp(HelpEvent{ + pLOKEv->mpWindow->OutputToScreenPixel(pLOKEv->maMouseEvent.GetPosPixel()), + HelpEventMode::QUICK }); // If needed, HelpEventMode should be taken from a config break; case VclEventId::ExtTextInput: case VclEventId::EndExtTextInput: diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx b/sw/qa/extras/tiledrendering/tiledrendering.cxx index 91344a563505..ffa997cfdd67 100644 --- a/sw/qa/extras/tiledrendering/tiledrendering.cxx +++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx @@ -112,6 +112,11 @@ protected: OString m_aFormFieldButton; OString m_aContentControl; OString m_ShapeSelection; + struct + { + std::string text; + std::string rect; + } m_aTooltip; TestLokCallbackWrapper m_callbackWrapper; }; @@ -294,6 +299,15 @@ void SwTiledRenderingTest::callbackImpl(int nType, const char* pPayload) m_ShapeSelection = OString(pPayload); } break; + case LOK_CALLBACK_TOOLTIP: + { + std::stringstream aStream(pPayload); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + m_aTooltip.text = aTree.get_child("text").get_value<std::string>(); + m_aTooltip.rect = aTree.get_child("rectangle").get_value<std::string>(); + } + break; } } @@ -4086,6 +4100,37 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testSavedAuthorField) assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion[1]/SwLineLayout[1]/SwFieldPortion[1]", "expand", sAuthor); } +CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRedlineTooltip) +{ + SwXTextDocument* pXTextDoc = createDoc(); + SwWrtShell* pWrtShell = pXTextDoc->GetDocShell()->GetWrtShell(); + CPPUNIT_ASSERT(pWrtShell); + setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell()); + pWrtShell->SetRedlineFlagsAndCheckInsMode(RedlineFlags::On | RedlineFlags::ShowMask); + uno::Reference<text::XText> xText(pXTextDoc->getText(), uno::UNO_SET_THROW); + xText->insertString(xText->getEnd(), "test", /*bAbsorb=*/false); + + SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false); + CPPUNIT_ASSERT(pShellCursor); + + pWrtShell->EndOfSection(/*bSelect=*/false); + Point aEnd = pShellCursor->GetSttPos(); + pWrtShell->StartOfSection(/*bSelect=*/false); + Point aStart = pShellCursor->GetSttPos(); + Point aMiddle((aStart.getX() + aEnd.getX()) / 2, (aStart.getY() + aEnd.getY()) / 2); + pXTextDoc->postMouseEvent(LOK_MOUSEEVENT_MOUSEMOVE, aMiddle.getX(), aMiddle.getY(), 1, 0, 0); + Scheduler::ProcessEventsToIdle(); + + CPPUNIT_ASSERT(OString(m_aTooltip.text).startsWith("Inserted: ")); + + std::vector<OUString> vec = comphelper::string::split(OUString::fromUtf8(m_aTooltip.rect), ','); + CPPUNIT_ASSERT_EQUAL(size_t(4), vec.size()); + CPPUNIT_ASSERT(vec[0].toInt32() != 0); + CPPUNIT_ASSERT(vec[1].toInt32() != 0); + CPPUNIT_ASSERT(vec[2].toInt32() != 0); + CPPUNIT_ASSERT(vec[3].toInt32() != 0); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/edtwin2.cxx b/sw/source/uibase/docvw/edtwin2.cxx index 73e4f8dbb237..ec3b76324e79 100644 --- a/sw/source/uibase/docvw/edtwin2.cxx +++ b/sw/source/uibase/docvw/edtwin2.cxx @@ -21,6 +21,7 @@ #include <osl/diagnose.h> #include <osl/thread.h> #include <vcl/help.hxx> +#include <tools/json_writer.hxx> #include <tools/urlobj.hxx> #include <fmtrfmrk.hxx> #include <svl/urihelper.hxx> @@ -54,6 +55,8 @@ #include <comphelper/lok.hxx> #include <authfld.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + static OUString lcl_GetRedlineHelp( const SwRangeRedline& rRedl, bool bBalloon, bool bTableChange ) { TranslateId pResId; @@ -107,6 +110,16 @@ OUString SwEditWin::ClipLongToolTip(const OUString& rText) return sDisplayText; } +static OString getTooltipPayload(const OUString& tooltip, const SwRect& rect) +{ + tools::JsonWriter writer; + { + writer.put("text", tooltip); + writer.put("rectangle", rect.SVRect().toString()); + } + return writer.extractAsOString(); +} + void SwEditWin::RequestHelp(const HelpEvent &rEvt) { SwWrtShell &rSh = m_rView.GetWrtShell(); @@ -405,28 +418,36 @@ void SwEditWin::RequestHelp(const HelpEvent &rEvt) } if (!sText.isEmpty()) { - tools::Rectangle aRect( aFieldRect.SVRect() ); - Point aPt( OutputToScreenPixel( LogicToPixel( aRect.TopLeft() ))); - aRect.SetLeft( aPt.X() ); - aRect.SetTop( aPt.Y() ); - aPt = OutputToScreenPixel( LogicToPixel( aRect.BottomRight() )); - aRect.SetRight( aPt.X() ); - aRect.SetBottom( aPt.Y() ); - - // tdf#136336 ensure tooltip area surrounds the current mouse position with at least a pixel margin - aRect.Union(tools::Rectangle(rEvt.GetMousePosPixel(), Size(1, 1))); - aRect.AdjustLeft(-1); - aRect.AdjustRight(1); - aRect.AdjustTop(-1); - aRect.AdjustBottom(1); - - if( bBalloon ) - Help::ShowBalloon( this, rEvt.GetMousePosPixel(), aRect, sText ); + if (comphelper::LibreOfficeKit::isActive()) + { + m_rView.libreOfficeKitViewCallback( + LOK_CALLBACK_TOOLTIP, getTooltipPayload(sText, aFieldRect).getStr()); + } else { - // the show the help - OUString sDisplayText(ClipLongToolTip(sText)); - Help::ShowQuickHelp(this, aRect, sDisplayText, nStyle); + tools::Rectangle aRect(aFieldRect.SVRect()); + Point aPt(OutputToScreenPixel(LogicToPixel(aRect.TopLeft()))); + aRect.SetLeft(aPt.X()); + aRect.SetTop(aPt.Y()); + aPt = OutputToScreenPixel(LogicToPixel(aRect.BottomRight())); + aRect.SetRight(aPt.X()); + aRect.SetBottom(aPt.Y()); + + // tdf#136336 ensure tooltip area surrounds the current mouse position with at least a pixel margin + aRect.Union(tools::Rectangle(rEvt.GetMousePosPixel(), Size(1, 1))); + aRect.AdjustLeft(-1); + aRect.AdjustRight(1); + aRect.AdjustTop(-1); + aRect.AdjustBottom(1); + + if (bBalloon) + Help::ShowBalloon(this, rEvt.GetMousePosPixel(), aRect, sText); + else + { + // the show the help + OUString sDisplayText(ClipLongToolTip(sText)); + Help::ShowQuickHelp(this, aRect, sDisplayText, nStyle); + } } }
