<rdar://problem/
5415006> Command Left in a To Do causes caret to disappear
The selection was ending up inside non-editable content at the To Do Options
arrow image, rather then at the editable position just to the left of that image.
The problem was that startPositionForLine looked only at line boxes, and there
is no linebox for the editable position at the far left of a To Do, which is
a table. Addressed by having startPositionForLine use table offset 0 instead
of the first VisiblePosition inside the table.
Found and fixed the similar case with option-left (move by word position).
Test cases:
* editing/selection/mixed-editability-8.html: Added.
* editing/selection/mixed-editability-9.html: Added.
Source changes:
* editing/SelectionController.cpp:
(WebCore::SelectionController::modifyMovingLeftBackward):
* editing/VisiblePosition.cpp:
(WebCore::VisiblePosition::next):
(WebCore::VisiblePosition::previous):
(WebCore::VisiblePosition::stayInEditableContentLeft):
(WebCore::VisiblePosition::stayInEditableContentRight):
Factored stayInEditableContentLeft() and stayInEditableContentRight()
out of previous() and next().
* editing/VisiblePosition.h:
Declare stayInEditableContentLeft() and stayInEditableContentRight().
* editing/visible_units.cpp:
(WebCore::previousWordPosition):
(WebCore::nextWordPosition):
(WebCore::startOfLine):
(WebCore::endOfLine):
(WebCore::previousSentencePosition):
(WebCore::nextSentencePosition):
Call stayInEditableContentLeft() or stayInEditableContentRight(), as
appropriate, so prevent crossing from editable content into
uneditable content.
(WebCore::startPositionForLine):
Use table offset 0 instead of the first VisiblePosition in the table.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@25280
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2007-08-28 David Harrison <harrison@apple.com>
+
+ Reviewed by Darin.
+
+ <rdar://problem/5415006> Command Left in a To Do causes caret to disappear
+
+ The selection was ending up inside non-editable content at the To Do Options
+ arrow image, rather then at the editable position just to the left of that image.
+ The problem was that startPositionForLine looked only at line boxes, and there
+ is no linebox for the editable position at the far left of a To Do, which is
+ a table. Addressed by having startPositionForLine use table offset 0 instead
+ of the first VisiblePosition inside the table.
+
+ Found and fixed the similar case with option-left (move by word position).
+
+ Test cases:
+ * editing/selection/mixed-editability-8.html: Added.
+ * editing/selection/mixed-editability-9.html: Added.
+
+ Source changes:
+ * editing/SelectionController.cpp:
+ (WebCore::SelectionController::modifyMovingLeftBackward):
+
+ * editing/VisiblePosition.cpp:
+ (WebCore::VisiblePosition::next):
+ (WebCore::VisiblePosition::previous):
+ (WebCore::VisiblePosition::stayInEditableContentLeft):
+ (WebCore::VisiblePosition::stayInEditableContentRight):
+ Factored stayInEditableContentLeft() and stayInEditableContentRight()
+ out of previous() and next().
+
+ * editing/VisiblePosition.h:
+ Declare stayInEditableContentLeft() and stayInEditableContentRight().
+
+ * editing/visible_units.cpp:
+ (WebCore::previousWordPosition):
+ (WebCore::nextWordPosition):
+ (WebCore::startOfLine):
+ (WebCore::endOfLine):
+ (WebCore::previousSentencePosition):
+ (WebCore::nextSentencePosition):
+ Call stayInEditableContentLeft() or stayInEditableContentRight(), as
+ appropriate, so prevent crossing from editable content into
+ uneditable content.
+
+ (WebCore::startPositionForLine):
+ Use table offset 0 instead of the first VisiblePosition in the table.
+
2007-08-28 Mark Rowe <mrowe@apple.com>
Reviewed by Darin Adler.
VisiblePosition SelectionController::modifyMovingLeftBackward(TextGranularity granularity)
{
VisiblePosition pos;
- // FIXME: Stay in editable content for the less common granularities.
switch (granularity) {
case CharacterGranularity:
if (isRange())
{
VisiblePosition next(nextVisuallyDistinctCandidate(m_deepPosition), m_affinity);
- if (!stayInEditableContent || next.isNull())
+ if (!stayInEditableContent)
return next;
- Node* highestRoot = highestEditableRoot(deepEquivalent());
-
- if (!next.deepEquivalent().node()->isDescendantOf(highestRoot))
- return VisiblePosition();
-
- if (highestEditableRoot(next.deepEquivalent()) == highestRoot)
- return next;
-
- return firstEditablePositionAfterPositionInRoot(next.deepEquivalent(), highestRoot);
+ return firstEditablePositionAtOrAfter(next);
}
VisiblePosition VisiblePosition::previous(bool stayInEditableContent) const
}
#endif
- if (!stayInEditableContent || prev.isNull())
+ if (!stayInEditableContent)
return prev;
+ return lastEditablePositionAtOrBefore(prev);
+}
+
+VisiblePosition VisiblePosition::lastEditablePositionAtOrBefore(const VisiblePosition &pos) const
+{
+ if (pos.isNull())
+ return pos;
+
Node* highestRoot = highestEditableRoot(deepEquivalent());
- if (!prev.deepEquivalent().node()->isDescendantOf(highestRoot))
+ if (!pos.deepEquivalent().node()->isDescendantOf(highestRoot))
return VisiblePosition();
- if (highestEditableRoot(prev.deepEquivalent()) == highestRoot)
- return prev;
+ if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
+ return pos;
+
+ return lastEditablePositionBeforePositionInRoot(pos.deepEquivalent(), highestRoot);
+}
+
+VisiblePosition VisiblePosition::firstEditablePositionAtOrAfter(const VisiblePosition &pos) const
+{
+ if (pos.isNull())
+ return pos;
+
+ Node* highestRoot = highestEditableRoot(deepEquivalent());
+
+ if (!pos.deepEquivalent().node()->isDescendantOf(highestRoot))
+ return VisiblePosition();
+
+ if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
+ return pos;
- return lastEditablePositionBeforePositionInRoot(prev.deepEquivalent(), highestRoot);
+ return firstEditablePositionAfterPositionInRoot(pos.deepEquivalent(), highestRoot);
}
Position canonicalizeCandidate(const Position& candidate)
// next() and previous() will increment/decrement by a character cluster.
VisiblePosition next(bool stayInEditableContent = false) const;
VisiblePosition previous(bool stayInEditableContent = false) const;
+ VisiblePosition lastEditablePositionAtOrBefore(const VisiblePosition&) const;
+ VisiblePosition firstEditablePositionAtOrAfter(const VisiblePosition&) const;
UChar characterAfter() const;
UChar characterBefore() const { return previous().characterAfter(); }
};
// FIXME: This shouldn't ignore affinity.
-inline bool operator==(const VisiblePosition &a, const VisiblePosition &b)
+inline bool operator==(const VisiblePosition& a, const VisiblePosition& b)
{
return a.deepEquivalent() == b.deepEquivalent();
}
-inline bool operator!=(const VisiblePosition &a, const VisiblePosition &b)
+inline bool operator!=(const VisiblePosition& a, const VisiblePosition& b)
{
return !(a == b);
}
-PassRefPtr<Range> makeRange(const VisiblePosition &, const VisiblePosition &);
+PassRefPtr<Range> makeRange(const VisiblePosition&, const VisiblePosition&);
bool setStart(Range*, const VisiblePosition&);
bool setEnd(Range*, const VisiblePosition&);
VisiblePosition startVisiblePosition(const Range*, EAffinity);
VisiblePosition previousWordPosition(const VisiblePosition &c)
{
- return previousBoundary(c, previousWordPositionBoundary);
+ VisiblePosition prev = previousBoundary(c, previousWordPositionBoundary);
+ return c.firstEditablePositionAtOrAfter(prev);
}
static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length)
VisiblePosition nextWordPosition(const VisiblePosition &c)
{
- return nextBoundary(c, nextWordPositionBoundary);
+ VisiblePosition next = nextBoundary(c, nextWordPositionBoundary);
+ return c.lastEditablePositionAtOrBefore(next);
}
// ---------
InlineTextBox *startTextBox = static_cast<InlineTextBox *>(startBox);
startOffset = startTextBox->m_start;
}
-
- return VisiblePosition(startNode, startOffset, DOWNSTREAM);
+
+ VisiblePosition visPos = VisiblePosition(startNode, startOffset, DOWNSTREAM);
+
+ // return table offset 0 instead of the first VisiblePosition inside the table
+ VisiblePosition visPrevious = visPos.previous();
+ if (isLastPositionBeforeTable(visPrevious))
+ visPos = visPrevious;
+
+ return visPos;
}
VisiblePosition startOfLine(const VisiblePosition& c)
{
VisiblePosition visPos = startPositionForLine(c);
- if (visPos.isNotNull())
- {
+ if (visPos.isNotNull()) {
// Make sure the start of line is not greater than the given input position. Else use the previous position to
// obtain start of line. This condition happens when the input position is before the space character at the end
// of a soft-wrapped non-editable line. In this scenario, startPositionForLine would incorrectly hand back a position
visPos = startPositionForLine(visPos);
}
}
-
- return visPos;
+
+ return c.firstEditablePositionAtOrAfter(visPos);
}
static VisiblePosition endPositionForLine(const VisiblePosition& c)
visPos = endPositionForLine(visPos);
}
- return visPos;
+ return c.lastEditablePositionAtOrBefore(visPos);
}
bool inSameLine(const VisiblePosition &a, const VisiblePosition &b)
VisiblePosition previousSentencePosition(const VisiblePosition &c)
{
- return previousBoundary(c, previousSentencePositionBoundary);
+ VisiblePosition prev = previousBoundary(c, previousSentencePositionBoundary);
+ return c.firstEditablePositionAtOrAfter(prev);
}
static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length)
VisiblePosition nextSentencePosition(const VisiblePosition &c)
{
- return nextBoundary(c, nextSentencePositionBoundary);
+ VisiblePosition next = nextBoundary(c, nextSentencePositionBoundary);
+ return c.lastEditablePositionAtOrBefore(next);
}
// FIXME: Broken for positions before/after images that aren't inline (5027702)