| 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_granularity(CharacterGranularity) |
| 34 , m_lastMoveShrunkSelection(false) { } | 39 , m_lastMoveShrunkSelection(false) |
| 40 , m_offset(0) |
| 41 , m_cleared(true) { } |
| 35 | 42 |
| 36 DirectionGranularityStrategy::~DirectionGranularityStrategy() { } | 43 DirectionGranularityStrategy::~DirectionGranularityStrategy() { } |
| 37 | 44 |
| 38 SelectionStrategy DirectionGranularityStrategy::GetType() const | 45 SelectionStrategy DirectionGranularityStrategy::GetType() const |
| 39 { | 46 { |
| 40 return SelectionStrategy::Direction; | 47 return SelectionStrategy::Direction; |
| 41 } | 48 } |
| 42 | 49 |
| 43 void DirectionGranularityStrategy::Clear() | 50 void DirectionGranularityStrategy::Clear() |
| 44 { | 51 { |
| 52 m_cleared = true; |
| 45 m_granularity = CharacterGranularity; | 53 m_granularity = CharacterGranularity; |
| 46 m_lastMoveShrunkSelection = false; | 54 m_lastMoveShrunkSelection = false; |
| 55 m_offset = 0; |
| 56 m_extentPoint.zero(); |
| 47 } | 57 } |
| 48 | 58 |
| 59 bool DirectionGranularityStrategy::arePositionsInOrder( |
| 60 const VisiblePosition& pos1, |
| 61 const VisiblePosition& pos2, |
| 62 int order) |
| 63 { |
| 64 int positionOrder = comparePositions(pos1, pos2); |
| 65 if (order == 0) |
| 66 return positionOrder == 0; |
| 67 return order > 0 ? positionOrder > 0 : positionOrder < 0; |
| 68 } |
| 69 |
| 70 |
| 49 VisiblePosition DirectionGranularityStrategy::nextWordBound( | 71 VisiblePosition DirectionGranularityStrategy::nextWordBound( |
| 50 const VisiblePosition& pos, | 72 const VisiblePosition& pos, |
| 51 SearchDirection direction, | 73 SearchDirection direction, |
| 52 BoundAdjust wordBoundAdjust) | 74 BoundAdjust wordBoundAdjust) |
| 53 { | 75 { |
| 54 bool nextBoundIfOnBound = wordBoundAdjust == BoundAdjust::NextBoundIfOnBoun
d; | 76 bool nextBoundIfOnBound = wordBoundAdjust == BoundAdjust::NextBoundIfOnBoun
d; |
| 55 if (direction == SearchDirection::SearchForward) { | 77 if (direction == SearchDirection::SearchForward) { |
| 56 EWordSide wordSide = nextBoundIfOnBound ? RightWordIfOnBoundary : LeftWo
rdIfOnBoundary; | 78 EWordSide wordSide = nextBoundIfOnBound ? RightWordIfOnBoundary : LeftWo
rdIfOnBoundary; |
| 57 return endOfWord(pos, wordSide); | 79 return endOfWord(pos, wordSide); |
| 58 } | 80 } |
| 59 EWordSide wordSide = nextBoundIfOnBound ? LeftWordIfOnBoundary : RightWordIf
OnBoundary; | 81 EWordSide wordSide = nextBoundIfOnBound ? LeftWordIfOnBoundary : RightWordIf
OnBoundary; |
| 60 return startOfWord(pos, wordSide); | 82 return startOfWord(pos, wordSide); |
| 61 } | 83 } |
| 62 | 84 |
| 63 VisibleSelection DirectionGranularityStrategy::updateExtent(const VisiblePositio
n& extentPosition, const VisibleSelection& selection) | 85 VisibleSelection DirectionGranularityStrategy::updateExtent(const IntPoint& exte
ntPoint, LocalFrame* frame) |
| 64 { | 86 { |
| 65 if (extentPosition == selection.visibleExtent()) | 87 const VisibleSelection& selection = frame->selection().selection(); |
| 88 // Initialize m_extentPoint if this is the first update after a clear. |
| 89 if (m_cleared) { |
| 90 VisiblePosition pos = selection.isBaseFirst() ? selection.visibleEnd()
: selection.visibleStart(); |
| 91 m_extentPoint = IntPoint(pos.absoluteCaretBounds().x(), pos.absoluteCare
tBounds().y()); |
| 92 m_cleared = false; |
| 93 } |
| 94 |
| 95 IntPoint oldOffsetExtentPoint(m_extentPoint.x() + m_offset, m_extentPoint.y(
)); |
| 96 VisiblePosition oldOffsetExtentPosition = visiblePositionForContentsPoint(ol
dOffsetExtentPoint, frame); |
| 97 |
| 98 // Apply the offset. |
| 99 IntPoint offsetExtentPoint = extentPoint; |
| 100 int dx = extentPoint.x() - m_extentPoint.x(); |
| 101 if (m_offset != 0) { |
| 102 if (m_offset > 0 && dx > 0) |
| 103 m_offset = std::max(0, m_offset - dx); |
| 104 else if (m_offset < 0 && dx < 0) |
| 105 m_offset = std::min(0, m_offset - dx); |
| 106 offsetExtentPoint.move(m_offset, 0); |
| 107 } |
| 108 m_extentPoint = extentPoint; |
| 109 |
| 110 VisiblePosition offsetExtentPosition = visiblePositionForContentsPoint(offse
tExtentPoint, frame); |
| 111 |
| 112 if (!inSameLine(offsetExtentPosition, oldOffsetExtentPosition)) { |
| 113 m_offset = 0; |
| 114 offsetExtentPoint = extentPoint; |
| 115 offsetExtentPosition = visiblePositionForContentsPoint(extentPoint, fram
e); |
| 116 } |
| 117 |
| 118 const VisiblePosition base = selection.visibleBase(); |
| 119 int extentBaseOrder = comparePositions(offsetExtentPosition, base); |
| 120 |
| 121 // Do not allow selection and extent to be at the same position |
| 122 if (extentBaseOrder == 0) |
| 66 return selection; | 123 return selection; |
| 67 | 124 |
| 68 const VisiblePosition base = selection.visibleBase(); | 125 int oldExtentBaseOrder = comparePositions(selection.visibleExtent(), base); |
| 69 const VisiblePosition oldExtentWithGranularity = selection.isBaseFirst() ? s
election.visibleEnd() : selection.visibleStart(); | 126 bool extentBaseOrderSwitched = !arePositionsInOrder(offsetExtentPosition, ba
se, oldExtentBaseOrder); |
| 70 | |
| 71 int extentBaseOrder = comparePositions(extentPosition, base); | |
| 72 int oldExtentBaseOrder = comparePositions(oldExtentWithGranularity, base); | |
| 73 | |
| 74 bool extentBaseOrderSwitched = (extentBaseOrder > 0 && oldExtentBaseOrder <
0) | |
| 75 || (extentBaseOrder < 0 && oldExtentBaseOrder > 0); | |
| 76 | 127 |
| 77 // Determine the boundary of the 'current word', i.e. the boundary extending | 128 // Determine the boundary of the 'current word', i.e. the boundary extending |
| 78 // beyond which should change the granularity to WordGranularity. | 129 // beyond which should change the granularity to WordGranularity. |
| 79 // If the last move has shrunk the selection and is now exactly on the word | 130 VisiblePosition currentWordBoundary; |
| 80 // boundary - we need to take the next bound as the bound of the "current | |
| 81 // word". | |
| 82 VisiblePosition currentWordBoundary = nextWordBound( | |
| 83 oldExtentWithGranularity, | |
| 84 oldExtentBaseOrder > 0 ? SearchDirection::SearchForward : SearchDirectio
n::SearchBackwards, | |
| 85 m_lastMoveShrunkSelection ? BoundAdjust::NextBoundIfOnBound : BoundAdjus
t::CurrentPosIfOnBound); | |
| 86 | |
| 87 bool thisMoveShrunkSelection = (extentBaseOrder > 0 && comparePositions(exte
ntPosition, selection.visibleExtent()) < 0) | |
| 88 || (extentBaseOrder < 0 && comparePositions(extentPosition, selection.vi
sibleExtent()) > 0); | |
| 89 // If the extent-base order was switched, then the selection is now | |
| 90 // expanding in a different direction than before. Therefore we need to | |
| 91 // calculate the boundary of the 'current word' in this new direction in | |
| 92 // order to be able to tell if the selection expanded beyond it. | |
| 93 if (extentBaseOrderSwitched) { | 131 if (extentBaseOrderSwitched) { |
| 132 // Special case. |
| 133 // If the extent-base order was switched, then the selection is now |
| 134 // expanding in a different direction than before. Therefore we |
| 135 // calculate the current word boundary in this new direction and |
| 136 // based on the |base| position. |
| 94 currentWordBoundary = nextWordBound( | 137 currentWordBoundary = nextWordBound( |
| 95 base, | 138 base, |
| 96 extentBaseOrder > 0 ? SearchDirection::SearchForward : SearchDirecti
on::SearchBackwards, | 139 extentBaseOrder > 0 ? SearchDirection::SearchForward : SearchDirecti
on::SearchBackwards, |
| 97 BoundAdjust::NextBoundIfOnBound); | 140 BoundAdjust::NextBoundIfOnBound); |
| 98 m_granularity = CharacterGranularity; | 141 m_granularity = CharacterGranularity; |
| 99 // When the base/extent order switches it doesn't count as shrinking sel
ection. | 142 } else { |
| 100 thisMoveShrunkSelection = false; | 143 // Calculate the current word boundary based on |oldExtentWithGranularit
y|. |
| 144 // If the last move has shrunk the selection and is now exactly on the w
ord |
| 145 // boundary - we need to take the next bound as the bound of the current
word. |
| 146 currentWordBoundary = nextWordBound( |
| 147 oldOffsetExtentPosition, |
| 148 selection.isBaseFirst() ? SearchDirection::SearchForward : SearchDir
ection::SearchBackwards, |
| 149 m_lastMoveShrunkSelection ? BoundAdjust::NextBoundIfOnBound : BoundA
djust::CurrentPosIfOnBound); |
| 101 } | 150 } |
| 102 | 151 |
| 103 bool expandedBeyondWordBoundary; | 152 bool expandedBeyondWordBoundary = arePositionsInOrder(offsetExtentPosition,
currentWordBoundary, extentBaseOrder); |
| 104 if (extentBaseOrder > 0) | 153 |
| 105 expandedBeyondWordBoundary = comparePositions(extentPosition, currentWor
dBoundary) > 0; | 154 // The selection is shrunk if the extent changes position to be closer to |
| 106 else | 155 // the base, and the extent/base order wasn't switched. |
| 107 expandedBeyondWordBoundary = comparePositions(extentPosition, currentWor
dBoundary) < 0; | 156 bool thisMoveShrunkSelection = |
| 108 if (expandedBeyondWordBoundary) { | 157 !extentBaseOrderSwitched |
| 158 && oldOffsetExtentPosition != offsetExtentPosition |
| 159 && arePositionsInOrder(oldOffsetExtentPosition, offsetExtentPosition, ex
tentBaseOrder); |
| 160 |
| 161 if (expandedBeyondWordBoundary) |
| 109 m_granularity = WordGranularity; | 162 m_granularity = WordGranularity; |
| 110 } else if (thisMoveShrunkSelection) { | 163 else if (thisMoveShrunkSelection) |
| 111 m_granularity = CharacterGranularity; | 164 m_granularity = CharacterGranularity; |
| 112 m_lastMoveShrunkSelection = true; | 165 |
| 166 VisiblePosition newSelectionExtent = offsetExtentPosition; |
| 167 if (m_granularity == WordGranularity) { |
| 168 // Determine the bounds of the word where the extent is located. |
| 169 // Set the selection extent to one of the two bounds depending on |
| 170 // whether the extent is passed the middle of the word. |
| 171 VisiblePosition boundBeforeExtent = nextWordBound(offsetExtentPosition,
SearchDirection::SearchBackwards, BoundAdjust::CurrentPosIfOnBound); |
| 172 VisiblePosition boundAfterExtent = nextWordBound(offsetExtentPosition, S
earchDirection::SearchForward, BoundAdjust::CurrentPosIfOnBound); |
| 173 int xMiddleBetweenBounds = (boundAfterExtent.absoluteCaretBounds().x() +
boundBeforeExtent.absoluteCaretBounds().x()) / 2; |
| 174 bool extentBeforeMiddle = offsetExtentPoint.x() < xMiddleBetweenBounds; |
| 175 |
| 176 newSelectionExtent = extentBeforeMiddle ? boundBeforeExtent : boundAfter
Extent; |
| 177 // Update the offset if needed. |
| 178 if (!inSameLine(newSelectionExtent, selection.visibleExtent())) |
| 179 m_offset = 0; |
| 180 else if ((extentBaseOrder > 0 && !extentBeforeMiddle) || (extentBaseOrde
r < 0 && extentBeforeMiddle)) |
| 181 m_offset = newSelectionExtent.absoluteCaretBounds().x() - extentPoin
t.x(); |
| 113 } | 182 } |
| 114 | 183 |
| 115 m_lastMoveShrunkSelection = thisMoveShrunkSelection; | 184 // Only update m_lastMoveShrunkSelection if the selection actually changed |
| 185 // as a result of this move. |
| 186 if (newSelectionExtent != selection.visibleExtent()) |
| 187 m_lastMoveShrunkSelection = thisMoveShrunkSelection; |
| 188 |
| 116 VisibleSelection newSelection = selection; | 189 VisibleSelection newSelection = selection; |
| 117 newSelection.setExtent(extentPosition); | 190 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; | 191 return newSelection; |
| 126 } | 192 } |
| 127 | 193 |
| 128 } // namespace blink | 194 } // namespace blink |
| OLD | NEW |