summaryrefslogtreecommitdiff
path: root/starmath/source/ElementsDockingWindow.cxx
diff options
context:
space:
mode:
authorJan-Marek Glogowski <glogow@fbihome.de>2019-05-26 14:53:33 +0000
committerJan-Marek Glogowski <glogow@fbihome.de>2019-05-29 20:14:38 +0200
commita7e52282e36987f4ca6bed968e87ada6849b10e8 (patch)
tree891e7dd4e986528d55e6866801221273a87a3e17 /starmath/source/ElementsDockingWindow.cxx
parent3eba9602197da972b66f2b7cc72b61cbdffe5a52 (diff)
tdf#65587 SM implement ElementControl accessibility
This took me ages to implement. I'm still not 100% sure, it works correct. One main problem was the loop where the ATK listener will rebuild the list of children based on the current child count. Then there is the "broken" SPI bridge behaviour. I could actually test this with the gtk3 backend just fine, if I started LO *after* the accerciser. Otherwise the displayed tree will be really broken and the add and remove child events won't be correctly processed, because some Windows in the hierarchy will return a negative parent index. And generally the accerciser has various problems, with most result in Python backtraces an inconsistent app state, but even SIGSEGV happened a few time. Already have some patches for the easy reproducible ones. No idea what will happen on any other setup then Linux with ATK. Change-Id: I3280fd8622966be74e3833621952d95a2671d214 Reviewed-on: https://gerrit.libreoffice.org/73077 Tested-by: Jenkins Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
Diffstat (limited to 'starmath/source/ElementsDockingWindow.cxx')
-rw-r--r--starmath/source/ElementsDockingWindow.cxx155
1 files changed, 144 insertions, 11 deletions
diff --git a/starmath/source/ElementsDockingWindow.cxx b/starmath/source/ElementsDockingWindow.cxx
index d34062ddb158..45bfb7541099 100644
--- a/starmath/source/ElementsDockingWindow.cxx
+++ b/starmath/source/ElementsDockingWindow.cxx
@@ -261,8 +261,9 @@ const size_t SmElementsControl::m_aCategoriesSize = SAL_N_ELEMENTS(m_aCategories
SmElementsControl::SmElementsControl(vcl::Window *pParent)
: Control(pParent, WB_TABSTOP)
, mpDocShell(new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT))
- , m_nCurrentElement(0)
+ , m_nCurrentElement(SAL_MAX_UINT16)
, m_nCurrentRolloverElement(SAL_MAX_UINT16)
+ , m_nCurrentOffset(1) // Default offset of 1 due to the ScrollBar child
, mbVerticalMode(true)
, mxScroll(VclPtr<ScrollBar>::Create(this, WB_VERT))
, m_bFirstPaintAfterLayout(false)
@@ -311,6 +312,19 @@ SmElement* SmElementsControl::current() const
return (nCur < maElementList.size()) ? maElementList[nCur].get() : nullptr;
}
+void SmElementsControl::setCurrentElement(sal_uInt16 nPos)
+{
+ if (m_nCurrentElement == nPos)
+ return;
+ if (nPos != SAL_MAX_UINT16 && nPos >= maElementList.size())
+ return;
+ if (m_xAccessible.is() && m_nCurrentElement != SAL_MAX_UINT16)
+ m_xAccessible->ReleaseFocus(m_nCurrentElement);
+ m_nCurrentElement = nPos;
+ if (m_xAccessible.is() && m_nCurrentElement != SAL_MAX_UINT16)
+ m_xAccessible->AcquireFocus();
+}
+
/**
* !pContext => layout only
*
@@ -357,6 +371,9 @@ void SmElementsControl::LayoutOrPaintContents(vcl::RenderContext *pContext)
x += boxX;
y = 0;
+ element->mBoxLocation = Point(x, y);
+ element->mBoxSize = Size(10, nControlHeight);
+
tools::Rectangle aSelectionRectangle(x + 5 - 1, y + 5,
x + 5 + 1, nControlHeight - 5);
@@ -369,6 +386,9 @@ void SmElementsControl::LayoutOrPaintContents(vcl::RenderContext *pContext)
x = 0;
y += boxY;
+ element->mBoxLocation = Point(x, y);
+ element->mBoxSize = Size(nControlWidth, 10);
+
tools::Rectangle aSelectionRectangle(x + 5, y + 5 - 1,
nControlWidth - 5, y + 5 + 1);
@@ -603,7 +623,7 @@ void SmElementsControl::MouseButtonDown(const MouseEvent& rMouseEvent)
tools::Rectangle rect(pPrevElement->mBoxLocation, pPrevElement->mBoxSize);
if (rect.IsInside(rMouseEvent.GetPosPixel()))
{
- m_nCurrentElement = m_nCurrentRolloverElement;
+ setCurrentElement(m_nCurrentRolloverElement);
maSelectHdlLink.Call(*const_cast<SmElement*>(pPrevElement));
collectUIInformation(OUString::number(m_nCurrentRolloverElement));
return;
@@ -617,7 +637,7 @@ void SmElementsControl::MouseButtonDown(const MouseEvent& rMouseEvent)
tools::Rectangle rect(element->mBoxLocation, element->mBoxSize);
if (rect.IsInside(rMouseEvent.GetPosPixel()))
{
- m_nCurrentElement = n;
+ setCurrentElement(n);
maSelectHdlLink.Call(*element);
collectUIInformation(OUString::number(n));
return;
@@ -697,8 +717,8 @@ void SmElementsControl::stepFocus(const bool bBackward)
sal_uInt16 nPos = nextElement(bBackward, nStartPos, nLastElement);
if (nStartPos != nPos)
{
- m_nCurrentElement = nPos;
m_nCurrentRolloverElement = SAL_MAX_UINT16;
+ setCurrentElement(nPos);
const tools::Rectangle outputRect(Point(0,0), GetOutputSizePixel());
const SmElement *pCur = maElementList[nPos].get();
@@ -752,7 +772,7 @@ void SmElementsControl::pageFocus(const bool bBackward)
if (nStartPos != nPos)
{
- m_nCurrentElement = nPos;
+ setCurrentElement(nPos);
if (bMoved)
scrollToElement(bBackward, maElementList[nPos].get());
Invalidate();
@@ -793,12 +813,18 @@ void SmElementsControl::KeyInput(const KeyEvent& rKEvt)
break;
case KEY_HOME:
- m_nCurrentElement = 0;
- mxScroll->DoScroll(0);
+ if (!maElementList.empty())
+ {
+ setCurrentElement(0);
+ mxScroll->DoScroll(0);
+ }
break;
case KEY_END:
- m_nCurrentElement = (maElementList.size() ? maElementList.size() - 1 : 0);
- mxScroll->DoScroll(mxScroll->GetRangeMax());
+ if (!maElementList.empty())
+ {
+ setCurrentElement(maElementList.size() - 1);
+ mxScroll->DoScroll(mxScroll->GetRangeMax());
+ }
break;
case KEY_PAGEUP:
@@ -964,8 +990,21 @@ void SmElementsControl::addElements(const SmElementDescr aElementsArray[], sal_u
void SmElementsControl::build()
{
+ // The order is important!
+ // 1. Ensure there are no items left, including the default scrollbar!
+ // 2. Release all the current accessible items.
+ // This will check for new items after releasing them!
+ // 3. Set the cursor element
maElementList.clear();
-
+ mxScroll->SetThumbPos(0);
+ mxScroll->Hide();
+ if (m_xAccessible.is())
+ m_xAccessible->ReleaseAllItems(true);
+ setCurrentElement(SAL_MAX_UINT16);
+
+ // The first element is the scrollbar. We can't change its indexInParent
+ // value, as this is set by being a child of the SmElementsControl.
+ m_nCurrentOffset = 1;
for (sal_uInt16 n = 0; n < SAL_N_ELEMENTS(m_aCategories); ++n)
{
if (msCurrentSetId == std::get<0>(m_aCategories[n]))
@@ -973,11 +1012,15 @@ void SmElementsControl::build()
addElements(std::get<1>(m_aCategories[n]), std::get<2>(m_aCategories[n]));
break;
}
+ else
+ m_nCurrentOffset += std::get<2>(m_aCategories[n]);
}
- m_nCurrentElement = 0;
m_nCurrentRolloverElement = SAL_MAX_UINT16;
LayoutOrPaintContents();
+ if (m_xAccessible.is())
+ m_xAccessible->AddAllItems();
+ setCurrentElement(0);
Invalidate();
}
@@ -991,6 +1034,96 @@ FactoryFunction SmElementsControl::GetUITestFactory() const
return ElementSelectorUIObject::create;
}
+bool SmElementsControl::itemIsSeparator(sal_uInt16 nPos) const
+{
+ if (nPos < m_nCurrentOffset || (nPos -= m_nCurrentOffset) >= maElementList.size())
+ return true;
+ return maElementList[nPos].get()->isSeparator();
+}
+
+css::uno::Reference<css::accessibility::XAccessible> SmElementsControl::CreateAccessible()
+{
+ if (!m_xAccessible.is())
+ {
+ m_xAccessible = new AccessibleSmElementsControl(*this);
+ m_xAccessible->AddAllItems();
+ }
+ return m_xAccessible.get();
+}
+
+bool SmElementsControl::itemTrigger(sal_uInt16 nPos)
+{
+ if (nPos < m_nCurrentOffset || (nPos -= m_nCurrentOffset) >= maElementList.size())
+ return false;
+
+ maSelectHdlLink.Call(*maElementList[nPos].get());
+ collectUIInformation(OUString::number(nPos));
+ return true;
+}
+
+tools::Rectangle SmElementsControl::itemPosRect(sal_uInt16 nPos) const
+{
+ if (nPos < m_nCurrentOffset || (nPos -= m_nCurrentOffset) >= maElementList.size())
+ return tools::Rectangle();
+
+ SmElement* pItem = maElementList[nPos].get();
+ return tools::Rectangle(pItem->mBoxLocation, pItem->mBoxSize);
+}
+
+bool SmElementsControl::itemIsVisible(sal_uInt16 nPos) const
+{
+ tools::Rectangle elementRect = itemPosRect(nPos);
+ if (elementRect.IsEmpty())
+ return false;
+
+ tools::Rectangle outputRect(Point(0, 0), GetOutputSizePixel());
+ return outputRect.IsInside(elementRect);
+}
+
+sal_uInt16 SmElementsControl::itemCount() const { return maElementList.size(); }
+
+sal_uInt16 SmElementsControl::itemHighlighted() const { return m_nCurrentElement; }
+
+void SmElementsControl::setItemHighlighted(sal_uInt16 nPos)
+{
+ if (m_nCurrentRolloverElement == nPos)
+ return;
+ if (nPos != SAL_MAX_UINT16 && nPos >= maElementList.size())
+ return;
+
+ if (maElementList[nPos]->isSeparator())
+ m_nCurrentRolloverElement = SAL_MAX_UINT16;
+ else
+ m_nCurrentRolloverElement = nPos;
+ Invalidate();
+}
+
+OUString SmElementsControl::itemName(sal_uInt16 nPos) const
+{
+ if (nPos < m_nCurrentOffset || (nPos -= m_nCurrentOffset) >= maElementList.size())
+ return OUString();
+
+ return maElementList[nPos]->getHelpText();
+}
+
+sal_uInt16 SmElementsControl::itemAtPos(const Point& rPoint) const
+{
+ sal_uInt16 nElementCount = maElementList.size();
+ for (sal_uInt16 n = 0; n < nElementCount; n++)
+ {
+ const SmElement* pItem = maElementList[n].get();
+ tools::Rectangle elementRect(pItem->mBoxLocation, pItem->mBoxSize);
+ if (elementRect.IsInside(rPoint))
+ return n;
+ }
+ return SAL_MAX_UINT16;
+}
+
+css::uno::Reference<css::accessibility::XAccessible> SmElementsControl::scrollbarAccessible() const
+{
+ return mxScroll && mxScroll->IsVisible() ? mxScroll->GetAccessible() : css::uno::Reference<css::accessibility::XAccessible>();
+}
+
SmElementsDockingWindow::SmElementsDockingWindow(SfxBindings* pInputBindings, SfxChildWindow* pChildWindow, vcl::Window* pParent) :
SfxDockingWindow(pInputBindings, pChildWindow, pParent, "DockingElements",
"modules/smath/ui/dockingelements.ui")