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.

270 lines
7.8 KiB

4 years ago
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_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;
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;
else
self.bar:SetValue(min(self.range, value + step));
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
local d = self.Driver;
d.delta = delta;
local current = self.ScrollBar:GetValue();
if not((current <= 0.1 and delta > 0) or (current >= d.range - 0.1 and delta < 0 )) then
d.isScrolling = true;
if d.onScrollStartedFunc then
d.onScrollStartedFunc();
end
d:Show();
else
return;
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
end
local ScrollFrameMixin = {};
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.ScrollBar.onValueChangedFunc = onValueChangedFunc;
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.ScrollBar.onValueChangedFunc then
self.ScrollBar.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: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:Show();
end
function ScrollFrameMixin:ScrollByValue(value)
local current = self.ScrollBar:GetValue();
local offset = current + value;
if offset ~= current then
self:ScrollToOffset(offset);
end
end
function ScrollFrameMixin:ScrollToWidget(widget)
local top = self:GetTop();
local bottom = self:GetBottom();
local wTop = widget:GetTop();
local wBottom = widget:GetBottom();
if (wTop > top) and (wBottom < top - 4) then
self:ScrollByValue(top - wTop);
return true
else
if (wBottom < bottom) and (wTop > bottom + 4) then
self:ScrollByValue(bottom - wBottom);
return true
end
end
end
function ScrollFrameMixin:ScrollToTop()
self:ScrollToOffset(0);
end
function ScrollFrameMixin:IsScrolling()
return self.Driver.isScrolling;
end
---------------------------------------------------------------------
local function ApplySmoothScrollToScrollFrame(scrollFrame)
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
if self.onValueChangedFunc then
self.onValueChangedFunc(value);
end
end)
d.toValue = 0;
d:Hide();
d:SetScript("OnUpdate", Driver_OnUpdate);
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);
end
end
NarciAPI.CreateSmoothScroll = ApplySmoothScrollToScrollFrame;