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

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

Powered by Google App Engine
This is Rietveld 408576698