1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Accessibility: Add juce_AccessibilityTextHelpers.h

This commit is contained in:
ed 2021-07-21 17:16:01 +01:00
parent 588e776bb6
commit da57f65f3f
3 changed files with 165 additions and 97 deletions

View file

@ -252,6 +252,10 @@ namespace juce
#include "native/juce_MultiTouchMapper.h"
#endif
#if JUCE_WINDOWS
#include "native/accessibility/juce_AccessibilityTextHelpers.h"
#endif
namespace juce
{

View file

@ -0,0 +1,111 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace AccessibilityTextHelpers
{
enum class BoundaryType
{
character,
word,
line,
document
};
enum class Direction
{
forwards,
backwards
};
static int findTextBoundary (const AccessibilityTextInterface& textInterface,
int currentPosition,
BoundaryType boundary,
Direction direction)
{
const auto numCharacters = textInterface.getTotalNumCharacters();
const auto isForwards = (direction == Direction::forwards);
auto offsetWithDirecton = [isForwards] (int input) { return isForwards ? input : -input; };
switch (boundary)
{
case BoundaryType::character:
return jlimit (0, numCharacters, currentPosition + offsetWithDirecton (1));
case BoundaryType::word:
case BoundaryType::line:
{
const auto text = [&]() -> String
{
if (isForwards)
return textInterface.getText ({ currentPosition, textInterface.getTotalNumCharacters() });
const auto str = textInterface.getText ({ 0, currentPosition });
auto start = str.getCharPointer();
auto end = start.findTerminatingNull();
const auto size = getAddressDifference (end.getAddress(), start.getAddress());
String reversed;
if (size > 0)
{
reversed.preallocateBytes ((size_t) size);
auto destPtr = reversed.getCharPointer();
for (;;)
{
destPtr.write (*--end);
if (end == start)
break;
}
destPtr.writeNull();
}
return reversed;
}();
auto tokens = (boundary == BoundaryType::line ? StringArray::fromLines (text)
: StringArray::fromTokens (text, false));
return currentPosition + offsetWithDirecton (tokens[0].length());
}
case BoundaryType::document:
return isForwards ? numCharacters : 0;
}
jassertfalse;
return -1;
}
}
} // namespace juce

View file

