-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOptions.lua
More file actions
506 lines (475 loc) · 21.5 KB
/
Copy pathOptions.lua
File metadata and controls
506 lines (475 loc) · 21.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
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
-- WickCore
-- Options.lua — one "Wick's Mods" entry in the game's options, a page per product.
--
-- A:RegisterOptions(function(page, A)
-- local y = Core.Options:Heading(page, "General", -8)
-- y = Core.Options:Check(page, "Lock window", function() return A.db.profile.locked end,
-- function(v) A.db.profile.locked = v end, y)
-- end)
local ADDON = ...
local Core = LibStub("WickCore-1.0", true)
if not Core or Core._sourceAddon ~= ADDON then return end
local Options = {}
Core.Options = Options
Options.pages = Options.pages or {}
local Chrome = Core.Chrome
local ROOT_TITLE = "Wick's Mods"
local hasSettings = rawget(_G, "Settings") and Settings.RegisterCanvasLayoutCategory and true or false
local hasLegacy = rawget(_G, "InterfaceOptions_AddCategory") and true or false
local function paintPage(frame)
local bg = Chrome:Texture(frame, "BACKGROUND", Chrome.Colors.void)
bg:SetAllPoints()
end
local function ensureRoot()
if Options.root then return Options.root end
local frame = CreateFrame("Frame")
frame:Hide()
frame.name = ROOT_TITLE
paintPage(frame)
frame:SetScript("OnShow", function(f)
if f.built then return end
f.built = true
local title = Chrome:Text(f, 16)
title:SetPoint("TOPLEFT", 16, -16)
title:SetText(Chrome:TitleMarkup("Wick's Mods"))
local sub = Chrome:Text(f, 11, Chrome.Colors.muted)
sub:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -6)
sub:SetText("Precision addons for World of Warcraft. Each addon has its own page below.")
local y = -64
for _, A in Core:IterateAddons() do
if A ~= Core.self then
local line = Chrome:Text(f, 12)
line:SetPoint("TOPLEFT", 16, y)
line:SetText(Chrome:TitleMarkup(A.title) .. " " .. Chrome:Esc("muted") .. tostring(A.version or "") .. "|r")
y = y - 18
end
end
y = y - 12
-- The swatches wrap to the page: it is built on first show, when
-- the settings window has given it its width.
local w = f:GetWidth()
local ty = Options:ThemeSection(f, 16, y, { width = math.max(300, ((w and w > 0) and w or 640) - 32) })
if rawget(_G, "WicksUI") or (C_AddOns and C_AddOns.IsAddOnLoaded and C_AddOns.IsAddOnLoaded("WicksUI")) then
local shared = Chrome:Text(f, 10, Chrome.Colors.muted)
shared:SetPoint("TOPLEFT", 16, ty - 4)
shared:SetText("Wick's UI shows these same settings on its General page.")
end
local client = Chrome:Text(f, 11, Chrome.Colors.muted)
client:SetPoint("BOTTOMLEFT", 16, 16)
client:SetText(table.concat(Core.Client:Report(), " | "))
end)
local root = { frame = frame }
if hasSettings then
root.category = Settings.RegisterCanvasLayoutCategory(frame, ROOT_TITLE)
if Settings.RegisterAddOnCategory then Settings.RegisterAddOnCategory(root.category) end
elseif hasLegacy then
InterfaceOptions_AddCategory(frame)
end
Options.root = root
return root
end
-- Theme picker: one swatch per theme, the active one bracketed in its own
-- accent, plus a check to follow the player's class.
-- opts.width wraps the swatches to fit; opts.noExtras leaves out the class
-- colour and style checks, for a host (Wick's UI) that shows them itself.
-- Everything here is saved in WickCore and shared by the whole suite.
function Options:ThemeSection(parent, x, y, opts)
opts = opts or {}
local C = Chrome.Colors
local head = Chrome:Heading(parent, "Theme")
head:SetPoint("TOPLEFT", x, y)
y = y - 22
local swatches = {}
local function refresh()
local setting = Chrome:ThemeSetting()
if parent.themePickers then for _, pk in ipairs(parent.themePickers) do pk.Paint() end end
for _, sw in ipairs(swatches) do
if sw.Paint then sw.Paint() end
local on = sw.theme.id == Chrome.activeTheme
sw.ring:SetShown(on)
sw.label:SetTextColor(on and C.fel[1] or C.muted[1], on and C.fel[2] or C.muted[2], on and C.fel[3] or C.muted[3], 1)
end
if parent.themeNote then
local t = Chrome.ThemeByID[Chrome.activeTheme]
parent.themeNote:SetText(setting == "auto" and ("Following your class: " .. (t and t.name or "?"))
or (t and t.custom and "Custom: your main and accent colors."
or ((t and t.name or "?") .. (t and t.class and (" is the " .. t.class:sub(1, 1) .. t.class:sub(2):lower() .. " theme.") or ""))))
end
end
local SW = 52
local perRow = opts.width and math.max(1, math.floor((opts.width + 5) / (SW + 5))) or #Chrome.Themes
for i, def in ipairs(Chrome.Themes) do
local id = def.id
local b = CreateFrame("Button", nil, parent)
b:SetSize(SW, 40)
local col, row = (i - 1) % perRow, math.floor((i - 1) / perRow)
b:SetPoint("TOPLEFT", x + col * (SW + 5), y - row * 46)
local bg = b:CreateTexture(nil, "BACKGROUND")
bg:SetPoint("TOPLEFT", 0, 0); bg:SetPoint("BOTTOMRIGHT", 0, 14)
local strip = b:CreateTexture(nil, "ARTWORK")
strip:SetPoint("TOPLEFT", 1, -1); strip:SetPoint("TOPRIGHT", -1, -1); strip:SetHeight(7)
local acc = b:CreateTexture(nil, "OVERLAY")
acc:SetPoint("BOTTOMLEFT", bg, "BOTTOMLEFT", 4, 4); acc:SetSize(SW - 8, 3)
local edge = b:CreateTexture(nil, "BORDER")
edge:SetPoint("BOTTOMLEFT", bg, "BOTTOMLEFT", 0, 0); edge:SetPoint("BOTTOMRIGHT", bg, "BOTTOMRIGHT", 0, 0); edge:SetHeight(1)
local ring = b:CreateTexture(nil, "OVERLAY")
ring:SetPoint("TOPLEFT", bg, "TOPLEFT", 0, 0); ring:SetPoint("TOPRIGHT", bg, "TOPRIGHT", 0, 0); ring:SetHeight(2)
ring:Hide()
b.ring = ring
b.label = Chrome:Text(b, 10, C.muted)
b.label:SetPoint("BOTTOM", 0, 0)
b.label:SetText(def.name)
-- Themes rebuild when class colors or custom colors change, so the
-- swatch re-reads its theme by id each refresh.
b.Paint = function()
local t = Chrome.ThemeByID[id]
if not t then return end
b.theme = t
bg:SetColorTexture(t.colors.void[1], t.colors.void[2], t.colors.void[3], 1)
strip:SetColorTexture(t.colors.shadow[1], t.colors.shadow[2], t.colors.shadow[3], 1)
acc:SetColorTexture(t.colors.fel[1], t.colors.fel[2], t.colors.fel[3], 1)
edge:SetColorTexture(t.colors.border[1], t.colors.border[2], t.colors.border[3], 1)
ring:SetColorTexture(t.colors.fel[1], t.colors.fel[2], t.colors.fel[3], 1)
end
b.Paint()
-- Your own class's swatch follows your class, so each character
-- takes its own; any other swatch is that theme for all of them.
b:SetScript("OnClick", function()
local _, token = UnitClass("player")
local t = Chrome.ThemeByID[id]
Chrome:SetTheme((t and t.class and t.class == token) and "auto" or id)
refresh()
end)
b:SetScript("OnEnter", function()
local t = b.theme
GameTooltip:SetOwner(b, "ANCHOR_RIGHT")
GameTooltip:SetText(t.name, 1, 1, 1)
if t.class then GameTooltip:AddLine(t.class:sub(1, 1) .. t.class:sub(2):lower() .. " theme", 0.6, 0.6, 0.6) end
if t.custom then GameTooltip:AddLine("Your own main and accent colors, set below.", 0.6, 0.6, 0.6) end
GameTooltip:Show()
end)
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
swatches[#swatches + 1] = b
end
y = y - 46 * math.ceil(#Chrome.Themes / perRow)
-- Custom theme colors: two picker buttons that open Blizzard's color picker.
local function hexToRGB(h) return tonumber(h:sub(1, 2), 16) / 255, tonumber(h:sub(3, 4), 16) / 255, tonumber(h:sub(5, 6), 16) / 255 end
local function openPicker(r, g, b, onChange, onCancel)
local f = ColorPickerFrame
if not f then return end
if f.SetupColorPickerAndShow then
f:SetupColorPickerAndShow({ r = r, g = g, b = b, hasOpacity = false, swatchFunc = onChange, cancelFunc = onCancel })
else
f.func, f.cancelFunc, f.opacityFunc, f.hasOpacity = onChange, onCancel, nil, false
f:SetColorRGB(r, g, b)
f:Show()
end
end
local pickers = {}
local function makePicker(which, label, offsetX)
local b = CreateFrame("Button", nil, parent)
b:SetSize(150, 20)
b:SetPoint("TOPLEFT", x + offsetX, y)
local sw = b:CreateTexture(nil, "ARTWORK")
sw:SetSize(14, 14); sw:SetPoint("LEFT", 0, 0)
local swEdge = CreateFrame("Frame", nil, b)
swEdge:SetPoint("TOPLEFT", sw, "TOPLEFT", -1, 1); swEdge:SetPoint("BOTTOMRIGHT", sw, "BOTTOMRIGHT", 1, -1)
Chrome:AddBorder(swEdge)
b.lbl = Chrome:Text(b, 11)
b.lbl:SetPoint("LEFT", sw, "RIGHT", 8, 0)
b.Paint = function()
local h = Chrome.customColors[which]
local r, g, bb = hexToRGB(h)
sw:SetColorTexture(r, g, bb, 1)
b.lbl:SetText(label .. " " .. Chrome:Esc("muted") .. h .. "|r")
end
b:SetScript("OnClick", function()
local before = Chrome.customColors[which]
local themeBefore = Chrome:ThemeSetting()
local r, g, bb = hexToRGB(before)
local function apply()
local nr, ng, nb = ColorPickerFrame:GetColorRGB()
-- The picker calls this once as it opens, before the player has
-- touched anything. Opening a picker is not choosing Custom.
local untouched = math.abs(nr - r) < 0.003 and math.abs(ng - g) < 0.003 and math.abs(nb - bb) < 0.003
if untouched and Chrome.activeTheme ~= "custom" then return end
if which == "main" then Chrome:SetCustomColors({ nr, ng, nb }, nil) else Chrome:SetCustomColors(nil, { nr, ng, nb }) end
if Chrome.activeTheme ~= "custom" then Chrome:SetTheme("custom") end
refresh()
end
openPicker(r, g, bb, apply, function()
if which == "main" then Chrome:SetCustomColors(before, nil) else Chrome:SetCustomColors(nil, before) end
-- Cancel puts the theme back too, not just the colours.
if themeBefore ~= "custom" and Chrome.activeTheme == "custom" then Chrome:SetTheme(themeBefore) end
refresh()
end)
end)
b.Paint()
pickers[#pickers + 1] = b
return b
end
local customHead = Chrome:Text(parent, 11, C.muted)
customHead:SetPoint("TOPLEFT", x, y)
customHead:SetText("Custom:")
makePicker("main", "Main", 58)
makePicker("accent", "Accent", 220)
local resetBtn = Chrome:Button(parent, "Reset", 60, 20)
resetBtn:SetPoint("TOPLEFT", x + 384, y)
resetBtn:SetScript("OnClick", function()
Chrome:SetCustomColors(Chrome.CUSTOM_DEFAULT.main, Chrome.CUSTOM_DEFAULT.accent)
refresh()
end)
parent.themePickers = pickers
y = y - 26
parent.themeNote = Chrome:Text(parent, 10, C.muted)
parent.themeNote:SetPoint("TOPLEFT", x, y - 4)
y = y - 22
if opts.noExtras then
refresh()
Chrome:OnThemeChanged(refresh)
return y
end
parent.themeClassic = Chrome:Check(parent, "Classic-era class colors (the set TBC UIs show)",
function() return Chrome.classColorSet == "classic" end,
function(v) Chrome:SetClassColorSet(v and "classic" or "client"); refresh() end)
parent.themeClassic:SetPoint("TOPLEFT", x, y)
parent.themeClassic:SetWidth(360)
y = y - 22
-- The shape the whole suite is drawn in. Panels are built once, so a
-- change is saved and the interface reloaded.
local styleHead = Chrome:Text(parent, 11, C.muted)
styleHead:SetPoint("TOPLEFT", x, y - 4)
styleHead:SetText("Style:")
local current = Chrome:StyleID()
for i, st in ipairs(Chrome.Styles) do
-- Choosing a style reloads; the current one does nothing.
local b = Chrome:Button(parent, st.name, 76, 20)
local col, row = (i - 1) % 5, math.floor((i - 1) / 5)
b:SetPoint("TOPLEFT", x + 44 + col * 80, y - row * 24)
if st.id == current then b.label:SetTextColor(C.fel[1], C.fel[2], C.fel[3], 1) end
b:HookScript("OnEnter", function()
GameTooltip:SetOwner(b, "ANCHOR_RIGHT")
GameTooltip:SetText(st.name, 1, 1, 1)
GameTooltip:AddLine(st.blurb, 0.8, 0.8, 0.8, true)
GameTooltip:AddLine("Click to switch; the interface reloads.", 0.5, 0.5, 0.5)
GameTooltip:Show()
end)
b:HookScript("OnLeave", function() GameTooltip:Hide() end)
b:SetScript("OnClick", function()
if st.id == Chrome:StyleID() then return end
Chrome:SetStyle(st.id)
Chrome:Reload()
end)
end
y = y - 24 * math.ceil(#Chrome.Styles / 5) - 2
refresh()
Chrome:OnThemeChanged(refresh)
return y
end
-- Pages outgrew the panel once the client-error and profile sections
-- arrived, so they scroll. A bare ScrollFrame keeps the flat look:
-- Blizzard's scroll templates bring their dialog textures with them.
local function makeScroller(parent)
local scroll = CreateFrame("ScrollFrame", nil, parent)
local body = CreateFrame("Frame", nil, scroll)
body:SetSize(1, 1)
scroll:SetScrollChild(body)
-- The suite's scroll bar: a 4 px track in the border colour, a 4 px
-- thumb in the accent, rounded in the modern style (as Wick's UI draws
-- every scroll bar).
local track, thumb
if Chrome.Modern and Chrome:Modern() then
track = Chrome:Glass(parent, "ARTWORK", Chrome.Colors.border, 0.6)
thumb = Chrome:Glass(parent, "OVERLAY", Chrome.Colors.fel, 0.8)
else
track = Chrome:Texture(parent, "ARTWORK", Chrome.Colors.border)
thumb = Chrome:Texture(parent, "OVERLAY", Chrome.Colors.fel)
end
track:SetWidth(4)
thumb:SetWidth(4)
track:Hide()
thumb:Hide()
local function span(f) return tonumber(f:GetHeight()) or 0 end
local function range() return math.max(0, span(body) - span(scroll)) end
local function refresh()
local r = range()
if r <= 0 then
track:Hide()
thumb:Hide()
scroll:SetVerticalScroll(0)
return
end
track:Show()
thumb:Show()
local view, full = span(scroll), span(body)
local th = math.max(20, view * (view / full))
thumb:SetHeight(th)
local at = math.min(r, math.max(0, tonumber(scroll:GetVerticalScroll()) or 0))
thumb:ClearAllPoints()
thumb:SetPoint("TOP", track, "TOP", 0, -((at / r) * (view - th)))
end
local function scrollBy(delta)
local r = range()
if r <= 0 then return end
local at = math.min(r, math.max(0, ((tonumber(scroll:GetVerticalScroll()) or 0) - delta * 32)))
scroll:SetVerticalScroll(at)
refresh()
end
scroll:EnableMouseWheel(true)
scroll:SetScript("OnMouseWheel", function(_, delta) scrollBy(delta) end)
-- The body is the thing under the cursor over most of the page.
body:EnableMouseWheel(true)
body:SetScript("OnMouseWheel", function(_, delta) scrollBy(delta) end)
scroll.track, scroll.thumb, scroll.Refresh = track, thumb, refresh
return scroll, body
end
-- Lay the body out to fit what was built into it, then show or hide the bar.
local function fitBody(scroll, body)
local w = tonumber(scroll:GetWidth()) or 0
if w > 0 then body:SetWidth(w) end
body:SetHeight(math.max(1, -(body.__extent or 0) + 16))
scroll:Refresh()
end
-- Register a page for an addon. buildFn(page, addon) runs on first show.
function Options:Register(addon, buildFn)
local root = ensureRoot()
local page = CreateFrame("Frame")
page:Hide()
page.name = addon.title
page.parent = ROOT_TITLE
paintPage(page)
page:SetScript("OnShow", function(p)
if p.built then return end
p.built = true
local title = Chrome:Text(p, 16)
title:SetPoint("TOPLEFT", 16, -16)
title:SetText(Chrome:TitleMarkup(addon.title))
local ver = Chrome:Text(p, 11, Chrome.Colors.muted)
ver:SetPoint("LEFT", title, "RIGHT", 10, 0)
ver:SetText(tostring(addon.version or ""))
local scroll, body = makeScroller(p)
scroll:SetPoint("TOPLEFT", 16, -48)
scroll:SetPoint("BOTTOMRIGHT", -22, 16)
scroll.track:SetPoint("TOPRIGHT", p, "TOPRIGHT", -14, -48)
scroll.track:SetPoint("BOTTOMRIGHT", p, "BOTTOMRIGHT", -14, 16)
p.scroll, p.body = scroll, body
Core.safe(buildFn, body, addon)
fitBody(scroll, body)
-- The panel has no size yet on the first show, so measure again once
-- the frame system has placed it, and on every resize after that.
scroll:SetScript("OnSizeChanged", function() fitBody(scroll, body) end)
if C_Timer and C_Timer.After then
C_Timer.After(0, function() fitBody(scroll, body) end)
end
end)
local entry = { frame = page }
if hasSettings and root.category and Settings.RegisterCanvasLayoutSubcategory then
entry.category = Settings.RegisterCanvasLayoutSubcategory(root.category, page, addon.title)
elseif hasLegacy then
InterfaceOptions_AddCategory(page)
end
self.pages[addon.name] = entry
return page
end
function Options:Open(addonName)
local entry = addonName and self.pages[addonName] or self.root
if not entry then return end
if hasSettings and Settings.OpenToCategory and entry.category then
local id = entry.category.GetID and entry.category:GetID() or entry.category.ID
Settings.OpenToCategory(id)
elseif rawget(_G, "InterfaceOptionsFrame_OpenToCategory") then
InterfaceOptionsFrame_OpenToCategory(entry.frame)
InterfaceOptionsFrame_OpenToCategory(entry.frame)
end
end
-- ============================================================
-- Layout helpers: each returns the next y offset
-- ============================================================
-- Every helper reports how far down the page it reached, so the scroll
-- child can be sized to its content without each addon having to say.
local function consume(parent, y)
parent.__extent = math.min(parent.__extent or 0, y)
return y
end
function Options:Heading(parent, text, y)
local fs = Chrome:Heading(parent, text)
fs:SetPoint("TOPLEFT", 0, y)
return consume(parent, y - 22)
end
function Options:Check(parent, label, get, set, y)
-- Every change made through an options page is something worth
-- keeping, so the store hears about it.
local c = Chrome:Check(parent, label, get, function(...)
set(...)
if Core.Store then Core.Store:Dirty() end
end)
c:SetPoint("TOPLEFT", 0, y)
return consume(parent, y - 22)
end
function Options:Stepper(parent, label, get, set, y, opts)
local c = Chrome:Stepper(parent, label, get, function(...)
set(...)
if Core.Store then Core.Store:Dirty() end
end, opts)
c:SetPoint("TOPLEFT", 0, y)
return consume(parent, y - 22)
end
function Options:Button(parent, text, onClick, y, width)
local b = Chrome:Button(parent, text, width)
b:SetPoint("TOPLEFT", 0, y)
b:SetScript("OnClick", onClick)
return consume(parent, y - 28)
end
function Options:Note(parent, text, y)
local fs = Chrome:Text(parent, 11, Chrome.Colors.muted)
fs:SetPoint("TOPLEFT", 0, y)
local w = tonumber(parent:GetWidth())
fs:SetWidth((w and w > 80) and (w - 8) or 520)
fs:SetJustifyH("LEFT")
fs:SetText(text)
-- Ask the font string how tall it actually came out.
local h = tonumber(fs.GetStringHeight and fs:GetStringHeight())
if not h or h <= 0 then
h = 13 + select(2, text:gsub("\n", "")) * 13
end
return consume(parent, y - math.ceil(h) - 6)
end
-- Standard profile controls every product gets for free.
function Options:ProfileSection(parent, addon, y)
local db = addon.db
if not db then return y end
y = self:Heading(parent, "Profiles", y)
y = self:Note(parent, "Current: " .. db:GetCurrentProfile() .. " Keyed by: " .. db:GetKeyMode(), y)
for _, mode in ipairs({ "char", "spec", "class", "mode" }) do
y = self:Check(parent, "Key profiles by " .. mode,
function() return db:GetKeyMode() == mode end,
function(v) if v then db:SetKeyMode(mode) end end, y)
end
y = self:Button(parent, "Reset profile", function() db:ResetProfile() end, y, 110)
y = self:Button(parent, "Copy export string", function()
Options:ShowExport(addon, db:Export())
end, y, 150)
return y
end
-- A panel with a selectable EditBox holding an export string, or accepting an import.
function Options:ShowExport(addon, text, onImport)
if not self.exportPanel then
local p = Chrome:NewPanel("WickCoreExportFrame", { title = "Wick's Export", width = 520, height = 200, strata = "DIALOG" })
local eb = CreateFrame("EditBox", nil, p.content)
eb:SetMultiLine(true)
eb:SetAutoFocus(true)
eb:SetFontObject("GameFontHighlightSmall")
eb:SetAllPoints()
eb:SetScript("OnEscapePressed", function() p:Hide() end)
p.editBox = eb
self.exportPanel = p
end
local p = self.exportPanel
p.title:SetText(Chrome:TitleMarkup(addon.title) .. " " .. Chrome:Esc("muted") .. (onImport and "import" or "export") .. "|r")
p.editBox:SetText(text or "")
p.editBox:HighlightText()
p.editBox:SetScript("OnEnterPressed", function(eb)
if onImport then onImport(eb:GetText()); p:Hide() end
end)
p:Show()
end