sw/inc/view.hxx                              |   16 ++++
 sw/source/core/access/AccessibilityIssue.cxx |   86 ++++++++++++++++++++++
 sw/source/uibase/uiview/view.cxx             |  104 ++++++++++++++++++++++++++-
 3 files changed, 205 insertions(+), 1 deletion(-)

New commits:
commit c603d37223d4f7c3594515fb2bbac22015bc146b
Author:     Jim Raykowski <rayk...@gmail.com>
AuthorDate: Fri Sep 22 16:35:05 2023 -0800
Commit:     Jim Raykowski <rayk...@gmail.com>
CommitDate: Tue Oct 3 06:50:02 2023 +0200

    tdf#157370 Bring A11y issue to attention in the document view
    
    when issue is clicked on in the A11y sidebar
    
    Change-Id: I9122e608e88568fa2deeee91b6cf4616b0f61db8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157200
    Tested-by: Jenkins
    Reviewed-by: Jim Raykowski <rayk...@gmail.com>

diff --git a/sw/inc/view.hxx b/sw/inc/view.hxx
index dee2363dedd9..c6add7e23364 100644
--- a/sw/inc/view.hxx
+++ b/sw/inc/view.hxx
@@ -32,6 +32,8 @@
 #include "swtypes.hxx"
 #include "shellid.hxx"
 
+#include <svx/sdr/overlay/overlayobject.hxx>
+
 class SwTextFormatColl;
 class SwPageDesc;
 class SwFrameFormat;
@@ -68,6 +70,7 @@ enum class SotExchangeDest;
 class SwCursorShell;
 enum class SvxSearchCmd;
 enum class SelectionType : sal_Int32;
+class SwNode;
 
 namespace com::sun::star::view { class XSelectionSupplier; }
 namespace sfx2 { class FileDialogHelper; }
@@ -716,6 +719,19 @@ public:
     virtual std::optional<OString> getLOKPayload(int nType, int nViewId) const 
override;
 
     bool IsHighlightCharDF() { return m_bIsHighlightCharDF; }
+
+private:
+    AutoTimer m_aBringToAttentionBlinkTimer;
+    size_t m_nBringToAttentionBlinkTimeOutsRemaining;
+
+    std::unique_ptr<sdr::overlay::OverlayObject> 
m_xBringToAttentionOverlayObject;
+
+    DECL_LINK(BringToAttentionBlinkTimerHdl, Timer*, void);
+
+public:
+    void BringToAttention(std::vector<basegfx::B2DRange>&& aRanges = {});
+    void BringToAttention(const tools::Rectangle& rRect);
+    void BringToAttention(const SwNode* pNode);
 };
 
 inline tools::Long SwView::GetXScroll() const
diff --git a/sw/source/core/access/AccessibilityIssue.cxx 
b/sw/source/core/access/AccessibilityIssue.cxx
index 6ec39238cda9..8056408a5b02 100644
--- a/sw/source/core/access/AccessibilityIssue.cxx
+++ b/sw/source/core/access/AccessibilityIssue.cxx
@@ -28,6 +28,11 @@
 #include <svx/svdpage.hxx>
 #include <svx/svxdlg.hxx>
 
