diff --git a/src/gui.lua b/src/gui.lua
index 60fad70..d7adbb4 100644
--- a/src/gui.lua
+++ b/src/gui.lua
@@ -12,11 +12,10 @@ addon.gui = gui;
-- AceGUI
local AceGUI = LibStub("AceGUI-3.0");
+-- Constants
+local RAID_TIME_FORMAT = "%m/%d/%y %H:%M";
-local classIdToClassName = {};
-FillLocalizedClassList(classIdToClassName);
-
-
+-- Formats a long number as a more human-readable version.
function gui:FormatNumber(number)
if Skada and Skada.FormatNumber then
return Skada:FormatNumber(number)
@@ -30,58 +29,28 @@ function gui:FormatNumber(number)
end
end
-function gui:CreateHighScoreParseEntry(parse, role, rank)
- local entryWidget = AceGUI:Create("SimpleGroup");
- entryWidget:SetFullWidth(true);
- entryWidget:SetLayout("Flow");
- entryWidget:SetHeight(30);
-
- local classId = parse.class;
- local classColor = RAID_CLASS_COLORS[classId];
- local className = classIdToClassName[classId];
-
- local relativeWidth = floor((1/6)*100)/100;
-
- local rankLabel = AceGUI:Create("Label");
- rankLabel:SetText(rank);
- rankLabel:SetFontObject(GameFontHighlightSmall);
- rankLabel:SetRelativeWidth(relativeWidth);
-
- local dpsHpsLabel = AceGUI:Create("Label");
- local dpsHps = self:FormatNumber((role == "HEALER") and parse.hps or parse.dps);
- dpsHpsLabel:SetText(dpsHps);
- dpsHpsLabel:SetFontObject(GameFontHighlightSmall);
- dpsHpsLabel:SetRelativeWidth(relativeWidth);
-
- local nameLabel = AceGUI:Create("Label");
- nameLabel:SetText(parse.name);
- nameLabel:SetColor(classColor.r, classColor.g, classColor.b);
- nameLabel:SetFontObject(GameFontHighlightSmall);
- nameLabel:SetRelativeWidth(relativeWidth);
-
- local specLabel = AceGUI:Create("Label");
- specLabel:SetText(parse.specName or "");
- specLabel:SetFontObject(GameFontHighlightSmall);
- specLabel:SetRelativeWidth(relativeWidth);
-
- local ilvlLabel = AceGUI:Create("Label");
- ilvlLabel:SetText(parse.itemLevel or "");
- ilvlLabel:SetFontObject(GameFontHighlightSmall);
- ilvlLabel:SetRelativeWidth(relativeWidth);
-
- local dateLabel = AceGUI:Create("Label");
- dateLabel:SetText(date("%m/%d/%y", parse.startTime));
- dateLabel:SetFontObject(GameFontHighlightSmall);
- dateLabel:SetRelativeWidth(relativeWidth);
-
- entryWidget:AddChild(rankLabel);
- entryWidget:AddChild(dpsHpsLabel);
- entryWidget:AddChild(nameLabel);
- entryWidget:AddChild(specLabel);
- entryWidget:AddChild(ilvlLabel);
- entryWidget:AddChild(dateLabel);
-
- return entryWidget;
+-- Creats a row of labels taking up 1/numLabels relative
+-- width per label and adds each label to the container.
+-- The labels data should be a list of objects:
+-- {name, text, modifyFunction, onClick}
+function gui:CreateLabelRow(container, labelDatas)
+ local relativeWidth = floor((1/#labelDatas)*100)/100;
+ for _, labelData in pairs(labelDatas) do
+ local _, text, modifyFunction, onClick = unpack(labelData);
+ local label;
+ if onClick then
+ label = AceGUI:Create("InteractiveLabel");
+ label:SetCallback("OnClick", onClick);
+ else
+ label = AceGUI:Create("Label");
+ end
+ label:SetText(text);
+ label:SetRelativeWidth(relativeWidth);
+ if modifyFunction then
+ modifyFunction(label)
+ end
+ container:AddChild(label);
+ end
end
function gui:CreateGuildDropdown()
@@ -150,70 +119,137 @@ function gui:CreateEncounterDropdown()
return dropdown;
end
-function gui:CreateHighScoreScrollFrame()
- local scrollFrame = AceGUI:Create("ScrollFrame");
- scrollFrame:SetLayout("Flow");
- scrollFrame:SetFullWidth(true);
- scrollFrame:SetFullHeight(true);
- self.highScoreParsesScrollFrame = scrollFrame;
+function gui:CreateFilterEntry(filterId, filterValue)
+ local container = AceGUI:Create("SimpleGroup");
+ container:SetRelativeWidth(0.5);
+ container:SetLayout("Flow");
+
+ local label = AceGUI:Create("Label");
+ label:SetRelativeWidth(0.65);
+
+ if filterId == "startTime" then
+ label:SetText("Time: " ..
+ date(RAID_TIME_FORMAT, filterValue));
+ elseif filterId == "name" then
+ label:SetText("Name: " .. filterValue);
+ else
+ return;
+ end
+
+
+ local removeButton = AceGUI:Create("Button")
+ removeButton:SetText("Remove");
+ removeButton:SetHeight(18);
+ removeButton:SetRelativeWidth(0.30);
+ removeButton:SetCallback("OnClick", function()
+ self:UnsetParseFilter(filterId);
+ end)
+
+ container:AddChild(label);
+ container:AddChild(removeButton);
+
+ return container;
+end
- local relativeWidth = floor((1/6)*100)/100;
+-- Create the filter container, a row below the dropdowns
+-- that displays the current selected filters.
+-- Filtering by: "NameOfPlayer", "1/2/3 04:05"
+function gui:CreateFilterContainer()
+ local filterContainer = AceGUI:Create("InlineGroup");
+ self.filterContainer = filterContainer;
+ filterContainer:SetFullWidth(true);
+ filterContainer:SetLayout("Flow");
+ filterContainer:SetTitle("Filtered by");
+
+ local nothingLabel = AceGUI:Create("Label");
+ nothingLabel:SetText("-- Nothing, click on a player name or a time to filter. --");
+ nothingLabel:SetFullWidth(true);
+ filterContainer:AddChild(nothingLabel);
+
+ return filterContainer;
+end
- -- Header:
- -- Rank | DPS/HPS | Name | Class | Spec | Item Level | Date
+-- Creates the headers for the parses:
+-- Rank | DPS/HPS | Name | Spec | Item Level | Time
+function gui:CreateHighScoreParseHeader()
local headerContainer = AceGUI:Create("SimpleGroup");
headerContainer:SetFullWidth(true);
headerContainer:SetLayout("Flow");
+
+ local labelDatas = {
+ {"rank", "Rank"},
+ {"dpsHps", "DPS/HPS"},
+ {"name", "Name"},
+ {"spec", "Spec"},
+ {"ilvl", "Item Level"},
+ {"time", "Time"}
+ }
+ self:CreateLabelRow(headerContainer, labelDatas);
+ return headerContainer;
+end
+
+-- Creates a row for a parse entry.
+-- Rank | DPS/HPS | Name | Spec | Item Level | Time
+function gui:CreateHighScoreParseEntry(parse, role, rank)
+ local entryWidget = AceGUI:Create("SimpleGroup");
+ entryWidget:SetFullWidth(true);
+ entryWidget:SetLayout("Flow");
+ entryWidget:SetHeight(30);
- local rankLabel = AceGUI:Create("Label");
- rankLabel:SetText("Rank");
- rankLabel:SetFontObject(GameFontHighlightSmall);
- rankLabel:SetRelativeWidth(relativeWidth);
-
- local dpsHpsLabel = AceGUI:Create("Label");
- dpsHpsLabel:SetText("DPS/HPS");
- dpsHpsLabel:SetFontObject(GameFontHighlightSmall);
- dpsHpsLabel:SetRelativeWidth(relativeWidth);
-
- local nameLabel = AceGUI:Create("Label");
- nameLabel:SetText("Name");
- nameLabel:SetFontObject(GameFontHighlightSmall);
- nameLabel:SetRelativeWidth(relativeWidth);
-
- local specLabel = AceGUI:Create("Label");
- specLabel:SetText("Spec");
- specLabel:SetFontObject(GameFontHighlightSmall);
- specLabel:SetRelativeWidth(relativeWidth);
-
- local ilvlLabel = AceGUI:Create("Label");
- ilvlLabel:SetText("Item Level");
- ilvlLabel:SetFontObject(GameFontHighlightSmall);
- ilvlLabel:SetRelativeWidth(relativeWidth);
-
- local dateLabel = AceGUI:Create("Label");
- dateLabel:SetText("Date");
- dateLabel:SetFontObject(GameFontHighlightSmall);
- dateLabel:SetRelativeWidth(relativeWidth);
-
- headerContainer:AddChild(rankLabel);
- headerContainer:AddChild(dpsHpsLabel);
- headerContainer:AddChild(nameLabel);
- headerContainer:AddChild(specLabel);
- headerContainer:AddChild(ilvlLabel);
- headerContainer:AddChild(dateLabel);
+ local classColor = {RAID_CLASS_COLORS[parse.class].r,
+ RAID_CLASS_COLORS[parse.class].g,
+ RAID_CLASS_COLORS[parse.class].b};
+ local dpsHps = self:FormatNumber((role == "HEALER") and parse.hps or parse.dps);
+
+ local labelDatas = {
+ {"rank", rank},
+ {"dpsHps", dpsHps},
+ {"name",
+ parse.name,
+ function(label)
+ label:SetColor(unpack(classColor));
+ end,
+ function()
+ self:ToggleParseFilter("name", parse.name);
+ end
+ },
+ {"spec", parse.specName},
+ {"ilvl", parse.itemLevel},
+ {"time",
+ date(RAID_TIME_FORMAT, parse.startTime),
+ nil,
+ function()
+ self:ToggleParseFilter("startTime", parse.startTime)
+ end
+ }
+ }
+
+ self:CreateLabelRow(entryWidget, labelDatas);
+ return entryWidget;
+end
+
+function gui:CreateHighScoreScrollFrame()
+ local scrollFrame = AceGUI:Create("ScrollFrame");
+ scrollFrame:SetLayout("Flow");
+ scrollFrame:SetFullWidth(true);
+ scrollFrame:SetFullHeight(true);
+
+ self.highScoreParsesScrollFrame = scrollFrame;
local parsesContainer = AceGUI:Create("SimpleGroup");
self.highScoreParsesContainer = parsesContainer;
parsesContainer:SetFullWidth(true);
parsesContainer:SetLayout("Flow");
- scrollFrame:AddChild(headerContainer);
+ scrollFrame:AddChild(self:CreateHighScoreParseHeader());
scrollFrame:AddChild(parsesContainer);
return scrollFrame;
end
+-- Creates container in the center of the GUI holding the parses.
+-- The group has 3 tabs, one for each of DPSers, Healers and Tanks.
function gui:CreateHighScoreTabGroup()
local container = AceGUI:Create("TabGroup");
self.highScoreTabGroup = container;
@@ -235,28 +271,103 @@ function gui:CreateHighScoreTabGroup()
return container;
end
+-- Creates the main GUI frame.
function gui:CreateMainFrame()
local frame = AceGUI:Create("Frame")
self.mainFrame = frame;
- frame:Hide()
- frame:SetWidth(800)
- frame:SetHeight(600)
+ frame:Hide();
+ frame:SetWidth(800);
+ frame:SetHeight(600);
frame:SetTitle(format("Guild Skada High Score (%s)", addon.versionName));
- frame:SetLayout("Flow")
+ frame:SetLayout("Flow");
frame:SetCallback("OnClose", function()
- gui:HideMainFrame()
+ gui:HideMainFrame();
end)
- frame:AddChild(self:CreateGuildDropdown());
- frame:AddChild(self:CreateZoneDropdown());
- frame:AddChild(self:CreateDifficultyDropdown());
- frame:AddChild(self:CreateEncounterDropdown());
+ local dropdownContainer = AceGUI:Create("InlineGroup");
+ dropdownContainer:SetLayout("Flow");
+ dropdownContainer:SetFullWidth(true);
+ dropdownContainer:SetTitle("Select an Encounter");
+
+ dropdownContainer:AddChild(self:CreateGuildDropdown());
+ dropdownContainer:AddChild(self:CreateZoneDropdown());
+ dropdownContainer:AddChild(self:CreateDifficultyDropdown());
+ dropdownContainer:AddChild(self:CreateEncounterDropdown());
+
+ frame:AddChild(dropdownContainer);
+ frame:AddChild(self:CreateFilterContainer());
frame:AddChild(self:CreateHighScoreTabGroup());
return frame;
end
+-- Takes a list of parse objects and applies filters them
+-- by the attribute filters defined in parseFilters,
+-- returning those passing all filters.
+function gui:FilterParses(parses)
+ if not self.parseFilters then
+ return parses
+ end;
+
+ local filteredParses = {}
+ for _, parse in ipairs(parses) do
+ local passedAll = true;
+ for attribute, matchValue in pairs(self.parseFilters) do
+ if not parse[attribute] or parse[attribute] ~= matchValue then
+ passedAll = false;
+ break;
+ end
+ end
+ if passedAll then
+ tinsert(filteredParses, parse);
+ end
+ end
+ return filteredParses;
+end
+
+function gui:SetParseFilter(attribute, value)
+ self.parseFilters[attribute] = value;
+ self:DisplayParses();
+ self:DisplayParseFilters();
+end
+
+function gui:UnsetParseFilter(attribute)
+ self:SetParseFilter(attribute, nil);
+end
+
+function gui:ToggleParseFilter(attribute, value)
+ if self.parseFilters[attribute] ~= value then
+ self:SetParseFilter(attribute, value);
+ else
+ self:UnsetParseFilter(attribute, value);
+ end
+end
+
+function gui:DisplayParseFilters()
+ self.filterContainer:ReleaseChildren();
+
+ local filterEntries = {}
+ for filterId, filterValue in pairs(self.parseFilters) do
+ local filterEntry = self:CreateFilterEntry(filterId, filterValue);
+ tinsert(filterEntries, filterEntry);
+ end
+
+ if #filterEntries > 0 then
+ for _, filterEntry in ipairs(filterEntries) do
+ self.filterContainer:AddChild(filterEntry)
+ end
+ else
+ local nothingLabel = AceGUI:Create("Label");
+ nothingLabel:SetText("-- Nothing, click on a name or a time to filter. --");
+ nothingLabel:SetFullWidth(true);
+ self.filterContainer:AddChild(nothingLabel);
+ end
+end
+
+-- Uses the currently selected guild/zone/diff/encounter/role
+-- and attempts to fetch all parses for this combination. The
+-- parses are also run trough #FilterParses before being displayed.
function gui:DisplayParses()
local guildName = self.selectedGuild;
local zoneId = self.selectedZone;
@@ -269,15 +380,18 @@ function gui:DisplayParses()
parsesContainer:ReleaseChildren();
if guildName and zoneId and difficultyId and encounter and roleId then
- local parses, numParses = addon.highscore:GetParses(guildName,
- zoneId, difficultyId, encounter, roleId);
- if numParses > 0 then
+ local parses, _ = addon.highscore:GetParses(guildName, zoneId, difficultyId, encounter, roleId);
+ parses = self:FilterParses(parses);
+
+ if #parses > 0 then
parsesContainer:PauseLayout();
scrollFrame:PauseLayout();
+
for rank, parse in ipairs(parses) do
local entryWidget = self:CreateHighScoreParseEntry(parse, roleId, rank);
parsesContainer:AddChild(entryWidget);
end
+
parsesContainer:ResumeLayout();
parsesContainer:DoLayout();
scrollFrame:ResumeLayout();
@@ -416,6 +530,7 @@ function gui:HideMainFrame()
self.zoneDropdown = nil;
self.difficultyDropdown = nil;
self.encounterDropdown = nil;
+ self.filterContainer = nil;
self.highScoreTabGroup = nil;
self.highScoreParsesContainer = nil;
self.highScoreParsesScrollFrame = nil;
@@ -433,10 +548,14 @@ end
function gui:OnEnable()
+ self.parseFilters = {};
+
self:RawHook("CloseSpecialWindows", "OnCloseSpecialWindows", true);
end
function gui:OnDisable()
+ wipe(self.parseFilters);
+
self:HideMainFrame();
self:UnHook("CloseSpecialWindows");
end
\ No newline at end of file