Repository navigation
Expand file tree
/
Copy pathUI.lua
More file actions
477 lines (443 loc) · 18.5 KB
/
Copy pathUI.lua
File metadata and controls
477 lines (443 loc) · 18.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
-- Wick's Poisons and Things
-- UI.lua: the compact strip and the details panel.
--
-- The strip is two blades side by side, each showing its coating and how
-- long it has left. Clicking a blade recoats it, which is the same secure
-- button the keybind uses. Everything else lives in tooltips and in the
-- panel a right-click opens.
local ADDON, ns = ...
if not WickCore then return end -- said once in Core.lua
local Core = WickCore
local Chrome, R = Core.Chrome, Core.Restrict
local C = Chrome.Colors
local UI = {}
ns.UI = UI
local RED = { 0.80, 0.30, 0.30, 1 }
local AMBER = { 0.85, 0.65, 0.25, 1 }
local BLANK = "Interface\\Icons\\INV_Misc_QuestionMark"
local function tint(fs, c) fs:SetTextColor(c[1], c[2], c[3], c[4] or 1) end
local function handName(hand) return hand == "main" and "Main hand" or "Off hand" end
local function minutesLeft(h)
if not h.msLeft then return nil end
return h.msLeft / 60000
end
local function clockText(h)
local m = minutesLeft(h)
if not m then return h.coated and "on" or "" end
if m >= 60 then return ("%dh"):format(math.floor(m / 60)) end
if m >= 1 then return ("%dm"):format(math.floor(m)) end
return ("%ds"):format(math.max(0, math.floor(h.msLeft / 1000)))
end
local function handColor(h, warnMinutes)
if not h.hasWeapon then return C.muted end
if not h.coated then return RED end
local m = minutesLeft(h)
if m and m < warnMinutes then return AMBER end
return C.fel
end
local function handLines(tt, h, pick, warnMinutes)
tt:AddLine(handName(h.hand), 1, 1, 1)
if not h.hasWeapon then
tt:AddLine("Nothing equipped in this hand", 0.6, 0.6, 0.6)
return
end
tt:AddLine(h.weapon or "Weapon", 0.83, 0.78, 0.63)
if h.coated then
local m = minutesLeft(h)
local c = handColor(h, warnMinutes)
-- Which coating, here rather than on the strip, where it
-- truncated to three letters on both blades.
tt:AddLine(m and ("%s, %d minutes left"):format(h.coating or "Coated", math.floor(m))
or (h.coating or "Coated"), c[1], c[2], c[3])
if h.charges then tt:AddLine(("%d charges"):format(h.charges), 0.83, 0.78, 0.63) end
else
tt:AddLine("Not coated", RED[1], RED[2], RED[3])
end
tt:AddLine(" ")
if pick then
tt:AddLine(("Click to apply %s x%d%s"):format(pick.name, pick.count or 0, pick.pinned and " (pinned)" or ""),
0.6, 0.6, 0.6, true)
else
tt:AddLine("No coating in your bags for this hand.", 0.6, 0.6, 0.6, true)
end
end
-- ============================================================
-- Compact strip
-- ============================================================
local STRIP_H = 26
local SWAP_W = STRIP_H - 4
local HAND_W = 92
local PAD = 6
local function makeBlade(parent, hand, strip)
local b = CreateFrame("Button", nil, parent, "SecureActionButtonTemplate")
b:SetSize(HAND_W, STRIP_H - 4)
b:RegisterForClicks("AnyUp", "AnyDown")
ns.Poisons:RegisterButton(hand, b)
b.icon = b:CreateTexture(nil, "ARTWORK")
b.icon:SetSize(16, 16)
b.icon:SetPoint("LEFT", 3, 0)
b.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
b.text = Chrome:Text(b, 11)
b.text:SetPoint("LEFT", b.icon, "RIGHT", 5, 0)
b.text:SetPoint("RIGHT", -3, 0)
b.text:SetJustifyH("LEFT")
b.text:SetWordWrap(false)
b.hl = b:CreateTexture(nil, "HIGHLIGHT")
b.hl:SetAllPoints()
b.hl:SetColorTexture(1, 1, 1, 0.10)
b:SetScript("OnEnter", function(s)
GameTooltip:SetOwner(s, "ANCHOR_TOP")
handLines(GameTooltip, ns.Poisons:Hand(hand), ns.Poisons.choice and ns.Poisons.choice[hand],
ns.db and ns.db.profile.warnMinutes or 5)
GameTooltip:Show()
end)
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
return b
end
-- Each of the two swap keys, as a button on the strip. The icon is the
-- weapon that key puts in your main hand, so the one you are not
-- holding is the one worth pressing.
local function makeSwap(parent, which)
local b = CreateFrame("Button", nil, parent, "SecureActionButtonTemplate")
b:SetSize(SWAP_W, SWAP_W)
b:RegisterForClicks("AnyUp", "AnyDown")
ns.swap:RegisterButton(which, b)
b.icon = b:CreateTexture(nil, "ARTWORK")
b.icon:SetPoint("TOPLEFT", 1, -1)
b.icon:SetPoint("BOTTOMRIGHT", -1, 1)
b.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
-- The same fel edge the paperdoll uses for the piece that is on:
-- here it means the weapon already in your main hand.
b.live = Chrome:Texture(b, "OVERLAY", C.fel)
b.live:SetPoint("BOTTOMLEFT", 1, 1)
b.live:SetPoint("BOTTOMRIGHT", -1, 1)
b.live:SetHeight(2)
b.live:Hide()
b.hl = b:CreateTexture(nil, "HIGHLIGHT")
b.hl:SetAllPoints()
b.hl:SetColorTexture(1, 1, 1, 0.10)
b:SetScript("OnEnter", function(s)
GameTooltip:SetOwner(s, "ANCHOR_TOP")
local faces = ns.swap:Faces()
local face = faces and faces[which]
if not face then
GameTooltip:SetText("Weapon swap")
GameTooltip:AddLine(tostring(ns.swap.why or "nothing to swap"), 0.5, 0.5, 0.5, true)
else
local name = Core.Dialect.GetItemNameByID(face.id) or "that weapon"
GameTooltip:SetText(which == "stealth" and "Stealth, dagger to main hand"
or "Strike, slow weapon back")
GameTooltip:AddLine(name .. " to your main hand.", 0.8, 0.8, 0.8, true)
if face.live then
GameTooltip:AddLine("Already there.", 0.5, 0.5, 0.5, true)
end
if which == "stealth" then
GameTooltip:AddLine("Pressed again while stealthed, it drops stealth and puts the slow weapon back. Nothing moves in combat.",
0.5, 0.5, 0.5, true)
else
GameTooltip:AddLine("Leaves your hands alone while you are stealthed, so it cannot strip the dagger you are opening with.",
0.5, 0.5, 0.5, true)
end
GameTooltip:AddLine("Costs a swing, and your coatings travel with the blades.",
0.5, 0.5, 0.5, true)
end
GameTooltip:Show()
end)
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
-- Said out loud at build time. A protected frame cannot be shown or
-- hidden once a fight starts, so its visibility is settled here and
-- never touched again.
b:Show()
return b
end
function UI:BuildStrip()
if self.strip then return self.strip end
local db = ns.db and ns.db.profile
local f = CreateFrame("Frame", "WicksPoisonsStrip", UIParent)
self.strip = f
-- A block of its own on the right. Riding the hands put a secure
-- button on top of a secure button and the press stopped swapping
-- anything, whatever the frame levels said. Width is settled at
-- build time: a protected child cannot be shown, hidden or
-- re-anchored in a fight, so nothing here moves once it is up.
local swapW = (ns.swap and ns.swap:Shown()) and (1 + SWAP_W * 2 + 2) or 0
f.swapW = swapW
f:SetSize(PAD + HAND_W * 2 + 3 + swapW + PAD, STRIP_H)
f:SetPoint("CENTER", 0, -250)
f:SetFrameStrata("MEDIUM")
f:SetMovable(true)
f:SetClampedToScreen(true)
f:EnableMouse(true)
f:RegisterForDrag("LeftButton")
f:SetScript("OnDragStart", function(s)
-- A lock stops a nudge, not a deliberate move: shift overrides it.
if Chrome:DragAllowed(db and db.stripLocked) then s:StartMoving() end
end)
f:SetScript("OnDragStop", function(s)
s:StopMovingOrSizing()
if db then db.strip = db.strip or {}; Chrome:SavePosition(s, db.strip) end
end)
f:SetScript("OnMouseUp", function(_, btn) if btn == "RightButton" then UI:Toggle() end end)
if db and db.strip and db.strip.point then
local w, h = f:GetWidth(), f:GetHeight()
Chrome:RestorePosition(f, db.strip)
f:SetSize(w, h)
end
f:Hide()
local bg = Chrome:Texture(f, "BACKGROUND", C.voidBG); bg:SetAllPoints()
Chrome:AddBorder(f)
Chrome:AddBrackets(f)
-- An anchor frame per side, because the blades are protected and can
-- only anchor to frames.
local left = CreateFrame("Frame", nil, f)
left:SetPoint("TOPLEFT", PAD, -2); left:SetSize(HAND_W, STRIP_H - 4)
f.mainBtn = makeBlade(f, "main", f)
f.mainBtn:SetPoint("TOPLEFT", left, "TOPLEFT", 0, 0)
local mid = CreateFrame("Frame", nil, f)
mid:SetPoint("LEFT", left, "RIGHT", 1, 0); mid:SetSize(1, STRIP_H - 6)
local div = Chrome:Texture(mid, "ARTWORK", C.border); div:SetAllPoints()
f.offBtn = makeBlade(f, "off", f)
f.offBtn:SetPoint("LEFT", mid, "RIGHT", 1, 0)
if swapW > 0 then
local sdiv = CreateFrame("Frame", nil, f)
sdiv:SetPoint("LEFT", f.offBtn, "RIGHT", 1, 0); sdiv:SetSize(1, STRIP_H - 6)
local sd = Chrome:Texture(sdiv, "ARTWORK", C.border); sd:SetAllPoints()
f.swapStealth = makeSwap(f, "stealth")
f.swapStealth:SetPoint("LEFT", sdiv, "RIGHT", 1, 0)
f.swapStrike = makeSwap(f, "strike")
f.swapStrike:SetPoint("LEFT", f.swapStealth, "RIGHT", 1, 0)
end
f:SetScript("OnShow", function() UI:RefreshStrip() end)
R:OnChange(function() if f:IsShown() then UI:RefreshStrip() end end)
-- Coatings tick down, so the clock needs its own beat.
f.ticker = C_Timer and C_Timer.NewTicker and C_Timer.NewTicker(20, function()
if f:IsShown() then UI:RefreshStrip() end
end)
return f
end
function UI:ApplyStripVisibility()
local db = ns.db and ns.db.profile
local want = ns.isRogue and db and db.showStrip ~= false
if want then
self:BuildStrip()
self.strip:Show()
self:RefreshStrip()
elseif self.strip then
self.strip:Hide()
end
end
function UI:SetStripLocked(locked)
local db = ns.db and ns.db.profile
if db then db.stripLocked = locked and true or false end
ns.A:Print(locked and "strip locked." or "strip unlocked: drag it into place, then /wpt lock.")
end
local function dressBlade(btn, h, pick, warn)
if not btn or not btn.text then return end
local c = handColor(h, warn)
if h.coated and h.icon then
btn.icon:SetTexture(h.icon)
btn.icon:SetDesaturated(false)
btn.icon:SetAlpha(1)
elseif pick then
btn.icon:SetTexture(pick.icon or BLANK)
btn.icon:SetDesaturated(not h.coated)
btn.icon:SetAlpha(h.coated and 1 or 0.45)
else
btn.icon:SetTexture(BLANK)
btn.icon:SetDesaturated(true)
btn.icon:SetAlpha(0.35)
end
if not h.hasWeapon then
btn.text:SetText(handName(h.hand) == "Main hand" and "Main: empty" or "Off: empty")
elseif h.coated then
-- The name went in and came straight back out: at this width it
-- truncated to "Ins..." on both blades, which is worse than not
-- saying. It is on the hover instead.
btn.text:SetText(("%s %s"):format(h.hand == "main" and "Main" or "Off", clockText(h)))
else
btn.text:SetText(("%s bare"):format(h.hand == "main" and "Main" or "Off"))
end
tint(btn.text, c)
end
-- Icons and the live edge only. Nothing here shows, hides or moves a
-- protected frame, so it is safe to run mid-fight, which the ticker
-- does.
function UI:RefreshSwap()
local f = self.strip
if not (f and f.swapStealth) then return end
local faces = ns.swap and ns.swap:Faces()
for which, b in pairs({ stealth = f.swapStealth, strike = f.swapStrike }) do
local face = faces and faces[which]
b.icon:SetTexture(face and face.icon or "Interface\\Icons\\INV_Misc_QuestionMark")
-- A pair it cannot make sense of goes grey rather than
-- disappearing, so the strip does not change shape on you.
b.icon:SetDesaturated(face == nil)
b.icon:SetAlpha(face and 1 or 0.35)
b.live:SetShown(face ~= nil and face.live == true)
end
end
function UI:RefreshStrip()
local f = self.strip
if not f or not f.mainBtn or not f:IsShown() then return end
local warn = ns.db and ns.db.profile.warnMinutes or 5
local choice = ns.Poisons.choice or {}
dressBlade(f.mainBtn, ns.Poisons:Hand("main"), choice.main, warn)
dressBlade(f.offBtn, ns.Poisons:Hand("off"), choice.off, warn)
self:RefreshSwap()
end
-- ============================================================
-- Details panel
-- ============================================================
function UI:Build()
if self.panel then return self.panel end
local db = ns.db and ns.db.profile
local p = Chrome:NewPanel("WicksPoisonsPanel", {
title = "Wick's Poisons and Things", width = 340, height = 240,
closable = true, strata = "MEDIUM", db = db and db.window,
})
self.panel = p
local ct = p.content
local y = 0
local head = Chrome:Heading(ct, "Blades"); head:SetPoint("TOPLEFT", 0, y)
y = y - 20
p.rows = {}
for _, hand in ipairs({ "main", "off" }) do
local row = CreateFrame("Frame", nil, ct)
row:SetPoint("TOPLEFT", 0, y); row:SetSize(300, 34)
row.icon = row:CreateTexture(nil, "ARTWORK")
row.icon:SetSize(24, 24); row.icon:SetPoint("LEFT", 0, 0)
row.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
row.title = Chrome:Text(row, 12)
row.title:SetPoint("TOPLEFT", row.icon, "TOPRIGHT", 8, 0)
row.detail = Chrome:Text(row, 10, C.muted)
row.detail:SetPoint("BOTTOMLEFT", row.icon, "BOTTOMRIGHT", 8, 0)
p.rows[hand] = row
y = y - 38
end
y = y - 4
local shead = Chrome:Heading(ct, "In your bags"); shead:SetPoint("TOPLEFT", 0, y)
y = y - 20
-- Icons rather than a sentence. Switching coating used to mean
-- shift-clicking the item into chat and typing a command at it,
-- while this list sat here naming the thing you wanted and
-- refusing to be clicked.
p.stockBtns = {}
for i = 1, 10 do
local b = CreateFrame("Button", nil, ct)
b:SetSize(26, 26)
b:SetPoint("TOPLEFT", (i - 1) * 30, y)
b:RegisterForClicks("LeftButtonUp", "RightButtonUp")
b.icon = b:CreateTexture(nil, "ARTWORK")
b.icon:SetPoint("TOPLEFT", 1, -1)
b.icon:SetPoint("BOTTOMRIGHT", -1, 1)
b.icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
Chrome:AddBorder(b)
b.count = Chrome:Text(b, 9, C.text)
b.count:SetPoint("BOTTOMRIGHT", -1, 1)
-- Which hand's key will use it, said on the icon.
b.mark = Chrome:Text(b, 9, C.fel)
b.mark:SetPoint("TOPLEFT", 2, -1)
b.hl = b:CreateTexture(nil, "HIGHLIGHT")
b.hl:SetAllPoints()
b.hl:SetColorTexture(1, 1, 1, 0.12)
b:SetScript("OnClick", function(s, button)
if not s.itemID then return end
ns.Poisons:TogglePin(button == "RightButton" and "off" or "main", s.itemID)
UI:Refresh()
end)
b:SetScript("OnEnter", function(s)
if not s.itemID then return end
GameTooltip:SetOwner(s, "ANCHOR_TOP")
GameTooltip:SetHyperlink("item:" .. s.itemID)
GameTooltip:AddLine(" ")
local on = ns.Poisons:PinnedTo(s.itemID)
if #on > 0 then
GameTooltip:AddLine("Pinned to your " .. table.concat(on, " and ")
.. " hand. Click again to let go.", 0.31, 0.78, 0.47, true)
else
GameTooltip:AddLine("Click to pin to your main hand, right-click for the off hand.",
0.5, 0.5, 0.5, true)
end
GameTooltip:Show()
end)
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
b:Hide()
p.stockBtns[i] = b
end
p.stock = Chrome:Text(ct, 11, C.muted)
p.stock:SetPoint("TOPLEFT", 0, y)
p.stock:SetWidth(300)
p.stock:SetJustifyH("LEFT")
local kitBtn = Chrome:Button(ct, "Kit", 70, 20)
kitBtn:SetPoint("BOTTOMRIGHT", 0, 0)
kitBtn:SetScript("OnClick", function() ns.A.kit:Toggle() end)
local optBtn = Chrome:Button(ct, "Options", 70, 20)
optBtn:SetPoint("RIGHT", kitBtn, "LEFT", -6, 0)
optBtn:SetScript("OnClick", function() ns.A:OpenOptions() end)
local stripBtn = Chrome:Button(ct, "Strip", 70, 20)
stripBtn:SetPoint("RIGHT", optBtn, "LEFT", -6, 0)
stripBtn:SetScript("OnClick", function()
if db then db.showStrip = not (db.showStrip ~= false) end
UI:ApplyStripVisibility()
end)
p:SetScript("OnShow", function() UI:Refresh() end)
R:OnChange(function() if p:IsShown() then UI:Refresh() end end)
return p
end
function UI:Init()
self:ApplyStripVisibility()
end
function UI:Toggle()
self:Build()
self.panel:Toggle()
end
function UI:RefreshPanel()
local p = self.panel
if not p or not p.rows or not p:IsShown() then return end
local warn = ns.db and ns.db.profile.warnMinutes or 5
local choice = ns.Poisons.choice or {}
for _, hand in ipairs({ "main", "off" }) do
local h = ns.Poisons:Hand(hand)
local row = p.rows[hand]
local pick = choice[hand]
if h.coated and h.icon then row.icon:SetTexture(h.icon)
elseif pick then row.icon:SetTexture(pick.icon or BLANK)
else row.icon:SetTexture(BLANK) end
row.icon:SetDesaturated(not h.coated)
row.title:SetText(("%s: %s"):format(handName(hand), h.hasWeapon and (h.weapon or "weapon") or "nothing equipped"))
tint(row.title, handColor(h, warn))
if not h.hasWeapon then
row.detail:SetText("")
elseif h.coated then
local m = minutesLeft(h)
row.detail:SetText(("Coated%s%s"):format(
m and (", " .. math.floor(m) .. " minutes left") or "",
h.charges and (", " .. h.charges .. " charges") or ""))
else
row.detail:SetText(pick and ("Bare. The key applies " .. pick.name) or "Bare, and nothing in your bags to apply")
end
end
local list = ns.Poisons:Available()
p.stock:SetText(#list == 0 and "No coatings carried." or "")
for i, b in ipairs(p.stockBtns) do
local it = list[i]
if it then
b.itemID = it.itemID
b.icon:SetTexture(it.icon or BLANK)
b.count:SetText(it.count and it.count > 1 and tostring(it.count) or "")
local on = ns.Poisons:PinnedTo(it.itemID)
-- M, O, or both: which key reaches for this one.
b.mark:SetText(#on == 0 and "" or
(#on == 2 and "MO" or (on[1] == "main" and "M" or "O")))
b.icon:SetDesaturated(false)
b:Show()
else
b.itemID = nil
b:Hide()
end
end
end
function UI:Refresh()
self:RefreshPanel()
self:RefreshStrip()
end