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

Side by Side 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 unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved. 2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
86 static inline LayoutUnit NoXPosForVerticalArrowNavigation() 86 static inline LayoutUnit NoXPosForVerticalArrowNavigation()
87 { 87 {
88 return LayoutUnit::min(); 88 return LayoutUnit::min();
89 } 89 }
90 90
91 static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame) 91 static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame)
92 { 92 {
93 return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirecti onal(); 93 return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirecti onal();
94 } 94 }
95 95
96 class GranularityStrategy {
97 public:
98 virtual ~GranularityStrategy() { };
99 virtual void Clear();
100
101 // Calculates and returns the new selection based on the updated user select ion extent |extentPosition| and the granularity strategy.
102 virtual VisibleSelection updateExtent(const VisiblePosition& extentPosition, const VisibleSelection&);
103 protected:
104 GranularityStrategy() { };
105 };
106
107 class DefaultGranularityStrategy : public GranularityStrategy {
108 public:
109 DefaultGranularityStrategy() { };
110 ~DefaultGranularityStrategy() override { };
111
112 void Clear() override { };
113 VisibleSelection updateExtent(const VisiblePosition& extentPosition, const V isibleSelection& selection) override
114 {
115 return VisibleSelection(selection.visibleBase(), extentPosition);
116 };
117 };
118
119 // "Expand by word, shrink by character" selection strategy.
120 // Uses character granularity when selection is shrinking. If the selection is e xpanding,
121 // granularity doesn't change until a word boundary is passed, after which the g ranularity
122 // switches to "word".
123 class DirectionGranularityStrategy : public GranularityStrategy {
124 public:
125 DirectionGranularityStrategy();
126 ~DirectionGranularityStrategy() override { };
127
128 void Clear() override;
129 VisibleSelection updateExtent(const VisiblePosition&, const VisibleSelection &) override;
130 private:
131 // Returns the next word boundary starting from |pos|. |direction| specifies the direction
132 // in which to search for the next bound. nextIfOnBound controls whether |po s| or the next boundary
133 // is returned when |pos| is located exactly on word boundary.
134 VisiblePosition nextWordBound(const VisiblePosition& /*pos*/, int /*directio n*/, 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
135
136 // Current selection granularity being used
137 TextGranularity m_granularity;
138 // Set to true if the selection was shrunk (without changing relative base/e xtent order)
139 // as a result of the most recent updateExtent call.
140 bool m_lastMoveShrunkSelection;
141 };
142
143 DirectionGranularityStrategy::DirectionGranularityStrategy()
144 : m_granularity(CharacterGranularity)
145 , m_lastMoveShrunkSelection(false) { }
146
147 void DirectionGranularityStrategy::Clear()
148 {
149 m_granularity = CharacterGranularity;
150 m_lastMoveShrunkSelection = false;
151 }
152
153 VisiblePosition DirectionGranularityStrategy::nextWordBound(
154 const VisiblePosition& pos,
155 int direction,
156 bool nextIfOnBound)
157 {
158 return direction > 0 ? endOfWord(pos, nextIfOnBound ? RightWordIfOnBoundary : LeftWordIfOnBoundary)
159 : startOfWord(pos, nextIfOnBound ? LeftWordIfOnBoundary : RightWordIfOnB oundary);
160 }
161
162 VisibleSelection DirectionGranularityStrategy::updateExtent(const VisiblePositio n& extentPosition, const VisibleSelection& selection)
163 {
164 if (extentPosition == selection.visibleExtent())
165 return selection;
166
167 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.
168 const VisiblePosition& oldExtentWithGranularity = selection.isBaseFirst() ? selection.visibleEnd() : selection.visibleStart();
169
170 int extentBaseOrder = comparePositions(extentPosition, base);
171 int oldExtentBaseOrder = comparePositions(oldExtentWithGranularity, base);
172 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
173
174 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.
175
176 // Determine the boundary of the 'current word', i.e. the boundary extending beyond which
177 // should change the granularity to WordGranularity.
178 // If the last move has shrunk the selection and is now exactly on the word boundary -
179 // we need to take the next bound as the bound of the "current word".
180 VisiblePosition currentWordBoundary =
181 nextWordBound(oldExtentWithGranularity, oldExtentBaseOrder, m_lastMoveSh runkSelection);
182
183 bool thisMoveShrunkSelection = extentBaseOrder * comparePositions(extentPosi tion, selection.visibleExtent()) < 0;
yosin_UTC9 2015/04/16 04:11:26 ditto as L174.
mfomitchev1 2015/04/17 00:13:50 Done.
184 // If the extent-base order was switched, then the selection is now expandin g in a different
185 // direction than before. Therefore we need to calculate the boundary of the 'current word'
186 // in this new direction in order to be able to tell if the selection expand ed beyond it.
187 if (extentBaseOrderSwitched) {
188 currentWordBoundary = nextWordBound(base, extentBaseOrder, true);
189 m_granularity = CharacterGranularity;
190 // When the base/extent order switches it doesn't count as shrinking sel ection.
191 thisMoveShrunkSelection = false;
192 }
193
194 bool expandedBeyondWordBoundary = extentBaseOrder > 0 ? comparePositions(ext entPosition, 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.
195 : comparePositions(extentPosition, currentWordBoundary) < 0;
196 if (expandedBeyondWordBoundary) {
197 m_granularity = WordGranularity;
198 } else if (thisMoveShrunkSelection) {
199 m_granularity = CharacterGranularity;
200 m_lastMoveShrunkSelection = true;
201 }
202
203 m_lastMoveShrunkSelection = thisMoveShrunkSelection;
204 VisibleSelection newSelection = selection;
205 newSelection.setExtent(extentPosition);
206 if (m_granularity == WordGranularity) {
207 if (extentBaseOrder > 0)
208 newSelection.setEndRespectingGranularity(m_granularity, LeftWordIfOn Boundary);
209 else
210 newSelection.setStartRespectingGranularity(m_granularity, RightWordI fOnBoundary);
211 }
212
213 return newSelection;
214 }
215
96 FrameSelection::FrameSelection(LocalFrame* frame) 216 FrameSelection::FrameSelection(LocalFrame* frame)
97 : m_frame(frame) 217 : m_frame(frame)
98 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()) 218 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
99 , m_observingVisibleSelection(false) 219 , m_observingVisibleSelection(false)
100 , m_granularity(CharacterGranularity) 220 , m_granularity(CharacterGranularity)
101 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired) 221 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired)
102 , m_caretRectDirty(true) 222 , m_caretRectDirty(true)
103 , m_shouldPaintCaret(true) 223 , m_shouldPaintCaret(true)
104 , m_isCaretBlinkingSuspended(false) 224 , m_isCaretBlinkingSuspended(false)
105 , m_focused(frame && frame->page() && frame->page()->focusController().focus edFrame() == frame) 225 , m_focused(frame && frame->page() && frame->page()->focusController().focus edFrame() == frame)
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
215 335
216 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent w ill make newSelection always directional 336 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent w ill make newSelection always directional
217 if (m_selection == newSelection) 337 if (m_selection == newSelection)
218 return; 338 return;
219 339
220 setSelection(newSelection, granularity); 340 setSelection(newSelection, granularity);
221 } 341 }
222 342
223 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelec tionOptions options, CursorAlignOnScroll align, TextGranularity granularity) 343 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelec tionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
224 { 344 {
345 if ((options & FrameSelection::DoNotClearStrategy) == 0)
346 granularityStrategy()->Clear();
225 bool closeTyping = options & CloseTyping; 347 bool closeTyping = options & CloseTyping;
226 bool shouldClearTypingStyle = options & ClearTypingStyle; 348 bool shouldClearTypingStyle = options & ClearTypingStyle;
227 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); 349 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
228 350
229 VisibleSelection s = validateSelection(newSelection); 351 VisibleSelection s = validateSelection(newSelection);
352
230 if (shouldAlwaysUseDirectionalSelection(m_frame)) 353 if (shouldAlwaysUseDirectionalSelection(m_frame))
231 s.setIsDirectional(true); 354 s.setIsDirectional(true);
232 355
233 if (!m_frame) { 356 if (!m_frame) {
234 m_selection = s; 357 m_selection = s;
235 return; 358 return;
236 } 359 }
237 360
238 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at Fra meSelection::setSelection 361 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at Fra meSelection::setSelection
239 // if document->frame() == m_frame we can get into an infinite loop 362 // if document->frame() == m_frame we can get into an infinite loop
(...skipping 926 matching lines...) Expand 10 before | Expand all | Expand 10 after
1166 m_xPosForVerticalArrowNavigation = x; 1289 m_xPosForVerticalArrowNavigation = x;
1167 } else 1290 } else
1168 x = m_xPosForVerticalArrowNavigation; 1291 x = m_xPosForVerticalArrowNavigation;
1169 1292
1170 return x; 1293 return x;
1171 } 1294 }
1172 1295
1173 void FrameSelection::clear() 1296 void FrameSelection::clear()
1174 { 1297 {
1175 m_granularity = CharacterGranularity; 1298 m_granularity = CharacterGranularity;
1299 granularityStrategy()->Clear();
1176 setSelection(VisibleSelection()); 1300 setSelection(VisibleSelection());
1177 } 1301 }
1178 1302
1179 void FrameSelection::prepareForDestruction() 1303 void FrameSelection::prepareForDestruction()
1180 { 1304 {
1181 m_granularity = CharacterGranularity; 1305 m_granularity = CharacterGranularity;
1182 1306
1183 m_caretBlinkTimer.stop(); 1307 m_caretBlinkTimer.stop();
1184 1308
1185 LayoutView* view = m_frame->contentRenderer(); 1309 LayoutView* view = m_frame->contentRenderer();
(...skipping 720 matching lines...) Expand 10 before | Expand all | Expand 10 after
1906 String text = plainText(start.deepEquivalent(), end.deepEquivalent()); 2030 String text = plainText(start.deepEquivalent(), end.deepEquivalent());
1907 if (!text.isEmpty() && !isSeparator(text.characterStartingAt(0))) { 2031 if (!text.isEmpty() && !isSeparator(text.characterStartingAt(0))) {
1908 setSelection(VisibleSelection(start, end), WordGranularity); 2032 setSelection(VisibleSelection(start, end), WordGranularity);
1909 return true; 2033 return true;
1910 } 2034 }
1911 } 2035 }
1912 2036
1913 return false; 2037 return false;
1914 } 2038 }
1915 2039
1916 void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosit ion, TextGranularity granularity) 2040 GranularityStrategy* const FrameSelection::granularityStrategy()
yosin_UTC9 2015/04/16 04:11:26 nit: omit |const|
mfomitchev1 2015/04/17 00:13:51 Done.
1917 { 2041 {
1918 if (isNone()) 2042 if (m_granularityStrategy)
2043 return m_granularityStrategy.get();
2044 // We do lazy initalization for m_granularityStrategy, because if we initial ize it
2045 // 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.
2046 Settings* settings = m_frame ? m_frame->settings() : 0;
2047 if (settings && settings->selectionStrategy() == StrategyDirection)
2048 m_granularityStrategy = adoptPtr(new DirectionGranularityStrategy());
2049 else
2050 m_granularityStrategy = adoptPtr(new DefaultGranularityStrategy());
2051 return m_granularityStrategy.get();
2052 }
2053
2054 void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosit ion)
2055 {
2056 const VisiblePosition base = m_selection.visibleBase();
2057
2058 if (isNone() || base == extentPosition)
1919 return; 2059 return;
1920 2060
1921 const VisiblePosition basePosition = m_selection.isBaseFirst() ? m_selection .visibleStart() : m_selection.visibleEnd(); 2061 VisibleSelection newSelection = granularityStrategy()->updateExtent(extentPo sition, selection());
1922 VisibleSelection newSelection(basePosition, extentPosition); 2062 setSelection(
1923 if (newSelection.isBaseFirst()) 2063 newSelection,
1924 newSelection.setEndRespectingGranularity(granularity); 2064 FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | FrameSe lection::DoNotClearStrategy | UserTriggered,
1925 else 2065 FrameSelection::AlignCursorOnScrollIfNeeded,
1926 newSelection.setStartRespectingGranularity(granularity); 2066 CharacterGranularity);
1927 if (!newSelection.isRange())
1928 return;
1929
1930 setSelection(newSelection, FrameSelection::CloseTyping | FrameSelection::Cle arTypingStyle | UserTriggered, FrameSelection::AlignCursorOnScrollIfNeeded, gran ularity);
1931 } 2067 }
1932 2068
1933 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, con st VisiblePosition& extentPosition, TextGranularity granularity) 2069 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, con st VisiblePosition& extentPosition, TextGranularity granularity)
1934 { 2070 {
1935 VisibleSelection newSelection(basePosition, extentPosition); 2071 VisibleSelection newSelection(basePosition, extentPosition);
1936 newSelection.expandUsingGranularity(granularity); 2072 newSelection.expandUsingGranularity(granularity);
1937 2073
1938 if (newSelection.isNone()) 2074 if (newSelection.isNone())
1939 return; 2075 return;
1940 2076
1941 setSelection(newSelection, granularity); 2077 setSelection(newSelection, granularity);
1942 } 2078 }
1943 2079
1944 } 2080 }
1945 2081
1946 #ifndef NDEBUG 2082 #ifndef NDEBUG
1947 2083
1948 void showTree(const blink::FrameSelection& sel) 2084 void showTree(const blink::FrameSelection& sel)
1949 { 2085 {
1950 sel.showTreeForThis(); 2086 sel.showTreeForThis();
1951 } 2087 }
1952 2088
1953 void showTree(const blink::FrameSelection* sel) 2089 void showTree(const blink::FrameSelection* sel)
1954 { 2090 {
1955 if (sel) 2091 if (sel)
1956 sel->showTreeForThis(); 2092 sel->showTreeForThis();
1957 } 2093 }
1958 2094
1959 #endif 2095 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698