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) |