зеркало из https://github.com/mozilla/gecko-dev.git
2200 строки
66 KiB
C++
2200 строки
66 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef WritingModes_h_
|
|
#define WritingModes_h_
|
|
|
|
#include "mozilla/ComputedStyle.h"
|
|
#include "mozilla/ComputedStyleInlines.h"
|
|
|
|
#include "nsRect.h"
|
|
#include "nsBidiUtils.h"
|
|
|
|
// It is the caller's responsibility to operate on logical-coordinate objects
|
|
// with matched writing modes. Failure to do so will be a runtime bug; the
|
|
// compiler can't catch it, but in debug mode, we'll throw an assertion.
|
|
// NOTE that in non-debug builds, a writing mode mismatch error will NOT be
|
|
// detected, yet the results will be nonsense (and may lead to further layout
|
|
// failures). Therefore, it is important to test (and fuzz-test) writing-mode
|
|
// support using debug builds.
|
|
|
|
// Methods in logical-coordinate classes that take another logical-coordinate
|
|
// object as a parameter should call CHECK_WRITING_MODE on it to verify that
|
|
// the writing modes match.
|
|
// (In some cases, there are internal (private) methods that don't do this;
|
|
// such methods should only be used by other methods that have already checked
|
|
// the writing modes.)
|
|
// The check ignores the eSidewaysMask bit of writing mode, because this does
|
|
// not affect the interpretation of logical coordinates.
|
|
|
|
#define CHECK_WRITING_MODE(param) \
|
|
NS_ASSERTION(param.IgnoreSideways() == GetWritingMode().IgnoreSideways(), \
|
|
"writing-mode mismatch")
|
|
|
|
namespace mozilla {
|
|
|
|
namespace widget {
|
|
struct IMENotification;
|
|
} // namespace widget
|
|
|
|
// Physical axis constants.
|
|
enum PhysicalAxis {
|
|
eAxisVertical = 0x0,
|
|
eAxisHorizontal = 0x1
|
|
};
|
|
|
|
inline LogicalAxis GetOrthogonalAxis(LogicalAxis aAxis)
|
|
{
|
|
return aAxis == eLogicalAxisBlock ? eLogicalAxisInline : eLogicalAxisBlock;
|
|
}
|
|
|
|
inline bool IsInline(LogicalSide aSide) { return aSide & 0x2; }
|
|
inline bool IsBlock(LogicalSide aSide) { return !IsInline(aSide); }
|
|
inline bool IsEnd(LogicalSide aSide) { return aSide & 0x1; }
|
|
inline bool IsStart(LogicalSide aSide) { return !IsEnd(aSide); }
|
|
|
|
inline LogicalAxis GetAxis(LogicalSide aSide)
|
|
{
|
|
return IsInline(aSide) ? eLogicalAxisInline : eLogicalAxisBlock;
|
|
}
|
|
|
|
inline LogicalEdge GetEdge(LogicalSide aSide)
|
|
{
|
|
return IsEnd(aSide) ? eLogicalEdgeEnd : eLogicalEdgeStart;
|
|
}
|
|
|
|
inline LogicalEdge GetOppositeEdge(LogicalEdge aEdge)
|
|
{
|
|
// This relies on the only two LogicalEdge enum values being 0 and 1.
|
|
return LogicalEdge(1 - aEdge);
|
|
}
|
|
|
|
inline LogicalSide
|
|
MakeLogicalSide(LogicalAxis aAxis, LogicalEdge aEdge)
|
|
{
|
|
return LogicalSide((aAxis << 1) | aEdge);
|
|
}
|
|
|
|
inline LogicalSide GetOppositeSide(LogicalSide aSide)
|
|
{
|
|
return MakeLogicalSide(GetAxis(aSide), GetOppositeEdge(GetEdge(aSide)));
|
|
}
|
|
|
|
enum LogicalSideBits {
|
|
eLogicalSideBitsNone = 0,
|
|
eLogicalSideBitsBStart = 1 << eLogicalSideBStart,
|
|
eLogicalSideBitsBEnd = 1 << eLogicalSideBEnd,
|
|
eLogicalSideBitsIEnd = 1 << eLogicalSideIEnd,
|
|
eLogicalSideBitsIStart = 1 << eLogicalSideIStart,
|
|
eLogicalSideBitsBBoth = eLogicalSideBitsBStart | eLogicalSideBitsBEnd,
|
|
eLogicalSideBitsIBoth = eLogicalSideBitsIStart | eLogicalSideBitsIEnd,
|
|
eLogicalSideBitsAll = eLogicalSideBitsBBoth | eLogicalSideBitsIBoth
|
|
};
|
|
|
|
enum LineRelativeDir {
|
|
eLineRelativeDirOver = eLogicalSideBStart,
|
|
eLineRelativeDirUnder = eLogicalSideBEnd,
|
|
eLineRelativeDirLeft = eLogicalSideIStart,
|
|
eLineRelativeDirRight = eLogicalSideIEnd
|
|
};
|
|
|
|
/**
|
|
* LogicalSides represents a set of logical sides.
|
|
*/
|
|
struct LogicalSides final {
|
|
LogicalSides() : mBits(0) {}
|
|
explicit LogicalSides(LogicalSideBits aSideBits)
|
|
{
|
|
MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
|
|
mBits = aSideBits;
|
|
}
|
|
bool IsEmpty() const { return mBits == 0; }
|
|
bool BStart() const { return mBits & eLogicalSideBitsBStart; }
|
|
bool BEnd() const { return mBits & eLogicalSideBitsBEnd; }
|
|
bool IStart() const { return mBits & eLogicalSideBitsIStart; }
|
|
bool IEnd() const { return mBits & eLogicalSideBitsIEnd; }
|
|
bool Contains(LogicalSideBits aSideBits) const
|
|
{
|
|
MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
|
|
return (mBits & aSideBits) == aSideBits;
|
|
}
|
|
LogicalSides operator|(LogicalSides aOther) const
|
|
{
|
|
return LogicalSides(LogicalSideBits(mBits | aOther.mBits));
|
|
}
|
|
LogicalSides operator|(LogicalSideBits aSideBits) const
|
|
{
|
|
return *this | LogicalSides(aSideBits);
|
|
}
|
|
LogicalSides& operator|=(LogicalSides aOther)
|
|
{
|
|
mBits |= aOther.mBits;
|
|
return *this;
|
|
}
|
|
LogicalSides& operator|=(LogicalSideBits aSideBits)
|
|
{
|
|
return *this |= LogicalSides(aSideBits);
|
|
}
|
|
bool operator==(LogicalSides aOther) const
|
|
{
|
|
return mBits == aOther.mBits;
|
|
}
|
|
bool operator!=(LogicalSides aOther) const
|
|
{
|
|
return !(*this == aOther);
|
|
}
|
|
|
|
private:
|
|
uint8_t mBits;
|
|
};
|
|
|
|
/**
|
|
* mozilla::WritingMode is an immutable class representing a
|
|
* writing mode.
|
|
*
|
|
* It efficiently stores the writing mode and can rapidly compute
|
|
* interesting things about it for use in layout.
|
|
*
|
|
* Writing modes are computed from the CSS 'direction',
|
|
* 'writing-mode', and 'text-orientation' properties.
|
|
* See CSS3 Writing Modes for more information
|
|
* http://www.w3.org/TR/css3-writing-modes/
|
|
*/
|
|
class WritingMode {
|
|
public:
|
|
/**
|
|
* Absolute inline flow direction
|
|
*/
|
|
enum InlineDir {
|
|
eInlineLTR = 0x00, // text flows horizontally left to right
|
|
eInlineRTL = 0x02, // text flows horizontally right to left
|
|
eInlineTTB = 0x01, // text flows vertically top to bottom
|
|
eInlineBTT = 0x03, // text flows vertically bottom to top
|
|
};
|
|
|
|
/**
|
|
* Absolute block flow direction
|
|
*/
|
|
enum BlockDir {
|
|
eBlockTB = 0x00, // horizontal lines stack top to bottom
|
|
eBlockRL = 0x01, // vertical lines stack right to left
|
|
eBlockLR = 0x05, // vertical lines stack left to right
|
|
};
|
|
|
|
/**
|
|
* Line-relative (bidi-relative) inline flow direction
|
|
*/
|
|
enum BidiDir {
|
|
eBidiLTR = 0x00, // inline flow matches bidi LTR text
|
|
eBidiRTL = 0x10, // inline flow matches bidi RTL text
|
|
};
|
|
|
|
/**
|
|
* Unknown writing mode (should never actually be stored or used anywhere).
|
|
*/
|
|
enum {
|
|
eUnknownWritingMode = 0xff
|
|
};
|
|
|
|
/**
|
|
* Return the absolute inline flow direction as an InlineDir
|
|
*/
|
|
InlineDir GetInlineDir() const { return InlineDir(mWritingMode & eInlineMask); }
|
|
|
|
/**
|
|
* Return the absolute block flow direction as a BlockDir
|
|
*/
|
|
BlockDir GetBlockDir() const { return BlockDir(mWritingMode & eBlockMask); }
|
|
|
|
/**
|
|
* Return the line-relative inline flow direction as a BidiDir
|
|
*/
|
|
BidiDir GetBidiDir() const { return BidiDir(mWritingMode & eBidiMask); }
|
|
|
|
/**
|
|
* Return true if the inline flow direction is against physical direction
|
|
* (i.e. right-to-left or bottom-to-top).
|
|
* This occurs when writing-mode is sideways-lr OR direction is rtl (but not
|
|
* if both of those are true).
|
|
*/
|
|
bool IsInlineReversed() const { return !!(mWritingMode & eInlineFlowMask); }
|
|
|
|
/**
|
|
* Return true if bidi direction is LTR. (Convenience method)
|
|
*/
|
|
bool IsBidiLTR() const { return eBidiLTR == GetBidiDir(); }
|
|
|
|
/**
|
|
* True if vertical-mode block direction is LR (convenience method).
|
|
*/
|
|
bool IsVerticalLR() const { return eBlockLR == GetBlockDir(); }
|
|
|
|
/**
|
|
* True if vertical-mode block direction is RL (convenience method).
|
|
*/
|
|
bool IsVerticalRL() const { return eBlockRL == GetBlockDir(); }
|
|
|
|
/**
|
|
* True if vertical writing mode, i.e. when
|
|
* writing-mode: vertical-lr | vertical-rl.
|
|
*/
|
|
bool IsVertical() const { return !!(mWritingMode & eOrientationMask); }
|
|
|
|
/**
|
|
* True if line-over/line-under are inverted from block-start/block-end.
|
|
* This is true only when writing-mode is vertical-lr.
|
|
*/
|
|
bool IsLineInverted() const { return !!(mWritingMode & eLineOrientMask); }
|
|
|
|
/**
|
|
* Block-axis flow-relative to line-relative factor.
|
|
* May be used as a multiplication factor for block-axis coordinates
|
|
* to convert between flow- and line-relative coordinate systems (e.g.
|
|
* positioning an over- or under-line decoration).
|
|
*/
|
|
int FlowRelativeToLineRelativeFactor() const
|
|
{
|
|
return IsLineInverted() ? -1 : 1;
|
|
}
|
|
|
|
/**
|
|
* True if the text-orientation will force all text to be rendered sideways
|
|
* in vertical lines, in which case we should prefer an alphabetic baseline;
|
|
* otherwise, the default is centered.
|
|
* Note that some glyph runs may be rendered sideways even if this is false,
|
|
* due to text-orientation:mixed resolution, but in that case the dominant
|
|
* baseline remains centered.
|
|
*/
|
|
bool IsSideways() const { return !!(mWritingMode & eSidewaysMask); }
|
|
|
|
#ifdef DEBUG // Used by CHECK_WRITING_MODE to compare modes without regard
|
|
// for the eSidewaysMask flag.
|
|
WritingMode IgnoreSideways() const {
|
|
return WritingMode(mWritingMode & ~eSidewaysMask);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Return true if boxes with this writing mode should use central baselines.
|
|
*/
|
|
bool IsCentralBaseline() const { return IsVertical() && !IsSideways(); }
|
|
|
|
/**
|
|
* Return true if boxes with this writing mode should use alphabetical
|
|
* baselines.
|
|
*/
|
|
bool IsAlphabeticalBaseline() const { return !IsCentralBaseline(); }
|
|
|
|
|
|
static mozilla::PhysicalAxis PhysicalAxisForLogicalAxis(
|
|
uint8_t aWritingModeValue,
|
|
LogicalAxis aAxis)
|
|
{
|
|
// This relies on bit 0 of a writing-value mode indicating vertical
|
|
// orientation and bit 0 of a LogicalAxis value indicating the inline axis,
|
|
// so that it can correctly form mozilla::PhysicalAxis values using bit
|
|
// manipulation.
|
|
static_assert(NS_STYLE_WRITING_MODE_HORIZONTAL_TB == 0 &&
|
|
NS_STYLE_WRITING_MODE_VERTICAL_RL == 1 &&
|
|
NS_STYLE_WRITING_MODE_VERTICAL_LR == 3 &&
|
|
eLogicalAxisBlock == 0 &&
|
|
eLogicalAxisInline == 1 &&
|
|
eAxisVertical == 0 &&
|
|
eAxisHorizontal == 1,
|
|
"unexpected writing-mode, logical axis or physical axis "
|
|
"constant values");
|
|
return mozilla::PhysicalAxis((aWritingModeValue ^ aAxis) & 0x1);
|
|
}
|
|
|
|
mozilla::PhysicalAxis PhysicalAxis(LogicalAxis aAxis) const
|
|
{
|
|
// This will set wm to either NS_STYLE_WRITING_MODE_HORIZONTAL_TB or
|
|
// NS_STYLE_WRITING_MODE_VERTICAL_RL, and not the other two (real
|
|
// and hypothetical) values. But this is fine; we only need to
|
|
// distinguish between vertical and horizontal in
|
|
// PhysicalAxisForLogicalAxis.
|
|
int wm = mWritingMode & eOrientationMask;
|
|
return PhysicalAxisForLogicalAxis(wm, aAxis);
|
|
}
|
|
|
|
static mozilla::Side PhysicalSideForBlockAxis(uint8_t aWritingModeValue,
|
|
LogicalEdge aEdge)
|
|
{
|
|
// indexes are NS_STYLE_WRITING_MODE_* values, which are the same as these
|
|
// two-bit values:
|
|
// bit 0 = the eOrientationMask value
|
|
// bit 1 = the eBlockFlowMask value
|
|
static const mozilla::Side kLogicalBlockSides[][2] = {
|
|
{ eSideTop, eSideBottom }, // horizontal-tb
|
|
{ eSideRight, eSideLeft }, // vertical-rl
|
|
{ eSideBottom, eSideTop }, // (horizontal-bt)
|
|
{ eSideLeft, eSideRight }, // vertical-lr
|
|
};
|
|
|
|
// Ignore the SIDEWAYS_MASK bit of the writing-mode value, as this has no
|
|
// effect on the side mappings.
|
|
aWritingModeValue &= ~NS_STYLE_WRITING_MODE_SIDEWAYS_MASK;
|
|
|
|
// What's left of the writing-mode should be in the range 0-3:
|
|
NS_ASSERTION(aWritingModeValue < 4, "invalid aWritingModeValue value");
|
|
|
|
return kLogicalBlockSides[aWritingModeValue][aEdge];
|
|
}
|
|
|
|
mozilla::Side PhysicalSideForInlineAxis(LogicalEdge aEdge) const
|
|
{
|
|
// indexes are four-bit values:
|
|
// bit 0 = the eOrientationMask value
|
|
// bit 1 = the eInlineFlowMask value
|
|
// bit 2 = the eBlockFlowMask value
|
|
// bit 3 = the eLineOrientMask value
|
|
// Not all of these combinations can actually be specified via CSS: there
|
|
// is no horizontal-bt writing-mode, and no text-orientation value that
|
|
// produces "inverted" text. (The former 'sideways-left' value, no longer
|
|
// in the spec, would have produced this in vertical-rl mode.)
|
|
static const mozilla::Side kLogicalInlineSides[][2] = {
|
|
{ eSideLeft, eSideRight }, // horizontal-tb ltr
|
|
{ eSideTop, eSideBottom }, // vertical-rl ltr
|
|
{ eSideRight, eSideLeft }, // horizontal-tb rtl
|
|
{ eSideBottom, eSideTop }, // vertical-rl rtl
|
|
{ eSideRight, eSideLeft }, // (horizontal-bt) (inverted) ltr
|
|
{ eSideTop, eSideBottom }, // sideways-lr rtl
|
|
{ eSideLeft, eSideRight }, // (horizontal-bt) (inverted) rtl
|
|
{ eSideBottom, eSideTop }, // sideways-lr ltr
|
|
{ eSideLeft, eSideRight }, // horizontal-tb (inverted) rtl
|
|
{ eSideTop, eSideBottom }, // vertical-rl (inverted) rtl
|
|
{ eSideRight, eSideLeft }, // horizontal-tb (inverted) ltr
|
|
{ eSideBottom, eSideTop }, // vertical-rl (inverted) ltr
|
|
{ eSideLeft, eSideRight }, // (horizontal-bt) ltr
|
|
{ eSideTop, eSideBottom }, // vertical-lr ltr
|
|
{ eSideRight, eSideLeft }, // (horizontal-bt) rtl
|
|
{ eSideBottom, eSideTop }, // vertical-lr rtl
|
|
};
|
|
|
|
// Inline axis sides depend on all three of writing-mode, text-orientation
|
|
// and direction, which are encoded in the eOrientationMask,
|
|
// eInlineFlowMask, eBlockFlowMask and eLineOrientMask bits. Use these four
|
|
// bits to index into kLogicalInlineSides.
|
|
static_assert(eOrientationMask == 0x01 && eInlineFlowMask == 0x02 &&
|
|
eBlockFlowMask == 0x04 && eLineOrientMask == 0x08,
|
|
"unexpected mask values");
|
|
int index = mWritingMode & 0x0F;
|
|
return kLogicalInlineSides[index][aEdge];
|
|
}
|
|
|
|
/**
|
|
* Returns the physical side corresponding to the specified logical side,
|
|
* given the current writing mode.
|
|
*/
|
|
mozilla::Side PhysicalSide(LogicalSide aSide) const
|
|
{
|
|
if (IsBlock(aSide)) {
|
|
static_assert(eOrientationMask == 0x01 && eBlockFlowMask == 0x04,
|
|
"unexpected mask values");
|
|
int wm = ((mWritingMode & eBlockFlowMask) >> 1) |
|
|
(mWritingMode & eOrientationMask);
|
|
return PhysicalSideForBlockAxis(wm, GetEdge(aSide));
|
|
}
|
|
|
|
return PhysicalSideForInlineAxis(GetEdge(aSide));
|
|
}
|
|
|
|
/**
|
|
* Returns the logical side corresponding to the specified physical side,
|
|
* given the current writing mode.
|
|
* (This is the inverse of the PhysicalSide() method above.)
|
|
*/
|
|
LogicalSide LogicalSideForPhysicalSide(mozilla::Side aSide) const
|
|
{
|
|
// indexes are four-bit values:
|
|
// bit 0 = the eOrientationMask value
|
|
// bit 1 = the eInlineFlowMask value
|
|
// bit 2 = the eBlockFlowMask value
|
|
// bit 3 = the eLineOrientMask value
|
|
static const LogicalSide kPhysicalToLogicalSides[][4] = {
|
|
// top right
|
|
// bottom left
|
|
{ eLogicalSideBStart, eLogicalSideIEnd,
|
|
eLogicalSideBEnd, eLogicalSideIStart }, // horizontal-tb ltr
|
|
{ eLogicalSideIStart, eLogicalSideBStart,
|
|
eLogicalSideIEnd, eLogicalSideBEnd }, // vertical-rl ltr
|
|
{ eLogicalSideBStart, eLogicalSideIStart,
|
|
eLogicalSideBEnd, eLogicalSideIEnd }, // horizontal-tb rtl
|
|
{ eLogicalSideIEnd, eLogicalSideBStart,
|
|
eLogicalSideIStart, eLogicalSideBEnd }, // vertical-rl rtl
|
|
{ eLogicalSideBEnd, eLogicalSideIStart,
|
|
eLogicalSideBStart, eLogicalSideIEnd }, // (horizontal-bt) (inv) ltr
|
|
{ eLogicalSideIStart, eLogicalSideBEnd,
|
|
eLogicalSideIEnd, eLogicalSideBStart }, // vertical-lr sw-left rtl
|
|
{ eLogicalSideBEnd, eLogicalSideIEnd,
|
|
eLogicalSideBStart, eLogicalSideIStart }, // (horizontal-bt) (inv) rtl
|
|
{ eLogicalSideIEnd, eLogicalSideBEnd,
|
|
eLogicalSideIStart, eLogicalSideBStart }, // vertical-lr sw-left ltr
|
|
{ eLogicalSideBStart, eLogicalSideIEnd,
|
|
eLogicalSideBEnd, eLogicalSideIStart }, // horizontal-tb (inv) rtl
|
|
{ eLogicalSideIStart, eLogicalSideBStart,
|
|
eLogicalSideIEnd, eLogicalSideBEnd }, // vertical-rl sw-left rtl
|
|
{ eLogicalSideBStart, eLogicalSideIStart,
|
|
eLogicalSideBEnd, eLogicalSideIEnd }, // horizontal-tb (inv) ltr
|
|
{ eLogicalSideIEnd, eLogicalSideBStart,
|
|
eLogicalSideIStart, eLogicalSideBEnd }, // vertical-rl sw-left ltr
|
|
{ eLogicalSideBEnd, eLogicalSideIEnd,
|
|
eLogicalSideBStart, eLogicalSideIStart }, // (horizontal-bt) ltr
|
|
{ eLogicalSideIStart, eLogicalSideBEnd,
|
|
eLogicalSideIEnd, eLogicalSideBStart }, // vertical-lr ltr
|
|
{ eLogicalSideBEnd, eLogicalSideIStart,
|
|
eLogicalSideBStart, eLogicalSideIEnd }, // (horizontal-bt) rtl
|
|
{ eLogicalSideIEnd, eLogicalSideBEnd,
|
|
eLogicalSideIStart, eLogicalSideBStart }, // vertical-lr rtl
|
|
};
|
|
|
|
static_assert(eOrientationMask == 0x01 && eInlineFlowMask == 0x02 &&
|
|
eBlockFlowMask == 0x04 && eLineOrientMask == 0x08,
|
|
"unexpected mask values");
|
|
int index = mWritingMode & 0x0F;
|
|
return kPhysicalToLogicalSides[index][aSide];
|
|
}
|
|
|
|
/**
|
|
* Returns the logical side corresponding to the specified
|
|
* line-relative direction, given the current writing mode.
|
|
*/
|
|
LogicalSide LogicalSideForLineRelativeDir(LineRelativeDir aDir) const
|
|
{
|
|
auto side = static_cast<LogicalSide>(aDir);
|
|
if (IsInline(side)) {
|
|
return IsBidiLTR() ? side : GetOppositeSide(side);
|
|
}
|
|
return !IsLineInverted() ? side : GetOppositeSide(side);
|
|
}
|
|
|
|
/**
|
|
* Default constructor gives us a horizontal, LTR writing mode.
|
|
* XXX We will probably eliminate this and require explicit initialization
|
|
* in all cases once transition is complete.
|
|
*/
|
|
WritingMode()
|
|
: mWritingMode(0)
|
|
{ }
|
|
|
|
/**
|
|
* Construct writing mode based on a ComputedStyle.
|
|
*/
|
|
explicit WritingMode(ComputedStyle* aComputedStyle)
|
|
{
|
|
NS_ASSERTION(aComputedStyle, "we need an ComputedStyle here");
|
|
InitFromStyleVisibility(aComputedStyle->StyleVisibility());
|
|
}
|
|
|
|
explicit WritingMode(const nsStyleVisibility* aStyleVisibility)
|
|
{
|
|
NS_ASSERTION(aStyleVisibility, "we need an nsStyleVisibility here");
|
|
InitFromStyleVisibility(aStyleVisibility);
|
|
}
|
|
|
|
private:
|
|
void InitFromStyleVisibility(const nsStyleVisibility* aStyleVisibility)
|
|
{
|
|
switch (aStyleVisibility->mWritingMode) {
|
|
case NS_STYLE_WRITING_MODE_HORIZONTAL_TB:
|
|
mWritingMode = 0;
|
|
break;
|
|
|
|
case NS_STYLE_WRITING_MODE_VERTICAL_LR:
|
|
{
|
|
mWritingMode = eBlockFlowMask |
|
|
eLineOrientMask |
|
|
eOrientationMask;
|
|
uint8_t textOrientation = aStyleVisibility->mTextOrientation;
|
|
if (textOrientation == NS_STYLE_TEXT_ORIENTATION_SIDEWAYS) {
|
|
mWritingMode |= eSidewaysMask;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case NS_STYLE_WRITING_MODE_VERTICAL_RL:
|
|
{
|
|
mWritingMode = eOrientationMask;
|
|
uint8_t textOrientation = aStyleVisibility->mTextOrientation;
|
|
if (textOrientation == NS_STYLE_TEXT_ORIENTATION_SIDEWAYS) {
|
|
mWritingMode |= eSidewaysMask;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case NS_STYLE_WRITING_MODE_SIDEWAYS_LR:
|
|
mWritingMode = eBlockFlowMask |
|
|
eInlineFlowMask |
|
|
eOrientationMask |
|
|
eSidewaysMask;
|
|
break;
|
|
|
|
case NS_STYLE_WRITING_MODE_SIDEWAYS_RL:
|
|
mWritingMode = eOrientationMask |
|
|
eSidewaysMask;
|
|
break;
|
|
|
|
default:
|
|
NS_NOTREACHED("unknown writing mode!");
|
|
mWritingMode = 0;
|
|
break;
|
|
}
|
|
|
|
if (NS_STYLE_DIRECTION_RTL == aStyleVisibility->mDirection) {
|
|
mWritingMode ^= eInlineFlowMask | eBidiMask;
|
|
}
|
|
}
|
|
public:
|
|
|
|
/**
|
|
* This function performs fixup for elements with 'unicode-bidi: plaintext',
|
|
* where inline directionality is derived from the Unicode bidi categories
|
|
* of the element's content, and not the CSS 'direction' property.
|
|
*
|
|
* The WritingMode constructor will have already incorporated the 'direction'
|
|
* property into our flag bits, so such elements need to use this method
|
|
* (after resolving the bidi level of their content) to update the direction
|
|
* bits as needed.
|
|
*
|
|
* If it turns out that our bidi direction already matches what plaintext
|
|
* resolution determined, there's nothing to do here. If it didn't (i.e. if
|
|
* the rtl-ness doesn't match), then we correct the direction by flipping the
|
|
* same bits that get flipped in the constructor's CSS 'direction'-based
|
|
* chunk.
|
|
*
|
|
* XXX change uint8_t to UBiDiLevel after bug 924851
|
|
*/
|
|
void SetDirectionFromBidiLevel(uint8_t level)
|
|
{
|
|
if (IS_LEVEL_RTL(level) == IsBidiLTR()) {
|
|
mWritingMode ^= eBidiMask | eInlineFlowMask;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compare two WritingModes for equality.
|
|
*/
|
|
bool operator==(const WritingMode& aOther) const
|
|
{
|
|
return mWritingMode == aOther.mWritingMode;
|
|
}
|
|
|
|
bool operator!=(const WritingMode& aOther) const
|
|
{
|
|
return mWritingMode != aOther.mWritingMode;
|
|
}
|
|
|
|
/**
|
|
* Check whether two modes are orthogonal to each other.
|
|
*/
|
|
bool IsOrthogonalTo(const WritingMode& aOther) const
|
|
{
|
|
return IsVertical() != aOther.IsVertical();
|
|
}
|
|
|
|
/**
|
|
* Returns true if this WritingMode's aLogicalAxis has the same physical
|
|
* start side as the parallel axis of WritingMode |aOther|.
|
|
*
|
|
* @param aLogicalAxis The axis to compare from this WritingMode.
|
|
* @param aOther The other WritingMode (from which we'll choose the axis
|
|
* that's parallel to this WritingMode's aLogicalAxis, for
|
|
* comparison).
|
|
*/
|
|
bool ParallelAxisStartsOnSameSide(LogicalAxis aLogicalAxis,
|
|
const WritingMode& aOther) const
|
|
{
|
|
mozilla::Side myStartSide =
|
|
this->PhysicalSide(MakeLogicalSide(aLogicalAxis,
|
|
eLogicalEdgeStart));
|
|
|
|
// Figure out which of aOther's axes is parallel to |this| WritingMode's
|
|
// aLogicalAxis, and get its physical start side as well.
|
|
LogicalAxis otherWMAxis = aOther.IsOrthogonalTo(*this) ?
|
|
GetOrthogonalAxis(aLogicalAxis) : aLogicalAxis;
|
|
mozilla::Side otherWMStartSide =
|
|
aOther.PhysicalSide(MakeLogicalSide(otherWMAxis,
|
|
eLogicalEdgeStart));
|
|
|
|
NS_ASSERTION(myStartSide % 2 == otherWMStartSide % 2,
|
|
"Should end up with sides in the same physical axis");
|
|
return myStartSide == otherWMStartSide;
|
|
}
|
|
|
|
uint8_t GetBits() const { return mWritingMode; }
|
|
|
|
const char* DebugString() const {
|
|
return IsVertical()
|
|
? IsVerticalLR()
|
|
? IsBidiLTR()
|
|
? IsSideways() ? "sw-lr-ltr" : "v-lr-ltr"
|
|
: IsSideways() ? "sw-lr-rtl" : "v-lr-rtl"
|
|
: IsBidiLTR()
|
|
? IsSideways() ? "sw-rl-ltr" : "v-rl-ltr"
|
|
: IsSideways() ? "sw-rl-rtl" : "v-rl-rtl"
|
|
: IsBidiLTR() ? "h-ltr" : "h-rtl"
|
|
;
|
|
}
|
|
|
|
private:
|
|
friend class LogicalPoint;
|
|
friend class LogicalSize;
|
|
friend class LogicalMargin;
|
|
friend class LogicalRect;
|
|
|
|
friend struct IPC::ParamTraits<WritingMode>;
|
|
// IMENotification cannot store this class directly since this has some
|
|
// constructors. Therefore, it stores mWritingMode and recreate the
|
|
// instance from it.
|
|
friend struct widget::IMENotification;
|
|
|
|
/**
|
|
* Return a WritingMode representing an unknown value.
|
|
*/
|
|
static inline WritingMode Unknown()
|
|
{
|
|
return WritingMode(eUnknownWritingMode);
|
|
}
|
|
|
|
/**
|
|
* Constructing a WritingMode with an arbitrary value is a private operation
|
|
* currently only used by the Unknown() static method.
|
|
*/
|
|
explicit WritingMode(uint8_t aValue)
|
|
: mWritingMode(aValue)
|
|
{ }
|
|
|
|
uint8_t mWritingMode;
|
|
|
|
enum Masks {
|
|
// Masks for our bits; true chosen as opposite of commonest case
|
|
eOrientationMask = 0x01, // true means vertical text
|
|
eInlineFlowMask = 0x02, // true means absolute RTL/BTT (against physical coords)
|
|
eBlockFlowMask = 0x04, // true means vertical-LR (or horizontal-BT if added)
|
|
eLineOrientMask = 0x08, // true means over != block-start
|
|
eBidiMask = 0x10, // true means line-relative RTL (bidi RTL)
|
|
// Note: We have one excess bit of info; WritingMode can pack into 4 bits.
|
|
// But since we have space, we're caching interesting things for fast access.
|
|
|
|
eSidewaysMask = 0x20, // true means text is being rendered vertically
|
|
// using rotated glyphs (i.e. writing-mode is
|
|
// sideways-*, or writing-mode is vertical-* AND
|
|
// text-orientation is sideways),
|
|
// which means we'll use alphabetic instead of
|
|
// centered default baseline for vertical text
|
|
|
|
// Masks for output enums
|
|
eInlineMask = 0x03,
|
|
eBlockMask = 0x05
|
|
};
|
|
};
|
|
|
|
|
|
/**
|
|
* Logical-coordinate classes:
|
|
*
|
|
* There are three sets of coordinate space:
|
|
* - physical (top, left, bottom, right)
|
|
* relative to graphics coord system
|
|
* - flow-relative (block-start, inline-start, block-end, inline-end)
|
|
* relative to block/inline flow directions
|
|
* - line-relative (line-over, line-left, line-under, line-right)
|
|
* relative to glyph orientation / inline bidi directions
|
|
* See CSS3 Writing Modes for more information
|
|
* http://www.w3.org/TR/css3-writing-modes/#abstract-box
|
|
*
|
|
* For shorthand, B represents the block-axis
|
|
* I represents the inline-axis
|
|
*
|
|
* The flow-relative geometric classes store coords in flow-relative space.
|
|
* They use a private ns{Point,Size,Rect,Margin} member to store the actual
|
|
* coordinate values, but reinterpret them as logical instead of physical.
|
|
* This allows us to easily perform calculations in logical space (provided
|
|
* writing modes of the operands match), by simply mapping to nsPoint (etc)
|
|
* methods.
|
|
*
|
|
* Physical-coordinate accessors/setters are responsible to translate these
|
|
* internal logical values as necessary.
|
|
*
|
|
* In DEBUG builds, the logical types store their WritingMode and check
|
|
* that the same WritingMode is passed whenever callers ask them to do a
|
|
* writing-mode-dependent operation. Non-DEBUG builds do NOT check this,
|
|
* to avoid the overhead of storing WritingMode fields.
|
|
*
|
|
* Open question: do we need a different set optimized for line-relative
|
|
* math, for use in nsLineLayout and the like? Or is multiplying values
|
|
* by FlowRelativeToLineRelativeFactor() enough?
|
|
*/
|
|
|
|
/**
|
|
* Flow-relative point
|
|
*/
|
|
class LogicalPoint {
|
|
public:
|
|
explicit LogicalPoint(WritingMode aWritingMode)
|
|
:
|
|
#ifdef DEBUG
|
|
mWritingMode(aWritingMode),
|
|
#endif
|
|
mPoint(0, 0)
|
|
{ }
|
|
|
|
// Construct from a writing mode and individual coordinates (which MUST be
|
|
// values in that writing mode, NOT physical coordinates!)
|
|
LogicalPoint(WritingMode aWritingMode, nscoord aI, nscoord aB)
|
|
:
|
|
#ifdef DEBUG
|
|
mWritingMode(aWritingMode),
|
|
#endif
|
|
mPoint(aI, aB)
|
|
{ }
|
|
|
|
// Construct from a writing mode and a physical point, within a given
|
|
// containing rectangle's size (defining the conversion between LTR
|
|
// and RTL coordinates, and between TTB and BTT coordinates).
|
|
LogicalPoint(WritingMode aWritingMode,
|
|
const nsPoint& aPoint,
|
|
const nsSize& aContainerSize)
|
|
#ifdef DEBUG
|
|
: mWritingMode(aWritingMode)
|
|
#endif
|
|
{
|
|
if (aWritingMode.IsVertical()) {
|
|
I() = aWritingMode.IsInlineReversed() ? aContainerSize.height - aPoint.y
|
|
: aPoint.y;
|
|
B() = aWritingMode.IsVerticalLR() ? aPoint.x
|
|
: aContainerSize.width - aPoint.x;
|
|
} else {
|
|
I() = aWritingMode.IsInlineReversed() ? aContainerSize.width - aPoint.x
|
|
: aPoint.x;
|
|
B() = aPoint.y;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read-only (const) access to the logical coordinates.
|
|
*/
|
|
nscoord I(WritingMode aWritingMode) const // inline-axis
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mPoint.x;
|
|
}
|
|
nscoord B(WritingMode aWritingMode) const // block-axis
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mPoint.y;
|
|
}
|
|
nscoord LineRelative(WritingMode aWritingMode,
|
|
const nsSize& aContainerSize) const // line-axis
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
if (aWritingMode.IsBidiLTR()) {
|
|
return I();
|
|
}
|
|
return (aWritingMode.IsVertical() ? aContainerSize.height
|
|
: aContainerSize.width) - I();
|
|
}
|
|
|
|
/**
|
|
* These non-const accessors return a reference (lvalue) that can be
|
|
* assigned to by callers.
|
|
*/
|
|
nscoord& I(WritingMode aWritingMode) // inline-axis
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mPoint.x;
|
|
}
|
|
nscoord& B(WritingMode aWritingMode) // block-axis
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mPoint.y;
|
|
}
|
|
|
|
/**
|
|
* Return a physical point corresponding to our logical coordinates,
|
|
* converted according to our writing mode.
|
|
*/
|
|
nsPoint GetPhysicalPoint(WritingMode aWritingMode,
|
|
const nsSize& aContainerSize) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
if (aWritingMode.IsVertical()) {
|
|
return nsPoint(aWritingMode.IsVerticalLR()
|
|
? B() : aContainerSize.width - B(),
|
|
aWritingMode.IsInlineReversed()
|
|
? aContainerSize.height - I() : I());
|
|
} else {
|
|
return nsPoint(aWritingMode.IsInlineReversed()
|
|
? aContainerSize.width - I() : I(),
|
|
B());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the equivalent point in a different writing mode.
|
|
*/
|
|
LogicalPoint ConvertTo(WritingMode aToMode, WritingMode aFromMode,
|
|
const nsSize& aContainerSize) const
|
|
{
|
|
CHECK_WRITING_MODE(aFromMode);
|
|
return aToMode == aFromMode ?
|
|
*this : LogicalPoint(aToMode,
|
|
GetPhysicalPoint(aFromMode, aContainerSize),
|
|
aContainerSize);
|
|
}
|
|
|
|
bool operator==(const LogicalPoint& aOther) const
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
return mPoint == aOther.mPoint;
|
|
}
|
|
|
|
bool operator!=(const LogicalPoint& aOther) const
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
return mPoint != aOther.mPoint;
|
|
}
|
|
|
|
LogicalPoint operator+(const LogicalPoint& aOther) const
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
// In non-debug builds, LogicalPoint does not store the WritingMode,
|
|
// so the first parameter here (which will always be eUnknownWritingMode)
|
|
// is ignored.
|
|
return LogicalPoint(GetWritingMode(),
|
|
mPoint.x + aOther.mPoint.x,
|
|
mPoint.y + aOther.mPoint.y);
|
|
}
|
|
|
|
LogicalPoint& operator+=(const LogicalPoint& aOther)
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
I() += aOther.I();
|
|
B() += aOther.B();
|
|
return *this;
|
|
}
|
|
|
|
LogicalPoint operator-(const LogicalPoint& aOther) const
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
// In non-debug builds, LogicalPoint does not store the WritingMode,
|
|
// so the first parameter here (which will always be eUnknownWritingMode)
|
|
// is ignored.
|
|
return LogicalPoint(GetWritingMode(),
|
|
mPoint.x - aOther.mPoint.x,
|
|
mPoint.y - aOther.mPoint.y);
|
|
}
|
|
|
|
LogicalPoint& operator-=(const LogicalPoint& aOther)
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
I() -= aOther.I();
|
|
B() -= aOther.B();
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
friend class LogicalRect;
|
|
|
|
/**
|
|
* NOTE that in non-DEBUG builds, GetWritingMode() always returns
|
|
* eUnknownWritingMode, as the current mode is not stored in the logical-
|
|
* geometry classes. Therefore, this method is private; it is used ONLY
|
|
* by the DEBUG-mode checking macros in this class and its friends;
|
|
* other code is not allowed to ask a logical point for its writing mode,
|
|
* as this info will simply not be available in non-DEBUG builds.
|
|
*
|
|
* Also, in non-DEBUG builds, CHECK_WRITING_MODE does nothing, and the
|
|
* WritingMode parameter to logical methods will generally be optimized
|
|
* away altogether.
|
|
*/
|
|
#ifdef DEBUG
|
|
WritingMode GetWritingMode() const { return mWritingMode; }
|
|
#else
|
|
WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
|
|
#endif
|
|
|
|
// We don't allow construction of a LogicalPoint with no writing mode.
|
|
LogicalPoint() = delete;
|
|
|
|
// Accessors that don't take or check a WritingMode value.
|
|
// These are for internal use only; they are called by methods that have
|
|
// themselves already checked the WritingMode passed by the caller.
|
|
nscoord I() const // inline-axis
|
|
{
|
|
return mPoint.x;
|
|
}
|
|
nscoord B() const // block-axis
|
|
{
|
|
return mPoint.y;
|
|
}
|
|
|
|
nscoord& I() // inline-axis
|
|
{
|
|
return mPoint.x;
|
|
}
|
|
nscoord& B() // block-axis
|
|
{
|
|
return mPoint.y;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
WritingMode mWritingMode;
|
|
#endif
|
|
|
|
// We use an nsPoint to hold the coordinates, but reinterpret its .x and .y
|
|
// fields as the inline and block directions. Hence, this is not exposed
|
|
// directly, but only through accessors that will map them according to the
|
|
// writing mode.
|
|
nsPoint mPoint;
|
|
};
|
|
|
|
/**
|
|
* Flow-relative size
|
|
*/
|
|
class LogicalSize {
|
|
public:
|
|
explicit LogicalSize(WritingMode aWritingMode)
|
|
:
|
|
#ifdef DEBUG
|
|
mWritingMode(aWritingMode),
|
|
#endif
|
|
mSize(0, 0)
|
|
{ }
|
|
|
|
LogicalSize(WritingMode aWritingMode, nscoord aISize, nscoord aBSize)
|
|
:
|
|
#ifdef DEBUG
|
|
mWritingMode(aWritingMode),
|
|
#endif
|
|
mSize(aISize, aBSize)
|
|
{ }
|
|
|
|
LogicalSize(WritingMode aWritingMode, const nsSize& aPhysicalSize)
|
|
#ifdef DEBUG
|
|
: mWritingMode(aWritingMode)
|
|
#endif
|
|
{
|
|
if (aWritingMode.IsVertical()) {
|
|
ISize() = aPhysicalSize.height;
|
|
BSize() = aPhysicalSize.width;
|
|
} else {
|
|
ISize() = aPhysicalSize.width;
|
|
BSize() = aPhysicalSize.height;
|
|
}
|
|
}
|
|
|
|
void SizeTo(WritingMode aWritingMode, nscoord aISize, nscoord aBSize)
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
mSize.SizeTo(aISize, aBSize);
|
|
}
|
|
|
|
/**
|
|
* Dimensions in logical and physical terms
|
|
*/
|
|
nscoord ISize(WritingMode aWritingMode) const // inline-size
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mSize.width;
|
|
}
|
|
nscoord BSize(WritingMode aWritingMode) const // block-size
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mSize.height;
|
|
}
|
|
|
|
nscoord Width(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical() ? BSize() : ISize();
|
|
}
|
|
nscoord Height(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical() ? ISize() : BSize();
|
|
}
|
|
|
|
/**
|
|
* Writable references to the logical dimensions
|
|
*/
|
|
nscoord& ISize(WritingMode aWritingMode) // inline-size
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mSize.width;
|
|
}
|
|
nscoord& BSize(WritingMode aWritingMode) // block-size
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mSize.height;
|
|
}
|
|
|
|
/**
|
|
* Return an nsSize containing our physical dimensions
|
|
*/
|
|
nsSize GetPhysicalSize(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical() ?
|
|
nsSize(BSize(), ISize()) : nsSize(ISize(), BSize());
|
|
}
|
|
|
|
/**
|
|
* Return a LogicalSize representing this size in a different writing mode
|
|
*/
|
|
LogicalSize ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
|
|
{
|
|
#ifdef DEBUG
|
|
// In DEBUG builds make sure to return a LogicalSize with the
|
|
// expected writing mode
|
|
CHECK_WRITING_MODE(aFromMode);
|
|
return aToMode == aFromMode ?
|
|
*this : LogicalSize(aToMode, GetPhysicalSize(aFromMode));
|
|
#else
|
|
// optimization for non-DEBUG builds where LogicalSize doesn't store
|
|
// the writing mode
|
|
return (aToMode == aFromMode || !aToMode.IsOrthogonalTo(aFromMode))
|
|
? *this : LogicalSize(aToMode, BSize(), ISize());
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Test if a size is (0, 0).
|
|
*/
|
|
bool IsAllZero() const
|
|
{
|
|
return ISize() == 0 && BSize() == 0;
|
|
}
|
|
|
|
/**
|
|
* Various binary operators on LogicalSize. These are valid ONLY for operands
|
|
* that share the same writing mode.
|
|
*/
|
|
bool operator==(const LogicalSize& aOther) const
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
return mSize == aOther.mSize;
|
|
}
|
|
|
|
bool operator!=(const LogicalSize& aOther) const
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
return mSize != aOther.mSize;
|
|
}
|
|
|
|
LogicalSize operator+(const LogicalSize& aOther) const
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
return LogicalSize(GetWritingMode(), ISize() + aOther.ISize(),
|
|
BSize() + aOther.BSize());
|
|
}
|
|
LogicalSize& operator+=(const LogicalSize& aOther)
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
ISize() += aOther.ISize();
|
|
BSize() += aOther.BSize();
|
|
return *this;
|
|
}
|
|
|
|
LogicalSize operator-(const LogicalSize& aOther) const
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
return LogicalSize(GetWritingMode(), ISize() - aOther.ISize(),
|
|
BSize() - aOther.BSize());
|
|
}
|
|
LogicalSize& operator-=(const LogicalSize& aOther)
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
ISize() -= aOther.ISize();
|
|
BSize() -= aOther.BSize();
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
friend class LogicalRect;
|
|
|
|
LogicalSize() = delete;
|
|
|
|
#ifdef DEBUG
|
|
WritingMode GetWritingMode() const { return mWritingMode; }
|
|
#else
|
|
WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
|
|
#endif
|
|
|
|
nscoord ISize() const // inline-size
|
|
{
|
|
return mSize.width;
|
|
}
|
|
nscoord BSize() const // block-size
|
|
{
|
|
return mSize.height;
|
|
}
|
|
|
|
nscoord& ISize() // inline-size
|
|
{
|
|
return mSize.width;
|
|
}
|
|
nscoord& BSize() // block-size
|
|
{
|
|
return mSize.height;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
WritingMode mWritingMode;
|
|
#endif
|
|
nsSize mSize;
|
|
};
|
|
|
|
/**
|
|
* Flow-relative margin
|
|
*/
|
|
class LogicalMargin {
|
|
public:
|
|
explicit LogicalMargin(WritingMode aWritingMode)
|
|
:
|
|
#ifdef DEBUG
|
|
mWritingMode(aWritingMode),
|
|
#endif
|
|
mMargin(0, 0, 0, 0)
|
|
{ }
|
|
|
|
LogicalMargin(WritingMode aWritingMode,
|
|
nscoord aBStart, nscoord aIEnd,
|
|
nscoord aBEnd, nscoord aIStart)
|
|
:
|
|
#ifdef DEBUG
|
|
mWritingMode(aWritingMode),
|
|
#endif
|
|
mMargin(aBStart, aIEnd, aBEnd, aIStart)
|
|
{ }
|
|
|
|
LogicalMargin(WritingMode aWritingMode, const nsMargin& aPhysicalMargin)
|
|
#ifdef DEBUG
|
|
: mWritingMode(aWritingMode)
|
|
#endif
|
|
{
|
|
if (aWritingMode.IsVertical()) {
|
|
if (aWritingMode.IsVerticalLR()) {
|
|
mMargin.top = aPhysicalMargin.left;
|
|
mMargin.bottom = aPhysicalMargin.right;
|
|
} else {
|
|
mMargin.top = aPhysicalMargin.right;
|
|
mMargin.bottom = aPhysicalMargin.left;
|
|
}
|
|
if (aWritingMode.IsInlineReversed()) {
|
|
mMargin.left = aPhysicalMargin.bottom;
|
|
mMargin.right = aPhysicalMargin.top;
|
|
} else {
|
|
mMargin.left = aPhysicalMargin.top;
|
|
mMargin.right = aPhysicalMargin.bottom;
|
|
}
|
|
} else {
|
|
mMargin.top = aPhysicalMargin.top;
|
|
mMargin.bottom = aPhysicalMargin.bottom;
|
|
if (aWritingMode.IsInlineReversed()) {
|
|
mMargin.left = aPhysicalMargin.right;
|
|
mMargin.right = aPhysicalMargin.left;
|
|
} else {
|
|
mMargin.left = aPhysicalMargin.left;
|
|
mMargin.right = aPhysicalMargin.right;
|
|
}
|
|
}
|
|
}
|
|
|
|
nscoord IStart(WritingMode aWritingMode) const // inline-start margin
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mMargin.left;
|
|
}
|
|
nscoord IEnd(WritingMode aWritingMode) const // inline-end margin
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mMargin.right;
|
|
}
|
|
nscoord BStart(WritingMode aWritingMode) const // block-start margin
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mMargin.top;
|
|
}
|
|
nscoord BEnd(WritingMode aWritingMode) const // block-end margin
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mMargin.bottom;
|
|
}
|
|
|
|
nscoord& IStart(WritingMode aWritingMode) // inline-start margin
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mMargin.left;
|
|
}
|
|
nscoord& IEnd(WritingMode aWritingMode) // inline-end margin
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mMargin.right;
|
|
}
|
|
nscoord& BStart(WritingMode aWritingMode) // block-start margin
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mMargin.top;
|
|
}
|
|
nscoord& BEnd(WritingMode aWritingMode) // block-end margin
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mMargin.bottom;
|
|
}
|
|
|
|
nscoord IStartEnd(WritingMode aWritingMode) const // inline margins
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mMargin.LeftRight();
|
|
}
|
|
nscoord BStartEnd(WritingMode aWritingMode) const // block margins
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mMargin.TopBottom();
|
|
}
|
|
|
|
/*
|
|
* Return margin values for line-relative sides, as defined in
|
|
* http://www.w3.org/TR/css-writing-modes-3/#line-directions:
|
|
*
|
|
* line-left
|
|
* Nominally the side from which LTR text would start.
|
|
* line-right
|
|
* Nominally the side from which RTL text would start. (Opposite of
|
|
* line-left.)
|
|
*/
|
|
nscoord LineLeft(WritingMode aWritingMode) const
|
|
{
|
|
// We don't need to CHECK_WRITING_MODE here because the IStart or IEnd
|
|
// accessor that we call will do it.
|
|
return aWritingMode.IsBidiLTR()
|
|
? IStart(aWritingMode) : IEnd(aWritingMode);
|
|
}
|
|
nscoord LineRight(WritingMode aWritingMode) const
|
|
{
|
|
return aWritingMode.IsBidiLTR()
|
|
? IEnd(aWritingMode) : IStart(aWritingMode);
|
|
}
|
|
|
|
/**
|
|
* Return a LogicalSize representing the total size of the inline-
|
|
* and block-dimension margins.
|
|
*/
|
|
LogicalSize Size(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return LogicalSize(aWritingMode, IStartEnd(), BStartEnd());
|
|
}
|
|
|
|
/**
|
|
* Accessors for physical margins, using our writing mode to convert from
|
|
* logical values.
|
|
*/
|
|
nscoord Top(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical() ?
|
|
(aWritingMode.IsInlineReversed() ? IEnd() : IStart()) : BStart();
|
|
}
|
|
|
|
nscoord Bottom(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical() ?
|
|
(aWritingMode.IsInlineReversed() ? IStart() : IEnd()) : BEnd();
|
|
}
|
|
|
|
nscoord Left(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical() ?
|
|
(aWritingMode.IsVerticalLR() ? BStart() : BEnd()) :
|
|
(aWritingMode.IsInlineReversed() ? IEnd() : IStart());
|
|
}
|
|
|
|
nscoord Right(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical() ?
|
|
(aWritingMode.IsVerticalLR() ? BEnd() : BStart()) :
|
|
(aWritingMode.IsInlineReversed() ? IStart() : IEnd());
|
|
}
|
|
|
|
nscoord LeftRight(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical() ? BStartEnd() : IStartEnd();
|
|
}
|
|
|
|
nscoord TopBottom(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical() ? IStartEnd() : BStartEnd();
|
|
}
|
|
|
|
void SizeTo(WritingMode aWritingMode,
|
|
nscoord aBStart, nscoord aIEnd, nscoord aBEnd, nscoord aIStart)
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
mMargin.SizeTo(aBStart, aIEnd, aBEnd, aIStart);
|
|
}
|
|
|
|
/**
|
|
* Return an nsMargin containing our physical coordinates
|
|
*/
|
|
nsMargin GetPhysicalMargin(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical()
|
|
? (aWritingMode.IsVerticalLR()
|
|
? (aWritingMode.IsInlineReversed()
|
|
? nsMargin(IEnd(), BEnd(), IStart(), BStart())
|
|
: nsMargin(IStart(), BEnd(), IEnd(), BStart()))
|
|
: (aWritingMode.IsInlineReversed()
|
|
? nsMargin(IEnd(), BStart(), IStart(), BEnd())
|
|
: nsMargin(IStart(), BStart(), IEnd(), BEnd())))
|
|
: (aWritingMode.IsInlineReversed()
|
|
? nsMargin(BStart(), IStart(), BEnd(), IEnd())
|
|
: nsMargin(BStart(), IEnd(), BEnd(), IStart()));
|
|
}
|
|
|
|
/**
|
|
* Return a LogicalMargin representing this margin in a different
|
|
* writing mode
|
|
*/
|
|
LogicalMargin ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aFromMode);
|
|
return aToMode == aFromMode ?
|
|
*this : LogicalMargin(aToMode, GetPhysicalMargin(aFromMode));
|
|
}
|
|
|
|
void ApplySkipSides(LogicalSides aSkipSides)
|
|
{
|
|
if (aSkipSides.BStart()) {
|
|
BStart() = 0;
|
|
}
|
|
if (aSkipSides.BEnd()) {
|
|
BEnd() = 0;
|
|
}
|
|
if (aSkipSides.IStart()) {
|
|
IStart() = 0;
|
|
}
|
|
if (aSkipSides.IEnd()) {
|
|
IEnd() = 0;
|
|
}
|
|
}
|
|
|
|
bool IsAllZero() const
|
|
{
|
|
return (mMargin.left == 0 && mMargin.top == 0 &&
|
|
mMargin.right == 0 && mMargin.bottom == 0);
|
|
}
|
|
|
|
LogicalMargin operator+(const LogicalMargin& aMargin) const {
|
|
CHECK_WRITING_MODE(aMargin.GetWritingMode());
|
|
return LogicalMargin(GetWritingMode(),
|
|
BStart() + aMargin.BStart(),
|
|
IEnd() + aMargin.IEnd(),
|
|
BEnd() + aMargin.BEnd(),
|
|
IStart() + aMargin.IStart());
|
|
}
|
|
|
|
LogicalMargin operator+=(const LogicalMargin& aMargin)
|
|
{
|
|
CHECK_WRITING_MODE(aMargin.GetWritingMode());
|
|
mMargin += aMargin.mMargin;
|
|
return *this;
|
|
}
|
|
|
|
LogicalMargin operator-(const LogicalMargin& aMargin) const {
|
|
CHECK_WRITING_MODE(aMargin.GetWritingMode());
|
|
return LogicalMargin(GetWritingMode(),
|
|
BStart() - aMargin.BStart(),
|
|
IEnd() - aMargin.IEnd(),
|
|
BEnd() - aMargin.BEnd(),
|
|
IStart() - aMargin.IStart());
|
|
}
|
|
|
|
private:
|
|
friend class LogicalRect;
|
|
|
|
LogicalMargin() = delete;
|
|
|
|
#ifdef DEBUG
|
|
WritingMode GetWritingMode() const { return mWritingMode; }
|
|
#else
|
|
WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
|
|
#endif
|
|
|
|
nscoord IStart() const // inline-start margin
|
|
{
|
|
return mMargin.left;
|
|
}
|
|
nscoord IEnd() const // inline-end margin
|
|
{
|
|
return mMargin.right;
|
|
}
|
|
nscoord BStart() const // block-start margin
|
|
{
|
|
return mMargin.top;
|
|
}
|
|
nscoord BEnd() const // block-end margin
|
|
{
|
|
return mMargin.bottom;
|
|
}
|
|
|
|
nscoord& IStart() // inline-start margin
|
|
{
|
|
return mMargin.left;
|
|
}
|
|
nscoord& IEnd() // inline-end margin
|
|
{
|
|
return mMargin.right;
|
|
}
|
|
nscoord& BStart() // block-start margin
|
|
{
|
|
return mMargin.top;
|
|
}
|
|
nscoord& BEnd() // block-end margin
|
|
{
|
|
return mMargin.bottom;
|
|
}
|
|
|
|
nscoord IStartEnd() const // inline margins
|
|
{
|
|
return mMargin.LeftRight();
|
|
}
|
|
nscoord BStartEnd() const // block margins
|
|
{
|
|
return mMargin.TopBottom();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
WritingMode mWritingMode;
|
|
#endif
|
|
nsMargin mMargin;
|
|
};
|
|
|
|
/**
|
|
* Flow-relative rectangle
|
|
*/
|
|
class LogicalRect {
|
|
public:
|
|
explicit LogicalRect(WritingMode aWritingMode)
|
|
:
|
|
#ifdef DEBUG
|
|
mWritingMode(aWritingMode),
|
|
#endif
|
|
mIStart(0),
|
|
mBStart(0),
|
|
mISize(0),
|
|
mBSize(0)
|
|
{ }
|
|
|
|
LogicalRect(WritingMode aWritingMode,
|
|
nscoord aIStart, nscoord aBStart,
|
|
nscoord aISize, nscoord aBSize)
|
|
:
|
|
#ifdef DEBUG
|
|
mWritingMode(aWritingMode),
|
|
#endif
|
|
mIStart(aIStart),
|
|
mBStart(aBStart),
|
|
mISize(aISize),
|
|
mBSize(aBSize)
|
|
{ }
|
|
|
|
LogicalRect(WritingMode aWritingMode,
|
|
const LogicalPoint& aOrigin,
|
|
const LogicalSize& aSize)
|
|
:
|
|
#ifdef DEBUG
|
|
mWritingMode(aWritingMode),
|
|
#endif
|
|
mIStart(aOrigin.mPoint.x),
|
|
mBStart(aOrigin.mPoint.y),
|
|
mISize(aSize.mSize.width),
|
|
mBSize(aSize.mSize.height)
|
|
{
|
|
CHECK_WRITING_MODE(aOrigin.GetWritingMode());
|
|
CHECK_WRITING_MODE(aSize.GetWritingMode());
|
|
}
|
|
|
|
LogicalRect(WritingMode aWritingMode,
|
|
const nsRect& aRect,
|
|
const nsSize& aContainerSize)
|
|
#ifdef DEBUG
|
|
: mWritingMode(aWritingMode)
|
|
#endif
|
|
{
|
|
if (aWritingMode.IsVertical()) {
|
|
mBStart = aWritingMode.IsVerticalLR()
|
|
? aRect.X() : aContainerSize.width - aRect.XMost();
|
|
mIStart = aWritingMode.IsInlineReversed()
|
|
? aContainerSize.height - aRect.YMost() : aRect.Y();
|
|
mBSize = aRect.Width();
|
|
mISize = aRect.Height();
|
|
} else {
|
|
mIStart = aWritingMode.IsInlineReversed()
|
|
? aContainerSize.width - aRect.XMost() : aRect.X();
|
|
mBStart = aRect.Y();
|
|
mISize = aRect.Width();
|
|
mBSize = aRect.Height();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Inline- and block-dimension geometry.
|
|
*/
|
|
nscoord IStart(WritingMode aWritingMode) const // inline-start edge
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mIStart;
|
|
}
|
|
nscoord IEnd(WritingMode aWritingMode) const // inline-end edge
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mIStart + mISize;
|
|
}
|
|
nscoord ISize(WritingMode aWritingMode) const // inline-size
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mISize;
|
|
}
|
|
|
|
nscoord BStart(WritingMode aWritingMode) const // block-start edge
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mBStart;
|
|
}
|
|
nscoord BEnd(WritingMode aWritingMode) const // block-end edge
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mBStart + mBSize;
|
|
}
|
|
nscoord BSize(WritingMode aWritingMode) const // block-size
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mBSize;
|
|
}
|
|
|
|
/**
|
|
* Writable (reference) accessors are only available for the basic logical
|
|
* fields (Start and Size), not derivatives like End.
|
|
*/
|
|
nscoord& IStart(WritingMode aWritingMode) // inline-start edge
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mIStart;
|
|
}
|
|
nscoord& ISize(WritingMode aWritingMode) // inline-size
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mISize;
|
|
}
|
|
nscoord& BStart(WritingMode aWritingMode) // block-start edge
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mBStart;
|
|
}
|
|
nscoord& BSize(WritingMode aWritingMode) // block-size
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return mBSize;
|
|
}
|
|
|
|
/**
|
|
* Accessors for line-relative coordinates
|
|
*/
|
|
nscoord LineLeft(WritingMode aWritingMode,
|
|
const nsSize& aContainerSize) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
if (aWritingMode.IsBidiLTR()) {
|
|
return IStart();
|
|
}
|
|
nscoord containerISize =
|
|
aWritingMode.IsVertical() ? aContainerSize.height : aContainerSize.width;
|
|
return containerISize - IEnd();
|
|
}
|
|
nscoord LineRight(WritingMode aWritingMode,
|
|
const nsSize& aContainerSize) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
if (aWritingMode.IsBidiLTR()) {
|
|
return IEnd();
|
|
}
|
|
nscoord containerISize =
|
|
aWritingMode.IsVertical() ? aContainerSize.height : aContainerSize.width;
|
|
return containerISize - IStart();
|
|
}
|
|
|
|
/**
|
|
* Physical coordinates of the rect.
|
|
*/
|
|
nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
if (aWritingMode.IsVertical()) {
|
|
return aWritingMode.IsVerticalLR() ?
|
|
mBStart : aContainerWidth - BEnd();
|
|
} else {
|
|
return aWritingMode.IsInlineReversed() ?
|
|
aContainerWidth - IEnd() : mIStart;
|
|
}
|
|
}
|
|
|
|
nscoord Y(WritingMode aWritingMode, nscoord aContainerHeight) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
if (aWritingMode.IsVertical()) {
|
|
return aWritingMode.IsInlineReversed() ? aContainerHeight - IEnd()
|
|
: mIStart;
|
|
} else {
|
|
return mBStart;
|
|
}
|
|
}
|
|
|
|
nscoord Width(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical() ? mBSize : mISize;
|
|
}
|
|
|
|
nscoord Height(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return aWritingMode.IsVertical() ? mISize : mBSize;
|
|
}
|
|
|
|
nscoord XMost(WritingMode aWritingMode, nscoord aContainerWidth) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
if (aWritingMode.IsVertical()) {
|
|
return aWritingMode.IsVerticalLR() ?
|
|
BEnd() : aContainerWidth - mBStart;
|
|
} else {
|
|
return aWritingMode.IsInlineReversed() ?
|
|
aContainerWidth - mIStart : IEnd();
|
|
}
|
|
}
|
|
|
|
nscoord YMost(WritingMode aWritingMode, nscoord aContainerHeight) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
if (aWritingMode.IsVertical()) {
|
|
return aWritingMode.IsInlineReversed() ? aContainerHeight - mIStart
|
|
: IEnd();
|
|
} else {
|
|
return mBStart;
|
|
}
|
|
}
|
|
|
|
bool IsEmpty() const
|
|
{
|
|
return mISize <= 0 || mBSize <= 0;
|
|
}
|
|
|
|
bool IsAllZero() const
|
|
{
|
|
return (mIStart == 0 && mBStart == 0 &&
|
|
mISize == 0 && mBSize == 0);
|
|
}
|
|
|
|
bool IsZeroSize() const
|
|
{
|
|
return (mISize == 0 && mBSize == 0);
|
|
}
|
|
|
|
void SetEmpty() { mISize = mBSize = 0; }
|
|
|
|
bool IsEqualEdges(const LogicalRect aOther) const
|
|
{
|
|
CHECK_WRITING_MODE(aOther.GetWritingMode());
|
|
bool result = mIStart == aOther.mIStart && mBStart == aOther.mBStart &&
|
|
mISize == aOther.mISize && mBSize == aOther.mBSize;
|
|
|
|
// We want the same result as nsRect, so assert we get it.
|
|
MOZ_ASSERT(result == nsRect(mIStart, mBStart, mISize, mBSize).
|
|
IsEqualEdges(nsRect(aOther.mIStart, aOther.mBStart,
|
|
aOther.mISize, aOther.mBSize)));
|
|
return result;
|
|
}
|
|
|
|
LogicalPoint Origin(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return LogicalPoint(aWritingMode, IStart(), BStart());
|
|
}
|
|
void SetOrigin(WritingMode aWritingMode, const LogicalPoint& aPoint)
|
|
{
|
|
IStart(aWritingMode) = aPoint.I(aWritingMode);
|
|
BStart(aWritingMode) = aPoint.B(aWritingMode);
|
|
}
|
|
|
|
LogicalSize Size(WritingMode aWritingMode) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
return LogicalSize(aWritingMode, ISize(), BSize());
|
|
}
|
|
|
|
LogicalRect operator+(const LogicalPoint& aPoint) const
|
|
{
|
|
CHECK_WRITING_MODE(aPoint.GetWritingMode());
|
|
return LogicalRect(GetWritingMode(),
|
|
IStart() + aPoint.I(), BStart() + aPoint.B(),
|
|
ISize(), BSize());
|
|
}
|
|
|
|
LogicalRect& operator+=(const LogicalPoint& aPoint)
|
|
{
|
|
CHECK_WRITING_MODE(aPoint.GetWritingMode());
|
|
mIStart += aPoint.mPoint.x;
|
|
mBStart += aPoint.mPoint.y;
|
|
return *this;
|
|
}
|
|
|
|
LogicalRect operator-(const LogicalPoint& aPoint) const
|
|
{
|
|
CHECK_WRITING_MODE(aPoint.GetWritingMode());
|
|
return LogicalRect(GetWritingMode(),
|
|
IStart() - aPoint.I(), BStart() - aPoint.B(),
|
|
ISize(), BSize());
|
|
}
|
|
|
|
LogicalRect& operator-=(const LogicalPoint& aPoint)
|
|
{
|
|
CHECK_WRITING_MODE(aPoint.GetWritingMode());
|
|
mIStart -= aPoint.mPoint.x;
|
|
mBStart -= aPoint.mPoint.y;
|
|
return *this;
|
|
}
|
|
|
|
void MoveBy(WritingMode aWritingMode, const LogicalPoint& aDelta)
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
CHECK_WRITING_MODE(aDelta.GetWritingMode());
|
|
IStart() += aDelta.I();
|
|
BStart() += aDelta.B();
|
|
}
|
|
|
|
void Inflate(nscoord aD)
|
|
{
|
|
#ifdef DEBUG
|
|
// Compute using nsRect and assert the results match
|
|
nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
|
|
rectDebug.Inflate(aD);
|
|
#endif
|
|
mIStart -= aD;
|
|
mBStart -= aD;
|
|
mISize += 2 * aD;
|
|
mBSize += 2 * aD;
|
|
MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
|
|
}
|
|
void Inflate(nscoord aDI, nscoord aDB)
|
|
{
|
|
#ifdef DEBUG
|
|
// Compute using nsRect and assert the results match
|
|
nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
|
|
rectDebug.Inflate(aDI, aDB);
|
|
#endif
|
|
mIStart -= aDI;
|
|
mBStart -= aDB;
|
|
mISize += 2 * aDI;
|
|
mBSize += 2 * aDB;
|
|
MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
|
|
}
|
|
void Inflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
CHECK_WRITING_MODE(aMargin.GetWritingMode());
|
|
#ifdef DEBUG
|
|
// Compute using nsRect and assert the results match
|
|
nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
|
|
rectDebug.Inflate(aMargin.mMargin);
|
|
#endif
|
|
mIStart -= aMargin.mMargin.left;
|
|
mBStart -= aMargin.mMargin.top;
|
|
mISize += aMargin.mMargin.LeftRight();
|
|
mBSize += aMargin.mMargin.TopBottom();
|
|
MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
|
|
}
|
|
|
|
void Deflate(nscoord aD)
|
|
{
|
|
#ifdef DEBUG
|
|
// Compute using nsRect and assert the results match
|
|
nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
|
|
rectDebug.Deflate(aD);
|
|
#endif
|
|
mIStart += aD;
|
|
mBStart += aD;
|
|
mISize = std::max(0, mISize - 2 * aD);
|
|
mBSize = std::max(0, mBSize - 2 * aD);
|
|
MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
|
|
}
|
|
void Deflate(nscoord aDI, nscoord aDB)
|
|
{
|
|
#ifdef DEBUG
|
|
// Compute using nsRect and assert the results match
|
|
nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
|
|
rectDebug.Deflate(aDI, aDB);
|
|
#endif
|
|
mIStart += aDI;
|
|
mBStart += aDB;
|
|
mISize = std::max(0, mISize - 2 * aDI);
|
|
mBSize = std::max(0, mBSize - 2 * aDB);
|
|
MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
|
|
}
|
|
void Deflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
CHECK_WRITING_MODE(aMargin.GetWritingMode());
|
|
#ifdef DEBUG
|
|
// Compute using nsRect and assert the results match
|
|
nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
|
|
rectDebug.Deflate(aMargin.mMargin);
|
|
#endif
|
|
mIStart += aMargin.mMargin.left;
|
|
mBStart += aMargin.mMargin.top;
|
|
mISize = std::max(0, mISize - aMargin.mMargin.LeftRight());
|
|
mBSize = std::max(0, mBSize - aMargin.mMargin.TopBottom());
|
|
MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
|
|
}
|
|
|
|
/**
|
|
* Return an nsRect containing our physical coordinates within the given
|
|
* container size.
|
|
*/
|
|
nsRect GetPhysicalRect(WritingMode aWritingMode,
|
|
const nsSize& aContainerSize) const
|
|
{
|
|
CHECK_WRITING_MODE(aWritingMode);
|
|
if (aWritingMode.IsVertical()) {
|
|
return nsRect(aWritingMode.IsVerticalLR()
|
|
? BStart() : aContainerSize.width - BEnd(),
|
|
aWritingMode.IsInlineReversed()
|
|
? aContainerSize.height - IEnd() : IStart(),
|
|
BSize(), ISize());
|
|
} else {
|
|
return nsRect(aWritingMode.IsInlineReversed()
|
|
? aContainerSize.width - IEnd() : IStart(),
|
|
BStart(), ISize(), BSize());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return a LogicalRect representing this rect in a different writing mode
|
|
*/
|
|
LogicalRect ConvertTo(WritingMode aToMode, WritingMode aFromMode,
|
|
const nsSize& aContainerSize) const
|
|
{
|
|
CHECK_WRITING_MODE(aFromMode);
|
|
return aToMode == aFromMode ?
|
|
*this : LogicalRect(aToMode, GetPhysicalRect(aFromMode, aContainerSize),
|
|
aContainerSize);
|
|
}
|
|
|
|
/**
|
|
* Set *this to be the rectangle containing the intersection of aRect1
|
|
* and aRect2, return whether the intersection is non-empty.
|
|
*/
|
|
bool IntersectRect(const LogicalRect& aRect1, const LogicalRect& aRect2)
|
|
{
|
|
CHECK_WRITING_MODE(aRect1.mWritingMode);
|
|
CHECK_WRITING_MODE(aRect2.mWritingMode);
|
|
#ifdef DEBUG
|
|
// Compute using nsRect and assert the results match
|
|
nsRect rectDebug;
|
|
rectDebug.IntersectRect(nsRect(aRect1.mIStart, aRect1.mBStart,
|
|
aRect1.mISize, aRect1.mBSize),
|
|
nsRect(aRect2.mIStart, aRect2.mBStart,
|
|
aRect2.mISize, aRect2.mBSize));
|
|
#endif
|
|
|
|
nscoord iEnd = std::min(aRect1.IEnd(), aRect2.IEnd());
|
|
mIStart = std::max(aRect1.mIStart, aRect2.mIStart);
|
|
mISize = iEnd - mIStart;
|
|
|
|
nscoord bEnd = std::min(aRect1.BEnd(), aRect2.BEnd());
|
|
mBStart = std::max(aRect1.mBStart, aRect2.mBStart);
|
|
mBSize = bEnd - mBStart;
|
|
|
|
if (mISize < 0 || mBSize < 0) {
|
|
mISize = 0;
|
|
mBSize = 0;
|
|
}
|
|
|
|
MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
|
|
return mISize > 0 && mBSize > 0;
|
|
}
|
|
|
|
private:
|
|
LogicalRect() = delete;
|
|
|
|
#ifdef DEBUG
|
|
WritingMode GetWritingMode() const { return mWritingMode; }
|
|
#else
|
|
WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
|
|
#endif
|
|
|
|
nscoord IStart() const // inline-start edge
|
|
{
|
|
return mIStart;
|
|
}
|
|
nscoord IEnd() const // inline-end edge
|
|
{
|
|
return mIStart + mISize;
|
|
}
|
|
nscoord ISize() const // inline-size
|
|
{
|
|
return mISize;
|
|
}
|
|
|
|
nscoord BStart() const // block-start edge
|
|
{
|
|
return mBStart;
|
|
}
|
|
nscoord BEnd() const // block-end edge
|
|
{
|
|
return mBStart + mBSize;
|
|
}
|
|
nscoord BSize() const // block-size
|
|
{
|
|
return mBSize;
|
|
}
|
|
|
|
nscoord& IStart() // inline-start edge
|
|
{
|
|
return mIStart;
|
|
}
|
|
nscoord& ISize() // inline-size
|
|
{
|
|
return mISize;
|
|
}
|
|
nscoord& BStart() // block-start edge
|
|
{
|
|
return mBStart;
|
|
}
|
|
nscoord& BSize() // block-size
|
|
{
|
|
return mBSize;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
WritingMode mWritingMode;
|
|
#endif
|
|
// Inline- and block-geometry dimension
|
|
nscoord mIStart; // inline-start edge
|
|
nscoord mBStart; // block-start edge
|
|
nscoord mISize; // inline-size
|
|
nscoord mBSize; // block-size
|
|
};
|
|
|
|
} // namespace mozilla
|
|
|
|
// Definitions of inline methods for nsStyleSides, declared in nsStyleCoord.h
|
|
// but not defined there because they need WritingMode.
|
|
inline nsStyleUnit nsStyleSides::GetUnit(mozilla::WritingMode aWM,
|
|
mozilla::LogicalSide aSide) const
|
|
{
|
|
return GetUnit(aWM.PhysicalSide(aSide));
|
|
}
|
|
|
|
inline nsStyleUnit nsStyleSides::GetIStartUnit(mozilla::WritingMode aWM) const
|
|
{
|
|
return GetUnit(aWM, mozilla::eLogicalSideIStart);
|
|
}
|
|
|
|
inline nsStyleUnit nsStyleSides::GetBStartUnit(mozilla::WritingMode aWM) const
|
|
{
|
|
return GetUnit(aWM, mozilla::eLogicalSideBStart);
|
|
}
|
|
|
|
inline nsStyleUnit nsStyleSides::GetIEndUnit(mozilla::WritingMode aWM) const
|
|
{
|
|
return GetUnit(aWM, mozilla::eLogicalSideIEnd);
|
|
}
|
|
|
|
inline nsStyleUnit nsStyleSides::GetBEndUnit(mozilla::WritingMode aWM) const
|
|
{
|
|
return GetUnit(aWM, mozilla::eLogicalSideBEnd);
|
|
}
|
|
|
|
inline bool nsStyleSides::HasBlockAxisAuto(mozilla::WritingMode aWM) const
|
|
{
|
|
return GetBStartUnit(aWM) == eStyleUnit_Auto ||
|
|
GetBEndUnit(aWM) == eStyleUnit_Auto;
|
|
}
|
|
|
|
inline bool nsStyleSides::HasInlineAxisAuto(mozilla::WritingMode aWM) const
|
|
{
|
|
return GetIStartUnit(aWM) == eStyleUnit_Auto ||
|
|
GetIEndUnit(aWM) == eStyleUnit_Auto;
|
|
}
|
|
|
|
inline nsStyleCoord nsStyleSides::Get(mozilla::WritingMode aWM,
|
|
mozilla::LogicalSide aSide) const
|
|
{
|
|
return Get(aWM.PhysicalSide(aSide));
|
|
}
|
|
|
|
inline nsStyleCoord nsStyleSides::GetIStart(mozilla::WritingMode aWM) const
|
|
{
|
|
return Get(aWM, mozilla::eLogicalSideIStart);
|
|
}
|
|
|
|
inline nsStyleCoord nsStyleSides::GetBStart(mozilla::WritingMode aWM) const
|
|
{
|
|
return Get(aWM, mozilla::eLogicalSideBStart);
|
|
}
|
|
|
|
inline nsStyleCoord nsStyleSides::GetIEnd(mozilla::WritingMode aWM) const
|
|
{
|
|
return Get(aWM, mozilla::eLogicalSideIEnd);
|
|
}
|
|
|
|
inline nsStyleCoord nsStyleSides::GetBEnd(mozilla::WritingMode aWM) const
|
|
{
|
|
return Get(aWM, mozilla::eLogicalSideBEnd);
|
|
}
|
|
|
|
// Definitions of inline methods for nsStylePosition, declared in
|
|
// nsStyleStruct.h but not defined there because they need WritingMode.
|
|
inline nsStyleCoord& nsStylePosition::ISize(mozilla::WritingMode aWM)
|
|
{
|
|
return aWM.IsVertical() ? mHeight : mWidth;
|
|
}
|
|
inline nsStyleCoord& nsStylePosition::MinISize(mozilla::WritingMode aWM)
|
|
{
|
|
return aWM.IsVertical() ? mMinHeight : mMinWidth;
|
|
}
|
|
inline nsStyleCoord& nsStylePosition::MaxISize(mozilla::WritingMode aWM)
|
|
{
|
|
return aWM.IsVertical() ? mMaxHeight : mMaxWidth;
|
|
}
|
|
inline nsStyleCoord& nsStylePosition::BSize(mozilla::WritingMode aWM)
|
|
{
|
|
return aWM.IsVertical() ? mWidth : mHeight;
|
|
}
|
|
inline nsStyleCoord& nsStylePosition::MinBSize(mozilla::WritingMode aWM)
|
|
{
|
|
return aWM.IsVertical() ? mMinWidth : mMinHeight;
|
|
}
|
|
inline nsStyleCoord& nsStylePosition::MaxBSize(mozilla::WritingMode aWM)
|
|
{
|
|
return aWM.IsVertical() ? mMaxWidth : mMaxHeight;
|
|
}
|
|
|
|
inline const nsStyleCoord&
|
|
nsStylePosition::ISize(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? mHeight : mWidth;
|
|
}
|
|
inline const nsStyleCoord&
|
|
nsStylePosition::MinISize(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? mMinHeight : mMinWidth;
|
|
}
|
|
inline const nsStyleCoord&
|
|
nsStylePosition::MaxISize(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? mMaxHeight : mMaxWidth;
|
|
}
|
|
inline const nsStyleCoord&
|
|
nsStylePosition::BSize(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? mWidth : mHeight;
|
|
}
|
|
inline const nsStyleCoord&
|
|
nsStylePosition::MinBSize(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? mMinWidth : mMinHeight;
|
|
}
|
|
inline const nsStyleCoord&
|
|
nsStylePosition::MaxBSize(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? mMaxWidth : mMaxHeight;
|
|
}
|
|
|
|
inline bool
|
|
nsStylePosition::ISizeDependsOnContainer(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? HeightDependsOnContainer()
|
|
: WidthDependsOnContainer();
|
|
}
|
|
inline bool
|
|
nsStylePosition::MinISizeDependsOnContainer(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? MinHeightDependsOnContainer()
|
|
: MinWidthDependsOnContainer();
|
|
}
|
|
inline bool
|
|
nsStylePosition::MaxISizeDependsOnContainer(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? MaxHeightDependsOnContainer()
|
|
: MaxWidthDependsOnContainer();
|
|
}
|
|
inline bool
|
|
nsStylePosition::BSizeDependsOnContainer(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? WidthDependsOnContainer()
|
|
: HeightDependsOnContainer();
|
|
}
|
|
inline bool
|
|
nsStylePosition::MinBSizeDependsOnContainer(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? MinWidthDependsOnContainer()
|
|
: MinHeightDependsOnContainer();
|
|
}
|
|
inline bool
|
|
nsStylePosition::MaxBSizeDependsOnContainer(mozilla::WritingMode aWM) const
|
|
{
|
|
return aWM.IsVertical() ? MaxWidthDependsOnContainer()
|
|
: MaxHeightDependsOnContainer();
|
|
}
|
|
|
|
inline mozilla::StyleFloat
|
|
nsStyleDisplay::PhysicalFloats(mozilla::WritingMode aWM) const
|
|
{
|
|
using StyleFloat = mozilla::StyleFloat;
|
|
if (mFloat == StyleFloat::InlineStart) {
|
|
return aWM.IsBidiLTR() ? StyleFloat::Left : StyleFloat::Right;
|
|
}
|
|
if (mFloat == StyleFloat::InlineEnd) {
|
|
return aWM.IsBidiLTR() ? StyleFloat::Right : StyleFloat::Left;
|
|
}
|
|
return mFloat;
|
|
}
|
|
|
|
inline mozilla::StyleClear
|
|
nsStyleDisplay::PhysicalBreakType(mozilla::WritingMode aWM) const
|
|
{
|
|
using StyleClear = mozilla::StyleClear;
|
|
if (mBreakType == StyleClear::InlineStart) {
|
|
return aWM.IsBidiLTR() ? StyleClear::Left : StyleClear::Right;
|
|
}
|
|
if (mBreakType == StyleClear::InlineEnd) {
|
|
return aWM.IsBidiLTR() ? StyleClear::Right : StyleClear::Left;
|
|
}
|
|
return mBreakType;
|
|
}
|
|
|
|
inline bool
|
|
nsStyleMargin::HasBlockAxisAuto(mozilla::WritingMode aWM) const
|
|
{
|
|
return mMargin.HasBlockAxisAuto(aWM);
|
|
}
|
|
inline bool
|
|
nsStyleMargin::HasInlineAxisAuto(mozilla::WritingMode aWM) const
|
|
{
|
|
return mMargin.HasInlineAxisAuto(aWM);
|
|
}
|
|
|
|
#endif // WritingModes_h_
|