You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
473 lines
14 KiB
473 lines
14 KiB
local abs = math.abs;
|
|
local max = math.max;
|
|
local min = math.min;
|
|
local floor = math.floor;
|
|
|
|
local IsShiftKeyDown = IsShiftKeyDown; --For acceleration
|
|
|
|
local _, SCREEN_HEIGHT = GetPhysicalScreenSize();
|
|
local PIXEL_RATIO = 768 / SCREEN_HEIGHT;
|
|
|
|
local function Driver_OnHide(self)
|
|
self.isScrolling = nil;
|
|
end
|
|
|
|
local function Driver_OnUpdate(self, elapsed)
|
|
local value = self.bar:GetValue();
|
|
local step = max(abs(value - self.toValue)*(self.speed)*(elapsed*60), self.minOffset);
|
|
local remainedStep;
|
|
if ( self.delta == 1 ) then
|
|
--Up
|
|
remainedStep = min(self.toValue - value, 0);
|
|
if - remainedStep <= (self.minOffset) then
|
|
self.isScrolling = nil;
|
|
self:Hide();
|
|
self.bar:SetValue(self.toValue);
|
|
if self.onScrollFinishedFunc then
|
|
self.onScrollFinishedFunc();
|
|
end
|
|
self.toValue = nil;
|
|
|
|
if self.onValueChangedFunc then
|
|
self.onValueChangedFunc(self.bar:GetValue());
|
|
end
|
|
return
|
|
else
|
|
self.bar:SetValue(max(0, value - step));
|
|
end
|
|
else
|
|
remainedStep = max(self.toValue - value, 0);
|
|
if remainedStep <= (self.minOffset) then
|
|
self.isScrolling = nil;
|
|
self:Hide();
|
|
self.bar:SetValue(self.toValue);
|
|
if self.onScrollFinishedFunc then
|
|
self.onScrollFinishedFunc();
|
|
end
|
|
self.toValue = nil;
|
|
|
|
if self.onValueChangedFunc then
|
|
self.onValueChangedFunc(self.bar:GetValue());
|
|
end
|
|
return
|
|
else
|
|
self.bar:SetValue(min(self.range, value + step));
|
|
end
|
|
end
|
|
|
|
if self.t and self.onValueChangedFunc then
|
|
self.t = self.t + elapsed;
|
|
if self.t > self.updateIntervel then
|
|
self.t = 0;
|
|
self.onValueChangedFunc(self.bar:GetValue());
|
|
end
|
|
end
|
|
end
|
|
|
|
local function Driver_RunValueChangedFunc(self, elapsed)
|
|
if self.t and self.onValueChangedFunc then
|
|
self.t = self.t + elapsed;
|
|
if self.t > self.updateIntervel then
|
|
self.t = 0;
|
|
self.onValueChangedFunc(self.bar:GetValue());
|
|
end
|
|
end
|
|
end
|
|
|
|
local function ScrollFrame_OnShow(self)
|
|
local uiScale = self:GetEffectiveScale();
|
|
if not uiScale or uiScale == 0 then
|
|
uiScale = 0.6667;
|
|
end
|
|
self.Driver.minOffset = PIXEL_RATIO / uiScale;
|
|
end
|
|
|
|
local function ScrollFrame_OnMouseWheel(self, delta)
|
|
if ( not self.ScrollBar:IsVisible() ) then
|
|
if self.parentScrollFunc then
|
|
self.parentScrollFunc(delta);
|
|
else
|
|
return;
|
|
end
|
|
end
|
|
|
|
if self:IsScrollLocked() then
|
|
return
|
|
end
|
|
|
|
local d = self.Driver;
|
|
d.delta = delta;
|
|
|
|
local current = self.ScrollBar:GetValue();
|
|
if (current <= 0.1 and delta > 0) then
|
|
--already at top but still scroll up
|
|
if self.AnimAlertAtTop then
|
|
self.AnimAlertAtTop:Play();
|
|
end
|
|
return
|
|
elseif (current >= d.range - 0.1 and delta < 0 ) then
|
|
--already at bottom but still scroll down
|
|
if self.AnimAlertAtBottom then
|
|
self.AnimAlertAtBottom:Play();
|
|
end
|
|
return
|
|
else
|
|
d.isScrolling = true;
|
|
if d.onScrollStartedFunc then
|
|
d.onScrollStartedFunc();
|
|
end
|
|
d:SetScript("OnUpdate", Driver_OnUpdate);
|
|
d:Show();
|
|
end
|
|
|
|
local deltaMultiplier = d.deltaMultiplier or 1;
|
|
if IsShiftKeyDown() then
|
|
deltaMultiplier = 2 * deltaMultiplier;
|
|
end
|
|
|
|
if not d.toValue then
|
|
d.toValue = current;
|
|
end
|
|
local toValue = floor( (100 * min(max(0, d.toValue - delta * deltaMultiplier * d.buttonHeight), d.range) + 0.5)/100 );
|
|
d.toValue = toValue;
|
|
|
|
if d.positionFunc then
|
|
local isTop = toValue <= 0.1;
|
|
local isBottom = toValue >= d.range - 1;
|
|
d.positionFunc(toValue, isTop, isBottom);
|
|
end
|
|
|
|
d.t = 1;
|
|
end
|
|
|
|
|
|
local GetCursorDelta = GetCursorDelta;
|
|
|
|
local PressAndMoveDriver = CreateFrame("Frame");
|
|
|
|
local function PressAndMoveDriver_OnUpdate(self, elapsed)
|
|
self.deltaX, self.deltaY = GetCursorDelta();
|
|
if self.deltaY ~= 0 then
|
|
self.scrollFrame:ScrollBy(self.deltaY * self.ratio);
|
|
end
|
|
if self.t and self.updateFunc then
|
|
self.t = self.t + elapsed;
|
|
if self.t > 0.2 then
|
|
self.t = 0;
|
|
self.updateFunc(self.scrollFrame:GetOffset());
|
|
end
|
|
end
|
|
end
|
|
|
|
local function ScrollFrame_OnDragStart(self)
|
|
self:StopScrolling();
|
|
local scale = self:GetEffectiveScale();
|
|
PressAndMoveDriver.ratio = 1/scale;
|
|
PressAndMoveDriver.scrollFrame = self;
|
|
if self.Driver.onValueChangedFunc then
|
|
PressAndMoveDriver.updateFunc = self.Driver.onValueChangedFunc
|
|
PressAndMoveDriver.t = 0;
|
|
else
|
|
PressAndMoveDriver.updateFunc = nil;
|
|
PressAndMoveDriver.t = nil;
|
|
end
|
|
PressAndMoveDriver:SetScript("OnUpdate", PressAndMoveDriver_OnUpdate);
|
|
end
|
|
|
|
local function ScrollFrame_OnDragStop(self)
|
|
PressAndMoveDriver:SetScript("OnUpdate", nil);
|
|
PressAndMoveDriver.ratio = nil;
|
|
PressAndMoveDriver.scrollFrame = nil;
|
|
if self.Driver.onValueChangedFunc then
|
|
self.Driver.onValueChangedFunc(self:GetOffset());
|
|
end
|
|
end
|
|
|
|
local function ScrollFrame_OnHide(self)
|
|
ScrollFrame_OnDragStop(self);
|
|
self:StopScrolling();
|
|
end
|
|
|
|
|
|
local ScrollFrameMixin = {};
|
|
|
|
|
|
function ScrollFrameMixin:StopScrolling()
|
|
self.Driver:Hide();
|
|
end
|
|
|
|
function ScrollFrameMixin:SetScrollRange(range)
|
|
self.Driver.range = range;
|
|
self.ScrollBar:SetMinMaxValues(0, range);
|
|
self.ScrollBar:SetShown(range > 0.5);
|
|
end
|
|
|
|
function ScrollFrameMixin:SetDeltaMultiplier(multiplier)
|
|
--Travel distance per scroll
|
|
self.Driver.deltaMultiplier = multiplier;
|
|
end
|
|
|
|
function ScrollFrameMixin:SetStepSize(buttonHeight)
|
|
--Travel distance per scroll
|
|
self.Driver.buttonHeight = buttonHeight;
|
|
end
|
|
|
|
function ScrollFrameMixin:SetSpeedMultiplier(multiplier)
|
|
--How fast it scrolls
|
|
self.Driver.speed = multiplier;
|
|
end
|
|
|
|
function ScrollFrameMixin:SetOnValueChangedFunc(onValueChangedFunc)
|
|
self.Driver.onValueChangedFunc = onValueChangedFunc;
|
|
end
|
|
|
|
function ScrollFrameMixin:SetUpdateInterval(interval)
|
|
self.Driver.updateIntervel = interval or 0.2;
|
|
end
|
|
|
|
function ScrollFrameMixin:SetOnScrollStartedFunc(onScrollStartedFunc)
|
|
self.Driver.onScrollStartedFunc = onScrollStartedFunc;
|
|
end
|
|
|
|
function ScrollFrameMixin:SetOnScrollFinishedFunc(onScrollFinishedFunc)
|
|
self.Driver.onScrollFinishedFunc = onScrollFinishedFunc;
|
|
end
|
|
|
|
function ScrollFrameMixin:SetOnResetFunc(onResetFunc)
|
|
self.Driver.onResetFunc = onResetFunc;
|
|
end
|
|
|
|
function ScrollFrameMixin:Reset()
|
|
self.Driver:Hide();
|
|
if self.Driver.onResetFunc then
|
|
self.Driver.onResetFunc();
|
|
else
|
|
if self.ScrollBar:GetValue() == 0 then
|
|
if self.Driver.onValueChangedFunc then
|
|
self.Driver.onValueChangedFunc(0);
|
|
end
|
|
else
|
|
self.ScrollBar:SetValue(0);
|
|
end
|
|
end
|
|
end
|
|
|
|
function ScrollFrameMixin:SetOffset(value)
|
|
self.Driver:Hide();
|
|
self.Driver.toValue = value;
|
|
self.ScrollBar:SetValue(value);
|
|
end
|
|
|
|
function ScrollFrameMixin:GetOffset()
|
|
return self.ScrollBar:GetValue();
|
|
end
|
|
|
|
function ScrollFrameMixin:ScrollToOffset(offset)
|
|
--Note: Might cause performance issue if Δoffset is too large since button data update more frequently
|
|
|
|
local d = self.Driver;
|
|
local current = self.ScrollBar:GetValue();
|
|
local delta;
|
|
if offset > current then
|
|
delta = -1;
|
|
elseif offset < current then
|
|
delta = 1;
|
|
else
|
|
return
|
|
end
|
|
d.delta = delta;
|
|
|
|
local toValue = floor( (100 * min(max(0, offset), d.range) + 0.5)/100 );
|
|
d.toValue = toValue;
|
|
|
|
if d.positionFunc then
|
|
local isTop = toValue <= 0.1;
|
|
local isBottom = toValue >= d.range - 1;
|
|
d.positionFunc(toValue, isTop, isBottom);
|
|
end
|
|
|
|
if d.onScrollStartedFunc then
|
|
d.onScrollStartedFunc();
|
|
end
|
|
|
|
d.isScrolling = true;
|
|
d.t = nil;
|
|
d:SetScript("OnUpdate", Driver_OnUpdate);
|
|
d:Show();
|
|
end
|
|
|
|
function ScrollFrameMixin:ScrollBy(value)
|
|
local current = self.ScrollBar:GetValue();
|
|
local offset = current + value;
|
|
if offset ~= current then
|
|
self:SetOffset(offset);
|
|
end
|
|
end
|
|
|
|
function ScrollFrameMixin:SmoothScrollByValue(value)
|
|
local current = self.ScrollBar:GetValue();
|
|
local offset = current + value;
|
|
if offset ~= current then
|
|
self:ScrollToOffset(offset);
|
|
end
|
|
end
|
|
|
|
function ScrollFrameMixin:ScrollToWidget(widget, extraOffset)
|
|
local top = self:GetTop();
|
|
local bottom = self:GetBottom();
|
|
local wTop = widget:GetTop();
|
|
local wBottom = widget:GetBottom();
|
|
|
|
if extraOffset then
|
|
wTop = wTop + extraOffset;
|
|
wBottom = wBottom - extraOffset;
|
|
end
|
|
|
|
if (wTop > top) and (wBottom < top - 4) then
|
|
self:SmoothScrollByValue(top - wTop);
|
|
return true
|
|
else
|
|
if (wBottom < bottom) and (wTop > bottom + 4)then
|
|
self:SmoothScrollByValue(bottom - wBottom);
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
function ScrollFrameMixin:ScrollToTop()
|
|
self:ScrollToOffset(0);
|
|
end
|
|
|
|
function ScrollFrameMixin:IsScrolling()
|
|
return self.Driver.isScrolling;
|
|
end
|
|
|
|
function ScrollFrameMixin:LockScroll(state)
|
|
if state or state == nil then
|
|
self.locked = true;
|
|
else
|
|
self.locked = nil;
|
|
end
|
|
end
|
|
|
|
function ScrollFrameMixin:IsScrollLocked()
|
|
return self.locked
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
local function VirtualScrollBar_OnValueChanged(self, value, userInput)
|
|
scrollFrame:SetVerticalScroll(value);
|
|
if userInput then
|
|
d.toValue = value;
|
|
end
|
|
end
|
|
|
|
local function ApplySmoothScrollToScrollFrame(scrollFrame, enableSwipe, useReachLimitAnimation)
|
|
if not scrollFrame.Driver then
|
|
for k, v in pairs(ScrollFrameMixin) do
|
|
scrollFrame[k] = v;
|
|
end
|
|
|
|
local d = CreateFrame("Frame", nil, scrollFrame);
|
|
scrollFrame.Driver = d;
|
|
|
|
local bar = scrollFrame.ScrollBar;
|
|
if not bar then
|
|
bar = CreateFrame("Slider", nil, scrollFrame);
|
|
bar:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", 0, 0);
|
|
bar:SetPoint("BOTTOMRIGHT", scrollFrame, "BOTTOMRIGHT", 1, 0);
|
|
bar:SetMinMaxValues(0, 0);
|
|
scrollFrame.ScrollBar = bar;
|
|
end
|
|
bar:SetValue(0);
|
|
bar:SetValueStep(0.001);
|
|
|
|
bar:SetScript("OnValueChanged", function(self, value, userInput)
|
|
scrollFrame:SetVerticalScroll(value);
|
|
if userInput then
|
|
d.toValue = value;
|
|
end
|
|
end);
|
|
|
|
bar:SetScript("OnMouseDown", function()
|
|
d.t = 0;
|
|
d:SetScript("OnUpdate", Driver_RunValueChangedFunc);
|
|
d:Show();
|
|
end);
|
|
|
|
bar:SetScript("OnMouseUp", function()
|
|
d:SetScript("OnUpdate", nil)
|
|
d:Hide();
|
|
end);
|
|
|
|
d.toValue = 0;
|
|
d.updateIntervel = 0.2;
|
|
d:Hide();
|
|
d:SetScript("OnUpdate", Driver_OnUpdate);
|
|
d:SetScript("OnHide", Driver_OnHide);
|
|
|
|
d.minOffset = PIXEL_RATIO / 0.6667;
|
|
d.bar = bar;
|
|
|
|
scrollFrame:SetScript("OnMouseWheel", ScrollFrame_OnMouseWheel);
|
|
scrollFrame:SetScript("OnShow", ScrollFrame_OnShow);
|
|
scrollFrame:SetScrollRange(0);
|
|
scrollFrame:SetSpeedMultiplier(0.14);
|
|
scrollFrame:SetDeltaMultiplier(1);
|
|
local height = scrollFrame:GetHeight();
|
|
local step;
|
|
if height then
|
|
step = height * 0.5;
|
|
else
|
|
step = 64;
|
|
end
|
|
scrollFrame:SetStepSize(step);
|
|
scrollFrame:SetScript("OnHide", ScrollFrame_OnHide);
|
|
|
|
if enableSwipe then
|
|
scrollFrame:SetScript("OnDragStart", ScrollFrame_OnDragStart);
|
|
scrollFrame:SetScript("OnDragStop", ScrollFrame_OnDragStop);
|
|
scrollFrame:RegisterForDrag("LeftButton");
|
|
scrollFrame:EnableMouse(true);
|
|
end
|
|
|
|
if useReachLimitAnimation and scrollFrame.ScrollChild then
|
|
local ag, a1, p1, p2, p3;
|
|
|
|
scrollFrame.AnimAlertAtTop = scrollFrame.ScrollChild:CreateAnimationGroup();
|
|
ag = scrollFrame.AnimAlertAtTop;
|
|
|
|
a1 = ag:CreateAnimation("Path");
|
|
--a1:SetCurveType("SMOOTH");
|
|
a1:SetDuration(0.4);
|
|
p1 = a1:CreateControlPoint();
|
|
p1:SetOffset(0, -6);
|
|
p1:SetOrder(1);
|
|
p2 = a1:CreateControlPoint();
|
|
p2:SetOffset(0, 2);
|
|
p2:SetOrder(2);
|
|
p3 = a1:CreateControlPoint();
|
|
p3:SetOffset(0, 0);
|
|
p3:SetOrder(3);
|
|
|
|
scrollFrame.AnimAlertAtBottom = scrollFrame.ScrollChild:CreateAnimationGroup();
|
|
ag = scrollFrame.AnimAlertAtBottom;
|
|
|
|
a1 = ag:CreateAnimation("Path");
|
|
--a1:SetCurveType("SMOOTH");
|
|
a1:SetDuration(0.4);
|
|
p1 = a1:CreateControlPoint();
|
|
p1:SetOffset(0, 6);
|
|
p1:SetOrder(1);
|
|
p2 = a1:CreateControlPoint();
|
|
p2:SetOffset(0, -2);
|
|
p2:SetOrder(2);
|
|
p3 = a1:CreateControlPoint();
|
|
p3:SetOffset(0, 0);
|
|
p3:SetOrder(3);
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
NarciAPI.CreateSmoothScroll = ApplySmoothScrollToScrollFrame;
|