TabCardStack = class(Turbine.UI.Control);
function TabCardStack:Constructor()
Turbine.UI.Control.Constructor(self);
self:SetMouseVisible(false);
self.tabCards = {};
self.autoHidden = {};
end
function TabCardStack:AddTabCard(tabCard)
table.insert(self.tabCards, tabCard);
tabCard:SetParent(self);
self:BringToFront(tabCard);
self:_UpdateButtonPositions();
-- Default behavior when the tab is clicked is to bring the card to the front.
AddCallback(tabCard, "Click", function(ctl, args)
if (args.Button == Turbine.UI.MouseButton.Left) then
self:BringToFront(ctl);
end
end);
end
function TabCardStack:SetAddTabButtonEnabled(enable, tooltip)
local button = self.addTabButton;
if (enable) then
if (not button) then
button = self:_CreateButton("+", tooltip);
button.MouseClick = function(_, args)
DoCallbacks(self, "AddTabButtonClicked", args);
end
self.addTabButton = button;
end
button:SetParent(self);
self:_UpdateButtonPositions();
self:_UpdateButtonColors(button);
else
if (button) then
button:SetParent(nil);
end
self.addTabButton = nil;
end
end
function TabCardStack:_MoreTabsButtonClicked(args)
local menu = Turbine.UI.ContextMenu();
local items = menu:GetItems();
local hiddenTabs = {};
for _, tabCard in pairs(self.tabCards) do
if (tabCard.hidden or tabCard.autoHidden) then
table.insert(hiddenTabs, tabCard);
end
end
table.sort(hiddenTabs, function(a, b)
return (a:GetTabText() < b:GetTabText());
end);
for _, tabCard in ipairs(hiddenTabs) do
local item = Turbine.UI.MenuItem(tabCard:GetTabText(), true, false);
item.tabCard = tabCard;
item.Click = function(ctl)
ctl.tabCard:SetHidden(false);
ctl.tabCard:_SetAutoHidden(false);
self:BringToFront(ctl.tabCard);
self:RedistributeTabs();
end
items:Add(item);
end
menu:ShowMenu();
end
function TabCardStack:SetMoreTabsButtonEnabled(enable, tooltip)
local button = self.moreTabsButton;
if (enable) then
if (not button) then
button = self:_CreateButton("...", tooltip);
button.MouseClick = function(_, args)
DoCallbacks(self, "MoreTabsButtonClicked", args);
if (not args.Cancel) then
self:_MoreTabsButtonClicked(args);
end
end
self.moreTabsButton = button;
end
button:SetParent(self);
self:_UpdateButtonPositions();
self:_UpdateButtonColors(button);
else
if (button) then
button:SetParent(nil);
end
self.moreTabsButton = nil;
end
end
function TabCardStack:_CreateButton(text, tooltip)
button = Turbine.UI.Label();
button:SetMultiline(false);
button:SetFont(Turbine.UI.Lotro.Font.Verdana16);
button:SetTextAlignment(Turbine.UI.ContentAlignment.MiddleCenter);
button:SetFontStyle(Turbine.UI.FontStyle.Outline);
button:SetOutlineColor(Turbine.UI.Color.Black);
button:SetText(text);
button:AutoSize();
button:SetWidth(button:GetWidth() + 4);
button:SetZOrder(2); -- in front of all tabs
button.MouseEnter = function(ctl)
ctl.mouseInside = true;
self:_UpdateButtonColors(ctl);
end
button.MouseLeave = function(ctl)
ctl.mouseInside = false;
self:_UpdateButtonColors(ctl);
end
button.MouseDown = function(ctl, args)
if (args.Button == Turbine.UI.MouseButton.Left) then
ctl.mouseDown = true;
self:_UpdateButtonColors(ctl);
end
end
button.MouseUp = function(ctl, args)
if (args.Button == Turbine.UI.MouseButton.Left) then
ctl.mouseDown = false;
self:_UpdateButtonColors(ctl);
end
end
if (tooltip) then
Thurallor.UI.Tooltip(tooltip):Attach(button);
end
button:SetParent(self);
return button;
end
function TabCardStack:HasHiddenTabs()
for _, tabCard in pairs(self.tabCards) do
if (tabCard.hidden or tabCard.autoHidden) then
return true;
end
end
end
function TabCardStack:_GetRightmostVisibleTab()
for n = #self.tabCards, 1, -1 do
local tabCard = self.tabCards[n];
if (not (tabCard.hidden or tabCard.autoHidden)) then
return tabCard;
end
end
end
function TabCardStack:_UpdateButtonPositions()
local left = 0;
local rightTab = self:_GetRightmostVisibleTab();
if (rightTab) then
left = rightTab:GetTabRight();
end
if (self.moreTabsButton) then
if (self:HasHiddenTabs()) then
self.moreTabsButton:SetParent(self);
self.moreTabsButton:SetPosition(left, 2);
left = left + self.moreTabsButton:GetWidth();
else
self.moreTabsButton:SetParent(nil);
end
end
if (self.addTabButton) then
self.addTabButton:SetPosition(left, 2);
--left = left + self.addTabButton:GetWidth();
end
end
function TabCardStack:_UpdateButtonColors(button)
if (button.mouseDown) then
button:SetForeColor(Turbine.UI.Color.Black);
button:SetOutlineColor(Turbine.UI.Color.PaleGoldenrod);
elseif (button.mouseInside) then
--button:SetForeColor(Turbine.UI.Color(0.75, 1, 1, 1));
button:SetForeColor(Turbine.UI.Color.White);
button:SetOutlineColor(Turbine.UI.Color.DarkGoldenrod);
else
--button:SetForeColor(Turbine.UI.Color(0.5, 1, 1, 1));
button:SetForeColor(Turbine.UI.Color.PaleGoldenrod);
button:SetOutlineColor(Turbine.UI.Color.Black);
end
end
function TabCardStack:BringToFront(tabCard)
for t = 1, #self.tabCards do
local otherTabCard = self.tabCards[t];
if (otherTabCard == tabCard) then
tabCard:BringToFront(true);
tabCard:SetSize(self:GetSize());
self.frontTabCard = tabCard;
else
otherTabCard:SendToBack(true);
end
end
DoCallbacks(self, "FrontTabChanged", tabCard);
end
function TabCardStack:GetFrontTabCard()
return self.frontTabCard;
end
function TabCardStack:RedistributeTabs()
local width = self:GetWidth();
if (self.addTabButton) then
width = width - self.addTabButton:GetWidth();
end
if (self.moreTabsButton) then
width = width - self.moreTabsButton:GetWidth();
end
-- Sort the tabCards by left edge
local function compare(a, b)
return (a:GetTabLeft() < b:GetTabLeft());
end
table.sort(self.tabCards, compare);
-- Unhide auto-hidden tabs
for _, tabCard in pairs(self.tabCards) do
tabCard:_SetAutoHidden(false);
end
-- Spread out the tabCards to eliminate overlap
local left, rightTab = 0, nil;
for t = 1, #self.tabCards do
local tabCard = self.tabCards[t];
tabCard:SetTabLeft(left);
left = left + tabCard:GetTabWidth();
if (not (tabCard.hidden or tabCard.autoHidden)) then
rightTab = tabCard;
end
end
if (left <= width) then
-- All the tabs fit without overlapping. Done.
self:_UpdateButtonPositions(width);
return;
end
-- Too many tabs to display without overlapping
local frontTab = self.frontTabCard;
if (self.moreTabsButton) then
-- Auto-hide excess tabs
for _, tabCard in ipairs(self.tabCards) do
local left, right = tabCard:GetTabLeft(), tabCard:GetTabRight();
local hidden = tabCard:IsHidden();
if (not hidden) then
if ((right <= width) or (left == 0)) then
rightTab = tabCard;
else
tabCard:_SetAutoHidden(true);
end
end
end
-- If we auto-hid the front-tab, hide the rightmost non-hidden tab instead
if (frontTab:_IsAutoHidden()) then
frontTab:_SetAutoHidden(false);
frontTab:SetTabLeft(rightTab:GetTabLeft());
rightTab:_SetAutoHidden(true);
rightTab = frontTab;
end
else
-- Minimize overlapping. Distribute the left edges evenly.
local rightTabWidth = rightTab:GetTabWidth();
local newRightTabLeft = math.max(#self.tabCards, (width - rightTabWidth)); -- need at least as many pixels as tabs
local squeezeRatio = newRightTabLeft / rightTab:GetTabLeft();
for t = 1, #self.tabCards do
local tabCard = self.tabCards[t];
local left = math.floor(0.5 + tabCard:GetTabLeft() * squeezeRatio);
tabCard:SetTabLeft(left);
end
-- Bring front tabCard to the front again so its tab isn't concealed if there's overlap
self.frontTabCard:SetZOrder(1);
self.frontTabCard:SetZOrder(0);
end
self:_UpdateButtonPositions(width);
end
function TabCardStack:SetSize(width, height)
Turbine.UI.Control.SetSize(self, width, height);
for t = 1, #self.tabCards do
local tabCard = self.tabCards[t];
tabCard:SetSize(width, height);
end
self:_UpdateButtonPositions();
end
function TabCardStack:SetWidth(width)
self:SetSize(width, self:GetHeight());
end
function TabCardStack:SetHeight(height)
self:SetSize(self:GetWidth(), height);
end