Bug 1210328, compute correct flip state for popups, r=ksteuber
authorNeil Deakin <neil@mozilla.com>
Tue, 11 Oct 2016 14:13:36 -0400
changeset 317461 a9e9e6a3d720
parent 317460 fa18a2ef3c4e
child 317462 94812daa91cf
push id82658
push userneil@mozilla.com
push dateTue, 11 Oct 2016 18:17:24 +0000
treeherdermozilla-inbound@a9e9e6a3d720 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersksteuber
bugs1210328
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1210328, compute correct flip state for popups, r=ksteuber
layout/xul/nsMenuPopupFrame.cpp
layout/xul/nsMenuPopupFrame.h
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -1182,18 +1182,20 @@ nsMenuPopupFrame::SlideOrResize(nscoord&
 }
 
 nscoord
 nsMenuPopupFrame::FlipOrResize(nscoord& aScreenPoint, nscoord aSize, 
                                nscoord aScreenBegin, nscoord aScreenEnd,
                                nscoord aAnchorBegin, nscoord aAnchorEnd,
                                nscoord aMarginBegin, nscoord aMarginEnd,
                                nscoord aOffsetForContextMenu, FlipStyle aFlip,
-                               bool* aFlipSide)
+                               bool aEndAligned, bool* aFlipSide)
 {
+  // The flip side argument will be set to true if there wasn't room and we
+  // flipped to the opposite side.
   *aFlipSide = false;
 
   // all of the coordinates used here are in app units relative to the screen
   nscoord popupSize = aSize;
   if (aScreenPoint < aScreenBegin) {
     // at its current position, the popup would extend past the left or top
     // edge of the screen, so it will have to be moved or resized.
     if (aFlip) {
@@ -1201,25 +1203,26 @@ nsMenuPopupFrame::FlipOrResize(nscoord& 
       nscoord startpos = aFlip == FlipStyle_Outside ? aAnchorBegin : aAnchorEnd;
       nscoord endpos = aFlip == FlipStyle_Outside ? aAnchorEnd : aAnchorBegin;
 
       // check whether there is more room to the left and right (or top and
       // bottom) of the anchor and put the popup on the side with more room.
       if (startpos - aScreenBegin >= aScreenEnd - endpos) {
         aScreenPoint = aScreenBegin;
         popupSize = startpos - aScreenPoint - aMarginEnd;
+        *aFlipSide = !aEndAligned;
       }
       else {
         // If the newly calculated position is different than the existing
         // position, flip such that the popup is to the right or bottom of the
         // anchor point instead . However, when flipping use the same margin
         // size.
         nscoord newScreenPoint = endpos + aMarginEnd;
         if (newScreenPoint != aScreenPoint) {
-          *aFlipSide = true;
+          *aFlipSide = aEndAligned;
           aScreenPoint = newScreenPoint;
           // check if the new position is still off the right or bottom edge of
           // the screen. If so, resize the popup.
           if (aScreenPoint + aSize > aScreenEnd) {
             popupSize = aScreenEnd - aScreenPoint;
           }
         }
       }
@@ -1234,31 +1237,32 @@ nsMenuPopupFrame::FlipOrResize(nscoord& 
     if (aFlip) {
       // for inside flips, we flip on the opposite side of the anchor
       nscoord startpos = aFlip == FlipStyle_Outside ? aAnchorBegin : aAnchorEnd;
       nscoord endpos = aFlip == FlipStyle_Outside ? aAnchorEnd : aAnchorBegin;
 
       // check whether there is more room to the left and right (or top and
       // bottom) of the anchor and put the popup on the side with more room.
       if (aScreenEnd - endpos >= startpos - aScreenBegin) {
+        *aFlipSide = aEndAligned;
         if (mIsContextMenu) {
           aScreenPoint = aScreenEnd - aSize;
         }
         else {
           aScreenPoint = endpos + aMarginBegin;
           popupSize = aScreenEnd - aScreenPoint;
         }
       }
       else {
         // if the newly calculated position is different than the existing
         // position, we flip such that the popup is to the left or top of the
         // anchor point instead.
         nscoord newScreenPoint = startpos - aSize - aMarginBegin - std::max(aOffsetForContextMenu, 0);
         if (newScreenPoint != aScreenPoint) {
-          *aFlipSide = true;
+          *aFlipSide = !aEndAligned;
           aScreenPoint = newScreenPoint;
 
           // check if the new position is still off the left or top edge of the
           // screen. If so, resize the popup.
           if (aScreenPoint < aScreenBegin) {
             aScreenPoint = aScreenBegin;
             if (!mIsContextMenu) {
               popupSize = startpos - aScreenPoint - aMarginBegin;
@@ -1531,33 +1535,39 @@ nsMenuPopupFrame::SetPopupPosition(nsIFr
                         position <= POPUPPOSITION_AFTEREND;
       slideVertical = position >= POPUPPOSITION_STARTBEFORE &&
                       position <= POPUPPOSITION_ENDAFTER;
     }
 
     // Next, check if there is enough space to show the popup at full size when
     // positioned at screenPoint. If not, flip the popups to the opposite side
     // of their anchor point, or resize them as necessary.
+    bool endAligned = IsDirectionRTL() ?
+      mPopupAlignment == POPUPALIGNMENT_TOPLEFT || mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT :
+      mPopupAlignment == POPUPALIGNMENT_TOPRIGHT || mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
     if (slideHorizontal) {
       mRect.width = SlideOrResize(screenPoint.x, mRect.width, screenRect.x,
                                   screenRect.XMost(), &mAlignmentOffset);
     } else {
       mRect.width = FlipOrResize(screenPoint.x, mRect.width, screenRect.x,
                                  screenRect.XMost(), anchorRect.x, anchorRect.XMost(),
                                  margin.left, margin.right, offsetForContextMenu.x, hFlip,
-                                 &mHFlip);
+                                 endAligned, &mHFlip);
     }
+
+    endAligned = mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT ||
+                 mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
     if (slideVertical) {
       mRect.height = SlideOrResize(screenPoint.y, mRect.height, screenRect.y,
                                   screenRect.YMost(), &mAlignmentOffset);
     } else {
       mRect.height = FlipOrResize(screenPoint.y, mRect.height, screenRect.y,
                                   screenRect.YMost(), anchorRect.y, anchorRect.YMost(),
                                   margin.top, margin.bottom, offsetForContextMenu.y, vFlip,
-                                  &mVFlip);
+                                  endAligned, &mVFlip);
     }
 
     NS_ASSERTION(screenPoint.x >= screenRect.x && screenPoint.y >= screenRect.y &&
                  screenPoint.x + mRect.width <= screenRect.XMost() &&
                  screenPoint.y + mRect.height <= screenRect.YMost(),
                  "Popup is offscreen");
   }
 
--- a/layout/xul/nsMenuPopupFrame.h
+++ b/layout/xul/nsMenuPopupFrame.h
@@ -474,17 +474,17 @@ protected:
   //   aOffsetForContextMenu - the additional offset to add for context menus
   //   aFlip - how to flip or resize the popup when there isn't space
   //   aFlipSide - pointer to where current flip mode is stored
   nscoord FlipOrResize(nscoord& aScreenPoint, nscoord aSize, 
                        nscoord aScreenBegin, nscoord aScreenEnd,
                        nscoord aAnchorBegin, nscoord aAnchorEnd,
                        nscoord aMarginBegin, nscoord aMarginEnd,
                        nscoord aOffsetForContextMenu, FlipStyle aFlip,
-                       bool* aFlipSide);
+                       bool aIsOnEnd, bool* aFlipSide);
 
   // check if the popup can fit into the available space by "sliding" (i.e.,
   // by having the anchor arrow slide along one axis and only resizing if that
   // can't provide the requested size). Only one axis can be slid - the other
   // axis is "flipped" as normal. This method can handle either axis, but is
   // only called for the sliding axis. All coordinates are in app units
   // relative to the screen.
   //   aScreenPoint - the point where the popup should appear