Bug 422132 part.1 Store unused fractional scroll amount for later wheel events r=smaug

This commit is contained in:
Masayuki Nakano 2012-08-20 01:46:21 +09:00
parent 0ca4358d6c
commit 17d75f38f0
4 changed files with 121 additions and 56 deletions

View File

@ -2516,6 +2516,13 @@ nsEventStateManager::DispatchLegacyMouseScrollEvents(nsIFrame* aTargetFrame,
nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.width),
nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.height));
// XXX We don't deal with fractional amount in legacy event, though the
// default action handler (DoScrollText()) deals with it.
// If we implemented such strict computation, we would need additional
// accumulated delta values. It would made the code more complicated.
// And also it would computes different delta values from older version.
// It doesn't make sense to implement such code for legacy events and
// rare cases.
PRInt32 scrollDeltaX, scrollDeltaY, pixelDeltaX, pixelDeltaY;
switch (aEvent->deltaMode) {
case nsIDOMWheelEvent::DOM_DELTA_PAGE:
@ -2734,26 +2741,23 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
continue;
}
// Check if the scrollable view can be scrolled any further.
if (frameToScroll->GetLineScrollAmount().height) {
// For default action, we should climb up the tree if cannot scroll it
// by the event actually.
bool canScroll = CanScrollOn(frameToScroll,
aEvent->deltaX, aEvent->deltaY);
// Comboboxes need special care.
nsIComboboxControlFrame* comboBox = do_QueryFrame(scrollFrame);
if (comboBox) {
if (comboBox->IsDroppedDown()) {
// Don't propagate to parent when drop down menu is active.
return canScroll ? frameToScroll : nullptr;
}
// Always propagate when not dropped down (even if focused).
continue;
// For default action, we should climb up the tree if cannot scroll it
// by the event actually.
bool canScroll = CanScrollOn(frameToScroll,
aEvent->deltaX, aEvent->deltaY);
// Comboboxes need special care.
nsIComboboxControlFrame* comboBox = do_QueryFrame(scrollFrame);
if (comboBox) {
if (comboBox->IsDroppedDown()) {
// Don't propagate to parent when drop down menu is active.
return canScroll ? frameToScroll : nullptr;
}
// Always propagate when not dropped down (even if focused).
continue;
}
if (canScroll) {
return frameToScroll;
}
if (canScroll) {
return frameToScroll;
}
}
@ -2825,18 +2829,6 @@ nsEventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame,
return;
}
// If the wheel event is line scroll and the delta value is computed from
// system settings, allow to override the system speed.
bool allowScrollSpeedOverride =
(!aEvent->customizedByUserPrefs &&
aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE);
DeltaValues acceleratedDelta =
nsMouseWheelTransaction::AccelerateWheelDelta(aEvent,
allowScrollSpeedOverride);
bool isDeltaModePixel =
(aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL);
// Default action's actual scroll amount should be computed from device
// pixels.
nsPresContext* pc = scrollFrame->PresContext();
@ -2844,17 +2836,9 @@ nsEventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame,
nsIntSize scrollAmountInDevPixels(
pc->AppUnitsToDevPixels(scrollAmount.width),
pc->AppUnitsToDevPixels(scrollAmount.height));
nsIntPoint actualDevPixelScrollAmount(0, 0);
if (isDeltaModePixel) {
actualDevPixelScrollAmount.x = RoundDown(acceleratedDelta.deltaX);
actualDevPixelScrollAmount.y = RoundDown(acceleratedDelta.deltaY);
} else {
actualDevPixelScrollAmount.x =
RoundDown(scrollAmountInDevPixels.width * acceleratedDelta.deltaX);
actualDevPixelScrollAmount.y =
RoundDown(scrollAmountInDevPixels.height * acceleratedDelta.deltaY);
}
nsIntPoint actualDevPixelScrollAmount =
DeltaAccumulator::GetInstance()->
ComputeScrollAmountForDefaultAction(aEvent, scrollAmountInDevPixels);
nsIAtom* origin = nullptr;
switch (aEvent->deltaMode) {
@ -2888,6 +2872,9 @@ nsEventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame,
-devPixelPageSize.height;
}
bool isDeltaModePixel =
(aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL);
nsIScrollableFrame::ScrollMode mode;
switch (aEvent->scrollType) {
case widget::WheelEvent::SCROLL_DEFAULT:
@ -5087,13 +5074,6 @@ nsEventStateManager::DeltaAccumulator::InitLineOrPageDelta(
MOZ_ASSERT(aESM);
MOZ_ASSERT(aEvent);
if (!(aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL &&
aEvent->isPixelOnlyDevice) &&
!WheelPrefs::GetInstance()->NeedToComputeLineOrPageDelta(aEvent)) {
Reset();
return;
}
// Reset if the previous wheel event is too old.
if (!mLastTime.IsNull()) {
TimeDuration duration = TimeStamp::Now() - mLastTime;
@ -5102,7 +5082,7 @@ nsEventStateManager::DeltaAccumulator::InitLineOrPageDelta(
}
}
// If we have accumulated delta, we may need to reset it.
if (mHandlingDeltaMode != PR_UINT32_MAX) {
if (IsInTransaction()) {
// If wheel event type is changed, reset the values.
if (mHandlingDeltaMode != aEvent->deltaMode ||
mHandlingPixelOnlyDevice != aEvent->isPixelOnlyDevice) {
@ -5111,10 +5091,10 @@ nsEventStateManager::DeltaAccumulator::InitLineOrPageDelta(
// If the delta direction is changed, we should reset only the
// accumulated values.
if (mX && aEvent->deltaX && ((aEvent->deltaX > 0.0) != (mX > 0.0))) {
mX = 0.0;
mX = mPendingScrollAmountX = 0.0;
}
if (mY && aEvent->deltaY && ((aEvent->deltaY > 0.0) != (mY > 0.0))) {
mY = 0.0;
mY = mPendingScrollAmountY = 0.0;
}
}
}
@ -5122,6 +5102,28 @@ nsEventStateManager::DeltaAccumulator::InitLineOrPageDelta(
mHandlingDeltaMode = aEvent->deltaMode;
mHandlingPixelOnlyDevice = aEvent->isPixelOnlyDevice;
// If it's handling neither pixel scroll mode for pixel only device nor
// delta values multiplied by prefs, we must not modify lineOrPageDelta
// values.
if (!(mHandlingDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL &&
mHandlingPixelOnlyDevice) &&
!nsEventStateManager::WheelPrefs::GetInstance()->
NeedToComputeLineOrPageDelta(aEvent)) {
// Set the delta values to mX and mY. They would be used when above block
// resets mX/mY/mPendingScrollAmountX/mPendingScrollAmountY if the direction
// is changed.
// NOTE: We shouldn't accumulate the delta values, it might could cause
// overflow even though it's not a realistic situation.
if (aEvent->deltaX) {
mX = aEvent->deltaX;
}
if (aEvent->deltaY) {
mY = aEvent->deltaY;
}
mLastTime = TimeStamp::Now();
return;
}
mX += aEvent->deltaX;
mY += aEvent->deltaY;
@ -5162,10 +5164,45 @@ void
nsEventStateManager::DeltaAccumulator::Reset()
{
mX = mY = 0.0;
mPendingScrollAmountX = mPendingScrollAmountY = 0.0;
mHandlingDeltaMode = PR_UINT32_MAX;
mHandlingPixelOnlyDevice = false;
}
nsIntPoint
nsEventStateManager::DeltaAccumulator::ComputeScrollAmountForDefaultAction(
widget::WheelEvent* aEvent,
const nsIntSize& aScrollAmountInDevPixels)
{
MOZ_ASSERT(aEvent);
// If the wheel event is line scroll and the delta value is computed from
// system settings, allow to override the system speed.
bool allowScrollSpeedOverride =
(!aEvent->customizedByUserPrefs &&
aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE);
DeltaValues acceleratedDelta =
nsMouseWheelTransaction::AccelerateWheelDelta(aEvent,
allowScrollSpeedOverride);
nsIntPoint result(0, 0);
if (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) {
mPendingScrollAmountX += acceleratedDelta.deltaX;
mPendingScrollAmountY += acceleratedDelta.deltaY;
} else {
mPendingScrollAmountX +=
aScrollAmountInDevPixels.width * acceleratedDelta.deltaX;
mPendingScrollAmountY +=
aScrollAmountInDevPixels.height * acceleratedDelta.deltaY;
}
result.x = RoundDown(mPendingScrollAmountX);
result.y = RoundDown(mPendingScrollAmountY);
mPendingScrollAmountX -= result.x;
mPendingScrollAmountY -= result.y;
return result;
}
/******************************************************************/
/* nsEventStateManager::WheelPrefs */
/******************************************************************/

View File

@ -529,6 +529,8 @@ protected:
sInstance = nullptr;
}
bool IsInTransaction() { return mHandlingDeltaMode != PR_UINT32_MAX; }
/**
* InitLineOrPageDelta() stores pixel delta values of WheelEvents which are
* caused if it's needed. And if the accumulated delta becomes a
@ -539,19 +541,34 @@ protected:
mozilla::widget::WheelEvent* aEvent);
/**
* Reset() resets both delta values.
* Reset() resets all members.
*/
void Reset();
/**
* ComputeScrollAmountForDefaultAction() computes the default action's
* scroll amount in device pixels with mPendingScrollAmount*.
*/
nsIntPoint ComputeScrollAmountForDefaultAction(
mozilla::widget::WheelEvent* aEvent,
const nsIntSize& aScrollAmountInDevPixels);
private:
DeltaAccumulator() :
mX(0.0), mY(0.0), mHandlingDeltaMode(PR_UINT32_MAX),
mHandlingPixelOnlyDevice(false)
mX(0.0), mY(0.0), mPendingScrollAmountX(0.0), mPendingScrollAmountY(0.0),
mHandlingDeltaMode(PR_UINT32_MAX), mHandlingPixelOnlyDevice(false)
{
}
double mX;
double mY;
// When default action of a wheel event is scroll but some delta values
// are ignored because the computed amount values are not integer, the
// fractional values are saved by these members.
double mPendingScrollAmountX;
double mPendingScrollAmountY;
TimeStamp mLastTime;
PRUint32 mHandlingDeltaMode;

View File

@ -2548,9 +2548,16 @@ nsGfxScrollFrameInner::GetLineScrollAmount() const
nsLayoutUtils::GetFontMetricsForFrame(mOuter, getter_AddRefs(fm),
nsLayoutUtils::FontSizeInflationFor(mOuter));
NS_ASSERTION(fm, "FontMetrics is null, assuming fontHeight == 1 appunit");
nscoord fontHeight = 1;
static nscoord sMinLineScrollAmountInPixels = -1;
if (sMinLineScrollAmountInPixels < 0) {
Preferences::AddIntVarCache(&sMinLineScrollAmountInPixels,
"mousewheel.min_line_scroll_amount", 1);
}
PRUint32 appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
nscoord fontHeight =
NS_MAX(1, sMinLineScrollAmountInPixels) * appUnitsPerDevPixel;
if (fm) {
fontHeight = fm->MaxHeight();
fontHeight = NS_MAX(fm->MaxHeight(), fontHeight);
}
return nsSize(fontHeight, fontHeight);

View File

@ -1417,6 +1417,10 @@ pref("mousewheel.with_win.delta_multiplier_x", 100);
pref("mousewheel.with_win.delta_multiplier_y", 100);
pref("mousewheel.with_win.delta_multiplier_z", 100);
// If line-height is lower than this value (in device pixels), 1 line scroll
// scrolls this height.
pref("mousewheel.min_line_scroll_amount", 5);
// These define the smooth scroll behavior (min ms, max ms) for different triggers
// Some triggers:
// mouseWheel: Discrete mouse wheel events, Synaptics touchpads on windows (generate wheel events)