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:
Simon Montagu 2015-02-08 07:31:14 -08:00
Родитель b1b410165b
Коммит 37f044b028
10 изменённых файлов: 380 добавлений и 328 удалений

Просмотреть файл

@ -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;
fRect.width = GetScrollPortRect().width;
fRect.height = CalcFallbackRowHeight(inflation);
if (GetWritingMode().IsVertical()) {
fRect.width = GetScrollPortRect().width;
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
: NS_STYLE_OVERFLOW_SCROLL;
return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, verticalStyle);
int32_t style = IsInDropDownMode() ? NS_STYLE_OVERFLOW_AUTO
: NS_STYLE_OVERFLOW_SCROLL;
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,23 +1543,23 @@ 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,
int32_t aNumberOfOptions)
nsListControlFrame::CalcIntrinsicBSize(nscoord aBSizeOfARow,
int32_t aNumberOfOptions)
{
NS_PRECONDITION(!IsInDropDownMode(),
"Shouldn't be in dropdown mode when we call this");
@ -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)
@ -164,35 +167,38 @@ nsSelectsAreaFrame::Reflow(nsPresContext* aPresContext,
NS_ASSERTION(list,
"Must have an nsListControlFrame! Frame constructor is "
"broken");
bool isInDropdownMode = list->IsInDropDownMode();
// 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;