michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: michael@0: #include "nsMathMLmfracFrame.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsDisplayList.h" michael@0: #include "gfxContext.h" michael@0: #include "nsMathMLElement.h" michael@0: #include michael@0: michael@0: // michael@0: // -- form a fraction from two subexpressions - implementation michael@0: // michael@0: michael@0: // various fraction line thicknesses (multiplicative values of the default rule thickness) michael@0: michael@0: #define THIN_FRACTION_LINE 0.5f michael@0: #define THIN_FRACTION_LINE_MINIMUM_PIXELS 1 // minimum of 1 pixel michael@0: michael@0: #define THICK_FRACTION_LINE 2.0f michael@0: #define THICK_FRACTION_LINE_MINIMUM_PIXELS 2 // minimum of 2 pixels michael@0: michael@0: nsIFrame* michael@0: NS_NewMathMLmfracFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsMathMLmfracFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfracFrame) michael@0: michael@0: nsMathMLmfracFrame::~nsMathMLmfracFrame() michael@0: { michael@0: } michael@0: michael@0: eMathMLFrameType michael@0: nsMathMLmfracFrame::GetMathMLFrameType() michael@0: { michael@0: // frac is "inner" in TeXBook, Appendix G, rule 15e. See also page 170. michael@0: return eMathMLFrameType_Inner; michael@0: } michael@0: michael@0: uint8_t michael@0: nsMathMLmfracFrame::ScriptIncrement(nsIFrame* aFrame) michael@0: { michael@0: if (!StyleFont()->mMathDisplay && michael@0: aFrame && (mFrames.FirstChild() == aFrame || michael@0: mFrames.LastChild() == aFrame)) { michael@0: return 1; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMathMLmfracFrame::TransmitAutomaticData() michael@0: { michael@0: // The TeXbook (Ch 17. p.141) says the numerator inherits the compression michael@0: // while the denominator is compressed michael@0: UpdatePresentationDataFromChildAt(1, 1, michael@0: NS_MATHML_COMPRESSED, michael@0: NS_MATHML_COMPRESSED); michael@0: michael@0: // If displaystyle is false, then scriptlevel is incremented, so notify the michael@0: // children of this. michael@0: if (!StyleFont()->mMathDisplay) { michael@0: PropagateFrameFlagFor(mFrames.FirstChild(), michael@0: NS_FRAME_MATHML_SCRIPT_DESCENDANT); michael@0: PropagateFrameFlagFor(mFrames.LastChild(), michael@0: NS_FRAME_MATHML_SCRIPT_DESCENDANT); michael@0: } michael@0: michael@0: // if our numerator is an embellished operator, let its state bubble to us michael@0: GetEmbellishDataFrom(mFrames.FirstChild(), mEmbellishData); michael@0: if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) { michael@0: // even when embellished, we need to record that won't fire michael@0: // Stretch() on its embellished child michael@0: mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nscoord michael@0: nsMathMLmfracFrame::CalcLineThickness(nsPresContext* aPresContext, michael@0: nsStyleContext* aStyleContext, michael@0: nsString& aThicknessAttribute, michael@0: nscoord onePixel, michael@0: nscoord aDefaultRuleThickness) michael@0: { michael@0: nscoord defaultThickness = aDefaultRuleThickness; michael@0: nscoord lineThickness = aDefaultRuleThickness; michael@0: nscoord minimumThickness = onePixel; michael@0: michael@0: // linethickness michael@0: // michael@0: // "Specifies the thickness of the horizontal 'fraction bar', or 'rule'. The michael@0: // default value is 'medium', 'thin' is thinner, but visible, 'thick' is michael@0: // thicker; the exact thickness of these is left up to the rendering agent." michael@0: // michael@0: // values: length | "thin" | "medium" | "thick" michael@0: // default: medium michael@0: // michael@0: if (!aThicknessAttribute.IsEmpty()) { michael@0: if (aThicknessAttribute.EqualsLiteral("thin")) { michael@0: lineThickness = NSToCoordFloor(defaultThickness * THIN_FRACTION_LINE); michael@0: minimumThickness = onePixel * THIN_FRACTION_LINE_MINIMUM_PIXELS; michael@0: // should visually decrease by at least one pixel, if default is not a pixel michael@0: if (defaultThickness > onePixel && lineThickness > defaultThickness - onePixel) michael@0: lineThickness = defaultThickness - onePixel; michael@0: } michael@0: else if (aThicknessAttribute.EqualsLiteral("medium")) { michael@0: // medium is default michael@0: } michael@0: else if (aThicknessAttribute.EqualsLiteral("thick")) { michael@0: lineThickness = NSToCoordCeil(defaultThickness * THICK_FRACTION_LINE); michael@0: minimumThickness = onePixel * THICK_FRACTION_LINE_MINIMUM_PIXELS; michael@0: // should visually increase by at least one pixel michael@0: if (lineThickness < defaultThickness + onePixel) michael@0: lineThickness = defaultThickness + onePixel; michael@0: } michael@0: else { michael@0: // length value michael@0: lineThickness = defaultThickness; michael@0: ParseNumericValue(aThicknessAttribute, &lineThickness, michael@0: nsMathMLElement::PARSE_ALLOW_UNITLESS, michael@0: aPresContext, aStyleContext); michael@0: } michael@0: } michael@0: michael@0: // use minimum if the lineThickness is a non-zero value less than minimun michael@0: if (lineThickness && lineThickness < minimumThickness) michael@0: lineThickness = minimumThickness; michael@0: michael@0: return lineThickness; michael@0: } michael@0: michael@0: void michael@0: nsMathMLmfracFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: ///////////// michael@0: // paint the numerator and denominator michael@0: nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); michael@0: michael@0: ///////////// michael@0: // paint the fraction line michael@0: if (mIsBevelled) { michael@0: DisplaySlash(aBuilder, this, mLineRect, mLineThickness, aLists); michael@0: } else { michael@0: DisplayBar(aBuilder, this, mLineRect, aLists); michael@0: } michael@0: } michael@0: michael@0: /* virtual */ nsresult michael@0: nsMathMLmfracFrame::MeasureForWidth(nsRenderingContext& aRenderingContext, michael@0: nsHTMLReflowMetrics& aDesiredSize) michael@0: { michael@0: return PlaceInternal(aRenderingContext, michael@0: false, michael@0: aDesiredSize, michael@0: true); michael@0: } michael@0: michael@0: nscoord michael@0: nsMathMLmfracFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize) michael@0: { michael@0: nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize); michael@0: if (!gap) return 0; michael@0: michael@0: mLineRect.MoveBy(gap, 0); michael@0: return gap; michael@0: } michael@0: michael@0: /* virtual */ nsresult michael@0: nsMathMLmfracFrame::Place(nsRenderingContext& aRenderingContext, michael@0: bool aPlaceOrigin, michael@0: nsHTMLReflowMetrics& aDesiredSize) michael@0: { michael@0: return PlaceInternal(aRenderingContext, michael@0: aPlaceOrigin, michael@0: aDesiredSize, michael@0: false); michael@0: } michael@0: michael@0: nsresult michael@0: nsMathMLmfracFrame::PlaceInternal(nsRenderingContext& aRenderingContext, michael@0: bool aPlaceOrigin, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: bool aWidthOnly) michael@0: { michael@0: //////////////////////////////////// michael@0: // Get the children's desired sizes michael@0: nsBoundingMetrics bmNum, bmDen; michael@0: nsHTMLReflowMetrics sizeNum(aDesiredSize.GetWritingMode()); michael@0: nsHTMLReflowMetrics sizeDen(aDesiredSize.GetWritingMode()); michael@0: nsIFrame* frameDen = nullptr; michael@0: nsIFrame* frameNum = mFrames.FirstChild(); michael@0: if (frameNum) michael@0: frameDen = frameNum->GetNextSibling(); michael@0: if (!frameNum || !frameDen || frameDen->GetNextSibling()) { michael@0: // report an error, encourage people to get their markups in order michael@0: if (aPlaceOrigin) { michael@0: ReportChildCountError(); michael@0: } michael@0: return ReflowError(aRenderingContext, aDesiredSize); michael@0: } michael@0: GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum); michael@0: GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen); michael@0: michael@0: nsPresContext* presContext = PresContext(); michael@0: nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); michael@0: michael@0: nsRefPtr fm; michael@0: nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); michael@0: aRenderingContext.SetFont(fm); michael@0: michael@0: nscoord defaultRuleThickness, axisHeight; michael@0: GetRuleThickness(aRenderingContext, fm, defaultRuleThickness); michael@0: GetAxisHeight(aRenderingContext, fm, axisHeight); michael@0: michael@0: bool outermostEmbellished = false; michael@0: if (mEmbellishData.coreFrame) { michael@0: nsEmbellishData parentData; michael@0: GetEmbellishDataFrom(mParent, parentData); michael@0: outermostEmbellished = parentData.coreFrame != mEmbellishData.coreFrame; michael@0: } michael@0: michael@0: // see if the linethickness attribute is there michael@0: nsAutoString value; michael@0: mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::linethickness_, value); michael@0: mLineThickness = CalcLineThickness(presContext, mStyleContext, value, michael@0: onePixel, defaultRuleThickness); michael@0: michael@0: // bevelled attribute michael@0: mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::bevelled_, value); michael@0: mIsBevelled = value.EqualsLiteral("true"); michael@0: michael@0: if (!mIsBevelled) { michael@0: mLineRect.height = mLineThickness; michael@0: michael@0: // by default, leave at least one-pixel padding at either end, and add michael@0: // lspace & rspace that may come from if we are an outermost michael@0: // embellished container (we fetch values from the core since they may use michael@0: // units that depend on style data, and style changes could have occurred michael@0: // in the core since our last visit there) michael@0: nscoord leftSpace = onePixel; michael@0: nscoord rightSpace = onePixel; michael@0: if (outermostEmbellished) { michael@0: nsEmbellishData coreData; michael@0: GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData); michael@0: leftSpace += StyleVisibility()->mDirection ? michael@0: coreData.trailingSpace : coreData.leadingSpace; michael@0: rightSpace += StyleVisibility()->mDirection ? michael@0: coreData.leadingSpace : coreData.trailingSpace; michael@0: } michael@0: michael@0: ////////////////// michael@0: // Get shifts michael@0: nscoord numShift = 0; michael@0: nscoord denShift = 0; michael@0: michael@0: // Rule 15b, App. G, TeXbook michael@0: nscoord numShift1, numShift2, numShift3; michael@0: nscoord denShift1, denShift2; michael@0: michael@0: GetNumeratorShifts(fm, numShift1, numShift2, numShift3); michael@0: GetDenominatorShifts(fm, denShift1, denShift2); michael@0: if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) { michael@0: // C > T michael@0: numShift = numShift1; michael@0: denShift = denShift1; michael@0: } michael@0: else { michael@0: numShift = (0 < mLineRect.height) ? numShift2 : numShift3; michael@0: denShift = denShift2; michael@0: } michael@0: michael@0: nscoord minClearance = 0; michael@0: nscoord actualClearance = 0; michael@0: michael@0: nscoord actualRuleThickness = mLineThickness; michael@0: michael@0: if (0 == actualRuleThickness) { michael@0: // Rule 15c, App. G, TeXbook michael@0: michael@0: // min clearance between numerator and denominator michael@0: minClearance = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK ? michael@0: 7 * defaultRuleThickness : 3 * defaultRuleThickness; michael@0: actualClearance = michael@0: (numShift - bmNum.descent) - (bmDen.ascent - denShift); michael@0: // actualClearance should be >= minClearance michael@0: if (actualClearance < minClearance) { michael@0: nscoord halfGap = (minClearance - actualClearance)/2; michael@0: numShift += halfGap; michael@0: denShift += halfGap; michael@0: } michael@0: } michael@0: else { michael@0: // Rule 15d, App. G, TeXbook michael@0: michael@0: // min clearance between numerator or denominator and middle of bar michael@0: michael@0: // TeX has a different interpretation of the thickness. michael@0: // Try $a \above10pt b$ to see. Here is what TeX does: michael@0: // minClearance = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK michael@0: // ? 3 * actualRuleThickness : actualRuleThickness; michael@0: michael@0: // we slightly depart from TeX here. We use the defaultRuleThickness instead michael@0: // of the value coming from the linethickness attribute, i.e., we recover what michael@0: // TeX does if the user hasn't set linethickness. But when the linethickness michael@0: // is set, we avoid the wide gap problem. michael@0: minClearance = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK ? michael@0: 3 * defaultRuleThickness : defaultRuleThickness + onePixel; michael@0: michael@0: // adjust numShift to maintain minClearance if needed michael@0: actualClearance = michael@0: (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2); michael@0: if (actualClearance < minClearance) { michael@0: numShift += (minClearance - actualClearance); michael@0: } michael@0: // adjust denShift to maintain minClearance if needed michael@0: actualClearance = michael@0: (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift); michael@0: if (actualClearance < minClearance) { michael@0: denShift += (minClearance - actualClearance); michael@0: } michael@0: } michael@0: michael@0: ////////////////// michael@0: // Place Children michael@0: michael@0: // XXX Need revisiting the width. TeX uses the exact width michael@0: // e.g. in $$\huge\frac{\displaystyle\int}{i}$$ michael@0: nscoord width = std::max(bmNum.width, bmDen.width); michael@0: nscoord dxNum = leftSpace + (width - sizeNum.Width())/2; michael@0: nscoord dxDen = leftSpace + (width - sizeDen.Width())/2; michael@0: width += leftSpace + rightSpace; michael@0: michael@0: // see if the numalign attribute is there michael@0: mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::numalign_, value); michael@0: if (value.EqualsLiteral("left")) michael@0: dxNum = leftSpace; michael@0: else if (value.EqualsLiteral("right")) michael@0: dxNum = width - rightSpace - sizeNum.Width(); michael@0: michael@0: // see if the denomalign attribute is there michael@0: mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::denomalign_, value); michael@0: if (value.EqualsLiteral("left")) michael@0: dxDen = leftSpace; michael@0: else if (value.EqualsLiteral("right")) michael@0: dxDen = width - rightSpace - sizeDen.Width(); michael@0: michael@0: mBoundingMetrics.rightBearing = michael@0: std::max(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing); michael@0: if (mBoundingMetrics.rightBearing < width - rightSpace) michael@0: mBoundingMetrics.rightBearing = width - rightSpace; michael@0: mBoundingMetrics.leftBearing = michael@0: std::min(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing); michael@0: if (mBoundingMetrics.leftBearing > leftSpace) michael@0: mBoundingMetrics.leftBearing = leftSpace; michael@0: mBoundingMetrics.ascent = bmNum.ascent + numShift; michael@0: mBoundingMetrics.descent = bmDen.descent + denShift; michael@0: mBoundingMetrics.width = width; michael@0: michael@0: aDesiredSize.SetTopAscent(sizeNum.TopAscent() + numShift); michael@0: aDesiredSize.Height() = aDesiredSize.TopAscent() + michael@0: sizeDen.Height() - sizeDen.TopAscent() + denShift; michael@0: aDesiredSize.Width() = mBoundingMetrics.width; michael@0: aDesiredSize.mBoundingMetrics = mBoundingMetrics; michael@0: michael@0: mReference.x = 0; michael@0: mReference.y = aDesiredSize.TopAscent(); michael@0: michael@0: if (aPlaceOrigin) { michael@0: nscoord dy; michael@0: // place numerator michael@0: dy = 0; michael@0: FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dxNum, dy, 0); michael@0: // place denominator michael@0: dy = aDesiredSize.Height() - sizeDen.Height(); michael@0: FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dxDen, dy, 0); michael@0: // place the fraction bar - dy is top of bar michael@0: dy = aDesiredSize.TopAscent() - (axisHeight + actualRuleThickness/2); michael@0: mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace), michael@0: actualRuleThickness); michael@0: } michael@0: } else { michael@0: nscoord numShift = 0.0; michael@0: nscoord denShift = 0.0; michael@0: nscoord padding = 3 * defaultRuleThickness; michael@0: nscoord slashRatio = 3; michael@0: michael@0: // Define the constant used in the expression of the maximum width michael@0: nscoord em = fm->EmHeight(); michael@0: nscoord slashMaxWidthConstant = 2 * em; michael@0: michael@0: // For large line thicknesses the minimum slash height is limited to the michael@0: // largest expected height of a fraction michael@0: nscoord slashMinHeight = slashRatio * michael@0: std::min(2 * mLineThickness, slashMaxWidthConstant); michael@0: michael@0: nscoord leadingSpace = padding; michael@0: nscoord trailingSpace = padding; michael@0: if (outermostEmbellished) { michael@0: nsEmbellishData coreData; michael@0: GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData); michael@0: leadingSpace += coreData.leadingSpace; michael@0: trailingSpace += coreData.trailingSpace; michael@0: } michael@0: nscoord delta; michael@0: michael@0: // ___________ michael@0: // | | / michael@0: // {|-NUMERATOR-| / michael@0: // {|___________| S michael@0: // { L michael@0: // numShift{ A michael@0: // ------------------------------------------------------- baseline michael@0: // S _____________ } denShift michael@0: // H | |} michael@0: // / |-DENOMINATOR-|} michael@0: // / |_____________| michael@0: // michael@0: michael@0: // first, ensure that the top of the numerator is at least as high as the michael@0: // top of the denominator (and the reverse for the bottoms) michael@0: delta = std::max(bmDen.ascent - bmNum.ascent, michael@0: bmNum.descent - bmDen.descent) / 2; michael@0: if (delta > 0) { michael@0: numShift += delta; michael@0: denShift += delta; michael@0: } michael@0: michael@0: if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) { michael@0: delta = std::min(bmDen.ascent + bmDen.descent, michael@0: bmNum.ascent + bmNum.descent) / 2; michael@0: numShift += delta; michael@0: denShift += delta; michael@0: } else { michael@0: nscoord xHeight = fm->XHeight(); michael@0: numShift += xHeight / 2; michael@0: denShift += xHeight / 4; michael@0: } michael@0: michael@0: // Set the ascent/descent of our BoundingMetrics. michael@0: mBoundingMetrics.ascent = bmNum.ascent + numShift; michael@0: mBoundingMetrics.descent = bmDen.descent + denShift; michael@0: michael@0: // At this point the height of the slash is michael@0: // mBoundingMetrics.ascent + mBoundingMetrics.descent michael@0: // Ensure that it is greater than slashMinHeight michael@0: delta = (slashMinHeight - michael@0: (mBoundingMetrics.ascent + mBoundingMetrics.descent)) / 2; michael@0: if (delta > 0) { michael@0: mBoundingMetrics.ascent += delta; michael@0: mBoundingMetrics.descent += delta; michael@0: } michael@0: michael@0: // Set the width of the slash michael@0: if (aWidthOnly) { michael@0: mLineRect.width = mLineThickness + slashMaxWidthConstant; michael@0: } else { michael@0: mLineRect.width = mLineThickness + michael@0: std::min(slashMaxWidthConstant, michael@0: (mBoundingMetrics.ascent + mBoundingMetrics.descent) / michael@0: slashRatio); michael@0: } michael@0: michael@0: // Set horizontal bounding metrics michael@0: if (StyleVisibility()->mDirection) { michael@0: mBoundingMetrics.leftBearing = trailingSpace + bmDen.leftBearing; michael@0: mBoundingMetrics.rightBearing = trailingSpace + bmDen.width + mLineRect.width + bmNum.rightBearing; michael@0: } else { michael@0: mBoundingMetrics.leftBearing = leadingSpace + bmNum.leftBearing; michael@0: mBoundingMetrics.rightBearing = leadingSpace + bmNum.width + mLineRect.width + bmDen.rightBearing; michael@0: } michael@0: mBoundingMetrics.width = michael@0: leadingSpace + bmNum.width + mLineRect.width + bmDen.width + michael@0: trailingSpace; michael@0: michael@0: // Set aDesiredSize michael@0: aDesiredSize.SetTopAscent(mBoundingMetrics.ascent + padding); michael@0: aDesiredSize.Height() = michael@0: mBoundingMetrics.ascent + mBoundingMetrics.descent + 2 * padding; michael@0: aDesiredSize.Width() = mBoundingMetrics.width; michael@0: aDesiredSize.mBoundingMetrics = mBoundingMetrics; michael@0: michael@0: mReference.x = 0; michael@0: mReference.y = aDesiredSize.TopAscent(); michael@0: michael@0: if (aPlaceOrigin) { michael@0: nscoord dx, dy; michael@0: michael@0: // place numerator michael@0: dx = MirrorIfRTL(aDesiredSize.Width(), sizeNum.Width(), michael@0: leadingSpace); michael@0: dy = aDesiredSize.TopAscent() - numShift - sizeNum.TopAscent(); michael@0: FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dx, dy, 0); michael@0: michael@0: // place the fraction bar michael@0: dx = MirrorIfRTL(aDesiredSize.Width(), mLineRect.width, michael@0: leadingSpace + bmNum.width); michael@0: dy = aDesiredSize.TopAscent() - mBoundingMetrics.ascent; michael@0: mLineRect.SetRect(dx, dy, michael@0: mLineRect.width, aDesiredSize.Height() - 2 * padding); michael@0: michael@0: // place denominator michael@0: dx = MirrorIfRTL(aDesiredSize.Width(), sizeDen.Width(), michael@0: leadingSpace + bmNum.width + mLineRect.width); michael@0: dy = aDesiredSize.TopAscent() + denShift - sizeDen.TopAscent(); michael@0: FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dx, dy, 0); michael@0: } michael@0: michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class nsDisplayMathMLSlash : public nsDisplayItem { michael@0: public: michael@0: nsDisplayMathMLSlash(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, const nsRect& aRect, michael@0: nscoord aThickness, bool aRTL) michael@0: : nsDisplayItem(aBuilder, aFrame), mRect(aRect), mThickness(aThickness), michael@0: mRTL(aRTL) { michael@0: MOZ_COUNT_CTOR(nsDisplayMathMLSlash); michael@0: } michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: virtual ~nsDisplayMathMLSlash() { michael@0: MOZ_COUNT_DTOR(nsDisplayMathMLSlash); michael@0: } michael@0: #endif michael@0: michael@0: virtual void Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) MOZ_OVERRIDE; michael@0: NS_DISPLAY_DECL_NAME("MathMLSlash", TYPE_MATHML_SLASH) michael@0: michael@0: private: michael@0: nsRect mRect; michael@0: nscoord mThickness; michael@0: bool mRTL; michael@0: }; michael@0: michael@0: void nsDisplayMathMLSlash::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: // get the gfxRect michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: gfxRect rect = presContext->AppUnitsToGfxUnits(mRect + ToReferenceFrame()); michael@0: michael@0: // paint with the current text color michael@0: aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color)); michael@0: michael@0: // draw the slash as a parallelogram michael@0: gfxContext *gfxCtx = aCtx->ThebesContext(); michael@0: gfxPoint delta = gfxPoint(presContext->AppUnitsToGfxUnits(mThickness), 0); michael@0: gfxCtx->NewPath(); michael@0: michael@0: if (mRTL) { michael@0: gfxCtx->MoveTo(rect.TopLeft()); michael@0: gfxCtx->LineTo(rect.TopLeft() + delta); michael@0: gfxCtx->LineTo(rect.BottomRight()); michael@0: gfxCtx->LineTo(rect.BottomRight() - delta); michael@0: } else { michael@0: gfxCtx->MoveTo(rect.BottomLeft()); michael@0: gfxCtx->LineTo(rect.BottomLeft() + delta); michael@0: gfxCtx->LineTo(rect.TopRight()); michael@0: gfxCtx->LineTo(rect.TopRight() - delta); michael@0: } michael@0: michael@0: gfxCtx->ClosePath(); michael@0: gfxCtx->Fill(); michael@0: } michael@0: michael@0: void michael@0: nsMathMLmfracFrame::DisplaySlash(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, const nsRect& aRect, michael@0: nscoord aThickness, michael@0: const nsDisplayListSet& aLists) { michael@0: if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty()) michael@0: return; michael@0: michael@0: aLists.Content()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayMathMLSlash(aBuilder, aFrame, aRect, aThickness, michael@0: StyleVisibility()->mDirection)); michael@0: }