gecko-dev/accessible/base/TextLeafRange.h

361 lines
12 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_TextLeafRange_h__
#define mozilla_a11y_TextLeafRange_h__
#include <stdint.h>
#include "AccAttributes.h"
#include "nsDirection.h"
#include "nsIAccessibleText.h"
class nsRange;
namespace mozilla {
namespace dom {
class Document;
}
namespace a11y {
class Accessible;
class LocalAccessible;
/**
* Represents a point within accessible text.
* This is stored as a leaf Accessible and an offset into that Accessible.
* For an empty Accessible, the offset will always be 0.
* This will eventually replace TextPoint. Unlike TextPoint, this does not
* use HyperTextAccessible offsets.
*/
class TextLeafPoint final {
public:
TextLeafPoint(Accessible* aAcc, int32_t aOffset);
/**
* Constructs an invalid TextPoint (mAcc is null).
* A TextLeafPoint in this state will evaluate to false.
* mAcc can be set later. Alternatively, this can be used to indicate an error
* (e.g. if a requested point couldn't be found).
*/
TextLeafPoint() : mAcc(nullptr), mOffset(0) {}
/**
* Construct a TextLeafPoint representing the caret.
* The actual offset used for the caret differs depending on whether the
* caret is at the end of a line and the query being made. Thus, mOffset on
* the returned TextLeafPoint is not a valid offset.
*/
static TextLeafPoint GetCaret(Accessible* aAcc) {
return TextLeafPoint(aAcc, nsIAccessibleText::TEXT_OFFSET_CARET);
}
Accessible* mAcc;
int32_t mOffset;
bool operator==(const TextLeafPoint& aPoint) const {
return mAcc == aPoint.mAcc && mOffset == aPoint.mOffset;
}
bool operator!=(const TextLeafPoint& aPoint) const {
return !(*this == aPoint);
}
bool operator<(const TextLeafPoint& aPoint) const;
bool operator<=(const TextLeafPoint& aPoint) const;
/**
* A valid TextLeafPoint evaluates to true. An invalid TextLeafPoint
* evaluates to false.
*/
explicit operator bool() const { return !!mAcc; }
bool IsCaret() const {
return mOffset == nsIAccessibleText::TEXT_OFFSET_CARET;
}
bool IsCaretAtEndOfLine() const;
/**
* Get a TextLeafPoint at the actual caret offset.
* This should only be called on a TextLeafPoint created with GetCaret.
* If aAdjustAtEndOfLine is true, the point will be adjusted if the caret is
* at the end of a line so that word and line boundaries can be calculated
* correctly.
*/
TextLeafPoint ActualizeCaret(bool aAdjustAtEndOfLine = true) const;
enum class BoundaryFlags : uint32_t {
eDefaultBoundaryFlags = 0,
// Return point unchanged if it is at the given boundary type.
eIncludeOrigin = 1 << 0,
// If current point is in editable, return point within samme editable.
eStopInEditable = 1 << 1,
// Skip over list items in searches and don't consider them line or
// paragraph starts.
eIgnoreListItemMarker = 1 << 2,
};
/**
* Find a boundary (word start, line start, etc.) in a specific direction.
* If no boundary is found, the start/end of the document is returned
* (depending on the direction).
*/
TextLeafPoint FindBoundary(
AccessibleTextBoundary aBoundaryType, nsDirection aDirection,
BoundaryFlags aFlags = BoundaryFlags::eDefaultBoundaryFlags) const;
/**
* These two functions find a line start boundary within the same
* LocalAccessible as this. That is, they do not cross Accessibles. If no
* boundary is found, an invalid TextLeafPoint is returned.
* These are used by FindBoundary. Most callers will want FindBoundary
* instead.
*/
TextLeafPoint FindPrevLineStartSameLocalAcc(bool aIncludeOrigin) const;
TextLeafPoint FindNextLineStartSameLocalAcc(bool aIncludeOrigin) const;
/**
* These two functions find a word start boundary within the same
* Accessible as this. That is, they do not cross Accessibles. If no
* boundary is found, an invalid TextLeafPoint is returned.
* These are used by FindBoundary. Most callers will want FindBoundary
* instead.
*/
TextLeafPoint FindPrevWordStartSameAcc(bool aIncludeOrigin) const;
TextLeafPoint FindNextWordStartSameAcc(bool aIncludeOrigin) const;
/**
* Get the text attributes at this point.
* If aIncludeDefaults is true, default attributes on the HyperTextAccessible
* will be included.
*/
already_AddRefed<AccAttributes> GetTextAttributes(
bool aIncludeDefaults = true) const;
/**
* Get Get the text attributes at this point in a LocalAccessible.
* This is used by GetTextAttributes. Most callers will want GetTextAttributes
* instead.
*/
already_AddRefed<AccAttributes> GetTextAttributesLocalAcc(
bool aIncludeDefaults = true) const;
/**
* Get the offsets of all spelling errors in a given LocalAccessible. This
* should only be used when pushing the cache. Most callers will want
* FindTextAttrsStart instead.
*/
static nsTArray<int32_t> GetSpellingErrorOffsets(LocalAccessible* aAcc);
/**
* Queue a cache update for a spelling error in a given DOM range.
*/
static void UpdateCachedSpellingError(dom::Document* aDocument,
const nsRange& aRange);
/**
* Find the start of a run of text attributes in a specific direction.
* A text attributes run is a span of text where the attributes are the same.
* If no boundary is found, the start/end of the container is returned
* (depending on the direction).
* If aIncludeorigin is true and this is at a boundary, this will be
* returned unchanged.
*/
TextLeafPoint FindTextAttrsStart(nsDirection aDirection,
bool aIncludeOrigin = false) const;
/**
* Returns a rect (in dev pixels) describing position and size of
* the character at mOffset in mAcc. This rect is screen-relative.
* This function only works on remote accessibles, and assumes caching
* is enabled.
*/
LayoutDeviceIntRect CharBounds();
/**
* Returns true if the given point (in screen coords) is contained
* in the char bounds of the current TextLeafPoint. Returns false otherwise.
* If the current point is an empty container, we use the acc's bounds instead
* of char bounds. Because this depends on CharBounds, this function only
* works on remote accessibles, and assumes caching is enabled.
*/
bool ContainsPoint(int32_t aX, int32_t aY);
bool IsLineFeedChar() const { return GetChar() == '\n'; }
bool IsSpace() const;
bool IsParagraphStart(bool aIgnoreListItemMarker = false) const {
return mOffset == 0 &&
FindParagraphSameAcc(eDirPrevious, true, aIgnoreListItemMarker);
}
/**
* Translate given TextLeafPoint into a DOM point.
*/
MOZ_CAN_RUN_SCRIPT std::pair<nsIContent*, int32_t> ToDOMPoint(
bool aIncludeGenerated = true) const;
private:
bool IsEmptyLastLine() const;
bool IsDocEdge(nsDirection aDirection) const;
bool IsLeafAfterListItemMarker() const;
char16_t GetChar() const;
TextLeafPoint FindLineStartSameRemoteAcc(nsDirection aDirection,
bool aIncludeOrigin) const;
/**
* Helper which just calls the appropriate function based on whether mAcc
*is local or remote.
*/
TextLeafPoint FindLineStartSameAcc(nsDirection aDirection,
bool aIncludeOrigin,
bool aIgnoreListItemMarker = false) const;
TextLeafPoint FindLineEnd(nsDirection aDirection, BoundaryFlags aFlags) const;
TextLeafPoint FindWordEnd(nsDirection aDirection, BoundaryFlags aFlags) const;
TextLeafPoint FindParagraphSameAcc(nsDirection aDirection,
bool aIncludeOrigin,
bool aIgnoreListItemMarker = false) const;
bool IsInSpellingError() const;
/**
* Find a spelling error boundary in the same Accessible. This function
* searches for either start or end points, since either means a change in
* text attributes.
*/
TextLeafPoint FindSpellingErrorSameAcc(nsDirection aDirection,
bool aIncludeOrigin) const;
// Return the point immediately succeeding or preceding this leaf depending
// on given direction.
TextLeafPoint NeighborLeafPoint(nsDirection aDirection, bool aIsEditable,
bool aIgnoreListItemMarker) const;
/**
* This function assumes mAcc is a LocalAccessible.
* It iterates the continuations of mAcc's primary frame until it locates
* the continuation containing mOffset (a rendered offset). It then uses
* GetScreenRectInAppUnits to compute screen coords for the frame, resizing
* such that the resulting rect contains only one character.
*/
LayoutDeviceIntRect ComputeBoundsFromFrame() const;
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TextLeafPoint::BoundaryFlags)
/**
* Represents a range of accessible text.
* This will eventually replace TextRange.
*/
class TextLeafRange final {
public:
TextLeafRange(const TextLeafPoint& aStart, const TextLeafPoint& aEnd)
: mStart(aStart), mEnd(aEnd) {}
explicit TextLeafRange(const TextLeafPoint& aStart)
: mStart(aStart), mEnd(aStart) {}
explicit TextLeafRange() {}
/**
* A valid TextLeafRange evaluates to true. An invalid TextLeafRange
* evaluates to false.
*/
explicit operator bool() const { return !!mStart && !!mEnd; }
bool operator!=(const TextLeafRange& aOther) const {
return mEnd != aOther.mEnd || mStart != aOther.mStart;
}
bool operator==(const TextLeafRange& aOther) const {
return mEnd == aOther.mEnd && mStart == aOther.mStart;
}
TextLeafPoint Start() const { return mStart; }
void SetStart(const TextLeafPoint& aStart) { mStart = aStart; }
TextLeafPoint End() const { return mEnd; }
void SetEnd(const TextLeafPoint& aEnd) { mEnd = aEnd; }
bool Crop(Accessible* aContainer);
/**
* Returns a union rect (in dev pixels) of all character bounds in this range.
* This rect is screen-relative and inclusive of mEnd. This function only
* works on remote accessibles, and assumes caching is enabled.
*/
LayoutDeviceIntRect Bounds() const;
/**
* Set range as DOM selection.
* aSelectionNum is the selection index to use. If aSelectionNum is
* out of bounds for current selection ranges, or is -1, a new selection
* range is created.
*/
MOZ_CAN_RUN_SCRIPT bool SetSelection(int32_t aSelectionNum) const;
MOZ_CAN_RUN_SCRIPT void ScrollIntoView(uint32_t aScrollType) const;
private:
TextLeafPoint mStart;
TextLeafPoint mEnd;
public:
/**
* A TextLeafRange iterator will iterate through single leaf segments of the
* given range.
*/
class Iterator {
public:
Iterator(Iterator&& aOther)
: mRange(aOther.mRange),
mSegmentStart(aOther.mSegmentStart),
mSegmentEnd(aOther.mSegmentEnd) {}
static Iterator BeginIterator(const TextLeafRange& aRange);
static Iterator EndIterator(const TextLeafRange& aRange);
Iterator& operator++();
bool operator!=(const Iterator& aOther) const {
return mRange != aOther.mRange || mSegmentStart != aOther.mSegmentStart ||
mSegmentEnd != aOther.mSegmentEnd;
}
TextLeafRange operator*() {
return TextLeafRange(mSegmentStart, mSegmentEnd);
}
private:
explicit Iterator(const TextLeafRange& aRange) : mRange(aRange) {}
Iterator() = delete;
Iterator(const Iterator&) = delete;
Iterator& operator=(const Iterator&) = delete;
Iterator& operator=(const Iterator&&) = delete;
const TextLeafRange& mRange;
TextLeafPoint mSegmentStart;
TextLeafPoint mSegmentEnd;
};
Iterator begin() const { return Iterator::BeginIterator(*this); }
Iterator end() const { return Iterator::EndIterator(*this); }
};
} // namespace a11y
} // namespace mozilla
#endif