Thread Tools Display Modes
04-10-09, 12:26 AM   #1
Venificus
A Fallenroot Satyr
Join Date: Dec 2008
Posts: 24
More VFL fixes

- Arrows on scroll bars now scroll one "page" at a time instead of a fixed amount.
- General fixes to VFLUI.List. Increased efficiency, no longer necessary to call Rebuild() when resizing the list, fixed an unfreed scrollbar.

VFL\UI\FrameClasses\List.lua:
Code:
-- List.lua
-- VFL
-- (C)2006 Bill Johnson
-- 
-- A List is a frame that contains a number of subframes and (possibly) a scrollbar.
VFLUI.List = {};

--- Create a new List.
-- @param cellHeight The height of each cell in the list.
-- @param fnAlloc The function called to allocate a new frame in the list.
function VFLUI.List:new(parent, cellHeight, fnAlloc)
	local self = VFLUI.AcquireFrame("Frame");
	if parent then
		self:SetParent(parent); self:SetFrameStrata(parent:GetFrameStrata()); self:SetFrameLevel(parent:GetFrameLevel());
	end

	------- INTERNAL PARAMETERS
	-- Dimensions
	local nCells, dy = 0, 0;
	-- Scrollbar
	local scrollbar = VFLUI.VScrollBar:new(self);
	local scrollEnabled = true;
	-- Apply data function
	local fnSize, fnData = VFL.EmptyLiterator();
	local fnApplyData = VFL.Noop;

	------- GRID CORE
	local grid = VFLUI.Grid:new(self);
	grid:SetPoint("TOPLEFT", self, "TOPLEFT"); grid:Show();
	-- Anchor the scrollbar to the grid
	scrollbar:ClearAllPoints();
	scrollbar:SetPoint("TOPLEFT", grid, "TOPRIGHT", 0, -16);
	scrollbar:SetPoint("BOTTOMRIGHT", grid, "BOTTOMRIGHT", 16, 16);
	
	------- SCROLLING VIRTUALIZER
	local virt = VFLUI.VirtualGrid:new(grid);
	virt.GetVirtualSize = function()
		local q = fnSize() - nCells + 1;
		if(q < 1) then q = 1; end
		return 1,q;
	end
	virt.OnRenderCell = function(v, c, x, y, vx, vy)
		local pos = y + vy - 1;
		local qq = fnData(pos);
		if not qq then 
			c:Hide() 
		else
			c:Show(); fnApplyData(c, qq, pos, y);
		end
	end
	self.SetVerticalScroll = function(g, val)
		local oldv, newv = virt.vy, math.floor(val);
		if(oldv ~= newv) then virt:SetVirtualPosition(1, newv); end
	end

	------- SCROLLBAR HANDLER
	local CreateScrollBar = function()
		-- Don't create if we already have one
		if scrollbar:IsShown() then return; end
		-- Resize the grid to accomodate the new scrollbar
		grid:SetCellDimensions(self:GetWidth() - 16, cellHeight);
		scrollbar:Show();
	end
	
	local DestroyScrollBar = function()
		if not scrollbar:IsShown() then return; end
		grid:SetCellDimensions(self:GetWidth(), cellHeight);
		scrollbar:Hide();
	end

	--- Enable/disable the scroll bar for this grid
	function self:SetScrollBarEnabled(val)
		if val then scrollEnabled = true; else scrollEnabled = nil; end
	end

	--- Get the underlying grid.
	function self:_GetGrid() return grid; end

	--- Rebuild the list control. This should be used when the size of the container frame changes.
	function self:Rebuild()
		grid:SetCellDimensions(self:GetWidth(), cellHeight);
		local oldnCells = nCells;
		nCells = math.floor(self:GetHeight() / cellHeight);
		if(nCells < 0) then nCells = 0; end
		if(oldnCells ~= nCells) then
			grid:Clear();
			grid:Size(1, nCells, fnAlloc);
			self:Update();
		end
	end

	--- Get the cell height provided at list creation.
	function self:GetCellHeight() return cellHeight; end
	
	--- Set the underlying data source for this list. The data source must be a _linear iterator_
	-- which consists of a pair of functions; one to retrieve the size of the underlying list and
	-- one which takes a numerical parameter and returns the data at that index, or NIL for none.
	function self:SetDataSource(fnad, liSz, liData)
		fnSize = liSz; fnData = liData; fnApplyData = fnad;
		self:Update();
	end

	--- Set this list to empty.
	function self:SetEmpty() self:SetDataSource(VFL.EmptyLiterator(), VFL.Noop); end

	--- Update the list in response to an update of the data table.
	function self:Update()
		local _,dy = virt:GetVirtualSize();
		-- Get the pagesize for the scroll bar
		if(dy > 1) and scrollEnabled then
			CreateScrollBar();
			scrollbar:SetMinMaxValues(1, dy);
			scrollbar:SetPageSize(nCells);
		else
			DestroyScrollBar();
		end
		virt:SetVirtualPosition(1, virt.vy);
		if scrollbar:IsShown() then
			scrollbar:SetValue(virt.vy); VFLUI.ScrollBarRangeCheck(scrollbar);
		end
	end

	--- Change the scroll value of the list. If the second parameter is given, the scrollbar
	-- will be pinned to the maximum value.
	function self:SetScroll(n, max)
		-- Update the virtual position of the list
		if max then
			virt:SetVirtualPosition(virt:GetVirtualSize());
		else
			virt:SetVirtualPosition(1, n);
		end
		-- Paint the scrollbar
		if scrollbar:IsShown() then
			scrollbar:SetValue(virt.vy);
			VFLUI.ScrollBarRangeCheck(scrollbar);
		end
	end
	
	-- Enable mousewheel scrolling
	self:EnableMouseWheel(true)
	self:SetScript("OnMouseWheel", function(thisframe, delta)
		local newpos, _, maxpos = virt.vy - delta, virt:GetVirtualSize();
		if(newpos < 1) then newpos = 1; elseif (newpos > maxpos) then newpos = maxpos; end
		self:SetScroll(newpos);
	end)
	
	-- Auto-rebuild on size changed if necessary
	self:SetScript("OnSizeChanged", self.Rebuild);

	-- Destroy handler.
	self.Destroy = VFL.hook(function(s)
		s.SetVerticalScroll = nil;
		scrollbar:Destroy(); scrollbar = nil;
		virt:Destroy();
		self.Rebuild = nil;
		self.GetCellHeight = nil;
		self.SetDataSource = nil;
		self.SetEmpty = nil;
		self.Update = nil;
		self.SetScroll = nil; self.SetScrollBarEnabled = nil;
		self._GetGrid = nil;
		self:EnableMouseWheel(false);
		self:SetScript("OnMouseWheel", nil);
	end, self.Destroy);

	return self;
