Chromium Code Reviews| 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) |