diff --git a/Config.lua b/Config.lua index 38cd414..197af67 100644 --- a/Config.lua +++ b/Config.lua @@ -1641,7 +1641,10 @@ DF.PartyDefaults = { "WARLOCK", "WARRIOR", }, + + -- Sorting sortEnabled = true, + sortByPartyOrder = false, -- keep units in their original party/raid index order sortRoleOrder = {"TANK", "HEALER", "MELEE", "RANGED"}, sortSelfPosition = "SORTED", sortSeparateMeleeRanged = false, @@ -2848,7 +2851,10 @@ DF.RaidDefaults = { "WARLOCK", "WARRIOR", }, + + -- Sorting sortEnabled = true, + sortByPartyOrder = false, -- keep units in their original party/raid index order sortRoleOrder = {"TANK", "HEALER", "MELEE", "RANGED"}, sortSelfPosition = "SORTED", sortSeparateMeleeRanged = false, diff --git a/ExportCategories.lua b/ExportCategories.lua index dac408e..92a1ef0 100644 --- a/ExportCategories.lua +++ b/ExportCategories.lua @@ -39,6 +39,7 @@ DF.ExportCategories = { -- Sorting "sortEnabled", + "sortByPartyOrder", "sortAlphabetical", "sortByClass", "sortClassOrder", diff --git a/Features/FlatRaidFrames.lua b/Features/FlatRaidFrames.lua index dd9564c..476ccd4 100644 --- a/Features/FlatRaidFrames.lua +++ b/Features/FlatRaidFrames.lua @@ -883,12 +883,16 @@ function FlatRaidFrames:UpdateSorting() -- Check sorting settings local sortEnabled = db.sortEnabled + -- party/raid index option overrides all other custom sorting + if db.sortByPartyOrder then + sortEnabled = false + end local selfPosition = db.sortSelfPosition or "SORTED" local separateMeleeRanged = db.sortSeparateMeleeRanged local sortByClass = db.sortByClass local sortAlphabetical = db.sortAlphabetical - DebugPrint("UpdateSorting: sortEnabled=", sortEnabled, "selfPosition=", selfPosition) + DebugPrint("UpdateSorting: sortEnabled=", sortEnabled, "selfPosition=", selfPosition, "partyOrder=", tostring(db.sortByPartyOrder)) DebugPrint(" separateMeleeRanged=", separateMeleeRanged, "sortByClass=", sortByClass, "sortAlphabetical=", sortAlphabetical) -- CRITICAL: Handle sortEnabled=false first diff --git a/Features/SecureSort.lua b/Features/SecureSort.lua index 431905b..5e6ccf5 100644 --- a/Features/SecureSort.lua +++ b/Features/SecureSort.lua @@ -596,11 +596,20 @@ function SecureSort:CreateHandler() -- STEP 2: Get sort settings -- ===================================================== local sortEnabled = self:GetAttribute("sortEnabled") + local sortByPartyOrder = self:GetAttribute("sortByPartyOrder") or false + local partyOrderSort = sortByPartyOrder local selfPosition = self:GetAttribute("selfPosition") or "NORMAL" local sortAlphabetical = self:GetAttribute("sortAlphabetical") or false local sortAlphaReverse = (sortAlphabetical == "ZA") local sortByClass = self:GetAttribute("sortByClass") or false + if sortByPartyOrder then + -- ignore other criteria when using raw party order + sortEnabled = false + sortAlphabetical = false + sortByClass = false + end + -- ===================================================== -- STEP 3: Query roles (if sorting enabled) -- ===================================================== @@ -700,11 +709,23 @@ function SecureSort:CreateHandler() for idx = 0, frameCount - 1 do local i = visibleFrames[idx] local unit = frameUnits[i] - local role = unit2role[unit] or "DAMAGER" - local rp = rolePriority[role] or 99 - local cls = unit2class[unit] - local cp = sortByClass and (classPriority[cls] or 99) or 0 - frameSortKey[idx] = rp * 100 + cp + if partyOrderSort then + -- When using "party/raid index order", we want to KEEP the + -- existing visual order of the frames and only apply the + -- selfPosition override (player first/last/numeric slot). + -- + -- The existing order is exactly the order of visibleFrames, + -- so we just use the current index as the sort key. This + -- guarantees non-player units stay in their current order, + -- matching the behavior when sorting is disabled. + frameSortKey[idx] = idx + else + local role = unit2role[unit] or "DAMAGER" + local rp = rolePriority[role] or 99 + local cls = unit2class[unit] + local cp = sortByClass and (classPriority[cls] or 99) or 0 + frameSortKey[idx] = rp * 100 + cp + end -- Read name from attribute (pushed by Lua code) frameName[idx] = self:GetAttribute("frameName" .. i) or "" @@ -720,7 +741,7 @@ function SecureSort:CreateHandler() end -- Bubble sort by sort key, then by name as tiebreaker - if sortEnabled then + if sortEnabled or partyOrderSort then for i = 0, frameCount - 2 do for j = 0, frameCount - 2 - i do local a = sortOrder[j] @@ -2023,6 +2044,7 @@ function SecureSort:PushSortSettings() self.sortButton:SetAttribute("roleOrder3", roleOrder[3] or "DAMAGER") self.sortButton:SetAttribute("selfPosition", db.sortSelfPosition or "SORTED") self.sortButton:SetAttribute("sortEnabled", db.sortEnabled or false) + self.sortButton:SetAttribute("sortByPartyOrder", db.sortByPartyOrder or false) self.sortButton:SetAttribute("sortByClass", db.sortByClass or false) self.sortButton:SetAttribute("meleeBeforeRanged", meleeBeforeRanged) self.sortButton:SetAttribute("sortAlphabetical", db.sortAlphabetical or false) @@ -2034,6 +2056,7 @@ function SecureSort:PushSortSettings() self.handler:SetAttribute("roleOrder3", roleOrder[3] or "DAMAGER") self.handler:SetAttribute("selfPosition", db.sortSelfPosition or "SORTED") self.handler:SetAttribute("sortEnabled", db.sortEnabled or false) + self.handler:SetAttribute("sortByPartyOrder", db.sortByPartyOrder or false) self.handler:SetAttribute("sortByClass", db.sortByClass or false) self.handler:SetAttribute("meleeBeforeRanged", meleeBeforeRanged) self.handler:SetAttribute("sortAlphabetical", db.sortAlphabetical or false) @@ -2063,6 +2086,7 @@ function SecureSort:PushSortSettings() DebugPrint("Sort settings pushed: " .. (roleOrder[1] or "?") .. ">" .. (roleOrder[2] or "?") .. ">" .. (roleOrder[3] or "?") .. " self=" .. (db.sortSelfPosition or "SORTED") .. + " partyOrder=" .. tostring(db.sortByPartyOrder) .. " class=" .. tostring(db.sortByClass or false) .. " alpha=" .. tostring(db.sortAlphabetical or false)) diff --git a/Features/Sort.lua b/Features/Sort.lua index 85d74b5..64d6e3b 100644 --- a/Features/Sort.lua +++ b/Features/Sort.lua @@ -9,11 +9,16 @@ local addonName, DF = ... local pairs, ipairs, type, wipe = pairs, ipairs, type, wipe local sort = table.sort local tinsert = table.insert + local UnitExists = UnitExists local UnitGUID = UnitGUID local UnitGroupRolesAssigned = UnitGroupRolesAssigned local UnitClass = UnitClass +local UnitIsUnit = UnitIsUnit +local UnitName = UnitName local GetSpecializationInfoByID = GetSpecializationInfoByID +local GetSpecialization = GetSpecialization +local InCombatLockdown = InCombatLockdown -- NOTE: Previously used reusable tables here, but that caused bugs when -- SortFrameList was called while iterating over a previous result. @@ -157,6 +162,31 @@ end -- Compare function for sorting frames function Sort:CompareUnits(unitA, unitB, db) + -- Option to ignore role/class/name and simply use party/raid index + if db.sortByPartyOrder then + local function GetIndex(u) + if not u then return 9999 end + if UnitIsUnit(u, "player") then return 0 end + + -- Prefer a GUID->party slot lookup so the "index order" matches the actual party roster + -- even if the unit-id on a frame isn't literally "partyN" (e.g. raidN assignment). + local guid = UnitGUID(u) + if guid then + for i = 1, 4 do + local pu = "party" .. i + if UnitExists(pu) and UnitGUID(pu) == guid then + return i + end + end + end + + -- Fallback: parse digits from the unit string (party1/raid1/etc) + local num = tonumber((u:match("%d+"))) + return num or 9999 + end + return GetIndex(unitA) < GetIndex(unitB) + end + local roleA = self:GetUnitRole(unitA) local roleB = self:GetUnitRole(unitB) @@ -199,6 +229,13 @@ end -- Compare function for test mode using test data function Sort:CompareTestData(dataA, dataB, db) + -- If party-order sorting is requested, use the precomputed index + if db.sortByPartyOrder then + local idxA = dataA.index or 9999 + local idxB = dataB.index or 9999 + return idxA < idxB + end + -- Get roles from test data local roleA = dataA.role or "DAMAGER" local roleB = dataB.role or "DAMAGER" @@ -424,6 +461,7 @@ SlashCmdList["DFSORT"] = function(msg) print(" sortByClass:", db.sortByClass) print(" sortAlphabetical:", tostring(db.sortAlphabetical)) print(" sortSeparateMeleeRanged:", db.sortSeparateMeleeRanged) + print(" sortByPartyOrder:", tostring(db.sortByPartyOrder)) print(" sortRoleOrder:", table.concat(db.sortRoleOrder or {}, ", ")) if db.sortByClass then print(" sortClassOrder:", table.concat(db.sortClassOrder or {}, ", ")) diff --git a/Frames/Headers.lua b/Frames/Headers.lua index 6ecf6d4..db6c6f0 100755 --- a/Frames/Headers.lua +++ b/Frames/Headers.lua @@ -3928,6 +3928,7 @@ function DF:ApplyRaidGroupSorting() end -- Determine if we need nameList for advanced sorting + -- Party order option disables custom sorting entirely (sortEnabled=false above) -- Use nameList for ALL groups when: -- - Player position is FIRST/LAST (player's group only) -- - OR any advanced option is enabled (all groups need it) @@ -4323,19 +4324,78 @@ function DF:BuildRaidGroupNameList(groupIndex, selfPosition) return DF:BuildSortedNameList(members, DF:GetRaidDB(), selfPosition, playerInGroup) end --- Build a namelist for party that puts player first or last, sorted by role priority +-- Build a nameList for party, honoring sortByPartyOrder and selfPosition function DF:BuildPartyNameList(selfPosition) - -- Get members in party (player + party1-4) - local members = {} + local db = DF:GetDB() local playerName = UnitName("player") - + + if db.sortByPartyOrder then + local names = {} + + local partyNames = {} + for i = 1, 4 do + local unit = "party" .. i + if UnitExists(unit) and not UnitIsUnit(unit, "player") then + local name, realm = UnitName(unit) + if name then + local fullName = name + if realm and realm ~= "" then + fullName = name .. "-" .. realm + end + table.insert(partyNames, fullName) + end + end + end + + local targetPos + if selfPosition == "FIRST" or selfPosition == "1" then + targetPos = 1 + elseif selfPosition == "LAST" then + targetPos = #partyNames + 1 + else + local n = tonumber(selfPosition) + if n and n >= 1 then + if n > (#partyNames + 1) then n = #partyNames + 1 end + targetPos = n + else + targetPos = nil + end + end + + if targetPos then + local inserted = false + for i = 1, #partyNames + 1 do + if i == targetPos then + table.insert(names, playerName) + inserted = true + end + if i <= #partyNames then + table.insert(names, partyNames[i]) + end + end + if not inserted then + table.insert(names, playerName) + end + else + table.insert(names, playerName) + for _, n in ipairs(partyNames) do + table.insert(names, n) + end + end + + return table.concat(names, ",") + end + + -- Default path: role/class/name sorting via unified function + local members = {} + -- Add player table.insert(members, { unit = "player", name = playerName, isPlayer = true }) - + -- Add party members for i = 1, 4 do local unit = "party" .. i @@ -4355,8 +4415,7 @@ function DF:BuildPartyNameList(selfPosition) end end - -- Use the unified sorting function - return DF:BuildSortedNameList(members, DF:GetDB(), selfPosition, true) + return DF:BuildSortedNameList(members, db, selfPosition, true) end -- ============================================================ @@ -5750,8 +5809,12 @@ function DF:ApplyPartyGroupSorting() local sortByClass = db.sortByClass local sortAlphabetical = db.sortAlphabetical - -- Determine if we need nameList (any advanced option or FIRST/LAST) - local needsNameList = (selfPosition ~= "SORTED") or separateMeleeRanged or sortByClass or sortAlphabetical + -- Determine if we need nameList (any advanced option, FIRST/LAST, or party-index mode) + local needsNameList = db.sortByPartyOrder + or (selfPosition ~= "SORTED") + or separateMeleeRanged + or sortByClass + or sortAlphabetical if DF.debugHeaders then print("|cFF00FF00[DF Headers]|r ApplyPartyGroupSorting:") @@ -6780,10 +6843,12 @@ function DF:DumpHeaderInfo() print(" growthAnchor:", db.growthAnchor or "nil") print(" sortSelfPosition:", db.sortSelfPosition or "nil") print(" sortEnabled:", db.sortEnabled and "true" or "false") + print(" sortByPartyOrder:", db.sortByPartyOrder and "true" or "false") print("Raid Settings:") print(" raidUseGroups:", raidDb.raidUseGroups and "true" or "false") print(" raidEnabled:", raidDb.raidEnabled and "true" or "false") print(" sortEnabled:", raidDb.sortEnabled and "true" or "false") + print(" sortByPartyOrder:", raidDb.sortByPartyOrder and "true" or "false") print(" sortSelfPosition:", raidDb.sortSelfPosition or "nil") print(" sortSeparateMeleeRanged:", raidDb.sortSeparateMeleeRanged and "true" or "false") print(" sortByClass:", raidDb.sortByClass and "true" or "false") diff --git a/Options/Options.lua b/Options/Options.lua index a8a1b0c..0c32710 100644 --- a/Options/Options.lua +++ b/Options/Options.lua @@ -2456,6 +2456,10 @@ function DF:SetupGUIPages(GUI, CreateCategory, CreateSubTab, BuildPage) return not d.sortEnabled end + local function HideRoleSortOptions(d) + return not d.sortEnabled or d.sortByPartyOrder + end + -- Store reference to role widget so we can refresh it local roleOrderWidget = nil @@ -2487,7 +2491,7 @@ function DF:SetupGUIPages(GUI, CreateCategory, CreateSubTab, BuildPage) combatBanner:Show() local selfPos = db.sortSelfPosition or "SORTED" - local hasAdvancedOptions = db.sortSeparateMeleeRanged or db.sortByClass or db.sortAlphabetical + local hasAdvancedOptions = (not db.sortByPartyOrder) and (db.sortSeparateMeleeRanged or db.sortByClass or db.sortAlphabetical) if hasAdvancedOptions then -- All groups limited @@ -2526,7 +2530,7 @@ function DF:SetupGUIPages(GUI, CreateCategory, CreateSubTab, BuildPage) -- ===== SORTING OPTIONS GROUP (Column 1) ===== local sortOptionsGroup = GUI:CreateSettingsGroup(self.child, 280) sortOptionsGroup:AddWidget(GUI:CreateHeader(self.child, "Unit Frame Sorting"), 40) - sortOptionsGroup:AddWidget(GUI:CreateLabel(self.child, "Sort party members by role, class, and name.\n\nSort order: Self Position > Role > Class > Name", 250), 60) + sortOptionsGroup:AddWidget(GUI:CreateLabel(self.child, "Sort party members by role (or keep Blizzard party/raid index order).\n\nWhen sorting by role the order is: Self Position > Role > Class > Name", 250), 60) local raidSortNote = sortOptionsGroup:AddWidget(GUI:CreateLabel(self.child, "Raid: Group layout sorts within each group.\nFlat grid layout sorts all players together.", 250), 35) raidSortNote.hideOn = function() return GUI.SelectedMode ~= "raid" end @@ -2536,19 +2540,29 @@ function DF:SetupGUIPages(GUI, CreateCategory, CreateSubTab, BuildPage) UpdateCombatBanner() end), 30) - local sortMeleeRanged = sortOptionsGroup:AddWidget(GUI:CreateCheckbox(self.child, "Separate Melee & Ranged DPS", db, "sortSeparateMeleeRanged", function() + local sortMeleeRanged + + local sortByPartyOrder = sortOptionsGroup:AddWidget(GUI:CreateCheckbox(self.child, "Use Party/Raid Index Order (ignore role)", db, "sortByPartyOrder", function() + TriggerSortForCurrentMode() + UpdateCombatBanner() + if sortMeleeRanged and sortMeleeRanged.Refresh then sortMeleeRanged:Refresh() end + if roleOrderWidget and roleOrderWidget.Refresh then roleOrderWidget.Refresh() end + end), 30) + sortByPartyOrder.hideOn = HideSortOptions + + sortMeleeRanged = sortOptionsGroup:AddWidget(GUI:CreateCheckbox(self.child, "Separate Melee & Ranged DPS", db, "sortSeparateMeleeRanged", function() TriggerSortForCurrentMode() if roleOrderWidget and roleOrderWidget.Refresh then roleOrderWidget.Refresh() end UpdateCombatBanner() end), 30) - sortMeleeRanged.hideOn = HideSortOptions + sortMeleeRanged.hideOn = HideRoleSortOptions local sortByClass = sortOptionsGroup:AddWidget(GUI:CreateCheckbox(self.child, "Sort by Class (within role)", db, "sortByClass", function() TriggerSortForCurrentMode() self:RefreshStates() UpdateCombatBanner() end), 30) - sortByClass.hideOn = HideSortOptions + sortByClass.hideOn = HideRoleSortOptions local sortAlphaValues = { [false] = "Off", @@ -2560,7 +2574,7 @@ function DF:SetupGUIPages(GUI, CreateCategory, CreateSubTab, BuildPage) TriggerSortForCurrentMode() UpdateCombatBanner() end), 55) - sortAlpha.hideOn = HideSortOptions + sortAlpha.hideOn = HideRoleSortOptions Add(sortOptionsGroup, nil, 1) @@ -2591,7 +2605,7 @@ function DF:SetupGUIPages(GUI, CreateCategory, CreateSubTab, BuildPage) TriggerSortForCurrentMode() end, "sortSeparateMeleeRanged") rolePriorityGroup:AddWidget(roleOrderWidget, 135) - rolePriorityGroup.hideOn = HideSortOptions + rolePriorityGroup.hideOn = HideRoleSortOptions Add(rolePriorityGroup, nil, 2) -- ===== CLASS PRIORITY GROUP (Column 2) =====