summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCary Clark <cary@android.com>2010-12-15 04:57:30 -0800
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-12-15 04:57:30 -0800
commit052fc207e3b0c145df08f85c14a7946b5ae3b79f (patch)
tree0cafe512b96e7935a8172ba514cab53c38c44105
parentc7911ffd666a7e73131dfd68919b769086a093e0 (diff)
parentd2966aa787e0dcc4d26a10b6f0dc9f3a2f51abe4 (diff)
downloadexternal_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.cpp20
-rw-r--r--WebCore/platform/graphics/android/LayerAndroid.h2
-rw-r--r--WebKit/android/nav/CachedRoot.cpp6
-rw-r--r--WebKit/android/nav/SelectText.cpp432
-rw-r--r--WebKit/android/nav/SelectText.h12
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;
};
}