+#include <svx/svdview.hxx>
+#include <flyfrm.hxx>
+#include <txatbase.hxx>
+#include <txtfrm.hxx>
+
 namespace sw
 {
 AccessibilityIssue::AccessibilityIssue(sfx::AccessibilityIssueID eIssueID)
@@ -75,6 +80,21 @@ void AccessibilityIssue::gotoIssue() const
         {
             SwWrtShell* pWrtShell = 
TempIssueObject.m_pDoc->GetDocShell()->GetWrtShell();
             bool bSelected = pWrtShell->GotoFly(TempIssueObject.m_sObjectID, 
FLYCNTTYPE_ALL, true);
+
+            // bring issue to attention
+            if (bSelected)
+            {
+                if (const SwFlyFrameFormat* pFlyFormat
+                    = m_pDoc->FindFlyByName(TempIssueObject.m_sObjectID, 
SwNodeType::NONE))
+                {
+                    if (SwFlyFrame* pFlyFrame
+                        = SwIterator<SwFlyFrame, 
SwFormat>(*pFlyFormat).First())
+                    {
+                        
pWrtShell->GetView().BringToAttention(pFlyFrame->getFrameArea().SVRect());
+                    }
+                }
+            }
+
             if (bSelected && pWrtShell->IsFrameSelected())
             {
                 pWrtShell->HideCursor();
@@ -82,8 +102,19 @@ void AccessibilityIssue::gotoIssue() const
             }
 
             if (!bSelected && TempIssueObject.m_eIssueObject == 
IssueObject::TEXTFRAME)
+            {
                 pWrtShell->GotoDrawingObject(TempIssueObject.m_sObjectID);
 
+                // bring issue to attention
+                if (SdrPage* pPage
+                    = 
pWrtShell->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0))
+                {
+                    if (SdrObject* pObj = 
pPage->GetObjByName(TempIssueObject.m_sObjectID))
+                    {
+                        
pWrtShell->GetView().BringToAttention(pObj->GetLogicRect());
+                    }
+                }
+            }
             if (comphelper::LibreOfficeKit::isActive())
                 pWrtShell->ShowCursor();
         }
@@ -94,6 +125,17 @@ void AccessibilityIssue::gotoIssue() const
             if (pWrtShell->IsFrameSelected())
                 pWrtShell->LeaveSelFrameMode();
             pWrtShell->GotoDrawingObject(TempIssueObject.m_sObjectID);
+
+            // bring issue to attention
+            if (SdrPage* pPage
+                = 
pWrtShell->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0))
+            {
+                if (SdrObject* pObj = 
pPage->GetObjByName(TempIssueObject.m_sObjectID))
+                {
+                    
pWrtShell->GetView().BringToAttention(pObj->GetLogicRect());
+                }
+            }
+
             if (comphelper::LibreOfficeKit::isActive())
                 pWrtShell->ShowCursor();
         }
@@ -107,6 +149,17 @@ void AccessibilityIssue::gotoIssue() const
                 if (!bIsDesignMode)
                     pWrtShell->GetView().GetFormShell()->SetDesignMode(true);
                 pWrtShell->GotoDrawingObject(TempIssueObject.m_sObjectID);
+
+                // bring issue to attention
+                if (SdrPage* pPage
+                    = 
pWrtShell->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0))
+                {
+                    if (SdrObject* pObj = 
pPage->GetObjByName(TempIssueObject.m_sObjectID))
+                    {
+                        
pWrtShell->GetView().BringToAttention(pObj->GetLogicRect());
+                    }
+                }
+
                 if (comphelper::LibreOfficeKit::isActive())
                     pWrtShell->ShowCursor();
             }
@@ -116,6 +169,17 @@ void AccessibilityIssue::gotoIssue() const
         {
             SwWrtShell* pWrtShell = 
TempIssueObject.m_pDoc->GetDocShell()->GetWrtShell();
             pWrtShell->GotoTable(TempIssueObject.m_sObjectID);
+
+            // bring issue to attention
+            if (SwTable* pTmpTable = SwTable::FindTable(
+                    
TempIssueObject.m_pDoc->FindTableFormatByName(TempIssueObject.m_sObjectID)))
+            {
+                if (SwTableNode* pTableNode = pTmpTable->GetTableNode())
+                {
+                    pWrtShell->GetView().BringToAttention(pTableNode);
+                }
+            }
+
             if (comphelper::LibreOfficeKit::isActive())
                 pWrtShell->ShowCursor();
         }
@@ -133,6 +197,10 @@ void AccessibilityIssue::gotoIssue() const
             pPaM->SetMark();
             *pPaM->GetMark() = aMark;
             pWrtShell->EndAllAction();
