Icon = class(Turbine.UI.Window)
local importPath = getfenv(1)._.Name;
local imagePath = string.gsub(string.gsub(importPath, "%.Icon$", ""), "%.", "/") .. "/Images/Gloradan/";
function Icon:Constructor(window)
Turbine.UI.Window.Constructor(self);
self:SetMouseVisible(false);
self.window = window;
self.mouseLeaveOpacity = 1;
self.num = 0;
self:Localize();
self:SetStretchMode(2); -- restore native size
-- Overlay is needed to work around some bugs related to stretched windows
self.overlay = Turbine.UI.Window();
self.overlay.MouseEnter = function(_, args) DoCallbacks(self, "_MouseEnter", args) end;
self.overlay.MouseHover = function(_, args) DoCallbacks(self, "_MouseHover", args) end;
self.overlay.MouseClick = function(_, args) DoCallbacks(self, "_MouseClick", args) end;
self.overlay.MouseDown = function(_, args) DoCallbacks(self, "_MouseDown", args) end;
self.overlay.MouseMove = function(_, args) DoCallbacks(self, "_MouseMove", args) end;
self.overlay.MouseUp = function(_, args) DoCallbacks(self, "_MouseUp", args) end;
self.overlay.MouseLeave = function(_, args) DoCallbacks(self, "_MouseLeave", args) end;
-- Add object for cascading ("bubbling up") alert messages
self.cascade = Thurallor.UI.Cascade();
local function progress(elapsedTime)
-- We want the messages to disappear after 3 seconds.
return elapsedTime / 3;
end
local function opacity(progress)
-- We want the messages to disappear close to the end of the cascade; higher power => later disappearance
-- Range: Opacity
return 1 - (progress ^ 5);
end
self.cascade:SetProgressFunc(progress);
self.cascade:SetOpacityFunc(opacity);
self.cascade:SetSize(1, Turbine.UI.Display:GetHeight() * 0.1);
self.cascade:SetMinimumDelay(0.3);
-- width, position and path function (up for down) are dependent on icon position; see UpdateCascadePosition()
-- Stretched windows need to be stretched again when the display size changes (another bug)
AddCallback(Turbine.UI.Display, "SizeChanged", function()
self:DisplaySizeChanged();
end);
self:SetVisible(true);
self:SetWantsKeyEvents(true); -- to respond to F12
end
function Icon:Localize()
local language = {
[Turbine.Language.English] = "en";
[Turbine.Language.French] = "en";
[Turbine.Language.German] = "de";
[Turbine.Language.Russian] = "en"; -- "ru"
}
language = language[L:GetLanguage()];
self.icons = {
Turbine.UI.Graphic(imagePath .. "1.tga"),
Turbine.UI.Graphic(imagePath .. "2.tga"),
Turbine.UI.Graphic(imagePath .. "3.tga"),
Turbine.UI.Graphic(imagePath .. "4.tga"),
Turbine.UI.Graphic(imagePath .. "5.tga"),
Turbine.UI.Graphic(imagePath .. "6.tga"),
Turbine.UI.Graphic(imagePath .. "7.tga"),
Turbine.UI.Graphic(imagePath .. "8.tga"),
Turbine.UI.Graphic(imagePath .. "9.tga"),
Turbine.UI.Graphic(imagePath .. "9+.tga"),
Turbine.UI.Graphic(imagePath .. "0_" .. language ..".tga")
}
self:SetDisplayedNumber(self.num);
end
function Icon:SetZOrder(zOrder)
Turbine.UI.Window.SetZOrder(self, zOrder);
self.overlay:SetZOrder(zOrder);
self.cascade:SetZOrder(zOrder);
end
function Icon:Activated()
-- Apparently, calling "self.overlay:Activate()" here causes the client to crash
-- at logout. Therefore we'll postpone it until the next frame refresh.
--self.overlay:Activate();
self.activating = true;
self:SetWantsUpdates(true);
end
function Icon:KeyDown(args)
if (args.Action == Turbine.UI.Lotro.Action.ToggleHUD) then
self.window.hudVisible = not self.window.hudVisible;
self.window:SetVisible(self.window:IsVisible());
self:SetVisible(self.visible);
end
end
function Icon:SetDisplayedNumber(num)
if (num > 10) then
num = 10;
end
self.num = num;
if (num == 0) then
self:SetBackground(self.icons[11]);
else
self:SetBackground(self.icons[num]);
end
end
function Icon:GetDisplayedNumber()
return self.num;
end
function Icon:SetTooltip(tooltip)
Thurallor.UI.Tooltip(tooltip):Attach(self.overlay);
end
function Icon:SetVisible(visible)
self.visible = visible;
visible = visible and self.window.hudVisible;
Turbine.UI.Window.SetVisible(self, visible);
self.overlay:SetVisible(visible);
if (visible) then
self:Activate();
end
end
function Icon:SetLeft(left)
self:SetPosition(left, self:GetTop());
end
function Icon:SetTop(top)
self:SetPosition(self:GetLeft(), top);
end
function Icon:SetPosition(left, top)
self:SetPositionOnScreen(left, top);
-- Update fractional screen coordinates
local width, height = self:GetSize();
local center = left + width / 2;
local middle = top + height / 2;
local screenWidth, screenHeight = Turbine.UI.Display:GetSize();
self.x = center / screenWidth;
self.y = middle / screenHeight;
end
-- like SetPosition(), but won't allow you to move the icon off-screen.
function Icon:SetPositionOnScreen(left, top)
local screenWidth, screenHeight = Turbine.UI.Display:GetSize();
local width, height = self:GetSize();
margin = 0.1;
left = math.max(0 - margin * width, left);
top = math.max(0 - margin * height, top);
left = math.min(screenWidth - width * (1 - margin), left);
top = math.min(screenHeight - height * (1 - margin), top);
Turbine.UI.Window.SetPosition(self, left, top);
self.overlay:SetPosition(left, top);
self:UpdateCascadePosition();
end
-- Update cascade position and pathFunc
function Icon:UpdateCascadePosition()
local screenWidth, screenHeight = Turbine.UI.Display:GetSize();
local center, middle = screenWidth * self.x, screenHeight * self.y;
if (self.x < 0.5) then
self.cascade:SetLeft(center);
self.cascade:SetWidth(screenWidth - center);
else
self.cascade:SetLeft(0);
self.cascade:SetWidth(center);
end
if (self.y < 0.10) then
self.cascade:SetTop(middle);
self.cascade:SetPathFunc(
function(progress)
-- We want the messages to seem to "pop" out and bubble downwards.
-- Range: y = 100% (progress = 0) down to 0% (progress = 1)
local z = 1 / (progress + 1);
local y = 2 * z - 1;
return 0.5, 1 - y;
end
);
else
self.cascade:SetTop(middle - self.cascade:GetHeight());
self.cascade:SetPathFunc(
function(progress)
-- We want the messages to seem to "pop" out and bubble upwards.
-- Range: y = 100% (progress = 0) down to 0% (progress = 1)
local z = 1 / (progress + 1);
local y = 2 * z - 1;
return 0.5, y;
end
);
end
end
function Icon:SetFractionalPosition(x, y)
self.x, self.y = x, y;
local width, height = self:GetSize();
local screenWidth, screenHeight = Turbine.UI.Display:GetSize();
local center = screenWidth * x;
local middle = screenHeight * y;
local left = math.floor(0.5 + center - width / 2);
local top = math.floor(0.5 + middle - height / 2);
self:SetPositionOnScreen(left, top);
end
function Icon:GetFractionalPosition()
return self.x, self.y;
end
function Icon:DisplaySizeChanged()
self:SetFractionalPosition(self.x, self.y);
self:SetSize(self:GetSize()); -- workaround for bug that resets stretching when screen is resized
self.cascade:SetHeight(Turbine.UI.Display:GetHeight() * 0.1);
end
function Icon:Iconify()
self.window:SetVisible(false);
self.zoomer = Thurallor.UI.Zoomer(self.window, self);
end
function Icon:Restore()
local winSize = { self.window:GetSize() };
local winPos = { self.window:GetPosition() };
local iconSize = { self:GetSize() };
local iconPos = { self:GetPosition() };
self.zoomer = Thurallor.UI.Zoomer(self, self.window);
self.zoomer.ZoomComplete = function()
self.window:SetVisible(true);
self.zoomer = nil;
end
end
function Icon:_MouseEnter()
self.mouseInside = true;
self:SetOpacity(1);
end
function Icon:_MouseLeave()
self.mouseInside = false;
self:SetOpacity(self.mouseLeaveOpacity);
end
function Icon:SetMouseLeaveOpacity(opacity)
self.mouseLeaveOpacity = opacity;
self:SetOpacity((self.mouseInside and 1) or self.mouseLeaveOpacity);
end
function Icon:_MouseClick(args)
if ((args.Button == Turbine.UI.MouseButton.Left) and not self.moved) then
if (self:IsWindow()) then
self:Iconify();
else
self:Restore();
end
end
end
function Icon:_MouseDown(args)
self:Activate(); -- bring to front
if (args.Button == Turbine.UI.MouseButton.Left) then
self.mouseDown = { Turbine.UI.Display:GetMousePosition() };
self.moved = nil;
end
end
function Icon:_MouseMove()
if (self.mouseDown) then
self.moved = true;
local oldX, oldY = unpack(self.mouseDown);
local newX, newY = Turbine.UI.Display:GetMousePosition();
local left, top = self:GetPosition();
self:SetPosition(left + newX - oldX, top + newY - oldY);
self.mouseDown = { newX, newY };
end
end
function Icon:_MouseUp(args)
if (args.Button == Turbine.UI.MouseButton.Left) then
if (self.moved) then
DoCallbacks(self, "IconMoved");
end
self.mouseDown = nil;
end
end
function Icon:Update()
-- If user clicked on the icon, activate the overlay to bring it back in front.
-- Done here instead of Icon:Activated() to work around a client bug.
if (self.activating) then
self.overlay:Activate();
self.activating = nil;
end
self:SetWantsUpdates(false);
end
function Icon:SetSize(width, height)
Turbine.UI.Window.SetSize(self, width, height);
self.overlay:SetSize(width, height);
end
function Icon:GetSize()
-- Workaround for the bug that prevents GetSize() from working for stretched windows
return self.overlay:GetSize();
end
function Icon:GetWidth()
-- Workaround for the bug that prevents GetSize() from working for stretched windows
return self.overlay:GetWidth();
end
function Icon:GetHeight()
-- Workaround for the bug that prevents GetSize() from working for stretched windows
return self.overlay:GetHeight();
end
function Icon:IsIcon()
return not self:IsWindow();
end
function Icon:IsWindow()
return self.window:IsVisible();
end
function Icon:DoShockwave()
if (self.shockwave) then
self.shockwave:Close();
end
self.shockwave = Thurallor.UI.Shockwave();
local left, top = self.overlay:GetPosition();
local width, height = self.overlay:GetSize();
local x = left + math.floor(width / 2 + 0.5);
local y = top + math.floor(height / 2 + 0.5);
self.shockwave:SetCenter(x, y);
self.shockwave:SetDiameter(width);
self.shockwave:Explode();
AddCallback(self.shockwave, "Closing", function()
self.shockwave = nil;
end);
end
function Icon:AddCascadeMessage(text, color)
-- Add cascade message
local win = Turbine.UI.Window();
local ctl = Turbine.UI.Label();
ctl:SetParent(win);
ctl:SetForeColor(color);
ctl:SetFont(Turbine.UI.Lotro.Font.Verdana20);
ctl:SetFontStyle(Turbine.UI.FontStyle.Outline);
if (self.x < 0.5) then
ctl:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
else
ctl:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleRight);
end
ctl:SetMultiline(false);
ctl:SetText(text);
ctl:SetWidth(self.cascade:GetWidth());
win:SetSize(ctl:GetSize());
ctl:SetMouseVisible(false);
win:SetMouseVisible(false);
self.cascade:AddItem(win);
end