• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Lua] HoverOriginButton

This is a variation of the system I used in TasAbilityFieldTooltip to know that a CommandButton is hovered. It allows to add/Remove callbacks to the hover feature and have many of them.
Lua:
--[[ HoverOriginButton V1.1 by Tasyen
This System gives a easy way setup callbacks when a Command/Item Button is hovered. It uses Tooltips hence the executed callbacks are async.
I created this because one can do this Tooltip approach to know hovering only once per map so that systems don't have to fight over that feature but use it.

function HoverOriginButton.Add(forCommandButton, everyTime, action)
    forCommandButton (true) add to commandButton Actions
    everyTime (false) only happens when the current Frame was not hovered lastTime.
    action the callback
    function(buttonIndex)
        yourCode
        yourCode
    end
    returns the new objects can be used for HoverOriginButton.Remove
function HoverOriginButton.AddClose(action)
    add a callback that happens when the user stops hoveringer a Command/Item Button, async.
    function()
        yourCode
        yourCode
    end
function HoverOriginButton.Remove(object)
    removes the table from the callback List
--]]
do
    HoverOriginButton = {ItemButtonOffset = 30}
    local this = HoverOriginButton
    
local function InitFrames()
    this.Frames = {}
    local frame, button, CurrentSelectedButtonIndex, selectedAnything
    -- saves the last selected Button, async
    CurrentSelectedButtonIndex = nil
    --create one tooltip frame for each command button
    for int = 0, 11 do
       button = BlzGetOriginFrame(ORIGIN_FRAME_COMMAND_BUTTON, int)
       frame = BlzCreateFrameByType("SIMPLEFRAME", "", button, "", 0)
       BlzFrameSetTooltip(button, frame)
       BlzFrameSetVisible(frame, false)
       this.Frames[int] = frame
    end

    --create one tooltip frame for each command button
    for int = 0, 5 do
        button = BlzGetOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, int)
        frame = BlzCreateFrameByType("SIMPLEFRAME", "", button, "", 0)
        BlzFrameSetTooltip(button, frame)
        BlzFrameSetVisible(frame, false)
        this.Frames[int + this.ItemButtonOffset] = frame
     end

    button = nil
    frame = nil
 
    TimerStart(this.Timer, 1.0/32, true, function()        
       selectedAnything = false   
       -- loop all tooltips and check for the visible one
       for int = 0, 11 do
           if BlzFrameIsVisible(this.Frames[int]) then
               selectedAnything = true

               -- the new selected is not the same as the current one?
               for _, v in ipairs(this.ActionsCommand) do
                if CurrentSelectedButtonIndex ~= int or v[2] then
                    v[1](int)
                end                    
               end
               CurrentSelectedButtonIndex = int        
           end
       end

       local tableIndex
       for int = 0, 5 do
            tableIndex = int + this.ItemButtonOffset
            if BlzFrameIsVisible(this.Frames[tableIndex]) then
                selectedAnything = true

                -- the new selected is not the same as the current one?
                for _, v in ipairs(this.ActionsItem) do
                    if CurrentSelectedButtonIndex ~= tableIndex or v[2] then
                        v[1](int)
                    end                    
                end
                CurrentSelectedButtonIndex = tableIndex        
            end
        end

       -- now selects nothing?
       if not selectedAnything and CurrentSelectedButtonIndex then
          for _, v in ipairs(this.ActionsClose) do v[1]() end
            CurrentSelectedButtonIndex = nil
       end
       
   end)
 end
 local function Init()
    this.Timer = CreateTimer()
    InitFrames()
    if FrameLoaderAdd then FrameLoaderAdd(InitFrames) end
    this.ActionsCommand = {}
    this.ActionsItem = {}
    this.ActionsClose = {}
 end
 function HoverOriginButton.Add(forCommandButton, everyTime, action)
    if not this.Timer then Init() end
    local object = {action, everyTime}
    if forCommandButton then
        table.insert(this.ActionsCommand, object)
    else
        table.insert(this.ActionsItem, object)
    end
    return object
 end
 function HoverOriginButton.AddClose(action)
    if not this.Timer then Init() end
    local object = {action}
    table.insert(this.ActionsClose, object)
    return object
 end
 function HoverOriginButton.Remove(object)
    for i, v in ipairs(this.ActionsCommand) do
        if v == object then
            table.remove(this.ActionsCommand, i)
            return
        end                    
    end
    for i, v in ipairs(this.ActionsItem) do
        if v == object then
            table.remove(this.ActionsItem, i)
            return
        end                    
    end
    for i, v in ipairs(this.ActionsClose) do
        if v == object then
            table.remove(this.ActionsClose, i)
            return
        end                    
    end
 end
