mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Accessibility: Added VoiceOver (macOS) and Narrator (Windows) accessibility screen reader support to juce_gui_basics
This commit is contained in:
parent
1df59f7469
commit
ec990202b1
133 changed files with 10158 additions and 1297 deletions
|
|
@ -26,6 +26,131 @@
|
|||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class CodeEditorComponent::CodeEditorAccessibilityHandler : public AccessibilityHandler
|
||||
{
|
||||
public:
|
||||
explicit CodeEditorAccessibilityHandler (CodeEditorComponent& codeEditorComponentToWrap)
|
||||
: AccessibilityHandler (codeEditorComponentToWrap,
|
||||
codeEditorComponentToWrap.isReadOnly() ? AccessibilityRole::staticText
|
||||
: AccessibilityRole::editableText,
|
||||
{},
|
||||
{ codeEditorComponentToWrap.isReadOnly() ? nullptr
|
||||
: std::make_unique<CodeEditorComponentTextInterface> (codeEditorComponentToWrap) }),
|
||||
codeEditorComponent (codeEditorComponentToWrap)
|
||||
{
|
||||
}
|
||||
|
||||
String getTitle() const override
|
||||
{
|
||||
return codeEditorComponent.isReadOnly() ? codeEditorComponent.document.getAllContent()
|
||||
: codeEditorComponent.getTitle();
|
||||
}
|
||||
|
||||
private:
|
||||
class CodeEditorComponentTextInterface : public AccessibilityTextInterface
|
||||
{
|
||||
public:
|
||||
explicit CodeEditorComponentTextInterface (CodeEditorComponent& codeEditorComponentToWrap)
|
||||
: codeEditorComponent (codeEditorComponentToWrap)
|
||||
{
|
||||
}
|
||||
|
||||
bool isDisplayingProtectedText() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int getTotalNumCharacters() const override
|
||||
{
|
||||
return codeEditorComponent.document.getAllContent().length();
|
||||
}
|
||||
|
||||
Range<int> getSelection() const override
|
||||
{
|
||||
return { codeEditorComponent.selectionStart.getPosition(),
|
||||
codeEditorComponent.selectionEnd.getPosition() };
|
||||
}
|
||||
|
||||
void setSelection (Range<int> r) override
|
||||
{
|
||||
auto& doc = codeEditorComponent.document;
|
||||
|
||||
codeEditorComponent.selectRegion (CodeDocument::Position (doc, r.getStart()),
|
||||
CodeDocument::Position (doc, r.getEnd()));
|
||||
}
|
||||
|
||||
String getText (Range<int> r) const override
|
||||
{
|
||||
auto& doc = codeEditorComponent.document;
|
||||
|
||||
return doc.getTextBetween (CodeDocument::Position (doc, r.getStart()),
|
||||
CodeDocument::Position (doc, r.getEnd()));
|
||||
}
|
||||
|
||||
void setText (const String& newText) override
|
||||
{
|
||||
codeEditorComponent.document.replaceAllContent (newText);
|
||||
}
|
||||
|
||||
int getTextInsertionOffset() const override
|
||||
{
|
||||
return codeEditorComponent.caretPos.getPosition();
|
||||
}
|
||||
|
||||
RectangleList<int> getTextBounds (Range<int> textRange) const override
|
||||
{
|
||||
auto& doc = codeEditorComponent.document;
|
||||
|
||||
RectangleList<int> localRects;
|
||||
|
||||
CodeDocument::Position startPosition (doc, textRange.getStart());
|
||||
CodeDocument::Position endPosition (doc, textRange.getEnd());
|
||||
|
||||
for (int line = startPosition.getLineNumber(); line <= endPosition.getLineNumber(); ++line)
|
||||
{
|
||||
CodeDocument::Position lineStart (doc, line, 0);
|
||||
CodeDocument::Position lineEnd (doc, line, doc.getLine (line).length());
|
||||
|
||||
if (line == startPosition.getLineNumber())
|
||||
lineStart = lineStart.movedBy (startPosition.getIndexInLine());
|
||||
|
||||
if (line == endPosition.getLineNumber())
|
||||
lineEnd = { doc, line, endPosition.getIndexInLine() };
|
||||
|
||||
auto startPos = codeEditorComponent.getCharacterBounds (lineStart).getTopLeft();
|
||||
auto endPos = codeEditorComponent.getCharacterBounds (lineEnd).getTopLeft();
|
||||
|
||||
localRects.add (startPos.x,
|
||||
startPos.y,
|
||||
endPos.x - startPos.x,
|
||||
codeEditorComponent.getLineHeight());
|
||||
}
|
||||
|
||||
RectangleList<int> globalRects;
|
||||
|
||||
for (auto r : localRects)
|
||||
globalRects.add (codeEditorComponent.localAreaToGlobal (r));
|
||||
|
||||
return globalRects;
|
||||
}
|
||||
|
||||
int getOffsetAtPoint (Point<int> point) const override
|
||||
{
|
||||
return codeEditorComponent.getPositionAt (point.x, point.y).getPosition();
|
||||
}
|
||||
|
||||
private:
|
||||
CodeEditorComponent& codeEditorComponent;
|
||||
};
|
||||
|
||||
CodeEditorComponent& codeEditorComponent;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CodeEditorAccessibilityHandler)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class CodeEditorComponent::CodeEditorLine
|
||||
{
|
||||
public:
|
||||
|
|
@ -437,6 +562,8 @@ void CodeEditorComponent::setReadOnly (bool b) noexcept
|
|||
removeChildComponent (caret.get());
|
||||
else
|
||||
addAndMakeVisible (caret.get());
|
||||
|
||||
invalidateAccessibilityHandler();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -585,7 +712,12 @@ void CodeEditorComponent::retokenise (int startIndex, int endIndex)
|
|||
void CodeEditorComponent::updateCaretPosition()
|
||||
{
|
||||
if (caret != nullptr)
|
||||
{
|
||||
caret->setCaretPosition (getCharacterBounds (getCaretPos()));
|
||||
|
||||
if (auto* handler = getAccessibilityHandler())
|
||||
handler->notifyAccessibilityEvent (AccessibilityEvent::textSelectionChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeEditorComponent::moveCaretTo (const CodeDocument::Position& newPos, const bool highlighting)
|
||||
|
|
@ -598,38 +730,36 @@ void CodeEditorComponent::moveCaretTo (const CodeDocument::Position& newPos, con
|
|||
{
|
||||
if (dragType == notDragging)
|
||||
{
|
||||
if (std::abs (caretPos.getPosition() - selectionStart.getPosition())
|
||||
< std::abs (caretPos.getPosition() - selectionEnd.getPosition()))
|
||||
dragType = draggingSelectionStart;
|
||||
else
|
||||
dragType = draggingSelectionEnd;
|
||||
auto oldCaretPos = caretPos.getPosition();
|
||||
auto isStart = std::abs (oldCaretPos - selectionStart.getPosition())
|
||||
< std::abs (oldCaretPos - selectionEnd.getPosition());
|
||||
|
||||
dragType = isStart ? draggingSelectionStart : draggingSelectionEnd;
|
||||
}
|
||||
|
||||
if (dragType == draggingSelectionStart)
|
||||
{
|
||||
selectionStart = caretPos;
|
||||
|
||||
if (selectionEnd.getPosition() < selectionStart.getPosition())
|
||||
if (selectionEnd.getPosition() < caretPos.getPosition())
|
||||
{
|
||||
auto temp = selectionStart;
|
||||
selectionStart = selectionEnd;
|
||||
selectionEnd = temp;
|
||||
|
||||
setSelection (selectionEnd, caretPos);
|
||||
dragType = draggingSelectionEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
setSelection (caretPos, selectionEnd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
selectionEnd = caretPos;
|
||||
|
||||
if (selectionEnd.getPosition() < selectionStart.getPosition())
|
||||
if (caretPos.getPosition() < selectionStart.getPosition())
|
||||
{
|
||||
auto temp = selectionStart;
|
||||
selectionStart = selectionEnd;
|
||||
selectionEnd = temp;
|
||||
|
||||
setSelection (caretPos, selectionStart);
|
||||
dragType = draggingSelectionStart;
|
||||
}
|
||||
else
|
||||
{
|
||||
setSelection (selectionStart, caretPos);
|
||||
}
|
||||
}
|
||||
|
||||
rebuildLineTokensAsync();
|
||||
|
|
@ -644,6 +774,9 @@ void CodeEditorComponent::moveCaretTo (const CodeDocument::Position& newPos, con
|
|||
updateScrollBars();
|
||||
caretPositionMoved();
|
||||
|
||||
if (auto* handler = getAccessibilityHandler())
|
||||
handler->notifyAccessibilityEvent (AccessibilityEvent::textChanged);
|
||||
|
||||
if (appCommandManager != nullptr && selectionWasActive != isHighlightActive())
|
||||
appCommandManager->commandStatusChanged();
|
||||
}
|
||||
|
|
@ -653,8 +786,7 @@ void CodeEditorComponent::deselectAll()
|
|||
if (isHighlightActive())
|
||||
rebuildLineTokensAsync();
|
||||
|
||||
selectionStart = caretPos;
|
||||
selectionEnd = caretPos;
|
||||
setSelection (caretPos, caretPos);
|
||||
dragType = notDragging;
|
||||
}
|
||||
|
||||
|
|
@ -746,7 +878,7 @@ Rectangle<int> CodeEditorComponent::getCharacterBounds (const CodeDocument::Posi
|
|||
lineHeight };
|
||||
}
|
||||
|
||||
CodeDocument::Position CodeEditorComponent::getPositionAt (int x, int y)
|
||||
CodeDocument::Position CodeEditorComponent::getPositionAt (int x, int y) const
|
||||
{
|
||||
const int line = y / lineHeight + firstLineOnScreen;
|
||||
const int column = roundToInt ((x - (getGutterSize() - xOffset * charWidth)) / charWidth);
|
||||
|
|
@ -772,6 +904,9 @@ void CodeEditorComponent::insertText (const String& newText)
|
|||
|
||||
scrollToKeepCaretOnScreen();
|
||||
caretPositionMoved();
|
||||
|
||||
if (auto* handler = getAccessibilityHandler())
|
||||
handler->notifyAccessibilityEvent (AccessibilityEvent::textChanged);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -865,9 +1000,15 @@ void CodeEditorComponent::indentSelectedLines (const int spacesToAdd)
|
|||
}
|
||||
}
|
||||
|
||||
selectionStart = oldSelectionStart;
|
||||
selectionEnd = oldSelectionEnd;
|
||||
caretPos = oldCaret;
|
||||
setSelection (oldSelectionStart, oldSelectionEnd);
|
||||
|
||||
if (caretPos != oldCaret)
|
||||
{
|
||||
caretPos = oldCaret;
|
||||
|
||||
if (auto* handler = getAccessibilityHandler())
|
||||
handler->notifyAccessibilityEvent (AccessibilityEvent::textChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1341,6 +1482,20 @@ bool CodeEditorComponent::performCommand (const CommandID commandID)
|
|||
return true;
|
||||
}
|
||||
|
||||
void CodeEditorComponent::setSelection (CodeDocument::Position newSelectionStart,
|
||||
CodeDocument::Position newSelectionEnd)
|
||||
{
|
||||
if (selectionStart != newSelectionStart
|
||||
|| selectionEnd != newSelectionEnd)
|
||||
{
|
||||
selectionStart = newSelectionStart;
|
||||
selectionEnd = newSelectionEnd;
|
||||
|
||||
if (auto* handler = getAccessibilityHandler())
|
||||
handler->notifyAccessibilityEvent (AccessibilityEvent::textSelectionChanged);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CodeEditorComponent::addPopupMenuItems (PopupMenu& m, const MouseEvent*)
|
||||
{
|
||||
|
|
@ -1674,4 +1829,10 @@ String CodeEditorComponent::State::toString() const
|
|||
return String (lastTopLine) + ":" + String (lastCaretPos) + ":" + String (lastSelectionEnd);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> CodeEditorComponent::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<CodeEditorAccessibilityHandler> (*this);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public:
|
|||
/** Finds the character at a given on-screen position.
|
||||
The coordinates are relative to this component's top-left origin.
|
||||
*/
|
||||
CodeDocument::Position getPositionAt (int x, int y);
|
||||
CodeDocument::Position getPositionAt (int x, int y) const;
|
||||
|
||||
/** Returns the start of the selection as a position. */
|
||||
CodeDocument::Position getSelectionStart() const { return selectionStart; }
|
||||
|
|
@ -380,6 +380,8 @@ public:
|
|||
bool perform (const InvocationInfo&) override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
/** @internal */
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
|
|
@ -404,6 +406,8 @@ private:
|
|||
class GutterComponent;
|
||||
std::unique_ptr<GutterComponent> gutter;
|
||||
|
||||
class CodeEditorAccessibilityHandler;
|
||||
|
||||
enum DragType
|
||||
{
|
||||
notDragging,
|
||||
|
|
@ -442,6 +446,7 @@ private:
|
|||
void indentSelectedLines (int spacesToAdd);
|
||||
bool skipBackwardsToPreviousTab();
|
||||
bool performCommand (CommandID);
|
||||
void setSelection (CodeDocument::Position, CodeDocument::Position);
|
||||
|
||||
int indexToColumn (int line, int index) const noexcept;
|
||||
int columnToIndex (int line, int column) const noexcept;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue