WoW:UIOBJECT GameTooltip: Difference between revisions

From AddOn Studio
Jump to navigation Jump to search
m (Move page script moved page UIOBJECT GameTooltip to UIOBJECT GameTooltip without leaving a redirect)
 
(17 intermediate revisions by 16 users not shown)
Line 1: Line 1:
{{widget}}
UI Lua API for a GameTooltip frame type. WoW also creates a default game tool tip frame named "GameTooltip' using this type, which displays the regular in-game tool tip. See also [[XML/GameTooltip]].


== Summary ==
Tooltips are notoriously difficult to handle in WoW UI code, as the interaction of GameTooltip methods can be quite complicated. This page has some examples for the use of tooltips, and a collection of advanced points to note about the finer details. For an indepth analysis of GameTooltip methods, see [[Tooltip Pseudo Code]] for a Lua-ish pseudo code representation of every GameTooltip method. See [[Widget API]] for a reference list of all GameTooltip methods.
== Blizzard's GameTooltip ==
The default Blizzard interface code ([[FrameXML]]) defines a GameTooltip object called GameTooltip (confusing, eh). This is used as the game's main tooltip. It consists of a number of lines; to determine how many there are, you can call the [[API GameToolTip NumLines|GameTooltip:NumLines()]] function.
The game engine assumes that there is a GameTooltip object called GameTooltip, and that it has a number of known fontstrings in it. The GameTooltip:Set___() methods manipulate the object and its children directly and then show the tooltip object. When you mouse over things in the game world, the game engine internally calls the equivalent of :Set____() - not the actual Lua method. In particular, this means that hooked GameTooltip methods will never trigger for engine-generated tooltips. Hooks will however catch all other tooltips generated from the UI itself. For non-item/unit tooltips you will even see :AddLine and :AddDoubleLine calls.
=== GameTooltipTextLeft<num> ===
Each line of text from the game tooltip is represented by one of these objects, replace <num> with the appropriate line number (from 1 to the number of lines returned from GameTooltip:NumLines()). To obtain the text of a line call the [[API FontString GetText|GameTooltipTextLeft''<num>'':GetText()]] function.
:''Important: this is currently bugged. When a tooltip has 9 or more FontString GameTooltipTextLeft9 and GameTooltipTextRight9 do not exist. These FontStrings are incorrectly named GameTooltipTextLeft1 and GameTooltipTextRight1 duplicating those of the first line. The only reliable way to get the information out of the tooltip is to iterate through its child regions with GameTooltip:GetRegions().''
;Example
Looping through all tooltip lines
<pre>
local function EnumerateTooltipLines_helper(...)
    for i = 1, select("#", ...) do
        local region = select(i, ...)
        if region and region:GetObjectType() == "FontString" then
            local text = region:GetText() -- string or nil
        end
    end
end
function EnumerateTooltipLines(tooltip) -- good for script handlers that pass the tooltip as the first argument.
    EnumerateTooltipLines_helper(tooltip:GetRegions())
end
</pre>
;Macro Equivalent
/run for i = 1, GameTooltip:NumLines() do local mytext = _G["GameTooltipTextLeft"..i] local text = mytext:GetText(); print(text) end
;Notes
* The tooltip displayed when the mouse is hovering over players and/or NPCs on the minimap is such that all the units being displayed are actually only on the first tooltip line.  So, if the tooltip showed 4 players, the names are all in the first tooltip line, delimited by "\n".
=== GameTooltipTextRight&lt;num&gt; ===
Some tooltips have both left- and right-aligned sections. In order to get at the right-aligned section, use one of these objects instead of a GameTooltipTextLeft. Otherwise, the functionality is identical.
== Hidden tooltip for scanning ==
One of the most common uses of a tooltip is to use it to gain information text about an item, buff or spell. Many AddOns define their own tooltip for this purpose. This is a description of the best practice to follow when using a tooltip for this purpose, taking into account the need for performance.
The definition of the tooltip in your XML should look like this:
<GameTooltip name="MyScanningTooltip" inherits="GameTooltipTemplate">
  <Scripts>
    <Onload>
      self:SetOwner(WorldFrame, "ANCHOR_NONE");
    </Onload>
  </Scripts>
