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

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: Missing file. Created 5 years, 8 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
« no previous file with comments | « Source/core/editing/FrameSelection.h ('k') | Source/core/editing/FrameSelectionTest.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/editing/FrameSelection.cpp
diff --git a/Source/core/editing/FrameSelection.cpp b/Source/core/editing/FrameSelection.cpp
index 0cb682f643ce648cd3916e4093ffbb688e47085f..3d8d2d4d9d64d7f481b2c68911adb28d23955c5c 100644
--- a/Source/core/editing/FrameSelection.cpp
+++ b/Source/core/editing/FrameSelection.cpp
@@ -93,6 +93,140 @@ static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame)
return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional();
}
+class GranularityStrategy {
yosin_UTC9 2015/04/17 04:24:50 Is it better to move XXXStrategy to another file?
mfomitchev 2015/04/17 14:41:49 It's your call. The upside is reducing the file si
yosin_UTC9 2015/04/20 01:25:55 We've moved classes to their own files for TextIte
+public:
+ virtual ~GranularityStrategy() { };
+ virtual SelectionStrategy GetType() const;
yosin_UTC9 2015/04/17 04:24:50 As of link error, we want to put "= 0".
mfomitchev 2015/04/17 14:41:49 Done.
+ virtual void Clear();
yosin_UTC9 2015/04/17 04:24:50 ditto
mfomitchev 2015/04/17 14:41:49 Done.
+
+ // 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&);
yosin_UTC9 2015/04/17 04:24:50 ditto
mfomitchev 2015/04/17 14:41:49 Done.
+protected:
+ GranularityStrategy() { };
+};
+
+// Always uses character granularity.
+class CharacterGranularityStrategy : public GranularityStrategy {
yosin_UTC9 2015/04/17 04:24:50 nit: "final"
mfomitchev 2015/04/17 14:41:49 Done.
+public:
+ CharacterGranularityStrategy() { };
+ ~CharacterGranularityStrategy() override { };
+
+ SelectionStrategy GetType() const override { return StrategyCharacter; }
+ 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 {
yosin_UTC9 2015/04/17 04:24:50 nit: final
mfomitchev 2015/04/17 14:41:49 Done.
+public:
+ DirectionGranularityStrategy();
+ ~DirectionGranularityStrategy() override { };
+
+ SelectionStrategy GetType() const override { return StrategyDirection; }
+ void Clear() override;
+ VisibleSelection updateExtent(const VisiblePosition&, const VisibleSelection&) override;
+private:
+ enum EWordBoundAdjust {CurrentPosIfOnBound = false, NextBoundIfOnBound = true};
yosin_UTC9 2015/04/17 04:24:51 nit: we don't need to assign value for enum fields
yosin_UTC9 2015/04/17 04:24:51 nit: better to use |enum class|.
mfomitchev 2015/04/17 14:41:49 Done.
mfomitchev 2015/04/17 14:41:49 Neat! Thanks for the tip!
+ enum ESearchDirection {SearchBackwards = 0, SearchForward = 1};
yosin_UTC9 2015/04/17 04:24:50 nit: we don't need to assign value for enum fields
yosin_UTC9 2015/04/17 04:24:50 nit: better to use |enum class|.
mfomitchev 2015/04/17 14:41:49 Done.
mfomitchev 2015/04/17 14:41:49 Done.
+
+ // 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*/, ESearchDirection /*direction*/, EWordBoundAdjust /*nextIfOnBound*/);
+
+ // 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,
+ ESearchDirection direction,
+ EWordBoundAdjust wordBoundAdjust)
+{
+ if (direction == SearchForward)
+ return endOfWord(pos, wordBoundAdjust == CurrentPosIfOnBound ? LeftWordIfOnBoundary : RightWordIfOnBoundary);
+ return startOfWord(pos, wordBoundAdjust == CurrentPosIfOnBound ? RightWordIfOnBoundary : LeftWordIfOnBoundary);
+}
+
+VisibleSelection DirectionGranularityStrategy::updateExtent(const VisiblePosition& extentPosition, const VisibleSelection& selection)
+{
+ if (extentPosition == selection.visibleExtent())
+ return selection;
+
+ const VisiblePosition base = selection.visibleBase();
+ const VisiblePosition oldExtentWithGranularity = selection.isBaseFirst() ? selection.visibleEnd() : selection.visibleStart();
+
+ int extentBaseOrder = comparePositions(extentPosition, base);
+ int oldExtentBaseOrder = comparePositions(oldExtentWithGranularity, base);
+
+ bool extentBaseOrderSwitched = (extentBaseOrder > 0 && oldExtentBaseOrder < 0)
+ || (extentBaseOrder < 0 && oldExtentBaseOrder > 0);
+
+ // 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 > 0 ? SearchForward : SearchBackwards,
+ m_lastMoveShrunkSelection ? NextBoundIfOnBound : CurrentPosIfOnBound);
+
+ bool thisMoveShrunkSelection = (extentBaseOrder > 0 && comparePositions(extentPosition, selection.visibleExtent()) < 0)
+ || (extentBaseOrder < 0 && comparePositions(extentPosition, selection.visibleExtent()) > 0);
+ // 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 > 0 ? SearchForward : SearchBackwards, NextBoundIfOnBound);
+ m_granularity = CharacterGranularity;
+ // When the base/extent order switches it doesn't count as shrinking selection.
+ thisMoveShrunkSelection = false;
+ }
+
+ bool expandedBeyondWordBoundary;
+ if (extentBaseOrder > 0)
+ expandedBeyondWordBoundary = comparePositions(extentPosition, currentWordBoundary) > 0;
+ else
+ expandedBeyondWordBoundary = 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 +356,15 @@ void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection&
void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
{
+ if (m_granularityStrategy && (options & FrameSelection::DoNotClearStrategy) == 0) {
+ m_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 +1311,8 @@ LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositi
void FrameSelection::clear()
{
m_granularity = CharacterGranularity;
+ if (m_granularityStrategy)
+ m_granularityStrategy->Clear();
setSelection(VisibleSelection());
}
@@ -1913,21 +2053,38 @@ bool FrameSelection::selectWordAroundPosition(const VisiblePosition& position)
return false;
}
-void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosition, TextGranularity granularity)
+GranularityStrategy* FrameSelection::granularityStrategy()
{
- if (isNone())
- return;
+ // We do lazy initalization for m_granularityStrategy, because if we initialize it
+ // right in the constructor - the correct settings may not be set yet.
+ SelectionStrategy strategyType = StrategyCharacter;
+ Settings* settings = m_frame ? m_frame->settings() : 0;
+ if (settings && settings->selectionStrategy() == StrategyDirection)
+ strategyType = StrategyDirection;
- 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 && m_granularityStrategy->GetType() == strategyType)
+ return m_granularityStrategy.get();
+
+ if (strategyType == StrategyDirection)
+ m_granularityStrategy = adoptPtr(new DirectionGranularityStrategy());
else
- newSelection.setStartRespectingGranularity(granularity);
- if (!newSelection.isRange())
+ m_granularityStrategy = adoptPtr(new CharacterGranularityStrategy());
+ 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)
« no previous file with comments | « Source/core/editing/FrameSelection.h ('k') | Source/core/editing/FrameSelectionTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698