VisibleSelection CharacterGranularityStrategy::updateExtent(const IntPoint& extentPoint, LocalFrame* frame) { const VisiblePosition& extentPosition = visiblePositionForContentsPoint(extentPoint, frame); const VisibleSelection& selection = frame->selection().selection(); if (selection.visibleBase().deepEquivalent() == extentPosition.deepEquivalent()) return selection; return VisibleSelection(selection.visibleBase(), extentPosition); }
VisibleSelection DirectionGranularityStrategy::updateExtent( const IntPoint& extentPoint, LocalFrame* frame) { const VisibleSelection& selection = frame->selection().selection(); if (m_state == StrategyState::Cleared) m_state = StrategyState::Expanding; VisiblePosition oldOffsetExtentPosition = selection.visibleExtent(); IntPoint oldExtentLocation = positionLocation(oldOffsetExtentPosition); IntPoint oldOffsetExtentPoint = oldExtentLocation + m_diffExtentPointFromExtentPosition; IntPoint oldExtentPoint = IntPoint(oldOffsetExtentPoint.x() - m_offset, oldOffsetExtentPoint.y()); // Apply the offset. IntPoint newOffsetExtentPoint = extentPoint; int dx = extentPoint.x() - oldExtentPoint.x(); if (m_offset != 0) { if (m_offset > 0 && dx > 0) m_offset = std::max(0, m_offset - dx); else if (m_offset < 0 && dx < 0) m_offset = std::min(0, m_offset - dx); newOffsetExtentPoint.move(m_offset, 0); } VisiblePosition newOffsetExtentPosition = visiblePositionForContentsPoint(newOffsetExtentPoint, frame); IntPoint newOffsetLocation = positionLocation(newOffsetExtentPosition); // Reset the offset in case of a vertical change in the location (could be // due to a line change or due to an unusual layout, e.g. rotated text). bool verticalChange = newOffsetLocation.y() != oldExtentLocation.y(); if (verticalChange) { m_offset = 0; m_granularity = CharacterGranularity; newOffsetExtentPoint = extentPoint; newOffsetExtentPosition = visiblePositionForContentsPoint(extentPoint, frame); } const VisiblePosition base = selection.visibleBase(); // Do not allow empty selection. if (newOffsetExtentPosition.deepEquivalent() == base.deepEquivalent()) return selection; // The direction granularity strategy, particularly the "offset" feature // doesn't work with non-horizontal text (e.g. when the text is rotated). // So revert to the behavior equivalent to the character granularity // strategy if we detect that the text's baseline coordinate changed // without a line change. if (verticalChange && inSameLine(newOffsetExtentPosition, oldOffsetExtentPosition)) { return createVisibleSelection(selection.visibleBase(), newOffsetExtentPosition); } int oldExtentBaseOrder = selection.isBaseFirst() ? 1 : -1; int newExtentBaseOrder; bool thisMoveShrunkSelection; if (newOffsetExtentPosition.deepEquivalent() == oldOffsetExtentPosition.deepEquivalent()) { if (m_granularity == CharacterGranularity) return selection; // If we are in Word granularity, we cannot exit here, since we may pass // the middle of the word without changing the position (in which case // the selection needs to expand). thisMoveShrunkSelection = false; newExtentBaseOrder = oldExtentBaseOrder; } else { bool selectionExpanded = arePositionsInSpecifiedOrder( newOffsetExtentPosition, oldOffsetExtentPosition, oldExtentBaseOrder); bool extentBaseOrderSwitched = selectionExpanded ? false : !arePositionsInSpecifiedOrder(newOffsetExtentPosition, base, oldExtentBaseOrder); newExtentBaseOrder = extentBaseOrderSwitched ? -oldExtentBaseOrder : oldExtentBaseOrder; // Determine the word boundary, i.e. the boundary extending beyond which // should change the granularity to WordGranularity. VisiblePosition wordBoundary; if (extentBaseOrderSwitched) { // Special case. // If the extent-base order was switched, then the selection is now // expanding in a different direction than before. Therefore we // calculate the word boundary in this new direction and based on // the |base| position. wordBoundary = nextWordBound(base, newExtentBaseOrder > 0 ? SearchDirection::SearchForward : SearchDirection::SearchBackwards, BoundAdjust::NextBoundIfOnBound); m_granularity = CharacterGranularity; } else { // Calculate the word boundary based on |oldExtentWithGranularity|. // If selection was shrunk in the last update and the extent is now // exactly on the word boundary - we need to take the next bound as // the bound of the current word. wordBoundary = nextWordBound(oldOffsetExtentPosition, oldExtentBaseOrder > 0 ? SearchDirection::SearchForward : SearchDirection::SearchBackwards, m_state == StrategyState::Shrinking ? BoundAdjust::NextBoundIfOnBound : BoundAdjust::CurrentPosIfOnBound); } bool expandedBeyondWordBoundary; if (selectionExpanded) expandedBeyondWordBoundary = arePositionsInSpecifiedOrder( newOffsetExtentPosition, wordBoundary, newExtentBaseOrder); else if (extentBaseOrderSwitched) expandedBeyondWordBoundary = arePositionsInSpecifiedOrder( newOffsetExtentPosition, wordBoundary, newExtentBaseOrder); else expandedBeyondWordBoundary = false; // The selection is shrunk if the extent changes position to be closer to // the base, and the extent/base order wasn't switched. thisMoveShrunkSelection = !extentBaseOrderSwitched && !selectionExpanded; if (expandedBeyondWordBoundary) m_granularity = WordGranularity; else if (thisMoveShrunkSelection) m_granularity = CharacterGranularity; } VisiblePosition newSelectionExtent = newOffsetExtentPosition; if (m_granularity == WordGranularity) { // Determine the bounds of the word where the extent is located. // Set the selection extent to one of the two bounds depending on // whether the extent is passed the middle of the word. VisiblePosition boundBeforeExtent = nextWordBound(newOffsetExtentPosition, SearchDirection::SearchBackwards, BoundAdjust::CurrentPosIfOnBound); VisiblePosition boundAfterExtent = nextWordBound(newOffsetExtentPosition, SearchDirection::SearchForward, BoundAdjust::CurrentPosIfOnBound); int xMiddleBetweenBounds = (positionLocation(boundAfterExtent).x() + positionLocation(boundBeforeExtent).x()) / 2; bool offsetExtentBeforeMiddle = newOffsetExtentPoint.x() < xMiddleBetweenBounds; newSelectionExtent = offsetExtentBeforeMiddle ? boundBeforeExtent : boundAfterExtent; // Update the offset if selection expanded in word granularity. if (newSelectionExtent.deepEquivalent() != selection.visibleExtent().deepEquivalent() && ((newExtentBaseOrder > 0 && !offsetExtentBeforeMiddle) || (newExtentBaseOrder < 0 && offsetExtentBeforeMiddle))) { m_offset = positionLocation(newSelectionExtent).x() - extentPoint.x(); } } // Only update the state if the selection actually changed as a result of // this move. if (newSelectionExtent.deepEquivalent() != selection.visibleExtent().deepEquivalent()) m_state = thisMoveShrunkSelection ? StrategyState::Shrinking : StrategyState::Expanding; m_diffExtentPointFromExtentPosition = extentPoint + IntSize(m_offset, 0) - positionLocation(newSelectionExtent); VisibleSelection newSelection = selection; newSelection.setExtent(newSelectionExtent); return newSelection; }