end
VFL\UI\FrameClasses\Scroll.lua
Code:
-- Scroll.lua
-- VFL
-- (C)2006 Bill Johnson
--
-- Generators for horizontal and vertical scrollbars, and other scrolling-
-- related frametypes.

-- Internal: Create a scroll button with the given textures.
local function CreateScrollButton(nrm, psh, dis, hlt)
	local self = VFLUI.AcquireFrame("Button");
	-- Size is 16x16
	self:SetWidth(16); self:SetHeight(16);

	-- Button textures
	self:SetNormalTexture(nrm);
	self:SetPushedTexture(psh);
	self:SetDisabledTexture(dis);
	
	-- Highlight texture requires special handling (for blend mode)
	local hltTex = VFLUI.CreateTexture(self);
	hltTex:SetAllPoints(self); hltTex:Show();
	hltTex:SetTexture(hlt);
	hltTex:SetBlendMode("ADD");
	self:SetHighlightTexture(hltTex);
	self.hltTex = hltTex;

	self.Destroy = VFL.hook(function(s) 
		VFLUI.ReleaseRegion(s.hltTex);
		s.hltTex = nil;
	end, self.Destroy);

	return self;
end

--- Check the value of a scroll bar, and grey out the scroll buttons
-- as appropriate.
function VFLUI.ScrollBarRangeCheck(sb)
	local min,max = sb:GetMinMaxValues();
	local val = sb:GetValue();
	if(VFL.close(val,min)) then
		sb.btnDecrease:Disable();
	else
		sb.btnDecrease:Enable();
	end
	if(VFL.close(val, max)) then
		sb.btnIncrease:Disable();
	else
		sb.btnIncrease:Enable();
	end
end

