Bug 1226301. Remove Shumway from b2gdroid nightly builds. r=fabrice
[gecko.git] / layout / mathml / nsMathMLmfracFrame.cpp
blobbf5e0f54484174858d240634ffbac57f74d5d00c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsMathMLmfracFrame.h"
9 #include "gfxUtils.h"
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/RefPtr.h"
12 #include "nsLayoutUtils.h"
13 #include "nsPresContext.h"
14 #include "nsRenderingContext.h"
15 #include "nsDisplayList.h"
16 #include "gfxContext.h"
17 #include "nsMathMLElement.h"
18 #include <algorithm>
20 using namespace mozilla;
21 using namespace mozilla::gfx;
24 // <mfrac> -- form a fraction from two subexpressions - implementation
27 // various fraction line thicknesses (multiplicative values of the default rule thickness)
29 #define THIN_FRACTION_LINE 0.5f
30 #define THIN_FRACTION_LINE_MINIMUM_PIXELS 1 // minimum of 1 pixel
32 #define THICK_FRACTION_LINE 2.0f
33 #define THICK_FRACTION_LINE_MINIMUM_PIXELS 2 // minimum of 2 pixels
35 nsIFrame*
36 NS_NewMathMLmfracFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
38 return new (aPresShell) nsMathMLmfracFrame(aContext);
41 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfracFrame)
43 nsMathMLmfracFrame::~nsMathMLmfracFrame()
47 eMathMLFrameType
48 nsMathMLmfracFrame::GetMathMLFrameType()
50 // frac is "inner" in TeXBook, Appendix G, rule 15e. See also page 170.
51 return eMathMLFrameType_Inner;
54 uint8_t
55 nsMathMLmfracFrame::ScriptIncrement(nsIFrame* aFrame)
57 if (!StyleFont()->mMathDisplay &&
58 aFrame && (mFrames.FirstChild() == aFrame ||
59 mFrames.LastChild() == aFrame)) {
60 return 1;
62 return 0;
65 NS_IMETHODIMP
66 nsMathMLmfracFrame::TransmitAutomaticData()
68 // The TeXbook (Ch 17. p.141) says the numerator inherits the compression
69 // while the denominator is compressed
70 UpdatePresentationDataFromChildAt(1, 1,
71 NS_MATHML_COMPRESSED,
72 NS_MATHML_COMPRESSED);
74 // If displaystyle is false, then scriptlevel is incremented, so notify the
75 // children of this.
76 if (!StyleFont()->mMathDisplay) {
77 PropagateFrameFlagFor(mFrames.FirstChild(),
78 NS_FRAME_MATHML_SCRIPT_DESCENDANT);
79 PropagateFrameFlagFor(mFrames.LastChild(),
80 NS_FRAME_MATHML_SCRIPT_DESCENDANT);
83 // if our numerator is an embellished operator, let its state bubble to us
84 GetEmbellishDataFrom(mFrames.FirstChild(), mEmbellishData);
85 if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
86 // even when embellished, we need to record that <mfrac> won't fire
87 // Stretch() on its embellished child
88 mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
91 return NS_OK;
94 nscoord
95 nsMathMLmfracFrame::CalcLineThickness(nsPresContext* aPresContext,
96 nsStyleContext* aStyleContext,
97 nsString& aThicknessAttribute,
98 nscoord onePixel,
99 nscoord aDefaultRuleThickness,
100 float aFontSizeInflation)
102 nscoord defaultThickness = aDefaultRuleThickness;
103 nscoord lineThickness = aDefaultRuleThickness;
104 nscoord minimumThickness = onePixel;
106 // linethickness
108 // "Specifies the thickness of the horizontal 'fraction bar', or 'rule'. The
109 // default value is 'medium', 'thin' is thinner, but visible, 'thick' is
110 // thicker; the exact thickness of these is left up to the rendering agent."
112 // values: length | "thin" | "medium" | "thick"
113 // default: medium
115 if (!aThicknessAttribute.IsEmpty()) {
116 if (aThicknessAttribute.EqualsLiteral("thin")) {
117 lineThickness = NSToCoordFloor(defaultThickness * THIN_FRACTION_LINE);
118 minimumThickness = onePixel * THIN_FRACTION_LINE_MINIMUM_PIXELS;
119 // should visually decrease by at least one pixel, if default is not a pixel
120 if (defaultThickness > onePixel && lineThickness > defaultThickness - onePixel)
121 lineThickness = defaultThickness - onePixel;
123 else if (aThicknessAttribute.EqualsLiteral("medium")) {
124 // medium is default
126 else if (aThicknessAttribute.EqualsLiteral("thick")) {
127 lineThickness = NSToCoordCeil(defaultThickness * THICK_FRACTION_LINE);
128 minimumThickness = onePixel * THICK_FRACTION_LINE_MINIMUM_PIXELS;
129 // should visually increase by at least one pixel
130 if (lineThickness < defaultThickness + onePixel)
131 lineThickness = defaultThickness + onePixel;
133 else {
134 // length value
135 lineThickness = defaultThickness;
136 ParseNumericValue(aThicknessAttribute, &lineThickness,
137 nsMathMLElement::PARSE_ALLOW_UNITLESS,
138 aPresContext, aStyleContext, aFontSizeInflation);
142 // use minimum if the lineThickness is a non-zero value less than minimun
143 if (lineThickness && lineThickness < minimumThickness)
144 lineThickness = minimumThickness;
146 return lineThickness;
149 void
150 nsMathMLmfracFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
151 const nsRect& aDirtyRect,
152 const nsDisplayListSet& aLists)
154 /////////////
155 // paint the numerator and denominator
156 nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
158 /////////////
159 // paint the fraction line
160 if (mIsBevelled) {
161 DisplaySlash(aBuilder, this, mLineRect, mLineThickness, aLists);
162 } else {
163 DisplayBar(aBuilder, this, mLineRect, aLists);
167 /* virtual */ nsresult
168 nsMathMLmfracFrame::MeasureForWidth(DrawTarget* aDrawTarget,
169 nsHTMLReflowMetrics& aDesiredSize)
171 return PlaceInternal(aDrawTarget, false, aDesiredSize, true);
174 nscoord
175 nsMathMLmfracFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
177 nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
178 if (!gap) return 0;
180 mLineRect.MoveBy(gap, 0);
181 return gap;
184 /* virtual */ nsresult
185 nsMathMLmfracFrame::Place(DrawTarget* aDrawTarget,
186 bool aPlaceOrigin,
187 nsHTMLReflowMetrics& aDesiredSize)
189 return PlaceInternal(aDrawTarget, aPlaceOrigin, aDesiredSize, false);
192 nsresult
193 nsMathMLmfracFrame::PlaceInternal(DrawTarget* aDrawTarget,
194 bool aPlaceOrigin,
195 nsHTMLReflowMetrics& aDesiredSize,
196 bool aWidthOnly)
198 ////////////////////////////////////
199 // Get the children's desired sizes
200 nsBoundingMetrics bmNum, bmDen;
201 nsHTMLReflowMetrics sizeNum(aDesiredSize.GetWritingMode());
202 nsHTMLReflowMetrics sizeDen(aDesiredSize.GetWritingMode());
203 nsIFrame* frameDen = nullptr;
204 nsIFrame* frameNum = mFrames.FirstChild();
205 if (frameNum)
206 frameDen = frameNum->GetNextSibling();
207 if (!frameNum || !frameDen || frameDen->GetNextSibling()) {
208 // report an error, encourage people to get their markups in order
209 if (aPlaceOrigin) {
210 ReportChildCountError();
212 return ReflowError(aDrawTarget, aDesiredSize);
214 GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum);
215 GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen);
217 nsPresContext* presContext = PresContext();
218 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
220 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
221 RefPtr<nsFontMetrics> fm;
222 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
223 fontSizeInflation);
225 nscoord defaultRuleThickness, axisHeight;
226 nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
227 gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
228 if (mathFont) {
229 defaultRuleThickness =
230 mathFont->GetMathConstant(gfxFontEntry::FractionRuleThickness,
231 oneDevPixel);
232 } else {
233 GetRuleThickness(aDrawTarget, fm, defaultRuleThickness);
235 GetAxisHeight(aDrawTarget, fm, axisHeight);
237 bool outermostEmbellished = false;
238 if (mEmbellishData.coreFrame) {
239 nsEmbellishData parentData;
240 GetEmbellishDataFrom(GetParent(), parentData);
241 outermostEmbellished = parentData.coreFrame != mEmbellishData.coreFrame;
244 // see if the linethickness attribute is there
245 nsAutoString value;
246 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::linethickness_, value);
247 mLineThickness = CalcLineThickness(presContext, mStyleContext, value,
248 onePixel, defaultRuleThickness,
249 fontSizeInflation);
251 // bevelled attribute
252 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::bevelled_, value);
253 mIsBevelled = value.EqualsLiteral("true");
255 bool displayStyle = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK;
257 if (!mIsBevelled) {
258 mLineRect.height = mLineThickness;
260 // by default, leave at least one-pixel padding at either end, and add
261 // lspace & rspace that may come from <mo> if we are an outermost
262 // embellished container (we fetch values from the core since they may use
263 // units that depend on style data, and style changes could have occurred
264 // in the core since our last visit there)
265 nscoord leftSpace = onePixel;
266 nscoord rightSpace = onePixel;
267 if (outermostEmbellished) {
268 nsEmbellishData coreData;
269 GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
270 leftSpace += StyleVisibility()->mDirection ?
271 coreData.trailingSpace : coreData.leadingSpace;
272 rightSpace += StyleVisibility()->mDirection ?
273 coreData.leadingSpace : coreData.trailingSpace;
276 nscoord actualRuleThickness = mLineThickness;
278 //////////////////
279 // Get shifts
280 nscoord numShift = 0;
281 nscoord denShift = 0;
283 // Rule 15b, App. G, TeXbook
284 nscoord numShift1, numShift2, numShift3;
285 nscoord denShift1, denShift2;
287 GetNumeratorShifts(fm, numShift1, numShift2, numShift3);
288 GetDenominatorShifts(fm, denShift1, denShift2);
290 if (0 == actualRuleThickness) {
291 numShift = displayStyle ? numShift1 : numShift3;
292 denShift = displayStyle ? denShift1 : denShift2;
293 if (mathFont) {
294 numShift = mathFont->
295 GetMathConstant(displayStyle ?
296 gfxFontEntry::StackTopDisplayStyleShiftUp :
297 gfxFontEntry::StackTopShiftUp,
298 oneDevPixel);
299 denShift = mathFont->
300 GetMathConstant(displayStyle ?
301 gfxFontEntry::StackBottomDisplayStyleShiftDown :
302 gfxFontEntry::StackBottomShiftDown,
303 oneDevPixel);
305 } else {
306 numShift = displayStyle ? numShift1 : numShift2;
307 denShift = displayStyle ? denShift1 : denShift2;
308 if (mathFont) {
309 numShift = mathFont->
310 GetMathConstant(displayStyle ?
311 gfxFontEntry::FractionNumeratorDisplayStyleShiftUp :
312 gfxFontEntry::FractionNumeratorShiftUp,
313 oneDevPixel);
314 denShift = mathFont->
315 GetMathConstant(
316 displayStyle ?
317 gfxFontEntry::FractionDenominatorDisplayStyleShiftDown :
318 gfxFontEntry::FractionDenominatorShiftDown,
319 oneDevPixel);
323 if (0 == actualRuleThickness) {
324 // Rule 15c, App. G, TeXbook
326 // min clearance between numerator and denominator
327 nscoord minClearance = displayStyle ?
328 7 * defaultRuleThickness : 3 * defaultRuleThickness;
329 if (mathFont) {
330 minClearance =
331 mathFont->GetMathConstant(displayStyle ?
332 gfxFontEntry::StackDisplayStyleGapMin :
333 gfxFontEntry::StackGapMin,
334 oneDevPixel);
336 nscoord actualClearance =
337 (numShift - bmNum.descent) - (bmDen.ascent - denShift);
338 // actualClearance should be >= minClearance
339 if (actualClearance < minClearance) {
340 nscoord halfGap = (minClearance - actualClearance)/2;
341 numShift += halfGap;
342 denShift += halfGap;
345 else {
346 // Rule 15d, App. G, TeXbook
348 // min clearance between numerator or denominator and middle of bar
350 // TeX has a different interpretation of the thickness.
351 // Try $a \above10pt b$ to see. Here is what TeX does:
352 // minClearance = displayStyle ?
353 // 3 * actualRuleThickness : actualRuleThickness;
355 // we slightly depart from TeX here. We use the defaultRuleThickness instead
356 // of the value coming from the linethickness attribute, i.e., we recover what
357 // TeX does if the user hasn't set linethickness. But when the linethickness
358 // is set, we avoid the wide gap problem.
359 nscoord minClearanceNum = displayStyle ?
360 3 * defaultRuleThickness : defaultRuleThickness + onePixel;
361 nscoord minClearanceDen = minClearanceNum;
362 if (mathFont) {
363 minClearanceNum = mathFont->
364 GetMathConstant(displayStyle ?
365 gfxFontEntry::FractionNumDisplayStyleGapMin :
366 gfxFontEntry::FractionNumeratorGapMin,
367 oneDevPixel);
368 minClearanceDen = mathFont->
369 GetMathConstant(displayStyle ?
370 gfxFontEntry::FractionDenomDisplayStyleGapMin :
371 gfxFontEntry::FractionDenominatorGapMin,
372 oneDevPixel);
375 // adjust numShift to maintain minClearanceNum if needed
376 nscoord actualClearanceNum =
377 (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2);
378 if (actualClearanceNum < minClearanceNum) {
379 numShift += (minClearanceNum - actualClearanceNum);
381 // adjust denShift to maintain minClearanceDen if needed
382 nscoord actualClearanceDen =
383 (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift);
384 if (actualClearanceDen < minClearanceDen) {
385 denShift += (minClearanceDen - actualClearanceDen);
389 //////////////////
390 // Place Children
392 // XXX Need revisiting the width. TeX uses the exact width
393 // e.g. in $$\huge\frac{\displaystyle\int}{i}$$
394 nscoord width = std::max(bmNum.width, bmDen.width);
395 nscoord dxNum = leftSpace + (width - sizeNum.Width())/2;
396 nscoord dxDen = leftSpace + (width - sizeDen.Width())/2;
397 width += leftSpace + rightSpace;
399 // see if the numalign attribute is there
400 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::numalign_, value);
401 if (value.EqualsLiteral("left"))
402 dxNum = leftSpace;
403 else if (value.EqualsLiteral("right"))
404 dxNum = width - rightSpace - sizeNum.Width();
406 // see if the denomalign attribute is there
407 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::denomalign_, value);
408 if (value.EqualsLiteral("left"))
409 dxDen = leftSpace;
410 else if (value.EqualsLiteral("right"))
411 dxDen = width - rightSpace - sizeDen.Width();
413 mBoundingMetrics.rightBearing =
414 std::max(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing);
415 if (mBoundingMetrics.rightBearing < width - rightSpace)
416 mBoundingMetrics.rightBearing = width - rightSpace;
417 mBoundingMetrics.leftBearing =
418 std::min(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing);
419 if (mBoundingMetrics.leftBearing > leftSpace)
420 mBoundingMetrics.leftBearing = leftSpace;
421 mBoundingMetrics.ascent = bmNum.ascent + numShift;
422 mBoundingMetrics.descent = bmDen.descent + denShift;
423 mBoundingMetrics.width = width;
425 aDesiredSize.SetBlockStartAscent(sizeNum.BlockStartAscent() + numShift);
426 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
427 sizeDen.Height() - sizeDen.BlockStartAscent() + denShift;
428 aDesiredSize.Width() = mBoundingMetrics.width;
429 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
431 mReference.x = 0;
432 mReference.y = aDesiredSize.BlockStartAscent();
434 if (aPlaceOrigin) {
435 nscoord dy;
436 // place numerator
437 dy = 0;
438 FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dxNum, dy, 0);
439 // place denominator
440 dy = aDesiredSize.Height() - sizeDen.Height();
441 FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dxDen, dy, 0);
442 // place the fraction bar - dy is top of bar
443 dy = aDesiredSize.BlockStartAscent() - (axisHeight + actualRuleThickness/2);
444 mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace),
445 actualRuleThickness);
447 } else {
448 nscoord numShift = 0.0;
449 nscoord denShift = 0.0;
450 nscoord padding = 3 * defaultRuleThickness;
451 nscoord slashRatio = 3;
453 // Define the constant used in the expression of the maximum width
454 nscoord em = fm->EmHeight();
455 nscoord slashMaxWidthConstant = 2 * em;
457 // For large line thicknesses the minimum slash height is limited to the
458 // largest expected height of a fraction
459 nscoord slashMinHeight = slashRatio *
460 std::min(2 * mLineThickness, slashMaxWidthConstant);
462 nscoord leadingSpace = padding;
463 nscoord trailingSpace = padding;
464 if (outermostEmbellished) {
465 nsEmbellishData coreData;
466 GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
467 leadingSpace += coreData.leadingSpace;
468 trailingSpace += coreData.trailingSpace;
470 nscoord delta;
472 // ___________
473 // | | /
474 // {|-NUMERATOR-| /
475 // {|___________| S
476 // { L
477 // numShift{ A
478 // ------------------------------------------------------- baseline
479 // S _____________ } denShift
480 // H | |}
481 // / |-DENOMINATOR-|}
482 // / |_____________|
485 // first, ensure that the top of the numerator is at least as high as the
486 // top of the denominator (and the reverse for the bottoms)
487 delta = std::max(bmDen.ascent - bmNum.ascent,
488 bmNum.descent - bmDen.descent) / 2;
489 if (delta > 0) {
490 numShift += delta;
491 denShift += delta;
494 if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) {
495 delta = std::min(bmDen.ascent + bmDen.descent,
496 bmNum.ascent + bmNum.descent) / 2;
497 numShift += delta;
498 denShift += delta;
499 } else {
500 nscoord xHeight = fm->XHeight();
501 numShift += xHeight / 2;
502 denShift += xHeight / 4;
505 // Set the ascent/descent of our BoundingMetrics.
506 mBoundingMetrics.ascent = bmNum.ascent + numShift;
507 mBoundingMetrics.descent = bmDen.descent + denShift;
509 // At this point the height of the slash is
510 // mBoundingMetrics.ascent + mBoundingMetrics.descent
511 // Ensure that it is greater than slashMinHeight
512 delta = (slashMinHeight -
513 (mBoundingMetrics.ascent + mBoundingMetrics.descent)) / 2;
514 if (delta > 0) {
515 mBoundingMetrics.ascent += delta;
516 mBoundingMetrics.descent += delta;
519 // Set the width of the slash
520 if (aWidthOnly) {
521 mLineRect.width = mLineThickness + slashMaxWidthConstant;
522 } else {
523 mLineRect.width = mLineThickness +
524 std::min(slashMaxWidthConstant,
525 (mBoundingMetrics.ascent + mBoundingMetrics.descent) /
526 slashRatio);
529 // Set horizontal bounding metrics
530 if (StyleVisibility()->mDirection) {
531 mBoundingMetrics.leftBearing = trailingSpace + bmDen.leftBearing;
532 mBoundingMetrics.rightBearing = trailingSpace + bmDen.width + mLineRect.width + bmNum.rightBearing;
533 } else {
534 mBoundingMetrics.leftBearing = leadingSpace + bmNum.leftBearing;
535 mBoundingMetrics.rightBearing = leadingSpace + bmNum.width + mLineRect.width + bmDen.rightBearing;
537 mBoundingMetrics.width =
538 leadingSpace + bmNum.width + mLineRect.width + bmDen.width +
539 trailingSpace;
541 // Set aDesiredSize
542 aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + padding);
543 aDesiredSize.Height() =
544 mBoundingMetrics.ascent + mBoundingMetrics.descent + 2 * padding;
545 aDesiredSize.Width() = mBoundingMetrics.width;
546 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
548 mReference.x = 0;
549 mReference.y = aDesiredSize.BlockStartAscent();
551 if (aPlaceOrigin) {
552 nscoord dx, dy;
554 // place numerator
555 dx = MirrorIfRTL(aDesiredSize.Width(), sizeNum.Width(),
556 leadingSpace);
557 dy = aDesiredSize.BlockStartAscent() - numShift - sizeNum.BlockStartAscent();
558 FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dx, dy, 0);
560 // place the fraction bar
561 dx = MirrorIfRTL(aDesiredSize.Width(), mLineRect.width,
562 leadingSpace + bmNum.width);
563 dy = aDesiredSize.BlockStartAscent() - mBoundingMetrics.ascent;
564 mLineRect.SetRect(dx, dy,
565 mLineRect.width, aDesiredSize.Height() - 2 * padding);
567 // place denominator
568 dx = MirrorIfRTL(aDesiredSize.Width(), sizeDen.Width(),
569 leadingSpace + bmNum.width + mLineRect.width);
570 dy = aDesiredSize.BlockStartAscent() + denShift - sizeDen.BlockStartAscent();
571 FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dx, dy, 0);
576 return NS_OK;
579 class nsDisplayMathMLSlash : public nsDisplayItem {
580 public:
581 nsDisplayMathMLSlash(nsDisplayListBuilder* aBuilder,
582 nsIFrame* aFrame, const nsRect& aRect,
583 nscoord aThickness, bool aRTL)
584 : nsDisplayItem(aBuilder, aFrame), mRect(aRect), mThickness(aThickness),
585 mRTL(aRTL) {
586 MOZ_COUNT_CTOR(nsDisplayMathMLSlash);
588 #ifdef NS_BUILD_REFCNT_LOGGING
589 virtual ~nsDisplayMathMLSlash() {
590 MOZ_COUNT_DTOR(nsDisplayMathMLSlash);
592 #endif
594 virtual void Paint(nsDisplayListBuilder* aBuilder,
595 nsRenderingContext* aCtx) override;
596 NS_DISPLAY_DECL_NAME("MathMLSlash", TYPE_MATHML_SLASH)
598 private:
599 nsRect mRect;
600 nscoord mThickness;
601 bool mRTL;
604 void nsDisplayMathMLSlash::Paint(nsDisplayListBuilder* aBuilder,
605 nsRenderingContext* aCtx)
607 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
609 // get the gfxRect
610 nsPresContext* presContext = mFrame->PresContext();
611 Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
612 presContext->AppUnitsPerDevPixel());
614 ColorPattern color(ToDeviceColor(
615 mFrame->GetVisitedDependentColor(eCSSProperty_color)));
617 // draw the slash as a parallelogram
618 Point delta = Point(presContext->AppUnitsToGfxUnits(mThickness), 0);
619 RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
620 if (mRTL) {
621 builder->MoveTo(rect.TopLeft());
622 builder->LineTo(rect.TopLeft() + delta);
623 builder->LineTo(rect.BottomRight());
624 builder->LineTo(rect.BottomRight() - delta);
625 } else {
626 builder->MoveTo(rect.BottomLeft());
627 builder->LineTo(rect.BottomLeft() + delta);
628 builder->LineTo(rect.TopRight());
629 builder->LineTo(rect.TopRight() - delta);
631 RefPtr<Path> path = builder->Finish();
632 aDrawTarget.Fill(path, color);
635 void
636 nsMathMLmfracFrame::DisplaySlash(nsDisplayListBuilder* aBuilder,
637 nsIFrame* aFrame, const nsRect& aRect,
638 nscoord aThickness,
639 const nsDisplayListSet& aLists) {
640 if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
641 return;
643 aLists.Content()->AppendNewToTop(new (aBuilder)
644 nsDisplayMathMLSlash(aBuilder, aFrame, aRect, aThickness,
645 StyleVisibility()->mDirection));