+
+            // bring issue to attention
+            pWrtShell->GetView().BringToAttention(pContentNode);
+
             if (comphelper::LibreOfficeKit::isActive())
                 pWrtShell->ShowCursor();
         }
@@ -141,7 +209,25 @@ void AccessibilityIssue::gotoIssue() const
         {
             SwWrtShell* pWrtShell = 
TempIssueObject.m_pDoc->GetDocShell()->GetWrtShell();
             if (TempIssueObject.m_pTextFootnote)
+            {
                 
pWrtShell->GotoFootnoteAnchor(*TempIssueObject.m_pTextFootnote);
+
+                // bring issue to attention
+                const SwTextNode& rTextNode = 
TempIssueObject.m_pTextFootnote->GetTextNode();
+                if (SwTextFrame* pFrame
+                    = 
static_cast<SwTextFrame*>(rTextNode.getLayoutFrame(pWrtShell->GetLayout())))
+                {
+                    auto nStart = TempIssueObject.m_pTextFootnote->GetStart();
+                    auto nEnd = nStart + 1;
+                    SwPosition aStartPos(rTextNode, nStart), 
aEndPos(rTextNode, nEnd);
+                    SwRect aStartCharRect, aEndCharRect;
+                    pFrame->GetCharRect(aStartCharRect, aStartPos);
+                    pFrame->GetCharRect(aEndCharRect, aEndPos);
+                    tools::Rectangle aRect(aStartCharRect.Left() - 50, 
aStartCharRect.Top(),
+                                           aEndCharRect.Right() + 50, 
aStartCharRect.Bottom());
+                    pWrtShell->GetView().BringToAttention(aRect);
+                }
+            }
             if (comphelper::LibreOfficeKit::isActive())
                 pWrtShell->ShowCursor();
         }
diff --git a/sw/source/uibase/uiview/view.cxx b/sw/source/uibase/uiview/view.cxx
index 0e998a57fa71..85138778bf65 100644
--- a/sw/source/uibase/uiview/view.cxx
+++ b/sw/source/uibase/uiview/view.cxx
@@ -111,6 +111,14 @@
 #include <svtools/embedhlp.hxx>
 #include <tools/UnitConversion.hxx>
 
+#include <svx/sdr/overlay/overlayselection.hxx>
+#include <svx/sdr/overlay/overlayobject.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svdview.hxx>
+#include <node2lay.hxx>
+#include <cntfrm.hxx>
+
 using namespace ::com::sun::star;
 using namespace ::com::sun::star::uno;
 using namespace ::com::sun::star::lang;
@@ -795,7 +803,9 @@ SwView::SwView(SfxViewFrame& _rFrame, SfxViewShell* pOldSh)
     m_bIsPreviewDoubleClick(false),
     m_bMakeSelectionVisible(false),
     m_bForceChangesToolbar(true),
-    m_nLOKPageUpDownOffset(0)
+    m_nLOKPageUpDownOffset(0),
+    m_aBringToAttentionBlinkTimer("SwView m_aBringToAttentionBlinkTimer"),
+    m_nBringToAttentionBlinkTimeOutsRemaining(0)
 {
     static bool bRequestDoubleBuffering = getenv("VCL_DOUBLEBUFFERING_ENABLE");
     if (bRequestDoubleBuffering)
@@ -1099,6 +1109,10 @@ SwView::SwView(SfxViewFrame& _rFrame, SfxViewShell* 
pOldSh)
 
     if (!bFuzzing)
         GetViewFrame().GetWindow().AddChildEventListener(LINK(this, SwView, 
WindowChildEventListener));
+
+    m_aBringToAttentionBlinkTimer.SetInvokeHandler(
+                LINK(this, SwView, BringToAttentionBlinkTimerHdl));
+    m_aBringToAttentionBlinkTimer.SetTimeout(350);
 }
 
 SwViewGlueDocShell::SwViewGlueDocShell(SwView& rView, SwDocShell& rDocSh)
