Files
Freewood-OctoWoW-Vendor/VendorUI.lua
T
2026-10-27 14:13:20 +00:00

477 lines
19 KiB
Lua

-- Freewood-OctoWoW-Vendor: Tab "Haendler", Einstellungen und der Verkaufsknopf am Haendlerfenster.
-- Links die beiden Listen ("Nie verkaufen" oben, "Immer verkaufen" unten), rechts Hinzufuegen, Taschen und Protokoll.
local ADDON, NS = ...
local FW = _G.Freewood
local Vendor = NS.Vendor
if not FW or not Vendor then
return
end
local UI = FW.UI
local LIST_W = 380
local ROW_H, ROW_GAP = 34, 4
local SMALL_H = 30
local function ColoredName(name, quality, id)
local text = name or ("#" .. tostring(id))
return "|cff" .. UI.RarityHex(quality) .. text .. "|r"
end
local function ListRow(parent)
local row = UI.CreatePanel(parent, "card", "bronze")
row:SetHeight(ROW_H)
row.icon = row:CreateTexture(nil, "ARTWORK")
row.icon:SetSize(24, 24)
row.icon:SetPoint("LEFT", row, "LEFT", 6, 0)
row.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
row.name = UI.CreateLabel(row, "", { size = 12 })
row.name:SetPoint("LEFT", row.icon, "RIGHT", 10, 0)
row.name:SetPoint("RIGHT", row, "RIGHT", -112, 0)
row.name:SetWordWrap(false)
row.idLabel = UI.CreateLabel(row, "", { size = 10, color = "textDim", justify = "RIGHT" })
row.idLabel:SetPoint("RIGHT", row, "RIGHT", -40, 0)
row.remove = UI.CreateButton(row, "X", 26, 22, nil, "danger")
row.remove:SetPoint("RIGHT", row, "RIGHT", -6, 0)
UI.AttachTooltip(row.remove, function() return FW.L.VENDOR_REMOVE end, "")
row:EnableMouse(true)
row:SetScript("OnEnter", function(self)
if self.itemID and GameTooltip then
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
pcall(GameTooltip.SetItemByID, GameTooltip, self.itemID)
GameTooltip:Show()
end
end)
row:SetScript("OnLeave", function()
if GameTooltip then GameTooltip:Hide() end
end)
return row
end
-- Setzt Symbol und Name einer Zeile (die Item-Daten koennen verzoegert kommen)
local function FillItem(row, id, fallbackName)
row.itemID = id
row.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
row.name:SetText(ColoredName(fallbackName, 1, id))
FW.Util.RequestItem(id, function(info)
if row.itemID ~= id or not info then
return
end
row.icon:SetTexture(info.icon)
row.name:SetText(ColoredName(info.name, info.quality, id))
end)
end
------------------------------------------------------------------------
-- Tab
------------------------------------------------------------------------
local function Build(content)
local L = FW.L
local f = CreateFrame("Frame", nil, content)
local rowsBlack, rowsWhite, bagRows, logRows = {}, {}, {}, {}
---------------- links: zwei Listen ----------------
local function MakeList(titleText, topAnchor, topOffset, bottomAnchor, bottomOffset, emptyText)
local title = UI.CreateLabel(f, titleText, { font = "heading", size = 14, color = "goldLight" })
title:SetPoint("TOPLEFT", f, topAnchor, 0, topOffset)
local count = UI.CreateLabel(f, "", { size = 11, color = "textDim", justify = "RIGHT" })
count:SetPoint("TOPRIGHT", f, topAnchor, LIST_W, topOffset - 4)
local div = UI.CreateDivider(f)
div:SetPoint("TOPLEFT", f, topAnchor, 0, topOffset - 26)
div:SetWidth(LIST_W)
local scroll = UI.CreateScrollFrame(f)
scroll:SetPoint("TOPLEFT", f, topAnchor, 0, topOffset - 36)
scroll:SetPoint("BOTTOMLEFT", f, bottomAnchor, 0, bottomOffset)
scroll:SetWidth(LIST_W)
local empty = UI.CreateLabel(scroll.child, emptyText, { size = 12, color = "textDim", justifyV = "TOP" })
empty:SetPoint("TOPLEFT", scroll.child, "TOPLEFT", 4, -6)
empty:SetPoint("TOPRIGHT", scroll.child, "TOPRIGHT", -4, -6)
empty:SetWordWrap(true)
return { title = title, count = count, scroll = scroll, empty = empty }
end
local blackUI = MakeList(L.VENDOR_BLACK_HEADER, "TOPLEFT", 0, "LEFT", 8, L.VENDOR_BLACK_EMPTY)
local whiteUI = MakeList(L.VENDOR_WHITE_HEADER, "LEFT", -4, "BOTTOMLEFT", 0, L.VENDOR_WHITE_EMPTY)
local separator = f:CreateTexture(nil, "ARTWORK")
separator:SetColorTexture(UI.Color("bronze", 0.5))
separator:SetWidth(1)
separator:SetPoint("TOPLEFT", f, "TOPLEFT", LIST_W + 10, 0)
separator:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", LIST_W + 10, 0)
---------------- rechts ----------------
local right = CreateFrame("Frame", nil, f)
right:SetPoint("TOPLEFT", f, "TOPLEFT", LIST_W + 22, 0)
right:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 0, 0)
local addTitle = UI.CreateLabel(right, L.VENDOR_ADD_HEADER, { font = "heading", size = 14, color = "goldLight" })
addTitle:SetPoint("TOPLEFT", right, "TOPLEFT", 0, 0)
local addDivider = UI.CreateDivider(right)
addDivider:SetPoint("TOPLEFT", right, "TOPLEFT", 0, -26)
addDivider:SetPoint("TOPRIGHT", right, "TOPRIGHT", 0, -26)
local status = UI.CreateLabel(right, "", { size = 11, color = "textDim" })
local function SetStatus(text, colorKey)
status:SetText(text or "")
status:SetTextColor(UI.Color(colorKey or "textDim"))
end
local function TryAdd(which, input)
local ok, result = Vendor:Add(which, input)
if ok then
local info = FW.Util.GetItem and FW.Util.GetItem(result)
local key = which == "black" and "VENDOR_ADD_OK_BLACK" or "VENDOR_ADD_OK_WHITE"
SetStatus(string.format(FW.L[key], (info and info.name) or ("#" .. result)), "success")
return true
end
if result == "already" then
SetStatus(FW.L.VENDOR_ADD_ALREADY, "goldLight")
elseif result == "full" then
SetStatus(FW.L.VENDOR_ADD_FULL, "danger")
elseif result == "black" then
SetStatus(FW.L.VENDOR_ADD_ISBLACK, "danger")
else
SetStatus(FW.L.VENDOR_ADD_INVALID, "danger")
end
return false
end
-- Ablagefeld fuer Items aus der Tasche: es fragt nach der Liste
local drop = UI.CreateItemSlot(right, 44)
drop:SetPoint("TOPLEFT", right, "TOPLEFT", 0, -38)
drop:EnableMouse(true)
local pendingDrop
local function OnDrop()
local kind, id = GetCursorInfo()
if kind == "item" then
ClearCursor()
pendingDrop = id
SetStatus(FW.L.VENDOR_ADD_PICK, "goldLight")
end
end
drop:SetScript("OnReceiveDrag", OnDrop)
drop:SetScript("OnMouseUp", OnDrop)
local dropLabel = UI.CreateLabel(right, L.VENDOR_ADD_DROP, { size = 12, color = "textDim" })
dropLabel:SetPoint("LEFT", drop, "RIGHT", 12, 0)
-- Eingabefeld
local box = UI.CreatePanel(right, "bgDeep", "bronze")
box:SetHeight(26)
box:SetPoint("TOPLEFT", right, "TOPLEFT", 0, -92)
box:SetPoint("TOPRIGHT", right, "TOPRIGHT", 0, -92)
local edit = CreateFrame("EditBox", nil, box)
edit:SetAutoFocus(false)
edit:SetPoint("TOPLEFT", box, "TOPLEFT", 8, -2)
edit:SetPoint("BOTTOMRIGHT", box, "BOTTOMRIGHT", -8, 2)
edit:SetMaxLetters(200)
if not edit:SetFont(UI.GetFontPath("body"), 12, "") then
edit:SetFont("Fonts\\FRIZQT__.TTF", 12, "")
end
edit:SetTextColor(UI.Color("text"))
edit:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
local boxHint = UI.CreateLabel(box, L.VENDOR_ADD_BOX, { size = 12, color = "textDim" })
boxHint:SetPoint("LEFT", box, "LEFT", 10, 0)
edit:SetScript("OnTextChanged", function(self)
if self:GetText() == "" then boxHint:Show() else boxHint:Hide() end
end)
-- Zwei Knoepfe: in welche Liste?
local function Submit(which)
local input = pendingDrop
local text = edit:GetText()
if text and text ~= "" then input = text end
if not input then
SetStatus(FW.L.VENDOR_ADD_INVALID, "danger")
return
end
if TryAdd(which, input) then
pendingDrop = nil
edit:SetText("")
edit:ClearFocus()
end
end
local addBlack = UI.CreateButton(right, L.VENDOR_ADD_BLACK, 150, 26, function() Submit("black") end, "primary")
addBlack:SetPoint("TOPLEFT", right, "TOPLEFT", 0, -126)
local addWhite = UI.CreateButton(right, L.VENDOR_ADD_WHITE, 150, 26, function() Submit("white") end, "default")
addWhite:SetPoint("LEFT", addBlack, "RIGHT", 8, 0)
edit:SetScript("OnEnterPressed", function() Submit("black") end)
-- Shift-Klick auf Item-Links in unser Feld
local function InsertLink(link)
if type(link) == "string" and edit:HasFocus() then
edit:Insert(link)
end
end
if hooksecurefunc then
if _G.ChatEdit_InsertLink then
hooksecurefunc("ChatEdit_InsertLink", InsertLink)
elseif _G.ChatFrameUtil and _G.ChatFrameUtil.InsertLink then
hooksecurefunc(_G.ChatFrameUtil, "InsertLink", InsertLink)
end
end
local hint = UI.CreateLabel(right, L.VENDOR_ADD_HINT, { size = 11, color = "textDim", justifyV = "TOP" })
hint:SetPoint("TOPLEFT", right, "TOPLEFT", 0, -160)
hint:SetPoint("TOPRIGHT", right, "TOPRIGHT", 0, -160)
hint:SetHeight(42)
hint:SetWordWrap(true)
status:SetPoint("TOPLEFT", right, "TOPLEFT", 0, -206)
status:SetPoint("TOPRIGHT", right, "TOPRIGHT", 0, -206)
status:SetWordWrap(false)
-- In deinen Taschen
local bagTitle = UI.CreateLabel(right, L.VENDOR_BAGS_HEADER, { font = "heading", size = 14, color = "goldLight" })
bagTitle:SetPoint("TOPLEFT", right, "TOPLEFT", 0, -230)
local bagDivider = UI.CreateDivider(right)
bagDivider:SetPoint("TOPLEFT", right, "TOPLEFT", 0, -254)
bagDivider:SetPoint("TOPRIGHT", right, "TOPRIGHT", 0, -254)
local bagScroll = UI.CreateScrollFrame(right)
bagScroll:SetPoint("TOPLEFT", right, "TOPLEFT", 0, -262)
bagScroll:SetPoint("BOTTOMRIGHT", right, "BOTTOMRIGHT", 0, 164)
local bagEmpty = UI.CreateLabel(bagScroll.child, L.VENDOR_BAGS_EMPTY, { size = 12, color = "textDim" })
bagEmpty:SetPoint("TOPLEFT", bagScroll.child, "TOPLEFT", 4, -6)
-- Protokoll
local logTitle = UI.CreateLabel(right, L.VENDOR_LOG_HEADER, { font = "heading", size = 14, color = "goldLight" })
logTitle:SetPoint("BOTTOMLEFT", right, "BOTTOMLEFT", 0, 134)
local logDivider = UI.CreateDivider(right)
logDivider:SetPoint("BOTTOMLEFT", right, "BOTTOMLEFT", 0, 128)
logDivider:SetPoint("BOTTOMRIGHT", right, "BOTTOMRIGHT", 0, 128)
local logScroll = UI.CreateScrollFrame(right)
logScroll:SetPoint("BOTTOMLEFT", right, "BOTTOMLEFT", 0, 0)
logScroll:SetPoint("BOTTOMRIGHT", right, "BOTTOMRIGHT", 0, 0)
logScroll:SetHeight(120)
local logEmpty = UI.CreateLabel(logScroll.child, L.VENDOR_LOG_EMPTY, { size = 12, color = "textDim" })
logEmpty:SetPoint("TOPLEFT", logScroll.child, "TOPLEFT", 4, -6)
---------------- Aktualisieren ----------------
local function RefreshOne(which, ui, rows)
local list = Vendor:List(which)
for i, entry in ipairs(list) do local entry = entry
local row = rows[i]
if not row then
row = ListRow(ui.scroll.child)
rows[i] = row
end
row:ClearAllPoints()
local y = (i - 1) * (ROW_H + ROW_GAP)
row:SetPoint("TOPLEFT", ui.scroll.child, "TOPLEFT", 0, -y)
row:SetPoint("TOPRIGHT", ui.scroll.child, "TOPRIGHT", 0, -y)
FillItem(row, entry.id, entry.name)
row.idLabel:SetText(tostring(entry.id))
row.remove:SetScript("OnClick", function()
Vendor:Remove(which, entry.id)
end)
row:Show()
end
for i = #list + 1, #rows do
rows[i]:Hide()
end
ui.empty:SetShown(#list == 0)
ui.scroll:SetContentHeight(#list * (ROW_H + ROW_GAP))
ui.count:SetText(#list > 0 and tostring(#list) or "")
end
local function RefreshLists()
RefreshOne("black", blackUI, rowsBlack)
RefreshOne("white", whiteUI, rowsWhite)
end
local function RefreshBags()
local items = Vendor:BagItems()
table.sort(items, function(a, b) return a.id < b.id end)
for i, entry in ipairs(items) do local entry = entry
local row = bagRows[i]
if not row then
row = UI.CreatePanel(bagScroll.child, "card", "bronze")
row:SetHeight(SMALL_H)
row.icon = row:CreateTexture(nil, "ARTWORK")
row.icon:SetSize(22, 22)
row.icon:SetPoint("LEFT", row, "LEFT", 5, 0)
row.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
row.name = UI.CreateLabel(row, "", { size = 12 })
row.name:SetPoint("LEFT", row.icon, "RIGHT", 8, 0)
row.name:SetPoint("RIGHT", row, "RIGHT", -150, 0)
row.name:SetWordWrap(false)
row.never = UI.CreateButton(row, FW.L.VENDOR_BAG_NEVER, 66, 22, nil, "default")
row.never:SetPoint("RIGHT", row, "RIGHT", -4, 0)
row.always = UI.CreateButton(row, FW.L.VENDOR_BAG_ALWAYS, 72, 22, nil, "default")
row.always:SetPoint("RIGHT", row.never, "LEFT", -4, 0)
row.listed = UI.CreateLabel(row, "", { size = 10, color = "textDim", justify = "RIGHT" })
row.listed:SetPoint("RIGHT", row, "RIGHT", -10, 0)
bagRows[i] = row
end
row:ClearAllPoints()
local y = (i - 1) * (SMALL_H + 4)
row:SetPoint("TOPLEFT", bagScroll.child, "TOPLEFT", 0, -y)
row:SetPoint("TOPRIGHT", bagScroll.child, "TOPRIGHT", 0, -y)
FillItem(row, entry.id)
local isBlack, isWhite = Vendor:IsBlack(entry.id), Vendor:IsWhite(entry.id)
row.never:SetShown(not isBlack and not isWhite)
row.always:SetShown(not isBlack and not isWhite)
row.listed:SetShown(isBlack or isWhite)
row.listed:SetText(isBlack and FW.L.VENDOR_BAG_IS_NEVER or (isWhite and FW.L.VENDOR_BAG_IS_ALWAYS or ""))
row.never:SetScript("OnClick", function() TryAdd("black", entry.id) end)
row.always:SetScript("OnClick", function() TryAdd("white", entry.id) end)
row:Show()
end
for i = #items + 1, #bagRows do
bagRows[i]:Hide()
end
bagEmpty:SetShown(#items == 0)
bagScroll:SetContentHeight(#items * (SMALL_H + 4))
end
local function RefreshLog()
local log = Vendor.chardb.log
for i, entry in ipairs(log) do
local line = logRows[i]
if not line then
line = UI.CreateLabel(logScroll.child, "", { size = 11 })
logRows[i] = line
end
line:ClearAllPoints()
line:SetPoint("TOPLEFT", logScroll.child, "TOPLEFT", 4, -((i - 1) * 18 + 4))
line:SetPoint("TOPRIGHT", logScroll.child, "TOPRIGHT", -4, -((i - 1) * 18 + 4))
line:SetWordWrap(false)
line:SetText(string.format(FW.L.VENDOR_LOG_LINE, date("%H:%M", entry.t or 0), entry.name or "?", entry.count or 1,
Vendor.Money(entry.price or 0)))
line:Show()
end
for i = #log + 1, #logRows do
logRows[i]:Hide()
end
logEmpty:SetShown(#log == 0)
logScroll:SetContentHeight(#log * 18 + 8)
end
function f.Refresh()
RefreshLists()
RefreshBags()
RefreshLog()
end
local function IfShown(fn)
return function()
if f:IsShown() then
fn()
end
end
end
FW:On("VENDOR_LIST_CHANGED", IfShown(function() f.Refresh() end), f)
FW:On("VENDOR_LOG_CHANGED", IfShown(RefreshLog), f)
FW:On("VENDOR_COUNT_CHANGED", IfShown(RefreshBags), f)
return f
end
UI:RegisterTab("vendor", {
label = function()
return FW.L.VENDOR_TAB
end,
order = 80,
build = Build,
onShow = function(f)
f.Refresh()
end,
})
------------------------------------------------------------------------
-- Einstellungen
------------------------------------------------------------------------
if UI.RegisterSettingsSection then
UI:RegisterSettingsSection("vendor", {
order = 115,
height = 190,
build = function(parent)
local L = FW.L
local f, stack = UI.CreateSection(parent, L.VENDOR_SET_HEADER)
local boxes = {}
local defs = {
{ key = "gray", label = L.VENDOR_SET_GRAY },
{ key = "auto", label = L.VENDOR_SET_AUTO },
{ key = "chat", label = L.VENDOR_SET_CHAT },
}
for _, def in ipairs(defs) do local def = def
local cb = UI.CreateCheckbox(f, def.label, function()
return Vendor.db.settings[def.key]
end, function(value)
Vendor.db.settings[def.key] = value and true or false
Vendor:Recount()
end)
stack:Add(cb, { fit = true, gap = 4 })
boxes[#boxes + 1] = cb
end
local note = UI.CreateLabel(f, L.VENDOR_SET_NOTE, { size = 11, color = "textDim", justifyV = "TOP" })
stack:Add(note, { fit = true, gap = 10 })
function f.Refresh()
for _, cb in ipairs(boxes) do
cb:Refresh()
end
end
stack:Layout()
return f
end,
refresh = function(frame)
frame.Refresh()
end,
})
end
------------------------------------------------------------------------
-- Knopf am Haendlerfenster
------------------------------------------------------------------------
local sellButton
local function EnsureButton()
if sellButton or not _G.MerchantFrame then
return sellButton
end
local L = FW.L
sellButton = UI.CreateButton(_G.MerchantFrame, L.VENDOR_BUTTON, 110, 20, function()
Vendor:Start(true)
end, "primary")
sellButton:SetPoint("TOP", _G.MerchantFrame, "TOP", 22, -46)
sellButton:SetFrameLevel((_G.MerchantFrame:GetFrameLevel() or 1) + 5)
UI.AttachTooltip(sellButton, function() return FW.L.VENDOR_BUTTON_TIP_TITLE end, function()
local lines = {
FW.L.VENDOR_BUTTON_TIP_RULES,
string.format(FW.L.VENDOR_BUTTON_TIP_COUNT, Vendor.sellable),
}
if Vendor.sellable > 0 then
local _, total = Vendor:Collect()
lines[#lines + 1] = string.format(FW.L.VENDOR_BUTTON_TIP_VALUE, Vendor.Money(total))
end
return lines
end)
return sellButton
end
local function UpdateButton()
local b = EnsureButton()
if not b then return end
local n = Vendor.sellable or 0
b:SetLabel(n > 0 and string.format(FW.L.VENDOR_BUTTON_N, n) or FW.L.VENDOR_BUTTON)
b:SetButtonEnabled(n > 0 and not Vendor.run)
if Vendor.merchant then b:Show() else b:Hide() end
end
FW:On("VENDOR_MERCHANT_CHANGED", function() UpdateButton() end, sellButton or {})
FW:On("VENDOR_COUNT_CHANGED", function() UpdateButton() end, sellButton or {})
FW:On("VENDOR_LOG_CHANGED", function() UpdateButton() end, sellButton or {})
------------------------------------------------------------------------
-- Befehl
------------------------------------------------------------------------
SLASH_FREEWOODSELL1 = "/fwsell"
SlashCmdList["FREEWOODSELL"] = function(msg)
msg = (msg or ""):lower():match("^%s*(.-)%s*$")
if msg == "test" then
Vendor:RunTest()
else
UI:Toggle("vendor")
end
end