Xiiph@0: --[[----------------------------------------------------------------------------- Xiiph@0: ScrollFrame Container Xiiph@0: Plain container that scrolls its content and doesn't grow in height. Xiiph@0: -------------------------------------------------------------------------------]] Xiiph@0: local Type, Version = "ScrollFrame", 21 Xiiph@0: local AceGUI = LibStub and LibStub("AceGUI-3.0", true) Xiiph@0: if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end Xiiph@0: Xiiph@0: -- Lua APIs Xiiph@0: local pairs, assert, type = pairs, assert, type Xiiph@0: local min, max, floor, abs = math.min, math.max, math.floor, math.abs Xiiph@0: Xiiph@0: -- WoW APIs Xiiph@0: local CreateFrame, UIParent = CreateFrame, UIParent Xiiph@0: Xiiph@0: --[[----------------------------------------------------------------------------- Xiiph@0: Support functions Xiiph@0: -------------------------------------------------------------------------------]] Xiiph@0: local function FixScrollOnUpdate(frame) Xiiph@0: frame:SetScript("OnUpdate", nil) Xiiph@0: frame.obj:FixScroll() Xiiph@0: end Xiiph@0: Xiiph@0: --[[----------------------------------------------------------------------------- Xiiph@0: Scripts Xiiph@0: -------------------------------------------------------------------------------]] Xiiph@0: local function ScrollFrame_OnMouseWheel(frame, value) Xiiph@0: frame.obj:MoveScroll(value) Xiiph@0: end Xiiph@0: Xiiph@0: local function ScrollFrame_OnSizeChanged(frame) Xiiph@0: frame:SetScript("OnUpdate", FixScrollOnUpdate) Xiiph@0: end Xiiph@0: Xiiph@0: local function ScrollBar_OnScrollValueChanged(frame, value) Xiiph@0: frame.obj:SetScroll(value) Xiiph@0: end Xiiph@0: Xiiph@0: --[[----------------------------------------------------------------------------- Xiiph@0: Methods Xiiph@0: -------------------------------------------------------------------------------]] Xiiph@0: local methods = { Xiiph@0: ["OnAcquire"] = function(self) Xiiph@0: self:SetScroll(0) Xiiph@0: end, Xiiph@0: Xiiph@0: ["OnRelease"] = function(self) Xiiph@0: self.status = nil Xiiph@0: for k in pairs(self.localstatus) do Xiiph@0: self.localstatus[k] = nil Xiiph@0: end Xiiph@0: self.scrollframe:SetPoint("BOTTOMRIGHT") Xiiph@0: self.scrollbar:Hide() Xiiph@0: self.scrollBarShown = nil Xiiph@0: self.content.height, self.content.width = nil, nil Xiiph@0: end, Xiiph@0: Xiiph@0: ["SetScroll"] = function(self, value) Xiiph@0: local status = self.status or self.localstatus Xiiph@0: local viewheight = self.scrollframe:GetHeight() Xiiph@0: local height = self.content:GetHeight() Xiiph@0: local offset Xiiph@0: Xiiph@0: if viewheight > height then Xiiph@0: offset = 0 Xiiph@0: else Xiiph@0: offset = floor((height - viewheight) / 1000.0 * value) Xiiph@0: end Xiiph@0: self.content:ClearAllPoints() Xiiph@0: self.content:SetPoint("TOPLEFT", 0, offset) Xiiph@0: self.content:SetPoint("TOPRIGHT", 0, offset) Xiiph@0: status.offset = offset Xiiph@0: status.scrollvalue = value Xiiph@0: end, Xiiph@0: Xiiph@0: ["MoveScroll"] = function(self, value) Xiiph@0: local status = self.status or self.localstatus Xiiph@0: local height, viewheight = self.scrollframe:GetHeight(), self.content:GetHeight() Xiiph@0: Xiiph@0: if self.scrollBarShown then Xiiph@0: local diff = height - viewheight Xiiph@0: local delta = 1 Xiiph@0: if value < 0 then Xiiph@0: delta = -1 Xiiph@0: end Xiiph@0: self.scrollbar:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000)) Xiiph@0: end Xiiph@0: end, Xiiph@0: Xiiph@0: ["FixScroll"] = function(self) Xiiph@0: if self.updateLock then return end Xiiph@0: self.updateLock = true Xiiph@0: local status = self.status or self.localstatus Xiiph@0: local height, viewheight = self.scrollframe:GetHeight(), self.content:GetHeight() Xiiph@0: local offset = status.offset or 0 Xiiph@0: local curvalue = self.scrollbar:GetValue() Xiiph@0: -- Give us a margin of error of 2 pixels to stop some conditions that i would blame on floating point inaccuracys Xiiph@0: -- No-one is going to miss 2 pixels at the bottom of the frame, anyhow! Xiiph@0: if viewheight < height + 2 then Xiiph@0: if self.scrollBarShown then Xiiph@0: self.scrollBarShown = nil Xiiph@0: self.scrollbar:Hide() Xiiph@0: self.scrollbar:SetValue(0) Xiiph@0: self.scrollframe:SetPoint("BOTTOMRIGHT") Xiiph@0: self:DoLayout() Xiiph@0: end Xiiph@0: else Xiiph@0: if not self.scrollBarShown then Xiiph@0: self.scrollBarShown = true Xiiph@0: self.scrollbar:Show() Xiiph@0: self.scrollframe:SetPoint("BOTTOMRIGHT", -20, 0) Xiiph@0: self:DoLayout() Xiiph@0: end Xiiph@0: local value = (offset / (viewheight - height) * 1000) Xiiph@0: if value > 1000 then value = 1000 end Xiiph@0: self.scrollbar:SetValue(value) Xiiph@0: self:SetScroll(value) Xiiph@0: if value < 1000 then Xiiph@0: self.content:ClearAllPoints() Xiiph@0: self.content:SetPoint("TOPLEFT", 0, offset) Xiiph@0: self.content:SetPoint("TOPRIGHT", 0, offset) Xiiph@0: status.offset = offset Xiiph@0: end Xiiph@0: end Xiiph@0: self.updateLock = nil Xiiph@0: end, Xiiph@0: Xiiph@0: ["LayoutFinished"] = function(self, width, height) Xiiph@0: self.content:SetHeight(height or 0 + 20) Xiiph@0: self.scrollframe:SetScript("OnUpdate", FixScrollOnUpdate) Xiiph@0: end, Xiiph@0: Xiiph@0: ["SetStatusTable"] = function(self, status) Xiiph@0: assert(type(status) == "table") Xiiph@0: self.status = status Xiiph@0: if not status.scrollvalue then Xiiph@0: status.scrollvalue = 0 Xiiph@0: end Xiiph@0: end, Xiiph@0: Xiiph@0: ["OnWidthSet"] = function(self, width) Xiiph@0: local content = self.content Xiiph@0: content.width = width Xiiph@0: end, Xiiph@0: Xiiph@0: ["OnHeightSet"] = function(self, height) Xiiph@0: local content = self.content Xiiph@0: content.height = height Xiiph@0: end Xiiph@0: } Xiiph@0: --[[----------------------------------------------------------------------------- Xiiph@0: Constructor Xiiph@0: -------------------------------------------------------------------------------]] Xiiph@0: local function Constructor() Xiiph@0: local frame = CreateFrame("Frame", nil, UIParent) Xiiph@0: local num = AceGUI:GetNextWidgetNum(Type) Xiiph@0: Xiiph@0: local scrollframe = CreateFrame("ScrollFrame", nil, frame) Xiiph@0: scrollframe:SetPoint("TOPLEFT") Xiiph@0: scrollframe:SetPoint("BOTTOMRIGHT") Xiiph@0: scrollframe:EnableMouseWheel(true) Xiiph@0: scrollframe:SetScript("OnMouseWheel", ScrollFrame_OnMouseWheel) Xiiph@0: scrollframe:SetScript("OnSizeChanged", ScrollFrame_OnSizeChanged) Xiiph@0: Xiiph@0: local scrollbar = CreateFrame("Slider", ("AceConfigDialogScrollFrame%dScrollBar"):format(num), scrollframe, "UIPanelScrollBarTemplate") Xiiph@0: scrollbar:SetPoint("TOPLEFT", scrollframe, "TOPRIGHT", 4, -16) Xiiph@0: scrollbar:SetPoint("BOTTOMLEFT", scrollframe, "BOTTOMRIGHT", 4, 16) Xiiph@0: scrollbar:SetMinMaxValues(0, 1000) Xiiph@0: scrollbar:SetValueStep(1) Xiiph@0: scrollbar:SetValue(0) Xiiph@0: scrollbar:SetWidth(16) Xiiph@0: scrollbar:Hide() Xiiph@0: -- set the script as the last step, so it doesn't fire yet Xiiph@0: scrollbar:SetScript("OnValueChanged", ScrollBar_OnScrollValueChanged) Xiiph@0: Xiiph@0: local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND") Xiiph@0: scrollbg:SetAllPoints(scrollbar) Xiiph@0: scrollbg:SetTexture(0, 0, 0, 0.4) Xiiph@0: Xiiph@0: --Container Support Xiiph@0: local content = CreateFrame("Frame", nil, scrollframe) Xiiph@0: content:SetPoint("TOPLEFT") Xiiph@0: content:SetPoint("TOPRIGHT") Xiiph@0: content:SetHeight(400) Xiiph@0: scrollframe:SetScrollChild(content) Xiiph@0: Xiiph@0: local widget = { Xiiph@0: localstatus = { scrollvalue = 0 }, Xiiph@0: scrollframe = scrollframe, Xiiph@0: scrollbar = scrollbar, Xiiph@0: content = content, Xiiph@0: frame = frame, Xiiph@0: type = Type Xiiph@0: } Xiiph@0: for method, func in pairs(methods) do Xiiph@0: widget[method] = func Xiiph@0: end Xiiph@0: scrollframe.obj, scrollbar.obj = widget, widget Xiiph@0: Xiiph@0: return AceGUI:RegisterAsContainer(widget) Xiiph@0: end Xiiph@0: Xiiph@0: AceGUI:RegisterWidgetType(Type, Constructor, Version)