--- Create a button with the VFL "Scroll up" skin.
function VFLUI.CreateScrollUpButton()
	return CreateScrollButton("Interface\\Addons\\VFL\\Skin\\sb_up", 
		"Interface\\Addons\\VFL\\Skin\\sb_up_pressed", 
		"Interface\\Addons\\VFL\\Skin\\sb_up_disabled",
		"Interface\\Addons\\VFL\\Skin\\sb_up_hlt");
end

--- Create a button with the VFL "Scroll down" skin.
function VFLUI.CreateScrollDownButton()
	return CreateScrollButton("Interface\\Addons\\VFL\\Skin\\sb_down", 
		"Interface\\Addons\\VFL\\Skin\\sb_down_pressed", 
		"Interface\\Addons\\VFL\\Skin\\sb_down_disabled",
		"Interface\\Addons\\VFL\\Skin\\sb_down_hlt");
end

--- Create a button with the "Scroll right" skin
function VFLUI.CreateScrollRightButton()
	return CreateScrollButton("Interface\\Addons\\VFL\\Skin\\sb_right", 
		"Interface\\Addons\\VFL\\Skin\\sb_right_pressed", 
		"Interface\\Addons\\VFL\\Skin\\sb_right_disabled",
		"Interface\\Addons\\VFL\\Skin\\sb_right_hlt");
end

--- Create a button with the "Scroll left" skin
function VFLUI.CreateScrollLeftButton()
	return CreateScrollButton("Interface\\Addons\\VFL\\Skin\\sb_left", 
		"Interface\\Addons\\VFL\\Skin\\sb_left_pressed", 
		"Interface\\Addons\\VFL\\Skin\\sb_left_disabled",
		"Interface\\Addons\\VFL\\Skin\\sb_left_hlt");
end

local vsb_backdrop = {
	bgFile="Interface\\Addons\\VFL\\Skin\\sb_vgutter"; 
	insets = { left = 0; right = 0; top = 0; bottom = 0; }; 
	tile = true; tileSize = 16;
};

VFLUI.VScrollBar = {};
--- Create a new vertical scrollbar.
function VFLUI.VScrollBar:new(parent)
	local self = VFLUI.AcquireFrame("Slider");
	self:SetWidth(16); self:SetHeight(0);

	if parent then
		self:SetParent(parent);
		self:SetFrameStrata(parent:GetFrameStrata());
		self:SetFrameLevel(parent:GetFrameLevel() + 1);
	end

	-- Gutter texture
	local gutter = VFLUI.CreateTexture(self);
	gutter:SetAllPoints(self);
	gutter:SetTexture("Interface\\Addons\\VFL\\Skin\\sb_vgutter");
	gutter:Show();

	-- Thumb texture
	local sbThumb = self:CreateTexture();
	sbThumb:SetWidth(16); sbThumb:SetHeight(16); sbThumb:Show();
	sbThumb:SetTexture("Interface\\Addons\\VFL\\Skin\\sb_nub");
	self:SetThumbTexture(sbThumb);

	-- Create the up/down buttons
	local btn = VFLUI.CreateScrollUpButton();
	btn:SetParent(self); btn:SetFrameLevel(self:GetFrameLevel()); btn:Show();
	btn:SetPoint("BOTTOM", self, "TOP");
	btn:SetScript("OnClick", function(x)
		local sb = x:GetParent();
		sb:SetValue(sb:GetValue() - sb:GetPageSize());
		PlaySound("UChatScrollButton");
	end);
	self.btnDecrease = btn;

	btn = VFLUI.CreateScrollDownButton();
	btn:SetParent(self); btn:SetFrameLevel(self:GetFrameLevel()); btn:Show();
	btn:SetPoint("TOP", self, "BOTTOM");
	btn:SetScript("OnClick", function(x)
		local sb = x:GetParent();
		sb:SetValue(sb:GetValue() + sb:GetPageSize());
		PlaySound("UChatScrollButton");
	end);
	self.btnIncrease = btn;

	-- Create the onscroll script
	self:SetScript("OnValueChanged", function(x, val)
		VFLUI.ScrollBarRangeCheck(x);
		local p = x:GetParent();
		if p and p.SetVerticalScroll then p:SetVerticalScroll(val); end
	end);
	
	-- Page size handling
	local pageSize = nil;
	function self:SetPageSize(n) pageSize = tonumber(n); end
	function self:GetPageSize()
		if pageSize then return pageSize; end
		local min,max = self:GetMinMaxValues();
		return (max-min)/5;
	end

	-- Hook the destroy handler
	self.Destroy = VFL.hook(function(self)
		self.SetPageSize = nil; self.GetPageSize = nil;
		self.btnDecrease:Destroy(); self.btnDecrease = nil;
		self.btnIncrease:Destroy(); self.btnIncrease = nil;
		gutter:Destroy(); gutter = nil;
	end, self.Destroy);

	-- Done
	return self;