</GameTooltip>
When actually using the tooltip in your code, you need to do:
MyScanningTooltip:ClearLines()
MyScanningTooltip:SetX(arguments)
where SetX could be SetInventorySlot, SetBagItem, SetAction, SetUnitBuff etc. The explicit call to MyScanningTooltip:ClearLines() is necessary becuase a SetX call usually doesn't clear the tooltip if called with invalid arguments, e.g. an empty bag slot or nonexistent buff. Care must be taken so that MyScanningTooltip:Show() or MyScanningTooltip:Hide() doesn't get called at any time, otherwise the tooltip becomes unusable. Note that the way SetOwner was called, the tooltip will never be visible on the screen, and therefore it's not necessary to hide it.
=== Alternative solution ===
Instead of the above, here is an alternative solution. It is sufficient to have this in your XML:
<GameTooltip name="MyScanningTooltip" inherits="GameTooltipTemplate"/>
Each time you want to use the tooltip however, you must explicitly set the owner like this:
MyScanningTooltip:SetOwner(UIParent, "ANCHOR_NONE")
MyScanningTooltip:SetX(arguments)
... extract information from the tooltip ...
MyScanningTooltip:Hide()
This solution is safer and more robust to potential changes to tooltip behaviour in future patches. However, this solution incurs significant performance penalty. Repeatedly calling MyScanningTooltip:SetOwner in an OnUpdate handler can cause significant frame-rate drop. Hence this solution is recommended for tooltip scans that occur in response to for example key presses, whereas the previous solution is recommended for repeated, frequent scans.
=== Pure Lua compact solution ===
To create a GameTooltip without the overhead of inherited GameTooltipTemplate properties, a minimal scanning tooltip can be created in pure Lua as follows:
CreateFrame( "GameTooltip", "MyScanningTooltip", nil, "GameTooltipTemplate" ); -- Tooltip name cannot be nil
MyScanningTooltip:SetOwner( WorldFrame, "ANCHOR_NONE" );
-- Allow tooltip SetX() methods to dynamically add new lines based on these
MyScanningTooltip:AddFontStrings(
    MyScanningTooltip:CreateFontString( "$parentTextLeft1", nil, "GameTooltipText" ),
    MyScanningTooltip:CreateFontString( "$parentTextRight1", nil, "GameTooltipText" ) );
