void nsMathMLmoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) { bool useMathMLChar = UseMathMLChar(); if (!useMathMLChar) { // let the base class do everything nsMathMLTokenFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); } else { DisplayBorderBackgroundOutline(aBuilder, aLists); // make our char selected if our inner child text frame is selected bool isSelected = false; nsRect selectedRect; nsIFrame* firstChild = mFrames.FirstChild(); if (IsFrameInSelection(firstChild)) { mMathMLChar.GetRect(selectedRect); // add a one pixel border (it renders better for operators like minus) selectedRect.Inflate(nsPresContext::CSSPixelsToAppUnits(1)); isSelected = true; } mMathMLChar.Display(aBuilder, this, aLists, 0, isSelected ? &selectedRect : nullptr); #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX) // for visual debug DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists); #endif } }
/* virtual */ void nsMathMLmoFrame::GetIntrinsicISizeMetrics(nsRenderingContext *aRenderingContext, nsHTMLReflowMetrics& aDesiredSize) { ProcessOperatorData(); if (UseMathMLChar()) { uint32_t stretchHint = GetStretchHint(mFlags, mPresentationData, true, StyleFont()); aDesiredSize.Width() = mMathMLChar. GetMaxWidth(PresContext(), *aRenderingContext, stretchHint, mMaxSize, NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)); } else { nsMathMLTokenFrame::GetIntrinsicISizeMetrics(aRenderingContext, aDesiredSize); } // leadingSpace and trailingSpace are actually applied to the outermost // embellished container but for determining total intrinsic width it should // be safe to include it for the core here instead. bool isRTL = StyleVisibility()->mDirection; aDesiredSize.Width() += mEmbellishData.leadingSpace + mEmbellishData.trailingSpace; aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width(); if (isRTL) { aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.trailingSpace; aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.trailingSpace; } else { aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.leadingSpace; aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.leadingSpace; } }
NS_IMETHODIMP nsMathMLmoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) { nsresult rv = NS_OK; PRBool useMathMLChar = UseMathMLChar(); if (!useMathMLChar) { // let the base class do everything rv = nsMathMLTokenFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); NS_ENSURE_SUCCESS(rv, rv); } else { rv = DisplayBorderBackgroundOutline(aBuilder, aLists); NS_ENSURE_SUCCESS(rv, rv); // make our char selected if our inner child text frame is selected PRBool isSelected = PR_FALSE; nsRect selectedRect; nsIFrame* firstChild = mFrames.FirstChild(); if (IsFrameInSelection(firstChild)) { selectedRect = firstChild->GetRect(); isSelected = PR_TRUE; } rv = mMathMLChar.Display(aBuilder, this, aLists, isSelected ? &selectedRect : nsnull); NS_ENSURE_SUCCESS(rv, rv); #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX) // for visual debug rv = DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists); #endif } return rv; }
nsresult nsMathMLmoFrame::Place(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { nsresult rv = nsMathMLTokenFrame::Place(aRenderingContext, aPlaceOrigin, aDesiredSize); if (NS_FAILED(rv)) { return rv; } /* Special behaviour for largeops. In MathML "stretchy" and displaystyle "largeop" are different notions, even if we use the same technique to draw them (picking size variants). So largeop display operators should be considered "non-stretchy" and thus their sizes should be taken into account for the stretch size of other elements. This is a preliminary stretch - exact sizing/placement is handled by the Stretch() method. */ if (!aPlaceOrigin && StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK && NS_MATHML_OPERATOR_IS_LARGEOP(mFlags) && UseMathMLChar()) { nsBoundingMetrics newMetrics; rv = mMathMLChar.Stretch(PresContext(), aRenderingContext, nsLayoutUtils::FontSizeInflationFor(this), NS_STRETCH_DIRECTION_VERTICAL, aDesiredSize.mBoundingMetrics, newMetrics, NS_STRETCH_LARGEOP, StyleVisibility()->mDirection); if (NS_FAILED(rv)) { // Just use the initial size return NS_OK; } aDesiredSize.mBoundingMetrics = newMetrics; /* Treat the ascent/descent values calculated in the TokenFrame place calculations as the minimum for aDesiredSize calculations, rather than fetching them from font metrics again. */ aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent, newMetrics.ascent)); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + std::max(mBoundingMetrics.descent, newMetrics.descent); aDesiredSize.Width() = newMetrics.width; mBoundingMetrics = newMetrics; } return NS_OK; }
/* virtual */ nscoord nsMathMLmoFrame::GetIntrinsicWidth(nsRenderingContext *aRenderingContext) { ProcessOperatorData(); nscoord width; if (UseMathMLChar()) { uint32_t stretchHint = GetStretchHint(mFlags, mPresentationData, true); width = mMathMLChar. GetMaxWidth(PresContext(), *aRenderingContext, stretchHint, mMaxSize, NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)); } else { width = nsMathMLTokenFrame::GetIntrinsicWidth(aRenderingContext); } // leadingSpace and trailingSpace are actually applied to the outermost // embellished container but for determining total intrinsic width it should // be safe to include it for the core here instead. width += mEmbellishData.leadingSpace + mEmbellishData.trailingSpace; return width; }
// NOTE: aDesiredStretchSize is an IN/OUT parameter // On input - it contains our current size // On output - the same size or the new size that we want NS_IMETHODIMP nsMathMLmoFrame::Stretch(nsRenderingContext& aRenderingContext, nsStretchDirection aStretchDirection, nsBoundingMetrics& aContainerSize, nsHTMLReflowMetrics& aDesiredStretchSize) { if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) { NS_WARNING("it is wrong to fire stretch more than once on a frame"); return NS_OK; } mPresentationData.flags |= NS_MATHML_STRETCH_DONE; nsIFrame* firstChild = mFrames.FirstChild(); // get the axis height; nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); aRenderingContext.SetFont(fm); nscoord axisHeight, height; GetAxisHeight(aRenderingContext, fm, axisHeight); // get the leading to be left at the top and the bottom of the stretched char // this seems more reliable than using fm->GetLeading() on suspicious fonts nscoord em; GetEmHeight(fm, em); nscoord leading = NSToCoordRound(0.2f * em); // Operators that are stretchy, or those that are to be centered // to cater for fonts that are not math-aware, are handled by the MathMLChar // ('form' is reset if stretch fails -- i.e., we don't bother to stretch next time) bool useMathMLChar = UseMathMLChar(); nsBoundingMetrics charSize; nsBoundingMetrics container = aDesiredStretchSize.mBoundingMetrics; bool isVertical = false; if (((aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) || (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT)) && (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL)) { isVertical = true; } uint32_t stretchHint = GetStretchHint(mFlags, mPresentationData, isVertical); if (useMathMLChar) { nsBoundingMetrics initialSize = aDesiredStretchSize.mBoundingMetrics; if (stretchHint != NS_STRETCH_NONE) { container = aContainerSize; // some adjustments if the operator is symmetric and vertical if (isVertical && NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) { // we need to center about the axis nscoord delta = std::max(container.ascent - axisHeight, container.descent + axisHeight); container.ascent = delta + axisHeight; container.descent = delta - axisHeight; // get ready in case we encounter user-desired min-max size delta = std::max(initialSize.ascent - axisHeight, initialSize.descent + axisHeight); initialSize.ascent = delta + axisHeight; initialSize.descent = delta - axisHeight; } // check for user-desired min-max size if (mMaxSize != NS_MATHML_OPERATOR_SIZE_INFINITY && mMaxSize > 0.0f) { // if we are here, there is a user defined maxsize ... //XXX Set stretchHint = NS_STRETCH_NORMAL? to honor the maxsize as close as possible? if (NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)) { // there is an explicit value like maxsize="20pt" // try to maintain the aspect ratio of the char float aspect = mMaxSize / float(initialSize.ascent + initialSize.descent); container.ascent = std::min(container.ascent, nscoord(initialSize.ascent * aspect)); container.descent = std::min(container.descent, nscoord(initialSize.descent * aspect)); // below we use a type cast instead of a conversion to avoid a VC++ bug // see http://support.microsoft.com/support/kb/articles/Q115/7/05.ASP container.width = std::min(container.width, (nscoord)mMaxSize); } else { // multiplicative value container.ascent = std::min(container.ascent, nscoord(initialSize.ascent * mMaxSize)); container.descent = std::min(container.descent, nscoord(initialSize.descent * mMaxSize)); container.width = std::min(container.width, nscoord(initialSize.width * mMaxSize)); } if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) { // re-adjust to align the char with the bottom of the initial container height = container.ascent + container.descent; container.descent = aContainerSize.descent; container.ascent = height - container.descent; } } if (mMinSize > 0.0f) { // if we are here, there is a user defined minsize ... // always allow the char to stretch in its natural direction, // even if it is different from the caller's direction if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT && aStretchDirection != mEmbellishData.direction) { aStretchDirection = NS_STRETCH_DIRECTION_DEFAULT; // but when we are not honoring the requested direction // we should not use the caller's container size either container = initialSize; } if (NS_MATHML_OPERATOR_MINSIZE_IS_ABSOLUTE(mFlags)) { // there is an explicit value like minsize="20pt" // try to maintain the aspect ratio of the char float aspect = mMinSize / float(initialSize.ascent + initialSize.descent); container.ascent = std::max(container.ascent, nscoord(initialSize.ascent * aspect)); container.descent = std::max(container.descent, nscoord(initialSize.descent * aspect)); container.width = std::max(container.width, (nscoord)mMinSize); } else { // multiplicative value container.ascent = std::max(container.ascent, nscoord(initialSize.ascent * mMinSize)); container.descent = std::max(container.descent, nscoord(initialSize.descent * mMinSize)); container.width = std::max(container.width, nscoord(initialSize.width * mMinSize)); } if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) { // re-adjust to align the char with the bottom of the initial container height = container.ascent + container.descent; container.descent = aContainerSize.descent; container.ascent = height - container.descent; } } } // let the MathMLChar stretch itself... nsresult res = mMathMLChar.Stretch(PresContext(), aRenderingContext, aStretchDirection, container, charSize, stretchHint, StyleVisibility()->mDirection); if (NS_FAILED(res)) { // gracefully handle cases where stretching the char failed (i.e., GetBoundingMetrics failed) // clear our 'form' to behave as if the operator wasn't in the dictionary mFlags &= ~NS_MATHML_OPERATOR_FORM; useMathMLChar = false; } } // Child frames of invisble operators are not reflowed if (!NS_MATHML_OPERATOR_IS_INVISIBLE(mFlags)) { // Place our children using the default method // This will allow our child text frame to get its DidReflow() nsresult rv = Place(aRenderingContext, true, aDesiredStretchSize); if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { // Make sure the child frames get their DidReflow() calls. DidReflowChildren(mFrames.FirstChild()); } } if (useMathMLChar) { // update our bounding metrics... it becomes that of our MathML char mBoundingMetrics = charSize; // if the returned direction is 'unsupported', the char didn't actually change. // So we do the centering only if necessary if (mMathMLChar.GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) { bool largeopOnly = (NS_STRETCH_LARGEOP & stretchHint) != 0 && (NS_STRETCH_VARIABLE_MASK & stretchHint) == 0; if (isVertical || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) { // the desired size returned by mMathMLChar maybe different // from the size of the container. // the mMathMLChar.mRect.y calculation is subtle, watch out!!! height = mBoundingMetrics.ascent + mBoundingMetrics.descent; if (NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags) || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) { // For symmetric and vertical operators, or for operators that are always // centered ('+', '*', etc) we want to center about the axis of the container mBoundingMetrics.descent = height/2 - axisHeight; } else if (!largeopOnly) { // Align the center of the char with the center of the container mBoundingMetrics.descent = height/2 + (container.ascent + container.descent)/2 - container.ascent; } // else align the baselines mBoundingMetrics.ascent = height - mBoundingMetrics.descent; } } } // Fixup for the final height. // On one hand, our stretchy height can sometimes be shorter than surrounding // ASCII chars, e.g., arrow symbols have |mBoundingMetrics.ascent + leading| // that is smaller than the ASCII's ascent, hence when painting the background // later, it won't look uniform along the line. // On the other hand, sometimes we may leave too much gap when our glyph happens // to come from a font with tall glyphs. For example, since CMEX10 has very tall // glyphs, its natural font metrics are large, even if we pick a small glyph // whose size is comparable to the size of a normal ASCII glyph. // So to avoid uneven spacing in either of these two cases, we use the height // of the ASCII font as a reference and try to match it if possible. // special case for accents... keep them short to improve mouse operations... // an accent can only be the non-first child of <mover>, <munder>, <munderover> bool isAccent = NS_MATHML_EMBELLISH_IS_ACCENT(mEmbellishData.flags); if (isAccent) { nsEmbellishData parentData; GetEmbellishDataFrom(mParent, parentData); isAccent = (NS_MATHML_EMBELLISH_IS_ACCENTOVER(parentData.flags) || NS_MATHML_EMBELLISH_IS_ACCENTUNDER(parentData.flags)) && parentData.coreFrame != this; } if (isAccent && firstChild) { // see bug 188467 for what is going on here nscoord dy = aDesiredStretchSize.ascent - (mBoundingMetrics.ascent + leading); aDesiredStretchSize.ascent = mBoundingMetrics.ascent + leading; aDesiredStretchSize.height = aDesiredStretchSize.ascent + mBoundingMetrics.descent; firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy)); } else if (useMathMLChar) { nscoord ascent = fm->MaxAscent(); nscoord descent = fm->MaxDescent(); aDesiredStretchSize.ascent = std::max(mBoundingMetrics.ascent + leading, ascent); aDesiredStretchSize.height = aDesiredStretchSize.ascent + std::max(mBoundingMetrics.descent + leading, descent); } aDesiredStretchSize.width = mBoundingMetrics.width; aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredStretchSize.ascent; // Place our mMathMLChar, its origin is in our coordinate system if (useMathMLChar) { nscoord dy = aDesiredStretchSize.ascent - mBoundingMetrics.ascent; mMathMLChar.SetRect(nsRect(0, dy, charSize.width, charSize.ascent + charSize.descent)); } // Before we leave... there is a last item in the check-list: // If our parent is not embellished, it means we are the outermost embellished // container and so we put the spacing, otherwise we don't include the spacing, // the outermost embellished container will take care of it. if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) { // Account the spacing if we are not an accent with explicit attributes nscoord leadingSpace = mEmbellishData.leadingSpace; if (isAccent && !NS_MATHML_OPERATOR_HAS_LSPACE_ATTR(mFlags)) { leadingSpace = 0; } nscoord trailingSpace = mEmbellishData.trailingSpace; if (isAccent && !NS_MATHML_OPERATOR_HAS_RSPACE_ATTR(mFlags)) { trailingSpace = 0; } mBoundingMetrics.width += leadingSpace + trailingSpace; aDesiredStretchSize.width = mBoundingMetrics.width; aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width; nscoord dx = (StyleVisibility()->mDirection ? trailingSpace : leadingSpace); if (dx) { // adjust the offsets mBoundingMetrics.leftBearing += dx; mBoundingMetrics.rightBearing += dx; aDesiredStretchSize.mBoundingMetrics.leftBearing += dx; aDesiredStretchSize.mBoundingMetrics.rightBearing += dx; if (useMathMLChar) { nsRect rect; mMathMLChar.GetRect(rect); mMathMLChar.SetRect(nsRect(rect.x + dx, rect.y, rect.width, rect.height)); } else { nsIFrame* childFrame = firstChild; while (childFrame) { childFrame->SetPosition(childFrame->GetPosition() + nsPoint(dx, 0)); childFrame = childFrame->GetNextSibling(); } } } } // Finished with these: ClearSavedChildMetrics(); // Set our overflow area GatherAndStoreOverflow(&aDesiredStretchSize); // There used to be code here to change the height of the child frame to // change the caret height, but the text frame that manages the caret is now // not a direct child but wrapped in a block frame. See also bug 412033. return NS_OK; }