зеркало из https://github.com/mozilla/gecko-dev.git
Bug 655877 - Part 41b: Make SVG text selectable with the mouse. r=roc,jwatt
This commit is contained in:
Родитель
e8ca525d3a
Коммит
56be0350f9
|
@ -1312,15 +1312,14 @@ nsLayoutUtils::GetEventCoordinatesRelativeTo(nsIWidget* aWidget,
|
|||
/* If we encountered a transform, we can't do simple arithmetic to figure
|
||||
* out how to convert back to aFrame's coordinates and must use the CTM.
|
||||
*/
|
||||
if (transformFound) {
|
||||
if (transformFound || aFrame->IsSVGText()) {
|
||||
return TransformRootPointToFrame(aFrame, widgetToView);
|
||||
}
|
||||
|
||||
/* Otherwise, all coordinate systems are translations of one another,
|
||||
* so we can just subtract out the different.
|
||||
* so we can just subtract out the difference.
|
||||
*/
|
||||
nsPoint offset = aFrame->GetOffsetToCrossDoc(rootFrame);
|
||||
return widgetToView - offset;
|
||||
return widgetToView - aFrame->GetOffsetToCrossDoc(rootFrame);
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
|
@ -1538,15 +1537,35 @@ TransformGfxRectToAncestor(nsIFrame *aFrame,
|
|||
return ctm.TransformBounds(aRect);
|
||||
}
|
||||
|
||||
nsPoint
|
||||
nsLayoutUtils::TransformRootPointToFrame(nsIFrame *aFrame,
|
||||
const nsPoint &aPoint)
|
||||
static nsSVGTextFrame2*
|
||||
GetContainingSVGTextFrame(nsIFrame* aFrame)
|
||||
{
|
||||
if (!aFrame->IsSVGText()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return static_cast<nsSVGTextFrame2*>
|
||||
(nsLayoutUtils::GetClosestFrameOfType(aFrame->GetParent(),
|
||||
nsGkAtoms::svgTextFrame2));
|
||||
}
|
||||
|
||||
nsPoint
|
||||
nsLayoutUtils::TransformAncestorPointToFrame(nsIFrame* aFrame,
|
||||
const nsPoint& aPoint,
|
||||
nsIFrame* aAncestor)
|
||||
{
|
||||
nsSVGTextFrame2* text = GetContainingSVGTextFrame(aFrame);
|
||||
|
||||
float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor),
|
||||
NSAppUnitsToFloatPixels(aPoint.y, factor));
|
||||
|
||||
result = TransformGfxPointFromAncestor(aFrame, result, nullptr);
|
||||
if (text) {
|
||||
result = TransformGfxPointFromAncestor(text, result, aAncestor);
|
||||
result = text->TransformFramePointToTextChild(result, aFrame);
|
||||
} else {
|
||||
result = TransformGfxPointFromAncestor(aFrame, result, nullptr);
|
||||
}
|
||||
|
||||
return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
|
||||
NSFloatPixelsToAppUnits(float(result.y), factor));
|
||||
|
@ -1557,13 +1576,20 @@ nsLayoutUtils::TransformAncestorRectToFrame(nsIFrame* aFrame,
|
|||
const nsRect &aRect,
|
||||
const nsIFrame* aAncestor)
|
||||
{
|
||||
nsSVGTextFrame2* text = GetContainingSVGTextFrame(aFrame);
|
||||
|
||||
float srcAppUnitsPerDevPixel = aAncestor->PresContext()->AppUnitsPerDevPixel();
|
||||
gfxRect result(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel),
|
||||
NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
|
||||
NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
|
||||
NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
|
||||
|
||||
result = TransformGfxRectFromAncestor(aFrame, result, aAncestor);
|
||||
if (text) {
|
||||
result = TransformGfxRectFromAncestor(text, result, aAncestor);
|
||||
result = text->TransformFrameRectToTextChild(result, aFrame);
|
||||
} else {
|
||||
result = TransformGfxRectFromAncestor(aFrame, result, aAncestor);
|
||||
}
|
||||
|
||||
float destAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
return nsRect(NSFloatPixelsToAppUnits(float(result.x), destAppUnitsPerDevPixel),
|
||||
|
@ -1577,13 +1603,21 @@ nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
|
|||
const nsRect& aRect,
|
||||
const nsIFrame* aAncestor)
|
||||
{
|
||||
float srcAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
gfxRect result(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel),
|
||||
NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
|
||||
NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
|
||||
NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
|
||||
nsSVGTextFrame2* text = GetContainingSVGTextFrame(aFrame);
|
||||
|
||||
result = TransformGfxRectToAncestor(aFrame, result, aAncestor);
|
||||
float srcAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
gfxRect result;
|
||||
|
||||
if (text) {
|
||||
result = text->TransformFrameRectFromTextChild(aRect, aFrame);
|
||||
result = TransformGfxRectToAncestor(text, result, aAncestor);
|
||||
} else {
|
||||
result = gfxRect(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel),
|
||||
NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
|
||||
NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
|
||||
NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
|
||||
result = TransformGfxRectToAncestor(aFrame, result, aAncestor);
|
||||
}
|
||||
|
||||
float destAppUnitsPerDevPixel = aAncestor->PresContext()->AppUnitsPerDevPixel();
|
||||
return nsRect(NSFloatPixelsToAppUnits(float(result.x), destAppUnitsPerDevPixel),
|
||||
|
|
|
@ -473,7 +473,7 @@ public:
|
|||
static nsIFrame* GetPopupFrameForEventCoordinates(nsPresContext* aPresContext,
|
||||
const nsEvent* aEvent);
|
||||
|
||||
/**
|
||||
/**
|
||||
* Translate from widget coordinates to the view's coordinates
|
||||
* @param aPresContext the PresContext for the view
|
||||
* @param aWidget the widget
|
||||
|
@ -588,7 +588,18 @@ public:
|
|||
* @return aPoint, expressed in aFrame's canonical coordinate space.
|
||||
*/
|
||||
static nsPoint TransformRootPointToFrame(nsIFrame* aFrame,
|
||||
const nsPoint &aPt);
|
||||
const nsPoint &aPoint)
|
||||
{
|
||||
return TransformAncestorPointToFrame(aFrame, aPoint, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform aPoint relative to aAncestor down to the coordinate system of
|
||||
* aFrame.
|
||||
*/
|
||||
static nsPoint TransformAncestorPointToFrame(nsIFrame* aFrame,
|
||||
const nsPoint& aPoint,
|
||||
nsIFrame* aAncestor);
|
||||
|
||||
/**
|
||||
* Helper function that, given a rectangle and a matrix, returns the smallest
|
||||
|
|
|
@ -335,6 +335,18 @@ nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsIFrame::FindCloserFrameForSelection(
|
||||
nsPoint aPoint,
|
||||
nsIFrame::FrameWithDistance* aCurrentBestFrame)
|
||||
{
|
||||
if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
|
||||
aCurrentBestFrame->mXDistance,
|
||||
aCurrentBestFrame->mYDistance)) {
|
||||
aCurrentBestFrame->mFrame = this;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ApplyOverflowClipping(nsDisplayListBuilder* aBuilder,
|
||||
const nsIFrame* aFrame,
|
||||
const nsStyleDisplay* aDisp,
|
||||
|
@ -3522,24 +3534,18 @@ static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint,
|
|||
|
||||
if (kid) {
|
||||
// Go through all the child frames to find the closest one
|
||||
|
||||
// Large number to force the comparison to succeed
|
||||
const nscoord HUGE_DISTANCE = nscoord_MAX;
|
||||
nscoord closestXDistance = HUGE_DISTANCE;
|
||||
nscoord closestYDistance = HUGE_DISTANCE;
|
||||
nsIFrame *closestFrame = nullptr;
|
||||
|
||||
nsIFrame::FrameWithDistance closest = { nullptr, nscoord_MAX, nscoord_MAX };
|
||||
for (; kid; kid = kid->GetNextSibling()) {
|
||||
if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty())
|
||||
continue;
|
||||
|
||||
if (nsLayoutUtils::PointIsCloserToRect(aPoint, kid->GetRect(),
|
||||
closestXDistance,
|
||||
closestYDistance))
|
||||
closestFrame = kid;
|
||||
kid->FindCloserFrameForSelection(aPoint, &closest);
|
||||
}
|
||||
if (closest.mFrame) {
|
||||
if (closest.mFrame->IsSVGText())
|
||||
return FrameTarget(closest.mFrame, false, false);
|
||||
return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags);
|
||||
}
|
||||
if (closestFrame)
|
||||
return GetSelectionClosestFrameForChild(closestFrame, aPoint, aFlags);
|
||||
}
|
||||
return FrameTarget(aFrame, false, false);
|
||||
}
|
||||
|
@ -3616,8 +3622,8 @@ nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint,
|
|||
// should lead to the whole frame being selected
|
||||
if (adjustedFrame && adjustedFrame->GetStyleUIReset()->mUserSelect ==
|
||||
NS_STYLE_USER_SELECT_ALL) {
|
||||
return OffsetsForSingleFrame(adjustedFrame, aPoint +
|
||||
this->GetOffsetTo(adjustedFrame));
|
||||
nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
|
||||
return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
|
||||
}
|
||||
|
||||
// For other cases, try to find a closest frame starting from the parent of
|
||||
|
@ -3656,7 +3662,18 @@ nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint,
|
|||
offsets.associateWithNext = (offsets.offset == range.start);
|
||||
return offsets;
|
||||
}
|
||||
nsPoint pt = aPoint - closest.frame->GetOffsetTo(this);
|
||||
|
||||
nsPoint pt;
|
||||
if (closest.frame != this) {
|
||||
if (closest.frame->IsSVGText()) {
|
||||
pt = nsLayoutUtils::TransformAncestorPointToFrame(closest.frame,
|
||||
aPoint, this);
|
||||
} else {
|
||||
pt = aPoint - closest.frame->GetOffsetTo(this);
|
||||
}
|
||||
} else {
|
||||
pt = aPoint;
|
||||
}
|
||||
return static_cast<nsFrame*>(closest.frame)->CalcContentOffsetsFromFramePoint(pt);
|
||||
|
||||
// XXX should I add some kind of offset standardization?
|
||||
|
|
|
@ -2900,6 +2900,36 @@ NS_PTR_TO_INT32(frame->Properties().Get(nsIFrame::ParagraphDepthProperty()))
|
|||
};
|
||||
bool IsVisibleConsideringAncestors(uint32_t aFlags = 0) const;
|
||||
|
||||
struct FrameWithDistance
|
||||
{
|
||||
nsIFrame* mFrame;
|
||||
nscoord mXDistance;
|
||||
nscoord mYDistance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds a frame that is closer to a specified point than a current
|
||||
* distance. Distance is measured as for text selection -- a closer x
|
||||
* distance beats a closer y distance.
|
||||
*
|
||||
* Normally, this function will only check the distance between this
|
||||
* frame's rectangle and the specified point. nsSVGTextFrame2 overrides
|
||||
* this so that it can manage all of its descendant frames and take
|
||||
* into account any SVG text layout.
|
||||
*
|
||||
* If aPoint is closer to this frame's rectangle than aCurrentBestFrame
|
||||
* indicates, then aCurrentBestFrame is updated with the distance between
|
||||
* aPoint and this frame's rectangle, and with a pointer to this frame.
|
||||
* If aPoint is not closer, then aCurrentBestFrame is left unchanged.
|
||||
*
|
||||
* @param aPoint The point to check for its distance to this frame.
|
||||
* @param aCurrentBestFrame Pointer to a struct that will be updated with
|
||||
* a pointer to this frame and its distance to aPoint, if this frame
|
||||
* is indeed closer than the current distance in aCurrentBestFrame.
|
||||
*/
|
||||
virtual void FindCloserFrameForSelection(nsPoint aPoint,
|
||||
FrameWithDistance* aCurrentBestFrame);
|
||||
|
||||
inline bool IsBlockInside() const;
|
||||
inline bool IsBlockOutside() const;
|
||||
inline bool IsInlineOutside() const;
|
||||
|
|
|
@ -674,7 +674,7 @@ protected:
|
|||
bool CombineSelectionUnderlineRect(nsPresContext* aPresContext,
|
||||
nsRect& aRect);
|
||||
|
||||
ContentOffsets GetCharacterOffsetAtFramePointInternal(const nsPoint &aPoint,
|
||||
ContentOffsets GetCharacterOffsetAtFramePointInternal(nsPoint aPoint,
|
||||
bool aForInsertionPoint);
|
||||
|
||||
void ClearFrameOffsetCache();
|
||||
|
|
|
@ -6207,7 +6207,7 @@ nsTextFrame::GetCharacterOffsetAtFramePoint(const nsPoint &aPoint)
|
|||
}
|
||||
|
||||
nsIFrame::ContentOffsets
|
||||
nsTextFrame::GetCharacterOffsetAtFramePointInternal(const nsPoint &aPoint,
|
||||
nsTextFrame::GetCharacterOffsetAtFramePointInternal(nsPoint aPoint,
|
||||
bool aForInsertionPoint)
|
||||
{
|
||||
ContentOffsets offsets;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "gfxSkipChars.h"
|
||||
#include "gfxTypes.h"
|
||||
#include "LookAndFeel.h"
|
||||
#include "nsAlgorithm.h"
|
||||
#include "nsBlockFrame.h"
|
||||
#include "nsCaret.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
@ -124,6 +125,18 @@ ScaleAround(gfxRect& aRect, const gfxPoint& aPoint, double aScale)
|
|||
aRect.height *= aScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a gfxPoint lies within a gfxRect.
|
||||
*/
|
||||
static bool
|
||||
Inside(const gfxRect& aRect, const gfxPoint& aPoint)
|
||||
{
|
||||
return aPoint.x >= aRect.x &&
|
||||
aPoint.x < aRect.XMost() &&
|
||||
aPoint.y >= aRect.y &&
|
||||
aPoint.y < aRect.YMost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the measured ascent and descent of the text in the given nsTextFrame
|
||||
* in app units.
|
||||
|
@ -2731,8 +2744,10 @@ nsDisplaySVGText::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
|||
// ToReferenceFrame() includes frame->GetPosition(), our user space position.
|
||||
nsPoint userSpacePt = pointRelativeToReferenceFrame -
|
||||
(ToReferenceFrame() - frame->GetPosition());
|
||||
if (frame->GetFrameForPoint(userSpacePt)) {
|
||||
aOutFrames->AppendElement(frame);
|
||||
|
||||
nsIFrame* target = frame->GetFrameForPoint(userSpacePt);
|
||||
if (target) {
|
||||
aOutFrames->AppendElement(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2938,6 +2953,35 @@ nsSVGTextFrame2::InvalidateInternal(const nsRect& aDamageRect,
|
|||
(this, nsSVGUtils::OuterSVGIsCallingReflowSVG(this), nullptr, aFlags);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGTextFrame2::FindCloserFrameForSelection(
|
||||
nsPoint aPoint,
|
||||
nsIFrame::FrameWithDistance* aCurrentBestFrame)
|
||||
{
|
||||
UpdateGlyphPositioning(true);
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
|
||||
// Find the frame that has the closest rendered run rect to aPoint.
|
||||
TextRenderedRunIterator it(this);
|
||||
for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
|
||||
uint32_t flags = TextRenderedRun::eIncludeFill |
|
||||
TextRenderedRun::eIncludeStroke |
|
||||
TextRenderedRun::eNoHorizontalOverflow;
|
||||
gfxRect userRect = run.GetUserSpaceRect(presContext, flags);
|
||||
|
||||
nsRect rect = nsSVGUtils::ToCanvasBounds(userRect,
|
||||
GetCanvasTM(FOR_HIT_TESTING),
|
||||
presContext);
|
||||
|
||||
if (nsLayoutUtils::PointIsCloserToRect(aPoint, rect,
|
||||
aCurrentBestFrame->mXDistance,
|
||||
aCurrentBestFrame->mYDistance)) {
|
||||
aCurrentBestFrame->mFrame = run.mFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsISVGChildFrame methods
|
||||
|
||||
|
@ -3138,6 +3182,52 @@ nsSVGTextFrame2::PaintSVG(nsRenderingContext* aContext,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(nsIFrame*)
|
||||
nsSVGTextFrame2::GetFrameForPoint(const nsPoint& aPoint)
|
||||
{
|
||||
NS_ASSERTION(GetFirstPrincipalChild(), "must have a child frame");
|
||||
|
||||
AutoCanvasTMForMarker autoCanvasTMFor(this, FOR_HIT_TESTING);
|
||||
|
||||
if (mState & NS_STATE_SVG_NONDISPLAY_CHILD) {
|
||||
// Text frames inside <clipPath> will never have had ReflowSVG called on
|
||||
// them, so call UpdateGlyphPositioning to do this now. (Text frames
|
||||
// inside <mask> and other non-display containers will never need to
|
||||
// be hit tested.)
|
||||
UpdateGlyphPositioning(true);
|
||||
} else {
|
||||
NS_ASSERTION(!NS_SUBTREE_DIRTY(this), "reflow should have happened");
|
||||
}
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
|
||||
gfxPoint pointInOuterSVGUserUnits = AppUnitsToGfxUnits(aPoint, presContext);
|
||||
|
||||
TextRenderedRunIterator it(this);
|
||||
nsIFrame* hit = nullptr;
|
||||
for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
|
||||
uint16_t hitTestFlags = nsSVGUtils::GetGeometryHitTestFlags(run.mFrame);
|
||||
if (!(hitTestFlags & (SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
gfxMatrix m = GetCanvasTM(FOR_HIT_TESTING);
|
||||
m.PreMultiply(run.GetTransformFromRunUserSpaceToUserSpace(presContext));
|
||||
m.Invert();
|
||||
|
||||
gfxPoint pointInRunUserSpace = m.Transform(pointInOuterSVGUserUnits);
|
||||
gfxRect frameRect =
|
||||
run.GetRunUserSpaceRect(presContext, TextRenderedRun::eIncludeFill |
|
||||
TextRenderedRun::eIncludeStroke);
|
||||
|
||||
if (Inside(frameRect, pointInRunUserSpace) &&
|
||||
nsSVGUtils::HitTestClip(this, aPoint)) {
|
||||
hit = run.mFrame;
|
||||
}
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(nsRect)
|
||||
nsSVGTextFrame2::GetCoveredRegion()
|
||||
{
|
||||
|
@ -4589,3 +4679,213 @@ nsSVGTextFrame2::GetFontSizeScaleFactor() const
|
|||
{
|
||||
return mFontSizeScaleFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take aPoint, which is in the <text> element's user space, and convert
|
||||
* it to the appropriate frame user space of aChildFrame according to
|
||||
* which rendered run the point hits.
|
||||
*/
|
||||
gfxPoint
|
||||
nsSVGTextFrame2::TransformFramePointToTextChild(const gfxPoint& aPoint,
|
||||
nsIFrame* aChildFrame)
|
||||
{
|
||||
NS_ASSERTION(aChildFrame &&
|
||||
nsLayoutUtils::GetClosestFrameOfType
|
||||
(aChildFrame->GetParent(), nsGkAtoms::svgTextFrame2) == this,
|
||||
"aChildFrame must be a descendant of this frame");
|
||||
|
||||
UpdateGlyphPositioning(true);
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
|
||||
// Add in the mRect offset to aPoint, as that will have been taken into
|
||||
// account when transforming the point from the ancestor frame down
|
||||
// to this one.
|
||||
float cssPxPerDevPx = presContext->
|
||||
AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
|
||||
float factor = presContext->AppUnitsPerCSSPixel();
|
||||
gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
|
||||
NSAppUnitsToFloatPixels(mRect.y, factor));
|
||||
gfxPoint pointInUserSpace = aPoint * cssPxPerDevPx + framePosition;
|
||||
|
||||
// Find the closest rendered run for the text frames beneath aChildFrame.
|
||||
TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
|
||||
aChildFrame);
|
||||
TextRenderedRun hit;
|
||||
gfxPoint pointInRun;
|
||||
nscoord dx = nscoord_MAX;
|
||||
nscoord dy = nscoord_MAX;
|
||||
for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
|
||||
uint32_t flags = TextRenderedRun::eIncludeFill |
|
||||
TextRenderedRun::eIncludeStroke |
|
||||
TextRenderedRun::eNoHorizontalOverflow;
|
||||
gfxRect runRect = run.GetRunUserSpaceRect(presContext, flags);
|
||||
|
||||
gfxPoint pointInRunUserSpace =
|
||||
run.GetTransformFromRunUserSpaceToUserSpace(presContext).Invert().
|
||||
Transform(pointInUserSpace);
|
||||
|
||||
if (Inside(runRect, pointInRunUserSpace)) {
|
||||
// The point was inside the rendered run's rect, so we choose it.
|
||||
dx = 0;
|
||||
dy = 0;
|
||||
pointInRun = pointInRunUserSpace;
|
||||
hit = run;
|
||||
} else if (nsLayoutUtils::PointIsCloserToRect(pointInRunUserSpace,
|
||||
runRect, dx, dy)) {
|
||||
// The point was closer to this rendered run's rect than any others
|
||||
// we've seen so far.
|
||||
pointInRun.x = clamped(pointInRunUserSpace.x,
|
||||
runRect.X(), runRect.XMost());
|
||||
pointInRun.y = clamped(pointInRunUserSpace.y,
|
||||
runRect.Y(), runRect.YMost());
|
||||
hit = run;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hit.mFrame) {
|
||||
// We didn't find any rendered runs for the frame.
|
||||
return aPoint;
|
||||
}
|
||||
|
||||
// Return the point in user units relative to the nsTextFrame,
|
||||
// but taking into account mFontSizeScaleFactor.
|
||||
gfxMatrix m = hit.GetTransformFromRunUserSpaceToFrameUserSpace(presContext);
|
||||
m.Scale(mFontSizeScaleFactor, mFontSizeScaleFactor);
|
||||
return m.Transform(pointInRun) / cssPxPerDevPx;
|
||||
}
|
||||
|
||||
/**
|
||||
* For each rendered run for frames beneath aChildFrame, convert aRect
|
||||
* into the run's frame user space and intersect it with the run's
|
||||
* frame user space rectangle. For each of these intersections,
|
||||
* then translate them up into aChildFrame's coordinate space
|
||||
* and union them all together.
|
||||
*/
|
||||
gfxRect
|
||||
nsSVGTextFrame2::TransformFrameRectToTextChild(const gfxRect& aRect,
|
||||
nsIFrame* aChildFrame)
|
||||
{
|
||||
NS_ASSERTION(aChildFrame &&
|
||||
nsLayoutUtils::GetClosestFrameOfType
|
||||
(aChildFrame->GetParent(), nsGkAtoms::svgTextFrame2) == this,
|
||||
"aChildFrame must be a descendant of this frame");
|
||||
|
||||
UpdateGlyphPositioning(true);
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
|
||||
// Add in the mRect offset to aRect, as that will have been taken into
|
||||
// account when transforming the rect from the ancestor frame down
|
||||
// to this one.
|
||||
float cssPxPerDevPx = presContext->
|
||||
AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
|
||||
float factor = presContext->AppUnitsPerCSSPixel();
|
||||
gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
|
||||
NSAppUnitsToFloatPixels(mRect.y, factor));
|
||||
gfxRect incomingRectInUserSpace(aRect.x * cssPxPerDevPx + framePosition.x,
|
||||
aRect.y * cssPxPerDevPx + framePosition.y,
|
||||
aRect.width * cssPxPerDevPx,
|
||||
aRect.height * cssPxPerDevPx);
|
||||
|
||||
// Find each rendered run for text frames beneath aChildFrame.
|
||||
gfxRect result;
|
||||
TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
|
||||
aChildFrame);
|
||||
for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
|
||||
// Convert the incoming rect into frame user space.
|
||||
gfxMatrix m;
|
||||
m.PreMultiply(run.GetTransformFromRunUserSpaceToUserSpace(presContext).Invert());
|
||||
m.PreMultiply(run.GetTransformFromRunUserSpaceToFrameUserSpace(presContext));
|
||||
gfxRect incomingRectInFrameUserSpace =
|
||||
m.TransformBounds(incomingRectInUserSpace);
|
||||
|
||||
// Intersect it with this run's rectangle.
|
||||
uint32_t flags = TextRenderedRun::eIncludeFill |
|
||||
TextRenderedRun::eIncludeStroke;
|
||||
gfxRect runRectInFrameUserSpace = run.GetFrameUserSpaceRect(presContext, flags);
|
||||
gfxRect runIntersectionInFrameUserSpace =
|
||||
incomingRectInFrameUserSpace.Intersect(runRectInFrameUserSpace);
|
||||
|
||||
if (!runIntersectionInFrameUserSpace.IsEmpty()) {
|
||||
// Take the font size scale into account.
|
||||
runIntersectionInFrameUserSpace.x *= mFontSizeScaleFactor;
|
||||
runIntersectionInFrameUserSpace.y *= mFontSizeScaleFactor;
|
||||
runIntersectionInFrameUserSpace.width *= mFontSizeScaleFactor;
|
||||
runIntersectionInFrameUserSpace.height *= mFontSizeScaleFactor;
|
||||
|
||||
// Convert it into the coordinate space of aChildFrame.
|
||||
nsPoint offset = run.mFrame->GetOffsetTo(aChildFrame);
|
||||
gfxRect runIntersection =
|
||||
runIntersectionInFrameUserSpace +
|
||||
gfxPoint(NSAppUnitsToFloatPixels(offset.x, factor),
|
||||
NSAppUnitsToFloatPixels(offset.y, factor));
|
||||
|
||||
// Union it into the result.
|
||||
result.UnionRect(result, runIntersection);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* For each rendered run beneath aChildFrame, translate aRect from
|
||||
* aChildFrame to the run's text frame, transform it then into
|
||||
* the run's frame user space, intersect it with the run's
|
||||
* frame user space rect, then transform it up to user space.
|
||||
* The result is the union of all of these.
|
||||
*/
|
||||
gfxRect
|
||||
nsSVGTextFrame2::TransformFrameRectFromTextChild(const nsRect& aRect,
|
||||
nsIFrame* aChildFrame)
|
||||
{
|
||||
NS_ASSERTION(aChildFrame &&
|
||||
nsLayoutUtils::GetClosestFrameOfType
|
||||
(aChildFrame->GetParent(), nsGkAtoms::svgTextFrame2) == this,
|
||||
"aChildFrame must be a descendant of this frame");
|
||||
|
||||
UpdateGlyphPositioning(true);
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
|
||||
gfxRect result;
|
||||
TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames,
|
||||
aChildFrame);
|
||||
for (TextRenderedRun run = it.Current(); run.mFrame; run = it.Next()) {
|
||||
// First, translate aRect from aChildFrame to this run's frame.
|
||||
nsRect rectInTextFrame = aRect + aChildFrame->GetOffsetTo(run.mFrame);
|
||||
|
||||
// Scale it into frame user space.
|
||||
gfxRect rectInFrameUserSpace =
|
||||
AppUnitsToFloatCSSPixels(gfxRect(rectInTextFrame.x,
|
||||
rectInTextFrame.y,
|
||||
rectInTextFrame.width,
|
||||
rectInTextFrame.height), presContext);
|
||||
|
||||
// Intersect it with the run.
|
||||
uint32_t flags = TextRenderedRun::eIncludeFill |
|
||||
TextRenderedRun::eIncludeStroke;
|
||||
rectInFrameUserSpace.IntersectRect
|
||||
(rectInFrameUserSpace, run.GetFrameUserSpaceRect(presContext, flags));
|
||||
|
||||
if (!rectInFrameUserSpace.IsEmpty()) {
|
||||
// Transform it up to user space of the <text>, also taking into
|
||||
// account the font size scale.
|
||||
gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext);
|
||||
m.Scale(mFontSizeScaleFactor, mFontSizeScaleFactor);
|
||||
gfxRect rectInUserSpace = m.Transform(rectInFrameUserSpace);
|
||||
|
||||
// Union it into the result.
|
||||
result.UnionRect(result, rectInUserSpace);
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract the mRect offset from the result, as our user space for
|
||||
// this frame is relative to the top-left of mRect.
|
||||
float factor = presContext->AppUnitsPerCSSPixel();
|
||||
gfxPoint framePosition(NSAppUnitsToFloatPixels(mRect.x, factor),
|
||||
NSAppUnitsToFloatPixels(mRect.y, factor));
|
||||
|
||||
return result - framePosition;
|
||||
}
|
||||
|
|
|
@ -215,10 +215,18 @@ public:
|
|||
nscoord aX, nscoord aY, nsIFrame* aForChild,
|
||||
uint32_t aFlags);
|
||||
|
||||
/**
|
||||
* Finds the nsTextFrame for the closest rendered run to the specified point.
|
||||
*/
|
||||
virtual void FindCloserFrameForSelection(nsPoint aPoint,
|
||||
FrameWithDistance* aCurrentBestFrame);
|
||||
|
||||
|
||||
// nsISVGChildFrame interface:
|
||||
virtual void NotifySVGChanged(uint32_t aFlags);
|
||||
NS_IMETHOD PaintSVG(nsRenderingContext* aContext,
|
||||
const nsIntRect* aDirtyRect);
|
||||
NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint& aPoint);
|
||||
virtual void ReflowSVG();
|
||||
NS_IMETHOD_(nsRect) GetCoveredRegion();
|
||||
virtual SVGBBox GetBBoxContribution(const gfxMatrix& aToBBoxUserspace,
|
||||
|
@ -258,6 +266,32 @@ public:
|
|||
|
||||
double GetFontSizeScaleFactor() const;
|
||||
|
||||
/**
|
||||
* Takes a point from the <text> element's user space and
|
||||
* converts it to the appropriate frame user space of aChildFrame,
|
||||
* according to which rendered run the point hits.
|
||||
*/
|
||||
gfxPoint TransformFramePointToTextChild(const gfxPoint& aPoint,
|
||||
nsIFrame* aChildFrame);
|
||||
|
||||
/**
|
||||
* Takes a rectangle, aRect, in the <text> element's user space, and
|
||||
* returns a rectangle in aChildFrame's frame user space that
|
||||
* covers intersections of aRect with each rendered run for text frames
|
||||
* within aChildFrame.
|
||||
*/
|
||||
gfxRect TransformFrameRectToTextChild(const gfxRect& aRect,
|
||||
nsIFrame* aChildFrame);
|
||||
|
||||
/**
|
||||
* Takes an app unit rectangle in the coordinate space of a given descendant
|
||||
* frame of this frame, and returns a rectangle in the <text> element's user
|
||||
* space that covers all parts of rendered runs that intersect with the
|
||||
* rectangle.
|
||||
*/
|
||||
gfxRect TransformFrameRectFromTextChild(const nsRect& aRect,
|
||||
nsIFrame* aChildFrame);
|
||||
|
||||
private:
|
||||
/**
|
||||
* This class exists purely because it would be too messy to pass the "for"
|
||||
|
|
Загрузка…
Ссылка в новой задаче