end
Changelog:
V1.1 API functions became a table member of HoverOriginButton
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
In order to keep the amount of globals being filled into the _G table to a minimum, I would recommend swapping the public API functions to be member-functions of the table HoverOriginButton/this. In addition to the performance/scope gain, users equipped with VScode+a Lua API extension would then find it fairly straightfoward to identify the full API just by knowing the one global variable.
 
I made an upgrade V1.2.
V1.2 supports also system, hero, buff & minimap buttons. But the HoverOriginButton.Add function is not compatible with the old anymore.
Lua:
--[[ HoverOriginButton V1.2 by Tasyen
This System gives a easy way setup callbacks when a originframe Button is hovered. It uses Tooltips hence the executed callbacks are async.
I created this because one can do this Tooltip approach to know hovering only once per map so that systems don't have to fight over that feature but use it.
function HoverOriginButton.Add(originframeType, everyTime, action)
    originframeType ORIGIN_FRAME_COMMAND_BUTTON or ORIGIN_FRAME_HERO_BUTTON ...
    everyTime (false) only happens when the current Frame was not hovered lastTime.
    action the callback
    function(buttonIndex)
        yourCode
        yourCode
    end
    returns the new objects can be used for HoverOriginButton.Remove
function HoverOriginButton.AddClose(action)
    add a callback that happens when the user stops hoveringer a Command/Item Button, async.
    function()
        yourCode
        yourCode
    end
function HoverOriginButton.Remove(object)
    removes the table from the callback List
--]]
do
    HoverOriginButton = {}
    -- originTypesUsed which originframe are considered by the system this can only be changed here
    local originTypesUsed = {ORIGIN_FRAME_COMMAND_BUTTON, ORIGIN_FRAME_HERO_BUTTON, ORIGIN_FRAME_ITEM_BUTTON, ORIGIN_FRAME_SYSTEM_BUTTON, ORIGIN_FRAME_MINIMAP_BUTTON,ORIGIN_FRAME_UNIT_PANEL_BUFF_BAR}
    local this = HoverOriginButton
    local function CreateTooltipForOrigin(originframeType, firstIndex, lastIndex)       
        for int = firstIndex, lastIndex do
            local button = BlzGetOriginFrame(originframeType, int)
            local frame = BlzCreateFrameByType("SIMPLEFRAME", "", button, "", 0)
            BlzFrameSetTooltip(button, frame)
            BlzFrameSetVisible(frame, false)
            this[frame] = {originframeType, int}
            table.insert(this.Frames, frame)
         end
    end
    local function CreateTooltipForBuffs(firstIndex, lastIndex)
        if ORIGIN_FRAME_UNIT_PANEL_BUFF_BAR then
            local parent = BlzGetOriginFrame(ORIGIN_FRAME_UNIT_PANEL_BUFF_BAR, 0)
            for int = firstIndex, lastIndex do
                local button = BlzFrameGetChild(parent, int)
                local frame = BlzCreateFrameByType("SIMPLEFRAME", "", button, "", 0)
                BlzFrameSetTooltip(button, frame)
                BlzFrameSetVisible(frame, false)
                this[frame] = {ORIGIN_FRAME_UNIT_PANEL_BUFF_BAR, int}
                table.insert(this.Frames, frame)
             end
        end
       
    end
