зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1113206: Make nsComboboxControlFrame and nsListControlFrame use logical coordinates and support vertical writing modes - patch by smontagu with additions by jfkthame. r=jfkthame,smontagu
This commit is contained in:
Родитель
b1b410165b
Коммит
37f044b028
|
@ -225,11 +225,11 @@ nsComboboxControlFrame::nsComboboxControlFrame(nsStyleContext* aContext)
|
|||
, mButtonFrame(nullptr)
|
||||
, mDropdownFrame(nullptr)
|
||||
, mListControlFrame(nullptr)
|
||||
, mDisplayWidth(0)
|
||||
, mDisplayISize(0)
|
||||
, mRecentSelectedIndex(NS_SKIP_NOTIFY_INDEX)
|
||||
, mDisplayedIndex(-1)
|
||||
, mLastDropDownAboveScreenY(nscoord_MIN)
|
||||
, mLastDropDownBelowScreenY(nscoord_MIN)
|
||||
, mLastDropDownBeforeScreenBCoord(nscoord_MIN)
|
||||
, mLastDropDownAfterScreenBCoord(nscoord_MIN)
|
||||
, mDroppedDown(false)
|
||||
, mInRedisplayText(false)
|
||||
, mDelayedShowDropDown(false)
|
||||
|
@ -418,7 +418,7 @@ void
|
|||
nsComboboxControlFrame::ReflowDropdown(nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState)
|
||||
{
|
||||
// All we want out of it later on, really, is the height of a row, so we
|
||||
// All we want out of it later on, really, is the block size of a row, so we
|
||||
// don't even need to cache mDropdownFrame's ascent or anything. If we don't
|
||||
// need to reflow it, just bail out here.
|
||||
if (!aReflowState.ShouldReflowAllKids() &&
|
||||
|
@ -426,23 +426,24 @@ nsComboboxControlFrame::ReflowDropdown(nsPresContext* aPresContext,
|
|||
return;
|
||||
}
|
||||
|
||||
// XXXbz this will, for small-height dropdowns, have extra space on the right
|
||||
// edge for the scrollbar we don't show... but that's the best we can do here
|
||||
// for now.
|
||||
// XXXbz this will, for small-block-size dropdowns, have extra space
|
||||
// on the appropriate edge for the scrollbar we don't show... but
|
||||
// that's the best we can do here for now.
|
||||
WritingMode wm = mDropdownFrame->GetWritingMode();
|
||||
LogicalSize availSize = aReflowState.AvailableSize(wm);
|
||||
availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
|
||||
nsHTMLReflowState kidReflowState(aPresContext, aReflowState, mDropdownFrame,
|
||||
availSize);
|
||||
|
||||
// If the dropdown's intrinsic width is narrower than our specified width,
|
||||
// then expand it out. We want our border-box width to end up the same as
|
||||
// the dropdown's so account for both sets of mComputedBorderPadding.
|
||||
nscoord forcedWidth = aReflowState.ComputedWidth() +
|
||||
aReflowState.ComputedPhysicalBorderPadding().LeftRight() -
|
||||
kidReflowState.ComputedPhysicalBorderPadding().LeftRight();
|
||||
kidReflowState.SetComputedWidth(std::max(kidReflowState.ComputedWidth(),
|
||||
forcedWidth));
|
||||
// If the dropdown's intrinsic inline size is narrower than our
|
||||
// specified inline size, then expand it out. We want our border-box
|
||||
// inline size to end up the same as the dropdown's so account for
|
||||
// both sets of mComputedBorderPadding.
|
||||
nscoord forcedISize = aReflowState.ComputedISize() +
|
||||
aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm) -
|
||||
kidReflowState.ComputedLogicalBorderPadding().IStartEnd(wm);
|
||||
kidReflowState.SetComputedISize(std::max(kidReflowState.ComputedISize(),
|
||||
forcedISize));
|
||||
|
||||
// ensure we start off hidden
|
||||
if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
|
||||
|
@ -455,19 +456,26 @@ nsComboboxControlFrame::ReflowDropdown(nsPresContext* aPresContext,
|
|||
|
||||
// Allow the child to move/size/change-visibility its view if it's currently
|
||||
// dropped down
|
||||
int32_t flags = NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_VISIBILITY | NS_FRAME_NO_SIZE_VIEW;
|
||||
if (mDroppedDown) {
|
||||
flags = 0;
|
||||
}
|
||||
nsRect rect = mDropdownFrame->GetRect();
|
||||
int32_t flags = mDroppedDown ? 0
|
||||
: NS_FRAME_NO_MOVE_FRAME |
|
||||
NS_FRAME_NO_VISIBILITY |
|
||||
NS_FRAME_NO_SIZE_VIEW;
|
||||
|
||||
//XXX Can this be different from the dropdown's writing mode?
|
||||
// That would be odd!
|
||||
// Note that we don't need to pass the true frame position or container width
|
||||
// to ReflowChild or FinishReflowChild here; it will be positioned as needed
|
||||
// by AbsolutelyPositionDropDown().
|
||||
WritingMode outerWM = GetWritingMode();
|
||||
nsHTMLReflowMetrics desiredSize(aReflowState);
|
||||
nsReflowStatus ignoredStatus;
|
||||
ReflowChild(mDropdownFrame, aPresContext, desiredSize,
|
||||
kidReflowState, rect.x, rect.y, flags, ignoredStatus);
|
||||
kidReflowState, outerWM, LogicalPoint(outerWM), 0,
|
||||
flags, ignoredStatus);
|
||||
|
||||
// Set the child's width and height to its desired size
|
||||
FinishReflowChild(mDropdownFrame, aPresContext, desiredSize,
|
||||
&kidReflowState, rect.x, rect.y, flags);
|
||||
FinishReflowChild(mDropdownFrame, aPresContext, desiredSize, &kidReflowState,
|
||||
outerWM, LogicalPoint(outerWM), 0, flags);
|
||||
}
|
||||
|
||||
nsPoint
|
||||
|
@ -549,71 +557,81 @@ public:
|
|||
};
|
||||
|
||||
void
|
||||
nsComboboxControlFrame::GetAvailableDropdownSpace(nscoord* aAbove,
|
||||
nscoord* aBelow,
|
||||
nsPoint* aTranslation)
|
||||
nsComboboxControlFrame::GetAvailableDropdownSpace(WritingMode aWM,
|
||||
nscoord* aBefore,
|
||||
nscoord* aAfter,
|
||||
LogicalPoint* aTranslation)
|
||||
{
|
||||
// Note: At first glance, it appears that you could simply get the absolute
|
||||
// bounding box for the dropdown list by first getting its view, then getting
|
||||
// the view's nsIWidget, then asking the nsIWidget for its AbsoluteBounds.
|
||||
// The problem with this approach, is that the dropdown lists y location can
|
||||
// change based on whether the dropdown is placed below or above the display
|
||||
// frame. The approach, taken here is to get the absolute position of the
|
||||
// display frame and use its location to determine if the dropdown will go
|
||||
// offscreen.
|
||||
// Note: At first glance, it appears that you could simply get the
|
||||
// absolute bounding box for the dropdown list by first getting its
|
||||
// view, then getting the view's nsIWidget, then asking the nsIWidget
|
||||
// for its AbsoluteBounds.
|
||||
// The problem with this approach, is that the dropdown list's bcoord
|
||||
// location can change based on whether the dropdown is placed after
|
||||
// or before the display frame. The approach taken here is to get the
|
||||
// absolute position of the display frame and use its location to
|
||||
// determine if the dropdown will go offscreen.
|
||||
|
||||
// Normal frame geometry (eg GetOffsetTo, mRect) doesn't include transforms.
|
||||
// In the special case that our transform is only a 2D translation we
|
||||
// introduce this hack so that the dropdown will show up in the right place.
|
||||
*aTranslation = GetCSSTransformTranslation();
|
||||
*aAbove = 0;
|
||||
*aBelow = 0;
|
||||
*aTranslation = LogicalPoint(aWM, GetCSSTransformTranslation(), 0);
|
||||
*aBefore = 0;
|
||||
*aAfter = 0;
|
||||
|
||||
nsRect screen = nsFormControlFrame::GetUsableScreenRect(PresContext());
|
||||
if (mLastDropDownBelowScreenY == nscoord_MIN) {
|
||||
nsRect thisScreenRect = GetScreenRectInAppUnits();
|
||||
mLastDropDownBelowScreenY = thisScreenRect.YMost() + aTranslation->y;
|
||||
mLastDropDownAboveScreenY = thisScreenRect.y + aTranslation->y;
|
||||
nscoord containerWidth = screen.width;
|
||||
LogicalRect logicalScreen(aWM, screen, containerWidth);
|
||||
if (mLastDropDownAfterScreenBCoord == nscoord_MIN) {
|
||||
LogicalRect thisScreenRect(aWM, GetScreenRectInAppUnits(),
|
||||
containerWidth);
|
||||
mLastDropDownAfterScreenBCoord = thisScreenRect.BEnd(aWM) +
|
||||
aTranslation->B(aWM);
|
||||
mLastDropDownBeforeScreenBCoord = thisScreenRect.BEnd(aWM) +
|
||||
aTranslation->B(aWM);
|
||||
}
|
||||
|
||||
nscoord minY;
|
||||
nscoord minBCoord;
|
||||
nsPresContext* pc = PresContext()->GetToplevelContentDocumentPresContext();
|
||||
nsIFrame* root = pc ? pc->PresShell()->GetRootFrame() : nullptr;
|
||||
if (root) {
|
||||
minY = root->GetScreenRectInAppUnits().y;
|
||||
if (mLastDropDownBelowScreenY < minY) {
|
||||
// Don't allow the drop-down to be placed above the content area.
|
||||
minBCoord = LogicalRect(aWM,
|
||||
root->GetScreenRectInAppUnits(),
|
||||
containerWidth).BStart(aWM);
|
||||
if (mLastDropDownAfterScreenBCoord < minBCoord) {
|
||||
// Don't allow the drop-down to be placed before the content area.
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
minY = screen.y;
|
||||
minBCoord = logicalScreen.BStart(aWM);
|
||||
}
|
||||
|
||||
nscoord below = screen.YMost() - mLastDropDownBelowScreenY;
|
||||
nscoord above = mLastDropDownAboveScreenY - minY;
|
||||
nscoord after = logicalScreen.BEnd(aWM) - mLastDropDownAfterScreenBCoord;
|
||||
nscoord before = mLastDropDownBeforeScreenBCoord - minBCoord;
|
||||
|
||||
// If the difference between the space above and below is less
|
||||
// than a row-height, then we favor the space below.
|
||||
if (above >= below) {
|
||||
// If the difference between the space before and after is less
|
||||
// than a row-block-size, then we favor the space after.
|
||||
if (before >= after) {
|
||||
nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame);
|
||||
nscoord rowHeight = lcf->GetHeightOfARow();
|
||||
if (above < below + rowHeight) {
|
||||
above -= rowHeight;
|
||||
nscoord rowBSize = lcf->GetBSizeOfARow();
|
||||
if (before < after + rowBSize) {
|
||||
before -= rowBSize;
|
||||
}
|
||||
}
|
||||
|
||||
*aBelow = below;
|
||||
*aAbove = above;
|
||||
*aAfter = after;
|
||||
*aBefore = before;
|
||||
}
|
||||
|
||||
nsComboboxControlFrame::DropDownPositionState
|
||||
nsComboboxControlFrame::AbsolutelyPositionDropDown()
|
||||
{
|
||||
nsPoint translation;
|
||||
nscoord above, below;
|
||||
mLastDropDownBelowScreenY = nscoord_MIN;
|
||||
GetAvailableDropdownSpace(&above, &below, &translation);
|
||||
if (above <= 0 && below <= 0) {
|
||||
WritingMode wm = GetWritingMode();
|
||||
LogicalPoint translation(wm);
|
||||
nscoord before, after;
|
||||
mLastDropDownAfterScreenBCoord = nscoord_MIN;
|
||||
GetAvailableDropdownSpace(wm, &before, &after, &translation);
|
||||
if (before <= 0 && after <= 0) {
|
||||
if (IsDroppedDown()) {
|
||||
// Hide the view immediately to minimize flicker.
|
||||
nsView* view = mDropdownFrame->GetView();
|
||||
|
@ -623,17 +641,17 @@ nsComboboxControlFrame::AbsolutelyPositionDropDown()
|
|||
return eDropDownPositionSuppressed;
|
||||
}
|
||||
|
||||
nsSize dropdownSize = mDropdownFrame->GetSize();
|
||||
nscoord height = std::max(above, below);
|
||||
LogicalSize dropdownSize = mDropdownFrame->GetLogicalSize(wm);
|
||||
nscoord bSize = std::max(before, after);
|
||||
nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame);
|
||||
if (height < dropdownSize.height) {
|
||||
if (bSize < dropdownSize.BSize(wm)) {
|
||||
if (lcf->GetNumDisplayRows() > 1) {
|
||||
// The drop-down doesn't fit and currently shows more than 1 row -
|
||||
// schedule a resize to show fewer rows.
|
||||
NS_DispatchToCurrentThread(new nsAsyncResize(this));
|
||||
return eDropDownPositionPendingResize;
|
||||
}
|
||||
} else if (height > (dropdownSize.height + lcf->GetHeightOfARow() * 1.5) &&
|
||||
} else if (bSize > (dropdownSize.BSize(wm) + lcf->GetBSizeOfARow() * 1.5) &&
|
||||
lcf->GetDropdownCanGrow()) {
|
||||
// The drop-down fits but there is room for at least 1.5 more rows -
|
||||
// schedule a resize to show more rows if it has more rows to show.
|
||||
|
@ -643,22 +661,20 @@ nsComboboxControlFrame::AbsolutelyPositionDropDown()
|
|||
return eDropDownPositionPendingResize;
|
||||
}
|
||||
|
||||
// Position the drop-down below if there is room, otherwise place it above
|
||||
// Position the drop-down after if there is room, otherwise place it before
|
||||
// if there is room. If there is no room for it on either side then place
|
||||
// it below (to avoid overlapping UI like the URL bar).
|
||||
bool b = dropdownSize.height <= below || dropdownSize.height > above;
|
||||
nsPoint dropdownPosition(0, b ? GetRect().height : -dropdownSize.height);
|
||||
if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
|
||||
// Align the right edge of the drop-down with the right edge of the control.
|
||||
dropdownPosition.x = GetRect().width - dropdownSize.width;
|
||||
}
|
||||
// it after (to avoid overlapping UI like the URL bar).
|
||||
bool b = dropdownSize.BSize(wm)<= after || dropdownSize.BSize(wm) > before;
|
||||
LogicalPoint dropdownPosition(wm, 0, b ? BSize(wm) : -dropdownSize.BSize(wm));
|
||||
|
||||
// Don't position the view unless the position changed since it might cause
|
||||
// a call to NotifyGeometryChange() and an infinite loop here.
|
||||
const nsPoint currentPos = mDropdownFrame->GetPosition();
|
||||
const nsPoint newPos = dropdownPosition + translation;
|
||||
nscoord containerWidth = GetRect().width;
|
||||
const LogicalPoint currentPos =
|
||||
mDropdownFrame->GetLogicalPosition(containerWidth);
|
||||
const LogicalPoint newPos = dropdownPosition + translation;
|
||||
if (currentPos != newPos) {
|
||||
mDropdownFrame->SetPosition(newPos);
|
||||
mDropdownFrame->SetPosition(wm, newPos, containerWidth);
|
||||
nsContainerFrame::PositionFrameView(mDropdownFrame);
|
||||
}
|
||||
return eDropDownPositionFinal;
|
||||
|
@ -716,63 +732,63 @@ nsComboboxControlFrame::GetIntrinsicISize(nsRenderingContext* aRenderingContext,
|
|||
presContext, aRenderingContext);
|
||||
}
|
||||
|
||||
nscoord displayWidth = 0;
|
||||
nscoord displayISize = 0;
|
||||
if (MOZ_LIKELY(mDisplayFrame)) {
|
||||
displayWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
|
||||
displayISize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
|
||||
mDisplayFrame,
|
||||
aType);
|
||||
}
|
||||
|
||||
if (mDropdownFrame) {
|
||||
nscoord dropdownContentWidth;
|
||||
nscoord dropdownContentISize;
|
||||
bool isUsingOverlayScrollbars =
|
||||
LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0;
|
||||
if (aType == nsLayoutUtils::MIN_ISIZE) {
|
||||
dropdownContentWidth = mDropdownFrame->GetMinISize(aRenderingContext);
|
||||
dropdownContentISize = mDropdownFrame->GetMinISize(aRenderingContext);
|
||||
if (isUsingOverlayScrollbars) {
|
||||
dropdownContentWidth += scrollbarWidth;
|
||||
dropdownContentISize += scrollbarWidth;
|
||||
}
|
||||
} else {
|
||||
NS_ASSERTION(aType == nsLayoutUtils::PREF_ISIZE, "Unexpected type");
|
||||
dropdownContentWidth = mDropdownFrame->GetPrefISize(aRenderingContext);
|
||||
dropdownContentISize = mDropdownFrame->GetPrefISize(aRenderingContext);
|
||||
if (isUsingOverlayScrollbars) {
|
||||
dropdownContentWidth += scrollbarWidth;
|
||||
dropdownContentISize += scrollbarWidth;
|
||||
}
|
||||
}
|
||||
dropdownContentWidth = NSCoordSaturatingSubtract(dropdownContentWidth,
|
||||
dropdownContentISize = NSCoordSaturatingSubtract(dropdownContentISize,
|
||||
scrollbarWidth,
|
||||
nscoord_MAX);
|
||||
|
||||
displayWidth = std::max(dropdownContentWidth, displayWidth);
|
||||
displayISize = std::max(dropdownContentISize, displayISize);
|
||||
}
|
||||
|
||||
// add room for the dropmarker button if there is one
|
||||
if ((!IsThemed() ||
|
||||
presContext->GetTheme()->ThemeNeedsComboboxDropmarker()) &&
|
||||
StyleDisplay()->mAppearance != NS_THEME_NONE) {
|
||||
displayWidth += scrollbarWidth;
|
||||
displayISize += scrollbarWidth;
|
||||
}
|
||||
|
||||
return displayWidth;
|
||||
return displayISize;
|
||||
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsComboboxControlFrame::GetMinISize(nsRenderingContext *aRenderingContext)
|
||||
{
|
||||
nscoord minWidth;
|
||||
DISPLAY_MIN_WIDTH(this, minWidth);
|
||||
minWidth = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::MIN_ISIZE);
|
||||
return minWidth;
|
||||
nscoord minISize;
|
||||
DISPLAY_MIN_WIDTH(this, minISize);
|
||||
minISize = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::MIN_ISIZE);
|
||||
return minISize;
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsComboboxControlFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
|
||||
{
|
||||
nscoord prefWidth;
|
||||
DISPLAY_PREF_WIDTH(this, prefWidth);
|
||||
prefWidth = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::PREF_ISIZE);
|
||||
return prefWidth;
|
||||
nscoord prefISize;
|
||||
DISPLAY_PREF_WIDTH(this, prefISize);
|
||||
prefISize = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::PREF_ISIZE);
|
||||
return prefISize;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -784,13 +800,13 @@ nsComboboxControlFrame::Reflow(nsPresContext* aPresContext,
|
|||
MarkInReflow();
|
||||
// Constraints we try to satisfy:
|
||||
|
||||
// 1) Default width of button is the vertical scrollbar size
|
||||
// 2) If the width of button is bigger than our width, set width of
|
||||
// button to 0.
|
||||
// 3) Default height of button is height of display area
|
||||
// 4) Width of display area is whatever is left over from our width after
|
||||
// allocating width for the button.
|
||||
// 5) Height of display area is GetHeightOfARow() on the
|
||||
// 1) Default inline size of button is the vertical scrollbar size
|
||||
// 2) If the inline size of button is bigger than our inline size, set
|
||||
// inline size of button to 0.
|
||||
// 3) Default block size of button is block size of display area
|
||||
// 4) Inline size of display area is whatever is left over from our
|
||||
// inline size after allocating inline size for the button.
|
||||
// 5) Block Size of display area is GetBSizeOfARow() on the
|
||||
// mListControlFrame.
|
||||
|
||||
if (!mDisplayFrame || !mButtonFrame || !mDropdownFrame) {
|
||||
|
@ -826,48 +842,45 @@ nsComboboxControlFrame::Reflow(nsPresContext* aPresContext,
|
|||
unused << resize.forget();
|
||||
}
|
||||
|
||||
// Get the width of the vertical scrollbar. That will be the width of the
|
||||
// dropdown button.
|
||||
nscoord buttonWidth;
|
||||
// Get the width of the vertical scrollbar. That will be the inline
|
||||
// size of the dropdown button.
|
||||
nscoord buttonISize;
|
||||
const nsStyleDisplay *disp = StyleDisplay();
|
||||
if ((IsThemed(disp) && !aPresContext->GetTheme()->ThemeNeedsComboboxDropmarker()) ||
|
||||
StyleDisplay()->mAppearance == NS_THEME_NONE) {
|
||||
buttonWidth = 0;
|
||||
buttonISize = 0;
|
||||
}
|
||||
else {
|
||||
nsIScrollableFrame* scrollable = do_QueryFrame(mListControlFrame);
|
||||
NS_ASSERTION(scrollable, "List must be a scrollable frame");
|
||||
buttonWidth = scrollable->GetNondisappearingScrollbarWidth(
|
||||
buttonISize = scrollable->GetNondisappearingScrollbarWidth(
|
||||
PresContext(), aReflowState.rendContext);
|
||||
if (buttonWidth > aReflowState.ComputedWidth()) {
|
||||
buttonWidth = 0;
|
||||
if (buttonISize > aReflowState.ComputedISize()) {
|
||||
buttonISize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
mDisplayWidth = aReflowState.ComputedWidth() - buttonWidth;
|
||||
mDisplayISize = aReflowState.ComputedISize() - buttonISize;
|
||||
|
||||
nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
|
||||
|
||||
// The button should occupy the same space as a scrollbar
|
||||
nsRect buttonRect = mButtonFrame->GetRect();
|
||||
WritingMode wm = aReflowState.GetWritingMode();
|
||||
nscoord containerWidth = aReflowState.ComputedWidth();
|
||||
LogicalRect buttonRect = mButtonFrame->GetLogicalRect(containerWidth);
|
||||
|
||||
if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
|
||||
buttonRect.x = aReflowState.ComputedPhysicalBorderPadding().left -
|
||||
aReflowState.ComputedPhysicalPadding().left;
|
||||
}
|
||||
else {
|
||||
buttonRect.x = aReflowState.ComputedPhysicalBorderPadding().LeftRight() +
|
||||
mDisplayWidth -
|
||||
(aReflowState.ComputedPhysicalBorderPadding().right -
|
||||
aReflowState.ComputedPhysicalPadding().right);
|
||||
}
|
||||
buttonRect.width = buttonWidth;
|
||||
buttonRect.IStart(wm) =
|
||||
aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm) +
|
||||
mDisplayISize -
|
||||
(aReflowState.ComputedLogicalBorderPadding().IEnd(wm) -
|
||||
aReflowState.ComputedLogicalPadding().IEnd(wm));
|
||||
buttonRect.ISize(wm) = buttonISize;
|
||||
|
||||
buttonRect.y = this->GetUsedBorder().top;
|
||||
buttonRect.height = mDisplayFrame->GetRect().height +
|
||||
this->GetUsedPadding().TopBottom();
|
||||
buttonRect.BStart(wm) = this->GetLogicalUsedBorder(wm).BStart(wm);
|
||||
buttonRect.BSize(wm) = mDisplayFrame->BSize(wm) +
|
||||
this->GetLogicalUsedPadding(wm).BStartEnd(wm);
|
||||
|
||||
mButtonFrame->SetRect(buttonRect);
|
||||
mButtonFrame->SetRect(buttonRect, containerWidth);
|
||||
|
||||
if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) &&
|
||||
!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
|
||||
|
@ -1019,7 +1032,7 @@ void
|
|||
nsComboboxControlFrame::ActuallyDisplayText(bool aNotify)
|
||||
{
|
||||
if (mDisplayedOptionText.IsEmpty()) {
|
||||
// Have to use a non-breaking space for line-height calculations
|
||||
// Have to use a non-breaking space for line-block-size calculations
|
||||
// to be right
|
||||
static const char16_t space = 0xA0;
|
||||
mDisplayContent->SetText(&space, 1, aNotify);
|
||||
|
@ -1235,8 +1248,8 @@ public:
|
|||
mComboBox(aComboBox)
|
||||
{}
|
||||
|
||||
// Need this so that line layout knows that this block's width
|
||||
// depends on the available width.
|
||||
// Need this so that line layout knows that this block's inline size
|
||||
// depends on the available inline size.
|
||||
virtual nsIAtom* GetType() const override;
|
||||
|
||||
virtual bool IsFrameOfType(uint32_t aFlags) const override
|
||||
|
@ -1273,18 +1286,19 @@ nsComboboxDisplayFrame::Reflow(nsPresContext* aPresContext,
|
|||
nsReflowStatus& aStatus)
|
||||
{
|
||||
nsHTMLReflowState state(aReflowState);
|
||||
if (state.ComputedHeight() == NS_INTRINSICSIZE) {
|
||||
// Note that the only way we can have a computed height here is if the
|
||||
// combobox had a specified height. If it didn't, size based on what our
|
||||
// rows look like, for lack of anything better.
|
||||
state.SetComputedHeight(mComboBox->mListControlFrame->GetHeightOfARow());
|
||||
if (state.ComputedBSize() == NS_INTRINSICSIZE) {
|
||||
// Note that the only way we can have a computed block size here is
|
||||
// if the combobox had a specified block size. If it didn't, size
|
||||
// based on what our rows look like, for lack of anything better.
|
||||
state.SetComputedBSize(mComboBox->mListControlFrame->GetBSizeOfARow());
|
||||
}
|
||||
nscoord computedWidth = mComboBox->mDisplayWidth -
|
||||
state.ComputedPhysicalBorderPadding().LeftRight();
|
||||
if (computedWidth < 0) {
|
||||
computedWidth = 0;
|
||||
WritingMode wm = aReflowState.GetWritingMode();
|
||||
nscoord computedISize = mComboBox->mDisplayISize -
|
||||
state.ComputedLogicalBorderPadding().IStartEnd(wm);
|
||||
if (computedISize < 0) {
|
||||
computedISize = 0;
|
||||
}
|
||||
state.SetComputedWidth(computedWidth);
|
||||
state.SetComputedISize(computedISize);
|
||||
nsBlockFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
|
||||
aStatus = NS_FRAME_COMPLETE; // this type of frame can't be split
|
||||
}
|
||||
|
|
|
@ -146,14 +146,15 @@ public:
|
|||
virtual void RollupFromList() override;
|
||||
|
||||
/**
|
||||
* Return the available space above and below this frame for
|
||||
* Return the available space before and after this frame for
|
||||
* placing the drop-down list, and the current 2D translation.
|
||||
* Note that either or both can be less than or equal to zero,
|
||||
* if both are then the drop-down should be closed.
|
||||
*/
|
||||
void GetAvailableDropdownSpace(nscoord* aAbove,
|
||||
nscoord* aBelow,
|
||||
nsPoint* aTranslation);
|
||||
void GetAvailableDropdownSpace(mozilla::WritingMode aWM,
|
||||
nscoord* aBefore,
|
||||
nscoord* aAfter,
|
||||
mozilla::LogicalPoint* aTranslation);
|
||||
virtual int32_t GetIndexOfDisplayArea() override;
|
||||
/**
|
||||
* @note This method might destroy |this|.
|
||||
|
@ -271,9 +272,9 @@ protected:
|
|||
nsIFrame* mDropdownFrame; // dropdown list frame
|
||||
nsIListControlFrame * mListControlFrame; // ListControl Interface for the dropdown frame
|
||||
|
||||
// The width of our display area. Used by that frame's reflow to
|
||||
// size to the full width except the drop-marker.
|
||||
nscoord mDisplayWidth;
|
||||
// The inline size of our display area. Used by that frame's reflow
|
||||
// to size to the full inline size except the drop-marker.
|
||||
nscoord mDisplayISize;
|
||||
|
||||
nsRevocableEventPtr<RedisplayTextEvent> mRedisplayTextEvent;
|
||||
|
||||
|
@ -285,13 +286,13 @@ protected:
|
|||
// then open or close the combo box.
|
||||
nsCOMPtr<nsIDOMEventListener> mButtonListener;
|
||||
|
||||
// The last y-positions used for estimating available space above and
|
||||
// below for the dropdown list in GetAvailableDropdownSpace. These are
|
||||
// The last y-positions used for estimating available space before and
|
||||
// after for the dropdown list in GetAvailableDropdownSpace. These are
|
||||
// reset to nscoord_MIN in AbsolutelyPositionDropDown when placing the
|
||||
// dropdown at its actual position. The GetAvailableDropdownSpace call
|
||||
// from nsListControlFrame::ReflowAsDropdown use the last position.
|
||||
nscoord mLastDropDownAboveScreenY;
|
||||
nscoord mLastDropDownBelowScreenY;
|
||||
nscoord mLastDropDownBeforeScreenBCoord;
|
||||
nscoord mLastDropDownAfterScreenBCoord;
|
||||
// Current state of the dropdown list, true is dropped down.
|
||||
bool mDroppedDown;
|
||||
// See comment in HandleRedisplayTextEvent().
|
||||
|
|
|
@ -54,10 +54,10 @@ public:
|
|||
virtual void CaptureMouseEvents(bool aGrabMouseEvents) = 0;
|
||||
|
||||
/**
|
||||
* Returns the height of a single row in the list. This is the
|
||||
* maximum of the heights of all the options/optgroups.
|
||||
* Returns the block size of a single row in the list. This is the
|
||||
* maximum of the block sizes of all the options/optgroups.
|
||||
*/
|
||||
virtual nscoord GetHeightOfARow() = 0;
|
||||
virtual nscoord GetBSizeOfARow() = 0;
|
||||
|
||||
/**
|
||||
* Returns the number of options in the listbox
|
||||
|
|
|
@ -97,7 +97,7 @@ nsListControlFrame::nsListControlFrame(nsStyleContext* aContext)
|
|||
mHasPendingInterruptAtStartOfReflow(false),
|
||||
mDropdownCanGrow(false),
|
||||
mForceSelection(false),
|
||||
mLastDropdownComputedHeight(NS_UNCONSTRAINEDSIZE)
|
||||
mLastDropdownComputedBSize(NS_UNCONSTRAINEDSIZE)
|
||||
{
|
||||
mComboboxFrame = nullptr;
|
||||
mChangesSinceDragStart = false;
|
||||
|
@ -206,8 +206,13 @@ void nsListControlFrame::PaintFocus(nsRenderingContext& aRC, nsPoint aPt)
|
|||
} else {
|
||||
float inflation = nsLayoutUtils::FontSizeInflationFor(this);
|
||||
fRect.x = fRect.y = 0;
|
||||
if (GetWritingMode().IsVertical()) {
|
||||
fRect.width = GetScrollPortRect().width;
|
||||
fRect.height = CalcFallbackRowHeight(inflation);
|
||||
fRect.height = CalcFallbackRowBSize(inflation);
|
||||
} else {
|
||||
fRect.width = CalcFallbackRowBSize(inflation);
|
||||
fRect.height = GetScrollPortRect().height;
|
||||
}
|
||||
fRect.MoveBy(containerFrame->GetOffsetTo(this));
|
||||
}
|
||||
fRect += aPt;
|
||||
|
@ -257,22 +262,22 @@ nsListControlFrame::AccessibleType()
|
|||
#endif
|
||||
|
||||
static nscoord
|
||||
GetMaxOptionHeight(nsIFrame* aContainer)
|
||||
GetMaxOptionBSize(nsIFrame* aContainer, WritingMode aWM)
|
||||
{
|
||||
nscoord result = 0;
|
||||
for (nsIFrame* option = aContainer->GetFirstPrincipalChild();
|
||||
option; option = option->GetNextSibling()) {
|
||||
nscoord optionHeight;
|
||||
nscoord optionBSize;
|
||||
if (nsCOMPtr<nsIDOMHTMLOptGroupElement>
|
||||
(do_QueryInterface(option->GetContent()))) {
|
||||
// an optgroup
|
||||
optionHeight = GetMaxOptionHeight(option);
|
||||
optionBSize = GetMaxOptionBSize(option, aWM);
|
||||
} else {
|
||||
// an option
|
||||
optionHeight = option->GetSize().height;
|
||||
optionBSize = option->BSize(aWM);
|
||||
}
|
||||
if (result < optionHeight)
|
||||
result = optionHeight;
|
||||
if (result < optionBSize)
|
||||
result = optionBSize;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -282,22 +287,24 @@ GetMaxOptionHeight(nsIFrame* aContainer)
|
|||
//-----------------------------------------------------------------
|
||||
|
||||
nscoord
|
||||
nsListControlFrame::CalcHeightOfARow()
|
||||
nsListControlFrame::CalcBSizeOfARow()
|
||||
{
|
||||
// Calculate the height of a single row in the listbox or dropdown list by
|
||||
// using the tallest thing in the subtree, since there may be option groups
|
||||
// in addition to option elements, either of which may be visible or
|
||||
// invisible, may use different fonts, etc.
|
||||
int32_t heightOfARow = GetMaxOptionHeight(GetOptionsContainer());
|
||||
// Calculate the block size in our writing mode of a single row in the
|
||||
// listbox or dropdown list by using the tallest thing in the subtree,
|
||||
// since there may be option groups in addition to option elements,
|
||||
// either of which may be visible or invisible, may use different
|
||||
// fonts, etc.
|
||||
int32_t blockSizeOfARow = GetMaxOptionBSize(GetOptionsContainer(),
|
||||
GetWritingMode());
|
||||
|
||||
// Check to see if we have zero items (and optimize by checking
|
||||
// heightOfARow first)
|
||||
if (heightOfARow == 0 && GetNumberOfOptions() == 0) {
|
||||
// blockSizeOfARow first)
|
||||
if (blockSizeOfARow == 0 && GetNumberOfOptions() == 0) {
|
||||
float inflation = nsLayoutUtils::FontSizeInflationFor(this);
|
||||
heightOfARow = CalcFallbackRowHeight(inflation);
|
||||
blockSizeOfARow = CalcFallbackRowBSize(inflation);
|
||||
}
|
||||
|
||||
return heightOfARow;
|
||||
return blockSizeOfARow;
|
||||
}
|
||||
|
||||
nscoord
|
||||
|
@ -306,13 +313,15 @@ nsListControlFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
|
|||
nscoord result;
|
||||
DISPLAY_PREF_WIDTH(this, result);
|
||||
|
||||
// Always add scrollbar widths to the pref-width of the scrolled
|
||||
// content. Combobox frames depend on this happening in the dropdown,
|
||||
// and standalone listboxes are overflow:scroll so they need it too.
|
||||
// Always add scrollbar inline sizes to the pref-inline-size of the
|
||||
// scrolled content. Combobox frames depend on this happening in the
|
||||
// dropdown, and standalone listboxes are overflow:scroll so they need
|
||||
// it too.
|
||||
WritingMode wm = GetWritingMode();
|
||||
result = GetScrolledFrame()->GetPrefISize(aRenderingContext);
|
||||
result = NSCoordSaturatingAdd(result,
|
||||
GetDesiredScrollbarSizes(PresContext(), aRenderingContext).LeftRight());
|
||||
|
||||
LogicalMargin scrollbarSize(wm, GetDesiredScrollbarSizes(PresContext(),
|
||||
aRenderingContext));
|
||||
result = NSCoordSaturatingAdd(result, scrollbarSize.IStartEnd(wm));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -322,11 +331,15 @@ nsListControlFrame::GetMinISize(nsRenderingContext *aRenderingContext)
|
|||
nscoord result;
|
||||
DISPLAY_MIN_WIDTH(this, result);
|
||||
|
||||
// Always add scrollbar widths to the min-width of the scrolled
|
||||
// content. Combobox frames depend on this happening in the dropdown,
|
||||
// and standalone listboxes are overflow:scroll so they need it too.
|
||||
// Always add scrollbar inline sizes to the min-inline-size of the
|
||||
// scrolled content. Combobox frames depend on this happening in the
|
||||
// dropdown, and standalone listboxes are overflow:scroll so they need
|
||||
// it too.
|
||||
WritingMode wm = GetWritingMode();
|
||||
result = GetScrolledFrame()->GetMinISize(aRenderingContext);
|
||||
result += GetDesiredScrollbarSizes(PresContext(), aRenderingContext).LeftRight();
|
||||
LogicalMargin scrollbarSize(wm, GetDesiredScrollbarSizes(PresContext(),
|
||||
aRenderingContext));
|
||||
result += scrollbarSize.IStartEnd(wm);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -337,8 +350,8 @@ nsListControlFrame::Reflow(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
NS_PRECONDITION(aReflowState.ComputedWidth() != NS_UNCONSTRAINEDSIZE,
|
||||
"Must have a computed width");
|
||||
NS_PRECONDITION(aReflowState.ComputedISize() != NS_UNCONSTRAINEDSIZE,
|
||||
"Must have a computed inline size");
|
||||
|
||||
SchedulePaint();
|
||||
|
||||
|
@ -366,65 +379,69 @@ nsListControlFrame::Reflow(nsPresContext* aPresContext,
|
|||
|
||||
MarkInReflow();
|
||||
/*
|
||||
* Due to the fact that our intrinsic height depends on the heights of our
|
||||
* kids, we end up having to do two-pass reflow, in general -- the first pass
|
||||
* to find the intrinsic height and a second pass to reflow the scrollframe
|
||||
* at that height (which will size the scrollbars correctly, etc).
|
||||
* Due to the fact that our intrinsic block size depends on the block
|
||||
* sizes of our kids, we end up having to do two-pass reflow, in
|
||||
* general -- the first pass to find the intrinsic block size and a
|
||||
* second pass to reflow the scrollframe at that block size (which
|
||||
* will size the scrollbars correctly, etc).
|
||||
*
|
||||
* Naturaly, we want to avoid doing the second reflow as much as possible.
|
||||
* We can skip it in the following cases (in all of which the first reflow is
|
||||
* already happening at the right height):
|
||||
* Naturally, we want to avoid doing the second reflow as much as
|
||||
* possible.
|
||||
* We can skip it in the following cases (in all of which the first
|
||||
* reflow is already happening at the right block size):
|
||||
*
|
||||
* - We're reflowing with a constrained computed height -- just use that
|
||||
* height.
|
||||
* - We're not dirty and have no dirty kids and shouldn't be reflowing all
|
||||
* kids. In this case, our cached max height of a child is not going to
|
||||
* change.
|
||||
* - We do our first reflow using our cached max height of a child, then
|
||||
* compute the new max height and it's the same as the old one.
|
||||
* - We're reflowing with a constrained computed block size -- just
|
||||
* use that block size.
|
||||
* - We're not dirty and have no dirty kids and shouldn't be reflowing
|
||||
* all kids. In this case, our cached max block size of a child is
|
||||
* not going to change.
|
||||
* - We do our first reflow using our cached max block size of a
|
||||
* child, then compute the new max block size and it's the same as
|
||||
* the old one.
|
||||
*/
|
||||
|
||||
bool autoHeight = (aReflowState.ComputedHeight() == NS_UNCONSTRAINEDSIZE);
|
||||
bool autoBSize = (aReflowState.ComputedBSize() == NS_UNCONSTRAINEDSIZE);
|
||||
|
||||
mMightNeedSecondPass = autoHeight &&
|
||||
mMightNeedSecondPass = autoBSize &&
|
||||
(NS_SUBTREE_DIRTY(this) || aReflowState.ShouldReflowAllKids());
|
||||
|
||||
nsHTMLReflowState state(aReflowState);
|
||||
int32_t length = GetNumberOfRows();
|
||||
|
||||
nscoord oldHeightOfARow = HeightOfARow();
|
||||
nscoord oldBSizeOfARow = BSizeOfARow();
|
||||
|
||||
if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW) && autoHeight) {
|
||||
// When not doing an initial reflow, and when the height is auto, start off
|
||||
// with our computed height set to what we'd expect our height to be.
|
||||
nscoord computedHeight = CalcIntrinsicBSize(oldHeightOfARow, length);
|
||||
computedHeight = state.ApplyMinMaxHeight(computedHeight);
|
||||
state.SetComputedHeight(computedHeight);
|
||||
if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW) && autoBSize) {
|
||||
// When not doing an initial reflow, and when the block size is
|
||||
// auto, start off with our computed block size set to what we'd
|
||||
// expect our block size to be.
|
||||
nscoord computedBSize = CalcIntrinsicBSize(oldBSizeOfARow, length);
|
||||
computedBSize = state.ApplyMinMaxBSize(computedBSize);
|
||||
state.SetComputedBSize(computedBSize);
|
||||
}
|
||||
|
||||
nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
|
||||
|
||||
if (!mMightNeedSecondPass) {
|
||||
NS_ASSERTION(!autoHeight || HeightOfARow() == oldHeightOfARow,
|
||||
"How did our height of a row change if nothing was dirty?");
|
||||
NS_ASSERTION(!autoHeight ||
|
||||
NS_ASSERTION(!autoBSize || BSizeOfARow() == oldBSizeOfARow,
|
||||
"How did our BSize of a row change if nothing was dirty?");
|
||||
NS_ASSERTION(!autoBSize ||
|
||||
!(GetStateBits() & NS_FRAME_FIRST_REFLOW),
|
||||
"How do we not need a second pass during initial reflow at "
|
||||
"auto height?");
|
||||
"auto BSize?");
|
||||
NS_ASSERTION(!IsScrollbarUpdateSuppressed(),
|
||||
"Shouldn't be suppressing if we don't need a second pass!");
|
||||
if (!autoHeight) {
|
||||
// Update our mNumDisplayRows based on our new row height now that we
|
||||
// know it. Note that if autoHeight and we landed in this code then we
|
||||
// already set mNumDisplayRows in CalcIntrinsicBSize. Also note that we
|
||||
// can't use HeightOfARow() here because that just uses a cached value
|
||||
// that we didn't compute.
|
||||
nscoord rowHeight = CalcHeightOfARow();
|
||||
if (rowHeight == 0) {
|
||||
if (!autoBSize) {
|
||||
// Update our mNumDisplayRows based on our new row block size now
|
||||
// that we know it. Note that if autoBSize and we landed in this
|
||||
// code then we already set mNumDisplayRows in CalcIntrinsicBSize.
|
||||
// Also note that we can't use BSizeOfARow() here because that
|
||||
// just uses a cached value that we didn't compute.
|
||||
nscoord rowBSize = CalcBSizeOfARow();
|
||||
if (rowBSize == 0) {
|
||||
// Just pick something
|
||||
mNumDisplayRows = 1;
|
||||
} else {
|
||||
mNumDisplayRows = std::max(1, state.ComputedHeight() / rowHeight);
|
||||
mNumDisplayRows = std::max(1, state.ComputedBSize() / rowBSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,12 +450,12 @@ nsListControlFrame::Reflow(nsPresContext* aPresContext,
|
|||
|
||||
mMightNeedSecondPass = false;
|
||||
|
||||
// Now see whether we need a second pass. If we do, our nsSelectsAreaFrame
|
||||
// will have suppressed the scrollbar update.
|
||||
// Now see whether we need a second pass. If we do, our
|
||||
// nsSelectsAreaFrame will have suppressed the scrollbar update.
|
||||
if (!IsScrollbarUpdateSuppressed()) {
|
||||
// All done. No need to do more reflow.
|
||||
NS_ASSERTION(!IsScrollbarUpdateSuppressed(),
|
||||
"Shouldn't be suppressing if the height of a row has not "
|
||||
"Shouldn't be suppressing if the block size of a row has not "
|
||||
"changed!");
|
||||
return;
|
||||
}
|
||||
|
@ -446,17 +463,18 @@ nsListControlFrame::Reflow(nsPresContext* aPresContext,
|
|||
SetSuppressScrollbarUpdate(false);
|
||||
|
||||
// Gotta reflow again.
|
||||
// XXXbz We're just changing the height here; do we need to dirty ourselves
|
||||
// or anything like that? We might need to, per the letter of the reflow
|
||||
// protocol, but things seem to work fine without it... Is that just an
|
||||
// implementation detail of nsHTMLScrollFrame that we're depending on?
|
||||
// XXXbz We're just changing the block size here; do we need to dirty
|
||||
// ourselves or anything like that? We might need to, per the letter
|
||||
// of the reflow protocol, but things seem to work fine without it...
|
||||
// Is that just an implementation detail of nsHTMLScrollFrame that
|
||||
// we're depending on?
|
||||
nsHTMLScrollFrame::DidReflow(aPresContext, &state,
|
||||
nsDidReflowStatus::FINISHED);
|
||||
|
||||
// Now compute the height we want to have
|
||||
nscoord computedHeight = CalcIntrinsicBSize(HeightOfARow(), length);
|
||||
computedHeight = state.ApplyMinMaxHeight(computedHeight);
|
||||
state.SetComputedHeight(computedHeight);
|
||||
// Now compute the block size we want to have
|
||||
nscoord computedBSize = CalcIntrinsicBSize(BSizeOfARow(), length);
|
||||
computedBSize = state.ApplyMinMaxBSize(computedBSize);
|
||||
state.SetComputedBSize(computedBSize);
|
||||
|
||||
// XXXbz to make the ascent really correct, we should add our
|
||||
// mComputedPadding.top to it (and subtract it from descent). Need that
|
||||
|
@ -470,36 +488,39 @@ nsListControlFrame::ReflowAsDropdown(nsPresContext* aPresContext,
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
{
|
||||
NS_PRECONDITION(aReflowState.ComputedHeight() == NS_UNCONSTRAINEDSIZE,
|
||||
"We should not have a computed height here!");
|
||||
NS_PRECONDITION(aReflowState.ComputedBSize() == NS_UNCONSTRAINEDSIZE,
|
||||
"We should not have a computed block size here!");
|
||||
|
||||
mMightNeedSecondPass = NS_SUBTREE_DIRTY(this) ||
|
||||
aReflowState.ShouldReflowAllKids();
|
||||
|
||||
WritingMode wm = aReflowState.GetWritingMode();
|
||||
#ifdef DEBUG
|
||||
nscoord oldHeightOfARow = HeightOfARow();
|
||||
nscoord oldVisibleHeight = (GetStateBits() & NS_FRAME_FIRST_REFLOW) ?
|
||||
NS_UNCONSTRAINEDSIZE : GetScrolledFrame()->GetSize().height;
|
||||
nscoord oldBSizeOfARow = BSizeOfARow();
|
||||
nscoord oldVisibleBSize = (GetStateBits() & NS_FRAME_FIRST_REFLOW) ?
|
||||
NS_UNCONSTRAINEDSIZE : GetScrolledFrame()->BSize(wm);
|
||||
#endif
|
||||
|
||||
nsHTMLReflowState state(aReflowState);
|
||||
|
||||
if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
|
||||
// When not doing an initial reflow, and when the height is auto, start off
|
||||
// with our computed height set to what we'd expect our height to be.
|
||||
// Note: At this point, mLastDropdownComputedHeight can be
|
||||
// NS_UNCONSTRAINEDSIZE in cases when last time we didn't have to constrain
|
||||
// the height. That's fine; just do the same thing as last time.
|
||||
state.SetComputedHeight(mLastDropdownComputedHeight);
|
||||
// When not doing an initial reflow, and when the block size is
|
||||
// auto, start off with our computed block size set to what we'd
|
||||
// expect our block size to be.
|
||||
// Note: At this point, mLastDropdownComputedBSize can be
|
||||
// NS_UNCONSTRAINEDSIZE in cases when last time we didn't have to
|
||||
// constrain the block size. That's fine; just do the same thing as
|
||||
// last time.
|
||||
state.SetComputedBSize(mLastDropdownComputedBSize);
|
||||
}
|
||||
|
||||
nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
|
||||
|
||||
if (!mMightNeedSecondPass) {
|
||||
NS_ASSERTION(oldVisibleHeight == GetScrolledFrame()->GetSize().height,
|
||||
"How did our kid's height change if nothing was dirty?");
|
||||
NS_ASSERTION(HeightOfARow() == oldHeightOfARow,
|
||||
"How did our height of a row change if nothing was dirty?");
|
||||
NS_ASSERTION(oldVisibleBSize == GetScrolledFrame()->BSize(wm),
|
||||
"How did our kid's BSize change if nothing was dirty?");
|
||||
NS_ASSERTION(BSizeOfARow() == oldBSizeOfARow,
|
||||
"How did our BSize of a row change if nothing was dirty?");
|
||||
NS_ASSERTION(!IsScrollbarUpdateSuppressed(),
|
||||
"Shouldn't be suppressing if we don't need a second pass!");
|
||||
NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW),
|
||||
|
@ -520,61 +541,64 @@ nsListControlFrame::ReflowAsDropdown(nsPresContext* aPresContext,
|
|||
|
||||
SetSuppressScrollbarUpdate(false);
|
||||
|
||||
nscoord visibleHeight = GetScrolledFrame()->GetSize().height;
|
||||
nscoord heightOfARow = HeightOfARow();
|
||||
nscoord visibleBSize = GetScrolledFrame()->BSize(wm);
|
||||
nscoord blockSizeOfARow = BSizeOfARow();
|
||||
|
||||
// Gotta reflow again.
|
||||
// XXXbz We're just changing the height here; do we need to dirty ourselves
|
||||
// or anything like that? We might need to, per the letter of the reflow
|
||||
// protocol, but things seem to work fine without it... Is that just an
|
||||
// implementation detail of nsHTMLScrollFrame that we're depending on?
|
||||
// XXXbz We're just changing the block size here; do we need to dirty
|
||||
// ourselves or anything like that? We might need to, per the letter
|
||||
// of the reflow protocol, but things seem to work fine without it...
|
||||
// Is that just an implementation detail of nsHTMLScrollFrame that
|
||||
// we're depending on?
|
||||
nsHTMLScrollFrame::DidReflow(aPresContext, &state,
|
||||
nsDidReflowStatus::FINISHED);
|
||||
|
||||
// Now compute the height we want to have.
|
||||
// Now compute the block size we want to have.
|
||||
// Note: no need to apply min/max constraints, since we have no such
|
||||
// rules applied to the combobox dropdown.
|
||||
|
||||
mDropdownCanGrow = false;
|
||||
if (visibleHeight <= 0 || heightOfARow <= 0) {
|
||||
// Looks like we have no options. Just size us to a single row height.
|
||||
state.SetComputedHeight(heightOfARow);
|
||||
if (visibleBSize <= 0 || blockSizeOfARow <= 0) {
|
||||
// Looks like we have no options. Just size us to a single row
|
||||
// block size.
|
||||
state.SetComputedBSize(blockSizeOfARow);
|
||||
mNumDisplayRows = 1;
|
||||
} else {
|
||||
nsComboboxControlFrame* combobox = static_cast<nsComboboxControlFrame*>(mComboboxFrame);
|
||||
nsPoint translation;
|
||||
nscoord above, below;
|
||||
combobox->GetAvailableDropdownSpace(&above, &below, &translation);
|
||||
if (above <= 0 && below <= 0) {
|
||||
state.SetComputedHeight(heightOfARow);
|
||||
nsComboboxControlFrame* combobox =
|
||||
static_cast<nsComboboxControlFrame*>(mComboboxFrame);
|
||||
LogicalPoint translation(wm);
|
||||
nscoord before, after;
|
||||
combobox->GetAvailableDropdownSpace(wm, &before, &after, &translation);
|
||||
if (before <= 0 && after <= 0) {
|
||||
state.SetComputedBSize(blockSizeOfARow);
|
||||
mNumDisplayRows = 1;
|
||||
mDropdownCanGrow = GetNumberOfRows() > 1;
|
||||
} else {
|
||||
nscoord bp = aReflowState.ComputedPhysicalBorderPadding().TopBottom();
|
||||
nscoord availableHeight = std::max(above, below) - bp;
|
||||
nscoord newHeight;
|
||||
nscoord bp = aReflowState.ComputedLogicalBorderPadding().BStartEnd(wm);
|
||||
nscoord availableBSize = std::max(before, after) - bp;
|
||||
nscoord newBSize;
|
||||
uint32_t rows;
|
||||
if (visibleHeight <= availableHeight) {
|
||||
// The dropdown fits in the available height.
|
||||
if (visibleBSize <= availableBSize) {
|
||||
// The dropdown fits in the available block size.
|
||||
rows = GetNumberOfRows();
|
||||
mNumDisplayRows = clamped<uint32_t>(rows, 1, kMaxDropDownRows);
|
||||
if (mNumDisplayRows == rows) {
|
||||
newHeight = visibleHeight; // use the exact height
|
||||
newBSize = visibleBSize; // use the exact block size
|
||||
} else {
|
||||
newHeight = mNumDisplayRows * heightOfARow; // approximate
|
||||
newBSize = mNumDisplayRows * blockSizeOfARow; // approximate
|
||||
}
|
||||
} else {
|
||||
rows = availableHeight / heightOfARow;
|
||||
rows = availableBSize / blockSizeOfARow;
|
||||
mNumDisplayRows = clamped<uint32_t>(rows, 1, kMaxDropDownRows);
|
||||
newHeight = mNumDisplayRows * heightOfARow; // approximate
|
||||
newBSize = mNumDisplayRows * blockSizeOfARow; // approximate
|
||||
}
|
||||
state.SetComputedHeight(newHeight);
|
||||
mDropdownCanGrow = visibleHeight - newHeight >= heightOfARow &&
|
||||
state.SetComputedBSize(newBSize);
|
||||
mDropdownCanGrow = visibleBSize - newBSize >= blockSizeOfARow &&
|
||||
mNumDisplayRows != kMaxDropDownRows;
|
||||
}
|
||||
}
|
||||
|
||||
mLastDropdownComputedHeight = state.ComputedHeight();
|
||||
mLastDropdownComputedBSize = state.ComputedBSize();
|
||||
|
||||
nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
|
||||
}
|
||||
|
@ -584,13 +608,17 @@ nsListControlFrame::GetScrollbarStyles() const
|
|||
{
|
||||
// We can't express this in the style system yet; when we can, this can go away
|
||||
// and GetScrollbarStyles can be devirtualized
|
||||
int32_t verticalStyle = IsInDropDownMode() ? NS_STYLE_OVERFLOW_AUTO
|
||||
int32_t style = IsInDropDownMode() ? NS_STYLE_OVERFLOW_AUTO
|
||||
: NS_STYLE_OVERFLOW_SCROLL;
|
||||
return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, verticalStyle);
|
||||
if (GetWritingMode().IsVertical()) {
|
||||
return ScrollbarStyles(style, NS_STYLE_OVERFLOW_HIDDEN);
|
||||
} else {
|
||||
return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, style);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsListControlFrame::ShouldPropagateComputedHeightToScrolledContent() const
|
||||
nsListControlFrame::ShouldPropagateComputedBSizeToScrolledContent() const
|
||||
{
|
||||
return !IsInDropDownMode();
|
||||
}
|
||||
|
@ -1480,9 +1508,9 @@ nsListControlFrame::GetFrameName(nsAString& aResult) const
|
|||
#endif
|
||||
|
||||
nscoord
|
||||
nsListControlFrame::GetHeightOfARow()
|
||||
nsListControlFrame::GetBSizeOfARow()
|
||||
{
|
||||
return HeightOfARow();
|
||||
return BSizeOfARow();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -1515,22 +1543,22 @@ nsListControlFrame::IsLeftButton(nsIDOMEvent* aMouseEvent)
|
|||
}
|
||||
|
||||
nscoord
|
||||
nsListControlFrame::CalcFallbackRowHeight(float aFontSizeInflation)
|
||||
nsListControlFrame::CalcFallbackRowBSize(float aFontSizeInflation)
|
||||
{
|
||||
nscoord rowHeight = 0;
|
||||
nscoord rowBSize = 0;
|
||||
|
||||
nsRefPtr<nsFontMetrics> fontMet;
|
||||
nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet),
|
||||
aFontSizeInflation);
|
||||
if (fontMet) {
|
||||
rowHeight = fontMet->MaxHeight();
|
||||
rowBSize = fontMet->MaxHeight();
|
||||
}
|
||||
|
||||
return rowHeight;
|
||||
return rowBSize;
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsListControlFrame::CalcIntrinsicBSize(nscoord aHeightOfARow,
|
||||
nsListControlFrame::CalcIntrinsicBSize(nscoord aBSizeOfARow,
|
||||
int32_t aNumberOfOptions)
|
||||
{
|
||||
NS_PRECONDITION(!IsInDropDownMode(),
|
||||
|
@ -1548,7 +1576,7 @@ nsListControlFrame::CalcIntrinsicBSize(nscoord aHeightOfARow,
|
|||
mNumDisplayRows = 4;
|
||||
}
|
||||
|
||||
return mNumDisplayRows * aHeightOfARow;
|
||||
return mNumDisplayRows * aBSizeOfARow;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
|
|
@ -109,7 +109,7 @@ public:
|
|||
virtual void SetFocus(bool aOn = true, bool aRepaint = false) override;
|
||||
|
||||
virtual mozilla::ScrollbarStyles GetScrollbarStyles() const override;
|
||||
virtual bool ShouldPropagateComputedHeightToScrolledContent() const override;
|
||||
virtual bool ShouldPropagateComputedBSizeToScrolledContent() const override;
|
||||
|
||||
// for accessibility purposes
|
||||
#ifdef ACCESSIBILITY
|
||||
|
@ -129,7 +129,7 @@ public:
|
|||
virtual void GetOptionText(uint32_t aIndex, nsAString& aStr) override;
|
||||
|
||||
virtual void CaptureMouseEvents(bool aGrabMouseEvents) override;
|
||||
virtual nscoord GetHeightOfARow() override;
|
||||
virtual nscoord GetBSizeOfARow() override;
|
||||
virtual uint32_t GetNumberOfOptions() override;
|
||||
virtual void AboutToDropDown() override;
|
||||
|
||||
|
@ -203,10 +203,11 @@ public:
|
|||
void InvalidateFocus();
|
||||
|
||||
/**
|
||||
* Function to calculate the height a row, for use with the "size" attribute.
|
||||
* Function to calculate the block size of a row, for use with the
|
||||
* "size" attribute.
|
||||
* Can't be const because GetNumberOfOptions() isn't const.
|
||||
*/
|
||||
nscoord CalcHeightOfARow();
|
||||
nscoord CalcBSizeOfARow();
|
||||
|
||||
/**
|
||||
* Function to ask whether we're currently in what might be the
|
||||
|
@ -327,12 +328,13 @@ protected:
|
|||
bool CheckIfAllFramesHere();
|
||||
bool IsLeftButton(nsIDOMEvent* aMouseEvent);
|
||||
|
||||
// guess at a row height based on our own style.
|
||||
nscoord CalcFallbackRowHeight(float aFontSizeInflation);
|
||||
// guess at a row block size based on our own style.
|
||||
nscoord CalcFallbackRowBSize(float aFontSizeInflation);
|
||||
|
||||
// CalcIntrinsicBSize computes our intrinsic height (taking the "size"
|
||||
// attribute into account). This should only be called in non-dropdown mode.
|
||||
nscoord CalcIntrinsicBSize(nscoord aHeightOfARow, int32_t aNumberOfOptions);
|
||||
// CalcIntrinsicBSize computes our intrinsic block size (taking the
|
||||
// "size" attribute into account). This should only be called in
|
||||
// non-dropdown mode.
|
||||
nscoord CalcIntrinsicBSize(nscoord aBSizeOfARow, int32_t aNumberOfOptions);
|
||||
|
||||
// Dropped down stuff
|
||||
void SetComboboxItem(int32_t aIndex);
|
||||
|
@ -378,8 +380,8 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
nscoord HeightOfARow() {
|
||||
return GetOptionsContainer()->HeightOfARow();
|
||||
nscoord BSizeOfARow() {
|
||||
return GetOptionsContainer()->BSizeOfARow();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -425,10 +427,11 @@ protected:
|
|||
// True if the selection can be set to nothing or disabled options.
|
||||
bool mForceSelection:1;
|
||||
|
||||
// The last computed height we reflowed at if we're a combobox dropdown.
|
||||
// The last computed block size we reflowed at if we're a combobox
|
||||
// dropdown.
|
||||
// XXXbz should we be using a subclass here? Or just not worry
|
||||
// about the extra member on listboxes?
|
||||
nscoord mLastDropdownComputedHeight;
|
||||
nscoord mLastDropdownComputedBSize;
|
||||
|
||||
// At the time of our last dropdown, the backstop color to draw in case we
|
||||
// are translucent.
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
#include "nsIContent.h"
|
||||
#include "nsListControlFrame.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "WritingModes.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
nsContainerFrame*
|
||||
NS_NewSelectsAreaFrame(nsIPresShell* aShell, nsStyleContext* aContext, nsFrameState aFlags)
|
||||
|
@ -169,30 +172,33 @@ nsSelectsAreaFrame::Reflow(nsPresContext* aPresContext,
|
|||
|
||||
// See similar logic in nsListControlFrame::Reflow and
|
||||
// nsListControlFrame::ReflowAsDropdown. We need to match it here.
|
||||
nscoord oldHeight;
|
||||
WritingMode wm = aReflowState.GetWritingMode();
|
||||
nscoord oldBSize;
|
||||
if (isInDropdownMode) {
|
||||
// Store the height now in case it changes during
|
||||
// Store the block size now in case it changes during
|
||||
// nsBlockFrame::Reflow for some odd reason.
|
||||
if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
|
||||
oldHeight = GetSize().height;
|
||||
oldBSize = BSize(wm);
|
||||
} else {
|
||||
oldHeight = NS_UNCONSTRAINEDSIZE;
|
||||
oldBSize = NS_UNCONSTRAINEDSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
|
||||
|
||||
// Check whether we need to suppress scrollbar updates. We want to do that if
|
||||
// we're in a possible first pass and our height of a row has changed.
|
||||
// Check whether we need to suppress scrollbar updates. We want to do
|
||||
// that if we're in a possible first pass and our block size of a row
|
||||
// has changed.
|
||||
if (list->MightNeedSecondPass()) {
|
||||
nscoord newHeightOfARow = list->CalcHeightOfARow();
|
||||
// We'll need a second pass if our height of a row changed. For
|
||||
// comboboxes, we'll also need it if our height changed. If we're going
|
||||
// to do a second pass, suppress scrollbar updates for this pass.
|
||||
if (newHeightOfARow != mHeightOfARow ||
|
||||
(isInDropdownMode && (oldHeight != aDesiredSize.Height() ||
|
||||
oldHeight != GetSize().height))) {
|
||||
mHeightOfARow = newHeightOfARow;
|
||||
nscoord newBSizeOfARow = list->CalcBSizeOfARow();
|
||||
// We'll need a second pass if our block size of a row changed. For
|
||||
// comboboxes, we'll also need it if our block size changed. If
|
||||
// we're going to do a second pass, suppress scrollbar updates for
|
||||
// this pass.
|
||||
if (newBSizeOfARow != mBSizeOfARow ||
|
||||
(isInDropdownMode && (oldBSize != aDesiredSize.BSize(wm) ||
|
||||
oldBSize != BSize(wm)))) {
|
||||
mBSizeOfARow = newBSizeOfARow;
|
||||
list->SetSuppressScrollbarUpdate(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,19 +30,20 @@ public:
|
|||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus) override;
|
||||
|
||||
nscoord HeightOfARow() const { return mHeightOfARow; }
|
||||
nscoord BSizeOfARow() const { return mBSizeOfARow; }
|
||||
|
||||
protected:
|
||||
explicit nsSelectsAreaFrame(nsStyleContext* aContext) :
|
||||
nsBlockFrame(aContext),
|
||||
mHeightOfARow(0)
|
||||
mBSizeOfARow(0)
|
||||
{}
|
||||
|
||||
// We cache the height of a single row so that changes to the "size"
|
||||
// attribute, padding, etc. can all be handled with only one reflow. We'll
|
||||
// have to reflow twice if someone changes our font size or something like
|
||||
// that, so that the heights of our options will change.
|
||||
nscoord mHeightOfARow;
|
||||
// We cache the block size of a single row so that changes to the
|
||||
// "size" attribute, padding, etc. can all be handled with only one
|
||||
// reflow. We'll have to reflow twice if someone changes our font
|
||||
// size or something like that, so that the block size of our options
|
||||
// will change.
|
||||
nscoord mBSizeOfARow;
|
||||
};
|
||||
|
||||
#endif /* nsSelectsAreaFrame_h___ */
|
||||
|
|
|
@ -438,7 +438,7 @@ nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowState* aState,
|
|||
nscoord computedBSize = aState->mReflowState.ComputedBSize();
|
||||
nscoord computedMinBSize = aState->mReflowState.ComputedMinBSize();
|
||||
nscoord computedMaxBSize = aState->mReflowState.ComputedMaxBSize();
|
||||
if (!ShouldPropagateComputedHeightToScrolledContent()) {
|
||||
if (!ShouldPropagateComputedBSizeToScrolledContent()) {
|
||||
computedBSize = NS_UNCONSTRAINEDSIZE;
|
||||
computedMinBSize = 0;
|
||||
computedMaxBSize = NS_UNCONSTRAINEDSIZE;
|
||||
|
|
|
@ -939,11 +939,11 @@ protected:
|
|||
bool InInitialReflow() const;
|
||||
|
||||
/**
|
||||
* Override this to return false if computed height/min-height/max-height
|
||||
* Override this to return false if computed bsize/min-bsize/max-bsize
|
||||
* should NOT be propagated to child content.
|
||||
* nsListControlFrame uses this.
|
||||
*/
|
||||
virtual bool ShouldPropagateComputedHeightToScrolledContent() const { return true; }
|
||||
virtual bool ShouldPropagateComputedBSizeToScrolledContent() const { return true; }
|
||||
|
||||
private:
|
||||
friend class mozilla::ScrollFrameHelper;
|
||||
|
|
|
@ -192,7 +192,6 @@ textarea:-moz-read-write {
|
|||
}
|
||||
|
||||
select {
|
||||
writing-mode: horizontal-tb !important; /* XXX remove when bug 1112954 is fixed */
|
||||
margin: 0;
|
||||
border-color: ThreeDFace;
|
||||
background-color: -moz-Combobox;
|
||||
|
|
Загрузка…
Ссылка в новой задаче