WoW Addon - Dynamic tooltip while key is pressed - lua

I've got a tooltip and I want it to show more detailed information if the SHIFT key is pressed/held. The following code works so far:
function myAddonFrame_OnEnter(self)
myAddon_GenerateTooltip(self)
end
function myAddonFrame_OnLeave(self)
GameTooltip:Hide()
end
function myAddon_GenerateTooltip(self)
GameTooltip:SetOwner(self or UIParent, "ANCHOR_LEFT")
GameTooltip:SetText(“myAddon”, 0.7, 0, 1)
GameTooltip:AddLine(" ", 1, 1, 1)
if IsShiftKeyDown() then
-- Show some more details, if the SHIFT key is pressed.
GameTooltip:AddLine(“Some detailed stuff about god and the world.”, 1, 1, 1)
GameTooltip:AddLine(“Even more great stuff to read.”, 0.9, 0.8, 0.1, 1)
else
-- Basic information to be displayed when no button is pressed.
GameTooltip:AddLine(“Some basic information. Yaaay!“, 1, 1, 1)
GameTooltip:AddLine(" ", 1, 1, 1)
GameTooltip:AddLine(“Hold SHIFT for more information…”, 0.5, 0.5, 0.5)
end
GameTooltip:Show()
end
But this only works if the SHIFT key is held before the mouse hovers the myAddonFrame. It also will display the detailed information even after releasing SHIFT as long as the mouse cursor remains on the myAddonFrame. But I want it dynamic!
My question here is:
How can I make the tooltip to refresh itself according to the SHIFT key when the mouse cursor stays on the frame?
I have in mind something like when I hover over an item in WoW and only as long as I hold the SHIFT key, that item will be compared with what I currently wear. I want exactly this effect, just within the same tooltip.
I hope somebody can push me in the right direction. I have tried using MODIFIER_STATE_CHANGED but I didn't get it working.
Oh, and while we are talking about toolips...here's a bonus question:
Is it possible to format them a bit? Bold, italics, fontsize, colorchanges for a single word in a line, etc. ? I didn't find anything particular here (wowprogramming.com), but maybe I overlooked it.
--- SOLVED ---
For the ones interested in the solution:
I used the code snippet of Nathanyel and adjusted it a bit. Because I use one XML file for all the frames and one corresponding LUA file for just the functions, I didn't wanted to create a new frame there. But I figgured out a way to use the existing myAddonFrame for the dynamic tooltip as well.
My code from above works perfectly and I just had to add the following:
-- [ALL THE CODE ABOVE!]
function myAddonFrame_OnLoad(frame)
frame:RegisterEvent("MODIFIER_STATE_CHANGED") -- Needed for the dynamic tool tip.
end
function myAddonFrame_OnEvent(frame, event, ...)
-- Fired, when any keyboard key is pressed.
local key, state = select(1, ...)
if (event == "MODIFIER_STATE_CHANGED") then
-- Switch the dynamic tooltip when the SHIFT key is held.
if myAddonFrame:IsMouseOver() and ((key == "LSHIFT") or (key == "RSHIFT")) then
myAddon_GenerateTooltip(frame)
end
end
end
That's it! Thanks to Nathanyel for the food for thought. :)

