Tooltip = class(Turbine.UI.Control);
-- The 'content' argument can be:
-- * a one-line string, in which case an optimally-sized control will be created for it
-- * a control, something derived from Turbine.UI.Control
-- * a function that returns a string or a control, which will be called each time the Tooltip displays
-- If you want the content to be continuously updated while the tooltip is open, call Tooltip:SetWantsUpdates(true).
-- Otherwise, you can call Update() any time you want it to be updated.
-- A standard border will be added to the resulting control before display.
function Tooltip:Constructor(content)
Turbine.UI.Control.Constructor(self);
self.content = content;
if (content == nil) then
error("content is nil", 2);
end
-- Mouse behaviors to be added to the attached control
self._ShowTooltip = function()
self:SetVisible(true);
end
self._HideTooltip = function()
self:SetVisible(false);
end
self._MoveTooltip = function()
if (self.window) then
self:UpdateWindowPosition();
end
end
end
function Tooltip:CreateBorderControl()
local control = Turbine.UI.Control();
local parts = {
topLeft = { 0x4114061F, 10, 10, 0, 0 };
top = { 0x4114061D, -20, 7, 10, 0 };
topRight = { 0x41140618, 10, 10, -10, 0 };
left = { 0x4114061A, 7, -20, 0, 10 };
right = { 0x4114061E, 7, -20, -7, 10 };
bottomLeft = { 0x41140619, 10, 10, 0, -10 };
bottom = { 0x4114061C, -20, 7, 10, -7 };
bottomRight = { 0x41140620, 10, 10, -10, -10 };
}
for name, info in pairs(parts) do
local assetId, w, h, x, y = unpack(info);
local ctl = Turbine.UI.Control();
ctl:SetBackground(assetId);
ctl:SetParent(control);
ctl:SetBlendMode(Turbine.UI.BlendMode.AlphaBlend);
ctl:SetSize(w, h);
ctl.w, ctl.h, ctl.x, ctl.y = w, h, x, y;
end
control.SetSize = function(_, w, h)
Turbine.UI.Control.SetSize(control, w, h);
for c = 1, 8 do
local ctl = control:GetControls():Get(c);
ctl:SetSize((w + ctl.w) % w, (h + ctl.h) % h);
ctl:SetPosition((w + ctl.x) % w, (h + ctl.y) % h);
end
end
return control;
end
function Tooltip:AddBorder(content)
local border = self:CreateBorderControl();
if (type(content) == "string") then
-- Make an optimally-sized Label control to contain the text
local label = Turbine.UI.Label();
label:SetBackColor(Turbine.UI.Color(0.9, 0, 0, 0));
label:SetFont(Turbine.UI.Lotro.Font.TrajanPro16);
label:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
label:SetText(content);
label:AutoSize();
content = label;
self.simple = true;
else
self.simple = false;
end
content:SetParent(border);
content:SetPosition(3, 3);
border:SetSize(content:GetWidth() + 6, content:GetHeight() + 6);
return border;
end
function Tooltip:GetContent()
if (type(self.content) == "function") then
-- Content is a user-supplied function that returns a string or a Turbine.UI.Control
local result = self.content(self.attachment);
if (result == nil) then
error("tooltip function returned nil");
end
return self:AddBorder(result);
else
-- Content is a user-supplied string or Turbine.UI.Control
return self:AddBorder(self.content);
end
end
-- If the tooltip is currently being displayed, and 'content' is a function, it will be called again and the display updated.
function Tooltip:Update()
if (self.window) then
self:SetVisible(false);
self:SetVisible(true);
end
end
-- Note: This should only be called once; changing the attachment to a different control is not implemented.
function Tooltip:Attach(control)
if (control == nil) then
error("control is nil", 2);
end
self.attachment = control;
AddCallback(control, "MouseHover", self._ShowTooltip);
AddCallback(control, "MouseMove", self._MoveTooltip);
AddCallback(control, "MouseLeave", self._HideTooltip);
end
function Tooltip:Detach()
if (self.attachment) then
RemoveCallback(self.attachment, "MouseHover", self._ShowTooltip);
RemoveCallback(self.attachment, "MouseMove", self._MoveTooltip);
RemoveCallback(self.attachment, "MouseLeave", self._HideTooltip);
end
self.attachment = nil;
end
function Tooltip:SetVisible(visible)
if (visible) then
if (not self.window) then
self:CreateWindow();
end
else
if (self.window) then
self:DestroyWindow();
end
end
end
function Tooltip:CreateWindow()
local content = self:GetContent();
local width, height = content:GetSize();
local window = Turbine.UI.Window();
window:SetVisible(true);
content:SetParent(window);
window.content = content;
window:SetSize(width, height);
window:SetZOrder(2147483647); -- in front of everything, including Turbine tooltip (if any)
window.MouseEnter = function()
self:SetVisible(false);
end
self.window = window;
self:UpdateWindowPosition();
end
function Tooltip:DestroyWindow()
self.window.content:SetParent(nil);
self.window:SetVisible(false);
self.window:Close();
self.window = nil;
end
function Tooltip:UpdateWindowPosition()
local width, height = self.window:GetSize();
-- Determine optimal positioning of the tooltip
local displayWidth, displayHeight = Turbine.UI.Display.GetSize();
local x, y = Turbine.UI.Display.GetMousePosition();
local left, top = x + 30, y + 31;
if (left + width > displayWidth) then
left = displayWidth - width;
end
if (self.simple) then
-- For a simple one-line tooltip, we try to match Turbine's tooltip
-- location so we can cover it up (if it's showing).
if (top + height > displayHeight) then
top = displayHeight - height;
end
else
-- For multiline tooltips, use a similar strategy, but we try a little
-- harder to position the tooltip such that it doesn't overlap the
-- mouse cursor.
if (top + height > displayHeight) then
top = y - height - 20;
end
end
self.window:SetPosition(left, top);
end
Thurallor = Thurallor or {};
Thurallor.UI = Thurallor.UI or {};
Thurallor.UI.Tooltip = Tooltip;