@@ -1991,6 +2005,94 @@ bool SwView::IsDataSourceAvailable(const OUString 
sDataSourceName)
     return xDatabaseContext->hasByName(sDataSourceName);
 }
 
+void SwView::BringToAttention(std::vector<basegfx::B2DRange>&& aRanges)
+{
+    m_nBringToAttentionBlinkTimeOutsRemaining = 0;
+    m_aBringToAttentionBlinkTimer.Stop();
+    if (aRanges.empty())
+        m_xBringToAttentionOverlayObject.reset();
+    else
+    {
+        m_xBringToAttentionOverlayObject.reset(
+                    new 
sdr::overlay::OverlaySelection(sdr::overlay::OverlayType::Invert,
+                                                       Color(), 
std::move(aRanges),
+                                                       true /*unused for 
Invert type*/));
+        m_nBringToAttentionBlinkTimeOutsRemaining = 4;
+        m_aBringToAttentionBlinkTimer.Start();
+    }
+}
+
+void SwView::BringToAttention(const tools::Rectangle& rRect)
+{
+    std::vector<basegfx::B2DRange> aRanges{ basegfx::B2DRange(rRect.Left(), 
rRect.Top(),
+                                                              rRect.Right(), 
rRect.Bottom()) };
+    BringToAttention(std::move(aRanges));
+}
+
+void SwView::BringToAttention(const SwNode* pNode)
+{
+    if (!pNode)
+        return;
+
+    std::vector<basegfx::B2DRange> aRanges;
+    const SwFrame* pFrame;
+    if (pNode->IsContentNode())
+    {
+        pFrame = 
pNode->GetContentNode()->getLayoutFrame(GetWrtShell().GetLayout());
+    }
+    else
+    {
+        // section and table nodes
+        SwNode2Layout aTmp(*pNode, pNode->GetIndex() - 1);
+        pFrame = aTmp.NextFrame();
+    }
+    while (pFrame)
+    {
+        const SwRect& rFrameRect = pFrame->getFrameArea();
+        if (!rFrameRect.IsEmpty())
+            aRanges.emplace_back(rFrameRect.Left(), rFrameRect.Top() + 
pFrame->GetTopMargin(),
+                                 rFrameRect.Right(), rFrameRect.Bottom());
+        if (!pFrame->IsFlowFrame())
+            break;
+        const SwFlowFrame* pFollow = 
SwFlowFrame::CastFlowFrame(pFrame)->GetFollow();
+        if (!pFollow)
+            break;
+        pFrame = &pFollow->GetFrame();
+    }
+    BringToAttention(std::move(aRanges));
+}
+
+IMPL_LINK_NOARG(SwView, BringToAttentionBlinkTimerHdl, Timer*, void)
+{
+    if (GetDrawView() && m_xBringToAttentionOverlayObject)
+    {
+        if (SdrView* pView = GetDrawView())
+        {
+            if (SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(0))
+            {
+                const rtl::Reference<sdr::overlay::OverlayManager>& 
xOverlayManager
+                    = pPaintWindow->GetOverlayManager();
+                if (m_nBringToAttentionBlinkTimeOutsRemaining % 2 == 0)
+                    xOverlayManager->add(*m_xBringToAttentionOverlayObject);
+                else
+                    xOverlayManager->remove(*m_xBringToAttentionOverlayObject);
+                --m_nBringToAttentionBlinkTimeOutsRemaining;
+            }
+            else
+                m_nBringToAttentionBlinkTimeOutsRemaining = 0;
+        }
+        else
+            m_nBringToAttentionBlinkTimeOutsRemaining = 0;
+    }
+    else
+        m_nBringToAttentionBlinkTimeOutsRemaining = 0;
+    if (m_nBringToAttentionBlinkTimeOutsRemaining == 0)
+    {
+        m_xBringToAttentionOverlayObject.reset();
+        m_aBringToAttentionBlinkTimer.Stop();
+    }
+}
+
 namespace sw {
 
 void InitPrintOptionsFromApplication(SwPrintData & o_rData, bool const bWeb)

Reply via email to