mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Add wheel transaction support to APZ. (bug 1142866 part 1, r=kats)
This commit is contained in:
parent
275609c516
commit
a0a7580fcf
@ -682,7 +682,11 @@ public:
|
||||
, mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
|
||||
aSpringConstant, aDampingRatio)
|
||||
, mSource(aSource)
|
||||
, mAllowOverscroll(true)
|
||||
{
|
||||
if (mSource == ScrollSource::Wheel) {
|
||||
mAllowOverscroll = mApzc.AllowScrollHandoffInWheelTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -739,8 +743,7 @@ public:
|
||||
// This can happen if either the layout.css.scroll-behavior.damping-ratio
|
||||
// preference is set to less than 1 (underdamped) or if a smooth scroll
|
||||
// inherits velocity from a fling gesture.
|
||||
if (!IsZero(overscroll)) {
|
||||
|
||||
if (!IsZero(overscroll) && mAllowOverscroll) {
|
||||
// Hand off a fling with the remaining momentum to the next APZC in the
|
||||
// overscroll handoff chain.
|
||||
|
||||
@ -778,6 +781,7 @@ private:
|
||||
AsyncPanZoomController& mApzc;
|
||||
AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
|
||||
ScrollSource mSource;
|
||||
bool mAllowOverscroll;
|
||||
};
|
||||
|
||||
void
|
||||
@ -1418,21 +1422,59 @@ AsyncPanZoomController::ConvertToGecko(const ParentLayerPoint& aPoint, CSSPoint*
|
||||
return false;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent)
|
||||
void
|
||||
AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent,
|
||||
double& aOutDeltaX,
|
||||
double& aOutDeltaY) const
|
||||
{
|
||||
double deltaX = aEvent.mDeltaX;
|
||||
double deltaY = aEvent.mDeltaY;
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
|
||||
aOutDeltaX = aEvent.mDeltaX;
|
||||
aOutDeltaY = aEvent.mDeltaY;
|
||||
switch (aEvent.mDeltaType) {
|
||||
case ScrollWheelInput::SCROLLDELTA_LINE: {
|
||||
LayoutDeviceIntSize scrollAmount = mFrameMetrics.GetLineScrollAmount();
|
||||
deltaX *= scrollAmount.width;
|
||||
deltaY *= scrollAmount.height;
|
||||
aOutDeltaX *= scrollAmount.width;
|
||||
aOutDeltaY *= scrollAmount.height;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected scroll delta type");
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
}
|
||||
|
||||
// Return whether or not the underlying layer can be scrolled on either axis.
|
||||
bool
|
||||
AsyncPanZoomController::CanScroll(const ScrollWheelInput& aEvent) const
|
||||
{
|
||||
double deltaX, deltaY;
|
||||
GetScrollWheelDelta(aEvent, deltaX, deltaY);
|
||||
|
||||
if (!deltaX && !deltaY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CanScroll(deltaX, deltaY);
|
||||
}
|
||||
|
||||
bool
|
||||
AsyncPanZoomController::CanScroll(double aDeltaX, double aDeltaY) const
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
return mX.CanScroll(aDeltaX) || mY.CanScroll(aDeltaY);
|
||||
}
|
||||
|
||||
bool
|
||||
AsyncPanZoomController::AllowScrollHandoffInWheelTransaction() const
|
||||
{
|
||||
WheelBlockState* block = mInputQueue->CurrentWheelBlock();
|
||||
return block->AllowScrollHandoff();
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent)
|
||||
{
|
||||
double deltaX, deltaY;
|
||||
GetScrollWheelDelta(aEvent, deltaX, deltaY);
|
||||
|
||||
switch (aEvent.mScrollMode) {
|
||||
case ScrollWheelInput::SCROLLMODE_INSTANT: {
|
||||
@ -1916,6 +1958,13 @@ bool AsyncPanZoomController::AttemptScroll(const ParentLayerPoint& aStartPoint,
|
||||
return true;
|
||||
}
|
||||
|
||||
// If in a wheel transaction that has not ended, we drop overscroll.
|
||||
if (aOverscrollHandoffState.mScrollSource == ScrollSource::Wheel &&
|
||||
!AllowScrollHandoffInWheelTransaction())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If there is overscroll, first try to hand it off to an APZC later
|
||||
// in the handoff chain to consume (either as a normal scroll or as
|
||||
// overscroll).
|
||||
|
@ -361,6 +361,14 @@ public:
|
||||
ParentLayerPoint ToParentLayerCoordinates(const ScreenPoint& aVector,
|
||||
const ScreenPoint& aAnchor) const;
|
||||
|
||||
// Return whether or not a wheel event will be able to scroll in either
|
||||
// direction.
|
||||
bool CanScroll(const ScrollWheelInput& aEvent) const;
|
||||
|
||||
// Return whether or not a scroll delta will be able to scroll in either
|
||||
// direction.
|
||||
bool CanScroll(double aDeltaX, double aDeltaY) const;
|
||||
|
||||
protected:
|
||||
// Protected destructor, to discourage deletion outside of Release():
|
||||
~AsyncPanZoomController();
|
||||
@ -423,6 +431,10 @@ protected:
|
||||
*/
|
||||
nsEventStatus OnScrollWheel(const ScrollWheelInput& aEvent);
|
||||
|
||||
void GetScrollWheelDelta(const ScrollWheelInput& aEvent,
|
||||
double& aOutDeltaX,
|
||||
double& aOutDeltaY) const;
|
||||
|
||||
/**
|
||||
* Helper methods for long press gestures.
|
||||
*/
|
||||
@ -849,6 +861,9 @@ private:
|
||||
|
||||
void StartSmoothScroll(ScrollSource aSource);
|
||||
|
||||
// Returns whether overscroll is allowed during a wheel event.
|
||||
bool AllowScrollHandoffInWheelTransaction() const;
|
||||
|
||||
/* ===================================================================
|
||||
* The functions and members in this section are used to make ancestor chains
|
||||
* out of APZC instances. These chains can only be walked or manipulated
|
||||
|
@ -394,6 +394,16 @@ bool Axis::CanScroll() const {
|
||||
return GetPageLength() - GetCompositionLength() > COORDINATE_EPSILON;
|
||||
}
|
||||
|
||||
bool Axis::CanScroll(double aDelta) const
|
||||
{
|
||||
if (!CanScroll() || mAxisLocked) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ParentLayerCoord delta = aDelta;
|
||||
return DisplacementWillOverscrollAmount(delta) != delta;
|
||||
}
|
||||
|
||||
bool Axis::CanScrollNow() const {
|
||||
return !mAxisLocked && CanScroll();
|
||||
}
|
||||
|
@ -147,6 +147,11 @@ public:
|
||||
*/
|
||||
bool CanScroll() const;
|
||||
|
||||
/**
|
||||
* Returns whether this axis can scroll any more in a particular direction.
|
||||
*/
|
||||
bool CanScroll(double aDelta) const;
|
||||
|
||||
/**
|
||||
* Returns true if the page has room to be scrolled along this axis
|
||||
* and this axis is not scroll-locked.
|
||||
|
@ -49,11 +49,17 @@ InputBlockState::SetConfirmedTargetApzc(const nsRefPtr<AsyncPanZoomController>&
|
||||
printf_stderr("%p replacing unconfirmed target %p with real target %p\n",
|
||||
this, mTargetApzc.get(), aTargetApzc.get());
|
||||
|
||||
UpdateTargetApzc(aTargetApzc);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
InputBlockState::UpdateTargetApzc(const nsRefPtr<AsyncPanZoomController>& aTargetApzc)
|
||||
{
|
||||
// note that aTargetApzc MAY be null here.
|
||||
mTargetApzc = aTargetApzc;
|
||||
mTransformToApzc = aTargetApzc ? aTargetApzc->GetTransformToThis() : gfx::Matrix4x4();
|
||||
mOverscrollHandoffChain = (mTargetApzc ? mTargetApzc->BuildOverscrollHandoffChain() : nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
const nsRefPtr<AsyncPanZoomController>&
|
||||
@ -135,25 +141,58 @@ CancelableBlockState::IsReadyForHandling() const
|
||||
}
|
||||
|
||||
void
|
||||
CancelableBlockState::DispatchImmediate(const InputData& aEvent) const
|
||||
CancelableBlockState::DispatchImmediate(const InputData& aEvent)
|
||||
{
|
||||
MOZ_ASSERT(!HasEvents());
|
||||
MOZ_ASSERT(GetTargetApzc());
|
||||
GetTargetApzc()->HandleInputEvent(aEvent, mTransformToApzc);
|
||||
}
|
||||
|
||||
// This is used to track the current wheel transaction.
|
||||
static uint64_t sLastWheelBlockId = InputBlockState::NO_BLOCK_ID;
|
||||
|
||||
WheelBlockState::WheelBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
|
||||
bool aTargetConfirmed)
|
||||
bool aTargetConfirmed,
|
||||
const ScrollWheelInput& aInitialEvent)
|
||||
: CancelableBlockState(aTargetApzc, aTargetConfirmed)
|
||||
, mTransactionEnded(false)
|
||||
{
|
||||
sLastWheelBlockId = GetBlockId();
|
||||
Update(aInitialEvent);
|
||||
|
||||
if (aTargetConfirmed) {
|
||||
// Find the nearest APZC in the overscroll handoff chain that is scrollable.
|
||||
// If we get a content confirmation later that the apzc is different, then
|
||||
// content should have found a scrollable apzc, so we don't need to handle
|
||||
// that case.
|
||||
nsRefPtr<AsyncPanZoomController> apzc =
|
||||
mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent);
|
||||
if (apzc && apzc != GetTargetApzc()) {
|
||||
UpdateTargetApzc(apzc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WheelBlockState::Update(const ScrollWheelInput& aEvent)
|
||||
{
|
||||
mLastEventTime = aEvent.mTimeStamp;
|
||||
}
|
||||
|
||||
void
|
||||
WheelBlockState::AddEvent(const ScrollWheelInput& aEvent)
|
||||
{
|
||||
Update(aEvent);
|
||||
mEvents.AppendElement(aEvent);
|
||||
}
|
||||
|
||||
void
|
||||
WheelBlockState::DispatchImmediate(const InputData& aEvent)
|
||||
{
|
||||
Update(aEvent.AsScrollWheelInput());
|
||||
CancelableBlockState::DispatchImmediate(aEvent);
|
||||
}
|
||||
|
||||
bool
|
||||
WheelBlockState::IsReadyForHandling() const
|
||||
{
|
||||
@ -190,7 +229,7 @@ WheelBlockState::HandleEvents()
|
||||
bool
|
||||
WheelBlockState::MustStayActive()
|
||||
{
|
||||
return false;
|
||||
return !mTransactionEnded;
|
||||
}
|
||||
|
||||
const char*
|
||||
@ -199,6 +238,54 @@ WheelBlockState::Type()
|
||||
return "scroll wheel";
|
||||
}
|
||||
|
||||
bool
|
||||
WheelBlockState::ShouldAcceptNewEvent(const ScrollWheelInput& aEvent) const
|
||||
{
|
||||
if (!InTransaction()) {
|
||||
// If we're not in a transaction, start a new one.
|
||||
return false;
|
||||
}
|
||||
nsRefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
|
||||
if (!apzc || apzc->IsDestroyed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// End the transaction if the event occurred > 1.5s after the most recently
|
||||
// seen wheel event.
|
||||
TimeDuration duration = aEvent.mTimeStamp - mLastEventTime;
|
||||
if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelTransactionTimeoutMs()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Accept the event, even if we can't scroll anymore.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WheelBlockState::InTransaction() const
|
||||
{
|
||||
// We consider a wheel block to be in a transaction if it has a confirmed
|
||||
// target and is the most recent wheel input block to be created.
|
||||
if (GetBlockId() != sLastWheelBlockId) {
|
||||
return false;
|
||||
}
|
||||
return !mTransactionEnded;
|
||||
}
|
||||
|
||||
bool
|
||||
WheelBlockState::AllowScrollHandoff() const
|
||||
{
|
||||
// If we're in a wheel transaction, we do not allow overscroll handoff until
|
||||
// a new event ends the wheel transaction.
|
||||
return !IsTargetConfirmed() || !InTransaction();
|
||||
}
|
||||
|
||||
void
|
||||
WheelBlockState::EndTransaction()
|
||||
{
|
||||
mTransactionEnded = true;
|
||||
}
|
||||
|
||||
TouchBlockState::TouchBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
|
||||
bool aTargetConfirmed)
|
||||
: CancelableBlockState(aTargetApzc, aTargetConfirmed)
|
||||
|
@ -45,12 +45,16 @@ public:
|
||||
|
||||
bool IsTargetConfirmed() const;
|
||||
|
||||
protected:
|
||||
void UpdateTargetApzc(const nsRefPtr<AsyncPanZoomController>& aTargetApzc);
|
||||
|
||||
private:
|
||||
nsRefPtr<AsyncPanZoomController> mTargetApzc;
|
||||
nsRefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
|
||||
bool mTargetConfirmed;
|
||||
const uint64_t mBlockId;
|
||||
protected:
|
||||
nsRefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
|
||||
|
||||
// Used to transform events from global screen space to |mTargetApzc|'s
|
||||
// screen space. It's cached at the beginning of the input block so that
|
||||
// all events in the block are in the same coordinate space.
|
||||
@ -106,7 +110,7 @@ public:
|
||||
* This input block must not have pending events, and its apzc must not be
|
||||
* nullptr.
|
||||
*/
|
||||
void DispatchImmediate(const InputData& aEvent) const;
|
||||
virtual void DispatchImmediate(const InputData& aEvent);
|
||||
|
||||
/**
|
||||
* @return true iff this block has received all the information needed
|
||||
@ -154,7 +158,8 @@ class WheelBlockState : public CancelableBlockState
|
||||
{
|
||||
public:
|
||||
WheelBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
|
||||
bool aTargetConfirmed);
|
||||
bool aTargetConfirmed,
|
||||
const ScrollWheelInput& aEvent);
|
||||
|
||||
bool IsReadyForHandling() const override;
|
||||
bool HasEvents() const override;
|
||||
@ -162,6 +167,7 @@ public:
|
||||
void HandleEvents() override;
|
||||
bool MustStayActive() override;
|
||||
const char* Type() override;
|
||||
void DispatchImmediate(const InputData& aEvent) override;
|
||||
|
||||
void AddEvent(const ScrollWheelInput& aEvent);
|
||||
|
||||
@ -169,8 +175,46 @@ public:
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the block should accept new events. If the APZC
|
||||
* has been destroyed, or the block is not part of a wheel transaction, then
|
||||
* this will return false.
|
||||
*
|
||||
* @return True if the event should be accepted, false otherwise.
|
||||
*/
|
||||
bool ShouldAcceptNewEvent(const ScrollWheelInput& aEvent) const;
|
||||
|
||||
/**
|
||||
* Returns whether or not the block is participating in a wheel transaction.
|
||||
* This means that the block is the most recent input block to be created,
|
||||
* and no events have occurred that would require scrolling a different
|
||||
* frame.
|
||||
*
|
||||
* @return True if in a transaction, false otherwise.
|
||||
*/
|
||||
bool InTransaction() const;
|
||||
|
||||
/**
|
||||
* Mark the block as no longer participating in a wheel transaction. This
|
||||
* will force future wheel events to begin a new input block.
|
||||
*/
|
||||
void EndTransaction();
|
||||
|
||||
/**
|
||||
* @return Whether or not overscrolling is prevented for this wheel block.
|
||||
*/
|
||||
bool AllowScrollHandoff() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Update the wheel transaction state for a new event.
|
||||
*/
|
||||
void Update(const ScrollWheelInput& aEvent);
|
||||
|
||||
private:
|
||||
nsTArray<ScrollWheelInput> mEvents;
|
||||
TimeStamp mLastEventTime;
|
||||
bool mTransactionEnded;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -160,14 +160,18 @@ InputQueue::ReceiveScrollWheelInput(const nsRefPtr<AsyncPanZoomController>& aTar
|
||||
if (!mInputBlockQueue.IsEmpty()) {
|
||||
block = mInputBlockQueue.LastElement()->AsWheelBlock();
|
||||
|
||||
// If the block's APZC has been destroyed, request a new block.
|
||||
if (block && block->GetTargetApzc()->IsDestroyed()) {
|
||||
// If the block is not accepting new events we'll create a new input block
|
||||
// (and therefore a new wheel transaction).
|
||||
if (block && !block->ShouldAcceptNewEvent(aEvent)) {
|
||||
block = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a block, it should be in a wheel transaction.
|
||||
MOZ_ASSERT(!block || block->InTransaction());
|
||||
|
||||
if (!block) {
|
||||
block = new WheelBlockState(aTarget, aTargetConfirmed);
|
||||
block = new WheelBlockState(aTarget, aTargetConfirmed, aEvent);
|
||||
INPQ_LOG("started new scroll wheel block %p for target %p\n", block, aTarget.get());
|
||||
|
||||
SweepDepletedBlocks();
|
||||
@ -286,11 +290,32 @@ InputQueue::CurrentBlock() const
|
||||
TouchBlockState*
|
||||
InputQueue::CurrentTouchBlock() const
|
||||
{
|
||||
TouchBlockState *block = CurrentBlock()->AsTouchBlock();
|
||||
TouchBlockState* block = CurrentBlock()->AsTouchBlock();
|
||||
MOZ_ASSERT(block);
|
||||
return block;
|
||||
}
|
||||
|
||||
WheelBlockState*
|
||||
InputQueue::CurrentWheelBlock() const
|
||||
{
|
||||
WheelBlockState* block = CurrentBlock()->AsWheelBlock();
|
||||
MOZ_ASSERT(block);
|
||||
return block;
|
||||
}
|
||||
|
||||
WheelBlockState*
|
||||
InputQueue::GetCurrentWheelTransaction() const
|
||||
{
|
||||
if (mInputBlockQueue.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
WheelBlockState* block = CurrentBlock()->AsWheelBlock();
|
||||
if (!block || !block->InTransaction()) {
|
||||
return nullptr;
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
bool
|
||||
InputQueue::HasReadyTouchBlock() const
|
||||
{
|
||||
@ -426,7 +451,7 @@ InputQueue::ProcessInputBlocks() {
|
||||
|
||||
// If we get here, we know there are more touch blocks in the queue after
|
||||
// |curBlock|, so we can remove |curBlock| and try to process the next one.
|
||||
INPQ_LOG("discarding depleted touch block %p\n", curBlock);
|
||||
INPQ_LOG("discarding processed %s block %p\n", curBlock->Type(), curBlock);
|
||||
mInputBlockQueue.RemoveElementAt(0);
|
||||
} while (!mInputBlockQueue.IsEmpty());
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ class AsyncPanZoomController;
|
||||
class OverscrollHandoffChain;
|
||||
class CancelableBlockState;
|
||||
class TouchBlockState;
|
||||
class WheelBlockState;
|
||||
|
||||
/**
|
||||
* This class stores incoming input events, separated into "input blocks", until
|
||||
@ -80,15 +81,25 @@ public:
|
||||
*/
|
||||
CancelableBlockState* CurrentBlock() const;
|
||||
/**
|
||||
* Returns the current pending input block as a touch block. It must only
|
||||
* Returns the current pending input block as a touch block. It must only be
|
||||
* called if the current pending block is a touch block.
|
||||
*/
|
||||
TouchBlockState* CurrentTouchBlock() const;
|
||||
/**
|
||||
* Returns the current pending input block as a wheel block. It must only be
|
||||
* called if the current pending block is a wheel block.
|
||||
*/
|
||||
WheelBlockState* CurrentWheelBlock() const;
|
||||
/**
|
||||
* Returns true iff the pending block at the head of the queue is ready for
|
||||
* handling.
|
||||
*/
|
||||
bool HasReadyTouchBlock() const;
|
||||
/**
|
||||
* If there is a wheel transaction, returns the WheelBlockState representing
|
||||
* the transaction. Otherwise, returns null.
|
||||
*/
|
||||
WheelBlockState* GetCurrentWheelTransaction() const;
|
||||
|
||||
private:
|
||||
~InputQueue();
|
||||
|
@ -63,7 +63,6 @@ OverscrollHandoffChain::IndexOf(const AsyncPanZoomController* aApzc) const
|
||||
void
|
||||
OverscrollHandoffChain::ForEachApzc(APZCMethod aMethod) const
|
||||
{
|
||||
MOZ_ASSERT(Length() > 0);
|
||||
for (uint32_t i = 0; i < Length(); ++i) {
|
||||
(mChain[i]->*aMethod)();
|
||||
}
|
||||
@ -156,6 +155,16 @@ OverscrollHandoffChain::HasFastMovingApzc() const
|
||||
return AnyApzc(&AsyncPanZoomController::IsMovingFast);
|
||||
}
|
||||
|
||||
nsRefPtr<AsyncPanZoomController>
|
||||
OverscrollHandoffChain::FindFirstScrollable(const ScrollWheelInput& aInput) const
|
||||
{
|
||||
for (size_t i = 0; i < Length(); i++) {
|
||||
if (mChain[i]->CanScroll(aInput)) {
|
||||
return mChain[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
@ -113,6 +113,8 @@ public:
|
||||
// Determine whether any APZC along this handoff chain is moving fast.
|
||||
bool HasFastMovingApzc() const;
|
||||
|
||||
nsRefPtr<AsyncPanZoomController> FindFirstScrollable(const ScrollWheelInput& aInput) const;
|
||||
|
||||
private:
|
||||
std::vector<nsRefPtr<AsyncPanZoomController>> mChain;
|
||||
|
||||
|
@ -337,6 +337,8 @@ private:
|
||||
// This affects whether events will be routed through APZ or not.
|
||||
DECL_GFX_PREF(Once, "mousewheel.system_scroll_override_on_root_content.enabled",
|
||||
MouseWheelHasScrollDeltaOverride, bool, false);
|
||||
DECL_GFX_PREF(Live, "mousewheel.transaction.timeout", MouseWheelTransactionTimeoutMs, int32_t, (int32_t)1500);
|
||||
|
||||
|
||||
DECL_GFX_PREF(Live, "nglayout.debug.widget_update_flashing", WidgetUpdateFlashing, bool, false);
|
||||
DECL_GFX_PREF(Live, "ui.click_hold_context_menus.delay", UiClickHoldContextMenusDelay, int32_t, 500);
|
||||
|
Loading…
Reference in New Issue
Block a user