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

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: 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 unified diff | Download patch
« no previous file with comments | « Source/core/editing/FrameSelection.h ('k') | Source/core/editing/FrameSelectionTest.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 {
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
97 public:
98 virtual ~GranularityStrategy() { };
99 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.
100 virtual void Clear();
yosin_UTC9 2015/04/17 04:24:50 ditto
mfomitchev 2015/04/17 14:41:49 Done.
101
102 // Calculates and returns the new selection based on the updated user select ion extent |extentPosition| and the granularity strategy.
103 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.
104 protected:
105 GranularityStrategy() { };
106 };
107
108 // Always uses character granularity.
109 class CharacterGranularityStrategy : public GranularityStrategy {
yosin_UTC9 2015/04/17 04:24:50 nit: "final"
mfomitchev 2015/04/17 14:41:49 Done.
110 public:
111 CharacterGranularityStrategy() { };
112 ~CharacterGranularityStrategy() override { };
113
114 SelectionStrategy GetType() const override { return StrategyCharacter; }
115 void Clear() override { };
116 VisibleSelection updateExtent(const VisiblePosition& extentPosition, const V isibleSelection& selection) override
117 {
118 return VisibleSelection(selection.visibleBase(), extentPosition);
119 };
120 };
121
122 // "Expand by word, shrink by character" selection strategy.
123 // Uses character granularity when selection is shrinking. If the selection is e xpanding,
124 // granularity doesn't change until a word boundary is passed, after which the g ranularity
125 // switches to "word".
126 class DirectionGranularityStrategy : public GranularityStrategy {
yosin_UTC9 2015/04/17 04:24:50 nit: final
mfomitchev 2015/04/17 14:41:49 Done.
127 public:
128 DirectionGranularityStrategy();
129 ~DirectionGranularityStrategy() override { };
130
131 SelectionStrategy GetType() const override { return StrategyDirection; }
132 void Clear() override;
133 VisibleSelection updateExtent(const VisiblePosition&, const VisibleSelection &) override;
134 private:
135 enum EWordBoundAdjust {CurrentPosIfOnBound = false, NextBoundIfOnBound = tru e};
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!
136 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.
137
138 // Returns the next word boundary starting from |pos|. |direction| specifies the direction
139 // in which to search for the next bound. nextIfOnBound controls whether |po s| or the next boundary
140 // is returned when |pos| is located exactly on word boundary.
141 VisiblePosition nextWordBound(const VisiblePosition& /*pos*/, ESearchDirecti on /*direction*/, EWordBoundAdjust /*nextIfOnBound*/);
142
143 // Current selection granularity being used
144 TextGranularity m_granularity;
145 // Set to true if the selection was shrunk (without changing relative base/e xtent order)
146 // as a result of the most recent updateExtent call.
147 bool m_lastMoveShrunkSelection;
148 };
149
150 DirectionGranularityStrategy::DirectionGranularityStrategy()
151 : m_granularity(CharacterGranularity)
152 , m_lastMoveShrunkSelection(false) { }
153
154 void DirectionGranularityStrategy::Clear()
155 {
156 m_granularity = CharacterGranularity;
157 m_lastMoveShrunkSelection = false;
158 }
159
160 VisiblePosition DirectionGranularityStrategy::nextWordBound(
161 const VisiblePosition& pos,
162 ESearchDirection direction,
163 EWordBoundAdjust wordBoundAdjust)
164 {
165 if (direction == SearchForward)
166 return endOfWord(pos, wordBoundAdjust == CurrentPosIfOnBound ? LeftWord IfOnBoundary : RightWordIfOnBoundary);
167 return startOfWord(pos, wordBoundAdjust == CurrentPosIfOnBound ? RightWordI fOnBoundary : LeftWordIfOnBoundary);
168 }
169
170 VisibleSelection DirectionGranularityStrategy::updateExtent(const VisiblePositio n& extentPosition, const VisibleSelection& selection)
171 {
172 if (extentPosition == selection.visibleExtent())
173 return selection;
174
175 const VisiblePosition base = selection.visibleBase();
176 const VisiblePosition oldExtentWithGranularity = selection.isBaseFirst() ? s election.visibleEnd() : selection.visibleStart();
177
178 int extentBaseOrder = comparePositions(extentPosition, base);
179 int oldExtentBaseOrder = comparePositions(oldExtentWithGranularity, base);
180
181 bool extentBaseOrderSwitched = (extentBaseOrder > 0 && oldExtentBaseOrder < 0)
182 || (extentBaseOrder < 0 && oldExtentBaseOrder > 0);
183
184 // Determine the boundary of the 'current word', i.e. the boundary extending beyond which
185 // should change the granularity to WordGranularity.
186 // If the last move has shrunk the selection and is now exactly on the word boundary -
187 // we need to take the next bound as the bound of the "current word".
188 VisiblePosition currentWordBoundary = nextWordBound(
189 oldExtentWithGranularity,
190 oldExtentBaseOrder > 0 ? SearchForward : SearchBackwards,
191 m_lastMoveShrunkSelection ? NextBoundIfOnBound : CurrentPosIfOnBound);
192
193 bool thisMoveShrunkSelection = (extentBaseOrder > 0 && comparePositions(exte ntPosition, selection.visibleExtent()) < 0)
194 || (extentBaseOrder < 0 && comparePositions(extentPosition, selection.vi sibleExtent()) > 0);
195 // If the extent-base order was switched, then the selection is now expandin g in a different
196 // direction than before. Therefore we need to calculate the boundary of the 'current word'
197 // in this new direction in order to be able to tell if the selection expand ed beyond it.
198 if (extentBaseOrderSwitched) {
199 currentWordBoundary = nextWordBound(base, extentBaseOrder > 0 ? SearchFo rward : SearchBackwards, NextBoundIfOnBound);
200 m_granularity = CharacterGranularity;
201 // When the base/extent order switches it doesn't count as shrinking sel ection.
202 thisMoveShrunkSelection = false;
203 }
204
205 bool expandedBeyondWordBoundary;
206 if (extentBaseOrder > 0)
207 expandedBeyondWordBoundary = comparePositions(extentPosition, currentWor dBoundary) > 0;
208 else
209 expandedBeyondWordBoundary = comparePositions(extentPosition, currentWor dBoundary) < 0;
210 if (expandedBeyondWordBoundary) {
211 m_granularity = WordGranularity;
212 } else if (thisMoveShrunkSelection) {
213 m_granularity = CharacterGranularity;
214 m_lastMoveShrunkSelection = true;
215 }
216
217 m_lastMoveShrunkSelection = thisMoveShrunkSelection;
218 VisibleSelection newSelection = selection;
219 newSelection.setExtent(extentPosition);
220 if (m_granularity == WordGranularity) {
221 if (extentBaseOrder > 0)
222 newSelection.setEndRespectingGranularity(m_granularity, LeftWordIfOn Boundary);
223 else
224 newSelection.setStartRespectingGranularity(m_granularity, RightWordI fOnBoundary);
225 }
226
227 return newSelection;
228 }
229
96 FrameSelection::FrameSelection(LocalFrame* frame) 230 FrameSelection::FrameSelection(LocalFrame* frame)
97 : m_frame(frame) 231 : m_frame(frame)
98 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()) 232 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
99 , m_observingVisibleSelection(false) 233 , m_observingVisibleSelection(false)
100 , m_granularity(CharacterGranularity) 234 , m_granularity(CharacterGranularity)
101 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired) 235 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired)
102 , m_caretRectDirty(true) 236 , m_caretRectDirty(true)
103 , m_shouldPaintCaret(true) 237 , m_shouldPaintCaret(true)
104 , m_isCaretBlinkingSuspended(false) 238 , m_isCaretBlinkingSuspended(false)
105 , m_focused(frame && frame->page() && frame->page()->focusController().focus edFrame() == frame) 239 , m_focused(frame && frame->page() && frame->page()->focusController().focus edFrame() == frame)
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
215 349
216 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent w ill make newSelection always directional 350 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent w ill make newSelection always directional
217 if (m_selection == newSelection) 351 if (m_selection == newSelection)
218 return; 352 return;
219 353
220 setSelection(newSelection, granularity); 354 setSelection(newSelection, granularity);
221 } 355 }
222 356
223 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelec tionOptions options, CursorAlignOnScroll align, TextGranularity granularity) 357 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelec tionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
224 { 358 {
359 if (m_granularityStrategy && (options & FrameSelection::DoNotClearStrategy) == 0) {
360 m_granularityStrategy->Clear();
361 }
225 bool closeTyping = options & CloseTyping; 362 bool closeTyping = options & CloseTyping;
226 bool shouldClearTypingStyle = options & ClearTypingStyle; 363 bool shouldClearTypingStyle = options & ClearTypingStyle;
227 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); 364 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
228 365
229 VisibleSelection s = validateSelection(newSelection); 366 VisibleSelection s = validateSelection(newSelection);
367
230 if (shouldAlwaysUseDirectionalSelection(m_frame)) 368 if (shouldAlwaysUseDirectionalSelection(m_frame))
231 s.setIsDirectional(true); 369 s.setIsDirectional(true);
232 370
233 if (!m_frame) { 371 if (!m_frame) {
234 m_selection = s; 372 m_selection = s;
235 return; 373 return;
236 } 374 }
237 375
238 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at Fra meSelection::setSelection 376 // <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 377 // 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; 1304 m_xPosForVerticalArrowNavigation = x;
1167 } else 1305 } else
1168 x = m_xPosForVerticalArrowNavigation; 1306 x = m_xPosForVerticalArrowNavigation;
1169 1307
1170 return x; 1308 return x;
1171 } 1309 }
1172 1310
1173 void FrameSelection::clear() 1311 void FrameSelection::clear()
1174 { 1312 {
1175 m_granularity = CharacterGranularity; 1313 m_granularity = CharacterGranularity;
1314 if (m_granularityStrategy)
1315 m_granularityStrategy->Clear();
1176 setSelection(VisibleSelection()); 1316 setSelection(VisibleSelection());
1177 } 1317 }
1178 1318
1179 void FrameSelection::prepareForDestruction() 1319 void FrameSelection::prepareForDestruction()
1180 { 1320 {
1181 m_granularity = CharacterGranularity; 1321 m_granularity = CharacterGranularity;
1182 1322
1183 m_caretBlinkTimer.stop(); 1323 m_caretBlinkTimer.stop();
1184 1324
1185 LayoutView* view = m_frame->contentRenderer(); 1325 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()); 2046 String text = plainText(start.deepEquivalent(), end.deepEquivalent());
1907 if (!text.isEmpty() && !isSeparator(text.characterStartingAt(0))) { 2047 if (!text.isEmpty() && !isSeparator(text.characterStartingAt(0))) {
1908 setSelection(VisibleSelection(start, end), WordGranularity); 2048 setSelection(VisibleSelection(start, end), WordGranularity);
1909 return true; 2049 return true;
1910 } 2050 }
1911 } 2051 }
1912 2052
1913 return false; 2053 return false;
1914 } 2054 }
1915 2055
1916 void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosit ion, TextGranularity granularity) 2056 GranularityStrategy* FrameSelection::granularityStrategy()
1917 { 2057 {
1918 if (isNone()) 2058 // We do lazy initalization for m_granularityStrategy, because if we initial ize it
2059 // right in the constructor - the correct settings may not be set yet.
2060 SelectionStrategy strategyType = StrategyCharacter;
2061 Settings* settings = m_frame ? m_frame->settings() : 0;
2062 if (settings && settings->selectionStrategy() == StrategyDirection)
2063 strategyType = StrategyDirection;
2064
2065 if (m_granularityStrategy && m_granularityStrategy->GetType() == strategyTyp e)
2066 return m_granularityStrategy.get();
2067
2068 if (strategyType == StrategyDirection)
2069 m_granularityStrategy = adoptPtr(new DirectionGranularityStrategy());
2070 else
2071 m_granularityStrategy = adoptPtr(new CharacterGranularityStrategy());
2072 return m_granularityStrategy.get();
2073 }
2074
2075 void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosit ion)
2076 {
2077 const VisiblePosition base = m_selection.visibleBase();
2078
2079 if (isNone() || base == extentPosition)
1919 return; 2080 return;
1920 2081
1921 const VisiblePosition basePosition = m_selection.isBaseFirst() ? m_selection .visibleStart() : m_selection.visibleEnd(); 2082 VisibleSelection newSelection = granularityStrategy()->updateExtent(extentPo sition, selection());
1922 VisibleSelection newSelection(basePosition, extentPosition); 2083 setSelection(
1923 if (newSelection.isBaseFirst()) 2084 newSelection,
1924 newSelection.setEndRespectingGranularity(granularity); 2085 FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | FrameSe lection::DoNotClearStrategy | UserTriggered,
1925 else 2086 FrameSelection::AlignCursorOnScrollIfNeeded,
1926 newSelection.setStartRespectingGranularity(granularity); 2087 CharacterGranularity);
1927 if (!newSelection.isRange())
1928 return;
1929
1930 setSelection(newSelection, FrameSelection::CloseTyping | FrameSelection::Cle arTypingStyle | UserTriggered, FrameSelection::AlignCursorOnScrollIfNeeded, gran ularity);
1931 } 2088 }
1932 2089
1933 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, con st VisiblePosition& extentPosition, TextGranularity granularity) 2090 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, con st VisiblePosition& extentPosition, TextGranularity granularity)
1934 { 2091 {
1935 VisibleSelection newSelection(basePosition, extentPosition); 2092 VisibleSelection newSelection(basePosition, extentPosition);
1936 newSelection.expandUsingGranularity(granularity); 2093 newSelection.expandUsingGranularity(granularity);
1937 2094
1938 if (newSelection.isNone()) 2095 if (newSelection.isNone())
1939 return; 2096 return;
1940 2097
1941 setSelection(newSelection, granularity); 2098 setSelection(newSelection, granularity);
1942 } 2099 }
1943 2100
1944 } 2101 }
1945 2102
1946 #ifndef NDEBUG 2103 #ifndef NDEBUG
1947 2104
1948 void showTree(const blink::FrameSelection& sel) 2105 void showTree(const blink::FrameSelection& sel)
1949 { 2106 {
1950 sel.showTreeForThis(); 2107 sel.showTreeForThis();
1951 } 2108 }
1952 2109
1953 void showTree(const blink::FrameSelection* sel) 2110 void showTree(const blink::FrameSelection* sel)
1954 { 2111 {
1955 if (sel) 2112 if (sel)
1956 sel->showTreeForThis(); 2113 sel->showTreeForThis();
1957 } 2114 }
1958 2115
1959 #endif 2116 #endif
OLDNEW
« 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