Bug 1203871 - Part 1. Add nsIFrame::GetCharacterRectsInRange. r?jfkthame
Masayuki suggests GetCharcterRectsInRange instead of first idea's API by part 2 implementation.
IME wants to need the width per character. Now nsTextFrame/nsIFrmae has only API to get point of string. So I want to add this method to calculate simply by comment #3.
If no text frame, I would like to return error due to no character. (Caller shouldn't call this API on non-text frame.)
MozReview-Commit-ID: LQHUTzhnGn
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -6602,16 +6602,24 @@ nsFrame::GetPointFromOffset(int32_t inOf
}
}
}
*outPoint = pt;
return NS_OK;
}
nsresult
+nsFrame::GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
+ nsTArray<nsRect>& aOutRect)
+{
+ /* no text */
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
nsFrame::GetChildFrameContainingOffset(int32_t inContentOffset, bool inHint, int32_t* outFrameContentOffset, nsIFrame **outChildFrame)
{
NS_PRECONDITION(outChildFrame && outFrameContentOffset, "Null parameter");
*outFrameContentOffset = (int32_t)inHint;
//the best frame to reflect any given offset would be a visible frame if possible
//i.e. we are looking for a valid frame to place the blinking caret
nsRect rect = GetRect();
if (!rect.width || !rect.height)
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -164,16 +164,19 @@ public:
nsEventStatus* aEventStatus) override;
virtual nsresult GetContentForEvent(mozilla::WidgetEvent* aEvent,
nsIContent** aContent) override;
virtual nsresult GetCursor(const nsPoint& aPoint,
nsIFrame::Cursor& aCursor) override;
virtual nsresult GetPointFromOffset(int32_t inOffset,
nsPoint* outPoint) override;
+ virtual nsresult GetCharacterRectsInRange(int32_t aInOffset,
+ int32_t aLength,
+ nsTArray<nsRect>& aOutRect) override;
virtual nsresult GetChildFrameContainingOffset(int32_t inContentOffset,
bool inHint,
int32_t* outFrameContentOffset,
nsIFrame** outChildFrame) override;
static nsresult GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
nsPeekOffsetStruct *aPos,
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1539,16 +1539,24 @@ public:
/**
* Get a point (in the frame's coordinate space) given an offset into
* the content. This point should be on the baseline of text with
* the correct horizontal offset
*/
virtual nsresult GetPointFromOffset(int32_t inOffset,
nsPoint* outPoint) = 0;
+
+ /**
+ * Get a list of character rects in a given range.
+ * This is similar version of GetPointFromOffset.
+ */
+ virtual nsresult GetCharacterRectsInRange(int32_t aInOffset,
+ int32_t aLength,
+ nsTArray<nsRect>& aRects) = 0;
/**
* Get the child frame of this frame which contains the given
* content offset. outChildFrame may be this frame, or nullptr on return.
* outContentOffset returns the content offset relative to the start
* of the returned node. You can also pass a hint which tells the method
* to stick to the end of the first found frame or the beginning of the
* next in case the offset falls on a boundary.
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -2955,19 +2955,19 @@ public:
*/
void ComputeJustification(Range aRange);
const nsStyleText* StyleText() { return mTextStyle; }
nsTextFrame* GetFrame() { return mFrame; }
// This may not be equal to the frame offset/length in because we may have
// adjusted for whitespace trimming according to the state bits set in the frame
// (for the static provider)
- const gfxSkipCharsIterator& GetStart() { return mStart; }
+ const gfxSkipCharsIterator& GetStart() const { return mStart; }
// May return INT32_MAX if that was given to the constructor
- uint32_t GetOriginalLength() {
+ uint32_t GetOriginalLength() const {
NS_ASSERTION(mLength != INT32_MAX, "Length not known");
return mLength;
}
const nsTextFragment* GetFragment() { return mFrag; }
gfxFontGroup* GetFontGroup() {
if (!mFontGroup)
InitFontGroupAndFontMetrics();
@@ -7167,84 +7167,183 @@ nsTextFrame::SetSelectedRange(uint32_t a
}
// Selection might change anything. Invalidate the overflow area.
f->InvalidateFrame();
f = static_cast<nsTextFrame*>(f->GetNextContinuation());
}
}
+void
+nsTextFrame::UpdateIteratorFromOffset(const PropertyProvider& aProperties,
+ int32_t& aInOffset,
+ gfxSkipCharsIterator& aIter)
+{
+ if (aInOffset < GetContentOffset()){
+ NS_WARNING("offset before this frame's content");
+ aInOffset = GetContentOffset();
+ } else if (aInOffset > GetContentEnd()) {
+ NS_WARNING("offset after this frame's content");
+ aInOffset = GetContentEnd();
+ }
+
+ int32_t trimmedOffset = aProperties.GetStart().GetOriginalOffset();
+ int32_t trimmedEnd = trimmedOffset + aProperties.GetOriginalLength();
+ aInOffset = std::max(aInOffset, trimmedOffset);
+ aInOffset = std::min(aInOffset, trimmedEnd);
+
+ aIter.SetOriginalOffset(aInOffset);
+
+ if (aInOffset < trimmedEnd &&
+ !aIter.IsOriginalCharSkipped() &&
+ !mTextRun->IsClusterStart(aIter.GetSkippedOffset())) {
+ NS_WARNING("called for non-cluster boundary");
+ FindClusterStart(mTextRun, trimmedOffset, &aIter);
+ }
+}
+
+nsPoint
+nsTextFrame::GetPointFromIterator(const gfxSkipCharsIterator& aIter,
+ PropertyProvider& aProperties)
+{
+ Range range(aProperties.GetStart().GetSkippedOffset(),
+ aIter.GetSkippedOffset());
+ gfxFloat advance = mTextRun->GetAdvanceWidth(range, &aProperties);
+ nscoord iSize = NSToCoordCeilClamped(advance);
+ nsPoint point;
+
+ if (mTextRun->IsVertical()) {
+ point.x = 0;
+ if (mTextRun->IsInlineReversed()) {
+ point.y = mRect.height - iSize;
+ } else {
+ point.y = iSize;
+ }
+ } else {
+ point.y = 0;
+ if (mTextRun->IsInlineReversed()) {
+ point.x = mRect.width - iSize;
+ } else {
+ point.x = iSize;
+ }
+ if (StyleContext()->IsTextCombined()) {
+ point.x *= GetTextCombineScaleFactor(this);
+ }
+ }
+ return point;
+}
+
nsresult
nsTextFrame::GetPointFromOffset(int32_t inOffset,
nsPoint* outPoint)
{
if (!outPoint)
return NS_ERROR_NULL_POINTER;
- outPoint->x = 0;
- outPoint->y = 0;
-
DEBUG_VERIFY_NOT_DIRTY(mState);
if (mState & NS_FRAME_IS_DIRTY)
return NS_ERROR_UNEXPECTED;
if (GetContentLength() <= 0) {
+ outPoint->x = 0;
+ outPoint->y = 0;
return NS_OK;
}
gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
if (!mTextRun)
return NS_ERROR_FAILURE;
PropertyProvider properties(this, iter, nsTextFrame::eInflated);
// Don't trim trailing whitespace, we want the caret to appear in the right
// place if it's positioned there
- properties.InitializeForDisplay(false);
-
- if (inOffset < GetContentOffset()){
- NS_WARNING("offset before this frame's content");
- inOffset = GetContentOffset();
- } else if (inOffset > GetContentEnd()) {
- NS_WARNING("offset after this frame's content");
- inOffset = GetContentEnd();
- }
- int32_t trimmedOffset = properties.GetStart().GetOriginalOffset();
- int32_t trimmedEnd = trimmedOffset + properties.GetOriginalLength();
- inOffset = std::max(inOffset, trimmedOffset);
- inOffset = std::min(inOffset, trimmedEnd);
-
- iter.SetOriginalOffset(inOffset);
-
- if (inOffset < trimmedEnd &&
- !iter.IsOriginalCharSkipped() &&
- !mTextRun->IsClusterStart(iter.GetSkippedOffset())) {
- NS_WARNING("GetPointFromOffset called for non-cluster boundary");
- FindClusterStart(mTextRun, trimmedOffset, &iter);
- }
-
- Range range(properties.GetStart().GetSkippedOffset(),
- iter.GetSkippedOffset());
- gfxFloat advance = mTextRun->GetAdvanceWidth(range, &properties);
- nscoord iSize = NSToCoordCeilClamped(advance);
-
- if (mTextRun->IsVertical()) {
- if (mTextRun->IsInlineReversed()) {
- outPoint->y = mRect.height - iSize;
+ properties.InitializeForDisplay(false);
+
+ UpdateIteratorFromOffset(properties, inOffset, iter);
+
+ *outPoint = GetPointFromIterator(iter, properties);
+
+ return NS_OK;
+}
+
+nsresult
+nsTextFrame::GetCharacterRectsInRange(int32_t aInOffset,
+ int32_t aLength,
+ nsTArray<nsRect>& aRects)
+{
+ DEBUG_VERIFY_NOT_DIRTY(mState);
+ if (mState & NS_FRAME_IS_DIRTY) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (GetContentLength() <= 0) {
+ return NS_OK;
+ }
+
+ if (!mTextRun) {
+ return NS_ERROR_FAILURE;
+ }
+
+ gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
+ PropertyProvider properties(this, iter, nsTextFrame::eInflated);
+ // Don't trim trailing whitespace, we want the caret to appear in the right
+ // place if it's positioned there
+ properties.InitializeForDisplay(false);
+
+ UpdateIteratorFromOffset(properties, aInOffset, iter);
+
+ for (int32_t i = 0; i < aLength; i++) {
+ if (aInOffset > GetContentEnd()) {
+ break;
+ }
+
+ if (!iter.IsOriginalCharSkipped() &&
+ !mTextRun->IsClusterStart(iter.GetSkippedOffset())) {
+ FindClusterStart(mTextRun,
+ properties.GetStart().GetOriginalOffset() +
+ properties.GetOriginalLength(),
+ &iter);
+ }
+
+ nsPoint point = GetPointFromIterator(iter, properties);
+ nsRect rect;
+ rect.x = point.x;
+ rect.y = point.y;
+
+ nscoord iSize = 0;
+ gfxSkipCharsIterator nextIter(iter);
+ if (aInOffset < GetContentEnd()) {
+ nextIter.AdvanceOriginal(1);
+ if (!nextIter.IsOriginalCharSkipped() &&
+ !mTextRun->IsClusterStart(nextIter.GetSkippedOffset())) {
+ FindClusterEnd(mTextRun, GetContentEnd(), &nextIter);
+ }
+
+ gfxFloat advance =
+ mTextRun->GetAdvanceWidth(Range(iter.GetSkippedOffset(),
+ nextIter.GetSkippedOffset()),
+ &properties);
+ iSize = NSToCoordCeilClamped(advance);
+ }
+
+ if (mTextRun->IsVertical()) {
+ rect.width = mRect.width;
+ rect.height = iSize;
} else {
- outPoint->y = iSize;
- }
- } else {
- if (mTextRun->IsInlineReversed()) {
- outPoint->x = mRect.width - iSize;
- } else {
- outPoint->x = iSize;
- }
- if (StyleContext()->IsTextCombined()) {
- outPoint->x *= GetTextCombineScaleFactor(this);
- }
+ rect.width = iSize;
+ rect.height = mRect.height;
+
+ if (StyleContext()->IsTextCombined()) {
+ rect.width *= GetTextCombineScaleFactor(this);
+ }
+ }
+ aRects.AppendElement(rect);
+
+ iter.AdvanceOriginal(1);
+ aInOffset++;
}
return NS_OK;
}
nsresult
nsTextFrame::GetChildFrameContainingOffset(int32_t aContentOffset,
bool aHint,
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -179,16 +179,19 @@ public:
uint32_t aSetLengthFlags = 0);
virtual nsresult GetOffsets(int32_t &start, int32_t &end)const override;
virtual void AdjustOffsetsForBidi(int32_t start, int32_t end) override;
virtual nsresult GetPointFromOffset(int32_t inOffset,
nsPoint* outPoint) override;
+ virtual nsresult GetCharacterRectsInRange(int32_t aInOffset,
+ int32_t aLength,
+ nsTArray<nsRect>& aRects) override;
virtual nsresult GetChildFrameContainingOffset(int32_t inContentOffset,
bool inHint,
int32_t* outFrameContentOffset,
nsIFrame** outChildFrame) override;
virtual bool IsVisibleInSelection(nsISelection* aSelection) override;
@@ -787,11 +790,23 @@ protected:
ContentOffsets GetCharacterOffsetAtFramePointInternal(nsPoint aPoint,
bool aForInsertionPoint);
void ClearFrameOffsetCache();
virtual bool HasAnyNoncollapsedCharacters() override;
void ClearMetrics(nsHTMLReflowMetrics& aMetrics);
+
+ /**
+ * UpdateIteratorFromOffset() updates the iterator from a given offset.
+ * Also, aInOffset may be updated to cluster start if aInOffset isn't
+ * the offset of cluster start.
+ */
+ void UpdateIteratorFromOffset(const PropertyProvider& aProperties,
+ int32_t& aInOffset,
+ gfxSkipCharsIterator& aIter);
+
+ nsPoint GetPointFromIterator(const gfxSkipCharsIterator& aIter,
+ PropertyProvider& aProperties);
};
#endif