--[[ Name: LibWindow-1.1 Revision: $Rev: 3 $ Author(s): Mikk (dpsgnome@mail.com) Website: http://old.wowace.com/wiki/LibWindow-1.1 Documentation: http://old.wowace.com/wiki/LibWindow-1.1 SVN: http://svn.wowace.com/root/trunk/WindowLib/Window-1.0 Description: A library that handles the basics of "window" style frames: scaling, smart position saving, dragging.. Dependencies: none License: Public Domain ]] local MAJOR = "LibWindow-1.1" local MINOR = tonumber(("$Revision: 3 $"):match("(%d+)")) local lib = LibStub:NewLibrary(MAJOR,MINOR) if not lib then return end local function print(msg) ChatFrame1:AddMessage(MAJOR..": "..tostring(msg)) end lib.utilFrame = lib.utilFrame or CreateFrame("Frame") lib.delayedSavePosition = lib.delayedSavePosition or {} lib.windowData = lib.windowData or {} --[frameref]={ -- names={optional names data from .RegisterConfig()} -- storage= -- tableref where config data is read/written -- altEnable=true/false --} lib.embeds = lib.embeds or {} local mixins = {} -- "FuncName"=true --------------------------------------------------------- -- UTILITIES --------------------------------------------------------- local function getStorageName(frame, name) local names = lib.windowData[frame].names if names then if names[name] then return names[name] end if names.prefix then return names.prefix .. name; end end return name; end local function setStorage(frame, name, value) lib.windowData[frame].storage[getStorageName(frame, name)] = value end local function getStorage(frame, name) return lib.windowData[frame].storage[getStorageName(frame, name)] end lib.utilFrame:SetScript("OnUpdate", function(this) this:Hide() for frame,_ in pairs(lib.delayedSavePosition) do lib.delayedSavePosition[frame] = nil lib.SavePosition(frame) end end) local function queueSavePosition(frame) lib.delayedSavePosition[frame] = true lib.utilFrame:Show() end --------------------------------------------------------- -- IMPORTANT APIS --------------------------------------------------------- mixins["RegisterConfig"]=true function lib.RegisterConfig(frame, storage, names) if not lib.windowData[frame] then lib.windowData[frame] = {} end lib.windowData[frame].names = names lib.windowData[frame].storage = storage --[[ debug frame.tx = frame:CreateTexture() frame.tx:SetTexture(0,0,0, 0.4) frame.tx:SetAllPoints(frame) frame.tx:Show() ]] end --------------------------------------------------------- -- POSITIONING AND SCALING --------------------------------------------------------- local nilParent = { GetWidth = function() return GetScreenWidth() * UIParent:GetScale() end, GetHeight = function() return GetScreenHeight() * UIParent:GetScale() end, GetScale = function() return 1 end, } mixins["SavePosition"]=true function lib.SavePosition(frame) local parent = frame:GetParent() or nilParent -- No, this won't work very well with frames that aren't parented to nil or UIParent local s = frame:GetScale() local left,top = frame:GetLeft()*s, frame:GetTop()*s local right,bottom = frame:GetRight()*s, frame:GetBottom()*s local pwidth, pheight = parent:GetWidth(), parent:GetHeight() local x,y,point; if left < (pwidth-right) and left < abs((left+right)/2 - pwidth/2) then x = left; point="LEFT"; elseif (pwidth-right) < abs((left+right)/2 - pwidth/2) then x = right-pwidth; point="RIGHT"; else x = (left+right)/2 - pwidth/2; point=""; end if bottom < (pheight-top) and bottom < abs((bottom+top)/2 - pheight/2) then y = bottom; point="BOTTOM"..point; elseif (pheight-top) < abs((bottom+top)/2 - pheight/2) then y = top-pheight; point="TOP"..point; else y = (bottom+top)/2 - pheight/2; -- point=""..point; end if point=="" then point = "CENTER" end setStorage(frame, "x", x) setStorage(frame, "y", y) setStorage(frame, "point", point) setStorage(frame, "scale", s) frame:ClearAllPoints() frame:SetPoint(point, frame:GetParent(), point, x/s, y/s); end mixins["RestorePosition"]=true function lib.RestorePosition(frame) local x = getStorage(frame, "x") local y = getStorage(frame, "y") local point = getStorage(frame, "point") local s = getStorage(frame, "scale") if s then frame:SetScale(s) else s = frame:GetScale() end if not x or not y then -- nothing stored in config yet, smack it in the center x=0; y=0; point="CENTER" end x = x/s y = y/s frame:ClearAllPoints() if not point and y==0 then -- errr why did i do this check again? must have been a reason, but i can't remember it =/ point="CENTER" end if not point then -- we have position, but no point, which probably means we're going from data stored by the addon itself before LibWindow was added to it. It was PROBABLY topleft->bottomleft anchored. Most do it that way. frame:SetPoint("TOPLEFT", frame:GetParent(), "BOTTOMLEFT", x, y) -- make it compute a better attachpoint (on next update) queueSavePosition(frame) return end frame:SetPoint(point, frame:GetParent(), point, x, y) end mixins["SetScale"]=true function lib.SetScale(frame, scale) setStorage(frame, "scale", scale) frame:SetScale(scale) lib.RestorePosition(frame) end --------------------------------------------------------- -- DRAG SUPPORT --------------------------------------------------------- function lib.OnDragStart(frame) lib.windowData[frame].isDragging = true frame:StartMoving() end function lib.OnDragStop(frame) frame:StopMovingOrSizing() lib.SavePosition(frame) lib.windowData[frame].isDragging = false if lib.windowData[frame].altEnable and not IsAltKeyDown() then frame:EnableMouse(false) end end local function onDragStart(...) return lib.OnDragStart(...) end -- upgradable local function onDragStop(...) return lib.OnDragStop(...) end -- upgradable mixins["MakeDraggable"]=true function lib.MakeDraggable(frame) assert(lib.windowData[frame]) frame:SetMovable(true) frame:SetScript("OnDragStart", onDragStart) frame:SetScript("OnDragStop", onDragStop) frame:RegisterForDrag("LeftButton") end --------------------------------------------------------- -- MOUSEWHEEL --------------------------------------------------------- function lib.OnMouseWheel(frame, dir) local scale = getStorage(frame, "scale") if dir<0 then scale=max(scale*0.9, 0.1) else scale=min(scale/0.9, 3) end lib.SetScale(frame, scale) end local function onMouseWheel(...) return lib.OnMouseWheel(...) end -- upgradable mixins["EnableMouseWheelScaling"]=true function lib.EnableMouseWheelScaling(frame) frame:SetScript("OnMouseWheel", onMouseWheel) end --------------------------------------------------------- -- ENABLEMOUSE-ON-ALT --------------------------------------------------------- lib.utilFrame:SetScript("OnEvent", function(this, event, key, state) if event=="MODIFIER_STATE_CHANGED" then if key == "LALT" or key == "RALT" then for frame,_ in pairs(lib.altEnabledFrames) do if not lib.windowData[frame].isDragging then -- if it's already dragging, it'll disable mouse on DragStop instead frame:EnableMouse(state == 1) end end end end end) mixins["MakeDraggable"]=true function lib.EnableMouseOnAlt(frame) assert(lib.windowData[frame]) lib.windowData[frame].altEnable = true frame:EnableMouse(not not IsAltKeyDown()) if not lib.altEnabledFrames then lib.altEnabledFrames = {} lib.utilFrame:RegisterEvent("MODIFIER_STATE_CHANGED") end lib.altEnabledFrames[frame] = true end --------------------------------------------------------- -- Embed support (into FRAMES, not addons!) --------------------------------------------------------- function lib:Embed(target) if not target or not target[0] or not target.GetFrameType then error("Usage: LibWindow:Embed(frame)", 1) end for name, _ in pairs(mixins) do target[name] = self[name] end lib.embeds[target] = true return target end for target, _ in pairs(lib.embeds) do lib:Embed(target) end