@ -249,43 +249,19 @@ private:
if (auto* textInterface = owner->getHandler().getTextInterface())
{
auto numCharacters = textInterface->getTotalNumCharacters();
const auto boundaryType = getBoundaryType (unit);
if (numCharacters == 0)
{
selectionRange = {};
return S_OK;
}
const auto start = AccessibilityTextHelpers::findTextBoundary (*textInterface,
selectionRange.getStart(),
boundaryType,
AccessibilityTextHelpers::Direction::backwards);
if (unit == TextUnit_Character)
{
selectionRange.setStart (jlimit (0, numCharacters - 1, selectionRange.getStart()));
selectionRange.setEnd (selectionRange.getStart() + 1);
}
else if (unit == TextUnit_Paragraph
|| unit == TextUnit_Page
|| unit == TextUnit_Document)
{
selectionRange = { 0, numCharacters };
}
else if (unit == TextUnit_Word
|| unit == TextUnit_Format
|| unit == TextUnit_Line)
{
const auto boundaryType = (unit == TextUnit_Line ? BoundaryType::line : BoundaryType::word);
const auto end = AccessibilityTextHelpers::findTextBoundary (*textInterface,
start,
boundaryType,
AccessibilityTextHelpers::Direction::forwards);
auto start = findBoundary (*textInterface,
selectionRange.getStart(),
boundaryType,
NextEndpointDirection::backwards);
auto end = findBoundary (*textInterface,
start,
boundaryType,
NextEndpointDirection::forwards);
selectionRange = Range<int> (start, end);
}
selectionRange = Range<int> (start, end);
return S_OK;
}
@ -485,60 +461,37 @@ private:
{
return owner->withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
{
auto numCharacters = textInterface.getTotalNumCharacters();
if (count == 0 || numCharacters == 0)
if (count == 0 || textInterface.getTotalNumCharacters() == 0)
return S_OK;
const auto isStart = (endpoint == TextPatternRangeEndpoint_Start);
auto endpointToMove = (isStart ? selectionRange.getStart() : selectionRange.getEnd());
auto endpointToMove = (endpoint == TextPatternRangeEndpoint_Start ? selectionRange.getStart()
: selectionRange.getEnd());
if (unit == TextUnit_Character)
const auto direction = (count > 0 ? AccessibilityTextHelpers::Direction::forwards
: AccessibilityTextHelpers::Direction::backwards);
const auto boundaryType = getBoundaryType (unit);
// handle case where endpoint is on a boundary
if (AccessibilityTextHelpers::findTextBoundary (textInterface, endpointToMove, boundaryType, direction) == endpointToMove)
endpointToMove += (direction == AccessibilityTextHelpers::Direction::forwards ? 1 : -1);
int numMoved;
for (numMoved = 0; numMoved < std::abs (count); ++numMoved)
{
const auto targetPoint = jlimit (0, numCharacters, endpointToMove + count);
auto nextEndpoint = AccessibilityTextHelpers::findTextBoundary (textInterface,
endpointToMove,
boundaryType,
direction);
*pRetVal = targetPoint - endpointToMove;
setEndpointChecked (endpoint, targetPoint);
if (nextEndpoint == endpointToMove)
break;
return S_OK;
endpointToMove = nextEndpoint;
}
auto direction = (count > 0 ? NextEndpointDirection::forwards
: NextEndpointDirection::backwards);
if (unit == TextUnit_Paragraph
|| unit == TextUnit_Page
|| unit == TextUnit_Document)
{
*pRetVal = (direction == NextEndpointDirection::forwards ? 1 : -1);
setEndpointChecked (endpoint, numCharacters);
return S_OK;
}
if (unit == TextUnit_Word
|| unit == TextUnit_Format
|| unit == TextUnit_Line)
{
const auto boundaryType = unit == TextUnit_Line ? BoundaryType::line : BoundaryType::word;
// handle case where endpoint is on a boundary
if (findBoundary (textInterface, endpointToMove, boundaryType, direction) == endpointToMove)
endpointToMove += (direction == NextEndpointDirection::forwards ? 1 : -1);
int numMoved;
for (numMoved = 0; numMoved < std::abs (count); ++numMoved)
{
auto nextEndpoint = findBoundary (textInterface, endpointToMove, boundaryType, direction);
if (nextEndpoint == endpointToMove)
break;
endpointToMove = nextEndpoint;
}
*pRetVal = numMoved;
setEndpointChecked (endpoint, endpointToMove);
}
*pRetVal = numMoved;
setEndpointChecked (endpoint, endpointToMove);
return S_OK;
});
@ -583,28 +536,28 @@ private:
}
private:
enum class NextEndpointDirection { forwards, backwards };
enum class BoundaryType { word, line };
static int findBoundary (const AccessibilityTextInterface& textInterface,
int currentPosition,
BoundaryType boundary,
NextEndpointDirection direction)
static AccessibilityTextHelpers::BoundaryType getBoundaryType (TextUnit unit)
{
const auto text = [&]() -> String
switch (unit)
{
if (direction == NextEndpointDirection::forwards)
return textInterface.getText ({ currentPosition, textInterface.getTotalNumCharacters() });
case TextUnit_Character:
return AccessibilityTextHelpers::BoundaryType::character;
auto stdString = textInterface.getText ({ 0, currentPosition }).toStdString();
std::reverse (stdString.begin(), stdString.end());
return stdString;
}();
case TextUnit_Format:
case TextUnit_Word:
return AccessibilityTextHelpers::BoundaryType::word;
auto tokens = (boundary == BoundaryType::line ? StringArray::fromLines (text)
: StringArray::fromTokens (text, false));
case TextUnit_Line:
return AccessibilityTextHelpers::BoundaryType::line;
return currentPosition + (direction == NextEndpointDirection::forwards ? tokens[0].length() : -(tokens[0].length()));
case TextUnit_Paragraph:
case TextUnit_Page:
case TextUnit_Document:
return AccessibilityTextHelpers::BoundaryType::document;
};
jassertfalse;
return AccessibilityTextHelpers::BoundaryType::character;
}
void setEndpointChecked (TextPatternRangeEndpoint endpoint, int newEndpoint)