Example #1
0
  /**
   * Returns the next valid offset within this directional run, skipping conjuncts and zero-width
   * characters. This should not be called to walk off the end of the line, since the returned
   * values might not be valid on neighboring lines. If the returned offset is less than zero or
   * greater than the line length, the offset should be recomputed on the preceding or following
   * line, respectively.
   *
   * @param runIndex the run index
   * @param runStart the start of the run
   * @param runLimit the limit of the run
   * @param runIsRtl true if the run is right-to-left
   * @param offset the offset
   * @param after true if the new offset should logically follow the provided offset
   * @return the new offset
   */
  private int getOffsetBeforeAfter(
      int runIndex, int runStart, int runLimit, boolean runIsRtl, int offset, boolean after) {

    if (runIndex < 0 || offset == (after ? mLen : 0)) {
      // Walking off end of line.  Since we don't know
      // what cursor positions are available on other lines, we can't
      // return accurate values.  These are a guess.
      if (after) {
        return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart;
      }
      return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart;
    }

    TextPaint wp = mWorkPaint;
    wp.set(mPaint);

    int spanStart = runStart;
    int spanLimit;
    if (mSpanned == null) {
      spanLimit = runLimit;
    } else {
      int target = after ? offset + 1 : offset;
      int limit = mStart + runLimit;
      while (true) {
        spanLimit =
            mSpanned.nextSpanTransition(mStart + spanStart, limit, MetricAffectingSpan.class)
                - mStart;
        if (spanLimit >= target) {
          break;
        }
        spanStart = spanLimit;
      }

      MetricAffectingSpan[] spans =
          mSpanned.getSpans(mStart + spanStart, mStart + spanLimit, MetricAffectingSpan.class);
      spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);

      if (spans.length > 0) {
        ReplacementSpan replacement = null;
        for (int j = 0; j < spans.length; j++) {
          MetricAffectingSpan span = spans[j];
          if (span instanceof ReplacementSpan) {
            replacement = (ReplacementSpan) span;
          } else {
            span.updateMeasureState(wp);
          }
        }

        if (replacement != null) {
          // If we have a replacement span, we're moving either to
          // the start or end of this span.
          return after ? spanLimit : spanStart;
        }
      }
    }

    int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
    int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
    if (mCharsValid) {
      return wp.getTextRunCursor(
          mChars, spanStart, spanLimit - spanStart, flags, offset, cursorOpt);
    } else {
      return wp.getTextRunCursor(
              mText, mStart + spanStart, mStart + spanLimit, flags, mStart + offset, cursorOpt)
          - mStart;
    }
  }