end

---------------------------------------
-- @class VFLUI.VScrollFrame
-- A class similar to a Blizzard ScrollFrame, preloaded with VFL-themed
-- scrollbars and appropriate scripts.
---------------------------------------
VFLUI.VScrollFrame = {};
function VFLUI.VScrollFrame:new(parent)
	local self = VFLUI.AcquireFrame("ScrollFrame");
	self.offset = 0; self.scrollBarHideable = true;

	if parent then
		self:SetParent(parent);
		self:SetFrameStrata(parent:GetFrameStrata());
		self:SetFrameLevel(parent:GetFrameLevel() + 1);
	end

	local sb = VFLUI.VScrollBar:new(self);
	sb:SetPoint("TOPLEFT", self, "TOPRIGHT", 0, -16);
	sb:SetPoint("BOTTOMLEFT", self, "BOTTOMRIGHT", 0, 16);
	sb.btnIncrease:Disable(); sb.btnDecrease:Disable();
	sb:Show();

	local function OnScrollRangeChanged()
		scrollrange = self:GetVerticalScrollRange();
		sb:SetMinMaxValues(0, scrollrange);
		sb:SetPageSize(self:GetHeight());
		local value = sb:GetValue();
		if ( value > scrollrange ) then value = scrollrange; end
		sb:SetValue(value);
		if ( math.floor(scrollrange) == 0 ) then
			if (this.scrollBarHideable ) then
				sb:Hide();
			else
				sb:Show();
				sb.btnIncrease:Disable(); sb.btnIncrease:Show();
				sb.btnDecrease:Disable(); sb.btnDecrease:Show();
			end
		else
			sb:Show();
			sb.btnDecrease:Show();
			sb.btnIncrease:Show(); sb.btnIncrease:Enable();
		end
	end

	self:SetScript("OnScrollRangeChanged", OnScrollRangeChanged);

	--- Scroll child management.
	local BlizzSetScrollChild = self.SetScrollChild;
	self.SetScrollChild = function(s, sc)
		-- If we had an old scroll child, be sure to undo any changes we made to it.
		if s._scrollChild then
			s._scrollChild:SetScript("OnSizeChanged", s._oldChildOnSizeChanged);
			s._oldChildOnSizeChanged = nil;
		end
		-- Point to the new scroll child
		s._scrollChild = sc;
		BlizzSetScrollChild(s, sc);
		-- If the new scroll child is extant...
		if sc then
			-- Apply a new SizeChanged script.
			local osc = sc:GetScript("OnSizeChanged");
			s._oldChildOnSizeChanged = osc;
			if not osc then osc = VFL.Noop; end
			sc:SetScript("OnSizeChanged", function(th, w, h)
				osc(th, w, h);
				s:UpdateScrollChildRect();
				OnScrollRangeChanged();
			end);
		end
	end
	
	-- Enable mousewheel scrolling
	self:EnableMouseWheel(true)
	self:SetScript("OnMouseWheel", function(thisframe, delta)
		local curpos, minpos, maxpos = sb:GetValue(), sb:GetMinMaxValues();
		-- Move a fifth of a page
		local mv = self:GetHeight() / 5;
		curpos = curpos - (delta*mv);
		if(curpos < minpos) then curpos = minpos; elseif (curpos > maxpos) then curpos = maxpos; end
		sb:SetValue(curpos);
	end)

	self:SetScript("OnVerticalScroll", function() sb:SetValue(arg1); VFLUI.ScrollBarRangeCheck(sb); end);

	self.Destroy = VFL.hook(function(s)
		s:SetScrollChild(nil); s.SetScrollChild = nil; -- Deallocate extant scroll children.
		s.offset = nil; s.scrollBarHideable = nil;
		s._oldChildOnSizeChanged = nil;
		sb:Destroy();
	end, self.Destroy);
	return self;
