Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | |
michael@0 | 7 | #include "nsMathMLmfracFrame.h" |
michael@0 | 8 | #include "nsPresContext.h" |
michael@0 | 9 | #include "nsRenderingContext.h" |
michael@0 | 10 | #include "nsDisplayList.h" |
michael@0 | 11 | #include "gfxContext.h" |
michael@0 | 12 | #include "nsMathMLElement.h" |
michael@0 | 13 | #include <algorithm> |
michael@0 | 14 | |
michael@0 | 15 | // |
michael@0 | 16 | // <mfrac> -- form a fraction from two subexpressions - implementation |
michael@0 | 17 | // |
michael@0 | 18 | |
michael@0 | 19 | // various fraction line thicknesses (multiplicative values of the default rule thickness) |
michael@0 | 20 | |
michael@0 | 21 | #define THIN_FRACTION_LINE 0.5f |
michael@0 | 22 | #define THIN_FRACTION_LINE_MINIMUM_PIXELS 1 // minimum of 1 pixel |
michael@0 | 23 | |
michael@0 | 24 | #define THICK_FRACTION_LINE 2.0f |
michael@0 | 25 | #define THICK_FRACTION_LINE_MINIMUM_PIXELS 2 // minimum of 2 pixels |
michael@0 | 26 | |
michael@0 | 27 | nsIFrame* |
michael@0 | 28 | NS_NewMathMLmfracFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
michael@0 | 29 | { |
michael@0 | 30 | return new (aPresShell) nsMathMLmfracFrame(aContext); |
michael@0 | 31 | } |
michael@0 | 32 | |
michael@0 | 33 | NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfracFrame) |
michael@0 | 34 | |
michael@0 | 35 | nsMathMLmfracFrame::~nsMathMLmfracFrame() |
michael@0 | 36 | { |
michael@0 | 37 | } |
michael@0 | 38 | |
michael@0 | 39 | eMathMLFrameType |
michael@0 | 40 | nsMathMLmfracFrame::GetMathMLFrameType() |
michael@0 | 41 | { |
michael@0 | 42 | // frac is "inner" in TeXBook, Appendix G, rule 15e. See also page 170. |
michael@0 | 43 | return eMathMLFrameType_Inner; |
michael@0 | 44 | } |
michael@0 | 45 | |
michael@0 | 46 | uint8_t |
michael@0 | 47 | nsMathMLmfracFrame::ScriptIncrement(nsIFrame* aFrame) |
michael@0 | 48 | { |
michael@0 | 49 | if (!StyleFont()->mMathDisplay && |
michael@0 | 50 | aFrame && (mFrames.FirstChild() == aFrame || |
michael@0 | 51 | mFrames.LastChild() == aFrame)) { |
michael@0 | 52 | return 1; |
michael@0 | 53 | } |
michael@0 | 54 | return 0; |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | NS_IMETHODIMP |
michael@0 | 58 | nsMathMLmfracFrame::TransmitAutomaticData() |
michael@0 | 59 | { |
michael@0 | 60 | // The TeXbook (Ch 17. p.141) says the numerator inherits the compression |
michael@0 | 61 | // while the denominator is compressed |
michael@0 | 62 | UpdatePresentationDataFromChildAt(1, 1, |
michael@0 | 63 | NS_MATHML_COMPRESSED, |
michael@0 | 64 | NS_MATHML_COMPRESSED); |
michael@0 | 65 | |
michael@0 | 66 | // If displaystyle is false, then scriptlevel is incremented, so notify the |
michael@0 | 67 | // children of this. |
michael@0 | 68 | if (!StyleFont()->mMathDisplay) { |
michael@0 | 69 | PropagateFrameFlagFor(mFrames.FirstChild(), |
michael@0 | 70 | NS_FRAME_MATHML_SCRIPT_DESCENDANT); |
michael@0 | 71 | PropagateFrameFlagFor(mFrames.LastChild(), |
michael@0 | 72 | NS_FRAME_MATHML_SCRIPT_DESCENDANT); |
michael@0 | 73 | } |
michael@0 | 74 | |
michael@0 | 75 | // if our numerator is an embellished operator, let its state bubble to us |
michael@0 | 76 | GetEmbellishDataFrom(mFrames.FirstChild(), mEmbellishData); |
michael@0 | 77 | if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) { |
michael@0 | 78 | // even when embellished, we need to record that <mfrac> won't fire |
michael@0 | 79 | // Stretch() on its embellished child |
michael@0 | 80 | mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED; |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | return NS_OK; |
michael@0 | 84 | } |
michael@0 | 85 | |
michael@0 | 86 | nscoord |
michael@0 | 87 | nsMathMLmfracFrame::CalcLineThickness(nsPresContext* aPresContext, |
michael@0 | 88 | nsStyleContext* aStyleContext, |
michael@0 | 89 | nsString& aThicknessAttribute, |
michael@0 | 90 | nscoord onePixel, |
michael@0 | 91 | nscoord aDefaultRuleThickness) |
michael@0 | 92 | { |
michael@0 | 93 | nscoord defaultThickness = aDefaultRuleThickness; |
michael@0 | 94 | nscoord lineThickness = aDefaultRuleThickness; |
michael@0 | 95 | nscoord minimumThickness = onePixel; |
michael@0 | 96 | |
michael@0 | 97 | // linethickness |
michael@0 | 98 | // |
michael@0 | 99 | // "Specifies the thickness of the horizontal 'fraction bar', or 'rule'. The |
michael@0 | 100 | // default value is 'medium', 'thin' is thinner, but visible, 'thick' is |
michael@0 | 101 | // thicker; the exact thickness of these is left up to the rendering agent." |
michael@0 | 102 | // |
michael@0 | 103 | // values: length | "thin" | "medium" | "thick" |
michael@0 | 104 | // default: medium |
michael@0 | 105 | // |
michael@0 | 106 | if (!aThicknessAttribute.IsEmpty()) { |
michael@0 | 107 | if (aThicknessAttribute.EqualsLiteral("thin")) { |
michael@0 | 108 | lineThickness = NSToCoordFloor(defaultThickness * THIN_FRACTION_LINE); |
michael@0 | 109 | minimumThickness = onePixel * THIN_FRACTION_LINE_MINIMUM_PIXELS; |
michael@0 | 110 | // should visually decrease by at least one pixel, if default is not a pixel |
michael@0 | 111 | if (defaultThickness > onePixel && lineThickness > defaultThickness - onePixel) |
michael@0 | 112 | lineThickness = defaultThickness - onePixel; |
michael@0 | 113 | } |
michael@0 | 114 | else if (aThicknessAttribute.EqualsLiteral("medium")) { |
michael@0 | 115 | // medium is default |
michael@0 | 116 | } |
michael@0 | 117 | else if (aThicknessAttribute.EqualsLiteral("thick")) { |
michael@0 | 118 | lineThickness = NSToCoordCeil(defaultThickness * THICK_FRACTION_LINE); |
michael@0 | 119 | minimumThickness = onePixel * THICK_FRACTION_LINE_MINIMUM_PIXELS; |
michael@0 | 120 | // should visually increase by at least one pixel |
michael@0 | 121 | if (lineThickness < defaultThickness + onePixel) |
michael@0 | 122 | lineThickness = defaultThickness + onePixel; |
michael@0 | 123 | } |
michael@0 | 124 | else { |
michael@0 | 125 | // length value |
michael@0 | 126 | lineThickness = defaultThickness; |
michael@0 | 127 | ParseNumericValue(aThicknessAttribute, &lineThickness, |
michael@0 | 128 | nsMathMLElement::PARSE_ALLOW_UNITLESS, |
michael@0 | 129 | aPresContext, aStyleContext); |
michael@0 | 130 | } |
michael@0 | 131 | } |
michael@0 | 132 | |
michael@0 | 133 | // use minimum if the lineThickness is a non-zero value less than minimun |
michael@0 | 134 | if (lineThickness && lineThickness < minimumThickness) |
michael@0 | 135 | lineThickness = minimumThickness; |
michael@0 | 136 | |
michael@0 | 137 | return lineThickness; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | void |
michael@0 | 141 | nsMathMLmfracFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 142 | const nsRect& aDirtyRect, |
michael@0 | 143 | const nsDisplayListSet& aLists) |
michael@0 | 144 | { |
michael@0 | 145 | ///////////// |
michael@0 | 146 | // paint the numerator and denominator |
michael@0 | 147 | nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); |
michael@0 | 148 | |
michael@0 | 149 | ///////////// |
michael@0 | 150 | // paint the fraction line |
michael@0 | 151 | if (mIsBevelled) { |
michael@0 | 152 | DisplaySlash(aBuilder, this, mLineRect, mLineThickness, aLists); |
michael@0 | 153 | } else { |
michael@0 | 154 | DisplayBar(aBuilder, this, mLineRect, aLists); |
michael@0 | 155 | } |
michael@0 | 156 | } |
michael@0 | 157 | |
michael@0 | 158 | /* virtual */ nsresult |
michael@0 | 159 | nsMathMLmfracFrame::MeasureForWidth(nsRenderingContext& aRenderingContext, |
michael@0 | 160 | nsHTMLReflowMetrics& aDesiredSize) |
michael@0 | 161 | { |
michael@0 | 162 | return PlaceInternal(aRenderingContext, |
michael@0 | 163 | false, |
michael@0 | 164 | aDesiredSize, |
michael@0 | 165 | true); |
michael@0 | 166 | } |
michael@0 | 167 | |
michael@0 | 168 | nscoord |
michael@0 | 169 | nsMathMLmfracFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize) |
michael@0 | 170 | { |
michael@0 | 171 | nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize); |
michael@0 | 172 | if (!gap) return 0; |
michael@0 | 173 | |
michael@0 | 174 | mLineRect.MoveBy(gap, 0); |
michael@0 | 175 | return gap; |
michael@0 | 176 | } |
michael@0 | 177 | |
michael@0 | 178 | /* virtual */ nsresult |
michael@0 | 179 | nsMathMLmfracFrame::Place(nsRenderingContext& aRenderingContext, |
michael@0 | 180 | bool aPlaceOrigin, |
michael@0 | 181 | nsHTMLReflowMetrics& aDesiredSize) |
michael@0 | 182 | { |
michael@0 | 183 | return PlaceInternal(aRenderingContext, |
michael@0 | 184 | aPlaceOrigin, |
michael@0 | 185 | aDesiredSize, |
michael@0 | 186 | false); |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | nsresult |
michael@0 | 190 | nsMathMLmfracFrame::PlaceInternal(nsRenderingContext& aRenderingContext, |
michael@0 | 191 | bool aPlaceOrigin, |
michael@0 | 192 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 193 | bool aWidthOnly) |
michael@0 | 194 | { |
michael@0 | 195 | //////////////////////////////////// |
michael@0 | 196 | // Get the children's desired sizes |
michael@0 | 197 | nsBoundingMetrics bmNum, bmDen; |
michael@0 | 198 | nsHTMLReflowMetrics sizeNum(aDesiredSize.GetWritingMode()); |
michael@0 | 199 | nsHTMLReflowMetrics sizeDen(aDesiredSize.GetWritingMode()); |
michael@0 | 200 | nsIFrame* frameDen = nullptr; |
michael@0 | 201 | nsIFrame* frameNum = mFrames.FirstChild(); |
michael@0 | 202 | if (frameNum) |
michael@0 | 203 | frameDen = frameNum->GetNextSibling(); |
michael@0 | 204 | if (!frameNum || !frameDen || frameDen->GetNextSibling()) { |
michael@0 | 205 | // report an error, encourage people to get their markups in order |
michael@0 | 206 | if (aPlaceOrigin) { |
michael@0 | 207 | ReportChildCountError(); |
michael@0 | 208 | } |
michael@0 | 209 | return ReflowError(aRenderingContext, aDesiredSize); |
michael@0 | 210 | } |
michael@0 | 211 | GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum); |
michael@0 | 212 | GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen); |
michael@0 | 213 | |
michael@0 | 214 | nsPresContext* presContext = PresContext(); |
michael@0 | 215 | nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); |
michael@0 | 216 | |
michael@0 | 217 | nsRefPtr<nsFontMetrics> fm; |
michael@0 | 218 | nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); |
michael@0 | 219 | aRenderingContext.SetFont(fm); |
michael@0 | 220 | |
michael@0 | 221 | nscoord defaultRuleThickness, axisHeight; |
michael@0 | 222 | GetRuleThickness(aRenderingContext, fm, defaultRuleThickness); |
michael@0 | 223 | GetAxisHeight(aRenderingContext, fm, axisHeight); |
michael@0 | 224 | |
michael@0 | 225 | bool outermostEmbellished = false; |
michael@0 | 226 | if (mEmbellishData.coreFrame) { |
michael@0 | 227 | nsEmbellishData parentData; |
michael@0 | 228 | GetEmbellishDataFrom(mParent, parentData); |
michael@0 | 229 | outermostEmbellished = parentData.coreFrame != mEmbellishData.coreFrame; |
michael@0 | 230 | } |
michael@0 | 231 | |
michael@0 | 232 | // see if the linethickness attribute is there |
michael@0 | 233 | nsAutoString value; |
michael@0 | 234 | mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::linethickness_, value); |
michael@0 | 235 | mLineThickness = CalcLineThickness(presContext, mStyleContext, value, |
michael@0 | 236 | onePixel, defaultRuleThickness); |
michael@0 | 237 | |
michael@0 | 238 | // bevelled attribute |
michael@0 | 239 | mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::bevelled_, value); |
michael@0 | 240 | mIsBevelled = value.EqualsLiteral("true"); |
michael@0 | 241 | |
michael@0 | 242 | if (!mIsBevelled) { |
michael@0 | 243 | mLineRect.height = mLineThickness; |
michael@0 | 244 | |
michael@0 | 245 | // by default, leave at least one-pixel padding at either end, and add |
michael@0 | 246 | // lspace & rspace that may come from <mo> if we are an outermost |
michael@0 | 247 | // embellished container (we fetch values from the core since they may use |
michael@0 | 248 | // units that depend on style data, and style changes could have occurred |
michael@0 | 249 | // in the core since our last visit there) |
michael@0 | 250 | nscoord leftSpace = onePixel; |
michael@0 | 251 | nscoord rightSpace = onePixel; |
michael@0 | 252 | if (outermostEmbellished) { |
michael@0 | 253 | nsEmbellishData coreData; |
michael@0 | 254 | GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData); |
michael@0 | 255 | leftSpace += StyleVisibility()->mDirection ? |
michael@0 | 256 | coreData.trailingSpace : coreData.leadingSpace; |
michael@0 | 257 | rightSpace += StyleVisibility()->mDirection ? |
michael@0 | 258 | coreData.leadingSpace : coreData.trailingSpace; |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | ////////////////// |
michael@0 | 262 | // Get shifts |
michael@0 | 263 | nscoord numShift = 0; |
michael@0 | 264 | nscoord denShift = 0; |
michael@0 | 265 | |
michael@0 | 266 | // Rule 15b, App. G, TeXbook |
michael@0 | 267 | nscoord numShift1, numShift2, numShift3; |
michael@0 | 268 | nscoord denShift1, denShift2; |
michael@0 | 269 | |
michael@0 | 270 | GetNumeratorShifts(fm, numShift1, numShift2, numShift3); |
michael@0 | 271 | GetDenominatorShifts(fm, denShift1, denShift2); |
michael@0 | 272 | if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) { |
michael@0 | 273 | // C > T |
michael@0 | 274 | numShift = numShift1; |
michael@0 | 275 | denShift = denShift1; |
michael@0 | 276 | } |
michael@0 | 277 | else { |
michael@0 | 278 | numShift = (0 < mLineRect.height) ? numShift2 : numShift3; |
michael@0 | 279 | denShift = denShift2; |
michael@0 | 280 | } |
michael@0 | 281 | |
michael@0 | 282 | nscoord minClearance = 0; |
michael@0 | 283 | nscoord actualClearance = 0; |
michael@0 | 284 | |
michael@0 | 285 | nscoord actualRuleThickness = mLineThickness; |
michael@0 | 286 | |
michael@0 | 287 | if (0 == actualRuleThickness) { |
michael@0 | 288 | // Rule 15c, App. G, TeXbook |
michael@0 | 289 | |
michael@0 | 290 | // min clearance between numerator and denominator |
michael@0 | 291 | minClearance = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK ? |
michael@0 | 292 | 7 * defaultRuleThickness : 3 * defaultRuleThickness; |
michael@0 | 293 | actualClearance = |
michael@0 | 294 | (numShift - bmNum.descent) - (bmDen.ascent - denShift); |
michael@0 | 295 | // actualClearance should be >= minClearance |
michael@0 | 296 | if (actualClearance < minClearance) { |
michael@0 | 297 | nscoord halfGap = (minClearance - actualClearance)/2; |
michael@0 | 298 | numShift += halfGap; |
michael@0 | 299 | denShift += halfGap; |
michael@0 | 300 | } |
michael@0 | 301 | } |
michael@0 | 302 | else { |
michael@0 | 303 | // Rule 15d, App. G, TeXbook |
michael@0 | 304 | |
michael@0 | 305 | // min clearance between numerator or denominator and middle of bar |
michael@0 | 306 | |
michael@0 | 307 | // TeX has a different interpretation of the thickness. |
michael@0 | 308 | // Try $a \above10pt b$ to see. Here is what TeX does: |
michael@0 | 309 | // minClearance = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK |
michael@0 | 310 | // ? 3 * actualRuleThickness : actualRuleThickness; |
michael@0 | 311 | |
michael@0 | 312 | // we slightly depart from TeX here. We use the defaultRuleThickness instead |
michael@0 | 313 | // of the value coming from the linethickness attribute, i.e., we recover what |
michael@0 | 314 | // TeX does if the user hasn't set linethickness. But when the linethickness |
michael@0 | 315 | // is set, we avoid the wide gap problem. |
michael@0 | 316 | minClearance = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK ? |
michael@0 | 317 | 3 * defaultRuleThickness : defaultRuleThickness + onePixel; |
michael@0 | 318 | |
michael@0 | 319 | // adjust numShift to maintain minClearance if needed |
michael@0 | 320 | actualClearance = |
michael@0 | 321 | (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2); |
michael@0 | 322 | if (actualClearance < minClearance) { |
michael@0 | 323 | numShift += (minClearance - actualClearance); |
michael@0 | 324 | } |
michael@0 | 325 | // adjust denShift to maintain minClearance if needed |
michael@0 | 326 | actualClearance = |
michael@0 | 327 | (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift); |
michael@0 | 328 | if (actualClearance < minClearance) { |
michael@0 | 329 | denShift += (minClearance - actualClearance); |
michael@0 | 330 | } |
michael@0 | 331 | } |
michael@0 | 332 | |
michael@0 | 333 | ////////////////// |
michael@0 | 334 | // Place Children |
michael@0 | 335 | |
michael@0 | 336 | // XXX Need revisiting the width. TeX uses the exact width |
michael@0 | 337 | // e.g. in $$\huge\frac{\displaystyle\int}{i}$$ |
michael@0 | 338 | nscoord width = std::max(bmNum.width, bmDen.width); |
michael@0 | 339 | nscoord dxNum = leftSpace + (width - sizeNum.Width())/2; |
michael@0 | 340 | nscoord dxDen = leftSpace + (width - sizeDen.Width())/2; |
michael@0 | 341 | width += leftSpace + rightSpace; |
michael@0 | 342 | |
michael@0 | 343 | // see if the numalign attribute is there |
michael@0 | 344 | mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::numalign_, value); |
michael@0 | 345 | if (value.EqualsLiteral("left")) |
michael@0 | 346 | dxNum = leftSpace; |
michael@0 | 347 | else if (value.EqualsLiteral("right")) |
michael@0 | 348 | dxNum = width - rightSpace - sizeNum.Width(); |
michael@0 | 349 | |
michael@0 | 350 | // see if the denomalign attribute is there |
michael@0 | 351 | mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::denomalign_, value); |
michael@0 | 352 | if (value.EqualsLiteral("left")) |
michael@0 | 353 | dxDen = leftSpace; |
michael@0 | 354 | else if (value.EqualsLiteral("right")) |
michael@0 | 355 | dxDen = width - rightSpace - sizeDen.Width(); |
michael@0 | 356 | |
michael@0 | 357 | mBoundingMetrics.rightBearing = |
michael@0 | 358 | std::max(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing); |
michael@0 | 359 | if (mBoundingMetrics.rightBearing < width - rightSpace) |
michael@0 | 360 | mBoundingMetrics.rightBearing = width - rightSpace; |
michael@0 | 361 | mBoundingMetrics.leftBearing = |
michael@0 | 362 | std::min(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing); |
michael@0 | 363 | if (mBoundingMetrics.leftBearing > leftSpace) |
michael@0 | 364 | mBoundingMetrics.leftBearing = leftSpace; |
michael@0 | 365 | mBoundingMetrics.ascent = bmNum.ascent + numShift; |
michael@0 | 366 | mBoundingMetrics.descent = bmDen.descent + denShift; |
michael@0 | 367 | mBoundingMetrics.width = width; |
michael@0 | 368 | |
michael@0 | 369 | aDesiredSize.SetTopAscent(sizeNum.TopAscent() + numShift); |
michael@0 | 370 | aDesiredSize.Height() = aDesiredSize.TopAscent() + |
michael@0 | 371 | sizeDen.Height() - sizeDen.TopAscent() + denShift; |
michael@0 | 372 | aDesiredSize.Width() = mBoundingMetrics.width; |
michael@0 | 373 | aDesiredSize.mBoundingMetrics = mBoundingMetrics; |
michael@0 | 374 | |
michael@0 | 375 | mReference.x = 0; |
michael@0 | 376 | mReference.y = aDesiredSize.TopAscent(); |
michael@0 | 377 | |
michael@0 | 378 | if (aPlaceOrigin) { |
michael@0 | 379 | nscoord dy; |
michael@0 | 380 | // place numerator |
michael@0 | 381 | dy = 0; |
michael@0 | 382 | FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dxNum, dy, 0); |
michael@0 | 383 | // place denominator |
michael@0 | 384 | dy = aDesiredSize.Height() - sizeDen.Height(); |
michael@0 | 385 | FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dxDen, dy, 0); |
michael@0 | 386 | // place the fraction bar - dy is top of bar |
michael@0 | 387 | dy = aDesiredSize.TopAscent() - (axisHeight + actualRuleThickness/2); |
michael@0 | 388 | mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace), |
michael@0 | 389 | actualRuleThickness); |
michael@0 | 390 | } |
michael@0 | 391 | } else { |
michael@0 | 392 | nscoord numShift = 0.0; |
michael@0 | 393 | nscoord denShift = 0.0; |
michael@0 | 394 | nscoord padding = 3 * defaultRuleThickness; |
michael@0 | 395 | nscoord slashRatio = 3; |
michael@0 | 396 | |
michael@0 | 397 | // Define the constant used in the expression of the maximum width |
michael@0 | 398 | nscoord em = fm->EmHeight(); |
michael@0 | 399 | nscoord slashMaxWidthConstant = 2 * em; |
michael@0 | 400 | |
michael@0 | 401 | // For large line thicknesses the minimum slash height is limited to the |
michael@0 | 402 | // largest expected height of a fraction |
michael@0 | 403 | nscoord slashMinHeight = slashRatio * |
michael@0 | 404 | std::min(2 * mLineThickness, slashMaxWidthConstant); |
michael@0 | 405 | |
michael@0 | 406 | nscoord leadingSpace = padding; |
michael@0 | 407 | nscoord trailingSpace = padding; |
michael@0 | 408 | if (outermostEmbellished) { |
michael@0 | 409 | nsEmbellishData coreData; |
michael@0 | 410 | GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData); |
michael@0 | 411 | leadingSpace += coreData.leadingSpace; |
michael@0 | 412 | trailingSpace += coreData.trailingSpace; |
michael@0 | 413 | } |
michael@0 | 414 | nscoord delta; |
michael@0 | 415 | |
michael@0 | 416 | // ___________ |
michael@0 | 417 | // | | / |
michael@0 | 418 | // {|-NUMERATOR-| / |
michael@0 | 419 | // {|___________| S |
michael@0 | 420 | // { L |
michael@0 | 421 | // numShift{ A |
michael@0 | 422 | // ------------------------------------------------------- baseline |
michael@0 | 423 | // S _____________ } denShift |
michael@0 | 424 | // H | |} |
michael@0 | 425 | // / |-DENOMINATOR-|} |
michael@0 | 426 | // / |_____________| |
michael@0 | 427 | // |
michael@0 | 428 | |
michael@0 | 429 | // first, ensure that the top of the numerator is at least as high as the |
michael@0 | 430 | // top of the denominator (and the reverse for the bottoms) |
michael@0 | 431 | delta = std::max(bmDen.ascent - bmNum.ascent, |
michael@0 | 432 | bmNum.descent - bmDen.descent) / 2; |
michael@0 | 433 | if (delta > 0) { |
michael@0 | 434 | numShift += delta; |
michael@0 | 435 | denShift += delta; |
michael@0 | 436 | } |
michael@0 | 437 | |
michael@0 | 438 | if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) { |
michael@0 | 439 | delta = std::min(bmDen.ascent + bmDen.descent, |
michael@0 | 440 | bmNum.ascent + bmNum.descent) / 2; |
michael@0 | 441 | numShift += delta; |
michael@0 | 442 | denShift += delta; |
michael@0 | 443 | } else { |
michael@0 | 444 | nscoord xHeight = fm->XHeight(); |
michael@0 | 445 | numShift += xHeight / 2; |
michael@0 | 446 | denShift += xHeight / 4; |
michael@0 | 447 | } |
michael@0 | 448 | |
michael@0 | 449 | // Set the ascent/descent of our BoundingMetrics. |
michael@0 | 450 | mBoundingMetrics.ascent = bmNum.ascent + numShift; |
michael@0 | 451 | mBoundingMetrics.descent = bmDen.descent + denShift; |
michael@0 | 452 | |
michael@0 | 453 | // At this point the height of the slash is |
michael@0 | 454 | // mBoundingMetrics.ascent + mBoundingMetrics.descent |
michael@0 | 455 | // Ensure that it is greater than slashMinHeight |
michael@0 | 456 | delta = (slashMinHeight - |
michael@0 | 457 | (mBoundingMetrics.ascent + mBoundingMetrics.descent)) / 2; |
michael@0 | 458 | if (delta > 0) { |
michael@0 | 459 | mBoundingMetrics.ascent += delta; |
michael@0 | 460 | mBoundingMetrics.descent += delta; |
michael@0 | 461 | } |
michael@0 | 462 | |
michael@0 | 463 | // Set the width of the slash |
michael@0 | 464 | if (aWidthOnly) { |
michael@0 | 465 | mLineRect.width = mLineThickness + slashMaxWidthConstant; |
michael@0 | 466 | } else { |
michael@0 | 467 | mLineRect.width = mLineThickness + |
michael@0 | 468 | std::min(slashMaxWidthConstant, |
michael@0 | 469 | (mBoundingMetrics.ascent + mBoundingMetrics.descent) / |
michael@0 | 470 | slashRatio); |
michael@0 | 471 | } |
michael@0 | 472 | |
michael@0 | 473 | // Set horizontal bounding metrics |
michael@0 | 474 | if (StyleVisibility()->mDirection) { |
michael@0 | 475 | mBoundingMetrics.leftBearing = trailingSpace + bmDen.leftBearing; |
michael@0 | 476 | mBoundingMetrics.rightBearing = trailingSpace + bmDen.width + mLineRect.width + bmNum.rightBearing; |
michael@0 | 477 | } else { |
michael@0 | 478 | mBoundingMetrics.leftBearing = leadingSpace + bmNum.leftBearing; |
michael@0 | 479 | mBoundingMetrics.rightBearing = leadingSpace + bmNum.width + mLineRect.width + bmDen.rightBearing; |
michael@0 | 480 | } |
michael@0 | 481 | mBoundingMetrics.width = |
michael@0 | 482 | leadingSpace + bmNum.width + mLineRect.width + bmDen.width + |
michael@0 | 483 | trailingSpace; |
michael@0 | 484 | |
michael@0 | 485 | // Set aDesiredSize |
michael@0 | 486 | aDesiredSize.SetTopAscent(mBoundingMetrics.ascent + padding); |
michael@0 | 487 | aDesiredSize.Height() = |
michael@0 | 488 | mBoundingMetrics.ascent + mBoundingMetrics.descent + 2 * padding; |
michael@0 | 489 | aDesiredSize.Width() = mBoundingMetrics.width; |
michael@0 | 490 | aDesiredSize.mBoundingMetrics = mBoundingMetrics; |
michael@0 | 491 | |
michael@0 | 492 | mReference.x = 0; |
michael@0 | 493 | mReference.y = aDesiredSize.TopAscent(); |
michael@0 | 494 | |
michael@0 | 495 | if (aPlaceOrigin) { |
michael@0 | 496 | nscoord dx, dy; |
michael@0 | 497 | |
michael@0 | 498 | // place numerator |
michael@0 | 499 | dx = MirrorIfRTL(aDesiredSize.Width(), sizeNum.Width(), |
michael@0 | 500 | leadingSpace); |
michael@0 | 501 | dy = aDesiredSize.TopAscent() - numShift - sizeNum.TopAscent(); |
michael@0 | 502 | FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dx, dy, 0); |
michael@0 | 503 | |
michael@0 | 504 | // place the fraction bar |
michael@0 | 505 | dx = MirrorIfRTL(aDesiredSize.Width(), mLineRect.width, |
michael@0 | 506 | leadingSpace + bmNum.width); |
michael@0 | 507 | dy = aDesiredSize.TopAscent() - mBoundingMetrics.ascent; |
michael@0 | 508 | mLineRect.SetRect(dx, dy, |
michael@0 | 509 | mLineRect.width, aDesiredSize.Height() - 2 * padding); |
michael@0 | 510 | |
michael@0 | 511 | // place denominator |
michael@0 | 512 | dx = MirrorIfRTL(aDesiredSize.Width(), sizeDen.Width(), |
michael@0 | 513 | leadingSpace + bmNum.width + mLineRect.width); |
michael@0 | 514 | dy = aDesiredSize.TopAscent() + denShift - sizeDen.TopAscent(); |
michael@0 | 515 | FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dx, dy, 0); |
michael@0 | 516 | } |
michael@0 | 517 | |
michael@0 | 518 | } |
michael@0 | 519 | |
michael@0 | 520 | return NS_OK; |
michael@0 | 521 | } |
michael@0 | 522 | |
michael@0 | 523 | class nsDisplayMathMLSlash : public nsDisplayItem { |
michael@0 | 524 | public: |
michael@0 | 525 | nsDisplayMathMLSlash(nsDisplayListBuilder* aBuilder, |
michael@0 | 526 | nsIFrame* aFrame, const nsRect& aRect, |
michael@0 | 527 | nscoord aThickness, bool aRTL) |
michael@0 | 528 | : nsDisplayItem(aBuilder, aFrame), mRect(aRect), mThickness(aThickness), |
michael@0 | 529 | mRTL(aRTL) { |
michael@0 | 530 | MOZ_COUNT_CTOR(nsDisplayMathMLSlash); |
michael@0 | 531 | } |
michael@0 | 532 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 533 | virtual ~nsDisplayMathMLSlash() { |
michael@0 | 534 | MOZ_COUNT_DTOR(nsDisplayMathMLSlash); |
michael@0 | 535 | } |
michael@0 | 536 | #endif |
michael@0 | 537 | |
michael@0 | 538 | virtual void Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 539 | nsRenderingContext* aCtx) MOZ_OVERRIDE; |
michael@0 | 540 | NS_DISPLAY_DECL_NAME("MathMLSlash", TYPE_MATHML_SLASH) |
michael@0 | 541 | |
michael@0 | 542 | private: |
michael@0 | 543 | nsRect mRect; |
michael@0 | 544 | nscoord mThickness; |
michael@0 | 545 | bool mRTL; |
michael@0 | 546 | }; |
michael@0 | 547 | |
michael@0 | 548 | void nsDisplayMathMLSlash::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 549 | nsRenderingContext* aCtx) |
michael@0 | 550 | { |
michael@0 | 551 | // get the gfxRect |
michael@0 | 552 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 553 | gfxRect rect = presContext->AppUnitsToGfxUnits(mRect + ToReferenceFrame()); |
michael@0 | 554 | |
michael@0 | 555 | // paint with the current text color |
michael@0 | 556 | aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color)); |
michael@0 | 557 | |
michael@0 | 558 | // draw the slash as a parallelogram |
michael@0 | 559 | gfxContext *gfxCtx = aCtx->ThebesContext(); |
michael@0 | 560 | gfxPoint delta = gfxPoint(presContext->AppUnitsToGfxUnits(mThickness), 0); |
michael@0 | 561 | gfxCtx->NewPath(); |
michael@0 | 562 | |
michael@0 | 563 | if (mRTL) { |
michael@0 | 564 | gfxCtx->MoveTo(rect.TopLeft()); |
michael@0 | 565 | gfxCtx->LineTo(rect.TopLeft() + delta); |
michael@0 | 566 | gfxCtx->LineTo(rect.BottomRight()); |
michael@0 | 567 | gfxCtx->LineTo(rect.BottomRight() - delta); |
michael@0 | 568 | } else { |
michael@0 | 569 | gfxCtx->MoveTo(rect.BottomLeft()); |
michael@0 | 570 | gfxCtx->LineTo(rect.BottomLeft() + delta); |
michael@0 | 571 | gfxCtx->LineTo(rect.TopRight()); |
michael@0 | 572 | gfxCtx->LineTo(rect.TopRight() - delta); |
michael@0 | 573 | } |
michael@0 | 574 | |
michael@0 | 575 | gfxCtx->ClosePath(); |
michael@0 | 576 | gfxCtx->Fill(); |
michael@0 | 577 | } |
michael@0 | 578 | |
michael@0 | 579 | void |
michael@0 | 580 | nsMathMLmfracFrame::DisplaySlash(nsDisplayListBuilder* aBuilder, |
michael@0 | 581 | nsIFrame* aFrame, const nsRect& aRect, |
michael@0 | 582 | nscoord aThickness, |
michael@0 | 583 | const nsDisplayListSet& aLists) { |
michael@0 | 584 | if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty()) |
michael@0 | 585 | return; |
michael@0 | 586 | |
michael@0 | 587 | aLists.Content()->AppendNewToTop(new (aBuilder) |
michael@0 | 588 | nsDisplayMathMLSlash(aBuilder, aFrame, aRect, aThickness, |
michael@0 | 589 | StyleVisibility()->mDirection)); |
michael@0 | 590 | } |