The resulting tooltip will not contain a status bar, icons, backdrop, or any event handlers.  Like the other ways to create scanning tooltips, you should call <tt>MyScanningTooltip:ClearLines()</tt> before calling any <tt>MyScanningTooltip:SetX()</tt> methods.
''Note that using this method, tooltip icons (such as sockets) will not be added.''
== Advanced Notes on GameTooltip methods ==
This is a list of the finer details of using tooltips correctly:
* Unlike other UI objects, a tooltip has both a parent ''and'' an owner, the two are completely independent.
* A tooltip must have an owner to be usable. Therefore you must call SetOwner at least once in the lifetime of a tooltip before you can use it.
* SetOwner clears tooltip contents and hides the tooltip, making it ready for a SetX call.
* If ANCHOR_NONE is used in SetOwner, and you call SetX without setting the anchors manually first, the equivalent of SetPoint("TOPLEFT", UIParent, #INF, 0) happens (regardless of the ClampedToScreen property), independent of who the owner is.  This means the tooltip is positioned off-screen, but its contents can be altered as normal.
* SetX erases the existing contents of the tooltip.
* SetX with valid data causes Show to be called.
* SetX with invalid data may or may not clear the tooltip's contents. (e.g. SetBagItem/SetInventorySlot with empty slot leaves tooltip unchanged, but SetAction with empty slot clears its contents).
* Show (and therefore SetX) causes the size of the tooltip to be reevaluated.
* Show (and therefore SetX) causes the anchors of the tooltip to be reset to the one specified in SetOwner, unless it was ANCHOR_NONE.
* Hide clears the contents of the tooltip and removes its owner. After a Hide, SetOwner must be called again.
* The contents of a hidden tooltip can not be altered using SetX.
* AddLine and AddDoubleLine do not call Show; you need to call it manually.
* AppendText does call Show.
* ClearLines clears lines by setting all TextLeftX fontstrings to hidden and nil text, and all TextRightX fontstrings to hidden (but leaves text of those intact).
* Calling Show on a tooltip with zero lines (e.g. immediately after ClearLines) hides it and removes its owner, and therefore makes the tooltip unusable.
* Hiding the parent of the tooltip clears it and removes its owner. SetOwner must be called again.
* Showing the parent of the tooltip causes Show to be called on the tooltip.
* It is therefore recommended practice to not set a parent to your tooltip, and especially not UIParent.
* A tooltip that has a parent="<nowiki><parentname></nowiki>" specified in its XML definition can't call SetOwner in the tooltip's <nowiki><OnLoad></nowiki> handler. (unconfirmed, seems to be true in some cases, so best avoid it)

Latest revision as of 04:49, 15 August 2023

Widget API < GameTooltip

UI Lua API for a GameTooltip frame type. WoW also creates a default game tool tip frame named "GameTooltip' using this type, which displays the regular in-game tool tip. See also XML/GameTooltip.

Summary[edit]

Tooltips are notoriously difficult to handle in WoW UI code, as the interaction of GameTooltip methods can be quite complicated. This page has some examples for the use of tooltips, and a collection of advanced points to note about the finer details. For an indepth analysis of GameTooltip methods, see Tooltip Pseudo Code for a Lua-ish pseudo code representation of every GameTooltip method. See Widget API for a reference list of all GameTooltip methods.

Blizzard's GameTooltip[edit]

The default Blizzard interface code (FrameXML) defines a GameTooltip object called GameTooltip (confusing, eh). This is used as the game's main tooltip. It consists of a number of lines; to determine how many there are, you can call the GameTooltip:NumLines() function.

The game engine assumes that there is a GameTooltip object called GameTooltip, and that it has a number of known fontstrings in it. The GameTooltip:Set___() methods manipulate the object and its children directly and then show the tooltip object. When you mouse over things in the game world, the game engine internally calls the equivalent of :Set____() - not the actual Lua method. In particular, this means that hooked GameTooltip methods will never trigger for engine-generated tooltips. Hooks will however catch all other tooltips generated from the UI itself. For non-item/unit tooltips you will even see :AddLine and :AddDoubleLine calls.

GameTooltipTextLeft<num>[edit]

Each line of text from the game tooltip is represented by one of these objects, replace <num> with the appropriate line number (from 1 to the number of lines returned from GameTooltip:NumLines()). To obtain the text of a line call the GameTooltipTextLeft<i><num></i>:GetText() function.

Important: this is currently bugged. When a tooltip has 9 or more FontString GameTooltipTextLeft9 and GameTooltipTextRight9 do not exist. These FontStrings are incorrectly named GameTooltipTextLeft1 and GameTooltipTextRight1 duplicating those of the first line. The only reliable way to get the information out of the tooltip is to iterate through its child regions with GameTooltip:GetRegions().
Example

Looping through all tooltip lines

local function EnumerateTooltipLines_helper(...)
    for i = 1, select("#", ...) do
        local region = select(i, ...)
        if region and region:GetObjectType() == "FontString" then
            local text = region:GetText() -- string or nil
        end
    end
end

function EnumerateTooltipLines(tooltip) -- good for script handlers that pass the tooltip as the first argument.
    EnumerateTooltipLines_helper(tooltip:GetRegions())
end
Macro Equivalent
/run for i = 1, GameTooltip:NumLines() do local mytext = _G["GameTooltipTextLeft"..i] local text = mytext:GetText(); print(text) end
Notes
  • The tooltip displayed when the mouse is hovering over players and/or NPCs on the minimap is such that all the units being displayed are actually only on the first tooltip line. So, if the tooltip showed 4 players, the names are all in the first tooltip line, delimited by "\n".

GameTooltipTextRight<num>[edit]

Some tooltips have both left- and right-aligned sections. In order to get at the right-aligned section, use one of these objects instead of a GameTooltipTextLeft. Otherwise, the functionality is identical.

Hidden tooltip for scanning[edit]

One of the most common uses of a tooltip is to use it to gain information text about an item, buff or spell. Many AddOns define their own tooltip for this purpose. This is a description of the best practice to follow when using a tooltip for this purpose, taking into account the need for performance.

The definition of the tooltip in your XML should look like this:

<GameTooltip name="MyScanningTooltip" inherits="GameTooltipTemplate">
  <Scripts>
    <Onload>
      self:SetOwner(WorldFrame, "ANCHOR_NONE");
    </Onload>
  </Scripts>
</GameTooltip>


When actually using the tooltip in your code, you need to do:

MyScanningTooltip:ClearLines()
MyScanningTooltip:SetX(arguments)

where SetX could be SetInventorySlot, SetBagItem, SetAction, SetUnitBuff etc. The explicit call to MyScanningTooltip:ClearLines() is necessary becuase a SetX call usually doesn't clear the tooltip if called with invalid arguments, e.g. an empty bag slot or nonexistent buff. Care must be taken so that MyScanningTooltip:Show() or MyScanningTooltip:Hide() doesn't get called at any time, otherwise the tooltip becomes unusable. Note that the way SetOwner was called, the tooltip will never be visible on the screen, and therefore it's not necessary to hide it.

Alternative solution[edit]

Instead of the above, here is an alternative solution. It is sufficient to have this in your XML:

<GameTooltip name="MyScanningTooltip" inherits="GameTooltipTemplate"/>

Each time you want to use the tooltip however, you must explicitly set the owner like this:

MyScanningTooltip:SetOwner(UIParent, "ANCHOR_NONE")
MyScanningTooltip:SetX(arguments)
... extract information from the tooltip ...
MyScanningTooltip:Hide()

This solution is safer and more robust to potential changes to tooltip behaviour in future patches. However, this solution incurs significant performance penalty. Repeatedly calling MyScanningTooltip:SetOwner in an OnUpdate handler can cause significant frame-rate drop. Hence this solution is recommended for tooltip scans that occur in response to for example key presses, whereas the previous solution is recommended for repeated, frequent scans.

Pure Lua compact solution[edit]

To create a GameTooltip without the overhead of inherited GameTooltipTemplate properties, a minimal scanning tooltip can be created in pure Lua as follows:

CreateFrame( "GameTooltip", "MyScanningTooltip", nil, "GameTooltipTemplate" ); -- Tooltip name cannot be nil
MyScanningTooltip:SetOwner( WorldFrame, "ANCHOR_NONE" );
-- Allow tooltip SetX() methods to dynamically add new lines based on these
MyScanningTooltip:AddFontStrings(
    MyScanningTooltip:CreateFontString( "$parentTextLeft1", nil, "GameTooltipText" ),
    MyScanningTooltip:CreateFontString( "$parentTextRight1", nil, "GameTooltipText" ) );

The resulting tooltip will not contain a status bar, icons, backdrop, or any event handlers. Like the other ways to create scanning tooltips, you should call MyScanningTooltip:ClearLines() before calling any MyScanningTooltip:SetX() methods.

Note that using this method, tooltip icons (such as sockets) will not be added.

Advanced Notes on GameTooltip methods[edit]

This is a list of the finer details of using tooltips correctly:

  • Unlike other UI objects, a tooltip has both a parent and an owner, the two are completely independent.
  • A tooltip must have an owner to be usable. Therefore you must call SetOwner at least once in the lifetime of a tooltip before you can use it.
  • SetOwner clears tooltip contents and hides the tooltip, making it ready for a SetX call.
  • If ANCHOR_NONE is used in SetOwner, and you call SetX without setting the anchors manually first, the equivalent of SetPoint("TOPLEFT", UIParent, #INF, 0) happens (regardless of the ClampedToScreen property), independent of who the owner is. This means the tooltip is positioned off-screen, but its contents can be altered as normal.
  • SetX erases the existing contents of the tooltip.
  • SetX with valid data causes Show to be called.
  • SetX with invalid data may or may not clear the tooltip's contents. (e.g. SetBagItem/SetInventorySlot with empty slot leaves tooltip unchanged, but SetAction with empty slot clears its contents).
  • Show (and therefore SetX) causes the size of the tooltip to be reevaluated.
  • Show (and therefore SetX) causes the anchors of the tooltip to be reset to the one specified in SetOwner, unless it was ANCHOR_NONE.
  • Hide clears the contents of the tooltip and removes its owner. After a Hide, SetOwner must be called again.
  • The contents of a hidden tooltip can not be altered using SetX.
  • AddLine and AddDoubleLine do not call Show; you need to call it manually.
  • AppendText does call Show.
  • ClearLines clears lines by setting all TextLeftX fontstrings to hidden and nil text, and all TextRightX fontstrings to hidden (but leaves text of those intact).
  • Calling Show on a tooltip with zero lines (e.g. immediately after ClearLines) hides it and removes its owner, and therefore makes the tooltip unusable.
  • Hiding the parent of the tooltip clears it and removes its owner. SetOwner must be called again.
  • Showing the parent of the tooltip causes Show to be called on the tooltip.
  • It is therefore recommended practice to not set a parent to your tooltip, and especially not UIParent.
  • A tooltip that has a parent="<parentname>" specified in its XML definition can't call SetOwner in the tooltip's <OnLoad> handler. (unconfirmed, seems to be true in some cases, so best avoid it)