end

---------------------------------------------------
-- @class VFLUI.HScrollBar
-- A horizontal slider.
---------------------------------------------------
local hsb_backdrop = {
	bgFile="Interface\\Addons\\VFL\\Skin\\sb_hgutter"; 
	insets = { left = 0; right = 0; top = 0; bottom = 0; }; 
	tile = true; tileSize = 16;
};
VFLUI.HScrollBar = {};
function VFLUI.HScrollBar:new(parent, noButtons)
	local self = VFLUI.AcquireFrame("Slider");
	self:SetHeight(16); self:SetWidth(0);
	self:SetOrientation("HORIZONTAL");
	if parent then
		self:SetParent(parent);
		self:SetFrameStrata(parent:GetFrameStrata());
		self:SetFrameLevel(parent:GetFrameLevel());
	end

	-- Gutter texture
	self:SetBackdrop(hsb_backdrop);
	-- Thumb texture
	local sbThumb = self:CreateTexture();
	sbThumb:SetWidth(16); sbThumb:SetHeight(16); sbThumb:Show();
	sbThumb:SetTexture("Interface\\Addons\\VFL\\Skin\\sb_nub");
	self:SetThumbTexture(sbThumb);

	if not noButtons then
		-- Create the up/down buttons
		local btn = VFLUI.CreateScrollLeftButton();
		btn:SetParent(self); btn:Show();
		btn:SetPoint("RIGHT", self, "LEFT");
		btn:SetScript("OnClick", function()
			local sb = this:GetParent();
			local min,max = sb:GetMinMaxValues();
			sb:SetValue(sb:GetValue() - ((max-min) / 5));
			PlaySound("UChatScrollButton");
		end);
		self.btnDecrease = btn;

		btn = VFLUI.CreateScrollRightButton();
		btn:SetParent(self); btn:Show();
		btn:SetPoint("LEFT", self, "RIGHT");
		btn:SetScript("OnClick", function()
			local sb = this:GetParent();
			local min,max = sb:GetMinMaxValues();
			sb:SetValue(sb:GetValue() + ((max-min) / 5));
			PlaySound("UChatScrollButton");
		end);
		self.btnIncrease = btn;
	end

	-- Create the onscroll script
	self:SetScript("OnValueChanged", function()
		VFLUI.ScrollBarRangeCheck(this);
		local p = this:GetParent();
		if p and p.SetHorizontalScroll then
			p:SetHorizontalScroll(arg1);
		end
	end);

	-- Hook the destroy handler
	self.Destroy = VFL.hook(function(s)
		if s.btnDecrease then s.btnDecrease:Destroy(); s.btnDecrease = nil; end
		if s.btnIncrease then s.btnIncrease:Destroy(); s.btnIncrease = nil; end
	end, self.Destroy);

	-- Done
	return self;
end

------------------------------------------------------------------------
-- Bind a slider to an edit box.
------------------------------------------------------------------------
function VFLUI.BindSliderToEdit(slider, edit)
	local _recurse_prevent = false;

	local old_ovc = slider:GetScript("OnValueChanged");
	slider:SetScript("OnValueChanged", function()
		if not _recurse_prevent then
			_recurse_prevent = true;
			edit:SetText(string.format("%0.2f", arg1));
			_recurse_prevent = false;
		end
		if old_ovc then old_ovc(); end
	end);

	local old_otc = edit:GetScript("OnTextChanged");
	edit:SetScript("OnTextChanged", function()
		if not _recurse_prevent then
			_recurse_prevent = true;
			local n = this:GetNumber();
			VFL.clamp(n, slider:GetMinMaxValues());
			slider:SetValue(n);
			_recurse_prevent = false;
		end
		if old_otc then old_otc(); end
	end);

	edit:SetText(string.format("%0.2f", slider:GetValue()));
end
  Reply With Quote
04-10-09, 01:24 AM   #2
sigg
Featured Artist
 
sigg's Avatar
Featured
Join Date: Aug 2008
Posts: 1,251
add to the svn
  Reply With Quote

WoWInterface » Featured Projects » OpenRDX » OpenRDX Community » OpenRDX: Community Chat » More VFL fixes


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off