Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 |
| OLD | NEW |