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.

191 lines
7.8 KiB

--[[
CompositeButtons are intrinsic buttons that create pseudo-buttons from the
textures in the ARTWORK layer of a button. Its aim is to solve the problem
of list buttons being encumbered by too many overlaying buttons and all the
different textures, script handlers and special handling it creates.
For example, the old (4.8.9) RematchTeamListButtonTemplate had a separate
button for notes, preferences, win record, 3 pets and a separate frame for
the favorites star (so highlight textures would appear below it). The pet
buttons had multiple layers of textures too. This represented 7 different
elements that intercept mouse events and three times as many layers of
textures with their own separate highlights and handling.
The new team list button template has no overlaying buttons. Everything
clickable is a texture in the ARTWORK layer of the button. When the mouse
moves over one of these textures, it's highlighted by a single reticle
duplicating the texture with an ADD blend mode, causing the texture to
brighten. OnClicks, OnEnters and OnLeaves are defined to the texture as if
it was a button.
To use:
1. Create a widget of type RematchCompositeButton
2. Add textures to the ARTWORK drawlayer (they will automatically gain
highlight behavior but will not intercept clicks)
3. Things that shouldn't be clicked should be in other layers
4. If they must be in ARTWORK and remain uninteractive:
texture.ignoreComposite = true
5. To set script handlers:
button:SetTextureScript(texture,"OnClick",func)
Supported handlers: "OnClick", "OnEnter", "OnLeave"
6. In the parent button's OnClick, check for button:HasFocus() to tell
if the button has focus but not an active texture. Clicks will go
through the textures to the underlying button; use HasFocus() to ignore.
]]
RematchCompositeButton = {} -- this is just a namespace for use bY XML
local allButtons = {} -- indexed by button, every created composite button
local onClicks = {} -- indexed by texture, function to run when a texture clicked
local onEnters = {} -- indexed by texture, function to run when mouse enters a texture
local onLeaves = {} -- indexed by texture, function to run when mouse leaves a texture
-- only one button can have focus and one texture in a button can share focus
local focusedButton = nil -- button that the mouse is currently over
local focusedTexture = nil -- texture within the above button that mouse is over
local reticle = CreateFrame("Frame", "RematchCompositeButtonReticle", UIParent)
-- method to add a script handler to a texture assigned to each button:
-- button:SetTextureScript(button.Icon,"OnClick",function() end)
local function setTextureScript(self, texture, handler, func)
if handler=="OnClick" then
onClicks[texture] = func -- note: self will be parent button, not texture
elseif handler=="OnEnter" then
onEnters[texture] = func
elseif handler=="OnLeave" then
onLeaves[texture] = func
end
end
-- method to return true if the mouse is over the button but not any active textures
local function hasFocus(self)
return focusedButton==self and not focusedTexture
end
-- when a button is created, add it and its textures to allButtons
-- (this assumes buttons are created in XML fully formed when OnLoad happens. for
-- buttons created with CreateFrame, call this after all textures are created.)
function RematchCompositeButton:OnPreLoad()
allButtons[self] = {}
-- add textures in the ARTWORK layer to the new allButtons entry
local index = 1
for _,child in ipairs({self:GetRegions()}) do
if child:GetObjectType()=="Texture" and child:GetDrawLayer()=="ARTWORK" and not child.ignoreComposite then
allButtons[self][index] = child
index = index + 1
end
end
self:RegisterForClicks("AnyUp") -- all buttons accept any mouse button
-- assign methods to the button
self.SetTextureScript = setTextureScript
self.HasFocus = hasFocus
end
-- onenter starts up reticle monitoring for texture interaction
function RematchCompositeButton:OnPreEnter()
focusedButton = self -- set focus to this button
focusedTexture = nil
reticle:SetParent(self)
reticle:SetFrameLevel(self:GetFrameLevel())
reticle.Overlay:Hide()
reticle:Show() -- start up reticle if it's not started
end
-- onleave stops reticle monitoring
function RematchCompositeButton:OnPreLeave()
-- if texture has focus, run its OnLeave if applicable
if focusedButton and focusedTexture and onLeaves[focusedTexture] then
onLeaves[focusedTexture](focusedTexture)
end
focusedButton = nil -- drop focus from this button
focusedTexture = nil
reticle:Hide() -- hide reticle
end
-- on a MouseDown, the overlay is hidden to show the normal texture underneath
-- this has the effect of making the texture look a bit darker
function RematchCompositeButton:OnPreMouseDown()
if focusedTexture then
reticle.Overlay:Hide()
end
end
-- on a MouseDown, then overlay is shown again, making the texture lighter
function RematchCompositeButton:OnPreMouseUp()
if focusedTexture then
reticle.Overlay:Show()
end
end
-- on a click of the button, run the texture's OnClick if defined
function RematchCompositeButton:OnPreClick(button)
if focusedButton==self and focusedTexture and onClicks[focusedTexture] then
onClicks[focusedTexture](focusedTexture,button)
end
end
-- when a button hides that has ownership of the reticle, the reticle hides too.
-- make sure the texture's OnLeave runs too
function RematchCompositeButton:OnPreHide()
if focusedButton==self and focusedTexture and onLeaves[focusedTexture] then
onLeaves[focusedTexture](focusedTexture)
end
end
-- the reticle is a frame that's on screen while the mouse is over a composite
-- button. it watches for the mouse moving over a texture, sets focusedTexture
-- and highlights the texture.
reticle:SetSize(32,32)
reticle.Overlay = reticle:CreateTexture(nil,"OVERLAY")
reticle.Overlay:SetAlpha(0.75)
reticle.Overlay:SetBlendMode("ADD")
reticle:EnableMouse(false)
reticle:SetScript("OnUpdate",function(self,elapsed)
if focusedButton and allButtons[focusedButton] then
for _,texture in ipairs(allButtons[focusedButton]) do
if MouseIsOver(texture) and texture:IsVisible() then
if focusedTexture ~= texture then
-- if leaving another texture, do its OnLeave first if needed
if focusedTexture and onLeaves[focusedTexture] then
onLeaves[focusedTexture](focusedTexture)
end
focusedTexture = texture
-- position reticle over the texture
self.Overlay:SetPoint("TOPLEFT", texture, "TOPLEFT")
self.Overlay:SetPoint("BOTTOMRIGHT", texture, "BOTTOMRIGHT")
local drawLayer,textureSubLevel = texture:GetDrawLayer()
self.Overlay:SetDrawLayer(drawLayer,textureSubLevel+1)
-- copy the texture to make it lighten the existing one
self.Overlay:SetTexture(texture:GetTexture())
self.Overlay:SetTexCoord(texture:GetTexCoord())
self.Overlay:SetVertexColor(0.75,0.75,0.75,1)
self.Overlay:SetDesaturated(true)
-- show reticle
self.Overlay:Show()
-- finally do an OnEnter of the focused texture
if onEnters[texture] then
onEnters[texture](texture)
end
else
-- check if texture has changed and update if so
local currentTexture = texture:GetTexture()
if self.Overlay:GetTexture()~=currentTexture then
self.Overlay:SetTexture(currentTexture)
end
end
return
end
end
-- if we reached here mouse is not over texture
-- first do any OnLeave of the focused texture if one exists
if focusedTexture and onLeaves[focusedTexture] then
onLeaves[focusedTexture](focusedTexture)
end
focusedTexture = nil
-- then hide the overlay
self.Overlay:ClearAllPoints()
self.Overlay:Hide()
end
end)