Bug 842179 - Keep the thumb for <input type=range> within its content box. r=dholbert.

This commit is contained in:
Jonathan Watt 2013-03-15 09:04:54 +00:00
Родитель 6f23cbee1b
Коммит efc4d6e563
2 изменённых файлов: 100 добавлений и 77 удалений

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

@ -242,63 +242,26 @@ nsRangeFrame::ReflowAnonymousContent(nsPresContext* aPresContext,
nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame();
if (thumbFrame) { // display:none?
// Position the thumb:
// The idea here is that we want to position the thumb so that the center
// of the thumb is on an imaginary line drawn from the middle of one edge
// of the range frame's content box to the middle of the opposite edge of
// its content box (the opposite edges being the left/right edge if the
// range is horizontal, or else the top/bottom edges if the range is
// vertical). How far along this line the center of the thumb is placed
// depends on the value of the range.
nsSize frameSizeOverride(aDesiredSize.width, aDesiredSize.height);
bool isHorizontal = IsHorizontal(&frameSizeOverride);
double valueAsFraction = GetValueAsFractionOfRange();
MOZ_ASSERT(valueAsFraction >= 0.0 && valueAsFraction <= 1.0);
nsHTMLReflowState thumbReflowState(aPresContext, aReflowState, thumbFrame,
nsSize(aReflowState.ComputedWidth(),
NS_UNCONSTRAINEDSIZE));
// Find the x/y position of the thumb frame such that it will be positioned
// as described above. These coordinates are with respect to the
// nsRangeFrame's border-box.
nscoord thumbX, thumbY;
if (isHorizontal) {
thumbX = NSToCoordRound(rangeFrameContentBoxWidth * valueAsFraction);
if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
thumbX = rangeFrameContentBoxWidth - thumbX;
}
thumbY = rangeFrameContentBoxHeight / 2;
} else {
thumbX = rangeFrameContentBoxWidth / 2;
// For vertical range zero is at the bottom, so subtract from height:
thumbY = rangeFrameContentBoxHeight -
NSToCoordRound(rangeFrameContentBoxHeight * valueAsFraction);
}
thumbX -= thumbReflowState.mComputedBorderPadding.left +
thumbReflowState.ComputedWidth() / 2;
thumbY -= thumbReflowState.mComputedBorderPadding.top +
thumbReflowState.ComputedHeight() / 2;
// Make relative to our border box instead of our content box:
thumbX += aReflowState.mComputedBorderPadding.left;
thumbY += aReflowState.mComputedBorderPadding.top;
// Where we position of the thumb depends on its size, so we first reflow
// the thumb at {0,0} to obtain its size, then position it afterwards.
nsReflowStatus frameStatus = NS_FRAME_COMPLETE;
nsHTMLReflowMetrics thumbDesiredSize;
nsresult rv = ReflowChild(thumbFrame, aPresContext, thumbDesiredSize,
thumbReflowState, thumbX, thumbY, 0, frameStatus);
thumbReflowState, 0, 0, 0, frameStatus);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus),
"We gave our child unconstrained height, so it should be complete");
rv = FinishReflowChild(thumbFrame, aPresContext, &thumbReflowState,
thumbDesiredSize, thumbX, thumbY, 0);
thumbDesiredSize, 0, 0, 0);
NS_ENSURE_SUCCESS(rv, rv);
DoUpdateThumbPosition(thumbFrame, nsSize(aDesiredSize.width,
aDesiredSize.height));
}
return NS_OK;
@ -348,34 +311,57 @@ nsRangeFrame::GetValueAtEventPoint(nsGUIEvent* aEvent)
MOZ_ASSERT(MOZ_DOUBLE_IS_FINITE(minimum) &&
MOZ_DOUBLE_IS_FINITE(maximum),
"type=range should have a default maximum/minimum");
nsRect contentRect = GetContentRectRelativeToSelf();
if (maximum <= minimum ||
(IsHorizontal() && contentRect.width <= 0) ||
(!IsHorizontal() && contentRect.height <= 0)) {
if (maximum <= minimum) {
return minimum;
}
double range = maximum - minimum;
nsIntPoint point;
nsIntPoint absPoint;
if (aEvent->eventStructType == NS_TOUCH_EVENT) {
MOZ_ASSERT(static_cast<nsTouchEvent*>(aEvent)->touches.Length() == 1,
"Unexpected number of touches");
point = static_cast<nsTouchEvent*>(aEvent)->touches[0]->mRefPoint;
absPoint = static_cast<nsTouchEvent*>(aEvent)->touches[0]->mRefPoint;
} else {
point = aEvent->refPoint;
absPoint = aEvent->refPoint;
}
nsPoint point =
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, absPoint, this);
nsRect rangeContentRect = GetContentRectRelativeToSelf();
nsSize thumbSize;
nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame();
if (thumbFrame) { // diplay:none?
thumbSize = thumbFrame->GetSize();
}
nsMargin borderAndPadding = GetUsedBorderAndPadding();
nsPoint contentPoint =
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, point, this) -
nsPoint(borderAndPadding.left, borderAndPadding.top);
contentPoint.x = mozilla::clamped(contentPoint.x, 0, contentRect.width);
contentPoint.y = mozilla::clamped(contentPoint.y, 0, contentRect.height);
if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
return maximum - (double(contentPoint.x) / double(contentRect.width)) * range;
double fraction;
if (IsHorizontal()) {
nscoord traversableDistance = rangeContentRect.width - thumbSize.width;
if (traversableDistance <= 0) {
return minimum;
}
nscoord posAtStart = rangeContentRect.x + thumbSize.width/2;
nscoord posAtEnd = posAtStart + traversableDistance;
nscoord posOfPoint = mozilla::clamped(point.x, posAtStart, posAtEnd);
fraction = (posOfPoint - posAtStart) / double(traversableDistance);
if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
fraction = 1.0 - fraction;
}
} else {
nscoord traversableDistance = rangeContentRect.height - thumbSize.height;
if (traversableDistance <= 0) {
return minimum;
}
nscoord posAtStart = rangeContentRect.y + thumbSize.height/2;
nscoord posAtEnd = posAtStart + traversableDistance;
nscoord posOfPoint = mozilla::clamped(point.y, posAtStart, posAtEnd);
// For a vertical range, the top (posAtStart) is the highest value, so we
// subtract the fraction from 1.0 to get that polarity correct.
fraction = 1.0 - (posOfPoint - posAtStart) / double(traversableDistance);
}
return minimum + (double(contentPoint.x) / double(contentRect.width)) * range;
MOZ_ASSERT(fraction >= 0.0 && fraction <= 1.0);
return minimum + fraction * range;
}
void
@ -388,25 +374,59 @@ nsRangeFrame::UpdateThumbPositionForValueChange()
if (!thumbFrame) {
return; // diplay:none?
}
// TODO in bug 842179 - factor out duplication here and in Reflow.
double fraction = GetValueAsFractionOfRange();
nsRect contentRect = GetContentRectRelativeToSelf();
nsMargin borderAndPadding = GetUsedBorderAndPadding();
nsSize thumbSize = thumbFrame->GetSize();
nsPoint newPosition(borderAndPadding.left, borderAndPadding.top);
if (IsHorizontal()) {
newPosition += nsPoint(NSToCoordRound(fraction * contentRect.width) -
thumbSize.width/2,
(contentRect.height - thumbSize.height)/2);
} else {
newPosition += nsPoint((contentRect.width - thumbSize.width)/2,
NSToCoordRound(fraction * contentRect.height) -
thumbSize.height/2);
}
thumbFrame->SetPosition(newPosition);
DoUpdateThumbPosition(thumbFrame, GetSize());
SchedulePaint();
}
void
nsRangeFrame::DoUpdateThumbPosition(nsIFrame* aThumbFrame,
const nsSize& aRangeSize)
{
MOZ_ASSERT(aThumbFrame);
// The idea here is that we want to position the thumb so that the center
// of the thumb is on an imaginary line drawn from the middle of one edge
// of the range frame's content box to the middle of the opposite edge of
// its content box (the opposite edges being the left/right edge if the
// range is horizontal, or else the top/bottom edges if the range is
// vertical). How far along this line the center of the thumb is placed
// depends on the value of the range.
nsMargin borderAndPadding = GetUsedBorderAndPadding();
nsPoint newPosition(borderAndPadding.left, borderAndPadding.top);
nsSize rangeContentBoxSize(aRangeSize);
rangeContentBoxSize.width -= borderAndPadding.LeftRight();
rangeContentBoxSize.height -= borderAndPadding.TopBottom();
nsSize thumbSize = aThumbFrame->GetSize();
double fraction = GetValueAsFractionOfRange();
MOZ_ASSERT(fraction >= 0.0 && fraction <= 1.0);
// We are called under Reflow, so we need to pass IsHorizontal a valid rect.
nsSize frameSizeOverride(aRangeSize.width, aRangeSize.height);
if (IsHorizontal(&frameSizeOverride)) {
if (thumbSize.width < rangeContentBoxSize.width) {
nscoord traversableDistance =
rangeContentBoxSize.width - thumbSize.width;
if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
newPosition.x += NSToCoordRound((1.0 - fraction) * traversableDistance);
} else {
newPosition.x += NSToCoordRound(fraction * traversableDistance);
}
newPosition.y += (rangeContentBoxSize.height - thumbSize.height)/2;
}
} else {
if (thumbSize.height < rangeContentBoxSize.height) {
nscoord traversableDistance =
rangeContentBoxSize.height - thumbSize.height;
newPosition.x += (rangeContentBoxSize.width - thumbSize.width)/2;
newPosition.y += NSToCoordRound((1.0 - fraction) * traversableDistance);
}
}
aThumbFrame->SetPosition(newPosition);
}
NS_IMETHODIMP
nsRangeFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,

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

@ -106,6 +106,9 @@ private:
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState);
void DoUpdateThumbPosition(nsIFrame* aThumbFrame,
const nsSize& aRangeSize);
/**
* Returns the input element's value as a fraction of the difference between
* the input's minimum and its maximum (i.e. returns 0.0 when the value is