| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE library.
 -    Copyright (c) 2022 - 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 7 End-User License
 -    Agreement and JUCE Privacy Policy.
 - 
 -    End User License Agreement: www.juce.com/juce-7-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
 - {
 - 
 - struct AccessibilityTextHelpers
 - {
 -     /*  Wraps a CharPtr into a stdlib-compatible iterator.
 - 
 -         MSVC's std::reverse_iterator requires the wrapped iterator to be default constructible
 -         when building in C++20 mode, but I don't really want to add public default constructors to
 -         the CharPtr types. Instead, we add a very basic default constructor here which sets the
 -         wrapped CharPtr to nullptr.
 -     */
 -     template <typename CharPtr>
 -     class CharPtrIteratorAdapter
 -     {
 -     public:
 -         using difference_type = int;
 -         using value_type = decltype (*std::declval<CharPtr>());
 -         using pointer = value_type*;
 -         using reference = value_type;
 -         using iterator_category = std::bidirectional_iterator_tag;
 - 
 -         CharPtrIteratorAdapter() = default;
 -         constexpr explicit CharPtrIteratorAdapter (CharPtr arg) : ptr (arg) {}
 - 
 -         constexpr auto operator*() const { return *ptr; }
 - 
 -         constexpr CharPtrIteratorAdapter& operator++()
 -         {
 -             ++ptr;
 -             return *this;
 -         }
 - 
 -         constexpr CharPtrIteratorAdapter& operator--()
 -         {
 -             --ptr;
 -             return *this;
 -         }
 - 
 -         constexpr bool operator== (const CharPtrIteratorAdapter& other) const { return ptr == other.ptr; }
 -         constexpr bool operator!= (const CharPtrIteratorAdapter& other) const { return ptr != other.ptr; }
 - 
 -         constexpr auto operator+ (difference_type offset) const { return CharPtrIteratorAdapter { ptr + offset }; }
 -         constexpr auto operator- (difference_type offset) const { return CharPtrIteratorAdapter { ptr - offset }; }
 - 
 -     private:
 -         CharPtr ptr { {} };
 -     };
 - 
 -     template <typename CharPtr>
 -     static auto makeCharPtrIteratorAdapter (CharPtr ptr)
 -     {
 -         return CharPtrIteratorAdapter<CharPtr> { ptr };
 -     }
 - 
 -     enum class BoundaryType
 -     {
 -         character,
 -         word,
 -         line,
 -         document
 -     };
 - 
 -     enum class Direction
 -     {
 -         forwards,
 -         backwards
 -     };
 - 
 -     enum class ExtendSelection
 -     {
 -         no,
 -         yes
 -     };
 - 
 -     /*  Indicates whether a function may return the current text position, in the case that the
 -         position already falls on a text unit boundary.
 -     */
 -     enum class IncludeThisBoundary
 -     {
 -         no,     //< Always search for the following boundary, even if the current position falls on a boundary
 -         yes     //< Return the current position if it falls on a boundary
 -     };
 - 
 -     /*  Indicates whether a word boundary should include any whitespaces that follow the
 -         non-whitespace characters.
 -     */
 -     enum class IncludeWhitespaceAfterWords
 -     {
 -         no,     //< The word ends on the first whitespace character
 -         yes     //< The word ends after the last whitespace character
 -     };
 - 
 -     /*  Like std::distance, but always does an O(N) count rather than an O(1) count, and doesn't
 -         require the iterators to have any member type aliases.
 -     */
 -     template <typename Iter>
 -     static int countDifference (Iter from, Iter to)
 -     {
 -         int distance = 0;
 - 
 -         while (from != to)
 -         {
 -             ++from;
 -             ++distance;
 -         }
 - 
 -         return distance;
 -     }
 - 
 -     /*  Returns the number of characters between ptr and the next word end in a specific
 -         direction.
 - 
 -         If ptr is inside a word, the result will be the distance to the end of the same
 -         word.
 -     */
 -     template <typename CharPtr>
 -     static int findNextWordEndOffset (CharPtr beginIn,
 -                                       CharPtr endIn,
 -                                       CharPtr ptrIn,
 -                                       Direction direction,
 -                                       IncludeThisBoundary includeBoundary,
 -                                       IncludeWhitespaceAfterWords includeWhitespace)
 -     {
 -         const auto begin = makeCharPtrIteratorAdapter (beginIn);
 -         const auto end   = makeCharPtrIteratorAdapter (endIn);
 -         const auto ptr   = makeCharPtrIteratorAdapter (ptrIn);
 - 
 -         const auto move = [&] (auto b, auto e, auto iter)
 -         {
 -             const auto isSpace = [] (juce_wchar c) { return CharacterFunctions::isWhitespace (c); };
 - 
 -             const auto start = [&]
 -             {
 -                 if (iter == b && includeBoundary == IncludeThisBoundary::yes)
 -                     return b;
 - 
 -                 const auto nudged = iter - (iter != b && includeBoundary == IncludeThisBoundary::yes ? 1 : 0);
 - 
 -                 return includeWhitespace == IncludeWhitespaceAfterWords::yes
 -                        ? std::find_if (nudged, e, isSpace)
 -                        : std::find_if_not (nudged, e, isSpace);
 -             }();
 - 
 -             const auto found = includeWhitespace == IncludeWhitespaceAfterWords::yes
 -                              ? std::find_if_not (start, e, isSpace)
 -                              : std::find_if (start, e, isSpace);
 - 
 -             return countDifference (iter, found);
 -         };
 - 
 -         return direction == Direction::forwards ? move (begin, end, ptr)
 -                                                 : -move (std::make_reverse_iterator (end),
 -                                                          std::make_reverse_iterator (begin),
 -                                                          std::make_reverse_iterator (ptr));
 -     }
 - 
 -     /*  Returns the number of characters between ptr and the beginning of the next line in a
 -         specific direction.
 -     */
 -     template <typename CharPtr>
 -     static int findNextLineOffset (CharPtr beginIn,
 -                                    CharPtr endIn,
 -                                    CharPtr ptrIn,
 -                                    Direction direction,
 -                                    IncludeThisBoundary includeBoundary)
 -     {
 -         const auto begin = makeCharPtrIteratorAdapter (beginIn);
 -         const auto end   = makeCharPtrIteratorAdapter (endIn);
 -         const auto ptr   = makeCharPtrIteratorAdapter (ptrIn);
 - 
 -         const auto findNewline = [] (auto from, auto to) { return std::find (from, to, juce_wchar { '\n' }); };
 - 
 -         if (direction == Direction::forwards)
 -         {
 -             if (ptr != begin && includeBoundary == IncludeThisBoundary::yes && *(ptr - 1) == '\n')
 -                 return 0;
 - 
 -             const auto newline = findNewline (ptr, end);
 -             return countDifference (ptr, newline) + (newline == end ? 0 : 1);
 -         }
 - 
 -         const auto rbegin   = std::make_reverse_iterator (ptr);
 -         const auto rend     = std::make_reverse_iterator (begin);
 - 
 -         return -countDifference (rbegin, findNewline (rbegin + (rbegin == rend || includeBoundary == IncludeThisBoundary::yes ? 0 : 1), rend));
 -     }
 - 
 -     /*  Unfortunately, the method of computing end-points of text units depends on context, and on
 -         the current platform.
 - 
 -         Some examples of different behaviour:
 -         - On Android, updating the cursor/selection always searches for the next text unit boundary;
 -           but on Windows, ExpandToEnclosingUnit() should not move the starting point of the
 -           selection if it already at a unit boundary. This means that we need both inclusive and
 -           exclusive methods for finding the next text boundary.
 -         - On Android, moving the cursor by 'words' should move to the first space following a
 -           non-space character in the requested direction. On Windows, a 'word' includes trailing
 -           whitespace, but not preceding whitespace. This means that we need a way of specifying
 -           whether whitespace should be included when navigating by words.
 -     */
 -     static int findTextBoundary (const AccessibilityTextInterface& textInterface,
 -                                  int currentPosition,
 -                                  BoundaryType boundary,
 -                                  Direction direction,
 -                                  IncludeThisBoundary includeBoundary,
 -                                  IncludeWhitespaceAfterWords includeWhitespace)
 -     {
 -         const auto numCharacters = textInterface.getTotalNumCharacters();
 -         const auto isForwards = (direction == Direction::forwards);
 -         const auto currentClamped = jlimit (0, numCharacters, currentPosition);
 - 
 -         switch (boundary)
 -         {
 -             case BoundaryType::character:
 -             {
 -                 const auto offset = includeBoundary == IncludeThisBoundary::yes ? 0
 -                                                                                 : (isForwards ? 1 : -1);
 -                 return jlimit (0, numCharacters, currentPosition + offset);
 -             }
 - 
 -             case BoundaryType::word:
 -             {
 -                 const auto str = textInterface.getText ({ 0, numCharacters });
 -                 return currentClamped + findNextWordEndOffset (str.begin(),
 -                                                                str.end(),
 -                                                                str.begin() + currentClamped,
 -                                                                direction,
 -                                                                includeBoundary,
 -                                                                includeWhitespace);
 -             }
 - 
 -             case BoundaryType::line:
 -             {
 -                 const auto str = textInterface.getText ({ 0, numCharacters });
 -                 return currentClamped + findNextLineOffset (str.begin(),
 -                                                             str.end(),
 -                                                             str.begin() + currentClamped,
 -                                                             direction,
 -                                                             includeBoundary);
 -             }
 - 
 -             case BoundaryType::document:
 -                 return isForwards ? numCharacters : 0;
 -         }
 - 
 -         jassertfalse;
 -         return -1;
 -     }
 - 
 -     /*  Adjusts the current text selection range, using an algorithm appropriate for cursor movement
 -         on Android.
 -     */
 -     static Range<int> findNewSelectionRangeAndroid (const AccessibilityTextInterface& textInterface,
 -                                                     BoundaryType boundaryType,
 -                                                     ExtendSelection extend,
 -                                                     Direction direction)
 -     {
 -         const auto oldPos = textInterface.getTextInsertionOffset();
 -         const auto cursorPos = findTextBoundary (textInterface,
 -                                                  oldPos,
 -                                                  boundaryType,
 -                                                  direction,
 -                                                  IncludeThisBoundary::no,
 -                                                  IncludeWhitespaceAfterWords::no);
 - 
 -         if (extend == ExtendSelection::no)
 -             return { cursorPos, cursorPos };
 - 
 -         const auto currentSelection = textInterface.getSelection();
 -         const auto start = currentSelection.getStart();
 -         const auto end   = currentSelection.getEnd();
 -         return Range<int>::between (cursorPos, oldPos == start ? end : start);
 -     }
 - };
 - 
 - } // namespace juce
 
 
  |