diff options
author | Cary Clark <cary@android.com> | 2010-12-15 04:57:30 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-12-15 04:57:30 -0800 |
commit | 052fc207e3b0c145df08f85c14a7946b5ae3b79f (patch) | |
tree | 0cafe512b96e7935a8172ba514cab53c38c44105 | |
parent | c7911ffd666a7e73131dfd68919b769086a093e0 (diff) | |
parent | d2966aa787e0dcc4d26a10b6f0dc9f3a2f51abe4 (diff) | |
download | external_webkit-052fc207e3b0c145df08f85c14a7946b5ae3b79f.zip external_webkit-052fc207e3b0c145df08f85c14a7946b5ae3b79f.tar.gz external_webkit-052fc207e3b0c145df08f85c14a7946b5ae3b79f.tar.bz2 |
Merge "improve text selection"
-rw-r--r-- | WebCore/platform/graphics/android/LayerAndroid.cpp | 20 | ||||
-rw-r--r-- | WebCore/platform/graphics/android/LayerAndroid.h | 2 | ||||
-rw-r--r-- | WebKit/android/nav/CachedRoot.cpp | 6 | ||||
-rw-r--r-- | WebKit/android/nav/SelectText.cpp | 432 | ||||
-rw-r--r-- | WebKit/android/nav/SelectText.h | 12 |
5 files changed, 334 insertions, 138 deletions
diff --git a/WebCore/platform/graphics/android/LayerAndroid.cpp b/WebCore/platform/graphics/android/LayerAndroid.cpp index f02136a..58d8772 100644 --- a/WebCore/platform/graphics/android/LayerAndroid.cpp +++ b/WebCore/platform/graphics/android/LayerAndroid.cpp @@ -255,7 +255,7 @@ protected: virtual bool onIRectGlyph(const SkIRect& , const SkBounder::GlyphRec& ) { - m_drewText = true; + m_drew = m_drewText = true; return false; } @@ -281,6 +281,8 @@ public: FindState(int x, int y) : m_x(x) , m_y(y) + , m_bestX(x) + , m_bestY(y) , m_best(0) { m_bitmap.setConfig(SkBitmap::kARGB_8888_Config, TOUCH_SLOP * 2, @@ -290,6 +292,8 @@ public: } const LayerAndroid* best() const { return m_best; } + int bestX() const { return m_bestX; } + int bestY() const { return m_bestY; } bool drew(SkPicture* picture, const SkRect& localBounds) { m_findCheck.reset(); @@ -301,7 +305,11 @@ public: bool drewText() { return m_findCheck.drewText(); } - void setBest(const LayerAndroid* best) { m_best = best; } + void setBest(const LayerAndroid* best) { + m_best = best; + m_bestX = m_x; + m_bestY = m_y; + } int x() const { return m_x; } int y() const { return m_y; } @@ -313,6 +321,8 @@ public: protected: int m_x; int m_y; + int m_bestX; + int m_bestY; const LayerAndroid* m_best; FindCheck m_findCheck; SkBitmap m_bitmap; @@ -340,14 +350,16 @@ void LayerAndroid::findInner(LayerAndroid::FindState& state) const state.setBest(this); // set last match (presumably on top) } -const LayerAndroid* LayerAndroid::find(int x, int y, SkPicture* root) const +const LayerAndroid* LayerAndroid::find(int* xPtr, int* yPtr, SkPicture* root) const { - FindState state(x, y); + FindState state(*xPtr, *yPtr); SkRect rootBounds; rootBounds.setEmpty(); if (root && state.drew(root, rootBounds) && state.drewText()) return 0; // use the root picture only if it contains the text findInner(state); + *xPtr = state.bestX(); + *yPtr = state.bestY(); return state.best(); } diff --git a/WebCore/platform/graphics/android/LayerAndroid.h b/WebCore/platform/graphics/android/LayerAndroid.h index 510014b..575ada7 100644 --- a/WebCore/platform/graphics/android/LayerAndroid.h +++ b/WebCore/platform/graphics/android/LayerAndroid.h @@ -163,7 +163,7 @@ public: void updatePositions(); void clipArea(SkTDArray<SkRect>* region) const; - const LayerAndroid* find(int x, int y, SkPicture* root) const; + const LayerAndroid* find(int* xPtr, int* yPtr, SkPicture* root) const; const LayerAndroid* findById(int uniqueID) const { return const_cast<LayerAndroid*>(this)->findById(uniqueID); } diff --git a/WebKit/android/nav/CachedRoot.cpp b/WebKit/android/nav/CachedRoot.cpp index 7f4f06f..7bedd4f 100644 --- a/WebKit/android/nav/CachedRoot.cpp +++ b/WebKit/android/nav/CachedRoot.cpp @@ -1674,16 +1674,12 @@ SkPicture* CachedRoot::pictureAt(int* xPtr, int* yPtr, int* id) const { #if USE(ACCELERATED_COMPOSITING) if (mRootLayer) { - const LayerAndroid* layer = mRootLayer->find(*xPtr, *yPtr, mPicture); + const LayerAndroid* layer = mRootLayer->find(xPtr, yPtr, mPicture); if (layer) { SkPicture* picture = layer->picture(); DBG_NAV_LOGD("layer %d picture=%p (%d,%d)", layer->uniqueId(), picture, picture ? picture->width() : 0, picture ? picture->height() : 0); - SkRect localBounds; - layer->bounds(&localBounds); - *xPtr -= localBounds.fLeft; - *yPtr -= localBounds.fTop; if (picture) { if (id) *id = layer->uniqueId(); diff --git a/WebKit/android/nav/SelectText.cpp b/WebKit/android/nav/SelectText.cpp index 6c9646c..6a22c73 100644 --- a/WebKit/android/nav/SelectText.cpp +++ b/WebKit/android/nav/SelectText.cpp @@ -182,12 +182,11 @@ public: class CommonCheck : public SkBounder { public: - CommonCheck(int width, int height) - : mHeight(height) + CommonCheck(const SkIRect& area) + : mArea(area) , mLastUni(0) , mMatrix(0) , mPaint(0) - , mWidth(width) { mLastGlyph.fGlyphID = static_cast<uint16_t>(-1); reset(); @@ -246,6 +245,10 @@ public: mLastUni = mLastUniCandidate; } + const SkIRect& getArea() const { + return mArea; + } + SkUnichar getUniChar(const SkBounder::GlyphRec& rec) { SkUnichar unichar; @@ -280,7 +283,7 @@ public: test[0] = mLastGlyph.fGlyphID; test[1] = rec.fGlyphID; SkIRect area; - area.set(0, 0, mWidth, mHeight); + area.set(0, 0, area.width(), area.height()); SpaceCanvas spaceChecker(area); spaceChecker.drawText(test, sizeof(test), SkFixedToScalar(mLastGlyph.fLSB.fX), @@ -346,6 +349,12 @@ public: reset(); } + void setGlyph(CommonCheck& check) + { + mLastGlyph = check.mLastGlyph; + mLastUni = check.mLastUni; + } + void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y, const void* text) { @@ -376,7 +385,7 @@ public: #endif protected: - int mHeight; + SkIRect mArea; SkBounder::GlyphRec mLastCandidate; SkBounder::GlyphRec mLastGlyph; SkUnichar mLastUni; @@ -384,7 +393,6 @@ protected: const SkMatrix* mMatrix; const SkPaint* mPaint; const uint16_t* mText; - int mWidth; SkScalar mY; private: int mBase; @@ -394,27 +402,144 @@ private: friend class EdgeCheck; }; -class FirstCheck : public CommonCheck { +// generate the limit area for the new selection +class LineCheck : public CommonCheck { +public: + LineCheck(int x, int y, const SkIRect& area) + : INHERITED(area) + , mX(x) + , mY(y) + , mInBetween(false) + { + mLast.setEmpty(); + } + + void finish(const SkRegion& selectedRgn) + { + processLine(); + bool above = false; + bool below = false; + bool selected = false; + SkRegion localRgn(selectedRgn); + localRgn.translate(-mArea.fLeft, -mArea.fTop, &localRgn); + DBG_NAV_LOGD("localRgn=(%d,%d,%d,%d)", + localRgn.getBounds().fLeft, localRgn.getBounds().fTop, + localRgn.getBounds().fRight, localRgn.getBounds().fBottom); + for (int index = 0; index < mParagraphs.count(); index++) { + const SkIRect& rect = mParagraphs[index]; + bool localSelected = localRgn.intersects(rect); + DBG_NAV_LOGD("[%d] rect=(%d,%d,%d,%d)", index, rect.fLeft, rect.fTop, + rect.fRight, rect.fBottom); + if (localSelected) { + DBG_NAV_LOGD("[%d] localSelected=true", index); + *mSelected.append() = rect; + } + if (rect.fRight <= mX || rect.fLeft >= mX) + continue; + if (mY > rect.fBottom) { + below = true; + selected |= localSelected; + DBG_NAV_LOGD("[%d] below=true localSelected=%s", index, + localSelected ? "true" : "false"); + } + if (mY < rect.fTop) { + above = true; + selected |= localSelected; + DBG_NAV_LOGD("[%d] above=true localSelected=%s", index, + localSelected ? "true" : "false"); + } + } + DBG_NAV_LOGD("mX=%d mY=%d above=%s below=%s selected=%s", + mX, mY, above ? "true" : "false", below ? "true" : "false", + selected ? "true" : "false"); + mInBetween = above && below && selected; + } + + bool inBetween() const + { + return mInBetween; + } + + bool inColumn(const SkIRect& test) const + { + for (int index = 0; index < mSelected.count(); index++) { + const SkIRect& rect = mSelected[index]; + if (rect.fRight > test.fLeft && rect.fLeft < test.fRight) + return true; + } + return false; + } + + virtual bool onIRectGlyph(const SkIRect& rect, const SkBounder::GlyphRec& ) + { + SkIRect bounds; + bounds.set(rect.fLeft, top(), rect.fRight, bottom()); + // assume that characters must be consecutive to describe spaces + // (i.e., don't join rects drawn at different times) + if (bounds.fTop != mLast.fTop || bounds.fBottom != mLast.fBottom + || bounds.fLeft > mLast.fRight + minSpaceWidth() + || bounds.fLeft < mLast.fLeft) { + processLine(); + mLast = bounds; + } else + mLast.join(bounds); + return false; + } + + void processLine() + { + // assume line spacing of 1.5 + int lineHeight = bottom() - top(); + mLast.inset(0, -lineHeight >> 1); + // collect arrays of rectangles making up glyphs below or above this one + for (int index = 0; index < mParagraphs.count(); index++) { + SkIRect& rect = mParagraphs[index]; + if (SkIRect::Intersects(rect, mLast)) { + rect.join(mLast); + return; + } + } + *mParagraphs.append() = mLast; + } + +protected: + int mX; + int mY; + SkIRect mLast; + SkTDArray<SkIRect> mParagraphs; + SkTDArray<SkIRect> mSelected; + SkTDArray<SkIRect> mInColumn; + bool mInBetween; +private: + typedef CommonCheck INHERITED; +}; + +class SelectText::FirstCheck : public CommonCheck { public: FirstCheck(int x, int y, const SkIRect& area) - : INHERITED(area.width(), area.height()) + : INHERITED(area) + , mLineCheck(0) , mFocusX(x - area.fLeft) , mFocusY(y - area.fTop) + , mBestInColumn(false) , mRecordGlyph(false) { reset(); } - const SkIRect& adjustedBounds(const SkIRect& area, int* base) + const SkIRect& adjustedBounds(int* base) { - *base = mBestBase + area.fTop; - mBestBounds.offset(area.fLeft, area.fTop); + *base = mBestBase + mArea.fTop; + mBestBounds.offset(mArea.fLeft, mArea.fTop); DBG_NAV_LOGD("FirstCheck mBestBounds:(%d, %d, %d, %d) mTop=%d mBottom=%d", mBestBounds.fLeft, mBestBounds.fTop, mBestBounds.fRight, mBestBounds.fBottom, topDebug(), bottomDebug()); return mBestBounds; } + int focusX() const { return mFocusX; } + int focusY() const { return mFocusY; } + virtual bool onIRectGlyph(const SkIRect& rect, const SkBounder::GlyphRec& rec) { @@ -441,25 +566,43 @@ public: overlaps ? "true" : "false", ch < 0x7f ? ch : '?'); } #endif - if ((mDy > dy && !overlaps) - || ((mDy == dy || overlaps) && mDx > dx)) { - mBestBase = base(); - mBestBounds = testBounds; + if ((mDy <= dy || overlaps) + && ((mDy != dy && !overlaps) || mDx <= dx)) { + return false; + } + bool testInColumn = false; + bool inBetween = false; + if (mLineCheck) { + testInColumn = mLineCheck->inColumn(testBounds); + inBetween = mLineCheck->inBetween(); + } +#ifdef EXTRA_NOISY_LOGGING + if (dy < 10) { + DBG_NAV_LOGD("FirstCheck bestIn=%s testIn=%s focusIn=%s", + mBestInColumn ? "true" : "false", testInColumn ? "true" : "false", + inBetween ? "true" : "false"); + } +#endif + if ((mBestInColumn || inBetween) && !testInColumn) + return false; + mBestBase = base(); + mBestBounds = testBounds; + mBestInColumn = testInColumn; #ifndef EXTRA_NOISY_LOGGING - if (dy < 10 && dx < 10) + if (dy < 10 && dx < 10) #endif - { - DBG_NAV_LOGD("FirstCheck dx/y=(%d,%d) mFocus=(%d,%d)" - " mBestBounds={%d,%d,r=%d,b=%d}", - dx, dy, mFocusX, mFocusY, - mBestBounds.fLeft, mBestBounds.fTop, - mBestBounds.fRight, mBestBounds.fBottom); - } - mDx = dx; - mDy = dy; - if (mRecordGlyph) - recordGlyph(rec); + { + DBG_NAV_LOGD("FirstCheck dx/y=(%d,%d) mFocus=(%d,%d)" + " mBestBounds={%d,%d,r=%d,b=%d} inColumn=%s", + dx, dy, mFocusX, mFocusY, + mBestBounds.fLeft, mBestBounds.fTop, + mBestBounds.fRight, mBestBounds.fBottom, + mBestInColumn ? "true" : "false"); } + mDx = dx; + mDy = dy; + if (mRecordGlyph) + recordGlyph(rec); return false; } @@ -469,33 +612,32 @@ public: mDx = mDy = INT_MAX; } - void setRecordGlyph() - { - mRecordGlyph = true; - } + void setLines(const LineCheck* lineCheck) { mLineCheck = lineCheck; } + void setRecordGlyph() { mRecordGlyph = true; } protected: + const LineCheck* mLineCheck; int mBestBase; SkIRect mBestBounds; int mDx; int mDy; int mFocusX; int mFocusY; + bool mBestInColumn; bool mRecordGlyph; private: typedef CommonCheck INHERITED; }; -class EdgeCheck : public FirstCheck { +class SelectText::EdgeCheck : public SelectText::FirstCheck { public: EdgeCheck(int x, int y, const SkIRect& area, CommonCheck& last, bool left) : INHERITED(x, y, area) - , mLast(area.width(), area.height()) + , mLast(area) , mLeft(left) { mLast.set(last); - mLastGlyph = last.mLastGlyph; - mLastUni = last.mLastUni; + setGlyph(last); } bool adjacent() @@ -556,15 +698,15 @@ protected: CommonCheck mLast; bool mLeft; private: - typedef FirstCheck INHERITED; + typedef SelectText::FirstCheck INHERITED; }; class FindFirst : public CommonCheck { public: - FindFirst(int width, int height) - : INHERITED(width, height) + FindFirst(const SkIRect& area) + : INHERITED(area) { - mBestBounds.set(width, height, width, height); + mBestBounds.set(area.width(), area.height(), area.width(), area.height()); } const SkIRect& bestBounds(int* base) @@ -591,8 +733,8 @@ private: class FindLast : public FindFirst { public: - FindLast(int width, int height) - : INHERITED(width, height) + FindLast(const SkIRect& area) + : INHERITED(area) { mBestBounds.setEmpty(); } @@ -625,7 +767,7 @@ protected: BuilderCheck(const SkIRect& start, int startBase, const SkIRect& end, int endBase, const SkIRect& area) - : INHERITED(area.width(), area.height()) + : INHERITED(area) , mCapture(false) , mEnd(end) , mEndBase(endBase) @@ -1020,10 +1162,11 @@ private: class TextCanvas : public ParseCanvas { public: - TextCanvas(CommonCheck* bounder, const SkIRect& area) + TextCanvas(CommonCheck* bounder) : mBounder(*bounder) { setBounder(bounder); SkBitmap bitmap; + const SkIRect& area = bounder->getArea(); bitmap.setConfig(SkBitmap::kARGB_8888_Config, area.width(), area.height()); setBitmapDevice(bitmap); @@ -1093,11 +1236,11 @@ static bool buildSelection(const SkPicture& picture, const SkIRect& area, selStart.fLeft, selStart.fTop, selStart.fRight, selStart.fBottom, selEnd.fLeft, selEnd.fTop, selEnd.fRight, selEnd.fBottom); MultilineBuilder builder(selStart, startBase, selEnd, endBase, area, region); - TextCanvas checker(&builder, area); + TextCanvas checker(&builder); checker.drawPicture(const_cast<SkPicture&>(picture)); bool flipped = builder.flipped(); if (flipped) { - TextCanvas checker(&builder, area); + TextCanvas checker(&builder); checker.drawPicture(const_cast<SkPicture&>(picture)); } builder.finish(); @@ -1105,103 +1248,32 @@ static bool buildSelection(const SkPicture& picture, const SkIRect& area, return flipped; } -static SkIRect findClosest(FirstCheck& _check, const SkPicture& picture, - const SkIRect& area, int* base) -{ - DBG_NAV_LOGD("area=(%d, %d, %d, %d)", area.fLeft, area.fTop, - area.fRight, area.fBottom); - TextCanvas checker(&_check, area); - checker.drawPicture(const_cast<SkPicture&>(picture)); - _check.finishGlyph(); - return _check.adjustedBounds(area, base); -} - -static SkIRect findEdge(const SkPicture& picture, const SkIRect& area, - int x, int y, bool left, int* base) -{ - SkIRect result; - result.setEmpty(); - FirstCheck center(x, y, area); - center.setRecordGlyph(); - int closestBase; - SkIRect closest = findClosest(center, picture, area, &closestBase); - closest.inset(-TOUCH_SLOP, -TOUCH_SLOP); - if (!closest.contains(x, y)) { - DBG_NAV_LOGD("closest=(%d, %d, %d, %d) area=(%d, %d, %d, %d) x/y=%d,%d", - closest.fLeft, closest.fTop, closest.fRight, closest.fBottom, - area.fLeft, area.fTop, area.fRight, area.fBottom, x, y); - return result; - } - EdgeCheck edge(x, y, area, center, left); - do { // detect left or right until there's a gap - DBG_NAV_LOGD("edge=%p picture=%p area=%d,%d,%d,%d", - &edge, &picture, area.fLeft, area.fTop, area.fRight, area.fBottom); - TextCanvas checker(&edge, area); - checker.drawPicture(const_cast<SkPicture&>(picture)); - edge.finishGlyph(); - if (!edge.adjacent()) { - DBG_NAV_LOG("adjacent break"); - break; - } - int nextBase; - const SkIRect& next = edge.bestBounds(&nextBase); - if (next.isEmpty()) { - DBG_NAV_LOG("empty"); - break; - } - if (result == next) { - DBG_NAV_LOG("result == next"); - break; - } - *base = nextBase; - result = next; - edge.shiftStart(result); - } while (true); - if (!result.isEmpty()) { - *base += area.fTop; - result.offset(area.fLeft, area.fTop); - } - return result; -} - static SkIRect findFirst(const SkPicture& picture, int* base) { - FindFirst finder(picture.width(), picture.height()); SkIRect area; area.set(0, 0, picture.width(), picture.height()); - TextCanvas checker(&finder, area); + FindFirst finder(area); + TextCanvas checker(&finder); checker.drawPicture(const_cast<SkPicture&>(picture)); return finder.bestBounds(base); } static SkIRect findLast(const SkPicture& picture, int* base) { - FindLast finder(picture.width(), picture.height()); SkIRect area; area.set(0, 0, picture.width(), picture.height()); - TextCanvas checker(&finder, area); + FindLast finder(area); + TextCanvas checker(&finder); checker.drawPicture(const_cast<SkPicture&>(picture)); return finder.bestBounds(base); } -static SkIRect findLeft(const SkPicture& picture, const SkIRect& area, - int x, int y, int* base) -{ - return findEdge(picture, area, x, y, true, base); -} - -static SkIRect findRight(const SkPicture& picture, const SkIRect& area, - int x, int y, int* base) -{ - return findEdge(picture, area, x, y, false, base); -} - static WTF::String text(const SkPicture& picture, const SkIRect& area, const SkIRect& start, int startBase, const SkIRect& end, int endBase, bool flipped) { TextExtractor extractor(start, startBase, end, endBase, area, flipped); - TextCanvas checker(&extractor, area); + TextCanvas checker(&extractor); checker.drawPicture(const_cast<SkPicture&>(picture)); return extractor.text(); } @@ -1469,7 +1541,7 @@ void SelectText::extendSelection(const IntRect& vis, int x, int y) DBG_NAV_LOGD("selStart clip=(%d,%d,%d,%d)", clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom); FirstCheck center(m_original.fX, m_original.fY, clipRect); - m_selStart = m_selEnd = findClosest(center, *m_picture, clipRect, &base); + m_selStart = m_selEnd = findClosest(center, *m_picture, &base); m_startBase = m_endBase = base; m_startSelection = false; m_extendSelection = true; @@ -1484,10 +1556,33 @@ void SelectText::extendSelection(const IntRect& vis, int x, int y) clipRect.sort(); clipRect.inset(-m_visibleRect.width(), -m_visibleRect.height()); } - DBG_NAV_LOGD("extend clip=(%d,%d,%d,%d)", clipRect.fLeft, - clipRect.fTop, clipRect.fRight, clipRect.fBottom); + DBG_NAV_LOGD("extend clip=(%d,%d,%d,%d) wordSel=%s outsideWord=%s", + clipRect.fLeft, clipRect.fTop, clipRect.fRight, clipRect.fBottom, + m_wordSelection ? "true" : "false", m_outsideWord ? "true" : "false"); FirstCheck extension(x, y, clipRect); - SkIRect found = findClosest(extension, *m_picture, clipRect, &base); + SkIRect found = findClosest(extension, *m_picture, &base); + if (m_wordSelection) { + SkIRect wordBounds = m_wordBounds; + if (!m_outsideWord) + wordBounds.inset(-TOUCH_SLOP, -TOUCH_SLOP); + DBG_NAV_LOGD("x=%d y=%d wordBounds=(%d,%d,r=%d,b=%d)" + " found=(%d,%d,r=%d,b=%d)", x, y, wordBounds.fLeft, wordBounds.fTop, + wordBounds.fRight, wordBounds.fBottom, found.fLeft, found.fTop, + found.fRight, found.fBottom); + if (wordBounds.contains(x, y)) { + DBG_NAV_LOG("wordBounds.contains=true"); + m_outsideWord = false; + return; + } + m_outsideWord = true; + if (found.fBottom <= wordBounds.fTop) + m_hitTopLeft = true; + else if (found.fTop >= wordBounds.fBottom) + m_hitTopLeft = false; + else + m_hitTopLeft = (found.fLeft + found.fRight) + < (wordBounds.fLeft + wordBounds.fRight); + } DBG_NAV_LOGD("x=%d y=%d m_startSelection=%s %s=(%d, %d, %d, %d)" " m_extendSelection=%s", x, y, m_startSelection ? "true" : "false", @@ -1504,6 +1599,80 @@ void SelectText::extendSelection(const IntRect& vis, int x, int y) swapAsNeeded(); } +SkIRect SelectText::findClosest(FirstCheck& check, const SkPicture& picture, + int* base) +{ + LineCheck lineCheck(check.focusX(), check.focusY(), check.getArea()); + TextCanvas lineChecker(&lineCheck); + lineChecker.drawPicture(const_cast<SkPicture&>(picture)); + lineCheck.finish(m_selRegion); + check.setLines(&lineCheck); + TextCanvas checker(&check); + checker.drawPicture(const_cast<SkPicture&>(picture)); + check.finishGlyph(); + return check.adjustedBounds(base); +} + +SkIRect SelectText::findEdge(const SkPicture& picture, const SkIRect& area, + int x, int y, bool left, int* base) +{ + SkIRect result; + result.setEmpty(); + FirstCheck center(x, y, area); + center.setRecordGlyph(); + int closestBase; + SkIRect closest = findClosest(center, picture, &closestBase); + closest.inset(-TOUCH_SLOP, -TOUCH_SLOP); + if (!closest.contains(x, y)) { + DBG_NAV_LOGD("closest=(%d, %d, %d, %d) area=(%d, %d, %d, %d) x/y=%d,%d", + closest.fLeft, closest.fTop, closest.fRight, closest.fBottom, + area.fLeft, area.fTop, area.fRight, area.fBottom, x, y); + return result; + } + EdgeCheck edge(x, y, area, center, left); + do { // detect left or right until there's a gap + DBG_NAV_LOGD("edge=%p picture=%p area=%d,%d,%d,%d", + &edge, &picture, area.fLeft, area.fTop, area.fRight, area.fBottom); + TextCanvas checker(&edge); + checker.drawPicture(const_cast<SkPicture&>(picture)); + edge.finishGlyph(); + if (!edge.adjacent()) { + DBG_NAV_LOG("adjacent break"); + break; + } + int nextBase; + const SkIRect& next = edge.bestBounds(&nextBase); + if (next.isEmpty()) { + DBG_NAV_LOG("empty"); + break; + } + if (result == next) { + DBG_NAV_LOG("result == next"); + break; + } + *base = nextBase; + result = next; + edge.shiftStart(result); + } while (true); + if (!result.isEmpty()) { + *base += area.fTop; + result.offset(area.fLeft, area.fTop); + } + return result; +} + +SkIRect SelectText::findLeft(const SkPicture& picture, const SkIRect& area, + int x, int y, int* base) +{ + return findEdge(picture, area, x, y, true, base); +} + +SkIRect SelectText::findRight(const SkPicture& picture, const SkIRect& area, + int x, int y, int* base) +{ + return findEdge(picture, area, x, y, false, base); +} + const String SelectText::getSelection() { if (!m_picture) @@ -1579,7 +1748,7 @@ void SelectText::moveSelection(const IntRect& vis, int x, int y) clipRect.join(m_selEnd); FirstCheck center(x, y, clipRect); int base; - SkIRect found = findClosest(center, *m_picture, clipRect, &base); + SkIRect found = findClosest(center, *m_picture, &base); if (m_hitTopLeft || !m_extendSelection) { m_startBase = base; m_selStart = found; @@ -1631,7 +1800,7 @@ int SelectText::selectionY() const void SelectText::setVisibleRect(const IntRect& vis) { - DBG_NAV_LOGD("vis=(%d,%d,w=%d,h=%d) offset=(%g,%g)", + DBG_NAV_LOGD("vis=(%d,%d,w=%d,h=%d) offset=(%d,%d)", vis.x(), vis.y(), vis.width(), vis.height(), m_startOffset.fX, m_startOffset.fY); m_visibleRect = vis; @@ -1641,9 +1810,13 @@ void SelectText::setVisibleRect(const IntRect& vis) bool SelectText::startSelection(const CachedRoot* root, const IntRect& vis, int x, int y) { + m_wordSelection = false; m_startOffset.set(x, y); + DBG_NAV_LOGD("x/y=(%d,%d)", x, y); m_picture->safeUnref(); m_picture = root->pictureAt(&x, &y, &m_layerId); + DBG_NAV_LOGD("m_picture=%p m_layerId=%d x/y=(%d,%d)", m_picture, m_layerId, + x, y); if (!m_picture) { DBG_NAV_LOG("picture==0"); return false; @@ -1711,7 +1884,10 @@ bool SelectText::wordSelection() m_selStart.fLeft, m_selStart.fTop, m_selStart.fRight, m_selStart.fBottom, m_selEnd.fLeft, m_selEnd.fTop, m_selEnd.fRight, m_selEnd.fBottom); if (!left.isEmpty() || !right.isEmpty()) { - m_extendSelection = true; + m_wordBounds = m_selStart; + m_wordBounds.join(m_selEnd); + m_extendSelection = m_wordSelection = true; + m_outsideWord = false; return true; } return false; diff --git a/WebKit/android/nav/SelectText.h b/WebKit/android/nav/SelectText.h index 32e1728..666cdd1 100644 --- a/WebKit/android/nav/SelectText.h +++ b/WebKit/android/nav/SelectText.h @@ -60,8 +60,17 @@ public: int m_selectX; int m_selectY; private: + class FirstCheck; + class EdgeCheck; void drawSelectionPointer(SkCanvas* , IntRect* ); void drawSelectionRegion(SkCanvas* , IntRect* ); + SkIRect findClosest(FirstCheck& , const SkPicture& , int* base); + SkIRect findEdge(const SkPicture& , const SkIRect& area, + int x, int y, bool left, int* base); + SkIRect findLeft(const SkPicture& picture, const SkIRect& area, + int x, int y, int* base); + SkIRect findRight(const SkPicture& picture, const SkIRect& area, + int x, int y, int* base); static void getSelectionArrow(SkPath* ); void getSelectionCaret(SkPath* ); bool hitCorner(int cx, int cy, int x, int y) const; @@ -73,6 +82,7 @@ private: SkIRect m_selEnd; SkIRect m_lastStart; SkIRect m_lastEnd; + SkIRect m_wordBounds; int m_startBase; int m_endBase; int m_layerId; @@ -86,6 +96,8 @@ private: bool m_flipped; bool m_hitTopLeft; bool m_startSelection; + bool m_wordSelection; + bool m_outsideWord; }; } |