SequenceEditor = class(Turbine.UI.Lotro.Window);
function SequenceEditor:Constructor(bar, settings)
Turbine.UI.Lotro.Window.Constructor(self);
self.bar = bar;
self.manager = self.bar.manager;
self.settings = settings;
self:SetText(bar:GetName());
self:SetZOrder(2147483647 - 5);
self:SetResizable(true);
self:SetVisible(true);
if (not self.settings.sequenceEditor) then
self.settings.sequenceEditor = {};
end
if (self.settings.sequenceEditor.position) then
self:SetPosition(unpack(self.settings.sequenceEditor.position));
else
self:SetPosition(self.bar:GetPosition());
end
if (self.settings.sequenceEditor.size) then
self:SetSize(unpack(self.settings.sequenceEditor.size));
else
self:SetSize(470, 187);
end
self:Redraw();
end
function SequenceEditor:SetText(barName)
local title = L:GetText("/SequenceEditor/Title");
title = string.gsub(title, "<name>", barName);
Turbine.UI.Lotro.Window.SetText(self, title);
end
function SequenceEditor:SaveSettings()
self.bar:SaveSettings();
end
function SequenceEditor:Redraw()
local width, height = self:GetSize();
-- Enforce minimum size of 468 x 152.
self:SetMinimumWidth(470);
self:SetMinimumHeight(152);
local xMargin = 16;
local topMargin = 35;
local bottomMargin = 42;
if (not self.slotTabCard) then
self.slotTabCard = Thurallor.UI.TabCard();
self.slotTabCard:SetParent(self);
self.slotTabCard:SetTop(topMargin);
self.slotTabCard:SetWidth(200);
self.slotTabCard:SetTabWidth(120);
self.slotTabCard:SetTabText(L:GetText("/SequenceEditor/SlotTab"));
end
self.slotTabCard:SetLeft(width - 200 - xMargin);
self.slotTabCard:SetHeight(height - topMargin - bottomMargin);
if (not self.sequenceTabCard) then
self.sequenceTabCard = Thurallor.UI.TabCard();
self.sequenceTabCard:SetParent(self);
self.sequenceTabCard:SetPosition(xMargin, topMargin);
self.sequenceTabCard:SetTabWidth(120);
self.sequenceTabCard:SetTabText(L:GetText("/SequenceEditor/SequenceTab"));
self.slotPanel = Turbine.UI.Label();
end
self.sequenceTabCard:SetSize(self.slotTabCard:GetLeft() - xMargin - 8, self.slotTabCard:GetHeight());
self:DeleteEmptyTrailingSlots();
local slotPanelWidth = self.sequenceTabCard:GetWidth() - 17;
local slotSize = self.settings.slotSize + self.settings.slotSpacing;
self.slotsWide = math.floor((slotPanelWidth - 3) / slotSize);
local slotsHigh = math.max(math.ceil(#self.settings.sequenceItemInfo / self.slotsWide) + 1, 2);
self:ExtendSequenceTo(slotsHigh * self.slotsWide);
self.slotPanel:SetSize(self.slotsWide * slotSize + 3, slotsHigh * slotSize + 3);
self.sequenceTabCard:SetInteriorControl(self.slotPanel);
self.slotPanel:SetBackColor(self.bar.color);
-- Draw some quickslots
if (not self.sequenceItems) then
self.sequenceItems = {};
end
local s = 1;
for y = 1, slotsHigh, 1 do
for x = 1, self.slotsWide, 1 do
self:RebuildSlot(s, x, y);
s = s + 1;
end
end
if (not self.instructions) then
self.instructions = Turbine.UI.Label();
self.instructions:SetParent(self);
self.instructions:SetMouseVisible(false);
self.instructions:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
self.instructions:SetFont(Turbine.UI.Lotro.Font.TrajanPro15);
self.instructions:SetMultiline(true);
self.instructions:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
self.instructions:SetText(L:GetText("/SequenceEditor/Instructions"));
end
self.instructions:SetSize(self:GetWidth() - 2 * xMargin, 30);
self.instructions:SetPosition(xMargin, self:GetHeight() - 40);
if (self.Marquee) then
self:SelectSlot(self.Marquee.s);
end
end
function SequenceEditor:SizeChanged()
self:Redraw();
self.settings.sequenceEditor.size = {self:GetSize()};
self:SaveSettings();
end
function SequenceEditor:PositionChanged()
self.settings.sequenceEditor.position = {self:GetPosition()};
self:SaveSettings();
end
function SequenceEditor:DisplaySlot(s)
self.slotTabCard:SetInteriorControl(nil);
self.slotTabCard:SetTabText(L:GetText("/SequenceEditor/SlotTab"));
if (s == nil) then
return;
end
self:ExtendSequenceTo(s);
local info = self.settings.sequenceItemInfo[s];
if (not info.class) then
-- Empty slot should be rendered as a standard quickslot
info.class = "Turbine.UI.Lotro.Quickslot";
info.temporary = true;
end
-- Update slot tab caption and create properties container
self.slotTabCard:SetTabText(L:GetText("/SequenceEditor/SlotTab") .. " " .. tostring(s));
self.slotProperties = Turbine.UI.Control();
self.slotTabCard:SetInteriorControl(self.slotProperties);
local width = self.slotTabCard:GetWidth() - 17;
self.slotProperties:SetWidth(width);
--self.slotProperties:SetBackColor(Turbine.UI.Color(1, 0.25, 0.25, 0.25));
-- Create the icon
local object = self:BuildSlot(s, self.slotProperties, nil, 72, 10);
object.numLabel:SetText(nil); -- don't display slot number overlay
object.numLabel.MouseClick = nil; -- disable right-click menu
-- Create a function for adding a centered line of text to the properties control
local AddLabel = function(top, text, color)
local label = Turbine.UI.Label();
label:SetParent(self.slotProperties);
label:SetPosition(0, top);
label:SetSize(180, 16);
top = top + 16;
label:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
label:SetFont(Turbine.UI.Lotro.Font.TrajanPro14);
label:SetForeColor(color);
label:SetMarkupEnabled(true);
label:SetText(text);
return top, label;
end
-- Create a function for adding a centered text box to the properties control
local AddTextBox = function(top, text, color)
local textBox = Turbine.UI.Lotro.TextBox();
textBox:SetParent(self.slotProperties);
textBox:SetPosition(0, top);
textBox:SetSize(180, 20);
top = top + 20;
textBox:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleLeft);
textBox:SetFont(Turbine.UI.Lotro.Font.TrajanPro14);
textBox:SetForeColor(color);
textBox:SetText(text);
return top, textBox;
end
-- Draw the caption
local top = 54;
if (info.class == "Turbine.UI.Lotro.Quickslot") then
top = AddLabel(top, L:GetText("/SequenceEditor/StandardQuickslot"), Turbine.UI.Color.PaleGoldenrod);
elseif (info.class == "Turbine.UI.Control") then
top = AddLabel(top, L:GetText("/SequenceEditor/SpecialSlot") .. ":", Turbine.UI.Color.PaleGoldenrod);
top = AddLabel(top, info.toolTip, Turbine.UI.Color.PaleGoldenrod);
end
-- Draw the arguments
if (info.class == "Turbine.UI.Lotro.Quickslot") then
top = top + 16; -- skip a line
for key, value in pairs(Turbine.UI.Lotro.ShortcutType) do
if (info.type == value) then
top = AddLabel(top, L:GetText("/SequenceEditor/ShortcutType") .. ": " .. L:GetText("/SequenceEditor/ShortcutTypes/" .. key), Turbine.UI.Color.Goldenrod);
break;
end
end
elseif (info.class == "Turbine.UI.Control") then
top = top + 16; -- skip a line
if (info.type == "GenerateEvent") then
top = AddLabel(top, L:GetText("/SequenceEditor/EventName"), Turbine.UI.Color.Goldenrod);
local textBox;
top, textBox = AddTextBox(top, info.eventName, Turbine.UI.Color.White);
textBox.TextChanged = function(sender, args)
info.eventName = textBox:GetText();
self.bar:ShortcutChanged();
end
end
end
if (info.temporary) then
-- Delete temporary empty slot
info.class = nil;
info.temporary = nil;
end
self.slotProperties:SetHeight(top);
end
function SequenceEditor:BuildSlot(s, parent, object, left, top)
-- Get slot info, or initialize to default
local info = self.settings.sequenceItemInfo[s];
if (type(info) ~= "table") then
info = {};
end
if (not info.class) then
-- Empty slot should be rendered as a standard quickslot
info.class = "Turbine.UI.Lotro.Quickslot";
info.temporary = true;
end
-- Discard old object, if it's a different type
if (object and (object.class ~= info.class)) then
if (object.numLabel) then
object.numLabel:SetParent(nil);
end
object:SetParent(nil);
object = nil;
end
-- Create the quickslot object or command icon
if (info.class == "Turbine.UI.Lotro.Quickslot") then
if (not object) then
object = Turbine.UI.Lotro.Quickslot();
end
object.ShortcutChanged = nil;
object:SetShortcut(Turbine.UI.Lotro.Shortcut(info.type, info.Data));
object.isQuickslot = true;
object:SetPosition(left, top);
elseif (info.class == "Turbine.UI.Control") then
if (not object) then
object = Turbine.UI.Control();
end
object:SetBackground(info.background);
object.isQuickslot = false;
object:SetSize(32, 32);
object:SetPosition(left + 3, top + 3);
end
object.class = info.class;
object.s = s;
object:SetParent(parent);
object:SetZOrder(parent:GetZOrder() + 2);
object:SetBlendMode(Turbine.UI.BlendMode.Overlay)
object:SetEnabled(false);
object:SetMouseVisible(false);
-- Create slot number overlay, which accepts mouse events
local numLabel = object.numLabel;
if (not object.numLabel) then
object.numLabel = Turbine.UI.Label();
numLabel = object.numLabel;
numLabel.object = object;
numLabel:SetParent(parent);
numLabel:SetFont(Turbine.UI.Lotro.Font.TrajanPro15);
numLabel:SetFontStyle(Turbine.UI.FontStyle.Outline);
numLabel:SetOutlineColor(Turbine.UI.Color(1, 0, 0, 0));
numLabel:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
numLabel:SetText(tonumber(s));
numLabel:SetSize(32, 32);
numLabel:SetZOrder(object:GetZOrder() + 1);
numLabel:SetMouseVisible(true);
numLabel:SetAllowDrop(true);
end
numLabel:SetVisible(true);
numLabel:SetPosition(left + 3, top + 3);
-- Desired mouse behavior:
-- Left-click should do nothing. (Maybe flash a red 'X' to indicate that nothing happened.)
-- The 'numLabel' object should handle right-click events.
-- The 'quickslot' object should accept 'drop' operations.
-- Ideally the quickslot should be draggable, but this may not be possible.
numLabel.DragDrop = function(sender, args)
local s = sender.object.s;
self:ExtendSequenceTo(s);
self.settings.sequenceItemInfo[s] = {};
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Lotro.Quickslot";
info.type = args.DragDropInfo:GetShortcut():GetType();
info.Data = args.DragDropInfo:GetShortcut():GetData();
self:RebuildSlot(s);
self.bar:ShortcutChanged();
self:Redraw();
self:SelectSlot(s);
-- Don't remove the item/skill from the location where it was dragged from.
args.DragDropInfo:SetSuccessful(false);
end
numLabel.MouseClick = function(sender, args)
-- Left-click selects the slot and shows its properties in the "Slot" tab.
if (args.Button == Turbine.UI.MouseButton.Left) then
self:SelectSlot(sender.object.s);
-- Right-click displays the settings menu.
elseif (args.Button == Turbine.UI.MouseButton.Right) then
self.clickedItem = sender.object.s;
self:ShowContextMenu();
end
end
if (info.temporary) then
-- Delete temporary empty slot
info.class = nil;
info.temporary = nil;
end
return object;
end
function SequenceEditor:RebuildSlot(s, x, y)
local oldObject = self.sequenceItems[s];
-- If x and y aren't specified, then presumably the object has already been positioned previously
if (x == nil) then
x = oldObject.x;
y = oldObject.y;
end
local left, top = self:GetSlotPosition(x, y);
local newObject = self:BuildSlot(s, self.slotPanel, oldObject, left, top);
newObject.x = x;
newObject.y = y;
self.sequenceItems[s] = newObject;
if (self.selectedSlot == s) then
self:DisplaySlot(s);
end
end
function SequenceEditor:SelectSlot(s)
if (self.Marquee) then
self.Marquee:SetParent(nil);
self.Marquee = nil;
end
if (self.selectedSlot) then
local prevObject = self.settings.sequenceItemInfo[self.selectedSlot];
if (prevObject and (prevObject.class == "Turbine.UI.Lotro.Quickslot") and (not prevObject.type)) then
-- Delete empty shortcut
prevObject.class = nil;
end
end
if (s ~= nil) then
self:ExtendSequenceTo(s);
object = self.sequenceItems[s];
self.Marquee = Thurallor.UI.Marquee();
self.Marquee.s = s;
self.Marquee:SetParent(self.slotPanel);
local left, top = self.slotPanel:PointToClient(object.numLabel:PointToScreen(0, 0));
local width, height = object.numLabel:GetSize();
self.Marquee:SetPosition(left - 1, top - 1);
self.Marquee:SetSize(width + 2, height + 2);
self.Marquee:SetMouseVisible(false);
end
self.selectedSlot = s;
self:DisplaySlot(s);
end
function SequenceEditor:GetSlotPosition(x, y)
local left = (x - 1) * (self.settings.slotSize + self.settings.slotSpacing) - self.settings.slotSpacing;
local top = (y - 1) * (self.settings.slotSize + self.settings.slotSpacing) - self.settings.slotSpacing;
return left, top;
end
function SequenceEditor:Closing()
self:DeleteEmptyTrailingSlots();
self.bar:ShortcutChanged();
end
function SequenceEditor:Close()
Turbine.UI.Lotro.Window.Close(self);
end
function SequenceEditor:ShowContextMenu()
-- Build the settings menu.
if (not self.settingsMenu) then
self.settingsMenu = Turbine.UI.ContextMenu();
end
local prevContext = L:SetContext("/SequenceEditor/RightClickMenu");
self:AddSettingsMenuItem(self.settingsMenu, "Root", false);
L:SetContext(prevContext);
self.settingsMenu:ShowMenu();
end
function SequenceEditor:AddSettingsMenuItem(parent, itemName)
local item = Turbine.UI.MenuItem(L:GetText(itemName), true, false);
parent:GetItems():Add(item);
if (itemName == "Root") then
parent:GetItems():Clear();
self:AddSettingsMenuItem(parent, "InsertEmptySlot");
self:AddSettingsMenuItem(parent, "DeleteSlot");
self:AddSettingsMenuItem(parent, "CreateSpecialSlot");
elseif (itemName == "InsertEmptySlot") then
item.Click = function(sender, args)
self:InsertEmptySlot(self.clickedItem);
end
elseif (itemName == ("DeleteSlot")) then
item.Click = function(sender, args)
self:DeleteSlot(self.clickedItem);
end
elseif (itemName == "CreateSpecialSlot") then
local prevContext = L:SetContext("SpecialSlotMenu");
self:AddSettingsMenuItem(item, "RemoveEquipment");
self:AddSettingsMenuItem(item, "StopAnimating");
self:AddSettingsMenuItem(item, "GenerateEvent");
L:SetContext(prevContext);
elseif (string.find("StopAnimating|GenerateEvent", itemName)) then
item.Click = function(sender, args)
self:CreateCommandSlot(self.clickedItem, itemName, item:GetText());
end
elseif (itemName == "RemoveEquipment") then
local prevContext = L:SetContext("RemoveEquipmentMenu");
for itemSlot in keys(Turbine.Gameplay.Equipment) do
local value = Turbine.Gameplay.Equipment[itemSlot];
if ((type(value) == "number") and (value > 0)) then
self:AddSettingsMenuItem(item, "Remove" .. itemSlot);
end
end
L:SetContext(prevContext);
elseif (string.sub(itemName, 1, 6) == "Remove") then
item.Click = function(sender, args)
self:CreateRemovalSlot(self.clickedItem, string.sub(itemName, 7), item:GetText());
end
end
end
function SequenceEditor:CreateCommandSlot(s, slotName, toolTip)
self:ExtendSequenceTo(s);
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Control";
info.background = resources.Icon[slotName];
if (slotName == "StopAnimating") then
info.action = "local item, args = ...; item.bar.animationStopped = true;";
elseif (slotName == "GenerateEvent") then
info.eventName = "New Event";
info.action = "local item, args = ...; item.bar.manager:PropagateEvent(item.eventName);";
end
info.type = slotName;
info.toolTip = toolTip;
self:RebuildSlot(s);
self.bar:ShortcutChanged();
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:CreateRemovalSlot(s, slotName, toolTip)
self:ExtendSequenceTo(s);
local info = self.settings.sequenceItemInfo[s];
info.class = "Turbine.UI.Control";
info.background = resources.Icon["Unequip" .. slotName];
info.action = "Unequip(Turbine.Gameplay.Equipment." .. slotName .. ", \"" .. slotName .. "\");";
info.type = slotName;
info.toolTip = toolTip;
self:RebuildSlot(s);
self.bar:ShortcutChanged();
self:Redraw();
self:SelectSlot(s);
end
function SequenceEditor:ExtendSequenceTo(s)
if (s <= #self.settings.sequenceItemInfo) then
return false;
end
while (s > #self.settings.sequenceItemInfo) do
table.insert(self.settings.sequenceItemInfo, {});
end
return true;
end
function SequenceEditor:DeleteEmptyTrailingSlots()
local sequenceItemInfo = self.settings.sequenceItemInfo;
for s = #sequenceItemInfo, 1, -1 do
local info = sequenceItemInfo[s];
if (not info.class) then
table.remove(sequenceItemInfo, s);
else
break;
end
end
end
function SequenceEditor:InsertEmptySlot(s)
if (s > #self.settings.sequenceItemInfo) then
self:ExtendSequenceTo(s);
else
table.insert(self.settings.sequenceItemInfo, s, {});
end
self:Redraw();
self.bar:ShortcutChanged();
self:SelectSlot(s);
end
function SequenceEditor:DeleteSlot(s)
table.remove(self.settings.sequenceItemInfo, s);
self:Redraw();
self.bar:ShortcutChanged();
self:ExtendSequenceTo(s);
self:SelectSlot(nil);
end