local function InitFrames()
    this.Frames = {}
    local frame, button, CurrentSelectedButtonIndex, selectedAnything
    CreateTooltipForOrigin(ORIGIN_FRAME_COMMAND_BUTTON, 0, 11)
    CreateTooltipForOrigin(ORIGIN_FRAME_ITEM_BUTTON, 0, 5)
    CreateTooltipForOrigin(ORIGIN_FRAME_HERO_BUTTON, 0, 6)
    CreateTooltipForOrigin(ORIGIN_FRAME_SYSTEM_BUTTON, 0, 3)
    CreateTooltipForOrigin(ORIGIN_FRAME_MINIMAP_BUTTON, 0, 4)
    CreateTooltipForBuffs(0, 7)
    button = nil
    frame = nil
     -- saves the last selected Button, async
     CurrentSelectedButtonIndex = nil
    TimerStart(this.Timer, 0.1, true, function()
       selectedAnything = false  
       -- loop all tooltips and check for the visible one
       for tooltipIndex, tooltip in ipairs(this.Frames) do
           if BlzFrameIsVisible(tooltip) then
               selectedAnything = true
                local actionType = this[tooltip][1]
                local buttonIndex = this[tooltip][2]
               -- the new selected is not the same as the current one?
               for _, v in ipairs(this.Actions[actionType]) do
                if CurrentSelectedButtonIndex ~= tooltipIndex or v[2] then
                    v[1](buttonIndex)
                end                
               end
               CurrentSelectedButtonIndex = tooltipIndex       
           end
       end
       -- now selects nothing?
       if not selectedAnything and CurrentSelectedButtonIndex then
          for _, v in ipairs(this.ActionsClose) do v[1]() end
            CurrentSelectedButtonIndex = nil
       end
      
   end)
 end
 local function Init()
    this.Timer = CreateTimer()
    InitFrames()
    if FrameLoaderAdd then FrameLoaderAdd(InitFrames) end
    this.Actions = {}
    for x, originType in ipairs(originTypesUsed) do this.Actions[originType] = {} end   
    this.ActionsClose = {}
 end
 function HoverOriginButton.Add(originframeType, everyTime, action)
    if not this.Timer then Init() end
    local object = {action, everyTime}
    if not originframeType then  originframeType = ORIGIN_FRAME_COMMAND_BUTTON end
    table.insert(this.Actions[originframeType], object)
    return object
 end
 function HoverOriginButton.AddClose(action)
    if not this.Timer then Init() end
    local object = {action}
    table.insert(this.ActionsClose, object)
    return object
 end
 function HoverOriginButton.Remove(object)
    for _, actions in pairs(this.Actions) do
        for i, v in ipairs(actions) do
            if v == object then
                table.remove(actions, i)
                return
            end
        end
    end
    for i, v in ipairs(this.ActionsClose) do
        if v == object then
            table.remove(this.ActionsClose, i)
            return
        end
    end
 end
end
examples
Lua:
HoverOriginButton.Add(ORIGIN_FRAME_HERO_BUTTON, false, function(buttonIndex) print("Hero",buttonIndex) end)
HoverOriginButton.Add(ORIGIN_FRAME_COMMAND_BUTTON, false, function(buttonIndex) print("Command",buttonIndex) end)
HoverOriginButton.Add(ORIGIN_FRAME_ITEM_BUTTON, false, function(buttonIndex) print("item",buttonIndex) end)
HoverOriginButton.Add(ORIGIN_FRAME_SYSTEM_BUTTON, false, function(buttonIndex) print("System",buttonIndex) end)
HoverOriginButton.Add(ORIGIN_FRAME_UNIT_PANEL_BUFF_BAR, false, function(buttonIndex) print("Buff",buttonIndex) end)
HoverOriginButton.Add(ORIGIN_FRAME_MINIMAP_BUTTON, false, function(buttonIndex) print("MiniMap",buttonIndex) end)
 
Last edited:
Top