mirror of
https://github.com/Lacustat/Freewood-OctoWoW-Core.git
synced 2026-11-04 01:56:57 +00:00
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016onJCSHLwhs9udhC9ZLzk9
776 lines
30 KiB
Lua
776 lines
30 KiB
Lua
-- Freewood-OctoWoW-Core: Reiter "Kanaele" als Chatfenster (Kategorien links, Chat Mitte, Mitglieder rechts).
|
|
-- ALLES UNGEPRUEFT im Spiel. Datenschicht: Core/Channels.lua (Mitglieder) und Core/ChatLog.lua (Verlauf).
|
|
|
|
local ADDON, FW = ...
|
|
|
|
local UI = FW.UI
|
|
local Ch = FW.Channels
|
|
local CL = FW.ChatLog
|
|
|
|
local LEFT_W, RIGHT_W = 220, 220
|
|
local ROW = 24
|
|
local MAX_BUBBLES = 60
|
|
local LOG_LINES = 200
|
|
local LOGO = "Interface\\AddOns\\Freewood-OctoWoW-Core\\Media\\FreeWood_Logo.tga"
|
|
local QMARK = "Interface\\Icons\\INV_Misc_QuestionMark"
|
|
|
|
local ICONS = {
|
|
general = "Interface\\Icons\\INV_Misc_Note_01",
|
|
localdefense = "Interface\\Icons\\INV_Shield_06",
|
|
world = "Interface\\Icons\\INV_Misc_Map_01",
|
|
lookingforgroup = "Interface\\Icons\\INV_Misc_GroupLooking",
|
|
lft = "Interface\\Icons\\INV_Misc_GroupNeedMore",
|
|
}
|
|
local ICON_GUILD = "Interface\\Icons\\INV_Shirt_GuildTabard_01"
|
|
local ICON_GROUP = "Interface\\Icons\\Spell_Holy_PrayerOfHealing"
|
|
local ICON_WHISPER = "Interface\\Icons\\INV_Letter_02"
|
|
|
|
local STD = { general = true, localdefense = true, world = true, lookingforgroup = true,
|
|
allgemein = true, lokaleverteidigung = true }
|
|
|
|
local function Hex(r, g, b)
|
|
return string.format("|cff%02x%02x%02x", math.floor(r * 255 + 0.5), math.floor(g * 255 + 0.5), math.floor(b * 255 + 0.5))
|
|
end
|
|
|
|
local function SetClassIcon(tex, token)
|
|
local c = token and CL.ICON_COORDS[token]
|
|
if c then
|
|
tex:SetTexture(CL.ICON_FILE)
|
|
tex:SetTexCoord(c[1], c[2], c[3], c[4])
|
|
else
|
|
tex:SetTexture(QMARK)
|
|
tex:SetTexCoord(0.08, 0.92, 0.08, 0.92)
|
|
end
|
|
end
|
|
|
|
local function SetIcon(tex, path)
|
|
tex:SetTexture(path or QMARK)
|
|
tex:SetTexCoord(0.08, 0.92, 0.08, 0.92)
|
|
end
|
|
|
|
local G_date = _G.date
|
|
local function TimeText(t)
|
|
if not (t and G_date) then return "" end
|
|
local ok, s = pcall(G_date, "%H:%M", t)
|
|
return ok and s or ""
|
|
end
|
|
|
|
|
|
local function Build(content)
|
|
local L = FW.L
|
|
local f = CreateFrame("Frame", nil, content)
|
|
f:SetAllPoints(content)
|
|
|
|
local selKey = nil
|
|
local entries = {}
|
|
local search = ""
|
|
local seen = { cl = -1, ch = -1, w = 0 }
|
|
local stick = true
|
|
local lastH, lastViewH = 0, 0
|
|
|
|
------------------------------------------------------------------
|
|
-- Kopierfeld und Menue (gemeinsam)
|
|
------------------------------------------------------------------
|
|
local copy = UI.CreatePanel(UIParent, "bgDeep", "gold")
|
|
copy:SetSize(560, 96)
|
|
copy:SetPoint("CENTER", UIParent, "CENTER", 0, 120)
|
|
copy:SetFrameStrata("FULLSCREEN_DIALOG")
|
|
copy:Hide()
|
|
local copyHint = UI.CreateLabel(copy, L.CHAT_COPY_HINT, { size = 11, color = "textDim" })
|
|
copyHint:SetPoint("TOPLEFT", copy, "TOPLEFT", 12, -8)
|
|
local cbox = UI.CreatePanel(copy, "bg", "bronze")
|
|
cbox:SetPoint("TOPLEFT", copy, "TOPLEFT", 10, -28)
|
|
cbox:SetPoint("TOPRIGHT", copy, "TOPRIGHT", -10, -28)
|
|
cbox:SetHeight(26)
|
|
local cedit = CreateFrame("EditBox", nil, cbox)
|
|
cedit:SetAutoFocus(false)
|
|
cedit:SetPoint("TOPLEFT", cbox, "TOPLEFT", 6, -3)
|
|
cedit:SetPoint("BOTTOMRIGHT", cbox, "BOTTOMRIGHT", -6, 3)
|
|
cedit:SetMaxLetters(600)
|
|
if not cedit:SetFont(UI.GetFontPath("body"), 12, "") then cedit:SetFont("Fonts\\FRIZQT__.TTF", 12, "") end
|
|
cedit:SetTextColor(UI.Color("text"))
|
|
cedit:SetScript("OnEscapePressed", function() copy:Hide() end)
|
|
local copyMsg, copyUrl
|
|
local bMsg = UI.CreateButton(copy, L.CHAT_COPY_MSG, 100, 24, function()
|
|
if copyMsg then cedit:SetText(copyMsg); cedit:SetFocus(); cedit:HighlightText() end
|
|
end)
|
|
bMsg:SetPoint("BOTTOMLEFT", copy, "BOTTOMLEFT", 10, 8)
|
|
local bUrl = UI.CreateButton(copy, L.CHAT_COPY_LINK, 100, 24, function()
|
|
if copyUrl then cedit:SetText(copyUrl); cedit:SetFocus(); cedit:HighlightText() end
|
|
end)
|
|
bUrl:SetPoint("LEFT", bMsg, "RIGHT", 6, 0)
|
|
local bX = UI.CreateButton(copy, "X", 30, 24, function() copy:Hide() end)
|
|
bX:SetPoint("BOTTOMRIGHT", copy, "BOTTOMRIGHT", -10, 8)
|
|
|
|
local function ShowCopy(text, url)
|
|
copyMsg, copyUrl = text, url
|
|
cedit:SetText(text or "")
|
|
if url then bUrl:Show() else bUrl:Hide() end
|
|
copy:Show()
|
|
cedit:SetFocus()
|
|
cedit:HighlightText()
|
|
end
|
|
|
|
local menu, menuCatcher, menuTitle
|
|
local menuName
|
|
local selectKey, Redraw
|
|
local input
|
|
local function HideMenu()
|
|
if menu then menu:Hide() end
|
|
if menuCatcher then menuCatcher:Hide() end
|
|
end
|
|
local function BuildMenu()
|
|
menuCatcher = CreateFrame("Button", nil, UIParent)
|
|
menuCatcher:SetAllPoints(UIParent)
|
|
menuCatcher:SetFrameStrata("FULLSCREEN_DIALOG")
|
|
menuCatcher:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
|
menuCatcher:SetScript("OnClick", HideMenu)
|
|
menuCatcher:Hide()
|
|
menu = UI.CreatePanel(UIParent, "bgDeep", "gold")
|
|
menu:SetSize(180, 28 + 6 * 24 + 8)
|
|
menu:SetFrameStrata("FULLSCREEN_DIALOG")
|
|
menu:SetFrameLevel(menuCatcher:GetFrameLevel() + 5)
|
|
menu:Hide()
|
|
menuTitle = UI.CreateLabel(menu, "", { size = 12, color = "gold" })
|
|
menuTitle:SetPoint("TOPLEFT", menu, "TOPLEFT", 10, -8)
|
|
local acts = {
|
|
{ L.CHAN_WHISPER, function(n)
|
|
CL.OpenWhisper(n)
|
|
if selectKey then selectKey("w:" .. n) end
|
|
if input then input:SetFocus() end
|
|
end },
|
|
{ L.CHAN_INVITE, function(n) Ch.Invite(n) end },
|
|
{ L.CHAN_IGNORE, function(n) Ch.Ignore(n) end },
|
|
{ L.CHAT_MENU_FRIEND, function(n) if _G.AddFriend then pcall(_G.AddFriend, n) end end },
|
|
{ L.CHAT_MENU_WHO, function(n) CL.Ask(n, true) end },
|
|
{ L.CHAT_MENU_COPY, function(n) ShowCopy(n, nil) end },
|
|
}
|
|
local i = 1
|
|
while i <= 6 do
|
|
local a = acts[i]
|
|
local b = UI.CreateButton(menu, a[1], 164, 22, function()
|
|
local n = menuName
|
|
HideMenu()
|
|
if n then a[2](n) end
|
|
end)
|
|
b:SetPoint("TOPLEFT", menu, "TOPLEFT", 8, -28 - (i - 1) * 24)
|
|
i = i + 1
|
|
end
|
|
end
|
|
local function ShowMenu(name)
|
|
if not name or name == "" then return end
|
|
if not menu then BuildMenu() end
|
|
menuName = name
|
|
menuTitle:SetText(name)
|
|
local x, y = GetCursorPosition()
|
|
local sc = UIParent:GetEffectiveScale()
|
|
menu:ClearAllPoints()
|
|
menu:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / sc, y / sc)
|
|
menuCatcher:Show()
|
|
menu:Show()
|
|
end
|
|
|
|
------------------------------------------------------------------
|
|
-- Linke Spalte: Kategorien und Chats
|
|
------------------------------------------------------------------
|
|
local left = UI.CreateScrollFrame(f)
|
|
left:SetPoint("TOPLEFT", f, "TOPLEFT", 0, 0)
|
|
left:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 0, 0)
|
|
left:SetWidth(LEFT_W)
|
|
local lrows = {}
|
|
|
|
local function LeftRow(i)
|
|
local r = lrows[i]
|
|
if not r then
|
|
r = CreateFrame("Button", nil, left.child)
|
|
r:SetHeight(ROW)
|
|
r:SetPoint("TOPLEFT", left.child, "TOPLEFT", 0, -(i - 1) * ROW)
|
|
r:SetPoint("TOPRIGHT", left.child, "TOPRIGHT", 0, -(i - 1) * ROW)
|
|
r:RegisterForClicks("LeftButtonUp")
|
|
r.sel = r:CreateTexture(nil, "BACKGROUND")
|
|
r.sel:SetAllPoints()
|
|
r.sel:SetColorTexture(UI.Color("bronze", 0.35))
|
|
r.hl = r:CreateTexture(nil, "HIGHLIGHT")
|
|
r.hl:SetAllPoints()
|
|
r.hl:SetColorTexture(UI.Color("gold", 0.15))
|
|
r.icon = r:CreateTexture(nil, "ARTWORK")
|
|
r.icon:SetSize(18, 18)
|
|
r.icon:SetPoint("LEFT", r, "LEFT", 8, 0)
|
|
r.text = UI.CreateLabel(r, "", { size = 12, justify = "LEFT" })
|
|
r.text:SetPoint("LEFT", r.icon, "RIGHT", 6, 0)
|
|
r.text:SetPoint("RIGHT", r, "RIGHT", -26, 0)
|
|
r.x = CreateFrame("Button", nil, r)
|
|
r.x:SetSize(18, 18)
|
|
r.x:SetPoint("RIGHT", r, "RIGHT", -4, 0)
|
|
r.xt = UI.CreateLabel(r.x, "x", { size = 13, color = "textDim" })
|
|
r.xt:SetPoint("CENTER", r.x, "CENTER", 0, 0)
|
|
r.x:SetScript("OnEnter", function() r.xt:SetTextColor(UI.Color("danger")) end)
|
|
r.x:SetScript("OnLeave", function() r.xt:SetTextColor(UI.Color("textDim")) end)
|
|
lrows[i] = r
|
|
end
|
|
return r
|
|
end
|
|
|
|
local function BuildEntries()
|
|
local std, other, service = {}, {}, {}
|
|
local k = 1
|
|
while k <= table.getn(Ch.list) do
|
|
local e = Ch.list[k]
|
|
local low = string.lower(e.name)
|
|
local en = { key = "c:" .. e.name, kind = "channel", num = e.num, label = e.num .. ". " .. e.name,
|
|
count = e.stamp and e.count or nil, e = e }
|
|
if string.find(e.name, "^Freewood") then
|
|
en.mode = "data"; en.logo = true
|
|
service[table.getn(service) + 1] = en
|
|
elseif low == "lft" then
|
|
en.mode = "log"; en.icon = ICONS.lft
|
|
service[table.getn(service) + 1] = en
|
|
elseif STD[low] then
|
|
en.mode = "bubble"; en.icon = ICONS[low] or ICONS.general
|
|
std[table.getn(std) + 1] = en
|
|
else
|
|
en.mode = "bubble"; en.icon = ICONS[low] or ICONS.general
|
|
other[table.getn(other) + 1] = en
|
|
end
|
|
k = k + 1
|
|
end
|
|
local out = {}
|
|
local function Head(t) out[table.getn(out) + 1] = { head = t } end
|
|
local function Add(en) out[table.getn(out) + 1] = en end
|
|
if table.getn(std) > 0 then
|
|
Head(L.CHAT_CAT_STD)
|
|
k = 1; while k <= table.getn(std) do Add(std[k]); k = k + 1 end
|
|
end
|
|
if table.getn(other) > 0 then
|
|
Head(L.CHAT_CAT_OTHER)
|
|
k = 1; while k <= table.getn(other) do Add(other[k]); k = k + 1 end
|
|
end
|
|
Head(L.CHAT_CAT_GUILD)
|
|
Add({ key = "guild", kind = "guild", mode = "bubble", label = L.CHAT_GUILD, icon = ICON_GUILD,
|
|
count = table.getn(CL.guild) })
|
|
Head(L.CHAT_CAT_GROUP)
|
|
Add({ key = "group", kind = "group", mode = "bubble", label = L.CHAT_GROUP, icon = ICON_GROUP })
|
|
Head(L.CHAT_CAT_WHISPER)
|
|
local ws = CL.OpenWhispers()
|
|
k = 1
|
|
while k <= table.getn(ws) do
|
|
Add({ key = "w:" .. ws[k].name, kind = "whisper", mode = "bubble", label = ws[k].name,
|
|
icon = ICON_WHISPER, closable = true, name = ws[k].name })
|
|
k = k + 1
|
|
end
|
|
if table.getn(ws) == 0 then out[table.getn(out) + 1] = { note = L.CHAT_NOWHISPER } end
|
|
if table.getn(service) > 0 then
|
|
Head(L.CHAT_CAT_SERVICE)
|
|
k = 1; while k <= table.getn(service) do Add(service[k]); k = k + 1 end
|
|
end
|
|
return out
|
|
end
|
|
|
|
local function EntryByKey(key)
|
|
local k = 1
|
|
while k <= table.getn(entries) do
|
|
if entries[k].key == key then return entries[k] end
|
|
k = k + 1
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function FirstEntry()
|
|
local k = 1
|
|
while k <= table.getn(entries) do
|
|
if entries[k].key then return entries[k] end
|
|
k = k + 1
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function DrawLeft()
|
|
entries = BuildEntries()
|
|
if not selKey or not EntryByKey(selKey) then
|
|
local fe = FirstEntry()
|
|
selKey = fe and fe.key or nil
|
|
stick = true
|
|
end
|
|
local n = table.getn(entries)
|
|
local i = 1
|
|
local y = 0
|
|
while i <= n do
|
|
local en = entries[i]
|
|
local r = LeftRow(i)
|
|
r:ClearAllPoints()
|
|
r:SetPoint("TOPLEFT", left.child, "TOPLEFT", 0, -y)
|
|
r:SetPoint("TOPRIGHT", left.child, "TOPRIGHT", 0, -y)
|
|
r.x:Hide()
|
|
if en.head then
|
|
r:SetHeight(26)
|
|
r.sel:Hide(); r.hl:Hide(); r.icon:Hide()
|
|
r.text:ClearAllPoints()
|
|
r.text:SetPoint("LEFT", r, "LEFT", 8, -4)
|
|
r.text:SetPoint("RIGHT", r, "RIGHT", -4, -4)
|
|
r.text:SetText(Hex(UI.Color("gold")) .. string.upper(en.head) .. "|r")
|
|
r:SetScript("OnClick", nil)
|
|
y = y + 26
|
|
elseif en.note then
|
|
r:SetHeight(44)
|
|
r.sel:Hide(); r.hl:Hide(); r.icon:Hide()
|
|
r.text:ClearAllPoints()
|
|
r.text:SetPoint("LEFT", r, "LEFT", 8, 0)
|
|
r.text:SetPoint("RIGHT", r, "RIGHT", -8, 0)
|
|
r.text:SetText("|cff9a978c" .. en.note .. "|r")
|
|
r:SetScript("OnClick", nil)
|
|
y = y + 44
|
|
else
|
|
r:SetHeight(ROW)
|
|
r.hl:Show(); r.icon:Show()
|
|
r.text:ClearAllPoints()
|
|
r.text:SetPoint("LEFT", r.icon, "RIGHT", 6, 0)
|
|
r.text:SetPoint("RIGHT", r, "RIGHT", en.closable and -26 or -6, 0)
|
|
if en.logo then
|
|
r.icon:SetTexture(LOGO); r.icon:SetTexCoord(0, 1, 0, 1)
|
|
else
|
|
SetIcon(r.icon, en.icon)
|
|
end
|
|
local txt = en.label
|
|
if en.count then txt = txt .. " |cff9a978c(" .. en.count .. ")|r" end
|
|
local un = CL.unread[en.key]
|
|
if un and un > 0 and en.key ~= selKey then txt = txt .. " |cffc89b3c+" .. un .. "|r" end
|
|
r.text:SetText(txt)
|
|
if en.key == selKey then r.sel:Show() else r.sel:Hide() end
|
|
local key = en.key
|
|
r:SetScript("OnClick", function() selectKey(key) end)
|
|
if en.closable then
|
|
local nm = en.name
|
|
r.x:Show()
|
|
r.x:SetScript("OnClick", function()
|
|
CL.CloseWhisper(nm)
|
|
if selKey == "w:" .. nm then selKey = nil end
|
|
Redraw()
|
|
end)
|
|
end
|
|
y = y + ROW
|
|
end
|
|
r:Show()
|
|
i = i + 1
|
|
end
|
|
while lrows[i] do lrows[i]:Hide(); i = i + 1 end
|
|
left:SetContentHeight(math.max(1, y))
|
|
end
|
|
|
|
------------------------------------------------------------------
|
|
-- Mitte: Kopf, Chat (Blasen / Log), Eingabe
|
|
------------------------------------------------------------------
|
|
local head = UI.CreateLabel(f, "", { font = "heading", size = 15, color = "gold", justify = "LEFT" })
|
|
head:SetPoint("TOPLEFT", f, "TOPLEFT", LEFT_W + 14, -2)
|
|
head:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(RIGHT_W + 14), -2)
|
|
local sub = UI.CreateLabel(f, "", { size = 11, color = "textDim", justify = "LEFT" })
|
|
sub:SetPoint("TOPLEFT", f, "TOPLEFT", LEFT_W + 14, -24)
|
|
sub:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(RIGHT_W + 14), -24)
|
|
|
|
local chat = UI.CreateScrollFrame(f)
|
|
chat:SetPoint("TOPLEFT", f, "TOPLEFT", LEFT_W + 14, -44)
|
|
chat:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -(RIGHT_W + 14), 40)
|
|
|
|
local logArea = UI.CreateTextArea(f)
|
|
logArea:SetPoint("TOPLEFT", f, "TOPLEFT", LEFT_W + 14, -44)
|
|
logArea:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -(RIGHT_W + 14), 40)
|
|
logArea:Hide()
|
|
|
|
local empty = UI.CreateLabel(f, "", { size = 12, color = "textDim", justify = "LEFT", justifyV = "TOP" })
|
|
empty:SetPoint("TOPLEFT", f, "TOPLEFT", LEFT_W + 24, -56)
|
|
empty:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(RIGHT_W + 24), -56)
|
|
empty:SetHeight(60)
|
|
|
|
local measure = f:CreateFontString(nil, "OVERLAY")
|
|
UI.SetFont(measure, "body", 12)
|
|
measure:Hide()
|
|
|
|
local ibox = UI.CreatePanel(f, "bgDeep", "bronze")
|
|
ibox:SetHeight(28)
|
|
ibox:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", LEFT_W + 14, 6)
|
|
ibox:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -(RIGHT_W + 14 + 96), 6)
|
|
input = CreateFrame("EditBox", nil, ibox)
|
|
input:SetAutoFocus(false)
|
|
input:SetPoint("TOPLEFT", ibox, "TOPLEFT", 8, -3)
|
|
input:SetPoint("BOTTOMRIGHT", ibox, "BOTTOMRIGHT", -8, 3)
|
|
input:SetMaxLetters(255)
|
|
if not input:SetFont(UI.GetFontPath("body"), 12, "") then input:SetFont("Fonts\\FRIZQT__.TTF", 12, "") end
|
|
input:SetTextColor(UI.Color("text"))
|
|
local ihint = UI.CreateLabel(ibox, L.CHAT_INPUT, { size = 12, color = "textDim" })
|
|
ihint:SetPoint("LEFT", ibox, "LEFT", 10, 0)
|
|
local function DoSend()
|
|
local txt = input:GetText() or ""
|
|
local en = selKey and EntryByKey(selKey)
|
|
if txt == "" or not en then return end
|
|
if en.kind == "channel" then CL.Send("channel", en.num, txt)
|
|
elseif en.kind == "guild" then CL.Send("guild", nil, txt)
|
|
elseif en.kind == "group" then CL.Send("group", nil, txt)
|
|
elseif en.kind == "whisper" then CL.Send("whisper", en.name, txt) end
|
|
input:SetText("")
|
|
end
|
|
input:SetScript("OnEnterPressed", function() DoSend(); input:SetFocus() end)
|
|
input:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
|
|
input:SetScript("OnTextChanged", function(self)
|
|
if (self:GetText() or "") == "" then ihint:Show() else ihint:Hide() end
|
|
end)
|
|
local sendBtn = UI.CreateButton(f, L.CHAT_SEND, 88, 28, DoSend)
|
|
sendBtn:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -(RIGHT_W + 14), 6)
|
|
|
|
-- Blasen-Pool
|
|
local bubbles = {}
|
|
local function Bubble(i)
|
|
local b = bubbles[i]
|
|
if b then return b end
|
|
b = CreateFrame("Frame", nil, chat.child)
|
|
b:SetHeight(60)
|
|
b.avBtn = CreateFrame("Button", nil, b)
|
|
b.avBtn:SetSize(36, 36)
|
|
b.avBtn:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
|
b.av = b.avBtn:CreateTexture(nil, "ARTWORK")
|
|
b.av:SetAllPoints()
|
|
b.lvl = UI.CreateLabel(b, "", { size = 11, color = "textDim" })
|
|
b.panel = CreateFrame("Button", nil, b)
|
|
b.panel:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
|
b.bg = b.panel:CreateTexture(nil, "BACKGROUND")
|
|
b.bg:SetAllPoints()
|
|
b.hl = b.panel:CreateTexture(nil, "HIGHLIGHT")
|
|
b.hl:SetAllPoints()
|
|
b.hl:SetColorTexture(1, 1, 1, 0.05)
|
|
b.name = UI.CreateLabel(b.panel, "", { size = 12, justify = "LEFT" })
|
|
b.name:SetPoint("TOPLEFT", b.panel, "TOPLEFT", 10, -7)
|
|
b.time = UI.CreateLabel(b.panel, "", { size = 10, color = "textDim", justify = "RIGHT" })
|
|
b.time:SetPoint("TOPRIGHT", b.panel, "TOPRIGHT", -8, -8)
|
|
b.text = UI.CreateLabel(b.panel, "", { size = 12, justify = "LEFT", justifyV = "TOP" })
|
|
b.text:SetPoint("TOPLEFT", b.panel, "TOPLEFT", 10, -24)
|
|
bubbles[i] = b
|
|
return b
|
|
end
|
|
|
|
local function Plain(t) return CL.Plain(t) end
|
|
|
|
local function DrawBubbles(key)
|
|
local lines = CL.Lines(key) or {}
|
|
local n = table.getn(lines)
|
|
local first = math.max(1, n - MAX_BUBBLES + 1)
|
|
local areaW = chat.child:GetWidth()
|
|
if not areaW or areaW < 200 then areaW = 560 end
|
|
seen.w = areaW
|
|
local maxBub = math.floor(areaW * 0.68)
|
|
local atBottom = stick or (chat.scroll:GetVerticalScroll() >= lastH - lastViewH - 8)
|
|
local y = 6
|
|
local idx = 0
|
|
local i = first
|
|
while i <= n do
|
|
local ln = lines[i]
|
|
idx = idx + 1
|
|
local b = Bubble(idx)
|
|
local own = ln.o ~= nil
|
|
local who = ln.s or "?"
|
|
local cls, lv = CL.Info(who)
|
|
SetClassIcon(b.av, cls)
|
|
b.lvl:SetText(lv and tostring(lv) or "?")
|
|
local r, g, bl = CL.ClassColor(cls)
|
|
b.name:SetTextColor(r, g, bl)
|
|
b.name:SetText(who)
|
|
b.time:SetText(TimeText(ln.t))
|
|
local txt = ln.m or ""
|
|
measure:SetText(Plain(txt))
|
|
local tw = measure:GetStringWidth() or 0
|
|
if tw <= 0 then tw = maxBub end
|
|
local nameW = (b.name:GetStringWidth() or 60) + 50
|
|
local pw = math.floor(math.max(math.min(math.max(tw, nameW) + 22, maxBub), 110))
|
|
b.text:SetWidth(pw - 20)
|
|
b.text:SetText(txt)
|
|
local th = (b.text.GetStringHeight and b.text:GetStringHeight()) or b.text:GetHeight() or 14
|
|
if not th or th < 14 then th = 14 end
|
|
local ph = math.floor(th + 38)
|
|
b.panel:SetSize(pw, ph)
|
|
if own then b.bg:SetColorTexture(0.13, 0.27, 0.46, 0.95) else b.bg:SetColorTexture(UI.Color("card", 0.95)) end
|
|
local rowH = math.max(ph, 56)
|
|
b:SetSize(areaW - 8, rowH)
|
|
b:ClearAllPoints()
|
|
b:SetPoint("TOPLEFT", chat.child, "TOPLEFT", 0, -y)
|
|
b.avBtn:ClearAllPoints(); b.lvl:ClearAllPoints(); b.panel:ClearAllPoints()
|
|
if own then
|
|
b.avBtn:SetPoint("TOPRIGHT", b, "TOPRIGHT", -6, 0)
|
|
b.lvl:SetPoint("TOP", b.avBtn, "BOTTOM", 0, -1)
|
|
b.panel:SetPoint("TOPRIGHT", b.avBtn, "TOPLEFT", -8, 0)
|
|
else
|
|
b.avBtn:SetPoint("TOPLEFT", b, "TOPLEFT", 6, 0)
|
|
b.lvl:SetPoint("TOP", b.avBtn, "BOTTOM", 0, -1)
|
|
b.panel:SetPoint("TOPLEFT", b.avBtn, "TOPRIGHT", 8, 0)
|
|
end
|
|
local msg, sender = Plain(txt), who
|
|
local url = CL.FirstUrl(txt)
|
|
b.avBtn:SetScript("OnClick", function() ShowMenu(sender) end)
|
|
b.panel:SetScript("OnClick", function(_, btn)
|
|
btn = btn or arg1
|
|
if btn == "RightButton" then ShowMenu(sender) else ShowCopy(msg, url) end
|
|
end)
|
|
b:Show()
|
|
y = y + rowH + 8
|
|
i = i + 1
|
|
end
|
|
idx = idx + 1
|
|
while bubbles[idx] do bubbles[idx]:Hide(); idx = idx + 1 end
|
|
local viewH = chat.scroll:GetHeight() or 300
|
|
chat:SetContentHeight(math.max(1, y))
|
|
lastH, lastViewH = y, viewH
|
|
if atBottom then
|
|
chat.scroll:SetVerticalScroll(math.max(0, y - viewH))
|
|
chat.UpdateScroll()
|
|
stick = false
|
|
end
|
|
return n
|
|
end
|
|
|
|
local function DrawLog(key)
|
|
local lines = CL.Lines(key) or {}
|
|
local n = table.getn(lines)
|
|
local first = math.max(1, n - LOG_LINES + 1)
|
|
local parts = {}
|
|
local i = first
|
|
while i <= n do
|
|
local ln = lines[i]
|
|
parts[table.getn(parts) + 1] = "[" .. TimeText(ln.t) .. "] " .. (ln.s or "?") .. ": " .. Plain(ln.m or "")
|
|
i = i + 1
|
|
end
|
|
if not logArea.edit:HasFocus() then
|
|
local text = table.concat(parts, "\n")
|
|
if logArea.lastText ~= text then
|
|
logArea.lastText = text
|
|
logArea:SetText(text)
|
|
if C_Timer and C_Timer.After then
|
|
C_Timer.After(0.15, function()
|
|
local h = logArea.child:GetHeight() or 0
|
|
local v = logArea.scroll:GetHeight() or 0
|
|
logArea.scroll:SetVerticalScroll(math.max(0, h - v))
|
|
logArea.UpdateScroll()
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
return n
|
|
end
|
|
|
|
local function DrawCenter()
|
|
local en = selKey and EntryByKey(selKey)
|
|
if not en then
|
|
head:SetText(""); sub:SetText(""); empty:SetText("")
|
|
chat:Hide(); logArea:Hide(); ibox:Hide(); sendBtn:Hide()
|
|
return
|
|
end
|
|
local title = en.label
|
|
if en.kind == "whisper" then
|
|
local c, lv = CL.Info(en.name)
|
|
title = en.name
|
|
sub:SetText((lv and (tostring(lv)) or "?") .. " - " .. L.CHAT_KEPT)
|
|
else
|
|
sub:SetText(L.CHAT_KEPT)
|
|
end
|
|
head:SetText(title)
|
|
local canWrite = en.mode ~= "data"
|
|
if canWrite then ibox:Show(); sendBtn:Show() else ibox:Hide(); sendBtn:Hide() end
|
|
if en.mode == "data" then
|
|
chat:Hide(); logArea:Hide()
|
|
empty:SetText(L.CHAT_DATA)
|
|
elseif en.mode == "log" then
|
|
chat:Hide(); logArea:Show()
|
|
local n = DrawLog(en.key)
|
|
if n == 0 then empty:SetText(L.CHAT_EMPTY) else empty:SetText("") end
|
|
else
|
|
logArea:Hide(); chat:Show()
|
|
local n = DrawBubbles(en.key)
|
|
if n == 0 then empty:SetText(L.CHAT_EMPTY) else empty:SetText("") end
|
|
end
|
|
if en.kind == "guild" and not (IsInGuild and IsInGuild()) then empty:SetText(L.CHAT_NOGUILD) end
|
|
end
|
|
|
|
------------------------------------------------------------------
|
|
-- Rechte Spalte: Suche und Mitglieder
|
|
------------------------------------------------------------------
|
|
local box = UI.CreatePanel(f, "bgDeep", "bronze")
|
|
box:SetHeight(26)
|
|
box:SetPoint("TOPRIGHT", f, "TOPRIGHT", 0, -2)
|
|
box:SetWidth(RIGHT_W)
|
|
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(40)
|
|
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 hint = UI.CreateLabel(box, L.CHAN_SEARCH, { size = 11, color = "textDim" })
|
|
hint:SetPoint("LEFT", box, "LEFT", 8, 0)
|
|
hint:SetPoint("RIGHT", box, "RIGHT", -4, 0)
|
|
|
|
local rhead = UI.CreateLabel(f, "", { size = 11, color = "textDim", justify = "LEFT" })
|
|
rhead:SetPoint("TOPRIGHT", f, "TOPRIGHT", 0, -34)
|
|
rhead:SetWidth(RIGHT_W)
|
|
local refresh = UI.CreateButton(f, L.CHAN_REFRESH, RIGHT_W, 24, function()
|
|
Ch.RefreshAll()
|
|
CL.RequestGuild()
|
|
end)
|
|
refresh:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 0, 6)
|
|
|
|
local right = UI.CreateScrollFrame(f)
|
|
right:SetPoint("TOPRIGHT", f, "TOPRIGHT", 0, -54)
|
|
right:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 0, 36)
|
|
right:SetWidth(RIGHT_W)
|
|
local rrows = {}
|
|
local function RightRow(i)
|
|
local r = rrows[i]
|
|
if not r then
|
|
r = CreateFrame("Button", nil, right.child)
|
|
r:SetHeight(22)
|
|
r:SetPoint("TOPLEFT", right.child, "TOPLEFT", 0, -(i - 1) * 22)
|
|
r:SetPoint("TOPRIGHT", right.child, "TOPRIGHT", 0, -(i - 1) * 22)
|
|
r:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
|
r.hl = r:CreateTexture(nil, "HIGHLIGHT")
|
|
r.hl:SetAllPoints()
|
|
r.hl:SetColorTexture(UI.Color("gold", 0.15))
|
|
r.icon = r:CreateTexture(nil, "ARTWORK")
|
|
r.icon:SetSize(16, 16)
|
|
r.icon:SetPoint("LEFT", r, "LEFT", 4, 0)
|
|
r.text = UI.CreateLabel(r, "", { size = 12, justify = "LEFT" })
|
|
r.text:SetPoint("LEFT", r.icon, "RIGHT", 6, 0)
|
|
r.text:SetPoint("RIGHT", r, "RIGHT", -4, 0)
|
|
rrows[i] = r
|
|
end
|
|
return r
|
|
end
|
|
|
|
local function MembersOf(en)
|
|
local out = {}
|
|
if not en then return out end
|
|
if en.kind == "channel" and en.e then
|
|
local k = 1
|
|
while k <= table.getn(en.e.members) do
|
|
out[table.getn(out) + 1] = { name = en.e.members[k] }
|
|
k = k + 1
|
|
end
|
|
elseif en.kind == "guild" then
|
|
local k = 1
|
|
while k <= table.getn(CL.guild) do
|
|
local g = CL.guild[k]
|
|
out[table.getn(out) + 1] = { name = g.name, class = g.class, level = g.level }
|
|
k = k + 1
|
|
end
|
|
elseif en.kind == "group" then
|
|
out = CL.ScanGroup()
|
|
elseif en.kind == "whisper" then
|
|
out[1] = { name = en.name }
|
|
end
|
|
return out
|
|
end
|
|
|
|
local function DrawRight()
|
|
local en = selKey and EntryByKey(selKey)
|
|
local items = {}
|
|
if search ~= "" then
|
|
local hits = Ch.Find(search, 300)
|
|
local h = 1
|
|
while h <= table.getn(hits) do
|
|
items[h] = { name = hits[h].name, extra = table.concat(hits[h].chans, ", ") }
|
|
h = h + 1
|
|
end
|
|
rhead:SetText(table.getn(items) == 0 and L.CHAN_NO_HITS or (Ch.moreFound and L.CHAN_MORE or ""))
|
|
Ch.moreFound = nil
|
|
else
|
|
items = MembersOf(en)
|
|
local title = (en and en.kind == "guild") and L.CHAT_ONLINE_HDR or L.CHAT_MEMBERS_HDR
|
|
rhead:SetText(title .. " (" .. table.getn(items) .. ")")
|
|
if en and en.kind == "group" and table.getn(items) <= 1 then rhead:SetText(L.CHAT_NOGROUP) end
|
|
end
|
|
local cnt = math.min(table.getn(items), 400)
|
|
local i = 1
|
|
while i <= cnt do
|
|
local it = items[i]
|
|
local r = RightRow(i)
|
|
local nm = it.name
|
|
local cls, lv = it.class, it.level
|
|
if not cls or not lv then
|
|
local c2, l2 = CL.Info(nm)
|
|
cls = cls or c2; lv = lv or l2
|
|
end
|
|
SetClassIcon(r.icon, cls)
|
|
local cr, cg, cb = CL.ClassColor(cls)
|
|
local txt = Hex(cr, cg, cb) .. nm .. "|r"
|
|
if lv then txt = txt .. " |cff9a978c" .. lv .. "|r" end
|
|
if it.extra then txt = txt .. " |cff9a978c" .. it.extra .. "|r" end
|
|
r.text:SetText(txt)
|
|
r:SetScript("OnClick", function() ShowMenu(nm) end)
|
|
r:Show()
|
|
i = i + 1
|
|
end
|
|
while rrows[i] do rrows[i]:Hide(); i = i + 1 end
|
|
right:SetContentHeight(math.max(1, cnt * 22))
|
|
end
|
|
|
|
edit:SetScript("OnTextChanged", function(self)
|
|
search = self:GetText() or ""
|
|
if search == "" then hint:Show() else hint:Hide() end
|
|
right:ScrollToTop()
|
|
DrawRight()
|
|
end)
|
|
|
|
------------------------------------------------------------------
|
|
-- Zusammenspiel
|
|
------------------------------------------------------------------
|
|
Redraw = function()
|
|
seen.cl, seen.ch = CL.version, Ch.version
|
|
DrawLeft()
|
|
CL.viewKey = selKey
|
|
if selKey then CL.unread[selKey] = nil end
|
|
DrawCenter()
|
|
DrawRight()
|
|
end
|
|
|
|
selectKey = function(key)
|
|
selKey = key
|
|
stick = true
|
|
CL.unread[key] = nil
|
|
Redraw()
|
|
end
|
|
|
|
f.Refresh = function()
|
|
CL.viewVisible = true
|
|
if Ch.dirty or table.getn(Ch.list) == 0 then
|
|
Ch.dirty = nil
|
|
Ch.RefreshAll()
|
|
else
|
|
Ch.Rescan()
|
|
end
|
|
CL.RequestGuild()
|
|
stick = true
|
|
Redraw()
|
|
end
|
|
|
|
f:SetScript("OnHide", function()
|
|
CL.viewVisible = false
|
|
HideMenu()
|
|
copy:Hide()
|
|
end)
|
|
|
|
local acc = 0
|
|
f:SetScript("OnUpdate", function(_, el)
|
|
acc = acc + (el or arg1 or 0)
|
|
if acc < 0.4 then return end
|
|
acc = 0
|
|
if not f:IsShown() then return end
|
|
local w = chat.child:GetWidth()
|
|
if seen.cl ~= CL.version or seen.ch ~= Ch.version or (w and math.abs(w - seen.w) > 4) then
|
|
Redraw()
|
|
end
|
|
end)
|
|
|
|
return f
|
|
end
|
|
|
|
UI:RegisterTab("channels", {
|
|
label = function() return FW.L.TAB_CHANNELS end,
|
|
order = 115,
|
|
build = Build,
|
|
onShow = function(f) f.Refresh() end,
|
|
})
|