layout/generic/WritingModes.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #ifndef WritingModes_h_
     7 #define WritingModes_h_
     9 #include "nsRect.h"
    10 #include "nsStyleStruct.h"
    12 // If WRITING_MODE_VERTICAL_ENABLED is defined, we will attempt to support
    13 // the vertical writing-mode values; if it is not defined, then
    14 // WritingMode.IsVertical() will be hard-coded to return false, allowing
    15 // many conditional branches to be optimized away while we're in the process
    16 // of transitioning layout to use writing-mode and logical directions, but
    17 // not yet ready to ship vertical support.
    19 /* #define WRITING_MODE_VERTICAL_ENABLED 1 */
    21 // It is the caller's responsibility to operate on logical-coordinate objects
    22 // with matched writing modes. Failure to do so will be a runtime bug; the
    23 // compiler can't catch it, but in debug mode, we'll throw an assertion.
    24 // NOTE that in non-debug builds, a writing mode mismatch error will NOT be
    25 // detected, yet the results will be nonsense (and may lead to further layout
    26 // failures). Therefore, it is important to test (and fuzz-test) writing-mode
    27 // support using debug builds.
    29 // Methods in logical-coordinate classes that take another logical-coordinate
    30 // object as a parameter should call CHECK_WRITING_MODE on it to verify that
    31 // the writing modes match.
    32 // (In some cases, there are internal (private) methods that don't do this;
    33 // such methods should only be used by other methods that have already checked
    34 // the writing modes.)
    36 #define CHECK_WRITING_MODE(param) \
    37    NS_ASSERTION(param == mWritingMode, "writing-mode mismatch")
    39 /**
    40  * mozilla::WritingMode is an immutable class representing a
    41  * writing mode.
    42  *
    43  * It efficiently stores the writing mode and can rapidly compute
    44  * interesting things about it for use in layout.
    45  *
    46  * Writing modes are computed from the CSS 'direction',
    47  * 'writing-mode', and 'text-orientation' properties.
    48  * See CSS3 Writing Modes for more information
    49  *   http://www.w3.org/TR/css3-writing-modes/
    50  */
    52 namespace mozilla {
    54 class WritingMode {
    55 public:
    56   /**
    57    * Absolute inline flow direction
    58    */
    59   enum InlineDir {
    60     eInlineLTR = 0x00, // text flows horizontally left to right
    61     eInlineRTL = 0x02, // text flows horizontally right to left
    62     eInlineTTB = 0x01, // text flows vertically top to bottom
    63     eInlineBTT = 0x03, // text flows vertically bottom to top
    64   };
    66   /**
    67    * Absolute block flow direction
    68    */
    69   enum BlockDir {
    70     eBlockTB = 0x00, // horizontal lines stack top to bottom
    71     eBlockRL = 0x01, // vertical lines stack right to left
    72     eBlockLR = 0x05, // vertical lines stack left to right
    73   };
    75   /**
    76    * Line-relative (bidi-relative) inline flow direction
    77    */
    78   enum BidiDir {
    79     eBidiLTR = 0x00, // inline flow matches bidi LTR text
    80     eBidiRTL = 0x10, // inline flow matches bidi RTL text
    81   };
    83   /**
    84    * Unknown writing mode (should never actually be stored or used anywhere).
    85    */
    86   enum {
    87     eUnknownWritingMode = 0xff
    88   };
    90   /**
    91    * Return the absolute inline flow direction as an InlineDir
    92    */
    93   InlineDir GetInlineDir() const { return InlineDir(mWritingMode & eInlineMask); }
    95   /**
    96    * Return the absolute block flow direction as a BlockDir
    97    */
    98   BlockDir GetBlockDir() const { return BlockDir(mWritingMode & eBlockMask); }
   100   /**
   101    * Return the line-relative inline flow direction as a BidiDir
   102    */
   103   BidiDir GetBidiDir() const { return BidiDir(mWritingMode & eBidiMask); }
   105   /**
   106    * Return true if LTR. (Convenience method)
   107    */
   108   bool IsBidiLTR() const { return eBidiLTR == (mWritingMode & eBidiMask); }
   110   /**
   111    * True if vertical-mode block direction is LR (convenience method).
   112    */
   113   bool IsVerticalLR() const { return eBlockLR == (mWritingMode & eBlockMask); }
   115   /**
   116    * True if vertical writing mode, i.e. when
   117    * writing-mode: vertical-lr | vertical-rl.
   118    */
   119 #ifdef WRITING_MODE_VERTICAL_ENABLED
   120   bool IsVertical() const { return !!(mWritingMode & eOrientationMask); }
   121 #else
   122   bool IsVertical() const { return false; }
   123 #endif
   125   /**
   126    * True if line-over/line-under are inverted from block-start/block-end.
   127    * This is true when
   128    *   - writing-mode is vertical-rl && text-orientation is sideways-left
   129    *   - writing-mode is vertical-lr && text-orientation is not sideways-left
   130    */
   131 #ifdef WRITING_MODE_VERTICAL_ENABLED
   132   bool IsLineInverted() const { return !!(mWritingMode & eLineOrientMask); }
   133 #else
   134   bool IsLineInverted() const { return false; }
   135 #endif
   137   /**
   138    * Block-axis flow-relative to line-relative factor.
   139    * May be used as a multiplication factor for block-axis coordinates
   140    * to convert between flow- and line-relative coordinate systems (e.g.
   141    * positioning an over- or under-line decoration).
   142    */
   143   int FlowRelativeToLineRelativeFactor() const
   144   {
   145     return IsLineInverted() ? -1 : 1;
   146   }
   148   /**
   149    * Default constructor gives us a horizontal, LTR writing mode.
   150    * XXX We will probably eliminate this and require explicit initialization
   151    *     in all cases once transition is complete.
   152    */
   153   WritingMode()
   154     : mWritingMode(0)
   155   { }
   157   /**
   158    * Construct writing mode based on a style context
   159    */
   160   WritingMode(const nsStyleVisibility* aStyleVisibility)
   161   {
   162     NS_ASSERTION(aStyleVisibility, "we need an nsStyleVisibility here");
   164 #ifdef WRITING_MODE_VERTICAL_ENABLED
   165     switch (aStyleVisibility->mWritingMode) {
   166       case NS_STYLE_WRITING_MODE_HORIZONTAL_TB:
   167         mWritingMode = 0;
   168         break;
   170       case NS_STYLE_WRITING_MODE_VERTICAL_LR:
   171         mWritingMode = eBlockFlowMask |
   172                        eLineOrientMask | //XXX needs update when text-orientation added
   173                        eOrientationMask;
   174         break;
   176       case NS_STYLE_WRITING_MODE_VERTICAL_RL:
   177         mWritingMode = eOrientationMask;
   178         break;
   180       default:
   181         NS_NOTREACHED("unknown writing mode!");
   182         mWritingMode = 0;
   183         break;
   184     }
   185 #else
   186     mWritingMode = 0;
   187 #endif
   189     if (NS_STYLE_DIRECTION_RTL == aStyleVisibility->mDirection) {
   190       mWritingMode |= eInlineFlowMask | //XXX needs update when text-orientation added
   191                       eBidiMask;
   192     }
   193   }
   195   // For unicode-bidi: plaintext, reset the direction of the writing mode from
   196   // the bidi paragraph level of the content
   198   //XXX change uint8_t to UBiDiLevel after bug 924851
   199   void SetDirectionFromBidiLevel(uint8_t level)
   200   {
   201     if (level & 1) {
   202       // odd level, set RTL
   203       mWritingMode |= eBidiMask;
   204     } else {
   205       // even level, set LTR
   206       mWritingMode &= ~eBidiMask;
   207     }
   208   }
   210   /**
   211    * Compare two WritingModes for equality.
   212    */
   213   bool operator==(const WritingMode& aOther) const
   214   {
   215     return mWritingMode == aOther.mWritingMode;
   216   }
   218 private:
   219   friend class LogicalPoint;
   220   friend class LogicalSize;
   221   friend class LogicalMargin;
   222   friend class LogicalRect;
   224   /**
   225    * Return a WritingMode representing an unknown value.
   226    */
   227   static inline WritingMode Unknown()
   228   {
   229     return WritingMode(eUnknownWritingMode);
   230   }
   232   /**
   233    * Constructing a WritingMode with an arbitrary value is a private operation
   234    * currently only used by the Unknown() static method.
   235    */
   236   WritingMode(uint8_t aValue)
   237     : mWritingMode(aValue)
   238   { }
   240   uint8_t mWritingMode;
   242   enum Masks {
   243     // Masks for our bits; true chosen as opposite of commonest case
   244     eOrientationMask = 0x01, // true means vertical text
   245     eInlineFlowMask  = 0x02, // true means absolute RTL/BTT (against physical coords)
   246     eBlockFlowMask   = 0x04, // true means vertical-LR (or horizontal-BT if added)
   247     eLineOrientMask  = 0x08, // true means over != block-start
   248     eBidiMask        = 0x10, // true means line-relative RTL (bidi RTL)
   249     // Note: We have one excess bit of info; WritingMode can pack into 4 bits.
   250     // But since we have space, we're caching interesting things for fast access.
   252     // Masks for output enums
   253     eInlineMask = 0x03,
   254     eBlockMask  = 0x05
   255   };
   256 };
   259 /**
   260  * Logical-coordinate classes:
   261  *
   262  * There are three sets of coordinate space:
   263  *   - physical (top, left, bottom, right)
   264  *       relative to graphics coord system
   265  *   - flow-relative (block-start, inline-start, block-end, inline-end)
   266  *       relative to block/inline flow directions
   267  *   - line-relative (line-over, line-left, line-under, line-right)
   268  *       relative to glyph orientation / inline bidi directions
   269  * See CSS3 Writing Modes for more information
   270  *   http://www.w3.org/TR/css3-writing-modes/#abstract-box
   271  *
   272  * For shorthand, B represents the block-axis
   273  *                I represents the inline-axis
   274  *
   275  * The flow-relative geometric classes store coords in flow-relative space.
   276  * They use a private ns{Point,Size,Rect,Margin} member to store the actual
   277  * coordinate values, but reinterpret them as logical instead of physical.
   278  * This allows us to easily perform calculations in logical space (provided
   279  * writing modes of the operands match), by simply mapping to nsPoint (etc)
   280  * methods.
   281  *
   282  * Physical-coordinate accessors/setters are responsible to translate these
   283  * internal logical values as necessary.
   284  *
   285  * In DEBUG builds, the logical types store their WritingMode and check
   286  * that the same WritingMode is passed whenever callers ask them to do a
   287  * writing-mode-dependent operation. Non-DEBUG builds do NOT check this,
   288  * to avoid the overhead of storing WritingMode fields.
   289  *
   290  * Open question: do we need a different set optimized for line-relative
   291  * math, for use in nsLineLayout and the like? Or is multiplying values
   292  * by FlowRelativeToLineRelativeFactor() enough?
   293  */
   295 /**
   296  * Flow-relative point
   297  */
   298 class LogicalPoint {
   299 public:
   300   LogicalPoint(WritingMode aWritingMode)
   301     :
   302 #ifdef DEBUG
   303       mWritingMode(aWritingMode),
   304 #endif
   305       mPoint(0, 0)
   306   { }
   308   // Construct from a writing mode and individual coordinates (which MUST be
   309   // values in that writing mode, NOT physical coordinates!)
   310   LogicalPoint(WritingMode aWritingMode, nscoord aI, nscoord aB)
   311     :
   312 #ifdef DEBUG
   313       mWritingMode(aWritingMode),
   314 #endif
   315       mPoint(aI, aB)
   316   { }
   318   // Construct from a writing mode and a physical point, within a given
   319   // containing rectangle's width (defining the conversion between LTR
   320   // and RTL coordinates).
   321   LogicalPoint(WritingMode aWritingMode,
   322                const nsPoint& aPoint,
   323                nscoord aContainerWidth)
   324 #ifdef DEBUG
   325     : mWritingMode(aWritingMode)
   326 #endif
   327   {
   328     if (aWritingMode.IsVertical()) {
   329       I() = aPoint.y;
   330       B() = aWritingMode.IsVerticalLR() ? aPoint.x : aContainerWidth - aPoint.x;
   331     } else {
   332       I() = aWritingMode.IsBidiLTR() ? aPoint.x : aContainerWidth - aPoint.x;
   333       B() = aPoint.y;
   334     }
   335   }
   337   /**
   338    * Read-only (const) access to the coordinates, in both logical
   339    * and physical terms.
   340    */
   341   nscoord I(WritingMode aWritingMode) const // inline-axis
   342   {
   343     CHECK_WRITING_MODE(aWritingMode);
   344     return mPoint.x;
   345   }
   346   nscoord B(WritingMode aWritingMode) const // block-axis
   347   {
   348     CHECK_WRITING_MODE(aWritingMode);
   349     return mPoint.y;
   350   }
   352   nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const
   353   {
   354     CHECK_WRITING_MODE(aWritingMode);
   355     if (aWritingMode.IsVertical()) {
   356       return aWritingMode.IsVerticalLR() ? B() : aContainerWidth - B();
   357     } else {
   358       return aWritingMode.IsBidiLTR() ? I() : aContainerWidth - I();
   359     }
   360   }
   361   nscoord Y(WritingMode aWritingMode) const
   362   {
   363     CHECK_WRITING_MODE(aWritingMode);
   364     return aWritingMode.IsVertical() ? I() : B();
   365   }
   367   /**
   368    * These non-const accessors return a reference (lvalue) that can be
   369    * assigned to by callers.
   370    */
   371   nscoord& I(WritingMode aWritingMode) // inline-axis
   372   {
   373     CHECK_WRITING_MODE(aWritingMode);
   374     return mPoint.x;
   375   }
   376   nscoord& B(WritingMode aWritingMode) // block-axis
   377   {
   378     CHECK_WRITING_MODE(aWritingMode);
   379     return mPoint.y;
   380   }
   382   /**
   383    * Setters for the physical coordinates.
   384    */
   385   void SetX(WritingMode aWritingMode, nscoord aX, nscoord aContainerWidth)
   386   {
   387     CHECK_WRITING_MODE(aWritingMode);
   388     if (aWritingMode.IsVertical()) {
   389       B() = aWritingMode.IsVerticalLR() ? aX : aContainerWidth - aX;
   390     } else {
   391       I() = aWritingMode.IsBidiLTR() ? aX : aContainerWidth - aX;
   392     }
   393   }
   394   void SetY(WritingMode aWritingMode, nscoord aY)
   395   {
   396     CHECK_WRITING_MODE(aWritingMode);
   397     if (aWritingMode.IsVertical()) {
   398       B() = aY;
   399     } else {
   400       I() = aY;
   401     }
   402   }
   404   /**
   405    * Return a physical point corresponding to our logical coordinates,
   406    * converted according to our writing mode.
   407    */
   408   nsPoint GetPhysicalPoint(WritingMode aWritingMode,
   409                            nscoord aContainerWidth) const
   410   {
   411     CHECK_WRITING_MODE(aWritingMode);
   412     if (aWritingMode.IsVertical()) {
   413       return nsPoint(aWritingMode.IsVerticalLR() ? B() : aContainerWidth - B(),
   414                      I());
   415     } else {
   416       return nsPoint(aWritingMode.IsBidiLTR() ? I() : aContainerWidth - I(),
   417                      B());
   418     }
   419   }
   421   /**
   422    * Return the equivalent point in a different writing mode.
   423    */
   424   LogicalPoint ConvertTo(WritingMode aToMode, WritingMode aFromMode,
   425                          nscoord aContainerWidth) const
   426   {
   427     CHECK_WRITING_MODE(aFromMode);
   428     return aToMode == aFromMode ?
   429       *this : LogicalPoint(aToMode,
   430                            GetPhysicalPoint(aFromMode, aContainerWidth),
   431                            aContainerWidth);
   432   }
   434   LogicalPoint operator+(const LogicalPoint& aOther) const
   435   {
   436     CHECK_WRITING_MODE(aOther.GetWritingMode());
   437     // In non-debug builds, LogicalPoint does not store the WritingMode,
   438     // so the first parameter here (which will always be eUnknownWritingMode)
   439     // is ignored.
   440     return LogicalPoint(GetWritingMode(),
   441                         mPoint.x + aOther.mPoint.x,
   442                         mPoint.y + aOther.mPoint.y);
   443   }
   445 private:
   446   friend class LogicalRect;
   448   /**
   449    * NOTE that in non-DEBUG builds, GetWritingMode() always returns
   450    * eUnknownWritingMode, as the current mode is not stored in the logical-
   451    * geometry classes. Therefore, this method is private; it is used ONLY
   452    * by the DEBUG-mode checking macros in this class and its friends;
   453    * other code is not allowed to ask a logical point for its writing mode,
   454    * as this info will simply not be available in non-DEBUG builds.
   455    *
   456    * Also, in non-DEBUG builds, CHECK_WRITING_MODE does nothing, and the
   457    * WritingMode parameter to logical methods will generally be optimized
   458    * away altogether.
   459    */
   460 #ifdef DEBUG
   461   WritingMode GetWritingMode() const { return mWritingMode; }
   462 #else
   463   WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
   464 #endif
   466   // We don't allow construction of a LogicalPoint with no writing mode.
   467   LogicalPoint() MOZ_DELETE;
   469   // Accessors that don't take or check a WritingMode value.
   470   // These are for internal use only; they are called by methods that have
   471   // themselves already checked the WritingMode passed by the caller.
   472   nscoord I() const // inline-axis
   473   {
   474     return mPoint.x;
   475   }
   476   nscoord B() const // block-axis
   477   {
   478     return mPoint.y;
   479   }
   481   nscoord& I() // inline-axis
   482   {
   483     return mPoint.x;
   484   }
   485   nscoord& B() // block-axis
   486   {
   487     return mPoint.y;
   488   }
   490   WritingMode mWritingMode;
   492   // We use an nsPoint to hold the coordinates, but reinterpret its .x and .y
   493   // fields as the inline and block directions. Hence, this is not exposed
   494   // directly, but only through accessors that will map them according to the
   495   // writing mode.
   496   nsPoint mPoint;
   497 };
   499 /**
   500  * Flow-relative size
   501  */
   502 class LogicalSize {
   503 public:
   504   LogicalSize(WritingMode aWritingMode)
   505     :
   506 #ifdef DEBUG
   507       mWritingMode(aWritingMode),
   508 #endif
   509       mSize(0, 0)
   510   { }
   512   LogicalSize(WritingMode aWritingMode, nscoord aISize, nscoord aBSize)
   513     :
   514 #ifdef DEBUG
   515       mWritingMode(aWritingMode),
   516 #endif
   517       mSize(aISize, aBSize)
   518   { }
   520   LogicalSize(WritingMode aWritingMode, const nsSize& aPhysicalSize)
   521 #ifdef DEBUG
   522     : mWritingMode(aWritingMode)
   523 #endif
   524   {
   525     if (aWritingMode.IsVertical()) {
   526       ISize() = aPhysicalSize.height;
   527       BSize() = aPhysicalSize.width;
   528     } else {
   529       ISize() = aPhysicalSize.width;
   530       BSize() = aPhysicalSize.height;
   531     }
   532   }
   534   /**
   535    * Dimensions in logical and physical terms
   536    */
   537   nscoord ISize(WritingMode aWritingMode) const // inline-size
   538   {
   539     CHECK_WRITING_MODE(aWritingMode);
   540     return mSize.width;
   541   }
   542   nscoord BSize(WritingMode aWritingMode) const // block-size
   543   {
   544     CHECK_WRITING_MODE(aWritingMode);
   545     return mSize.height;
   546   }
   548   nscoord Width(WritingMode aWritingMode) const
   549   {
   550     CHECK_WRITING_MODE(aWritingMode);
   551     return aWritingMode.IsVertical() ? BSize() : ISize();
   552   }
   553   nscoord Height(WritingMode aWritingMode) const
   554   {
   555     CHECK_WRITING_MODE(aWritingMode);
   556     return aWritingMode.IsVertical() ? ISize() : BSize();
   557   }
   559   /**
   560    * Writable references to the logical dimensions
   561    */
   562   nscoord& ISize(WritingMode aWritingMode) // inline-size
   563   {
   564     CHECK_WRITING_MODE(aWritingMode);
   565     return mSize.width;
   566   }
   567   nscoord& BSize(WritingMode aWritingMode) // block-size
   568   {
   569     CHECK_WRITING_MODE(aWritingMode);
   570     return mSize.height;
   571   }
   573   /**
   574    * Setters for the physical dimensions
   575    */
   576   void SetWidth(WritingMode aWritingMode, nscoord aWidth)
   577   {
   578     CHECK_WRITING_MODE(aWritingMode);
   579     if (aWritingMode.IsVertical()) {
   580       BSize() = aWidth;
   581     } else {
   582       ISize() = aWidth;
   583     }
   584   }
   585   void SetHeight(WritingMode aWritingMode, nscoord aHeight)
   586   {
   587     CHECK_WRITING_MODE(aWritingMode);
   588     if (aWritingMode.IsVertical()) {
   589       ISize() = aHeight;
   590     } else {
   591       BSize() = aHeight;
   592     }
   593   }
   595   /**
   596    * Return an nsSize containing our physical dimensions
   597    */
   598   nsSize GetPhysicalSize(WritingMode aWritingMode) const
   599   {
   600     CHECK_WRITING_MODE(aWritingMode);
   601     return aWritingMode.IsVertical() ?
   602       nsSize(BSize(), ISize()) : nsSize(ISize(), BSize());
   603   }
   605   /**
   606    * Return a LogicalSize representing this size in a different writing mode
   607    */
   608   LogicalSize ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
   609   {
   610     CHECK_WRITING_MODE(aFromMode);
   611     return aToMode == aFromMode ?
   612       *this : LogicalSize(aToMode, GetPhysicalSize(aFromMode));
   613   }
   615 private:
   616   friend class LogicalRect;
   618   LogicalSize() MOZ_DELETE;
   620 #ifdef DEBUG
   621   WritingMode GetWritingMode() const { return mWritingMode; }
   622 #else
   623   WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
   624 #endif
   626   nscoord ISize() const // inline-size
   627   {
   628     return mSize.width;
   629   }
   630   nscoord BSize() const // block-size
   631   {
   632     return mSize.height;
   633   }
   635   nscoord& ISize() // inline-size
   636   {
   637     return mSize.width;
   638   }
   639   nscoord& BSize() // block-size
   640   {
   641     return mSize.height;
   642   }
   644   WritingMode mWritingMode;
   645   nsSize      mSize;
   646 };
   648 /**
   649  * Flow-relative margin
   650  */
   651 class LogicalMargin {
   652 public:
   653   LogicalMargin(WritingMode aWritingMode)
   654     :
   655 #ifdef DEBUG
   656       mWritingMode(aWritingMode),
   657 #endif
   658       mMargin(0, 0, 0, 0)
   659   { }
   661   LogicalMargin(WritingMode aWritingMode,
   662                 nscoord aBStart, nscoord aIEnd,
   663                 nscoord aBEnd, nscoord aIStart)
   664     :
   665 #ifdef DEBUG
   666       mWritingMode(aWritingMode),
   667 #endif
   668       mMargin(aBStart, aIEnd, aBEnd, aIStart)
   669   { }
   671   LogicalMargin(WritingMode aWritingMode, const nsMargin& aPhysicalMargin)
   672 #ifdef DEBUG
   673     : mWritingMode(aWritingMode)
   674 #endif
   675   {
   676     if (aWritingMode.IsVertical()) {
   677       if (aWritingMode.IsVerticalLR()) {
   678         mMargin.top = aPhysicalMargin.left;
   679         mMargin.bottom = aPhysicalMargin.right;
   680       } else {
   681         mMargin.top = aPhysicalMargin.right;
   682         mMargin.bottom = aPhysicalMargin.left;
   683       }
   684       if (aWritingMode.IsBidiLTR()) {
   685         mMargin.left = aPhysicalMargin.top;
   686         mMargin.right = aPhysicalMargin.bottom;
   687       } else {
   688         mMargin.left = aPhysicalMargin.bottom;
   689         mMargin.right = aPhysicalMargin.top;
   690       }
   691     } else {
   692       mMargin.top = aPhysicalMargin.top;
   693       mMargin.bottom = aPhysicalMargin.bottom;
   694       if (aWritingMode.IsBidiLTR()) {
   695         mMargin.left = aPhysicalMargin.left;
   696         mMargin.right = aPhysicalMargin.right;
   697       } else {
   698         mMargin.left = aPhysicalMargin.right;
   699         mMargin.right = aPhysicalMargin.left;
   700       }
   701     }
   702   }
   704   nscoord IStart(WritingMode aWritingMode) const // inline-start margin
   705   {
   706     CHECK_WRITING_MODE(aWritingMode);
   707     return mMargin.left;
   708   }
   709   nscoord IEnd(WritingMode aWritingMode) const // inline-end margin
   710   {
   711     CHECK_WRITING_MODE(aWritingMode);
   712     return mMargin.right;
   713   }
   714   nscoord BStart(WritingMode aWritingMode) const // block-start margin
   715   {
   716     CHECK_WRITING_MODE(aWritingMode);
   717     return mMargin.top;
   718   }
   719   nscoord BEnd(WritingMode aWritingMode) const // block-end margin
   720   {
   721     CHECK_WRITING_MODE(aWritingMode);
   722     return mMargin.bottom;
   723   }
   725   nscoord& IStart(WritingMode aWritingMode) // inline-start margin
   726   {
   727     CHECK_WRITING_MODE(aWritingMode);
   728     return mMargin.left;
   729   }
   730   nscoord& IEnd(WritingMode aWritingMode) // inline-end margin
   731   {
   732     CHECK_WRITING_MODE(aWritingMode);
   733     return mMargin.right;
   734   }
   735   nscoord& BStart(WritingMode aWritingMode) // block-start margin
   736   {
   737     CHECK_WRITING_MODE(aWritingMode);
   738     return mMargin.top;
   739   }
   740   nscoord& BEnd(WritingMode aWritingMode) // block-end margin
   741   {
   742     CHECK_WRITING_MODE(aWritingMode);
   743     return mMargin.bottom;
   744   }
   746   nscoord IStartEnd(WritingMode aWritingMode) const // inline margins
   747   {
   748     CHECK_WRITING_MODE(aWritingMode);
   749     return mMargin.LeftRight();
   750   }
   751   nscoord BStartEnd(WritingMode aWritingMode) const // block margins
   752   {
   753     CHECK_WRITING_MODE(aWritingMode);
   754     return mMargin.TopBottom();
   755   }
   757   /**
   758    * Accessors for physical margins, using our writing mode to convert from
   759    * logical values.
   760    */
   761   nscoord Top(WritingMode aWritingMode) const
   762   {
   763     CHECK_WRITING_MODE(aWritingMode);
   764     return aWritingMode.IsVertical() ?
   765       (aWritingMode.IsBidiLTR() ? IStart() : IEnd()) : BStart();
   766   }
   768   nscoord Bottom(WritingMode aWritingMode) const
   769   {
   770     CHECK_WRITING_MODE(aWritingMode);
   771     return aWritingMode.IsVertical() ?
   772       (aWritingMode.IsBidiLTR() ? IEnd() : IStart()) : BEnd();
   773   }
   775   nscoord Left(WritingMode aWritingMode) const
   776   {
   777     CHECK_WRITING_MODE(aWritingMode);
   778     return aWritingMode.IsVertical() ?
   779       (aWritingMode.IsVerticalLR() ? BStart() : BEnd()) :
   780       (aWritingMode.IsBidiLTR() ? IStart() : IEnd());
   781   }
   783   nscoord Right(WritingMode aWritingMode) const
   784   {
   785     CHECK_WRITING_MODE(aWritingMode);
   786     return aWritingMode.IsVertical() ?
   787       (aWritingMode.IsVerticalLR() ? BEnd() : BStart()) :
   788       (aWritingMode.IsBidiLTR() ? IEnd() : IStart());
   789   }
   791   nscoord LeftRight(WritingMode aWritingMode) const
   792   {
   793     CHECK_WRITING_MODE(aWritingMode);
   794     return aWritingMode.IsVertical() ? BStartEnd() : IStartEnd();
   795   }
   797   nscoord TopBottom(WritingMode aWritingMode) const
   798   {
   799     CHECK_WRITING_MODE(aWritingMode);
   800     return aWritingMode.IsVertical() ? IStartEnd() : BStartEnd();
   801   }
   803   void SizeTo(WritingMode aWritingMode,
   804               nscoord aBStart, nscoord aIEnd, nscoord aBEnd, nscoord aIStart)
   805   {
   806     CHECK_WRITING_MODE(aWritingMode);
   807     mMargin.SizeTo(aBStart, aIEnd, aBEnd, aIStart);
   808   }
   810   /**
   811    * Return an nsMargin containing our physical coordinates
   812    */
   813   nsMargin GetPhysicalMargin(WritingMode aWritingMode) const
   814   {
   815     CHECK_WRITING_MODE(aWritingMode);
   816     return aWritingMode.IsVertical() ?
   817       (aWritingMode.IsVerticalLR() ?
   818         nsMargin(IStart(), BEnd(), IEnd(), BStart()) :
   819         nsMargin(IStart(), BStart(), IEnd(), BEnd())) :
   820       (aWritingMode.IsBidiLTR() ?
   821         nsMargin(BStart(), IEnd(), BEnd(), IStart()) :
   822         nsMargin(BStart(), IStart(), BEnd(), IEnd()));
   823   }
   825   /**
   826    * Return a LogicalMargin representing this margin in a different
   827    * writing mode
   828    */
   829   LogicalMargin ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
   830   {
   831     CHECK_WRITING_MODE(aFromMode);
   832     return aToMode == aFromMode ?
   833       *this : LogicalMargin(aToMode, GetPhysicalMargin(aFromMode));
   834   }
   836   bool IsEmpty() const
   837   {
   838     return (mMargin.left == 0 && mMargin.top == 0 &&
   839             mMargin.right == 0 && mMargin.bottom == 0);
   840   }
   842   LogicalMargin operator+(const LogicalMargin& aMargin) {
   843     CHECK_WRITING_MODE(aMargin.GetWritingMode());
   844     return LogicalMargin(GetWritingMode(),
   845                          BStart() + aMargin.BStart(),
   846                          IEnd() + aMargin.IEnd(),
   847                          BEnd() + aMargin.BEnd(),
   848                          IStart() + aMargin.IStart());
   849   }
   851   LogicalMargin operator-(const LogicalMargin& aMargin) {
   852     CHECK_WRITING_MODE(aMargin.GetWritingMode());
   853     return LogicalMargin(GetWritingMode(),
   854                          BStart() - aMargin.BStart(),
   855                          IEnd() - aMargin.IEnd(),
   856                          BEnd() - aMargin.BEnd(),
   857                          IStart() - aMargin.IStart());
   858   }
   860 private:
   861   friend class LogicalRect;
   863   LogicalMargin() MOZ_DELETE;
   865 #ifdef DEBUG
   866   WritingMode GetWritingMode() const { return mWritingMode; }
   867 #else
   868   WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
   869 #endif
   871   nscoord IStart() const // inline-start margin
   872   {
   873     return mMargin.left;
   874   }
   875   nscoord IEnd() const // inline-end margin
   876   {
   877     return mMargin.right;
   878   }
   879   nscoord BStart() const // block-start margin
   880   {
   881     return mMargin.top;
   882   }
   883   nscoord BEnd() const // block-end margin
   884   {
   885     return mMargin.bottom;
   886   }
   888   nscoord& IStart() // inline-start margin
   889   {
   890     return mMargin.left;
   891   }
   892   nscoord& IEnd() // inline-end margin
   893   {
   894     return mMargin.right;
   895   }
   896   nscoord& BStart() // block-start margin
   897   {
   898     return mMargin.top;
   899   }
   900   nscoord& BEnd() // block-end margin
   901   {
   902     return mMargin.bottom;
   903   }
   905   nscoord IStartEnd() const // inline margins
   906   {
   907     return mMargin.LeftRight();
   908   }
   909   nscoord BStartEnd() const // block margins
   910   {
   911     return mMargin.TopBottom();
   912   }
   914   WritingMode mWritingMode;
   915   nsMargin    mMargin;
   916 };
   918 /**
   919  * Flow-relative rectangle
   920  */
   921 class LogicalRect {
   922 public:
   923   LogicalRect(WritingMode aWritingMode)
   924     :
   925 #ifdef DEBUG
   926       mWritingMode(aWritingMode),
   927 #endif
   928       mRect(0, 0, 0, 0)
   929   { }
   931   LogicalRect(WritingMode aWritingMode,
   932               nscoord aIStart, nscoord aBStart,
   933               nscoord aISize, nscoord aBSize)
   934     :
   935 #ifdef DEBUG
   936       mWritingMode(aWritingMode),
   937 #endif
   938       mRect(aIStart, aBStart, aISize, aBSize)
   939   { }
   941   LogicalRect(WritingMode aWritingMode,
   942               const LogicalPoint& aOrigin,
   943               const LogicalSize& aSize)
   944     : 
   945 #ifdef DEBUG
   946       mWritingMode(aWritingMode),
   947 #endif
   948       mRect(aOrigin.mPoint, aSize.mSize)
   949   {
   950     CHECK_WRITING_MODE(aOrigin.GetWritingMode());
   951     CHECK_WRITING_MODE(aSize.GetWritingMode());
   952   }
   954   LogicalRect(WritingMode aWritingMode,
   955               const nsRect& aRect,
   956               nscoord aContainerWidth)
   957 #ifdef DEBUG
   958     : mWritingMode(aWritingMode)
   959 #endif
   960   {
   961     if (aWritingMode.IsVertical()) {
   962       if (aWritingMode.IsVerticalLR()) {
   963         mRect.y = aRect.x;
   964       } else {
   965         mRect.y = aContainerWidth - aRect.XMost();
   966       }
   967       mRect.height = aRect.width;
   968       mRect.x = aRect.y;
   969       mRect.width = aRect.height;
   970     } else {
   971       if (aWritingMode.IsBidiLTR()) {
   972         mRect.x = aRect.x;
   973       } else {
   974         mRect.x = aContainerWidth - aRect.XMost();
   975       }
   976       mRect.width = aRect.width;
   977       mRect.y = aRect.y;
   978       mRect.height = aRect.height;
   979     }
   980   }
   982   /**
   983    * Inline- and block-dimension geometry.
   984    */
   985   nscoord IStart(WritingMode aWritingMode) const // inline-start edge
   986   {
   987     CHECK_WRITING_MODE(aWritingMode);
   988     return mRect.X();
   989   }
   990   nscoord IEnd(WritingMode aWritingMode) const // inline-end edge
   991   {
   992     CHECK_WRITING_MODE(aWritingMode);
   993     return mRect.XMost();
   994   }
   995   nscoord ISize(WritingMode aWritingMode) const // inline-size
   996   {
   997     CHECK_WRITING_MODE(aWritingMode);
   998     return mRect.Width();
   999   }
  1001   nscoord BStart(WritingMode aWritingMode) const // block-start edge
  1003     CHECK_WRITING_MODE(aWritingMode);
  1004     return mRect.Y();
  1006   nscoord BEnd(WritingMode aWritingMode) const // block-end edge
  1008     CHECK_WRITING_MODE(aWritingMode);
  1009     return mRect.YMost();
  1011   nscoord BSize(WritingMode aWritingMode) const // block-size
  1013     CHECK_WRITING_MODE(aWritingMode);
  1014     return mRect.Height();
  1017   /**
  1018    * Writable (reference) accessors are only available for the basic logical
  1019    * fields (Start and Size), not derivatives like End.
  1020    */
  1021   nscoord& IStart(WritingMode aWritingMode) // inline-start edge
  1023     CHECK_WRITING_MODE(aWritingMode);
  1024     return mRect.x;
  1026   nscoord& ISize(WritingMode aWritingMode) // inline-size
  1028     CHECK_WRITING_MODE(aWritingMode);
  1029     return mRect.width;
  1031   nscoord& BStart(WritingMode aWritingMode) // block-start edge
  1033     CHECK_WRITING_MODE(aWritingMode);
  1034     return mRect.y;
  1036   nscoord& BSize(WritingMode aWritingMode) // block-size
  1038     CHECK_WRITING_MODE(aWritingMode);
  1039     return mRect.height;
  1042   /**
  1043    * Physical coordinates of the rect.
  1044    */
  1045   nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const
  1047     CHECK_WRITING_MODE(aWritingMode);
  1048     if (aWritingMode.IsVertical()) {
  1049       return aWritingMode.IsVerticalLR() ?
  1050              mRect.Y() : aContainerWidth - mRect.YMost();
  1051     } else {
  1052       return aWritingMode.IsBidiLTR() ?
  1053              mRect.X() : aContainerWidth - mRect.XMost();
  1057   void SetX(WritingMode aWritingMode, nscoord aX, nscoord aContainerWidth)
  1059     CHECK_WRITING_MODE(aWritingMode);
  1060     if (aWritingMode.IsVertical()) {
  1061       if (aWritingMode.IsVerticalLR()) {
  1062         BStart() = aX;
  1063       } else {
  1064         BStart() = aContainerWidth - aX - BSize();
  1066     } else {
  1067       if (aWritingMode.IsBidiLTR()) {
  1068         IStart() = aX;
  1069       } else {
  1070         IStart() = aContainerWidth - aX - ISize();
  1075   nscoord Y(WritingMode aWritingMode) const
  1077     CHECK_WRITING_MODE(aWritingMode);
  1078     return aWritingMode.IsVertical() ? mRect.X() : mRect.Y();
  1081   void SetY(WritingMode aWritingMode, nscoord aY)
  1083     CHECK_WRITING_MODE(aWritingMode);
  1084     if (aWritingMode.IsVertical()) {
  1085       IStart() = aY;
  1086     } else {
  1087       BStart() = aY;
  1091   nscoord Width(WritingMode aWritingMode) const
  1093     CHECK_WRITING_MODE(aWritingMode);
  1094     return aWritingMode.IsVertical() ? mRect.Height() : mRect.Width();
  1097   // When setting the Width of a rect, we keep its physical X-coord fixed
  1098   // and modify XMax. This means that in the RTL case, we'll be moving
  1099   // the IStart, so that IEnd remains constant.
  1100   void SetWidth(WritingMode aWritingMode, nscoord aWidth)
  1102     CHECK_WRITING_MODE(aWritingMode);
  1103     if (aWritingMode.IsVertical()) {
  1104       if (!aWritingMode.IsVerticalLR()) {
  1105         BStart() = BStart() + BSize() - aWidth;
  1107       BSize() = aWidth;
  1108     } else {
  1109       if (!aWritingMode.IsBidiLTR()) {
  1110         IStart() = IStart() + ISize() - aWidth;
  1112       ISize() = aWidth;
  1116   nscoord Height(WritingMode aWritingMode) const
  1118     CHECK_WRITING_MODE(aWritingMode);
  1119     return aWritingMode.IsVertical() ? mRect.Width() : mRect.Height();
  1122   void SetHeight(WritingMode aWritingMode, nscoord aHeight)
  1124     CHECK_WRITING_MODE(aWritingMode);
  1125     if (aWritingMode.IsVertical()) {
  1126       ISize() = aHeight;
  1127     } else {
  1128       BSize() = aHeight;
  1132   nscoord XMost(WritingMode aWritingMode, nscoord aContainerWidth) const
  1134     CHECK_WRITING_MODE(aWritingMode);
  1135     if (aWritingMode.IsVertical()) {
  1136       return aWritingMode.IsVerticalLR() ?
  1137              mRect.YMost() : aContainerWidth - mRect.Y();
  1138     } else {
  1139       return aWritingMode.IsBidiLTR() ?
  1140              mRect.XMost() : aContainerWidth - mRect.X();
  1144   nscoord YMost(WritingMode aWritingMode) const
  1146     CHECK_WRITING_MODE(aWritingMode);
  1147     return aWritingMode.IsVertical() ? mRect.XMost() : mRect.YMost();
  1150   bool IsEmpty() const
  1152     return (mRect.x == 0 && mRect.y == 0 &&
  1153             mRect.width == 0 && mRect.height == 0);
  1156   bool IsZeroSize() const
  1158     return (mRect.width == 0 && mRect.height == 0);
  1161 /* XXX are these correct?
  1162   nscoord ILeft(WritingMode aWritingMode) const
  1164     CHECK_WRITING_MODE(aWritingMode);
  1165     return aWritingMode.IsBidiLTR() ? IStart() : IEnd();
  1167   nscoord IRight(WritingMode aWritingMode) const
  1169     CHECK_WRITING_MODE(aWritingMode);
  1170     return aWritingMode.IsBidiLTR() ? IEnd() : IStart();
  1172 */
  1174   LogicalPoint Origin(WritingMode aWritingMode) const
  1176     CHECK_WRITING_MODE(aWritingMode);
  1177     return LogicalPoint(aWritingMode, IStart(), BStart());
  1179   LogicalSize Size(WritingMode aWritingMode) const
  1181     CHECK_WRITING_MODE(aWritingMode);
  1182     return LogicalSize(aWritingMode, ISize(), BSize());
  1185   LogicalRect operator+(const LogicalPoint& aPoint) const
  1187     CHECK_WRITING_MODE(aPoint.GetWritingMode());
  1188     return LogicalRect(GetWritingMode(),
  1189                        IStart() + aPoint.I(), BStart() + aPoint.B(),
  1190                        ISize(), BSize());
  1193   LogicalRect& operator+=(const LogicalPoint& aPoint)
  1195     CHECK_WRITING_MODE(aPoint.GetWritingMode());
  1196     mRect += aPoint.mPoint;
  1197     return *this;
  1200   LogicalRect operator-(const LogicalPoint& aPoint) const
  1202     CHECK_WRITING_MODE(aPoint.GetWritingMode());
  1203     return LogicalRect(GetWritingMode(),
  1204                        IStart() - aPoint.I(), BStart() - aPoint.B(),
  1205                        ISize(), BSize());
  1208   LogicalRect& operator-=(const LogicalPoint& aPoint)
  1210     CHECK_WRITING_MODE(aPoint.GetWritingMode());
  1211     mRect -= aPoint.mPoint;
  1212     return *this;
  1215   void MoveBy(WritingMode aWritingMode, const LogicalPoint& aDelta)
  1217     CHECK_WRITING_MODE(aWritingMode);
  1218     CHECK_WRITING_MODE(aDelta.GetWritingMode());
  1219     IStart() += aDelta.I();
  1220     BStart() += aDelta.B();
  1223   void Inflate(nscoord aD) { mRect.Inflate(aD); }
  1224   void Inflate(nscoord aDI, nscoord aDB) { mRect.Inflate(aDI, aDB); }
  1225   void Inflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
  1227     CHECK_WRITING_MODE(aWritingMode);
  1228     CHECK_WRITING_MODE(aMargin.GetWritingMode());
  1229     mRect.Inflate(aMargin.mMargin);
  1232   void Deflate(nscoord aD) { mRect.Deflate(aD); }
  1233   void Deflate(nscoord aDI, nscoord aDB) { mRect.Deflate(aDI, aDB); }
  1234   void Deflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
  1236     CHECK_WRITING_MODE(aWritingMode);
  1237     CHECK_WRITING_MODE(aMargin.GetWritingMode());
  1238     mRect.Deflate(aMargin.mMargin);
  1241   /**
  1242    * Return an nsRect containing our physical coordinates within the given
  1243    * container width
  1244    */
  1245   nsRect GetPhysicalRect(WritingMode aWritingMode,
  1246                          nscoord aContainerWidth) const
  1248     CHECK_WRITING_MODE(aWritingMode);
  1249     if (aWritingMode.IsVertical()) {
  1250       return nsRect(aWritingMode.IsVerticalLR() ?
  1251                       BStart() : aContainerWidth - BEnd(),
  1252                     IStart(), BSize(), ISize());
  1253     } else {
  1254       return nsRect(aWritingMode.IsBidiLTR() ?
  1255                       IStart() : aContainerWidth - IEnd(),
  1256                     BStart(), ISize(), BSize());
  1260 #if 0 // XXX this would require aContainerWidth as well
  1261   /**
  1262    * Return a LogicalRect representing this rect in a different writing mode
  1263    */
  1264   LogicalRect ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
  1266     CHECK_WRITING_MODE(aFromMode);
  1267     return aToMode == aFromMode ?
  1268       *this : LogicalRect(aToMode, GetPhysicalRect(aFromMode));
  1270 #endif
  1272 private:
  1273   LogicalRect() MOZ_DELETE;
  1275 #ifdef DEBUG
  1276   WritingMode GetWritingMode() const { return mWritingMode; }
  1277 #else
  1278   WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
  1279 #endif
  1281   nscoord IStart() const // inline-start edge
  1283     return mRect.X();
  1285   nscoord IEnd() const // inline-end edge
  1287     return mRect.XMost();
  1289   nscoord ISize() const // inline-size
  1291     return mRect.Width();
  1294   nscoord BStart() const // block-start edge
  1296     return mRect.Y();
  1298   nscoord BEnd() const // block-end edge
  1300     return mRect.YMost();
  1302   nscoord BSize() const // block-size
  1304     return mRect.Height();
  1307   nscoord& IStart() // inline-start edge
  1309     return mRect.x;
  1311   nscoord& ISize() // inline-size
  1313     return mRect.width;
  1315   nscoord& BStart() // block-start edge
  1317     return mRect.y;
  1319   nscoord& BSize() // block-size
  1321     return mRect.height;
  1324   WritingMode mWritingMode;
  1325   nsRect      mRect;
  1326 };
  1328 } // namespace mozilla
  1330 #endif // WritingModes_h_

mercurial