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/. */
6 #include "nsMathMLmrootFrame.h"
7 #include "nsPresContext.h"
8 #include "nsRenderingContext.h"
11 using namespace mozilla
;
14 // <mroot> -- form a radical - implementation
17 // additional style context to be used by our MathMLChar.
18 #define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
20 static const char16_t kSqrChar
= char16_t(0x221A);
23 NS_NewMathMLmrootFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
25 return new (aPresShell
) nsMathMLmrootFrame(aContext
);
28 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame
)
30 nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext
* aContext
) :
31 nsMathMLContainerFrame(aContext
),
37 nsMathMLmrootFrame::~nsMathMLmrootFrame()
42 nsMathMLmrootFrame::Init(nsIContent
* aContent
,
43 nsContainerFrame
* aParent
,
44 nsIFrame
* aPrevInFlow
)
46 nsMathMLContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
48 nsPresContext
*presContext
= PresContext();
50 // No need to track the style context given to our MathML char.
51 // The Style System will use Get/SetAdditionalStyleContext() to keep it
52 // up-to-date if dynamic changes arise.
53 nsAutoString sqrChar
; sqrChar
.Assign(kSqrChar
);
54 mSqrChar
.SetData(presContext
, sqrChar
);
55 ResolveMathMLCharStyle(presContext
, mContent
, mStyleContext
, &mSqrChar
);
59 nsMathMLmrootFrame::TransmitAutomaticData()
62 // The <mroot> element increments scriptlevel by 2, and sets displaystyle to
63 // "false", within index, but leaves both attributes unchanged within base.
64 // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
65 UpdatePresentationDataFromChildAt(1, 1,
67 NS_MATHML_COMPRESSED
);
68 UpdatePresentationDataFromChildAt(0, 0,
69 NS_MATHML_COMPRESSED
, NS_MATHML_COMPRESSED
);
71 PropagateFrameFlagFor(mFrames
.LastChild(),
72 NS_FRAME_MATHML_SCRIPT_DESCENDANT
);
78 nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
79 const nsRect
& aDirtyRect
,
80 const nsDisplayListSet
& aLists
)
83 // paint the content we are square-rooting
84 nsMathMLContainerFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
87 // paint the sqrt symbol
88 if (!NS_MATHML_HAS_ERROR(mPresentationData
.flags
)) {
89 mSqrChar
.Display(aBuilder
, this, aLists
, 0);
91 DisplayBar(aBuilder
, this, mBarRect
, aLists
);
93 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
96 mSqrChar
.GetRect(rect
);
98 mSqrChar
.GetBoundingMetrics(bm
);
99 DisplayBoundingMetrics(aBuilder
, this, rect
.TopLeft(), bm
, aLists
);
105 nsMathMLmrootFrame::GetRadicalXOffsets(nscoord aIndexWidth
, nscoord aSqrWidth
,
106 nsFontMetrics
* aFontMetrics
,
107 nscoord
* aIndexOffset
,
110 // The index is tucked in closer to the radical while making sure
111 // that the kern does not make the index and radical collide
112 nscoord dxIndex
, dxSqr
;
113 nscoord xHeight
= aFontMetrics
->XHeight();
114 nscoord indexRadicalKern
= NSToCoordRound(1.35f
* xHeight
);
115 nscoord oneDevPixel
= aFontMetrics
->AppUnitsPerDevPixel();
116 gfxFont
* mathFont
= aFontMetrics
->GetThebesFontGroup()->GetFirstMathFont();
119 mathFont
->GetMathConstant(gfxFontEntry::RadicalKernAfterDegree
,
121 indexRadicalKern
= -indexRadicalKern
;
123 if (indexRadicalKern
> aIndexWidth
) {
124 dxIndex
= indexRadicalKern
- aIndexWidth
;
129 dxSqr
= aIndexWidth
- indexRadicalKern
;
133 // add some kern before the radical index
134 nscoord indexRadicalKernBefore
= 0;
135 indexRadicalKernBefore
=
136 mathFont
->GetMathConstant(gfxFontEntry::RadicalKernBeforeDegree
,
138 dxIndex
+= indexRadicalKernBefore
;
139 dxSqr
+= indexRadicalKernBefore
;
141 // avoid collision by leaving a minimum space between index and radical
142 nscoord minimumClearance
= aSqrWidth
/ 2;
143 if (dxIndex
+ aIndexWidth
+ minimumClearance
> dxSqr
+ aSqrWidth
) {
144 if (aIndexWidth
+ minimumClearance
< aSqrWidth
) {
145 dxIndex
= aSqrWidth
- (aIndexWidth
+ minimumClearance
);
150 dxSqr
= (aIndexWidth
+ minimumClearance
) - aSqrWidth
;
156 *aIndexOffset
= dxIndex
;
162 nsMathMLmrootFrame::Reflow(nsPresContext
* aPresContext
,
163 nsHTMLReflowMetrics
& aDesiredSize
,
164 const nsHTMLReflowState
& aReflowState
,
165 nsReflowStatus
& aStatus
)
167 nsReflowStatus childStatus
;
169 aDesiredSize
.ClearSize();
170 aDesiredSize
.SetBlockStartAscent(0);
172 nsBoundingMetrics bmSqr
, bmBase
, bmIndex
;
173 nsRenderingContext
& renderingContext
= *aReflowState
.rendContext
;
179 nsIFrame
* baseFrame
= nullptr;
180 nsIFrame
* indexFrame
= nullptr;
181 nsHTMLReflowMetrics
baseSize(aReflowState
);
182 nsHTMLReflowMetrics
indexSize(aReflowState
);
183 nsIFrame
* childFrame
= mFrames
.FirstChild();
185 // ask our children to compute their bounding metrics
186 nsHTMLReflowMetrics
childDesiredSize(aReflowState
,
188 | NS_REFLOW_CALC_BOUNDING_METRICS
);
189 WritingMode wm
= childFrame
->GetWritingMode();
190 LogicalSize availSize
= aReflowState
.ComputedSize(wm
);
191 availSize
.BSize(wm
) = NS_UNCONSTRAINEDSIZE
;
192 nsHTMLReflowState
childReflowState(aPresContext
, aReflowState
,
193 childFrame
, availSize
);
194 ReflowChild(childFrame
, aPresContext
,
195 childDesiredSize
, childReflowState
, childStatus
);
196 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
199 baseFrame
= childFrame
;
200 baseSize
= childDesiredSize
;
201 bmBase
= childDesiredSize
.mBoundingMetrics
;
203 else if (1 == count
) {
205 indexFrame
= childFrame
;
206 indexSize
= childDesiredSize
;
207 bmIndex
= childDesiredSize
.mBoundingMetrics
;
210 childFrame
= childFrame
->GetNextSibling();
213 // report an error, encourage people to get their markups in order
214 ReportChildCountError();
215 ReflowError(renderingContext
, aDesiredSize
);
216 aStatus
= NS_FRAME_COMPLETE
;
217 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
218 // Call DidReflow() for the child frames we successfully did reflow.
219 DidReflowChildren(mFrames
.FirstChild(), childFrame
);
224 // Prepare the radical symbol and the overline bar
226 nsRefPtr
<nsFontMetrics
> fm
;
227 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
));
228 renderingContext
.SetFont(fm
);
230 nscoord ruleThickness
, leading
, psi
;
231 GetRadicalParameters(fm
, StyleFont()->mMathDisplay
==
232 NS_MATHML_DISPLAYSTYLE_BLOCK
,
233 ruleThickness
, leading
, psi
);
235 // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
237 nsBoundingMetrics bmOne
= renderingContext
.GetBoundingMetrics(&one
, 1);
238 if (bmOne
.ascent
> bmBase
.ascent
)
239 psi
+= bmOne
.ascent
- bmBase
.ascent
;
241 // make sure that the rule appears on on screen
242 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
243 if (ruleThickness
< onePixel
) {
244 ruleThickness
= onePixel
;
247 // adjust clearance psi to get an exact number of pixels -- this
248 // gives a nicer & uniform look on stacked radicals (bug 130282)
249 nscoord delta
= psi
% onePixel
;
251 psi
+= onePixel
- delta
; // round up
253 // Stretch the radical symbol to the appropriate height if it is not big enough.
254 nsBoundingMetrics contSize
= bmBase
;
255 contSize
.descent
= bmBase
.ascent
+ bmBase
.descent
+ psi
;
256 contSize
.ascent
= ruleThickness
;
258 // height(radical) should be >= height(base) + psi + ruleThickness
259 nsBoundingMetrics radicalSize
;
260 mSqrChar
.Stretch(aPresContext
, renderingContext
,
261 NS_STRETCH_DIRECTION_VERTICAL
,
262 contSize
, radicalSize
,
264 StyleVisibility()->mDirection
);
265 // radicalSize have changed at this point, and should match with
266 // the bounding metrics of the char
267 mSqrChar
.GetBoundingMetrics(bmSqr
);
269 // Update the desired size for the container (like msqrt, index is not yet included)
270 // the baseline will be that of the base.
271 mBoundingMetrics
.ascent
= bmBase
.ascent
+ psi
+ ruleThickness
;
272 mBoundingMetrics
.descent
=
273 std::max(bmBase
.descent
,
274 (bmSqr
.ascent
+ bmSqr
.descent
- mBoundingMetrics
.ascent
));
275 mBoundingMetrics
.width
= bmSqr
.width
+ bmBase
.width
;
276 mBoundingMetrics
.leftBearing
= bmSqr
.leftBearing
;
277 mBoundingMetrics
.rightBearing
= bmSqr
.width
+
278 std::max(bmBase
.width
, bmBase
.rightBearing
); // take also care of the rule
280 aDesiredSize
.SetBlockStartAscent(mBoundingMetrics
.ascent
+ leading
);
281 aDesiredSize
.Height() = aDesiredSize
.BlockStartAscent() +
282 std::max(baseSize
.Height() - baseSize
.BlockStartAscent(),
283 mBoundingMetrics
.descent
+ ruleThickness
);
284 aDesiredSize
.Width() = mBoundingMetrics
.width
;
287 // Re-adjust the desired size to include the index.
289 // the index is raised by some fraction of the height
290 // of the radical, see \mroot macro in App. B, TexBook
291 float raiseIndexPercent
= 0.6f
;
292 gfxFont
* mathFont
= fm
->GetThebesFontGroup()->GetFirstMathFont();
295 mathFont
->GetMathConstant(gfxFontEntry::RadicalDegreeBottomRaisePercent
);
297 nscoord raiseIndexDelta
= NSToCoordRound(raiseIndexPercent
*
298 (bmSqr
.ascent
+ bmSqr
.descent
));
299 nscoord indexRaisedAscent
= mBoundingMetrics
.ascent
// top of radical
300 - (bmSqr
.ascent
+ bmSqr
.descent
) // to bottom of radical
301 + raiseIndexDelta
+ bmIndex
.ascent
+ bmIndex
.descent
; // to top of raised index
303 nscoord indexClearance
= 0;
304 if (mBoundingMetrics
.ascent
< indexRaisedAscent
) {
306 indexRaisedAscent
- mBoundingMetrics
.ascent
; // excess gap introduced by a tall index
307 mBoundingMetrics
.ascent
= indexRaisedAscent
;
308 nscoord descent
= aDesiredSize
.Height() - aDesiredSize
.BlockStartAscent();
309 aDesiredSize
.SetBlockStartAscent(mBoundingMetrics
.ascent
+ leading
);
310 aDesiredSize
.Height() = aDesiredSize
.BlockStartAscent() + descent
;
313 nscoord dxIndex
, dxSqr
;
314 GetRadicalXOffsets(bmIndex
.width
, bmSqr
.width
, fm
, &dxIndex
, &dxSqr
);
316 mBoundingMetrics
.width
= dxSqr
+ bmSqr
.width
+ bmBase
.width
;
317 mBoundingMetrics
.leftBearing
=
318 std::min(dxIndex
+ bmIndex
.leftBearing
, dxSqr
+ bmSqr
.leftBearing
);
319 mBoundingMetrics
.rightBearing
= dxSqr
+ bmSqr
.width
+
320 std::max(bmBase
.width
, bmBase
.rightBearing
);
322 aDesiredSize
.Width() = mBoundingMetrics
.width
;
323 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
324 GatherAndStoreOverflow(&aDesiredSize
);
327 nscoord dx
= dxIndex
;
328 nscoord dy
= aDesiredSize
.BlockStartAscent() -
329 (indexRaisedAscent
+ indexSize
.BlockStartAscent() - bmIndex
.ascent
);
330 FinishReflowChild(indexFrame
, aPresContext
, indexSize
, nullptr,
331 MirrorIfRTL(aDesiredSize
.Width(), indexSize
.Width(), dx
),
334 // place the radical symbol and the radical bar
336 dy
= indexClearance
+ leading
; // leave a leading at the top
337 mSqrChar
.SetRect(nsRect(MirrorIfRTL(aDesiredSize
.Width(), bmSqr
.width
, dx
),
338 dy
, bmSqr
.width
, bmSqr
.ascent
+ bmSqr
.descent
));
340 mBarRect
.SetRect(MirrorIfRTL(aDesiredSize
.Width(), bmBase
.width
, dx
),
341 dy
, bmBase
.width
, ruleThickness
);
344 dy
= aDesiredSize
.BlockStartAscent() - baseSize
.BlockStartAscent();
345 FinishReflowChild(baseFrame
, aPresContext
, baseSize
, nullptr,
346 MirrorIfRTL(aDesiredSize
.Width(), baseSize
.Width(), dx
),
350 mReference
.y
= aDesiredSize
.BlockStartAscent();
352 aStatus
= NS_FRAME_COMPLETE
;
353 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
357 nsMathMLmrootFrame::GetIntrinsicISizeMetrics(nsRenderingContext
* aRenderingContext
, nsHTMLReflowMetrics
& aDesiredSize
)
359 nsIFrame
* baseFrame
= mFrames
.FirstChild();
360 nsIFrame
* indexFrame
= nullptr;
362 indexFrame
= baseFrame
->GetNextSibling();
363 if (!indexFrame
|| indexFrame
->GetNextSibling()) {
364 ReflowError(*aRenderingContext
, aDesiredSize
);
369 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, baseFrame
,
370 nsLayoutUtils::PREF_ISIZE
);
372 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, indexFrame
,
373 nsLayoutUtils::PREF_ISIZE
);
374 nscoord sqrWidth
= mSqrChar
.GetMaxWidth(PresContext(), *aRenderingContext
);
377 nsRefPtr
<nsFontMetrics
> fm
;
378 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
));
379 GetRadicalXOffsets(indexWidth
, sqrWidth
, fm
, nullptr, &dxSqr
);
381 nscoord width
= dxSqr
+ sqrWidth
+ baseWidth
;
383 aDesiredSize
.Width() = width
;
384 aDesiredSize
.mBoundingMetrics
.width
= width
;
385 aDesiredSize
.mBoundingMetrics
.leftBearing
= 0;
386 aDesiredSize
.mBoundingMetrics
.rightBearing
= width
;
389 // ----------------------
390 // the Style System will use these to pass the proper style context to our MathMLChar
392 nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex
) const
395 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX
:
396 return mSqrChar
.GetStyleContext();
404 nsMathMLmrootFrame::SetAdditionalStyleContext(int32_t aIndex
,
405 nsStyleContext
* aStyleContext
)
408 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX
:
409 mSqrChar
.SetStyleContext(aStyleContext
);