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

Side by Side Diff: Source/core/editing/GranularityStrategy.cpp

Issue 1123563003: Improving direction-based selection strategy. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Fixing the slow extend and expanding tests a bit. Created 5 years, 7 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 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "config.h" 5 #include "config.h"
6 #include "core/editing/GranularityStrategy.h" 6 #include "core/editing/GranularityStrategy.h"
7 7
8 #include "core/editing/FrameSelection.h"
8 #include "core/editing/htmlediting.h" 9 #include "core/editing/htmlediting.h"
9 10
10 namespace blink { 11 namespace blink {
11 12
12 GranularityStrategy::GranularityStrategy() { } 13 GranularityStrategy::GranularityStrategy() { }
13 14
14 GranularityStrategy::~GranularityStrategy() { } 15 GranularityStrategy::~GranularityStrategy() { }
15 16
16 CharacterGranularityStrategy::CharacterGranularityStrategy() { } 17 CharacterGranularityStrategy::CharacterGranularityStrategy() { }
17 18
18 CharacterGranularityStrategy::~CharacterGranularityStrategy() { } 19 CharacterGranularityStrategy::~CharacterGranularityStrategy() { }
19 20
20 SelectionStrategy CharacterGranularityStrategy::GetType() const 21 SelectionStrategy CharacterGranularityStrategy::GetType() const
21 { 22 {
22 return SelectionStrategy::Character; 23 return SelectionStrategy::Character;
23 } 24 }
24 25
25 void CharacterGranularityStrategy::Clear() { }; 26 void CharacterGranularityStrategy::Clear() { };
26 27
27 VisibleSelection CharacterGranularityStrategy::updateExtent(const VisiblePositio n& extentPosition, const VisibleSelection& selection) 28 VisibleSelection CharacterGranularityStrategy::updateExtent(const IntPoint& exte ntPoint, LocalFrame* frame)
28 { 29 {
30 const VisiblePosition& extentPosition = visiblePositionForContentsPoint(exte ntPoint, frame);
31 const VisibleSelection& selection = frame->selection().selection();
32 if (selection.visibleBase() == extentPosition)
33 return selection;
29 return VisibleSelection(selection.visibleBase(), extentPosition); 34 return VisibleSelection(selection.visibleBase(), extentPosition);
30 } 35 }
31 36
32 DirectionGranularityStrategy::DirectionGranularityStrategy() 37 DirectionGranularityStrategy::DirectionGranularityStrategy()
33 : m_granularity(CharacterGranularity) 38 : m_state(StrategyState::Cleared)
34 , m_lastMoveShrunkSelection(false) { } 39 , m_granularity(CharacterGranularity)
40 , m_offset(0) { }
35 41
36 DirectionGranularityStrategy::~DirectionGranularityStrategy() { } 42 DirectionGranularityStrategy::~DirectionGranularityStrategy() { }
37 43
38 SelectionStrategy DirectionGranularityStrategy::GetType() const 44 SelectionStrategy DirectionGranularityStrategy::GetType() const
39 { 45 {
40 return SelectionStrategy::Direction; 46 return SelectionStrategy::Direction;
41 } 47 }
42 48
43 void DirectionGranularityStrategy::Clear() 49 void DirectionGranularityStrategy::Clear()
44 { 50 {
51 m_state = StrategyState::Cleared;
45 m_granularity = CharacterGranularity; 52 m_granularity = CharacterGranularity;
46 m_lastMoveShrunkSelection = false; 53 m_offset = 0;
54 m_subPositionCorrection.setWidth(0);
55 m_subPositionCorrection.setHeight(0);
leviw_travelin_and_unemployed 2015/05/26 20:25:50 m_subPositionCorrection = IntSize();
mfomitchev 2015/05/26 22:32:47 Acknowledged.
mfomitchev 2015/06/03 18:57:54 Done.
47 } 56 }
48 57
58 bool DirectionGranularityStrategy::arePositionsInSpecifiedOrder(
59 const VisiblePosition& vp1,
60 const VisiblePosition& vp2,
61 int specifiedOrder)
62 {
63 int positionOrder = comparePositions(vp1, vp2);
64 if (specifiedOrder == 0)
65 return positionOrder == 0;
66 return specifiedOrder > 0 ? positionOrder > 0 : positionOrder < 0;
67 }
68
69
49 VisiblePosition DirectionGranularityStrategy::nextWordBound( 70 VisiblePosition DirectionGranularityStrategy::nextWordBound(
50 const VisiblePosition& pos, 71 const VisiblePosition& pos,
51 SearchDirection direction, 72 SearchDirection direction,
52 BoundAdjust wordBoundAdjust) 73 BoundAdjust wordBoundAdjust)
53 { 74 {
54 bool nextBoundIfOnBound = wordBoundAdjust == BoundAdjust::NextBoundIfOnBoun d; 75 bool nextBoundIfOnBound = wordBoundAdjust == BoundAdjust::NextBoundIfOnBoun d;
55 if (direction == SearchDirection::SearchForward) { 76 if (direction == SearchDirection::SearchForward) {
56 EWordSide wordSide = nextBoundIfOnBound ? RightWordIfOnBoundary : LeftWo rdIfOnBoundary; 77 EWordSide wordSide = nextBoundIfOnBound ? RightWordIfOnBoundary : LeftWo rdIfOnBoundary;
57 return endOfWord(pos, wordSide); 78 return endOfWord(pos, wordSide);
58 } 79 }
59 EWordSide wordSide = nextBoundIfOnBound ? LeftWordIfOnBoundary : RightWordIf OnBoundary; 80 EWordSide wordSide = nextBoundIfOnBound ? LeftWordIfOnBoundary : RightWordIf OnBoundary;
60 return startOfWord(pos, wordSide); 81 return startOfWord(pos, wordSide);
61 } 82 }
62 83
63 VisibleSelection DirectionGranularityStrategy::updateExtent(const VisiblePositio n& extentPosition, const VisibleSelection& selection) 84 VisibleSelection DirectionGranularityStrategy::updateExtent(const IntPoint& exte ntPoint, LocalFrame* frame)
64 { 85 {
65 if (extentPosition == selection.visibleExtent()) 86 const VisibleSelection& selection = frame->selection().selection();
87
88 if (m_state == StrategyState::Cleared)
89 m_state = StrategyState::Expanding;
90
91 VisiblePosition oldOffsetExtentPosition = selection.visibleExtent();
92 IntRect oldExtentCaretBounds = oldOffsetExtentPosition.absoluteCaretBounds() ;
93 IntPoint oldOffsetExtentPoint = oldExtentCaretBounds.minXMaxYCorner() + m_su bPositionCorrection;
leviw_travelin_and_unemployed 2015/05/26 20:25:50 There are subtle reasons for why you're using minX
mfomitchev 2015/05/26 22:32:47 Acknowledged.
mfomitchev 2015/06/03 18:57:54 Done.
94 IntPoint oldExtentPoint = IntPoint(oldOffsetExtentPoint.x() - m_offset, oldO ffsetExtentPoint.y());
95
96 // Apply the offset.
97 IntPoint newOffsetExtentPoint = extentPoint;
98 int dx = extentPoint.x() - oldExtentPoint.x();
99 if (m_offset != 0) {
100 if (m_offset > 0 && dx > 0)
101 m_offset = std::max(0, m_offset - dx);
102 else if (m_offset < 0 && dx < 0)
103 m_offset = std::min(0, m_offset - dx);
104 newOffsetExtentPoint.move(m_offset, 0);
105 }
106
107 VisiblePosition newOffsetExtentPosition = visiblePositionForContentsPoint(ne wOffsetExtentPoint, frame);
108 IntRect newOffsetExtentCaretBounds = newOffsetExtentPosition.absoluteCaretBo unds();
109
110 // Reset the offset in case of a line change. Also reset it if we detect a y
111 // component to the vector between the old and the new positions which would
112 // happen if the text is rotated. The offset feature doesn't work for
113 // non-horizontal text.
114 if (!inSameLine(newOffsetExtentPosition, oldOffsetExtentPosition)
115 || (newOffsetExtentCaretBounds.maxY() - oldExtentCaretBounds.maxY()) != 0) {
leviw_travelin_and_unemployed 2015/05/26 20:25:50 (newOffsetExtentCaretBounds.maxY() - oldExtentCare
mfomitchev 2015/05/26 22:32:47 Acknowledged.
mfomitchev 2015/06/03 18:57:54 Done.
116 m_offset = 0;
117 newOffsetExtentPoint = extentPoint;
118 newOffsetExtentPosition = visiblePositionForContentsPoint(extentPoint, f rame);
119 }
120
121 const VisiblePosition base = selection.visibleBase();
122
123 // Do not allow selection and extent to be at the same position
leviw_travelin_and_unemployed 2015/05/26 20:25:50 I'm a little confused by this statement. Are you n
mfomitchev 2015/05/26 22:32:46 This should read "base and extent", not "selection
leviw_travelin_and_unemployed 2015/05/26 22:47:49 SGTM.
mfomitchev 2015/06/03 18:57:54 Done.
124 if (newOffsetExtentPosition == base)
66 return selection; 125 return selection;
67 126
68 const VisiblePosition base = selection.visibleBase(); 127 int oldExtentBaseOrder = selection.isBaseFirst() ? 1 : -1;
69 const VisiblePosition oldExtentWithGranularity = selection.isBaseFirst() ? s election.visibleEnd() : selection.visibleStart();
70 128
71 int extentBaseOrder = comparePositions(extentPosition, base); 129 int newExtentBaseOrder;
72 int oldExtentBaseOrder = comparePositions(oldExtentWithGranularity, base); 130 bool thisMoveShrunkSelection;
131 if (newOffsetExtentPosition == oldOffsetExtentPosition) {
132 if (m_granularity == CharacterGranularity)
133 return selection;
73 134
74 bool extentBaseOrderSwitched = (extentBaseOrder > 0 && oldExtentBaseOrder < 0) 135 // If we are in Word granularity, we cannot exit here, since we may pass
75 || (extentBaseOrder < 0 && oldExtentBaseOrder > 0); 136 // the middle of the word without changing the position (in which case
137 // the selection needs to expand).
138 thisMoveShrunkSelection = false;
139 newExtentBaseOrder = oldExtentBaseOrder;
140 } else {
141 bool selectionExpanded = arePositionsInSpecifiedOrder(newOffsetExtentPos ition, oldOffsetExtentPosition, oldExtentBaseOrder);
142 bool extentBaseOrderSwitched = selectionExpanded ? false : !arePositions InSpecifiedOrder(newOffsetExtentPosition, base, oldExtentBaseOrder);
143 newExtentBaseOrder = extentBaseOrderSwitched ? -oldExtentBaseOrder : old ExtentBaseOrder;
76 144
77 // Determine the boundary of the 'current word', i.e. the boundary extending 145 // Determine the word boundary, i.e. the boundary extending beyond which
78 // beyond which should change the granularity to WordGranularity. 146 // should change the granularity to WordGranularity.
79 // If the last move has shrunk the selection and is now exactly on the word 147 VisiblePosition wordBoundary;
80 // boundary - we need to take the next bound as the bound of the "current 148 if (extentBaseOrderSwitched) {
81 // word". 149 // Special case.
82 VisiblePosition currentWordBoundary = nextWordBound( 150 // If the extent-base order was switched, then the selection is now
83 oldExtentWithGranularity, 151 // expanding in a different direction than before. Therefore we
84 oldExtentBaseOrder > 0 ? SearchDirection::SearchForward : SearchDirectio n::SearchBackwards, 152 // calculate the word boundary in this new direction and based on
85 m_lastMoveShrunkSelection ? BoundAdjust::NextBoundIfOnBound : BoundAdjus t::CurrentPosIfOnBound); 153 // the |base| position.
154 wordBoundary = nextWordBound(
155 base,
156 newExtentBaseOrder > 0 ? SearchDirection::SearchForward : Search Direction::SearchBackwards,
157 BoundAdjust::NextBoundIfOnBound);
158 m_granularity = CharacterGranularity;
159 } else {
160 // Calculate the word boundary based on |oldExtentWithGranularity|.
161 // If selection was shrunk in the last update and the extent is now
162 // exactly on the word boundary - we need to take the next bound as
163 // the bound of the current word.
164 wordBoundary = nextWordBound(
165 oldOffsetExtentPosition,
166 oldExtentBaseOrder > 0 ? SearchDirection::SearchForward : Search Direction::SearchBackwards,
167 m_state == StrategyState::Shrinking ? BoundAdjust::NextBoundIfOn Bound : BoundAdjust::CurrentPosIfOnBound);
168 }
86 169
87 bool thisMoveShrunkSelection = (extentBaseOrder > 0 && comparePositions(exte ntPosition, selection.visibleExtent()) < 0) 170 bool expandedBeyondWordBoundary;
88 || (extentBaseOrder < 0 && comparePositions(extentPosition, selection.vi sibleExtent()) > 0); 171 if (selectionExpanded)
89 // If the extent-base order was switched, then the selection is now 172 expandedBeyondWordBoundary = arePositionsInSpecifiedOrder(newOffsetE xtentPosition, wordBoundary, newExtentBaseOrder);
90 // expanding in a different direction than before. Therefore we need to 173 else if (extentBaseOrderSwitched)
91 // calculate the boundary of the 'current word' in this new direction in 174 expandedBeyondWordBoundary = arePositionsInSpecifiedOrder(newOffsetE xtentPosition, wordBoundary, newExtentBaseOrder);
leviw_travelin_and_unemployed 2015/05/26 20:25:50 So worst case we have to do 3 calls to comparePosi
mfomitchev 2015/05/26 22:32:47 Right. Typically we will have two tests. We will o
leviw_travelin_and_unemployed 2015/05/26 22:47:49 This function can at most call it three times, rig
mfomitchev 2015/05/27 17:24:29 I didn't word this very well. When I said "current
92 // order to be able to tell if the selection expanded beyond it. 175 else
93 if (extentBaseOrderSwitched) { 176 expandedBeyondWordBoundary = false;
94 currentWordBoundary = nextWordBound( 177
95 base, 178 // The selection is shrunk if the extent changes position to be closer t o
96 extentBaseOrder > 0 ? SearchDirection::SearchForward : SearchDirecti on::SearchBackwards, 179 // the base, and the extent/base order wasn't switched.
97 BoundAdjust::NextBoundIfOnBound); 180 thisMoveShrunkSelection = !extentBaseOrderSwitched && !selectionExpanded ;
98 m_granularity = CharacterGranularity; 181
99 // When the base/extent order switches it doesn't count as shrinking sel ection. 182 if (expandedBeyondWordBoundary)
100 thisMoveShrunkSelection = false; 183 m_granularity = WordGranularity;
184 else if (thisMoveShrunkSelection)
185 m_granularity = CharacterGranularity;
101 } 186 }
102 187
103 bool expandedBeyondWordBoundary; 188 VisiblePosition newSelectionExtent = newOffsetExtentPosition;
104 if (extentBaseOrder > 0) 189 if (m_granularity == WordGranularity) {
105 expandedBeyondWordBoundary = comparePositions(extentPosition, currentWor dBoundary) > 0; 190 // Determine the bounds of the word where the extent is located.
106 else 191 // Set the selection extent to one of the two bounds depending on
107 expandedBeyondWordBoundary = comparePositions(extentPosition, currentWor dBoundary) < 0; 192 // whether the extent is passed the middle of the word.
108 if (expandedBeyondWordBoundary) { 193 VisiblePosition boundBeforeExtent = nextWordBound(newOffsetExtentPositio n, SearchDirection::SearchBackwards, BoundAdjust::CurrentPosIfOnBound);
109 m_granularity = WordGranularity; 194 VisiblePosition boundAfterExtent = nextWordBound(newOffsetExtentPosition , SearchDirection::SearchForward, BoundAdjust::CurrentPosIfOnBound);
110 } else if (thisMoveShrunkSelection) { 195 int xMiddleBetweenBounds = (boundAfterExtent.absoluteCaretBounds().x() + boundBeforeExtent.absoluteCaretBounds().x()) / 2;
111 m_granularity = CharacterGranularity; 196 bool offsetExtentBeforeMiddle = newOffsetExtentPoint.x() < xMiddleBetwee nBounds;
112 m_lastMoveShrunkSelection = true; 197
198 newSelectionExtent = offsetExtentBeforeMiddle ? boundBeforeExtent : boun dAfterExtent;
199 // Update the offset if needed.
200 if (!inSameLine(newSelectionExtent, selection.visibleExtent())) {
201 m_offset = 0;
202 } else if (newSelectionExtent != selection.visibleExtent()
203 && ((newExtentBaseOrder > 0 && !offsetExtentBeforeMiddle) || (newExt entBaseOrder < 0 && offsetExtentBeforeMiddle))) {
204 m_offset = newSelectionExtent.absoluteCaretBounds().x() - extentPoin t.x();
205 }
113 } 206 }
114 207
115 m_lastMoveShrunkSelection = thisMoveShrunkSelection; 208 // Only update the state if the selection actually changed as a result of
209 // this move.
210 if (newSelectionExtent != selection.visibleExtent())
211 m_state = thisMoveShrunkSelection ? StrategyState::Shrinking : StrategyS tate::Expanding;
212
213 m_subPositionCorrection = extentPoint + IntSize(m_offset, 0) - newSelectionE xtent.absoluteCaretBounds().minXMaxYCorner();
leviw_travelin_and_unemployed 2015/05/26 20:25:50 This is super magical and needs some explanation.
mfomitchev 2015/05/26 22:32:47 I like storing the offset as an int since it makes
116 VisibleSelection newSelection = selection; 214 VisibleSelection newSelection = selection;
117 newSelection.setExtent(extentPosition); 215 newSelection.setExtent(newSelectionExtent);
118 if (m_granularity == WordGranularity) {
119 if (extentBaseOrder > 0)
120 newSelection.setEndRespectingGranularity(m_granularity, LeftWordIfOn Boundary);
121 else
122 newSelection.setStartRespectingGranularity(m_granularity, RightWordI fOnBoundary);
123 }
124
125 return newSelection; 216 return newSelection;
126 } 217 }
127 218
128 } // namespace blink 219 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698