Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(785)

Unified Diff: Source/core/editing/FrameSelection.cpp

Issue 988023005: Implementing directional selection strategy in Blink. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Addressing review feedback. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/core/editing/FrameSelection.cpp
diff --git a/Source/core/editing/FrameSelection.cpp b/Source/core/editing/FrameSelection.cpp
index 8fbfe7e993caa533506be2cf5dc1eb312796dfc9..1ec8124d706d686539e8043e1dbdde4553866d2a 100644
--- a/Source/core/editing/FrameSelection.cpp
+++ b/Source/core/editing/FrameSelection.cpp
@@ -92,6 +92,127 @@ static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame)
return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional();
}
+class GranularityStrategy {
+public:
+ virtual ~GranularityStrategy() { };
+ virtual void Clear();
+ // Returns the selection extent based on the strategy.
+ virtual VisiblePosition moveRangeSelectionExtent(const VisiblePosition&, const VisibleSelection&);
yosin_UTC9 2015/03/18 03:44:54 Since, |moveRangeSelectionExtent()| returns positi
mfomitchev 2015/03/19 01:24:39 Renamed and expanded on the comment. I wanted to c
+protected:
+ GranularityStrategy() { };
+};
+
+class DefaultGranularityStrategy : public GranularityStrategy {
+public:
+ DefaultGranularityStrategy() { };
+ ~DefaultGranularityStrategy() override { };
+
+ void Clear() override { };
+ VisiblePosition moveRangeSelectionExtent(const VisiblePosition& extentPosition, const VisibleSelection&) override
+ {
+ return extentPosition;
+ };
+};
+
+// "Expand by word, shrink by character" selection strategy.
+// Uses character granularity when selection is shrinking. If the selection is expanding,
+// granularity doesn't change until the word boundary is passed, after which the granularity
+// switches to "word".
+class DirectionGranularityStrategy : public GranularityStrategy {
+public:
+ DirectionGranularityStrategy();
+ ~DirectionGranularityStrategy() override { };
+
+ void Clear() override;
+ VisiblePosition moveRangeSelectionExtent(const VisiblePosition&, const VisibleSelection&) override;
+private:
+ // Returns the next word boundary starting from |pos|. |direction| specifies the direction
+ // in which to search for the next bound. nextIfOnBound controls whether |pos| or the next boundary
+ // is returned when |pos| is located exactly on word boundary.
+ VisiblePosition nextWordBound(const VisiblePosition& /*pos*/, int /*direction*/, bool /*nextIfOnBound*/);
+
+ // Current selection granularity being used
+ TextGranularity m_granularity;
+ // Marks the boundary of the "current word". If the selection extent is extended
+ // beyond this threshold - the granularity will be changed to WordGranularity.
+ VisiblePosition m_wordBoundary;
yosin_UTC9 2015/03/18 03:44:54 |VisiblePosition| isn't update at DOM mutation or
mfomitchev 2015/03/19 01:24:39 Ok. I really only need m_wordBoundary to determine
yosin_UTC9 2015/03/23 07:20:39 Saving |Position|, |VisibleSelection|, |VisiblePos
+ // Cached extentPosition passed in moveRangeSelectionExtent
+ VisiblePosition m_extentPosition;
+};
+
+DirectionGranularityStrategy::DirectionGranularityStrategy()
+ : m_granularity(CharacterGranularity) { }
+
+void DirectionGranularityStrategy::Clear()
+{
+ m_wordBoundary.clear();
+ m_extentPosition.clear();
+ m_granularity = CharacterGranularity;
+}
+
+VisiblePosition DirectionGranularityStrategy::nextWordBound(
+ const VisiblePosition& pos,
+ int direction,
+ bool nextIfOnBound)
+{
+ return direction > 0 ? endOfWord(pos, nextIfOnBound ? RightWordIfOnBoundary : LeftWordIfOnBoundary)
+ : startOfWord(pos, nextIfOnBound ? LeftWordIfOnBoundary : RightWordIfOnBoundary);
+}
+
+VisiblePosition DirectionGranularityStrategy::moveRangeSelectionExtent(const VisiblePosition& extentPosition, const VisibleSelection& selection)
+{
+ const VisiblePosition& base = selection.visibleBase();
+ const VisiblePosition& oldExtent = selection.visibleExtent();
+
+ int extentBaseOrder = comparePositions(extentPosition, base);
yoichio 2015/03/17 05:54:41 I wonder why this function looks procedural. It is
mfomitchev 2015/03/17 17:01:16 Ok, lets enumerate your cases one to four from top
yosin_UTC9 2015/03/18 03:44:54 It is better to ask |VisibleSelection| for DOM ord
+ int oldExtentBaseOrder = comparePositions(oldExtent, base);
+ ASSERT(extentBaseOrder != 0);
+
+ bool extentBaseOrderSwitched = extentBaseOrder * oldExtentBaseOrder < 0;
+ VisiblePosition prevWordBoundary = m_wordBoundary;
+
+ // If the previous word boundary is empty, this must be the first call to moveRangeSelectionExtent()
+ // after a Clear(). Initialize prevWordBoundary based on the current selection.
+ if (prevWordBoundary.isNull()) {
yoichio 2015/03/17 05:54:41 We can early return in this case.
mfomitchev 2015/03/17 17:01:16 We can't actually. extentPosition can still be any
+ ASSERT(m_extentPosition.isNull());
+ ASSERT(m_granularity == CharacterGranularity);
+ prevWordBoundary = nextWordBound(oldExtent, extentBaseOrder, false);
yosin_UTC9 2015/03/18 03:44:54 How about factor out |FrameSelection::modifyXXX()|
+ }
+ // If the extent-base order was switched, then the selection is now expanding in a different
+ // direction than before. Therefore we need to calculate the boundary of the 'current word'
+ // in this new direction in order to be able to tell if the selection expanded beyond it.
+ if (extentBaseOrderSwitched) {
+ prevWordBoundary = nextWordBound(base, extentBaseOrder, true);
+ m_granularity = CharacterGranularity;
+ }
+
+ VisiblePosition newWordBoundary = prevWordBoundary;
+ bool expandedBeyondWordBoundary = extentBaseOrder > 0 ? comparePositions(extentPosition, prevWordBoundary) > 0
+ : comparePositions(extentPosition, prevWordBoundary) < 0;
+ // If the selection expanded beyond the word boundary - switch to word granularity and calculate
+ // the boundary of the new 'current word'.
+ if (expandedBeyondWordBoundary) {
+ if (m_granularity == CharacterGranularity)
+ m_granularity = WordGranularity;
+ newWordBoundary = nextWordBound(extentPosition, extentBaseOrder, true);
+ } else if (!extentBaseOrderSwitched) {
+ // If selection was shrunk - switch to character granularity and recalculate the boundary
+ // of the 'current word' in case the selection was shrunk beyond the old 'current word'.
+ if (!m_extentPosition.isNull() && extentBaseOrder * comparePositions(extentPosition, m_extentPosition) < 0) {
+ m_granularity = CharacterGranularity;
+ newWordBoundary = nextWordBound(extentPosition, extentBaseOrder, true);
+ }
+ }
+ m_wordBoundary = newWordBoundary;
+ m_extentPosition = extentPosition;
+
+ VisiblePosition visibleExtent = extentPosition;
+ if (m_granularity == WordGranularity)
+ visibleExtent = nextWordBound(extentPosition, extentBaseOrder, false);
+
+ return visibleExtent;
+}
+
FrameSelection::FrameSelection(LocalFrame* frame)
: m_frame(frame)
, m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
@@ -221,6 +342,8 @@ void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection&
void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
{
+ if ((options & FrameSelection::DoNotClearStrategy) == 0)
+ granularityStrategy().Clear();
bool closeTyping = options & CloseTyping;
bool shouldClearTypingStyle = options & ClearTypingStyle;
EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
@@ -1173,6 +1296,7 @@ LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositi
void FrameSelection::clear()
{
m_granularity = CharacterGranularity;
+ granularityStrategy().Clear();
setSelection(VisibleSelection());
}
@@ -1912,27 +2036,34 @@ bool FrameSelection::selectWordAroundPosition(const VisiblePosition& position)
return false;
}
-void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosition, TextGranularity granularity)
+GranularityStrategy& FrameSelection::granularityStrategy()
{
- if (isNone())
- return;
+ if (m_granularityStrategy)
+ return *m_granularityStrategy;
+ // We do lazy initalization for m_granularityStrategy, because if we initialize it
+ // right in the constructor - the correct settings may not be set yet.
+ Settings* settings = m_frame ? m_frame->settings() : 0;
+ if (settings && settings->selectionStrategy() == StrategyDirection)
+ m_granularityStrategy = adoptPtr(new DirectionGranularityStrategy());
+ else
+ m_granularityStrategy = adoptPtr(new DefaultGranularityStrategy());
+ return *m_granularityStrategy;
+}
- const VisiblePosition basePosition = m_selection.isBaseFirst() ?
- m_selection.visibleStart() : m_selection.visibleEnd();
+void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosition)
+{
+ const VisiblePosition base = m_selection.visibleBase();
- int order = comparePositions(basePosition, extentPosition);
- if (!order)
+ if (isNone() || comparePositions(base, extentPosition) == 0)
yosin_UTC9 2015/03/18 03:44:54 nit: |base == extentPosision|
mfomitchev 2015/03/19 01:24:39 Done.
return;
- // Currently we support only CharaterGranularity and WordGranurarity.
- // If |granurarity| is not of them, we fall back it to
- // CharacterGranularity.
- VisiblePosition newExtentPosition = extentPosition;
- if (granularity == WordGranularity)
- newExtentPosition = order < 0 ? endOfWord(extentPosition) : startOfWord(extentPosition);
-
- VisibleSelection newSelection = VisibleSelection(basePosition, newExtentPosition);
- setSelection(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | UserTriggered, FrameSelection::AlignCursorOnScrollIfNeeded, granularity);
+ VisiblePosition newExtentPosition = granularityStrategy().moveRangeSelectionExtent(extentPosition, selection());
+ VisibleSelection newSelection(base, newExtentPosition);
+ setSelection(
+ newSelection,
+ FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | FrameSelection::DoNotClearStrategy | UserTriggered,
+ FrameSelection::AlignCursorOnScrollIfNeeded,
+ CharacterGranularity);
}
void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, const VisiblePosition& extentPosition, TextGranularity granularity)

Powered by Google App Engine
This is Rietveld 408576698