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.
364 lines
9.7 KiB
364 lines
9.7 KiB
local _, addon = ...
|
|
local API = addon.API;
|
|
local Clamp = API.Clamp;
|
|
|
|
local pairs = pairs;
|
|
local tinsert = table.insert;
|
|
|
|
|
|
local ScrollViewMixin = {};
|
|
|
|
function ScrollViewMixin:GetDataIndexBegin()
|
|
return self.dataIndexBegin or 0;
|
|
end
|
|
function ScrollViewMixin:GetDataIndexEnd()
|
|
return self.dataIndexEnd or 0;
|
|
end
|
|
function ScrollViewMixin:GetDataRange()
|
|
return self.dataIndexBegin, self.dataIndexEnd;
|
|
end
|
|
function ScrollViewMixin:SetDataRange(dataIndexBegin, dataIndexEnd)
|
|
self.dataIndexBegin = dataIndexBegin;
|
|
self.dataIndexEnd = dataIndexEnd;
|
|
end
|
|
|
|
function ScrollViewMixin:GetScrollOffset()
|
|
return self.scrollOffset or 0;
|
|
end
|
|
|
|
function ScrollViewMixin:SetAllowNegativeScrollRange(state)
|
|
self.allowNegativeScrollRange = state;
|
|
end
|
|
|
|
function ScrollViewMixin:SetScrollRange(maxScrollOffset)
|
|
if (not self.allowNegativeScrollRange) and maxScrollOffset < 0 then
|
|
maxScrollOffset = 0;
|
|
end
|
|
|
|
self.maxScrollOffset = maxScrollOffset;
|
|
end
|
|
|
|
function ScrollViewMixin:GetScrollRange()
|
|
return self.maxScrollOffset or 0
|
|
end
|
|
|
|
function ScrollViewMixin:IsScrollable()
|
|
return self:GetScrollRange() > 0
|
|
end
|
|
|
|
function ScrollViewMixin:IsAtBottom()
|
|
return self:GetScrollOffset() + 0.1 >= self:GetScrollRange();
|
|
end
|
|
|
|
function ScrollViewMixin:ScrollBy(offset)
|
|
self:SetScrollOffset( self:GetScrollOffset() + offset);
|
|
end
|
|
|
|
function ScrollViewMixin:ScrollToTop()
|
|
self:SetScrollOffset(0);
|
|
end
|
|
|
|
function ScrollViewMixin:ScrollToBottom()
|
|
self:SetScrollOffset(self:GetScrollRange());
|
|
end
|
|
|
|
function ScrollViewMixin:GetViewSize()
|
|
return self.viewSize or 0
|
|
end
|
|
|
|
function ScrollViewMixin:OnSizeChanged()
|
|
self.viewSize = self:GetHeight();
|
|
end
|
|
|
|
function ScrollViewMixin:SetSpacing(spacing)
|
|
self.spacing = spacing;
|
|
end
|
|
|
|
function ScrollViewMixin:GetExtent()
|
|
return self.dataProvider:GetExtent()
|
|
end
|
|
|
|
function ScrollViewMixin:GetMaxDataIndex()
|
|
return self.dataProvider:GetMaxDataIndex();
|
|
end
|
|
|
|
function ScrollViewMixin:CreateObject()
|
|
return self.dataProvider:CreateObject();
|
|
end
|
|
|
|
function ScrollViewMixin:SetObjectData(object, dataIndex)
|
|
return self.dataProvider:SetObjectData(object, dataIndex);
|
|
end
|
|
|
|
function ScrollViewMixin:GetMaxExtent()
|
|
return self.dataProvider and self.dataProvider:GetMaxExtent() or 0
|
|
end
|
|
|
|
function ScrollViewMixin:UpdateScrollRange()
|
|
self:SetScrollRange(self:GetMaxExtent() - self:GetViewSize());
|
|
end
|
|
|
|
function ScrollViewMixin:OnContentChanged(updateWhenAtBottom)
|
|
local instantUpdate = (not updateWhenAtBottom) or (self:IsAtBottom());
|
|
self:UpdateScrollRange();
|
|
|
|
if instantUpdate then
|
|
self.scrollOffset = self:GetScrollRange();
|
|
self:UpdateView();
|
|
self.dataProvider:OnViewUpdated();
|
|
else
|
|
--blip
|
|
|
|
end
|
|
|
|
if self.scrollBar then
|
|
self.scrollBar:UpdateThumbSize();
|
|
if instantUpdate then
|
|
self.scrollBar:UpdateThumbPosition();
|
|
else
|
|
self.scrollBar:SetHasNewMessage();
|
|
end
|
|
end
|
|
end
|
|
|
|
function ScrollViewMixin:SetDataProvider(dataProvider)
|
|
self.dataProvider = dataProvider;
|
|
dataProvider.owner = self;
|
|
self:SetSpacing(dataProvider:GetSpacing());
|
|
self:SetScrollRange(dataProvider:GetMaxExtent() - self:GetViewSize());
|
|
self:SetStep(dataProvider:GetStep());
|
|
self:SetDataRange(0, 0);
|
|
self.scrollOffset = 0;
|
|
self:UpdateView();
|
|
end
|
|
|
|
function ScrollViewMixin:SetScrollBar(scrollBar)
|
|
if self.scrollBar then
|
|
self.scrollBar:Detach();
|
|
end
|
|
self.scrollBar = scrollBar;
|
|
scrollBar:SetOwner(self);
|
|
end
|
|
|
|
function ScrollViewMixin:SetScrollOffset(scrollOffset)
|
|
scrollOffset = Clamp(scrollOffset, 0, self.maxScrollOffset);
|
|
|
|
self.scrollOffset = scrollOffset;
|
|
|
|
if self.hasContent and (scrollOffset < self.viewScrollBegin or scrollOffset > self.viewScrollEnd) then
|
|
self:UpdateView();
|
|
end
|
|
|
|
if self.activeObjects then
|
|
for object in pairs(self.activeObjects) do
|
|
self:SetObjectPositionByScrollOffset(object, scrollOffset);
|
|
end
|
|
end
|
|
|
|
if self.scrollBar then
|
|
self.scrollBar:UpdateThumbPosition();
|
|
end
|
|
end
|
|
|
|
function ScrollViewMixin:SetObjectPositionByScrollOffset(object, scrollOffset)
|
|
object:SetPoint("TOPLEFT", self, "TOPLEFT", 0, scrollOffset - object.fromOffset);
|
|
end
|
|
|
|
function ScrollViewMixin:SetStep(step)
|
|
self.step = step;
|
|
end
|
|
|
|
function ScrollViewMixin:OnMouseWheel(delta)
|
|
if delta > 0 then
|
|
if self.scrollOffset > 0 then
|
|
local scrollTarget = self.scrollOffset - self.step;
|
|
if scrollTarget < 0 then
|
|
scrollTarget = 0;
|
|
end
|
|
self:SetScrollOffset(scrollTarget);
|
|
end
|
|
else
|
|
if self.scrollOffset < self.maxScrollOffset then
|
|
local scrollTarget = self.scrollOffset + self.step;
|
|
if scrollTarget > self.maxScrollOffset then
|
|
scrollTarget = self.maxScrollOffset;
|
|
end
|
|
self:SetScrollOffset(scrollTarget);
|
|
end
|
|
end
|
|
|
|
if self.parent and self.parent.OnMouseWheelCallback then
|
|
self.parent:OnMouseWheelCallback(delta);
|
|
end
|
|
end
|
|
|
|
function ScrollViewMixin:UpdateView(forceUpdate)
|
|
local offset = self:GetScrollOffset();
|
|
local viewSize = self:GetViewSize();
|
|
local viewEnd = offset + viewSize;
|
|
local oldIndexBegin, oldIndexEnd = self:GetDataRange();
|
|
local extent = self:GetExtent();
|
|
local maxDataIndex = self:GetMaxDataIndex();
|
|
|
|
local newIndexBegin = oldIndexBegin;
|
|
local newIndexEnd;
|
|
|
|
if oldIndexBegin > 0 and offset < extent[oldIndexBegin] then
|
|
while newIndexBegin > 1 and offset < extent[newIndexBegin] do
|
|
newIndexBegin = newIndexBegin - 1;
|
|
end
|
|
else
|
|
while newIndexBegin < maxDataIndex and offset > extent[newIndexBegin + 1] do
|
|
newIndexBegin = newIndexBegin + 1;
|
|
end
|
|
end
|
|
|
|
newIndexEnd = newIndexBegin;
|
|
while newIndexEnd < maxDataIndex and viewEnd > extent[newIndexEnd + 1] do
|
|
newIndexEnd = newIndexEnd + 1;
|
|
end
|
|
|
|
newIndexBegin = Clamp(newIndexBegin, 0, maxDataIndex);
|
|
newIndexEnd = Clamp(newIndexEnd, 0, maxDataIndex);
|
|
|
|
if (newIndexBegin ~= oldIndexBegin) or (newIndexEnd ~= oldIndexEnd) or forceUpdate then
|
|
--print("RANGE", newIndexBegin, newIndexEnd)
|
|
self:SetDataRange(newIndexBegin, newIndexEnd);
|
|
|
|
self.viewScrollBegin = extent[newIndexBegin + 1];
|
|
self.viewScrollEnd = extent[newIndexEnd] - viewSize;
|
|
self.hasContent = true;
|
|
|
|
local recycledObjects = {};
|
|
local numUnused = 0;
|
|
local dataObjects = {};
|
|
|
|
if not self.objectPool then
|
|
self.objectPool = {};
|
|
self.activeObjects = {};
|
|
end
|
|
|
|
for object, index in pairs(self.activeObjects) do
|
|
dataObjects[index] = object;
|
|
if not (index >= newIndexBegin and index <= newIndexEnd) then
|
|
numUnused = numUnused + 1;
|
|
recycledObjects[numUnused] = object;
|
|
object:Hide();
|
|
object:ClearAllPoints();
|
|
end
|
|
end
|
|
|
|
local object;
|
|
|
|
for index = newIndexBegin, newIndexEnd do
|
|
if dataObjects[index] then
|
|
object = dataObjects[index];
|
|
elseif numUnused > 0 then
|
|
object = recycledObjects[numUnused];
|
|
numUnused = numUnused - 1;
|
|
else
|
|
object = self:CreateObject();
|
|
tinsert(self.objectPool, object);
|
|
--print("Created. Total:", #self.objectPool);
|
|
end
|
|
|
|
self.activeObjects[object] = index;
|
|
if self:SetObjectData(object, index) then
|
|
object:Show();
|
|
end
|
|
object:ClearAllPoints();
|
|
object.fromOffset = extent[index];
|
|
self:SetObjectPositionByScrollOffset(object, offset);
|
|
end
|
|
end
|
|
end
|
|
|
|
function ScrollViewMixin:OnShow()
|
|
if self.dataProvider then
|
|
self.dataProvider:OnShow(self);
|
|
end
|
|
end
|
|
|
|
function ScrollViewMixin:OnHide()
|
|
if self.dataProvider then
|
|
self.dataProvider:OnHide(self);
|
|
end
|
|
end
|
|
|
|
function ScrollViewMixin:IsDraggingThumb()
|
|
return self.scrollBar and self.scrollBar:IsDraggingThumb()
|
|
end
|
|
|
|
function ScrollViewMixin:OnCullingComplete(numCulled)
|
|
self:SetDataRange(-1, -1);
|
|
if self.activeObjects then
|
|
for object, index in pairs(self.activeObjects) do
|
|
if object.dataIndex then
|
|
self.activeObjects[object] = object.dataIndex - numCulled;
|
|
end
|
|
object:Hide();
|
|
object:ClearAllPoints();
|
|
object.dataIndex = nil;
|
|
end
|
|
end
|
|
self:UpdateView(true);
|
|
end
|
|
|
|
local function CreateScrollView(parent)
|
|
local f = CreateFrame("Frame", nil, parent);
|
|
f.parent = parent;
|
|
|
|
API.Mixin(f, ScrollViewMixin);
|
|
|
|
f:SetScript("OnSizeChanged", f.OnSizeChanged);
|
|
f:SetScript("OnMouseWheel", f.OnMouseWheel);
|
|
f:SetScript("OnShow", f.OnShow);
|
|
f:SetScript("OnHide", f.OnHide);
|
|
|
|
f:SetClipsChildren(true);
|
|
|
|
return f
|
|
end
|
|
addon.CreateScrollView = CreateScrollView;
|
|
|
|
|
|
|
|
--[[
|
|
local ScrollViewDataProvider = {};
|
|
|
|
function ScrollViewDataProvider:GetSpacing()
|
|
|
|
end
|
|
|
|
function ScrollViewDataProvider:CalculateExtent()
|
|
|
|
end
|
|
|
|
function ScrollViewDataProvider:GetExtent()
|
|
|
|
end
|
|
|
|
function ScrollViewDataProvider:GetMaxDataIndex()
|
|
|
|
end
|
|
|
|
function ScrollViewDataProvider:CreateObject()
|
|
|
|
end
|
|
|
|
function ScrollViewDataProvider:SetObjectData()
|
|
|
|
end
|
|
|
|
function ScrollViewDataProvider:OnShow(scrollView)
|
|
|
|
end
|
|
|
|
function ScrollViewDataProvider:OnHide(scrollView)
|
|
|
|
end
|
|
|
|
function ScrollViewDataProvider:OnViewUpdated()
|
|
|
|
end
|
|
-]]
|