That event is indeed the key, but as you need to register it to a frame, preferably the one affected by the code, you should use a new frame for your tooltip:
local myFrame = CreateFrame("GameTooltip","myFrame",UIParent,"GameTooltipTemplate")
myFrame:SetScript("OnEvent",function(self, event, arg, ...)
if myFrame:IsShown()
and event == "MODIFIER_STATE_CHANGED"
and (arg = "LSHIFT" or arg = "RSHIFT") then
myAddon_GenerateTooltip() -- might need a parameter
end
end
myFrame:RegisterEvent("MODIFIER_STATE_CHANGED")
This simply re-populates the tooltip when either Shift key is pressed, and your function can react to the new state of the key.
Napkin code, slash some copy&paste from an addon where I used this method, so it might not be perfect, but enough to convey the concept.

Related

Can't figure out what to hook into in order to add text into GameTooltip when hovering over applicants in LFG

I'm making an addon that adds some information to the LFG both when applying to them and when picking applicants for your own group. There are different functions and different templates for the frames for searching LFG and hosting LFG. For searching, you can use hooksecurefunc on LFGListUtil_SetSearchEntryTooltip and that will allow you to get the information on whatever group the player is hovering over.
hooksecurefunc("LFGListUtil_SetSearchEntryTooltip", function(gametooltip, resultID, autoAcceptOption)
--debugPrint("LFGListUtil_SetSearchEntryTooltip")
local entry = C_LFGList.GetSearchResultInfo(resultID)
if not entry or not entry.leaderName then
return
end
local skill, attitude, note, playerNumber = self:getPlayer(self:addServerName(entry.leaderName))
--debugPrint("hooked correctly!")
GameTooltip:AddLine("\n")
if skill ~= 0 then
GameTooltip:AddDoubleLine("Skill:", tostring(skill), 1,0.82,0.0,1,1,1)
end
if attitude ~= 0 then
GameTooltip:AddDoubleLine("Attitude:", tostring(attitude), 1,0.82,0.0,1,1,1)
end
if note ~= "" then
GameTooltip:AddLine(note,1,1,1,true)
end
end)
This method seems to almost work when you're looking for applicants to your own group. According to Wow's UI source xml, the function "LFGListApplicantMember_OnEnter" triggers whenever you enter a frame made with the LFGListApplicantMemberTemplate. As far as I can tell from the LFGList's .lua file, all of the members of an applying group should have this template. However, it only triggers when hovering over the not-leading member of a group or when hovering over their role icons.
Is there some other function I'd be able to hook into in order to make it work on the leading group? I know it's possible to add text to the tooltip of a leading member because the RaiderIO addon does it, but I can't figure out how they do it. It needs to have some way of getting the name of the player you're hovering over to use my getPlayer function on it.
This is my current test function, though obviously it doesn't work
hooksecurefunc("LFGListApplicantMember_OnEnter", function(...)
GameTooltip:AddLine("")
GameTooltip:AddLine("TestText")
debugPrint("Hooked Successfully")
end)
and this is debugPrint
local function debugPrint(...)
print(...)
end

Only show title bar on floating windows

In awesome 4.0, is there a way to only display the titlebar on floating windows?
Looking at the docs, there doesn't seem to be an option out of the box.
To specify; I'm looking for a solution that work when I dynamically switch windows between tiling and floating.
A bit late, but I wanted to do this too and I got it mostly working. It doesn't cover all the cases when you'd expect a client to show or hide its titlebar, but it's close enough for my use case.
It's rather simple, first you need to disable titlebars for every client, so add titlebars_enabled = false in the properties of the default rule matching all clients.
Then, when a client becomes floating you need to toggle on his titlebar, and toggle it off when it stops floating.
I wrote this little helper function to make the code clearer. It's rather simple, if s is true then show the bar, hide it otherwise. But there's a catch, in our case the windows never had a titlebar so it isn't created yet. We send the signal to have one built for us if the current one is empty.
-- Toggle titlebar on or off depending on s. Creates titlebar if it doesn't exist
local function setTitlebar(client, s)
if s then
if client.titlebar == nil then
client:emit_signal("request::titlebars", "rules", {})
end
awful.titlebar.show(client)
else
awful.titlebar.hide(client)
end
end
Now we can hook the property change:
--Toggle titlebar on floating status change
client.connect_signal("property::floating", function(c)
setTitlebar(c, c.floating)
end)
But that only applies to clients that changes states after being created. We need a hook for new clients that are born floating or in a floating tag:
-- Hook called when a client spawns
client.connect_signal("manage", function(c)
setTitlebar(c, c.floating or c.first_tag.layout == awful.layout.suit.floating)
end)
And finally, if the current layout is floating, clients don't have the floating property set, so we need to add a hook for layout changes to add the tittlebars on clients inside.
-- Show titlebars on tags with the floating layout
tag.connect_signal("property::layout", function(t)
-- New to Lua ?
-- pairs iterates on the table and return a key value pair
-- I don't need the key here, so I put _ to ignore it
for _, c in pairs(t:clients()) do
if t.layout == awful.layout.suit.floating then
setTitlebar(c, true)
else
setTitlebar(c, false)
end
end
end)
I didn't want to spend to much time on this so it doesn't cover cases where a client gets tagged in a floating layout, or when a client is tagged multiple times and one of those tag is floating.
Change
{ rule_any = {type = { "normal", "dialog" }
}, properties = { titlebars_enabled = true }
},
to
{ rule_any = {type = { "dialog" }
}, properties = { titlebars_enabled = true }
},
Niverton's solution works very well for simply switching from tiling to floating modes; however, floating windows will lose their titlebar when maximized and then unmaximized. To fix this, a better solution would be to replace
client.connect_signal("property::floating", function(c)
setTitlebar(c, c.floating)
end)
with
client.connect_signal("property::floating", function(c)
setTitlebar(c, c.floating or c.first_tag and c.first_tag.layout.name == "floating")
end)
This should fix the issue so that windows can be properly maximized without having to switch to tiling mode and back to get the titlebars again.
I found this general idea on a reddit post about the subject, provided by u/Ham5andw1ch. I have just simplified the code using Niverton's proposed function and some short-circuit logic.

Disable/Enable button based on which control has focus

What I'm trying to do here is loop through the different controls and if any of the controls got focus to disable a button. This is a loop that I used for another task, so I'm kinda basing it off of this...
Dim cControl As Control
For Each cControl In Me.controls
If Not cControl.Container Is framClient Then
If Mid(cControl.Name, 1, 3) = "txt" Or Mid(cControl.Name, 1, 3) = "msk" Or Mid(cControl.Name, 1, 3) = "cbo" Then
'WHAT I'm trying to do here is
'if cControl.gotfocus then
'cmdExit.enabled=false
'end if ' but it barks at me
'cControl.Enabled = False
End If
End If
Next
I didn't make these text boxes in an array and I do not want to go through each text box to see if got focus and do whatever. Please take a look.
In vb6 only one control can have focus: the ActiveControl, so there's no point in looping and asking if the control has focus.
If you need to do something based on whether a certain control with a particular name has focus you could do this:
cmdExit.Enabled = (Me.ActiveControl.Name = "MyTextBox")
If you need to do it based on whether any TextBox has focus:
If TypeOf Me.ActiveControl Is TextBox Then
cmdExit.Enabled = false
Else
cmdExit.Enabled = true
End If
You can go ahead with Subclassing of text boxes as suggested in this SO Post.
As the post suggests, you have to do this exercise for every form where you have this requirement. I think, from your post that you have to do it only for one form.

call a function from inside a table that's inside another table in lua

I am attempting to build my first game using love2D, I have hit a problem.
The game is a bubble popping game, I want to assign a bubble to each letter on the keyboard so that when a letter is pressed, the bubble will pop.
I have an external file called "bubble.lua" which I have tried to make an object "bubble" with.
to do that I have created a table "bubble" in the bubble.lua which contains functions and variables. Now, this file works when called from main.lua using just one bubble, however I am going to need 26 bubbles so I thought it would be best to store each bubble in another table. For the purpose of trying this I just stored one bubble using 1 as the key.This is where I have problems.
require "bubble"
local bubbles = {}
function love.load()
bubbles[1] = bubble.load(100, 100)
end
function love.draw()
for bubble in bubbles do
bubble.draw()
end
end
function love.keypressed(key)
bubbles[key].bubble.pop()
end
Firstly, I know that the for loop in love.draw() does not work, and the line "bubble[key].bubble.pop" seems to return nil as well
The for loop I can probably find the solution myself online, my main problem is the "bubble[key].bubble.pop()" line, I cannot work out what's wrong or how to fix it.
Can anybody help me?
You may want to look at this as well:
bubble.lua
bubble = {}
function bubble.load(posX, posY)
bubble.x = posX
bubble.y = posY
bubble.popped = false
end
function bubble.draw()
if not bubble.popped then
love.graphics.rectangle("line", bubble.x, bubble.y, 37, 37)
else
love.graphics.rectangle("line", bubble.x, bubble.y, 37, 100)
end
end
function bubble.pop()
bubble.popped = true
end
Edit:
Following the advice of the answer below I now have the following error when I press "a":
main.lua:14: attempt to index a nil value
the updated code is below
main.lua
require "bubble"
local bubbles = {}
function love.load()
bubbles["a"] = bubble.load(100, 100)
end
function love.draw()
for key, bubble in pairs(bubbles) do
bubble.draw()
end
end
function love.keypressed(key)
bubbles[key].pop()
end
any thoughts?
There are several issues with this code. First, you index by number when you initialize bubbles (bubbles[1]), but access them using the key as the index (bubbles[key]), which is NOT a number. You need to settle on one mechanism to index the bubbles. Let's say you picked using key as the index (instead of the number).
This loop:
for bubble in bubbles do
bubble.draw()
end
should be written as:
for key, bubble in pairs(bubbles) do
bubble.draw()
end
and instead of bubbles[key].bubble.pop() you can simply do bubbles[key].pop() as bubbles[key] already returns the bubble you can pop.
To initialize, instead of bubbles[1] you need to do bubbles['a'] (or whatever other value is used by key in love.keypressed(key)).

World of Warcraft Lua - Changing frame:SetAttribute()

I'm working on an addon for World of Warcraft that completely overhauls the interface to adapt to my play style.
In this addon, I would like to have a large button that acts as a "main dps rotation" for my mage. I would like it to change what spell it casts based on what is optimal at any given time. It doesn't cast the spell automatically, it just presents the next best option for the user.
Here is my code so far:
print "Interface Overhaul : LOADED"
heatingUpIsActive = false
print(heatingUpIsActive)
local Button = CreateFrame("Button", "MyButton", UIParent,"SecureActionButtonTemplate")
Button:SetWidth(256)
Button:SetHeight(256)
Button:SetFrameStrata("HIGH")
Button:SetPoint("LEFT")
Button:SetText("Main Rotation")
Button:RegisterForClicks("AnyUp")
Button:SetAttribute("type", "spell")
Button:SetAttribute("spell", "Fireball")
Button:RegisterEvent("UNIT_AURA");
local function auraGained(self, event, ...)
if (UnitAura("player", "Heating Up")) then
if (heatingUpIsActive == false) then
heatingUpIsActive = true
print (heatingUpIsActive)
print ("Heating Up is active!")
Button:SetAttribute("spell", "Inferno Blast")
end
else
heatingUpIsActive = false
print("Heating Up is NOT active.")
print(heatingUpIsActive)
end
end
Button:SetScript("OnEvent", auraGained);
local tex = Button:CreateTexture("ARTWORK");
tex:SetPoint("LEFT")
tex:SetWidth(256)
tex:SetHeight(256)
tex:SetTexture("Interface\\AddOns\\InterfaceOverhaul\\Button2")
If heatingUpIsActive == true, I would like the button to cast ("spell", "Inferno Blast") instead of ("spell", "Fireball"), but it doesn't work if I place that into the correct part of the if statements.
Any thoughts?
As Mud said, you cannot rebind buttons in combat anymore. Blizzard made this change to prevent bots from being able to automate combat. Notably, in order to cast a spell you need to use one of the secure templates, and these secure templates only allow modification of the attributes that control what they do when you're not in combat. So you cannot have one button change spells mid-combat. Similarly, they also prevent you from modifying attributes like their position or visibility, so you cannot move buttons under the mouse either.
The best you can do is display a visual indicator of what spell should be cast, but rely on the user to actually press the correct button.

Resources