Chromium Code Reviews| Index: Source/core/editing/FrameSelection.cpp |
| diff --git a/Source/core/editing/FrameSelection.cpp b/Source/core/editing/FrameSelection.cpp |
| index 0cb682f643ce648cd3916e4093ffbb688e47085f..98fffaf70a05642d9f0f956543fa44b36cd105bf 100644 |
| --- a/Source/core/editing/FrameSelection.cpp |
| +++ b/Source/core/editing/FrameSelection.cpp |
| @@ -93,6 +93,126 @@ static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame) |
| return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional(); |
| } |
| +class GranularityStrategy { |
| +public: |
| + virtual ~GranularityStrategy() { }; |
| + virtual void Clear(); |
| + |
| + // Calculates and returns the new selection based on the updated user selection extent |extentPosition| and the granularity strategy. |
| + virtual VisibleSelection updateExtent(const VisiblePosition& extentPosition, const VisibleSelection&); |
| +protected: |
| + GranularityStrategy() { }; |
| +}; |
| + |
| +class DefaultGranularityStrategy : public GranularityStrategy { |
| +public: |
| + DefaultGranularityStrategy() { }; |
| + ~DefaultGranularityStrategy() override { }; |
| + |
| + void Clear() override { }; |
| + VisibleSelection updateExtent(const VisiblePosition& extentPosition, const VisibleSelection& selection) override |
| + { |
| + return VisibleSelection(selection.visibleBase(), 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 a word boundary is passed, after which the granularity |
| +// switches to "word". |
| +class DirectionGranularityStrategy : public GranularityStrategy { |
| +public: |
| + DirectionGranularityStrategy(); |
| + ~DirectionGranularityStrategy() override { }; |
| + |
| + void Clear() override; |
| + VisibleSelection updateExtent(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*/); |
|
yosin_UTC9
2015/04/16 04:11:26
nit: Could you use enum for |nextIfOnBound|? In Bl
yosin_UTC9
2015/04/16 04:11:26
nit: It is better to use |SelectionDirection|, def
mfomitchev1
2015/04/17 00:13:50
Done.
mfomitchev1
2015/04/17 00:13:51
I created a local ESearchDirection instead. Select
|
| + |
| + // Current selection granularity being used |
| + TextGranularity m_granularity; |
| + // Set to true if the selection was shrunk (without changing relative base/extent order) |
| + // as a result of the most recent updateExtent call. |
| + bool m_lastMoveShrunkSelection; |
| +}; |
| + |
| +DirectionGranularityStrategy::DirectionGranularityStrategy() |
| + : m_granularity(CharacterGranularity) |
| + , m_lastMoveShrunkSelection(false) { } |
| + |
| +void DirectionGranularityStrategy::Clear() |
| +{ |
| + m_granularity = CharacterGranularity; |
| + m_lastMoveShrunkSelection = false; |
| +} |
| + |
| +VisiblePosition DirectionGranularityStrategy::nextWordBound( |
| + const VisiblePosition& pos, |
| + int direction, |
| + bool nextIfOnBound) |
| +{ |
| + return direction > 0 ? endOfWord(pos, nextIfOnBound ? RightWordIfOnBoundary : LeftWordIfOnBoundary) |
| + : startOfWord(pos, nextIfOnBound ? LeftWordIfOnBoundary : RightWordIfOnBoundary); |
| +} |
| + |
| +VisibleSelection DirectionGranularityStrategy::updateExtent(const VisiblePosition& extentPosition, const VisibleSelection& selection) |
| +{ |
| + if (extentPosition == selection.visibleExtent()) |
| + return selection; |
| + |
| + const VisiblePosition& base = selection.visibleBase(); |
|
yosin_UTC9
2015/04/16 04:11:26
nit: It is better to use |VisiblePostion| as value
mfomitchev1
2015/04/17 00:13:51
Done.
|
| + const VisiblePosition& oldExtentWithGranularity = selection.isBaseFirst() ? selection.visibleEnd() : selection.visibleStart(); |
| + |
| + int extentBaseOrder = comparePositions(extentPosition, base); |
| + int oldExtentBaseOrder = comparePositions(oldExtentWithGranularity, base); |
| + ASSERT(extentBaseOrder != 0); |
|
yosin_UTC9
2015/04/16 04:11:26
I'm not sure this assertion is always satisfied.
mfomitchev1
2015/04/17 00:13:51
I think it is - we short-circuit in FrameSelection
|
| + |
| + bool extentBaseOrderSwitched = extentBaseOrder * oldExtentBaseOrder < 0; |
|
yosin_UTC9
2015/04/16 04:11:26
This is too smart conditional expression. Could yo
mfomitchev1
2015/04/17 00:13:51
Done.
|
| + |
| + // Determine the boundary of the 'current word', i.e. the boundary extending beyond which |
| + // should change the granularity to WordGranularity. |
| + // If the last move has shrunk the selection and is now exactly on the word boundary - |
| + // we need to take the next bound as the bound of the "current word". |
| + VisiblePosition currentWordBoundary = |
| + nextWordBound(oldExtentWithGranularity, oldExtentBaseOrder, m_lastMoveShrunkSelection); |
| + |
| + bool thisMoveShrunkSelection = extentBaseOrder * comparePositions(extentPosition, selection.visibleExtent()) < 0; |
|
yosin_UTC9
2015/04/16 04:11:26
ditto as L174.
mfomitchev1
2015/04/17 00:13:50
Done.
|
| + // 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) { |
| + currentWordBoundary = nextWordBound(base, extentBaseOrder, true); |
| + m_granularity = CharacterGranularity; |
| + // When the base/extent order switches it doesn't count as shrinking selection. |
| + thisMoveShrunkSelection = false; |
| + } |
| + |
| + bool expandedBeyondWordBoundary = extentBaseOrder > 0 ? comparePositions(extentPosition, currentWordBoundary) > 0 |
|
yosin_UTC9
2015/04/16 04:11:26
This conditional expression looks complex. It is b
mfomitchev1
2015/04/17 00:13:51
Done.
|
| + : comparePositions(extentPosition, currentWordBoundary) < 0; |
| + if (expandedBeyondWordBoundary) { |
| + m_granularity = WordGranularity; |
| + } else if (thisMoveShrunkSelection) { |
| + m_granularity = CharacterGranularity; |
| + m_lastMoveShrunkSelection = true; |
| + } |
| + |
| + m_lastMoveShrunkSelection = thisMoveShrunkSelection; |
| + VisibleSelection newSelection = selection; |
| + newSelection.setExtent(extentPosition); |
| + if (m_granularity == WordGranularity) { |
| + if (extentBaseOrder > 0) |
| + newSelection.setEndRespectingGranularity(m_granularity, LeftWordIfOnBoundary); |
| + else |
| + newSelection.setStartRespectingGranularity(m_granularity, RightWordIfOnBoundary); |
| + } |
| + |
| + return newSelection; |
| +} |
| + |
| FrameSelection::FrameSelection(LocalFrame* frame) |
| : m_frame(frame) |
| , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()) |
| @@ -222,11 +342,14 @@ 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); |
| VisibleSelection s = validateSelection(newSelection); |
| + |
| if (shouldAlwaysUseDirectionalSelection(m_frame)) |
| s.setIsDirectional(true); |
| @@ -1173,6 +1296,7 @@ LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositi |
| void FrameSelection::clear() |
| { |
| m_granularity = CharacterGranularity; |
| + granularityStrategy()->Clear(); |
| setSelection(VisibleSelection()); |
| } |
| @@ -1913,21 +2037,33 @@ bool FrameSelection::selectWordAroundPosition(const VisiblePosition& position) |
| return false; |
| } |
| -void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosition, TextGranularity granularity) |
| +GranularityStrategy* const FrameSelection::granularityStrategy() |
|
yosin_UTC9
2015/04/16 04:11:26
nit: omit |const|
mfomitchev1
2015/04/17 00:13:51
Done.
|
| { |
| - if (isNone()) |
| - return; |
| - |
| - const VisiblePosition basePosition = m_selection.isBaseFirst() ? m_selection.visibleStart() : m_selection.visibleEnd(); |
| - VisibleSelection newSelection(basePosition, extentPosition); |
| - if (newSelection.isBaseFirst()) |
| - newSelection.setEndRespectingGranularity(granularity); |
| + if (m_granularityStrategy) |
| + return m_granularityStrategy.get(); |
| + // We do lazy initalization for m_granularityStrategy, because if we initialize it |
| + // right in the constructor - the correct settings may not be set yet. |
|
Rick Byers
2015/04/16 19:09:48
and you're sure granularityStrategy won't ever be
mfomitchev1
2015/04/16 19:33:00
Yeah, that's a good point. I don't really understa
Rick Byers
2015/04/16 19:35:10
Sure, sounds good.
mfomitchev1
2015/04/17 00:13:51
Done.
|
| + Settings* settings = m_frame ? m_frame->settings() : 0; |
| + if (settings && settings->selectionStrategy() == StrategyDirection) |
| + m_granularityStrategy = adoptPtr(new DirectionGranularityStrategy()); |
| else |
| - newSelection.setStartRespectingGranularity(granularity); |
| - if (!newSelection.isRange()) |
| + m_granularityStrategy = adoptPtr(new DefaultGranularityStrategy()); |
| + return m_granularityStrategy.get(); |
| +} |
| + |
| +void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosition) |
| +{ |
| + const VisiblePosition base = m_selection.visibleBase(); |
| + |
| + if (isNone() || base == extentPosition) |
| return; |
| - setSelection(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | UserTriggered, FrameSelection::AlignCursorOnScrollIfNeeded, granularity); |
| + VisibleSelection newSelection = granularityStrategy()->updateExtent(extentPosition, selection()); |
| + setSelection( |
| + newSelection, |
| + FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | FrameSelection::DoNotClearStrategy | UserTriggered, |
| + FrameSelection::AlignCursorOnScrollIfNeeded, |
| + CharacterGranularity); |
| } |
| void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, const VisiblePosition& extentPosition, TextGranularity granularity) |