comparison modules/ReAction_ConfigUI/lib/Dewdrop-2.0/Dewdrop-2.0.lua @ 28:21bcaf8215ff

- converted to Ace3 - rearranged file layout - configGUI menus not working right now
author Flick <flickerstreak@gmail.com>
date Mon, 17 Mar 2008 18:24:53 +0000
parents
children
comparison
equal deleted inserted replaced
27:f1e838841ce1 28:21bcaf8215ff
1 --[[
2 Name: Dewdrop-2.0
3 Revision: $Rev: 48630 $
4 Author(s): ckknight (ckknight@gmail.com)
5 Website: http://ckknight.wowinterface.com/
6 Documentation: http://wiki.wowace.com/index.php/Dewdrop-2.0
7 SVN: http://svn.wowace.com/root/trunk/DewdropLib/Dewdrop-2.0
8 Description: A library to provide a clean dropdown menu interface.
9 Dependencies: AceLibrary
10 License: LGPL v2.1
11 ]]
12
13 local MAJOR_VERSION = "Dewdrop-2.0"
14 local MINOR_VERSION = "$Revision: 48630 $"
15
16 if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
17 if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
18
19 local Dewdrop = {}
20
21 local SharedMedia
22
23 local CLOSE = "Close"
24 local CLOSE_DESC = "Close the menu."
25 local VALIDATION_ERROR = "Validation error."
26 local USAGE_TOOLTIP = "Usage: %s."
27 local RANGE_TOOLTIP = "Note that you can scroll your mouse wheel while over the slider to step by one."
28 local RESET_KEYBINDING_DESC = "Hit escape to clear the keybinding."
29 local KEY_BUTTON1 = "Left Mouse"
30 local KEY_BUTTON2 = "Right Mouse"
31 local DISABLED = "Disabled"
32 local DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?"
33
34 if GetLocale() == "deDE" then
35 CLOSE = "Schlie\195\159en"
36 CLOSE_DESC = "Men\195\188 schlie\195\159en."
37 VALIDATION_ERROR = "Validierungsfehler."
38 USAGE_TOOLTIP = "Benutzung: %s."
39 RANGE_TOOLTIP = "Beachte das du mit dem Mausrad scrollen kannst solange du \195\188ber dem Schieberegler bist, um 10er Spr\195\188nge zu machen."
40 RESET_KEYBINDING_DESC = "Escape dr\195\188cken, um die Tastenbelegung zu l\195\182schen."
41 KEY_BUTTON1 = "Linke Maustaste"
42 KEY_BUTTON2 = "Rechte Maustaste"
43 DISABLED = "Deaktiviert"
44 DEFAULT_CONFIRM_MESSAGE = "Bist du sicher das du `%s' machen willst?"
45 elseif GetLocale() == "koKR" then
46 CLOSE = "닫기"
47 CLOSE_DESC = "메뉴를 닫습니다."
48 VALIDATION_ERROR = "오류 확인."
49 USAGE_TOOLTIP = "사용법: %s."
50 RANGE_TOOLTIP = "알림 : 슬라이더 위에서 마우스 휠을 사용하면 한단계씩 조절할 수 있습니다."
51 RESET_KEYBINDING_DESC = "단축키를 해제하려면 ESC키를 누르세요."
52 KEY_BUTTON1 = "왼쪽 마우스"
53 KEY_BUTTON2 = "오른쪽 마우스"
54 DISABLED = "비활성화됨"
55 DEFAULT_CONFIRM_MESSAGE = "정말로 `%s' 실행을 하시겠습니까 ?"
56 elseif GetLocale() == "frFR" then
57 CLOSE = "Fermer"
58 CLOSE_DESC = "Ferme le menu."
59 VALIDATION_ERROR = "Erreur de validation."
60 USAGE_TOOLTIP = "Utilisation : %s."
61 RANGE_TOOLTIP = "Vous pouvez aussi utiliser la molette de la souris pour pour modifier progressivement."
62 RESET_KEYBINDING_DESC = "Appuyez sur la touche Echappement pour effacer le raccourci."
63 KEY_BUTTON1 = "Clic gauche"
64 KEY_BUTTON2 = "Clic droit"
65 DISABLED = "D\195\169sactiv\195\169"
66 DEFAULT_CONFIRM_MESSAGE = "\195\138tes-vous s\195\187r de vouloir effectuer '%s' ?"
67 elseif GetLocale() == "esES" then
68 CLOSE = "Cerrar"
69 CLOSE_DESC = "Cierra el menú."
70 VALIDATION_ERROR = "Error de validación."
71 USAGE_TOOLTIP = "Uso: %s."
72 RANGE_TOOLTIP = "Puedes desplazarte verticalmente con la rueda del ratón sobre el desplazador."
73 RESET_KEYBINDING_DESC = "Pulsa Escape para borrar la asignación de tecla."
74 KEY_BUTTON1 = "Clic Izquierdo"
75 KEY_BUTTON2 = "Clic Derecho"
76 DISABLED = "Desactivado"
77 DEFAULT_CONFIRM_MESSAGE = "¿Estás seguro de querer realizar `%s'?"
78 elseif GetLocale() == "zhTW" then
79 CLOSE = "關閉"
80 CLOSE_DESC = "關閉選單。"
81 VALIDATION_ERROR = "驗證錯誤。"
82 USAGE_TOOLTIP = "用法: %s。"
83 RANGE_TOOLTIP = "你可以在捲動條上使用滑鼠滾輪來捲動。"
84 RESET_KEYBINDING_DESC = "按Esc鍵清除快捷鍵。"
85 KEY_BUTTON1 = "滑鼠左鍵"
86 KEY_BUTTON2 = "滑鼠右鍵"
87 DISABLED = "停用"
88 DEFAULT_CONFIRM_MESSAGE = "是否執行「%s」?"
89 elseif GetLocale() == "zhCN" then
90 CLOSE = "关闭"
91 CLOSE_DESC = "关闭菜单"
92 VALIDATION_ERROR = "验证错误."
93 USAGE_TOOLTIP = "用法: %s."
94 RANGE_TOOLTIP = "你可以在滚动条上使用鼠标滚轮来翻页."
95 RESET_KEYBINDING_DESC = "按ESC键清除按键绑定"
96 KEY_BUTTON1 = "鼠标左键"
97 KEY_BUTTON2 = "鼠标右键"
98 DISABLED = "禁用"
99 DEFAULT_CONFIRM_MESSAGE = "是否执行'%s'?"
100 end
101
102 Dewdrop.KEY_BUTTON1 = KEY_BUTTON1
103 Dewdrop.KEY_BUTTON2 = KEY_BUTTON2
104
105 local function new(...)
106 local t = {}
107 for i = 1, select('#', ...), 2 do
108 local k = select(i, ...)
109 if k then
110 t[k] = select(i+1, ...)
111 else
112 break
113 end
114 end
115 return t
116 end
117
118 local tmp
119 do
120 local t = {}
121 function tmp(...)
122 for k in pairs(t) do
123 t[k] = nil
124 end
125 for i = 1, select('#', ...), 2 do
126 local k = select(i, ...)
127 if k then
128 t[k] = select(i+1, ...)
129 else
130 break
131 end
132 end
133 return t
134 end
135 end
136 local tmp2
137 do
138 local t = {}
139 function tmp2(...)
140 for k in pairs(t) do
141 t[k] = nil
142 end
143 for i = 1, select('#', ...), 2 do
144 local k = select(i, ...)
145 if k then
146 t[k] = select(i+1, ...)
147 else
148 break
149 end
150 end
151 return t
152 end
153 end
154 local levels
155 local buttons
156
157
158 -- Secure frame handling:
159 -- Rather than using secure buttons in the menu (has problems), we have one
160 -- master secureframe that we pop onto menu items on mouseover. This requires
161 -- some dark magic with OnLeave etc, but it's not too bad.
162
163 local secureFrame = CreateFrame("Button", nil, nil, "SecureActionButtonTemplate")
164 secureFrame:Hide()
165
166 local function secureFrame_Show(self)
167 local owner = self.owner
168
169 if self.secure then -- Leftovers from previos owner, clean up! ("Shouldn't" happen but does..)
170 for k,v in pairs(self.secure) do
171 self:SetAttribute(k, nil)
172 end
173 end
174 self.secure = owner.secure; -- Grab hold of new secure data
175
176 local scale = owner:GetEffectiveScale()
177
178 self:SetPoint("TOPLEFT", nil, "BOTTOMLEFT", owner:GetLeft() * scale, owner:GetTop() * scale)
179 self:SetPoint("BOTTOMRIGHT", nil, "BOTTOMLEFT", owner:GetRight() * scale, owner:GetBottom() * scale)
180 self:EnableMouse(true)
181 for k,v in pairs(self.secure) do
182 self:SetAttribute(k, v)
183 end
184
185 secureFrame:SetFrameStrata(owner:GetFrameStrata())
186 secureFrame:SetFrameLevel(owner:GetFrameLevel()+1)
187
188 self:Show()
189 end
190
191 local function secureFrame_Hide(self)
192 self:Hide()
193 if self.secure then
194 for k,v in pairs(self.secure) do
195 self:SetAttribute(k, nil)
196 end
197 end
198 self.secure = nil
199 end
200
201 secureFrame:SetScript("OnEvent",
202 function()
203 if event=="PLAYER_REGEN_ENABLED" then
204 this.combat = false
205 if not this:IsShown() and this.owner then
206 secureFrame_Show(this)
207 end
208 elseif event=="PLAYER_REGEN_DISABLED" then
209 this.combat = true
210 if this:IsShown() then
211 secureFrame_Hide(this)
212 end
213 end
214 end
215 )
216 secureFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
217 secureFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
218
219 secureFrame:SetScript("OnLeave",
220 function()
221 local owner=this.owner
222 this:Deactivate()
223 owner:GetScript("OnLeave")()
224 end
225 )
226
227 secureFrame:HookScript("OnClick",
228 function()
229 local realthis = this
230 this = this.owner
231 this:GetScript("OnClick")()
232 end
233 )
234
235 function secureFrame:IsOwnedBy(frame)
236 return self.owner == frame
237 end
238
239 function secureFrame:Activate(owner)
240 if self.owner then -- "Shouldn't" happen but apparently it does and I cba to troubleshoot...
241 if not self.combat then
242 secureFrame_Hide(self)
243 end
244 end
245 self.owner = owner
246 if not self.combat then
247 secureFrame_Show(self)
248 end
249 end
250
251 function secureFrame:Deactivate()
252 if not self.combat then
253 secureFrame_Hide(self)
254 end
255 self.owner = nil
256 end
257
258 -- END secure frame utilities
259
260
261 -- Underline on mouseover - use a single global underline that we move around, no point in creating lots of copies
262 local underlineFrame = CreateFrame("Frame", nil)
263 underlineFrame.tx = underlineFrame:CreateTexture()
264 underlineFrame.tx:SetTexture(1,1,0.5,0.75)
265 underlineFrame:SetScript("OnHide", function(this) this:Hide(); end)
266 underlineFrame:SetScript("OnShow", function(this) -- change sizing on the fly to catch runtime uiscale changes
267 underlineFrame.tx:SetPoint("TOPLEFT", -1, -2/this:GetEffectiveScale())
268 underlineFrame.tx:SetPoint("RIGHT", 1,0)
269 underlineFrame.tx:SetHeight(0.6 / this:GetEffectiveScale());
270 end)
271 underlineFrame:SetHeight(1)
272
273 -- END underline on mouseover
274
275
276 local function GetScaledCursorPosition()
277 local x, y = GetCursorPosition()
278 local scale = UIParent:GetEffectiveScale()
279 return x / scale, y / scale
280 end
281
282 local function StartCounting(self, level)
283 for i = level, 1, -1 do
284 if levels[i] then
285 levels[i].count = 3
286 end
287 end
288 end
289
290 local function StopCounting(self, level)
291 for i = level, 1, -1 do
292 if levels[i] then
293 levels[i].count = nil
294 end
295 end
296 end
297
298 local function OnUpdate(self, elapsed)
299 for _,level in ipairs(levels) do
300 local count = level.count
301 if count then
302 count = count - elapsed
303 if count < 0 then
304 level.count = nil
305 self:Close(level.num)
306 else
307 level.count = count
308 end
309 end
310 end
311 end
312
313 local function CheckDualMonitor(self, frame)
314 local ratio = GetScreenWidth() / GetScreenHeight()
315 if ratio >= 2.4 and frame:GetRight() > GetScreenWidth() / 2 and frame:GetLeft() < GetScreenWidth() / 2 then
316 local offsetx
317 if GetCursorPosition() / GetScreenHeight() * 768 < GetScreenWidth() / 2 then
318 offsetx = GetScreenWidth() / 2 - frame:GetRight()
319 else
320 offsetx = GetScreenWidth() / 2 - frame:GetLeft()
321 end
322 local point, parent, relativePoint, x, y = frame:GetPoint(1)
323 frame:SetPoint(point, parent, relativePoint, (x or 0) + offsetx, y or 0)
324 end
325 end
326
327 local function CheckSize(self, level)
328 if not level.buttons then
329 return
330 end
331 local height = 20
332 for _, button in ipairs(level.buttons) do
333 height = height + button:GetHeight()
334 end
335 level:SetHeight(height)
336 local width = 160
337 for _, button in ipairs(level.buttons) do
338 local extra = 1
339 if button.hasArrow or button.hasColorSwatch then
340 extra = extra + 16
341 end
342 if not button.notCheckable then
343 extra = extra + 24
344 end
345 button.text:SetFont(STANDARD_TEXT_FONT, button.textHeight)
346 if button.text:GetWidth() + extra > width then
347 width = button.text:GetWidth() + extra
348 end
349 end
350 level:SetWidth(width + 20)
351 if level:GetLeft() and level:GetRight() and level:GetTop() and level:GetBottom() and (level:GetLeft() < 0 or level:GetRight() > GetScreenWidth() or level:GetTop() > GetScreenHeight() or level:GetBottom() < 0) then
352 level:ClearAllPoints()
353 local parent = level.parent or level:GetParent()
354 if type(parent) ~= "table" then
355 parent = UIParent
356 end
357 if level.lastDirection == "RIGHT" then
358 if level.lastVDirection == "DOWN" then
359 level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
360 else
361 level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
362 end
363 else
364 if level.lastVDirection == "DOWN" then
365 level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
366 else
367 level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
368 end
369 end
370 end
371 local dirty = false
372 if not level:GetRight() then
373 self:Close()
374 return
375 end
376 if level:GetRight() > GetScreenWidth() and level.lastDirection == "RIGHT" then
377 level.lastDirection = "LEFT"
378 dirty = true
379 elseif level:GetLeft() < 0 and level.lastDirection == "LEFT" then
380 level.lastDirection = "RIGHT"
381 dirty = true
382 end
383 if level:GetTop() > GetScreenHeight() and level.lastVDirection == "UP" then
384 level.lastVDirection = "DOWN"
385 dirty = true
386 elseif level:GetBottom() < 0 and level.lastVDirection == "DOWN" then
387 level.lastVDirection = "UP"
388 dirty = true
389 end
390 if dirty then
391 level:ClearAllPoints()
392 local parent = level.parent or level:GetParent()
393 if type(parent) ~= "table" then
394 parent = UIParent
395 end
396 if level.lastDirection == "RIGHT" then
397 if level.lastVDirection == "DOWN" then
398 level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
399 else
400 level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
401 end
402 else
403 if level.lastVDirection == "DOWN" then
404 level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
405 else
406 level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
407 end
408 end
409 end
410 if level:GetTop() > GetScreenHeight() then
411 local top = level:GetTop()
412 local point, parent, relativePoint, x, y = level:GetPoint(1)
413 level:ClearAllPoints()
414 level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) + GetScreenHeight() - top)
415 elseif level:GetBottom() < 0 then
416 local bottom = level:GetBottom()
417 local point, parent, relativePoint, x, y = level:GetPoint(1)
418 level:ClearAllPoints()
419 level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) - bottom)
420 end
421 CheckDualMonitor(self, level)
422 if mod(level.num, 5) == 0 then
423 local left, bottom = level:GetLeft(), level:GetBottom()
424 level:ClearAllPoints()
425 level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
426 end
427 end
428
429 local Open
430 local OpenSlider
431 local OpenEditBox
432 local Refresh
433 local Clear
434 local function ReleaseButton(self, level, index)
435 if not level.buttons then
436 return
437 end
438 if not level.buttons[index] then
439 return
440 end
441 local button = level.buttons[index]
442 button:Hide()
443 if button.highlight then
444 button.highlight:Hide()
445 end
446 -- button.arrow:SetVertexColor(1, 1, 1)
447 -- button.arrow:SetHeight(16)
448 -- button.arrow:SetWidth(16)
449 table.remove(level.buttons, index)
450 table.insert(buttons, button)
451 for k in pairs(button) do
452 if k ~= 0 and k ~= "text" and k ~= "check" and k ~= "arrow" and k ~= "colorSwatch" and k ~= "highlight" and k ~= "radioHighlight" then
453 button[k] = nil
454 end
455 end
456 return true
457 end
458
459 local function Scroll(self, level, down)
460 if down then
461 if level:GetBottom() < 0 then
462 local point, parent, relativePoint, x, y = level:GetPoint(1)
463 level:SetPoint(point, parent, relativePoint, x, y + 50)
464 if level:GetBottom() > 0 then
465 level:SetPoint(point, parent, relativePoint, x, y + 50 - level:GetBottom())
466 end
467 end
468 else
469 if level:GetTop() > GetScreenHeight() then
470 local point, parent, relativePoint, x, y = level:GetPoint(1)
471 level:SetPoint(point, parent, relativePoint, x, y - 50)
472 if level:GetTop() < GetScreenHeight() then
473 level:SetPoint(point, parent, relativePoint, x, y - 50 + GetScreenHeight() - level:GetTop())
474 end
475 end
476 end
477 end
478
479 local function getArgs(t, str, num, ...)
480 local x = t[str .. num]
481 if x == nil then
482 return ...
483 else
484 return x, getArgs(t, str, num + 1, ...)
485 end
486 end
487
488 local sliderFrame
489 local editBoxFrame
490
491 local normalFont
492 local lastSetFont
493 local justSetFont = false
494 local regionTmp = {}
495 local function fillRegionTmp(...)
496 for i = 1, select('#', ...) do
497 regionTmp[i] = select(i, ...)
498 end
499 end
500
501 local function showGameTooltip(this)
502 if this.tooltipTitle or this.tooltipText then
503 GameTooltip_SetDefaultAnchor(GameTooltip, this)
504 local disabled = not this.isTitle and this.disabled
505 local font
506 if this.tooltipTitle then
507 if SharedMedia and SharedMedia:IsValid("font", this.tooltipTitle) then
508 font = SharedMedia:Fetch("font", this.tooltipTitle)
509 end
510 if disabled then
511 GameTooltip:SetText(this.tooltipTitle, 0.5, 0.5, 0.5, 1)
512 else
513 GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1)
514 end
515 if this.tooltipText then
516 if not font and SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then
517 font = SharedMedia:Fetch("font", this.tooltipText)
518 end
519 if disabled then
520 GameTooltip:AddLine(this.tooltipText, (NORMAL_FONT_COLOR.r + 0.5) / 2, (NORMAL_FONT_COLOR.g + 0.5) / 2, (NORMAL_FONT_COLOR.b + 0.5) / 2, 1)
521 else
522 GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
523 end
524 end
525 else
526 if SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then
527 font = SharedMedia:Fetch("font", this.tooltipText)
528 end
529 if disabled then
530 GameTooltip:SetText(this.tooltipText, 0.5, 0.5, 0.5, 1)
531 else
532 GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1)
533 end
534 end
535 if font then
536 fillRegionTmp(GameTooltip:GetRegions())
537 lastSetFont = font
538 justSetFont = true
539 for i,v in ipairs(regionTmp) do
540 if v.SetFont then
541 local norm,size,outline = v:GetFont()
542 v:SetFont(font, size, outline)
543 if not normalFont then
544 normalFont = norm
545 end
546 end
547 regionTmp[i] = nil
548 end
549 elseif not normalFont then
550 fillRegionTmp(GameTooltip:GetRegions())
551 for i,v in ipairs(regionTmp) do
552 if v.GetFont and not normalFont then
553 normalFont = v:GetFont()
554 end
555 regionTmp[i] = nil
556 end
557 end
558 GameTooltip:Show()
559 end
560 if this.tooltipFunc then
561 GameTooltip:SetOwner(this, "ANCHOR_NONE")
562 GameTooltip:SetPoint("TOPLEFT", this, "TOPRIGHT", 5, 0)
563 this.tooltipFunc(getArgs(this, 'tooltipArg', 1))
564 GameTooltip:Show()
565 end
566 end
567
568 local tmpt = setmetatable({}, {mode='v'})
569 local numButtons = 0
570 local function AcquireButton(self, level)
571 if not levels[level] then
572 return
573 end
574 level = levels[level]
575 if not level.buttons then
576 level.buttons = {}
577 end
578 local button
579 if #buttons == 0 then
580 numButtons = numButtons + 1
581 button = CreateFrame("Button", "Dewdrop20Button" .. numButtons, nil)
582 button:SetFrameStrata("FULLSCREEN_DIALOG")
583 button:SetHeight(16)
584 local highlight = button:CreateTexture(nil, "BACKGROUND")
585 highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
586 button.highlight = highlight
587 highlight:SetBlendMode("ADD")
588 highlight:SetAllPoints(button)
589 highlight:Hide()
590 local check = button:CreateTexture(nil, "ARTWORK")
591 button.check = check
592 check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
593 check:SetPoint("CENTER", button, "LEFT", 12, 0)
594 check:SetWidth(24)
595 check:SetHeight(24)
596 local radioHighlight = button:CreateTexture(nil, "ARTWORK")
597 button.radioHighlight = radioHighlight
598 radioHighlight:SetTexture("Interface\\Buttons\\UI-RadioButton")
599 radioHighlight:SetAllPoints(check)
600 radioHighlight:SetBlendMode("ADD")
601 radioHighlight:SetTexCoord(0.5, 0.75, 0, 1)
602 radioHighlight:Hide()
603 button:SetScript("OnEnter", function()
604 if (sliderFrame and sliderFrame:IsShown() and sliderFrame.mouseDown and sliderFrame.level == this.level.num + 1) or (editBoxFrame and editBoxFrame:IsShown() and editBoxFrame.mouseDown and editBoxFrame.level == this.level.num + 1) then
605 for i = 1, this.level.num do
606 Refresh(self, levels[i])
607 end
608 return
609 end
610 self:Close(this.level.num + 1)
611 if not this.disabled then
612 if this.secure then
613 secureFrame:Activate(this)
614 elseif this.hasSlider then
615 OpenSlider(self, this)
616 elseif this.hasEditBox then
617 OpenEditBox(self, this)
618 elseif this.hasArrow then
619 Open(self, this, nil, this.level.num + 1, this.value)
620 end
621 end
622 if not this.level then -- button reclaimed
623 return
624 end
625 StopCounting(self, this.level.num + 1)
626 if not this.disabled then
627 highlight:Show()
628 if this.isRadio then
629 button.radioHighlight:Show()
630 end
631 if this.mouseoverUnderline then
632 underlineFrame:SetParent(this)
633 underlineFrame:SetPoint("BOTTOMLEFT",this.text,0,0)
634 underlineFrame:SetWidth(this.text:GetWidth())
635 underlineFrame:Show()
636 end
637 end
638 showGameTooltip(this)
639 end)
640 button:SetScript("OnHide", function()
641 if this.secure and secureFrame:IsOwnedBy(this) then
642 secureFrame:Deactivate()
643 end
644 end)
645 button:SetScript("OnLeave", function()
646 if this.secure and secureFrame:IsShown() then
647 return; -- it's ok, we didn't actually mouse out of the button, only onto the secure frame on top of it
648 end
649 underlineFrame:Hide()
650 if not this.selected then
651 highlight:Hide()
652 end
653 button.radioHighlight:Hide()
654 if this.level then
655 StartCounting(self, this.level.num)
656 end
657 GameTooltip:Hide()
658 end)
659 local first = true
660 button:SetScript("OnClick", function()
661 if not this.disabled then
662 if this.hasColorSwatch then
663 local func = button.colorFunc
664 local hasOpacity = this.hasOpacity
665 local this = this
666 for k in pairs(tmpt) do
667 tmpt[k] = nil
668 end
669 for i = 1, 1000 do
670 local x = this['colorArg'..i]
671 if x == nil then
672 break
673 else
674 tmpt[i] = x
675 end
676 end
677 ColorPickerFrame.func = function()
678 if func then
679 local r,g,b = ColorPickerFrame:GetColorRGB()
680 local a = hasOpacity and 1 - OpacitySliderFrame:GetValue() or nil
681 local n = #tmpt
682 tmpt[n+1] = r
683 tmpt[n+2] = g
684 tmpt[n+3] = b
685 tmpt[n+4] = a
686 func(unpack(tmpt))
687 tmpt[n+1] = nil
688 tmpt[n+2] = nil
689 tmpt[n+3] = nil
690 tmpt[n+4] = nil
691 end
692 end
693 ColorPickerFrame.hasOpacity = this.hasOpacity
694 ColorPickerFrame.opacityFunc = ColorPickerFrame.func
695 ColorPickerFrame.opacity = 1 - this.opacity
696 ColorPickerFrame:SetColorRGB(this.r, this.g, this.b)
697 local r, g, b, a = this.r, this.g, this.b, this.opacity
698 ColorPickerFrame.cancelFunc = function()
699 if func then
700 local n = #tmpt
701 tmpt[n+1] = r
702 tmpt[n+2] = g
703 tmpt[n+3] = b
704 tmpt[n+4] = a
705 func(unpack(tmpt))
706 for i = 1, n+4 do
707 tmpt[i] = nil
708 end
709 end
710 end
711 self:Close(1)
712 ShowUIPanel(ColorPickerFrame)
713 elseif this.func then
714 local level = this.level
715 if type(this.func) == "string" then
716 if type(this.arg1[this.func]) ~= "function" then
717 self:error("Cannot call method %q", this.func)
718 end
719 this.arg1[this.func](this.arg1, getArgs(this, 'arg', 2))
720 else
721 this.func(getArgs(this, 'arg', 1))
722 end
723 if this.closeWhenClicked then
724 self:Close()
725 elseif level:IsShown() then
726 for i = 1, level.num do
727 Refresh(self, levels[i])
728 end
729 local value = levels[level.num].value
730 for i = level.num-1, 1, -1 do
731 local level = levels[i]
732 local good = false
733 for _,button in ipairs(level.buttons) do
734 if button.value == value then
735 good = true
736 break
737 end
738 end
739 if not good then
740 Dewdrop:Close(i+1)
741 end
742 value = levels[i].value
743 end
744 end
745 elseif this.closeWhenClicked then
746 self:Close()
747 end
748 end
749 end)
750 local text = button:CreateFontString(nil, "ARTWORK")
751 button.text = text
752 text:SetFontObject(GameFontHighlightSmall)
753 button.text:SetFont(STANDARD_TEXT_FONT, UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT)
754 button:SetScript("OnMouseDown", function()
755 if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
756 text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 1 or 25, -1)
757 end
758 end)
759 button:SetScript("OnMouseUp", function()
760 if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
761 text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 0 or 24, 0)
762 end
763 end)
764 local arrow = button:CreateTexture(nil, "ARTWORK")
765 button.arrow = arrow
766 arrow:SetPoint("LEFT", button, "RIGHT", -16, 0)
767 arrow:SetWidth(16)
768 arrow:SetHeight(16)
769 arrow:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
770 local colorSwatch = button:CreateTexture(nil, "ARTWORK")
771 button.colorSwatch = colorSwatch
772 colorSwatch:SetWidth(20)
773 colorSwatch:SetHeight(20)
774 colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
775 local texture = button:CreateTexture(nil, "OVERLAY")
776 colorSwatch.texture = texture
777 texture:SetTexture("Interface\\Buttons\\WHITE8X8")
778 texture:SetWidth(11.5)
779 texture:SetHeight(11.5)
780 texture:Show()
781 texture:SetPoint("CENTER", colorSwatch, "CENTER")
782 colorSwatch:SetPoint("RIGHT", button, "RIGHT", 0, 0)
783 else
784 button = table.remove(buttons)
785 end
786 button:ClearAllPoints()
787 button:SetParent(level)
788 button:SetFrameStrata(level:GetFrameStrata())
789 button:SetFrameLevel(level:GetFrameLevel() + 1)
790 button:SetPoint("LEFT", level, "LEFT", 10, 0)
791 button:SetPoint("RIGHT", level, "RIGHT", -10, 0)
792 if #level.buttons == 0 then
793 button:SetPoint("TOP", level, "TOP", 0, -10)
794 else
795 button:SetPoint("TOP", level.buttons[#level.buttons], "BOTTOM", 0, 0)
796 end
797 button.text:SetPoint("LEFT", button, "LEFT", 24, 0)
798 button:Show()
799 button.level = level
800 table.insert(level.buttons, button)
801 if not level.parented then
802 level.parented = true
803 level:ClearAllPoints()
804 if level.num == 1 then
805 if level.parent ~= UIParent and type(level.parent) == "table" then
806 level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT")
807 else
808 level:SetPoint("CENTER", UIParent, "CENTER")
809 end
810 else
811 if level.lastDirection == "RIGHT" then
812 if level.lastVDirection == "DOWN" then
813 level:SetPoint("TOPLEFT", level.parent, "TOPRIGHT", 5, 10)
814 else
815 level:SetPoint("BOTTOMLEFT", level.parent, "BOTTOMRIGHT", 5, -10)
816 end
817 else
818 if level.lastVDirection == "DOWN" then
819 level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT", -5, 10)
820 else
821 level:SetPoint("BOTTOMRIGHT", level.parent, "BOTTOMLEFT", -5, -10)
822 end
823 end
824 end
825 level:SetFrameStrata("FULLSCREEN_DIALOG")
826 end
827 button:SetAlpha(1)
828 return button
829 end
830
831 local numLevels = 0
832 local function AcquireLevel(self, level)
833 if not levels[level] then
834 for i = #levels + 1, level, -1 do
835 local i = i
836 numLevels = numLevels + 1
837 local frame = CreateFrame("Button", "Dewdrop20Level" .. numLevels, nil)
838 if i == 1 then
839 local old_CloseSpecialWindows = CloseSpecialWindows
840 function CloseSpecialWindows()
841 local found = old_CloseSpecialWindows()
842 if levels[1]:IsShown() then
843 self:Close()
844 return 1
845 end
846 return found
847 end
848 end
849 levels[i] = frame
850 frame.num = i
851 frame:SetParent(UIParent)
852 frame:SetFrameStrata("FULLSCREEN_DIALOG")
853 frame:Hide()
854 frame:SetWidth(180)
855 frame:SetHeight(10)
856 frame:SetFrameLevel(i * 3)
857 frame:SetScript("OnHide", function()
858 self:Close(level + 1)
859 end)
860 if frame.SetTopLevel then
861 frame:SetTopLevel(true)
862 end
863 frame:EnableMouse(true)
864 frame:EnableMouseWheel(true)
865 local backdrop = CreateFrame("Frame", nil, frame)
866 backdrop:SetAllPoints(frame)
867 backdrop:SetBackdrop(tmp(
868 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
869 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
870 'tile', true,
871 'insets', tmp2(
872 'left', 5,
873 'right', 5,
874 'top', 5,
875 'bottom', 5
876 ),
877 'tileSize', 16,
878 'edgeSize', 16
879 ))
880 backdrop:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
881 backdrop:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
882 frame:SetScript("OnClick", function()
883 self:Close(i)
884 end)
885 frame:SetScript("OnEnter", function()
886 StopCounting(self, i)
887 end)
888 frame:SetScript("OnLeave", function()
889 StartCounting(self, i)
890 end)
891 frame:SetScript("OnMouseWheel", function()
892 Scroll(self, frame, arg1 < 0)
893 end)
894 if i == 1 then
895 frame:SetScript("OnUpdate", function(this, arg1)
896 OnUpdate(self, arg1)
897 end)
898 levels[1].lastDirection = "RIGHT"
899 levels[1].lastVDirection = "DOWN"
900 else
901 levels[i].lastDirection = levels[i - 1].lastDirection
902 levels[i].lastVDirection = levels[i - 1].lastVDirection
903 end
904 end
905 end
906 local fullscreenFrame = GetUIPanel("fullscreen")
907 local l = levels[level]
908 local strata, framelevel = l:GetFrameStrata(), l:GetFrameLevel()
909 if fullscreenFrame then
910 l:SetParent(fullscreenFrame)
911 else
912 l:SetParent(UIParent)
913 end
914 l:SetFrameStrata(strata)
915 l:SetFrameLevel(framelevel)
916 l:SetAlpha(1)
917 return l
918 end
919
920 local function validateOptions(options, position, baseOptions, fromPass)
921 if not baseOptions then
922 baseOptions = options
923 end
924 if type(options) ~= "table" then
925 return "Options must be a table.", position
926 end
927 local kind = options.type
928 if type(kind) ~= "string" then
929 return '"type" must be a string.', position
930 elseif kind ~= "group" and kind ~= "range" and kind ~= "text" and kind ~= "execute" and kind ~= "toggle" and kind ~= "color" and kind ~= "dragLink" and kind ~= "header" then
931 return '"type" must either be "range", "text", "group", "toggle", "execute", "color", "dragLink", or "header".', position
932 end
933 if options.aliases then
934 if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then
935 return '"alias" must be a table or string', position
936 end
937 end
938 if not fromPass then
939 if kind == "execute" then
940 if type(options.func) ~= "string" and type(options.func) ~= "function" then
941 return '"func" must be a string or function', position
942 end
943 elseif kind == "range" or kind == "text" or kind == "toggle" then
944 if type(options.set) ~= "string" and type(options.set) ~= "function" then
945 return '"set" must be a string or function', position
946 end
947 if kind == "text" and options.get == false then
948 elseif type(options.get) ~= "string" and type(options.get) ~= "function" then
949 return '"get" must be a string or function', position
950 end
951 elseif kind == "group" and options.pass then
952 if options.pass ~= true then
953 return '"pass" must be either nil, true, or false', position
954 end
955 if not options.func then
956 if type(options.set) ~= "string" and type(options.set) ~= "function" then
957 return '"set" must be a string or function', position
958 end
959 if type(options.get) ~= "string" and type(options.get) ~= "function" then
960 return '"get" must be a string or function', position
961 end
962 elseif type(options.func) ~= "string" and type(options.func) ~= "function" then
963 return '"func" must be a string or function', position
964 end
965 end
966 end
967 if options ~= baseOptions then
968 if kind == "header" then
969 elseif type(options.desc) ~= "string" then
970 return '"desc" must be a string', position
971 elseif options.desc:len() == 0 then
972 return '"desc" cannot be a 0-length string', position
973 end
974 end
975 if options ~= baseOptions or kind == "range" or kind == "text" or kind == "toggle" or kind == "color" then
976 if options.type == "header" and not options.cmdName and not options.name then
977 elseif options.cmdName then
978 if type(options.cmdName) ~= "string" then
979 return '"cmdName" must be a string or nil', position
980 elseif options.cmdName:len() == 0 then
981 return '"cmdName" cannot be a 0-length string', position
982 end
983 if type(options.guiName) ~= "string" then
984 if not options.guiNameIsMap then
985 return '"guiName" must be a string or nil', position
986 end
987 elseif options.guiName:len() == 0 then
988 return '"guiName" cannot be a 0-length string', position
989 end
990 else
991 if type(options.name) ~= "string" then
992 return '"name" must be a string', position
993 elseif options.name:len() == 0 then
994 return '"name" cannot be a 0-length string', position
995 end
996 end
997 end
998 if options.guiNameIsMap then
999 if type(options.guiNameIsMap) ~= "boolean" then
1000 return '"guiNameIsMap" must be a boolean or nil', position
1001 elseif options.type ~= "toggle" then
1002 return 'if "guiNameIsMap" is true, then "type" must be set to \'toggle\'', position
1003 elseif type(options.map) ~= "table" then
1004 return '"map" must be a table', position
1005 end
1006 end
1007 if options.message and type(options.message) ~= "string" then
1008 return '"message" must be a string or nil', position
1009 end
1010 if options.error and type(options.error) ~= "string" then
1011 return '"error" must be a string or nil', position
1012 end
1013 if options.current and type(options.current) ~= "string" then
1014 return '"current" must be a string or nil', position
1015 end
1016 if options.order then
1017 if type(options.order) ~= "number" or (-1 < options.order and options.order < 0.999) then
1018 return '"order" must be a non-zero number or nil', position
1019 end
1020 end
1021 if options.disabled then
1022 if type(options.disabled) ~= "function" and type(options.disabled) ~= "string" and options.disabled ~= true then
1023 return '"disabled" must be a function, string, or boolean', position
1024 end
1025 end
1026 if options.cmdHidden then
1027 if type(options.cmdHidden) ~= "function" and type(options.cmdHidden) ~= "string" and options.cmdHidden ~= true then
1028 return '"cmdHidden" must be a function, string, or boolean', position
1029 end
1030 end
1031 if options.guiHidden then
1032 if type(options.guiHidden) ~= "function" and type(options.guiHidden) ~= "string" and options.guiHidden ~= true then
1033 return '"guiHidden" must be a function, string, or boolean', position
1034 end
1035 end
1036 if options.hidden then
1037 if type(options.hidden) ~= "function" and type(options.hidden) ~= "string" and options.hidden ~= true then
1038 return '"hidden" must be a function, string, or boolean', position
1039 end
1040 end
1041 if kind == "text" then
1042 if type(options.validate) == "table" then
1043 local t = options.validate
1044 local iTable = nil
1045 for k,v in pairs(t) do
1046 if type(k) == "number" then
1047 if iTable == nil then
1048 iTable = true
1049 elseif not iTable then
1050 return '"validate" must either have all keys be indexed numbers or strings', position
1051 elseif k < 1 or k > #t then
1052 return '"validate" numeric keys must be indexed properly. >= 1 and <= #t', position
1053 end
1054 else
1055 if iTable == nil then
1056 iTable = false
1057 elseif iTable then
1058 return '"validate" must either have all keys be indexed numbers or strings', position
1059 end
1060 end
1061 if type(v) ~= "string" then
1062 return '"validate" values must all be strings', position
1063 end
1064 end
1065 if options.multiToggle and options.multiToggle ~= true then
1066 return '"multiToggle" must be a boolean or nil if "validate" is a table', position
1067 end
1068 elseif options.validate == "keybinding" then
1069 -- no other checks
1070 else
1071 if type(options.usage) ~= "string" then
1072 return '"usage" must be a string', position
1073 elseif options.validate and type(options.validate) ~= "string" and type(options.validate) ~= "function" then
1074 return '"validate" must be a string, function, or table', position
1075 end
1076 end
1077 if options.multiToggle and type(options.validate) ~= "table" then
1078 return '"validate" must be a table if "multiToggle" is true', position
1079 end
1080 elseif kind == "range" then
1081 if options.min or options.max then
1082 if type(options.min) ~= "number" then
1083 return '"min" must be a number', position
1084 elseif type(options.max) ~= "number" then
1085 return '"max" must be a number', position
1086 elseif options.min >= options.max then
1087 return '"min" must be less than "max"', position
1088 end
1089 end
1090 if options.step then
1091 if type(options.step) ~= "number" then
1092 return '"step" must be a number', position
1093 elseif options.step < 0 then
1094 return '"step" must be nonnegative', position
1095 end
1096 end
1097 if options.bigStep then
1098 if type(options.bigStep) ~= "number" then
1099 return '"bigStep" must be a number', position
1100 elseif options.bigStep < 0 then
1101 return '"bigStep" must be nonnegative', position
1102 end
1103 end
1104 if options.isPercent and options.isPercent ~= true then
1105 return '"isPercent" must either be nil, true, or false', position
1106 end
1107 elseif kind == "toggle" then
1108 if options.map then
1109 if type(options.map) ~= "table" then
1110 return '"map" must be a table', position
1111 elseif type(options.map[true]) ~= "string" then
1112 return '"map[true]" must be a string', position
1113 elseif type(options.map[false]) ~= "string" then
1114 return '"map[false]" must be a string', position
1115 end
1116 end
1117 elseif kind == "color" then
1118 if options.hasAlpha and options.hasAlpha ~= true then
1119 return '"hasAlpha" must be nil, true, or false', position
1120 end
1121 elseif kind == "group" then
1122 if options.pass and options.pass ~= true then
1123 return '"pass" must be nil, true, or false', position
1124 end
1125 if type(options.args) ~= "table" then
1126 return '"args" must be a table', position
1127 end
1128 for k,v in pairs(options.args) do
1129 if type(k) ~= "number" then
1130 if type(k) ~= "string" then
1131 return '"args" keys must be strings or numbers', position
1132 elseif k:len() == 0 then
1133 return '"args" keys must not be 0-length strings.', position
1134 end
1135 end
1136 if type(v) ~= "table" then
1137 return '"args" values must be tables', position and position .. "." .. k or k
1138 end
1139 local newposition
1140 if position then
1141 newposition = position .. ".args." .. k
1142 else
1143 newposition = "args." .. k
1144 end
1145 local err, pos = validateOptions(v, newposition, baseOptions, options.pass)
1146 if err then
1147 return err, pos
1148 end
1149 end
1150 elseif kind == "execute" then
1151 if type(options.confirm) ~= "string" and type(options.confirm) ~= "boolean" and type(options.confirm) ~= "nil" then
1152 return '"confirm" must be a string, boolean, or nil', position
1153 end
1154 end
1155 if options.icon and type(options.icon) ~= "string" then
1156 return'"icon" must be a string', position
1157 end
1158 if options.iconWidth or options.iconHeight then
1159 if type(options.iconWidth) ~= "number" or type(options.iconHeight) ~= "number" then
1160 return '"iconHeight" and "iconWidth" must be numbers', position
1161 end
1162 end
1163 if options.iconCoordLeft or options.iconCoordRight or options.iconCoordTop or options.iconCoordBottom then
1164 if type(options.iconCoordLeft) ~= "number" or type(options.iconCoordRight) ~= "number" or type(options.iconCoordTop) ~= "number" or type(options.iconCoordBottom) ~= "number" then
1165 return '"iconCoordLeft", "iconCoordRight", "iconCoordTop", and "iconCoordBottom" must be numbers', position
1166 end
1167 end
1168 end
1169
1170 local validatedOptions
1171
1172 local values
1173 local mysort_args
1174 local mysort
1175 local othersort
1176 local othersort_validate
1177
1178 local baseFunc, currentLevel
1179
1180 local function confirmPopup(message, func, ...)
1181 if not StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] then
1182 StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] = {}
1183 end
1184 local t = StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"]
1185 for k in pairs(t) do
1186 t[k] = nil
1187 end
1188 t.text = message
1189 t.button1 = ACCEPT or "Accept"
1190 t.button2 = CANCEL or "Cancel"
1191 t.OnAccept = function()
1192 func(unpack(t))
1193 end
1194 for i = 1, select('#', ...) do
1195 t[i] = select(i, ...)
1196 end
1197 t.timeout = 0
1198 t.whileDead = 1
1199 t.hideOnEscape = 1
1200
1201 Dewdrop:Close()
1202 StaticPopup_Show("DEWDROP20_CONFIRM_DIALOG")
1203 end
1204
1205
1206 local function getMethod(settingname, handler, v, methodName, ...) -- "..." is simply returned straight out cause you can't do "a,b,c = 111,f(),222"
1207 assert(v and type(v)=="table")
1208 assert(methodName and type(methodName)=="string")
1209
1210 local method = v[methodName]
1211 if type(method)=="function" then
1212 return method, ...
1213 elseif type(method)=="string" then
1214 if not handler then
1215 Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method)
1216 elseif not handler[method] then
1217 Dewdrop:error("[%s] 'handler' method %q not defined", tostring(settingname), method)
1218 end
1219 return handler[method], handler, ...
1220 end
1221
1222 Dewdrop:error("[%s] Missing %q directive", tostring(settingname), methodName)
1223 end
1224
1225 local function callMethod(settingname, handler, v, methodName, ...)
1226 assert(v and type(v)=="table")
1227 assert(methodName and type(methodName)=="string")
1228
1229 local method = v[methodName]
1230 if type(method)=="function" then
1231 local success, ret,ret2,ret3,ret4 = pcall(v[methodName], ...)
1232 if not success then
1233 geterrorhandler()(ret)
1234 return nil
1235 end
1236 return ret,ret2,ret3,ret4
1237
1238 elseif type(method)=="string" then
1239
1240 local neg = method:match("^~(.-)$")
1241 if neg then
1242 method = neg
1243 end
1244 if not handler then
1245 Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method)
1246 elseif not handler[method] then
1247 Dewdrop:error("[%s] 'handler' (%q) method %q not defined", tostring(settingname), handler.name or "(unnamed)", method)
1248 end
1249 local success, ret,ret2,ret3,ret4 = pcall(handler[method], handler, ...)
1250 if not success then
1251 geterrorhandler()(ret)
1252 return nil
1253 end
1254 if neg then
1255 return not ret
1256 end
1257 return ret,ret2,ret3,ret4
1258 elseif method == false then
1259 return nil
1260 end
1261
1262 Dewdrop:error("[%s] Missing %q directive in %q", tostring(settingname), methodName, v.name or "(unnamed)")
1263 end
1264
1265 local function skip1Nil(...)
1266 if select(1,...)==nil then
1267 return select(2,...)
1268 end
1269 return ...
1270 end
1271
1272 function Dewdrop:FeedAceOptionsTable(options, difference)
1273 self:argCheck(options, 2, "table")
1274 self:argCheck(difference, 3, "nil", "number")
1275 if not currentLevel then
1276 self:error("Cannot call `FeedAceOptionsTable' outside of a Dewdrop declaration")
1277 end
1278 if not difference then
1279 difference = 0
1280 end
1281 if not validatedOptions then
1282 validatedOptions = {}
1283 end
1284 if not validatedOptions[options] then
1285 local err, position = validateOptions(options)
1286
1287 if err then
1288 if position then
1289 Dewdrop:error(position .. ": " .. err)
1290 else
1291 Dewdrop:error(err)
1292 end
1293 end
1294
1295 validatedOptions[options] = true
1296 end
1297 local level = levels[currentLevel]
1298 if not level then
1299 self:error("Improper level given")
1300 end
1301 if not values then
1302 values = {}
1303 else
1304 for k,v in pairs(values) do
1305 values[k] = nil
1306 end
1307 end
1308
1309 local current = level
1310 while current do -- this traverses from higher level numbers to lower, building "values" with leaf nodes first and trunk nodes later
1311 if current.num == difference + 1 then
1312 break
1313 end
1314 table.insert(values, current.value)
1315 current = levels[current.num - 1]
1316 end
1317
1318 local realOptions = options
1319 local handler = options.handler
1320 local passTable
1321 local passValue
1322 while #values > 0 do -- This loop traverses values from the END (trunk nodes first, then onto leaf nodes)
1323 if options.pass then
1324 if options.get and options.set then
1325 passTable = options
1326 elseif not passTable then
1327 passTable = options
1328 end
1329 else
1330 passTable = nil
1331 end
1332 local value = table.remove(values)
1333 options = options.args and options.args[value]
1334 if not options then
1335 return
1336 end
1337 handler = options.handler or handler
1338 passValue = passTable and value or nil
1339 end
1340
1341 if options.type == "group" then
1342 local hidden = options.hidden
1343 if type(hidden) == "function" or type(hidden) == "string" then
1344 hidden = callMethod(options.name or "(options root)", handler, options, "hidden", options.passValue) or false
1345 end
1346 if hidden then
1347 return
1348 end
1349 local disabled = options.disabled
1350 if type(disabled) == "function" or type(disabled) == "string" then
1351 disabled = callMethod(options.name or "(options root)", handler, options, "disabled", options.passValue) or false
1352 end
1353 if disabled then
1354 self:AddLine(
1355 'text', DISABLED,
1356 'disabled', true
1357 )
1358 return
1359 end
1360 for k in pairs(options.args) do
1361 table.insert(values, k)
1362 end
1363 if options.pass then
1364 if options.get and options.set then
1365 passTable = options
1366 elseif not passTable then
1367 passTable = options
1368 end
1369 else
1370 passTable = nil
1371 end
1372 if not mysort then
1373 mysort = function(a, b)
1374 local alpha, bravo = mysort_args[a], mysort_args[b]
1375 local alpha_order = alpha.order or 100
1376 local bravo_order = bravo.order or 100
1377 local alpha_name = alpha.guiName or alpha.name
1378 local bravo_name = bravo.guiName or bravo.name
1379 if alpha_order == bravo_order then
1380 if not alpha_name then
1381 return bravo_name
1382 elseif not bravo_name then
1383 return false
1384 else
1385 return alpha_name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() < bravo_name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper()
1386 end
1387 else
1388 if alpha_order < 0 then
1389 if bravo_order > 0 then
1390 return false
1391 end
1392 else
1393 if bravo_order < 0 then
1394 return true
1395 end
1396 end
1397 return alpha_order < bravo_order
1398 end
1399 end
1400 end
1401 mysort_args = options.args
1402 table.sort(values, mysort)
1403 mysort_args = nil
1404 local hasBoth = #values >= 1 and (options.args[values[1]].order or 100) > 0 and (options.args[values[#values]].order or 100) < 0
1405 local last_order = 1
1406 for _,k in ipairs(values) do
1407 local v = options.args[k]
1408 local handler = v.handler or handler
1409 if hasBoth and last_order > 0 and (v.order or 100) < 0 then
1410 hasBoth = false
1411 self:AddLine()
1412 end
1413 local hidden, disabled = v.guiHidden or v.hidden, v.disabled
1414
1415 if type(hidden) == "function" or type(hidden) == "string" then
1416 hidden = callMethod(k, handler, v, "hidden", v.passValue) or false
1417 end
1418 if not hidden then
1419 if type(disabled) == "function" or type(disabled) == "string" then
1420 disabled = callMethod(k, handler, v, "disabled", v.passValue) or false
1421 end
1422 local name = (v.guiIconOnly and v.icon) and "" or (v.guiName or v.name)
1423 local desc = v.guiDesc or v.desc
1424 local iconHeight = v.iconHeight or 16
1425 local iconWidth = v.iconWidth or 16
1426 local iconCoordLeft = v.iconCoordLeft
1427 local iconCoordRight = v.iconCoordRight
1428 local iconCoordBottom = v.iconCoordBottom
1429 local iconCoordTop = v.iconCoordTop
1430 local tooltipTitle, tooltipText
1431 tooltipTitle = name
1432 if name ~= desc then
1433 tooltipText = desc
1434 end
1435 if type(v.usage) == "string" and v.usage:trim():len() > 0 then
1436 if tooltipText then
1437 tooltipText = tooltipText .. "\n\n" .. USAGE_TOOLTIP:format(v.usage)
1438 else
1439 tooltipText = USAGE_TOOLTIP:format(v.usage)
1440 end
1441 end
1442 local v_p = passTable
1443 if not v_p or (v.type ~= "execute" and v.get and v.set) or (v.type == "execute" and v.func) then
1444 v_p = v
1445 end
1446 local passValue = v.passValue or (v_p~=v and k) or nil
1447 if v.type == "toggle" then
1448 local checked = callMethod(name, handler, v_p, "get", passValue) or false
1449 local checked_arg = checked
1450 if type(v_p.get)=="string" and v_p.get:match("^~") then
1451 checked_arg = not checked
1452 end
1453 local func, arg1, arg2, arg3 = getMethod(name, handler, v_p, "set", skip1Nil(passValue, not checked_arg))
1454 if v.guiNameIsMap then
1455 checked = checked and true or false
1456 name = tostring(v.map and v.map[checked]):gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
1457 tooltipTitle = name
1458 checked = true--nil
1459 end
1460 self:AddLine(
1461 'text', name,
1462 'checked', checked,
1463 'isRadio', v.isRadio,
1464 'func', func,
1465 'arg1', arg1,
1466 'arg2', arg2,
1467 'arg3', arg3,
1468 'disabled', disabled,
1469 'tooltipTitle', tooltipTitle,
1470 'tooltipText', tooltipText
1471 )
1472 elseif v.type == "execute" then
1473 local func, arg1, arg2, arg3, arg4
1474 local confirm = v.confirm
1475 if confirm == true then
1476 confirm = DEFAULT_CONFIRM_MESSAGE:format(tooltipText or tooltipTitle)
1477 func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue)
1478 elseif type(confirm) == "string" then
1479 func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue)
1480 else
1481 func,arg1,arg2 = getMethod(name, handler, v_p, "func", passValue)
1482 end
1483 self:AddLine(
1484 'text', name,
1485 'checked', checked,
1486 'func', func,
1487 'arg1', arg1,
1488 'arg2', arg2,
1489 'arg3', arg3,
1490 'arg4', arg4,
1491 'disabled', disabled,
1492 'tooltipTitle', tooltipTitle,
1493 'tooltipText', tooltipText,
1494 'icon', v.icon,
1495 'iconHeight', iconHeight,
1496 'iconWidth', iconWidth,
1497 'iconCoordLeft', iconCoordLeft,
1498 'iconCoordRight', iconCoordRight,
1499 'iconCoordTop', iconCoordTop,
1500 'iconCoordBottom', iconCoordBottom
1501 )
1502 elseif v.type == "range" then
1503 local sliderValue
1504 sliderValue = callMethod(name, handler, v_p, "get", passValue) or 0
1505 local sliderFunc, sliderArg1, sliderArg2 = getMethod(name, handler, v_p, "set", passValue)
1506 if tooltipText then
1507 tooltipText = format("%s\n\n%s", tooltipText, RANGE_TOOLTIP)
1508 else
1509 tooltipText = RANGE_TOOLTIP
1510 end
1511 self:AddLine(
1512 'text', name,
1513 'hasArrow', true,
1514 'hasSlider', true,
1515 'sliderMin', v.min or 0,
1516 'sliderMax', v.max or 1,
1517 'sliderStep', v.step or 0,
1518 'sliderBigStep', v.bigStep or nil,
1519 'sliderIsPercent', v.isPercent or false,
1520 'sliderValue', sliderValue,
1521 'sliderFunc', sliderFunc,
1522 'sliderArg1', sliderArg1,
1523 'sliderArg2', sliderArg2,
1524 'fromAceOptions', true,
1525 'disabled', disabled,
1526 'tooltipTitle', tooltipTitle,
1527 'tooltipText', tooltipText,
1528 'icon', v.icon,
1529 'iconHeight', iconHeight,
1530 'iconWidth', iconWidth,
1531 'iconCoordLeft', iconCoordLeft,
1532 'iconCoordRight', iconCoordRight,
1533 'iconCoordTop', iconCoordTop,
1534 'iconCoordBottom', iconCoordBottom
1535 )
1536 elseif v.type == "color" then
1537 local r,g,b,a = callMethod(name, handler, v_p, "get", passValue)
1538 if not r then
1539 r,g,b,a = 0,0,0,0
1540 end
1541 local colorFunc, colorArg1, colorArg2 = getMethod(name, handler, v_p, "set", passValue)
1542 self:AddLine(
1543 'text', name,
1544 'hasArrow', true,
1545 'hasColorSwatch', true,
1546 'r', r,
1547 'g', g,
1548 'b', b,
1549 'opacity', v.hasAlpha and a or nil,
1550 'hasOpacity', v.hasAlpha,
1551 'colorFunc', colorFunc,
1552 'colorArg1', colorArg1,
1553 'colorArg2', colorArg2,
1554 'disabled', disabled,
1555 'tooltipTitle', tooltipTitle,
1556 'tooltipText', tooltipText
1557 )
1558 elseif v.type == "text" then
1559 if type(v.validate) == "table" then
1560 local func,arg1,arg2
1561 if v.onClick then
1562 func,arg1,arg2 = getMethod(name, handler, v, "onClick", passValue)
1563 end
1564 local checked
1565 if v.isChecked then
1566 checked = callMethod(name, handler, v, "isChecked", passValue) or false
1567 end
1568 self:AddLine(
1569 'text', name,
1570 'hasArrow', true,
1571 'value', k,
1572 'func', func,
1573 'arg1', arg1,
1574 'arg2', arg2,
1575 'mouseoverUnderline', func and true or nil,
1576 'disabled', disabled,
1577 'checked', checked,
1578 'tooltipTitle', tooltipTitle,
1579 'tooltipText', tooltipText,
1580 'icon', v.icon,
1581 'iconHeight', iconHeight,
1582 'iconWidth', iconWidth,
1583 'iconCoordLeft', iconCoordLeft,
1584 'iconCoordRight', iconCoordRight,
1585 'iconCoordTop', iconCoordTop,
1586 'iconCoordBottom', iconCoordBottom
1587 )
1588 else
1589 local editBoxText
1590 editBoxText = callMethod(name, handler, v_p, "get", passValue) or ""
1591 local editBoxFunc, editBoxArg1, editBoxArg2 = getMethod(name, handler, v_p, "set", passValue)
1592
1593 local editBoxValidateFunc, editBoxValidateArg1
1594
1595 if v.validate and v.validate ~= "keybinding" then
1596 if v.validate == "keybinding" then
1597 if tooltipText then
1598 tooltipText = format("%s\n\n%s", tooltipText, RESET_KEYBINDING_DESC)
1599 else
1600 tooltipText = RESET_KEYBINDING_DESC
1601 end
1602 else
1603 editBoxValidateFunc, editBoxValidateArg1 = getMethod(name, handler, v, "validate") -- no passvalue!
1604 end
1605 end
1606
1607 self:AddLine(
1608 'text', name,
1609 'hasArrow', true,
1610 'icon', v.icon,
1611 'iconHeight', iconHeight,
1612 'iconWidth', iconWidth,
1613 'iconCoordLeft', iconCoordLeft,
1614 'iconCoordRight', iconCoordRight,
1615 'iconCoordTop', iconCoordTop,
1616 'iconCoordBottom', iconCoordBottom,
1617 'hasEditBox', true,
1618 'editBoxText', editBoxText,
1619 'editBoxFunc', editBoxFunc,
1620 'editBoxArg1', editBoxArg1,
1621 'editBoxArg2', editBoxArg2,
1622 'editBoxValidateFunc', editBoxValidateFunc,
1623 'editBoxValidateArg1', editBoxValidateArg1,
1624 'editBoxIsKeybinding', v.validate == "keybinding",
1625 'editBoxKeybindingOnly', v.keybindingOnly,
1626 'editBoxKeybindingExcept', v.keybindingExcept,
1627 'disabled', disabled,
1628 'tooltipTitle', tooltipTitle,
1629 'tooltipText', tooltipText
1630 )
1631 end
1632 elseif v.type == "group" then
1633 local func,arg1,arg2
1634 if v.onClick then
1635 func,arg1,arg2 = getMethod(name, handler, v, "onClick", passValue)
1636 end
1637 local checked
1638 if v.isChecked then
1639 checked = callMethod(name, handler, v, "isChecked", passValue) or false
1640 end
1641 self:AddLine(
1642 'text', name,
1643 'hasArrow', true,
1644 'value', k,
1645 'func', func,
1646 'arg1', arg1,
1647 'arg2', arg2,
1648 'mouseoverUnderline', func and true or nil,
1649 'disabled', disabled,
1650 'checked', checked,
1651 'tooltipTitle', tooltipTitle,
1652 'tooltipText', tooltipText,
1653 'icon', v.icon,
1654 'iconHeight', iconHeight,
1655 'iconWidth', iconWidth,
1656 'iconCoordLeft', iconCoordLeft,
1657 'iconCoordRight', iconCoordRight,
1658 'iconCoordTop', iconCoordTop,
1659 'iconCoordBottom', iconCoordBottom
1660 )
1661 elseif v.type == "header" then
1662 if name == "" or not name then
1663 self:AddLine(
1664 'isTitle', true,
1665 'icon', v.icon,
1666 'iconHeight', iconHeight,
1667 'iconWidth', iconWidth,
1668 'iconCoordLeft', iconCoordLeft,
1669 'iconCoordRight', iconCoordRight,
1670 'iconCoordTop', iconCoordTop,
1671 'iconCoordBottom', iconCoordBottom
1672 )
1673 else
1674 self:AddLine(
1675 'text', name,
1676 'isTitle', true,
1677 'icon', v.icon,
1678 'iconHeight', iconHeight,
1679 'iconWidth', iconWidth,
1680 'iconCoordLeft', iconCoordLeft,
1681 'iconCoordRight', iconCoordRight,
1682 'iconCoordTop', iconCoordTop,
1683 'iconCoordBottom', iconCoordBottom
1684 )
1685 end
1686 end
1687 end
1688 last_order = v.order or 100
1689 end
1690 elseif options.type == "text" and type(options.validate) == "table" then
1691 local current
1692 local options_p = passTable
1693 if not options_p or (options.get and options.set) then
1694 options_p = options
1695 passTable = nil
1696 passValue = nil
1697 end
1698 local multiToggle = options.multiToggle
1699 local passValue = options.passValue or passValue
1700 if not multiToggle then
1701 current = callMethod(k, handler, options_p, "get", passValue)
1702 end
1703 local indexed = true
1704 for k,v in pairs(options.validate) do
1705 if type(k) ~= "number" then
1706 indexed = false
1707 end
1708 table.insert(values, k)
1709 end
1710 if not indexed then
1711 if not othersort then
1712 othersort = function(alpha, bravo)
1713 return othersort_validate[alpha]:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() < othersort_validate[bravo]:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper()
1714 end
1715 end
1716 othersort_validate = options.validate
1717 table.sort(values, othersort)
1718 othersort_validate = nil
1719 end
1720 for _,k in ipairs(values) do
1721 local v = options.validate[k]
1722 if type(k) == "number" then
1723 k = v
1724 end
1725 local func, arg1, arg2, arg3, arg4 = getMethod(k, handler, options_p, "set", skip1Nil(passValue, k))
1726 local checked
1727 if multiToggle then
1728 checked = callMethod(k, handler, options_p, "get", skip1Nil(passValue, k)) or false
1729 if arg2 == nil then
1730 arg2 = not checked
1731 elseif arg3 == nil then
1732 arg3 = not checked
1733 else
1734 arg4 = not checked
1735 end
1736 else
1737 checked = (k == current or (type(k) == "string" and type(current) == "string" and k:lower() == current:lower()))
1738 if checked then
1739 func, arg1, arg2, arg3, arg4 = nil, nil, nil, nil, nil
1740 end
1741 end
1742 local tooltipTitle
1743 local tooltipText
1744 if options.validateDesc then
1745 tooltipTitle = v
1746 tooltipText = options.validateDesc[k]
1747 else
1748 tooltipTitle = options.guiName or options.name
1749 tooltipText = v
1750 end
1751 self:AddLine(
1752 'text', v,
1753 'func', func,
1754 'arg1', arg1,
1755 'arg2', arg2,
1756 'arg3', arg3,
1757 'arg4', arg4,
1758 'isRadio', not multiToggle,
1759 'checked', checked,
1760 'tooltipTitle', tooltipTitle,
1761 'tooltipText', tooltipText
1762 )
1763 end
1764 for k in pairs(values) do
1765 values[k] = nil
1766 end
1767 else
1768 return false
1769 end
1770 return true
1771 end
1772
1773 function Dewdrop:FeedTable(s, difference)
1774 self:argCheck(s, 2, "table")
1775 self:argCheck(difference, 3, "nil", "number")
1776 if not currentLevel then
1777 self:error("Cannot call `FeedTable' outside of a Dewdrop declaration")
1778 end
1779 if not difference then
1780 difference = 0
1781 end
1782 local level = levels[currentLevel]
1783 if not level then
1784 self:error("Improper level given")
1785 end
1786 if not values then
1787 values = {}
1788 else
1789 for k,v in pairs(values) do
1790 values[k] = nil
1791 end
1792 end
1793 local t = s.subMenu and s or {subMenu = s}
1794 local current = level
1795 while current do
1796 if current.num == difference + 1 then
1797 break
1798 end
1799 table.insert(values, current.value)
1800 current = levels[current.num - 1]
1801 end
1802
1803 while #values > 0 do
1804 local value = table.remove(values)
1805 t = t.subMenu and t.subMenu[value]
1806 if not t then
1807 return
1808 end
1809 end
1810
1811 if t.subMenu or current.num == 1 then
1812 for k in pairs(t.subMenu) do
1813 table.insert(values, k)
1814 end
1815 table.sort(values)
1816 for _,k in ipairs(values) do
1817 local argTable = {"value", k}
1818 for key, val in pairs(t.subMenu[k]) do
1819 table.insert(argTable, key)
1820 table.insert(argTable, val)
1821 end
1822 self:AddLine(unpack(argTable))
1823 end
1824 for k in pairs(values) do
1825 values[k] = nil
1826 end
1827 return false
1828 end
1829 return true
1830 end
1831
1832 function Refresh(self, level)
1833 if type(level) == "number" then
1834 level = levels[level]
1835 end
1836 if not level then
1837 return
1838 end
1839 if baseFunc then
1840 Clear(self, level)
1841 currentLevel = level.num
1842 if type(baseFunc) == "table" then
1843 if currentLevel == 1 then
1844 local handler = baseFunc.handler
1845 if handler then
1846 local name = tostring(handler)
1847 if not name:find('^table:') and not handler.hideMenuTitle then
1848 name = name:gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
1849 self:AddLine(
1850 'text', name,
1851 'isTitle', true
1852 )
1853 end
1854 end
1855 -- elseif level.parentText then
1856 -- self:AddLine(
1857 -- 'text', level.parentText,
1858 -- 'tooltipTitle', level.parentTooltipTitle,
1859 -- 'tooltipText', level.parentTooltipText,
1860 -- 'tooltipFunc', level.parentTooltipFunc,
1861 -- 'isTitle', true
1862 -- )
1863 end
1864 self:FeedAceOptionsTable(baseFunc)
1865 if currentLevel == 1 then
1866 self:AddLine(
1867 'text', CLOSE,
1868 'tooltipTitle', CLOSE,
1869 'tooltipText', CLOSE_DESC,
1870 'closeWhenClicked', true
1871 )
1872 end
1873 else
1874 -- if level.parentText then
1875 -- self:AddLine(
1876 -- 'text', level.parentText,
1877 -- 'tooltipTitle', level.parentTooltipTitle,
1878 -- 'tooltipText', level.parentTooltipText,
1879 -- 'tooltipFunc', level.parentTooltipFunc,
1880 -- 'isTitle', true
1881 -- )
1882 -- end
1883 baseFunc(currentLevel, level.value, levels[level.num - 1] and levels[level.num - 1].value, levels[level.num - 2] and levels[level.num - 2].value, levels[level.num - 3] and levels[level.num - 3].value, levels[level.num - 4] and levels[level.num - 4].value)
1884 end
1885 currentLevel = nil
1886 CheckSize(self, level)
1887 end
1888 end
1889
1890 function Dewdrop:Refresh(level)
1891 self:argCheck(level, 2, "number", "nil")
1892 if not level then
1893 for k,v in pairs(levels) do
1894 Refresh(self, v)
1895 end
1896 else
1897 Refresh(self, levels[level])
1898 end
1899 end
1900
1901 function OpenSlider(self, parent)
1902 if not sliderFrame then
1903 sliderFrame = CreateFrame("Frame", nil, nil)
1904 sliderFrame:SetWidth(100)
1905 sliderFrame:SetHeight(170)
1906 sliderFrame:SetScale(UIParent:GetScale())
1907 sliderFrame:SetBackdrop(tmp(
1908 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
1909 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
1910 'tile', true,
1911 'insets', tmp2(
1912 'left', 5,
1913 'right', 5,
1914 'top', 5,
1915 'bottom', 5
1916 ),
1917 'tileSize', 16,
1918 'edgeSize', 16
1919 ))
1920 sliderFrame:SetFrameStrata("FULLSCREEN_DIALOG")
1921 if sliderFrame.SetTopLevel then
1922 sliderFrame:SetTopLevel(true)
1923 end
1924 sliderFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
1925 sliderFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
1926 sliderFrame:EnableMouse(true)
1927 sliderFrame:EnableMouseWheel(true)
1928 sliderFrame:Hide()
1929 sliderFrame:SetPoint("CENTER", UIParent, "CENTER")
1930 local slider = CreateFrame("Slider", nil, sliderFrame)
1931 sliderFrame.slider = slider
1932 slider:SetOrientation("VERTICAL")
1933 slider:SetMinMaxValues(0, 1)
1934 slider:SetValueStep(0.000000001)
1935 slider:SetValue(0.5)
1936 slider:SetWidth(16)
1937 slider:SetHeight(128)
1938 slider:SetPoint("LEFT", sliderFrame, "LEFT", 15, 0)
1939 slider:SetBackdrop(tmp(
1940 'bgFile', "Interface\\Buttons\\UI-SliderBar-Background",
1941 'edgeFile', "Interface\\Buttons\\UI-SliderBar-Border",
1942 'tile', true,
1943 'edgeSize', 8,
1944 'tileSize', 8,
1945 'insets', tmp2(
1946 'left', 3,
1947 'right', 3,
1948 'top', 3,
1949 'bottom', 3
1950 )
1951 ))
1952 local texture = slider:CreateTexture()
1953 slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
1954 local text = slider:CreateFontString(nil, "ARTWORK")
1955 sliderFrame.topText = text
1956 text:SetFontObject(GameFontGreenSmall)
1957 text:SetText("100%")
1958 text:SetPoint("BOTTOM", slider, "TOP")
1959 local text = slider:CreateFontString(nil, "ARTWORK")
1960 sliderFrame.bottomText = text
1961 text:SetFontObject(GameFontGreenSmall)
1962 text:SetText("0%")
1963 text:SetPoint("TOP", slider, "BOTTOM")
1964 local editBox = CreateFrame("EditBox", nil, sliderFrame)
1965 sliderFrame.currentText = editBox
1966 editBox:SetFontObject(ChatFontNormal)
1967 editBox:SetHeight(13)
1968 editBox:SetPoint("RIGHT", sliderFrame, "RIGHT", -16, 0)
1969 editBox:SetPoint("LEFT", slider, "RIGHT", 12, 0)
1970 editBox:SetText("50%")
1971 editBox:SetJustifyH("CENTER")
1972
1973 local width = editBox:GetWidth()/2 + 10
1974 local left = editBox:CreateTexture(nil, "BACKGROUND")
1975 left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
1976 left:SetTexCoord(0, width / 256, 0, 1)
1977 left:SetWidth(width)
1978 left:SetHeight(32)
1979 left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
1980 local right = editBox:CreateTexture(nil, "BACKGROUND")
1981 right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
1982 right:SetTexCoord(1 - width / 256, 1, 0, 1)
1983 right:SetWidth(width)
1984 right:SetHeight(32)
1985 right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
1986
1987 local changed = false
1988 local inside = false
1989 slider:SetScript("OnValueChanged", function()
1990 if sliderFrame.changing then
1991 return
1992 end
1993 changed = true
1994 local done = false
1995 if sliderFrame.parent and sliderFrame.parent.sliderFunc then
1996 local min = sliderFrame.parent.sliderMin or 0
1997 local max = sliderFrame.parent.sliderMax or 1
1998 local step
1999 if sliderFrame.fineStep then
2000 step = sliderFrame.parent.sliderStep or (max - min) / 100
2001 else
2002 step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100
2003 end
2004 local value = (1 - slider:GetValue()) * (max - min) + min
2005 if step > 0 then
2006 value = math.floor((value - min) / step + 0.5) * step + min
2007 if value > max then
2008 value = max
2009 elseif value < min then
2010 value = min
2011 end
2012 end
2013 if value == sliderFrame.lastValue then
2014 return
2015 end
2016 sliderFrame.lastValue = value
2017 local text = sliderFrame.parent.sliderFunc(getArgs(sliderFrame.parent, 'sliderArg', 1, value))
2018 if sliderFrame.parent.fromAceOptions then
2019 text = nil
2020 elseif type(text) == "string" or type(text) == "number" then
2021 sliderFrame.currentText:SetText(text)
2022 done = true
2023 end
2024 end
2025 if not done then
2026 local min = sliderFrame.parent.sliderMin or 0
2027 local max = sliderFrame.parent.sliderMax or 1
2028 local step
2029 if sliderFrame.fineStep then
2030 step = sliderFrame.parent.sliderStep or (max - min) / 100
2031 else
2032 step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100
2033 end
2034 local value = (1 - slider:GetValue()) * (max - min) + min
2035 if step > 0 then
2036 value = math.floor((value - min) / step + 0.5) * step + min
2037 if value > max then
2038 value = max
2039 elseif value < min then
2040 value = min
2041 end
2042 end
2043 if sliderFrame.parent.sliderIsPercent then
2044 sliderFrame.currentText:SetText(string.format("%.0f%%", value * 100))
2045 else
2046 if step < 0.1 then
2047 sliderFrame.currentText:SetText(string.format("%.2f", value))
2048 elseif step < 1 then
2049 sliderFrame.currentText:SetText(string.format("%.1f", value))
2050 else
2051 sliderFrame.currentText:SetText(string.format("%.0f", value))
2052 end
2053 end
2054 end
2055 end)
2056 local function onEnter()
2057 StopCounting(self, sliderFrame.level)
2058 showGameTooltip(sliderFrame.parent)
2059 end
2060 local function onLeave()
2061 GameTooltip:Hide()
2062 end
2063 sliderFrame:SetScript("OnEnter", onEnter)
2064 sliderFrame:SetScript("OnLeave", function()
2065 GameTooltip:Hide()
2066 if changed then
2067 local parent = sliderFrame.parent
2068 local sliderFunc = parent.sliderFunc
2069 for i = 1, sliderFrame.level - 1 do
2070 Refresh(self, levels[i])
2071 end
2072 local newParent
2073 for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
2074 if button.sliderFunc == sliderFunc then
2075 newParent = button
2076 break
2077 end
2078 end
2079 if newParent then
2080 OpenSlider(self, newParent)
2081 else
2082 sliderFrame:Hide()
2083 end
2084 end
2085 end)
2086 editBox:SetScript("OnEnter", onEnter)
2087 editBox:SetScript("OnLeave", onLeave)
2088 slider:SetScript("OnMouseDown", function()
2089 sliderFrame.mouseDown = true
2090 GameTooltip:Hide()
2091 end)
2092 slider:SetScript("OnMouseUp", function()
2093 sliderFrame.mouseDown = false
2094 if changed--[[ and not inside]] then
2095 local parent = sliderFrame.parent
2096 local sliderFunc = parent.sliderFunc
2097 for i = 1, sliderFrame.level - 1 do
2098 Refresh(self, levels[i])
2099 end
2100 local newParent
2101 for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
2102 if button.sliderFunc == sliderFunc then
2103 newParent = button
2104 break
2105 end
2106 end
2107 if newParent then
2108 OpenSlider(self, newParent)
2109 else
2110 sliderFrame:Hide()
2111 end
2112 end
2113 if inside then
2114 showGameTooltip(sliderFrame.parent)
2115 end
2116 end)
2117 slider:SetScript("OnEnter", function()
2118 inside = true
2119 StopCounting(self, sliderFrame.level)
2120 showGameTooltip(sliderFrame.parent)
2121 end)
2122 slider:SetScript("OnLeave", function()
2123 inside = false
2124 GameTooltip:Hide()
2125 if changed and not sliderFrame.mouseDown then
2126 local parent = sliderFrame.parent
2127 local sliderFunc = parent.sliderFunc
2128 for i = 1, sliderFrame.level - 1 do
2129 Refresh(self, levels[i])
2130 end
2131 local newParent
2132 for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
2133 if button.sliderFunc == sliderFunc then
2134 newParent = button
2135 break
2136 end
2137 end
2138 if newParent then
2139 OpenSlider(self, newParent)
2140 else
2141 sliderFrame:Hide()
2142 end
2143
2144 changed = false
2145 end
2146 end)
2147 sliderFrame:SetScript("OnMouseWheel", function(t, a1)
2148 local arg1 = a1 or arg1
2149 local up = arg1 > 0
2150
2151 local min = sliderFrame.parent.sliderMin or 0
2152 local max = sliderFrame.parent.sliderMax or 1
2153 local step = sliderFrame.parent.sliderStep or (max - min) / 100
2154 if step <= 0 then
2155 step = (max - min) / 100
2156 end
2157
2158 local value = (1 - slider:GetValue()) * (max - min) + min
2159 if up then
2160 value = value + step
2161 else
2162 value = value - step
2163 end
2164 if value > max then
2165 value = max
2166 elseif value < min then
2167 value = min
2168 end
2169 sliderFrame.fineStep = true
2170 if max<=min then
2171 slider:SetValue(0)
2172 else
2173 slider:SetValue(1 - (value - min) / (max - min))
2174 end
2175 sliderFrame.fineStep = nil
2176 end)
2177 slider:SetScript("OnMouseWheel", sliderFrame:GetScript("OnMouseWheel"))
2178 editBox:SetScript("OnEnterPressed", function(t, a1)
2179 local value = editBox:GetNumber()
2180
2181 if sliderFrame.parent.sliderIsPercent then
2182 value = value / 100
2183 end
2184
2185 local min = sliderFrame.parent.sliderMin or 0
2186 local max = sliderFrame.parent.sliderMax or 1
2187
2188 if value > max then
2189 value = max
2190 elseif value < min then
2191 value = min
2192 end
2193 sliderFrame.fineStep = true
2194 if max <= min then
2195 slider:SetValue(0)
2196 else
2197 slider:SetValue(1 - (value - min) / (max - min))
2198 end
2199 sliderFrame.fineStep = nil
2200
2201 StartCounting(self, sliderFrame.level)
2202 end)
2203 editBox:SetScript("OnEscapePressed", function()
2204 self:Close(sliderFrame.level)
2205 StartCounting(self, sliderFrame.level)
2206 end)
2207 editBox:SetAutoFocus(false)
2208 end
2209 sliderFrame.parent = parent
2210 sliderFrame.level = parent.level.num + 1
2211 sliderFrame.parentValue = parent.level.value
2212 sliderFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
2213 sliderFrame.slider:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
2214 sliderFrame.currentText:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
2215 sliderFrame.currentText:ClearFocus()
2216 sliderFrame.changing = true
2217 if not parent.sliderMin or not parent.sliderMax then
2218 return
2219 end
2220
2221 if parent.arrow then
2222 -- parent.arrow:SetVertexColor(0.2, 0.6, 0)
2223 -- parent.arrow:SetHeight(24)
2224 -- parent.arrow:SetWidth(24)
2225 parent.selected = true
2226 parent.highlight:Show()
2227 end
2228
2229 sliderFrame:SetClampedToScreen(false)
2230 if not parent.sliderValue then
2231 parent.sliderValue = (parent.sliderMin + parent.sliderMax) / 2
2232 end
2233 if parent.sliderMax <= parent.sliderMin then
2234 sliderFrame.slider:SetValue(0)
2235 else
2236 sliderFrame.slider:SetValue(1 - (parent.sliderValue - parent.sliderMin) / (parent.sliderMax - parent.sliderMin))
2237 end
2238 sliderFrame.changing = false
2239 sliderFrame.bottomText:SetText(parent.sliderMinText or "0")
2240 sliderFrame.topText:SetText(parent.sliderMaxText or "1")
2241 local text
2242 if parent.sliderFunc and not parent.fromAceOptions then
2243 text = parent.sliderFunc(getArgs(parent, 'sliderArg', 1, parent.sliderValue))
2244 end
2245 if type(text) == "number" or type(text) == "string" then
2246 sliderFrame.currentText:SetText(text)
2247 elseif parent.sliderIsPercent then
2248 sliderFrame.currentText:SetText(string.format("%.0f%%", parent.sliderValue * 100))
2249 else
2250 if parent.sliderStep < 0.1 then
2251 sliderFrame.currentText:SetText(string.format("%.2f", parent.sliderValue))
2252 elseif parent.sliderStep < 1 then
2253 sliderFrame.currentText:SetText(string.format("%.1f", parent.sliderValue))
2254 else
2255 sliderFrame.currentText:SetText(string.format("%.0f", parent.sliderValue))
2256 end
2257 end
2258
2259
2260 sliderFrame.lastValue = parent.sliderValue
2261
2262 local level = parent.level
2263 sliderFrame:Show()
2264 sliderFrame:ClearAllPoints()
2265 if level.lastDirection == "RIGHT" then
2266 if level.lastVDirection == "DOWN" then
2267 sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
2268 else
2269 sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
2270 end
2271 else
2272 if level.lastVDirection == "DOWN" then
2273 sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
2274 else
2275 sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
2276 end
2277 end
2278 local dirty
2279 if level.lastDirection == "RIGHT" then
2280 if sliderFrame:GetRight() > GetScreenWidth() then
2281 level.lastDirection = "LEFT"
2282 dirty = true
2283 end
2284 elseif sliderFrame:GetLeft() < 0 then
2285 level.lastDirection = "RIGHT"
2286 dirty = true
2287 end
2288 if level.lastVDirection == "DOWN" then
2289 if sliderFrame:GetBottom() < 0 then
2290 level.lastVDirection = "UP"
2291 dirty = true
2292 end
2293 elseif sliderFrame:GetTop() > GetScreenWidth() then
2294 level.lastVDirection = "DOWN"
2295 dirty = true
2296 end
2297 if dirty then
2298 sliderFrame:ClearAllPoints()
2299 if level.lastDirection == "RIGHT" then
2300 if level.lastVDirection == "DOWN" then
2301 sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
2302 else
2303 sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
2304 end
2305 else
2306 if level.lastVDirection == "DOWN" then
2307 sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
2308 else
2309 sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
2310 end
2311 end
2312 end
2313 local left, bottom = sliderFrame:GetLeft(), sliderFrame:GetBottom()
2314 sliderFrame:ClearAllPoints()
2315 sliderFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
2316 if mod(level.num, 5) == 0 then
2317 local left, bottom = level:GetLeft(), level:GetBottom()
2318 level:ClearAllPoints()
2319 level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
2320 end
2321 sliderFrame:SetClampedToScreen(true)
2322 end
2323
2324 function OpenEditBox(self, parent)
2325 if not editBoxFrame then
2326 editBoxFrame = CreateFrame("Frame", nil, nil)
2327 editBoxFrame:SetWidth(200)
2328 editBoxFrame:SetHeight(40)
2329 editBoxFrame:SetScale(UIParent:GetScale())
2330 editBoxFrame:SetBackdrop(tmp(
2331 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
2332 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
2333 'tile', true,
2334 'insets', tmp2(
2335 'left', 5,
2336 'right', 5,
2337 'top', 5,
2338 'bottom', 5
2339 ),
2340 'tileSize', 16,
2341 'edgeSize', 16
2342 ))
2343 editBoxFrame:SetFrameStrata("FULLSCREEN_DIALOG")
2344 if editBoxFrame.SetTopLevel then
2345 editBoxFrame:SetTopLevel(true)
2346 end
2347 editBoxFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
2348 editBoxFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
2349 editBoxFrame:EnableMouse(true)
2350 editBoxFrame:EnableMouseWheel(true)
2351 editBoxFrame:Hide()
2352 editBoxFrame:SetPoint("CENTER", UIParent, "CENTER")
2353
2354 local editBox = CreateFrame("EditBox", nil, editBoxFrame)
2355 editBoxFrame.editBox = editBox
2356 editBox:SetFontObject(ChatFontNormal)
2357 editBox:SetWidth(160)
2358 editBox:SetHeight(13)
2359 editBox:SetPoint("CENTER", editBoxFrame, "CENTER", 0, 0)
2360
2361 local left = editBox:CreateTexture(nil, "BACKGROUND")
2362 left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
2363 left:SetTexCoord(0, 100 / 256, 0, 1)
2364 left:SetWidth(100)
2365 left:SetHeight(32)
2366 left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
2367 local right = editBox:CreateTexture(nil, "BACKGROUND")
2368 right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
2369 right:SetTexCoord(156/256, 1, 0, 1)
2370 right:SetWidth(100)
2371 right:SetHeight(32)
2372 right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
2373
2374 editBox:SetScript("OnEnterPressed", function()
2375 if editBoxFrame.parent and editBoxFrame.parent.editBoxValidateFunc then
2376 local t = editBox.realText or editBox:GetText() or ""
2377 local result = editBoxFrame.parent.editBoxValidateFunc(getArgs(editBoxFrame.parent, 'editBoxValidateArg', 1, t))
2378 if not result then
2379 UIErrorsFrame:AddMessage(VALIDATION_ERROR, 1, 0, 0)
2380 return
2381 end
2382 end
2383 if editBoxFrame.parent and editBoxFrame.parent.editBoxFunc then
2384 local t
2385 if editBox.realText ~= "NONE" then
2386 t = editBox.realText or editBox:GetText() or ""
2387 end
2388 editBoxFrame.parent.editBoxFunc(getArgs(editBoxFrame.parent, 'editBoxArg', 1, t))
2389 end
2390 self:Close(editBoxFrame.level)
2391 for i = 1, editBoxFrame.level - 1 do
2392 Refresh(self, levels[i])
2393 end
2394 StartCounting(self, editBoxFrame.level-1)
2395 end)
2396 editBox:SetScript("OnEscapePressed", function()
2397 self:Close(editBoxFrame.level)
2398 StartCounting(self, editBoxFrame.level-1)
2399 end)
2400 editBox:SetScript("OnReceiveDrag", function(this)
2401 if GetCursorInfo then
2402 local type, alpha, bravo = GetCursorInfo()
2403 local text
2404 if type == "spell" then
2405 text = GetSpellName(alpha, bravo)
2406 elseif type == "item" then
2407 text = bravo
2408 end
2409 if not text then
2410 return
2411 end
2412 ClearCursor()
2413 editBox:SetText(text)
2414 end
2415 end)
2416 local changing = false
2417 local skipNext = false
2418
2419 function editBox:SpecialSetText(text)
2420 local oldText = editBox:GetText() or ""
2421 if not text then
2422 text = ""
2423 end
2424 if text ~= oldText then
2425 changing = true
2426 self:SetText(tostring(text))
2427 changing = false
2428 skipNext = true
2429 end
2430 end
2431
2432 editBox:SetScript("OnTextChanged", function()
2433 if skipNext then
2434 skipNext = false
2435 elseif not changing and editBoxFrame.parent and editBoxFrame.parent.editBoxChangeFunc then
2436 local t
2437 if editBox.realText ~= "NONE" then
2438 t = editBox.realText or editBox:GetText() or ""
2439 end
2440 local text = editBoxFrame.parent.editBoxChangeFunc(getArgs(editBoxFrame.parent, 'editBoxChangeArg', 1, t))
2441 if text then
2442 editBox:SpecialSetText(text)
2443 end
2444 end
2445 end)
2446 editBoxFrame:SetScript("OnEnter", function()
2447 StopCounting(self, editBoxFrame.level)
2448 showGameTooltip(editBoxFrame.parent)
2449 end)
2450 editBoxFrame:SetScript("OnLeave", function()
2451 GameTooltip:Hide()
2452 end)
2453 editBox:SetScript("OnEnter", function()
2454 StopCounting(self, editBoxFrame.level)
2455 showGameTooltip(editBoxFrame.parent)
2456 end)
2457 editBox:SetScript("OnLeave", function()
2458 GameTooltip:Hide()
2459 end)
2460 editBoxFrame:SetScript("OnKeyDown", function(this, a1)
2461 if not editBox.keybinding then
2462 return
2463 end
2464 local arg1 = a1 or arg1
2465 local screenshotKey = GetBindingKey("SCREENSHOT")
2466 if screenshotKey and arg1 == screenshotKey then
2467 Screenshot()
2468 return
2469 end
2470
2471 if arg1 == "LeftButton" then
2472 arg1 = "BUTTON1"
2473 elseif arg1 == "RightButton" then
2474 arg1 = "BUTTON2"
2475 elseif arg1 == "MiddleButton" then
2476 arg1 = "BUTTON3"
2477 elseif arg1 == "Button4" then
2478 arg1 = "BUTTON4"
2479 elseif arg1 == "Button5" then
2480 arg1 = "BUTTON5"
2481 end
2482 if arg1 == "UNKNOWN" then
2483 return
2484 elseif arg1 == "SHIFT" or arg1 == "CTRL" or arg1 == "ALT" then
2485 return
2486 elseif arg1 == "ENTER" then
2487 if editBox.keybindingOnly and not editBox.keybindingOnly[editBox.realText] then
2488 return editBox:GetScript("OnEscapePressed")()
2489 elseif editBox.keybindingExcept and editBox.keybindingExcept[editBox.realText] then
2490 return editBox:GetScript("OnEscapePressed")()
2491 else
2492 return editBox:GetScript("OnEnterPressed")()
2493 end
2494 elseif arg1 == "ESCAPE" then
2495 if editBox.realText == "NONE" then
2496 return editBox:GetScript("OnEscapePressed")()
2497 else
2498 editBox:SpecialSetText(NONE or "NONE")
2499 editBox.realText = "NONE"
2500 return
2501 end
2502 elseif editBox.keybindingOnly and not editBox.keybindingOnly[arg1] then
2503 return
2504 elseif editBox.keybindingExcept and editBox.keybindingExcept[arg1] then
2505 return
2506 end
2507 local s = GetBindingText(arg1, "KEY_")
2508 if s == "BUTTON1" then
2509 s = KEY_BUTTON1
2510 elseif s == "BUTTON2" then
2511 s = KEY_BUTTON2
2512 end
2513 local real = arg1
2514 if IsShiftKeyDown() then
2515 s = "Shift-" .. s
2516 real = "SHIFT-" .. real
2517 end
2518 if IsControlKeyDown() then
2519 s = "Ctrl-" .. s
2520 real = "CTRL-" .. real
2521 end
2522 if IsAltKeyDown() then
2523 s = "Alt-" .. s
2524 real = "ALT-" .. real
2525 end
2526 if editBox:GetText() ~= s then
2527 editBox:SpecialSetText("-")
2528 editBox:SpecialSetText(s)
2529 editBox.realText = real
2530 return editBox:GetScript("OnTextChanged")()
2531 end
2532 end)
2533 editBoxFrame:SetScript("OnMouseDown", editBoxFrame:GetScript("OnKeyDown"))
2534 editBox:SetScript("OnMouseDown", function(this, ...)
2535 if GetCursorInfo and (CursorHasItem() or CursorHasSpell()) then
2536 return editBox:GetScript("OnReceiveDrag")(this, ...)
2537 end
2538 return editBoxFrame:GetScript("OnKeyDown")(this, ...)
2539 end)
2540 editBoxFrame:SetScript("OnMouseWheel", function(t, a1)
2541 local arg1 = a1 or arg1
2542 local up = arg1 > 0
2543 arg1 = up and "MOUSEWHEELUP" or "MOUSEWHEELDOWN"
2544 return editBoxFrame:GetScript("OnKeyDown")(t or this, arg1)
2545 end)
2546 editBox:SetScript("OnMouseWheel", editBoxFrame:GetScript("OnMouseWheel"))
2547 end
2548 editBoxFrame.parent = parent
2549 editBoxFrame.level = parent.level.num + 1
2550 editBoxFrame.parentValue = parent.level.value
2551 editBoxFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
2552 editBoxFrame.editBox:SetFrameLevel(editBoxFrame:GetFrameLevel() + 1)
2553 editBoxFrame.editBox.realText = nil
2554 editBoxFrame:SetClampedToScreen(false)
2555
2556 editBoxFrame.editBox:SpecialSetText("")
2557 if parent.editBoxIsKeybinding then
2558 local s = parent.editBoxText
2559 if s == "" then
2560 s = "NONE"
2561 end
2562 editBoxFrame.editBox.realText = s
2563 if s and s ~= "NONE" then
2564 local alpha,bravo = s:match("^(.+)%-(.+)$")
2565 if not bravo then
2566 alpha = nil
2567 bravo = s
2568 end
2569 bravo = GetBindingText(bravo, "KEY_")
2570 if alpha then
2571 editBoxFrame.editBox:SpecialSetText(alpha:upper() .. "-" .. bravo)
2572 else
2573 editBoxFrame.editBox:SpecialSetText(bravo)
2574 end
2575 else
2576 editBoxFrame.editBox:SpecialSetText(NONE or "NONE")
2577 end
2578 else
2579 editBoxFrame.editBox:SpecialSetText(parent.editBoxText)
2580 end
2581
2582 editBoxFrame.editBox.keybinding = parent.editBoxIsKeybinding
2583 editBoxFrame.editBox.keybindingOnly = parent.editBoxKeybindingOnly
2584 editBoxFrame.editBox.keybindingExcept = parent.editBoxKeybindingExcept
2585 editBoxFrame.editBox:EnableKeyboard(not parent.editBoxIsKeybinding)
2586 editBoxFrame:EnableKeyboard(parent.editBoxIsKeybinding)
2587
2588 if parent.arrow then
2589 -- parent.arrow:SetVertexColor(0.2, 0.6, 0)
2590 -- parent.arrow:SetHeight(24)
2591 -- parent.arrow:SetWidth(24)
2592 parent.selected = true
2593 parent.highlight:Show()
2594 end
2595
2596 local level = parent.level
2597 editBoxFrame:Show()
2598 editBoxFrame:ClearAllPoints()
2599 if level.lastDirection == "RIGHT" then
2600 if level.lastVDirection == "DOWN" then
2601 editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
2602 else
2603 editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
2604 end
2605 else
2606 if level.lastVDirection == "DOWN" then
2607 editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
2608 else
2609 editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
2610 end
2611 end
2612 local dirty
2613 if level.lastDirection == "RIGHT" then
2614 if editBoxFrame:GetRight() > GetScreenWidth() then
2615 level.lastDirection = "LEFT"
2616 dirty = true
2617 end
2618 elseif editBoxFrame:GetLeft() < 0 then
2619 level.lastDirection = "RIGHT"
2620 dirty = true
2621 end
2622 if level.lastVDirection == "DOWN" then
2623 if editBoxFrame:GetBottom() < 0 then
2624 level.lastVDirection = "UP"
2625 dirty = true
2626 end
2627 elseif editBoxFrame:GetTop() > GetScreenWidth() then
2628 level.lastVDirection = "DOWN"
2629 dirty = true
2630 end
2631 if dirty then
2632 editBoxFrame:ClearAllPoints()
2633 if level.lastDirection == "RIGHT" then
2634 if level.lastVDirection == "DOWN" then
2635 editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
2636 else
2637 editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
2638 end
2639 else
2640 if level.lastVDirection == "DOWN" then
2641 editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
2642 else
2643 editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
2644 end
2645 end
2646 end
2647 local left, bottom = editBoxFrame:GetLeft(), editBoxFrame:GetBottom()
2648 editBoxFrame:ClearAllPoints()
2649 editBoxFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
2650 if mod(level.num, 5) == 0 then
2651 local left, bottom = level:GetLeft(), level:GetBottom()
2652 level:ClearAllPoints()
2653 level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
2654 end
2655 editBoxFrame:SetClampedToScreen(true)
2656 end
2657
2658 function Dewdrop:EncodeKeybinding(text)
2659 if text == nil or text == "NONE" then
2660 return nil
2661 end
2662 text = tostring(text):upper()
2663 local shift, ctrl, alt
2664 local modifier
2665 while true do
2666 if text == "-" then
2667 break
2668 end
2669 modifier, text = strsplit('-', text, 2)
2670 if text then
2671 if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then
2672 return false
2673 end
2674 if modifier == "SHIFT" then
2675 if shift then
2676 return false
2677 end
2678 shift = true
2679 end
2680 if modifier == "CTRL" then
2681 if ctrl then
2682 return false
2683 end
2684 ctrl = true
2685 end
2686 if modifier == "ALT" then
2687 if alt then
2688 return false
2689 end
2690 alt = true
2691 end
2692 else
2693 text = modifier
2694 break
2695 end
2696 end
2697 if not text:find("^F%d+$") and text ~= "CAPSLOCK" and text:len() ~= 1 and (text:len() == 0 or text:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] and text ~= "BUTTON1" and text ~= "BUTTON2" then
2698 return false
2699 end
2700 local s = GetBindingText(text, "KEY_")
2701 if s == "BUTTON1" then
2702 s = KEY_BUTTON1
2703 elseif s == "BUTTON2" then
2704 s = KEY_BUTTON2
2705 end
2706 if shift then
2707 s = "Shift-" .. s
2708 end
2709 if ctrl then
2710 s = "Ctrl-" .. s
2711 end
2712 if alt then
2713 s = "Alt-" .. s
2714 end
2715 return s
2716 end
2717
2718 function Dewdrop:IsOpen(parent)
2719 self:argCheck(parent, 2, "table", "string", "nil")
2720 return levels[1] and levels[1]:IsShown() and (not parent or parent == levels[1].parent or parent == levels[1]:GetParent())
2721 end
2722
2723 function Dewdrop:GetOpenedParent()
2724 return (levels[1] and levels[1]:IsShown()) and (levels[1].parent or levels[1]:GetParent())
2725 end
2726
2727 function Open(self, parent, func, level, value, point, relativePoint, cursorX, cursorY)
2728 self:Close(level)
2729 if DewdropLib then
2730 local d = DewdropLib:GetInstance('1.0')
2731 local ret, val = pcall(d, IsOpen, d)
2732 if ret and val then
2733 DewdropLib:GetInstance('1.0'):Close()
2734 end
2735 end
2736 if type(parent) == "table" then
2737 parent:GetCenter()
2738 end
2739 local frame = AcquireLevel(self, level)
2740 if level == 1 then
2741 frame.lastDirection = "RIGHT"
2742 frame.lastVDirection = "DOWN"
2743 else
2744 frame.lastDirection = levels[level - 1].lastDirection
2745 frame.lastVDirection = levels[level - 1].lastVDirection
2746 end
2747 frame:SetFrameStrata("FULLSCREEN_DIALOG")
2748 frame:ClearAllPoints()
2749 frame.parent = parent
2750 frame:SetPoint("LEFT", UIParent, "RIGHT", 10000, 0)
2751 frame:Show()
2752 if level == 1 then
2753 baseFunc = func
2754 end
2755 levels[level].value = value
2756 -- levels[level].parentText = parent.text and parent.text:GetText() or nil
2757 -- levels[level].parentTooltipTitle = parent.tooltipTitle
2758 -- levels[level].parentTooltipText = parent.tooltipText
2759 -- levels[level].parentTooltipFunc = parent.tooltipFunc
2760 if type(parent) == "table" and parent.arrow then
2761 -- parent.arrow:SetVertexColor(0.2, 0.6, 0)
2762 -- parent.arrow:SetHeight(24)
2763 -- parent.arrow:SetWidth(24)
2764 parent.selected = true
2765 parent.highlight:Show()
2766 end
2767 relativePoint = relativePoint or point
2768 Refresh(self, levels[level])
2769 if point or (cursorX and cursorY) then
2770 frame:ClearAllPoints()
2771 if cursorX and cursorY then
2772 local curX, curY = GetScaledCursorPosition()
2773 if curY < GetScreenHeight() / 2 then
2774 point, relativePoint = "BOTTOM", "BOTTOM"
2775 else
2776 point, relativePoint = "TOP", "TOP"
2777 end
2778 if curX < GetScreenWidth() / 2 then
2779 point, relativePoint = point .. "LEFT", relativePoint .. "RIGHT"
2780 else
2781 point, relativePoint = point .. "RIGHT", relativePoint .. "LEFT"
2782 end
2783 end
2784 frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint)
2785 if cursorX and cursorY then
2786 local left = frame:GetLeft()
2787 local width = frame:GetWidth()
2788 local bottom = frame:GetBottom()
2789 local height = frame:GetHeight()
2790 local curX, curY = GetScaledCursorPosition()
2791 frame:ClearAllPoints()
2792 relativePoint = relativePoint or point
2793 if point == "BOTTOM" or point == "TOP" then
2794 if curX < GetScreenWidth() / 2 then
2795 point = point .. "LEFT"
2796 else
2797 point = point .. "RIGHT"
2798 end
2799 elseif point == "CENTER" then
2800 if curX < GetScreenWidth() / 2 then
2801 point = "LEFT"
2802 else
2803 point = "RIGHT"
2804 end
2805 end
2806 local xOffset, yOffset = 0, 0
2807 if curY > GetScreenHeight() / 2 then
2808 yOffset = -height
2809 end
2810 if curX > GetScreenWidth() / 2 then
2811 xOffset = -width
2812 end
2813 frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left + xOffset, curY - bottom + yOffset)
2814 if level == 1 then
2815 frame.lastDirection = "RIGHT"
2816 end
2817 elseif cursorX then
2818 local left = frame:GetLeft()
2819 local width = frame:GetWidth()
2820 local curX, curY = GetScaledCursorPosition()
2821 frame:ClearAllPoints()
2822 relativePoint = relativePoint or point
2823 if point == "BOTTOM" or point == "TOP" then
2824 if curX < GetScreenWidth() / 2 then
2825 point = point .. "LEFT"
2826 else
2827 point = point .. "RIGHT"
2828 end
2829 elseif point == "CENTER" then
2830 if curX < GetScreenWidth() / 2 then
2831 point = "LEFT"
2832 else
2833 point = "RIGHT"
2834 end
2835 end
2836 frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left - width / 2, 0)
2837 if level == 1 then
2838 frame.lastDirection = "RIGHT"
2839 end
2840 elseif cursorY then
2841 local bottom = frame:GetBottom()
2842 local height = frame:GetHeight()
2843 local curX, curY = GetScaledCursorPosition()
2844 frame:ClearAllPoints()
2845 relativePoint = relativePoint or point
2846 if point == "LEFT" or point == "RIGHT" then
2847 if curX < GetScreenHeight() / 2 then
2848 point = point .. "BOTTOM"
2849 else
2850 point = point .. "TOP"
2851 end
2852 elseif point == "CENTER" then
2853 if curX < GetScreenHeight() / 2 then
2854 point = "BOTTOM"
2855 else
2856 point = "TOP"
2857 end
2858 end
2859 frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, 0, curY - bottom - height / 2)
2860 if level == 1 then
2861 frame.lastDirection = "DOWN"
2862 end
2863 end
2864 if (strsub(point, 1, 3) ~= strsub(relativePoint, 1, 3)) then
2865 if frame:GetBottom() < 0 then
2866 local point, parent, relativePoint, x, y = frame:GetPoint(1)
2867 local change = GetScreenHeight() - frame:GetTop()
2868 local otherChange = -frame:GetBottom()
2869 if otherChange < change then
2870 change = otherChange
2871 end
2872 frame:SetPoint(point, parent, relativePoint, x, y + change)
2873 elseif frame:GetTop() > GetScreenHeight() then
2874 local point, parent, relativePoint, x, y = frame:GetPoint(1)
2875 local change = GetScreenHeight() - frame:GetTop()
2876 local otherChange = -frame:GetBottom()
2877 if otherChange < change then
2878 change = otherChange
2879 end
2880 frame:SetPoint(point, parent, relativePoint, x, y + change)
2881 end
2882 end
2883 end
2884 CheckDualMonitor(self, frame)
2885 frame:SetClampedToScreen(true)
2886 frame:SetClampedToScreen(false)
2887 StartCounting(self, level)
2888 end
2889
2890 function Dewdrop:IsRegistered(parent)
2891 self:argCheck(parent, 2, "table", "string")
2892 return not not self.registry[parent]
2893 end
2894
2895 function Dewdrop:Register(parent, ...)
2896 self:argCheck(parent, 2, "table", "string")
2897 if self.registry[parent] then
2898 self:Unregister(parent)
2899 end
2900 local info = new(...)
2901 if type(info.children) == "table" then
2902 local err, position = validateOptions(info.children)
2903
2904 if err then
2905 if position then
2906 Dewdrop:error(position .. ": " .. err)
2907 else
2908 Dewdrop:error(err)
2909 end
2910 end
2911 end
2912 self.registry[parent] = info
2913 if not info.dontHook and not self.onceRegistered[parent] and type(parent) == "table" then
2914 if parent:HasScript("OnMouseUp") then
2915 local script = parent:GetScript("OnMouseUp")
2916 parent:SetScript("OnMouseUp", function(this, ...)
2917 if script then
2918 script(this, ...)
2919 end
2920 if arg1 == "RightButton" and self.registry[parent] then
2921 if self:IsOpen(parent) then
2922 self:Close()
2923 else
2924 self:Open(parent)
2925 end
2926 end
2927 end)
2928 end
2929 if parent:HasScript("OnMouseDown") then
2930 local script = parent:GetScript("OnMouseDown")
2931 parent:SetScript("OnMouseDown", function(this, ...)
2932 if script then
2933 script(this, ...)
2934 end
2935 if self.registry[parent] then
2936 self:Close()
2937 end
2938 end)
2939 end
2940 end
2941 self.onceRegistered[parent] = true
2942 end
2943
2944 function Dewdrop:Unregister(parent)
2945 self:argCheck(parent, 2, "table", "string")
2946 self.registry[parent] = nil
2947 end
2948
2949 function Dewdrop:Open(parent, ...)
2950 self:argCheck(parent, 2, "table", "string")
2951 local info
2952 local k1 = ...
2953 if type(k1) == "table" and k1[0] and k1.IsFrameType and self.registry[k1] then
2954 info = tmp(select(2, ...))
2955 for k,v in pairs(self.registry[k1]) do
2956 if info[k] == nil then
2957 info[k] = v
2958 end
2959 end
2960 else
2961 info = tmp(...)
2962 if self.registry[parent] then
2963 for k,v in pairs(self.registry[parent]) do
2964 if info[k] == nil then
2965 info[k] = v
2966 end
2967 end
2968 end
2969 end
2970 local point = info.point
2971 local relativePoint = info.relativePoint
2972 local cursorX = info.cursorX
2973 local cursorY = info.cursorY
2974 if type(point) == "function" then
2975 local b
2976 point, b = point(parent)
2977 if b then
2978 relativePoint = b
2979 end
2980 end
2981 if type(relativePoint) == "function" then
2982 relativePoint = relativePoint(parent)
2983 end
2984 Open(self, parent, info.children, 1, nil, point, relativePoint, cursorX, cursorY)
2985 end
2986
2987 function Clear(self, level)
2988 if level then
2989 if level.buttons then
2990 for i = #level.buttons, 1, -1 do
2991 ReleaseButton(self, level, i)
2992 end
2993 end
2994 end
2995 end
2996
2997 function Dewdrop:Close(level)
2998 if DropDownList1:IsShown() then
2999 DropDownList1:Hide()
3000 end
3001 if DewdropLib then
3002 local d = DewdropLib:GetInstance('1.0')
3003 local ret, val = pcall(d, IsOpen, d)
3004 if ret and val then
3005 DewdropLib:GetInstance('1.0'):Close()
3006 end
3007 end
3008 self:argCheck(level, 2, "number", "nil")
3009 if not level then
3010 level = 1
3011 end
3012 if level == 1 and levels[level] then
3013 levels[level].parented = false
3014 end
3015 if level > 1 and levels[level-1].buttons then
3016 local buttons = levels[level-1].buttons
3017 for _,button in ipairs(buttons) do
3018 -- button.arrow:SetWidth(16)
3019 -- button.arrow:SetHeight(16)
3020 button.selected = nil
3021 button.highlight:Hide()
3022 -- button.arrow:SetVertexColor(1, 1, 1)
3023 end
3024 end
3025 if sliderFrame and sliderFrame.level >= level then
3026 sliderFrame:Hide()
3027 end
3028 if editBoxFrame and editBoxFrame.level >= level then
3029 editBoxFrame:Hide()
3030 end
3031 for i = level, #levels do
3032 Clear(self, levels[level])
3033 levels[i]:Hide()
3034 levels[i]:ClearAllPoints()
3035 levels[i]:SetPoint("CENTER", UIParent, "CENTER")
3036 levels[i].value = nil
3037 end
3038 end
3039
3040 function Dewdrop:AddSeparator(level)
3041 level = levels[level or currentLevel]
3042 if not level or not level.buttons then return; end
3043
3044 local prevbutton = level.buttons[#level.buttons]
3045 if not prevbutton then return; end
3046
3047 if prevbutton.disabled and prevbutton.text:GetText() == "" then
3048 return
3049 end
3050 self:AddLine("text", "", "disabled", true)
3051 end
3052
3053 function Dewdrop:AddLine(...)
3054 local info = tmp(...)
3055 local level = info.level or currentLevel
3056 info.level = nil
3057 local button = AcquireButton(self, level)
3058 if not next(info) then
3059 info.disabled = true
3060 end
3061 button.disabled = info.isTitle or info.notClickable or info.disabled or (self.combat and info.secure)
3062 button.isTitle = info.isTitle
3063 button.notClickable = info.notClickable
3064 if button.isTitle then
3065 button.text:SetFontObject(GameFontNormalSmall)
3066 elseif button.notClickable then
3067 button.text:SetFontObject(GameFontHighlightSmall)
3068 elseif button.disabled then
3069 button.text:SetFontObject(GameFontDisableSmall)
3070 else
3071 button.text:SetFontObject(GameFontHighlightSmall)
3072 end
3073 if info.disabled then
3074 button.arrow:SetDesaturated(true)
3075 button.check:SetDesaturated(true)
3076 else
3077 button.arrow:SetDesaturated(false)
3078 button.check:SetDesaturated(false)
3079 end
3080 if info.textR and info.textG and info.textB then
3081 button.textR = info.textR
3082 button.textG = info.textG
3083 button.textB = info.textB
3084 button.text:SetTextColor(button.textR, button.textG, button.textB)
3085 else
3086 button.text:SetTextColor(button.text:GetFontObject():GetTextColor())
3087 end
3088 button.notCheckable = info.notCheckable
3089 button.text:SetPoint("LEFT", button, "LEFT", button.notCheckable and 0 or 24, 0)
3090 button.checked = not info.notCheckable and info.checked
3091 button.mouseoverUnderline = info.mouseoverUnderline
3092 button.isRadio = not info.notCheckable and info.isRadio
3093 if info.isRadio then
3094 button.check:Show()
3095 button.check:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
3096 if button.checked then
3097 button.check:SetTexCoord(0.25, 0.5, 0, 1)
3098 button.check:SetVertexColor(1, 1, 1, 1)
3099 else
3100 button.check:SetTexCoord(0, 0.25, 0, 1)
3101 button.check:SetVertexColor(1, 1, 1, 0.5)
3102 end
3103 button.radioHighlight:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
3104 button.check:SetWidth(16)
3105 button.check:SetHeight(16)
3106 elseif info.icon then
3107 button.check:Show()
3108 button.check:SetTexture(info.icon)
3109 if info.iconWidth and info.iconHeight then
3110 button.check:SetWidth(info.iconWidth)
3111 button.check:SetHeight(info.iconHeight)
3112 else
3113 button.check:SetWidth(16)
3114 button.check:SetHeight(16)
3115 end
3116 if info.iconCoordLeft and info.iconCoordRight and info.iconCoordTop and info.iconCoordBottom then
3117 button.check:SetTexCoord(info.iconCoordLeft, info.iconCoordRight, info.iconCoordTop, info.iconCoordBottom)
3118 elseif info.icon:find("^Interface\\Icons\\") then
3119 button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
3120 else
3121 button.check:SetTexCoord(0, 1, 0, 1)
3122 end
3123 button.check:SetVertexColor(1, 1, 1, 1)
3124 else
3125 if button.checked then
3126 if info.checkIcon then
3127 button.check:SetWidth(16)
3128 button.check:SetHeight(16)
3129 button.check:SetTexture(info.checkIcon)
3130 if info.checkIcon:find("^Interface\\Icons\\") then
3131 button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
3132 else
3133 button.check:SetTexCoord(0, 1, 0, 1)
3134 end
3135 else
3136 button.check:SetWidth(24)
3137 button.check:SetHeight(24)
3138 button.check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
3139 button.check:SetTexCoord(0, 1, 0, 1)
3140 end
3141 button.check:SetVertexColor(1, 1, 1, 1)
3142 else
3143 button.check:SetVertexColor(1, 1, 1, 0)
3144 end
3145 end
3146 if not button.disabled then
3147 button.func = info.func
3148 button.secure = info.secure
3149 end
3150 button.hasColorSwatch = info.hasColorSwatch
3151 if button.hasColorSwatch then
3152 button.colorSwatch:Show()
3153 button.colorSwatch.texture:Show()
3154 button.r = info.r or 1
3155 button.g = info.g or 1
3156 button.b = info.b or 1
3157 button.colorSwatch.texture:SetVertexColor(button.r, button.g, button.b)
3158 button.checked = false
3159 button.func = nil
3160 button.colorFunc = info.colorFunc
3161 local i = 1
3162 while true do
3163 local k = "colorArg" .. i
3164 local x = info[k]
3165 if x == nil then
3166 break
3167 end
3168 button[k] = x
3169 i = i + 1
3170 end
3171 button.hasOpacity = info.hasOpacity
3172 button.opacity = info.opacity or 1
3173 else
3174 button.colorSwatch:Hide()
3175 button.colorSwatch.texture:Hide()
3176 end
3177 button.hasArrow = not button.hasColorSwatch and (info.value or info.hasSlider or info.hasEditBox) and info.hasArrow
3178 if button.hasArrow then
3179 button.arrow:SetAlpha(1)
3180 if info.hasSlider then
3181 button.hasSlider = true
3182 button.sliderMin = info.sliderMin or 0
3183 button.sliderMax = info.sliderMax or 1
3184 button.sliderStep = info.sliderStep or 0
3185 button.sliderBigStep = info.sliderBigStep or button.sliderStep
3186 if button.sliderBigStep < button.sliderStep then
3187 button.sliderBigStep = button.sliderStep
3188 end
3189 button.sliderIsPercent = info.sliderIsPercent and true or false
3190 button.sliderMinText = info.sliderMinText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMin * 100) or button.sliderMin
3191 button.sliderMaxText = info.sliderMaxText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMax * 100) or button.sliderMax
3192 button.sliderFunc = info.sliderFunc
3193 button.sliderValue = info.sliderValue
3194 button.fromAceOptions = info.fromAceOptions
3195 local i = 1
3196 while true do
3197 local k = "sliderArg" .. i
3198 local x = info[k]
3199 if x == nil then
3200 break
3201 end
3202 button[k] = x
3203 i = i + 1
3204 end
3205 elseif info.hasEditBox then
3206 button.hasEditBox = true
3207 button.editBoxText = info.editBoxText or ""
3208 button.editBoxFunc = info.editBoxFunc
3209 local i = 1
3210 while true do
3211 local k = "editBoxArg" .. i
3212 local x = info[k]
3213 if x == nil then
3214 break
3215 end
3216 button[k] = x
3217 i = i + 1
3218 end
3219 button.editBoxChangeFunc = info.editBoxChangeFunc
3220 local i = 1
3221 while true do
3222 local k = "editBoxChangeArg" .. i
3223 local x = info[k]
3224 if x == nil then
3225 break
3226 end
3227 button[k] = x
3228 i = i + 1
3229 end
3230 button.editBoxValidateFunc = info.editBoxValidateFunc
3231 local i = 1
3232 while true do
3233 local k = "editBoxValidateArg" .. i
3234 local x = info[k]
3235 if x == nil then
3236 break
3237 end
3238 button[k] = x
3239 i = i + 1
3240 end
3241 button.editBoxIsKeybinding = info.editBoxIsKeybinding
3242 button.editBoxKeybindingOnly = info.editBoxKeybindingOnly
3243 button.editBoxKeybindingExcept = info.editBoxKeybindingExcept
3244 else
3245 button.value = info.value
3246 local l = levels[level+1]
3247 if l and info.value == l.value then
3248 -- button.arrow:SetWidth(24)
3249 -- button.arrow:SetHeight(24)
3250 button.selected = true
3251 button.highlight:Show()
3252 end
3253 end
3254 else
3255 button.arrow:SetAlpha(0)
3256 end
3257 local i = 1
3258 while true do
3259 local k = "arg" .. i
3260 local x = info[k]
3261 if x == nil then
3262 break
3263 end
3264 button[k] = x
3265 i = i + 1
3266 end
3267 button.closeWhenClicked = info.closeWhenClicked
3268 button.textHeight = info.textHeight or UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT or 10
3269 local font,_ = button.text:GetFont()
3270 button.text:SetFont(STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF", button.textHeight)
3271 button:SetHeight(button.textHeight + 6)
3272 button.text:SetPoint("RIGHT", button.arrow, (button.hasColorSwatch or button.hasArrow) and "LEFT" or "RIGHT")
3273 button.text:SetJustifyH(info.justifyH or "LEFT")
3274 button.text:SetText(info.text)
3275 button.tooltipTitle = info.tooltipTitle
3276 button.tooltipText = info.tooltipText
3277 button.tooltipFunc = info.tooltipFunc
3278 local i = 1
3279 while true do
3280 local k = "tooltipArg" .. i
3281 local x = info[k]
3282 if x == nil then
3283 break
3284 end
3285 button[k] = x
3286 i = i + 1
3287 end
3288 if not button.tooltipTitle and not button.tooltipText and not button.tooltipFunc and not info.isTitle then
3289 button.tooltipTitle = info.text
3290 end
3291 if type(button.func) == "string" then
3292 if type(button.arg1) ~= "table" then
3293 self:error("Cannot call method %q on a non-table", button.func)
3294 end
3295 if type(button.arg1[button.func]) ~= "function" then
3296 self:error("Method %q nonexistant.", button.func)
3297 end
3298 end
3299 end
3300
3301 function Dewdrop:InjectAceOptionsTable(handler, options)
3302 self:argCheck(handler, 2, "table")
3303 self:argCheck(options, 3, "table")
3304 if tostring(options.type):lower() ~= "group" then
3305 self:error('Cannot inject into options table argument #3 if its type is not "group"')
3306 end
3307 if options.handler ~= nil and options.handler ~= handler then
3308 self:error("Cannot inject into options table argument #3 if it has a different handler than argument #2")
3309 end
3310 options.handler = handler
3311 local class = handler.class
3312 if not AceLibrary:HasInstance("AceOO-2.0") or not class then
3313 if Rock then
3314 -- possible Rock object
3315 for mixin in Rock:IterateObjectMixins(handler) do
3316 if type(mixin.GetAceOptionsDataTable) == "function" then
3317 local t = mixin:GetAceOptionsDataTable(handler)
3318 for k,v in pairs(t) do
3319 if type(options.args) ~= "table" then
3320 options.args = {}
3321 end
3322 if options.args[k] == nil then
3323 options.args[k] = v
3324 end
3325 end
3326 end
3327 end
3328 end
3329 else
3330 -- Ace2 object
3331 while class and class ~= AceLibrary("AceOO-2.0").Class do
3332 if type(class.GetAceOptionsDataTable) == "function" then
3333 local t = class:GetAceOptionsDataTable(handler)
3334 for k,v in pairs(t) do
3335 if type(options.args) ~= "table" then
3336 options.args = {}
3337 end
3338 if options.args[k] == nil then
3339 options.args[k] = v
3340 end
3341 end
3342 end
3343 local mixins = class.mixins
3344 if mixins then
3345 for mixin in pairs(mixins) do
3346 if type(mixin.GetAceOptionsDataTable) == "function" then
3347 local t = mixin:GetAceOptionsDataTable(handler)
3348 for k,v in pairs(t) do
3349 if type(options.args) ~= "table" then
3350 options.args = {}
3351 end
3352 if options.args[k] == nil then
3353 options.args[k] = v
3354 end
3355 end
3356 end
3357 end
3358 end
3359 class = class.super
3360 end
3361 end
3362 return options
3363 end
3364
3365 function Dewdrop:OnTooltipHide()
3366 if lastSetFont then
3367 if lastSetFont == normalFont then
3368 lastSetFont = nil
3369 return
3370 end
3371 fillRegionTmp(GameTooltip:GetRegions())
3372 for i,v in ipairs(regionTmp) do
3373 if v.GetFont then
3374 local font,size,outline = v:GetFont()
3375 if font == lastSetFont then
3376 v:SetFont(normalFont, size, outline)
3377 end
3378 end
3379 regionTmp[i] = nil
3380 end
3381 lastSetFont = nil
3382 end
3383 end
3384
3385 local function activate(self, oldLib, oldDeactivate)
3386 Dewdrop = self
3387 if oldLib and oldLib.registry then
3388 self.registry = oldLib.registry
3389 self.onceRegistered = oldLib.onceRegistered
3390 else
3391 self.registry = {}
3392 self.onceRegistered = {}
3393
3394 local WorldFrame_OnMouseDown = WorldFrame:GetScript("OnMouseDown")
3395 local WorldFrame_OnMouseUp = WorldFrame:GetScript("OnMouseUp")
3396 local oldX, oldY, clickTime
3397 WorldFrame:SetScript("OnMouseDown", function(this, ...)
3398 oldX,oldY = GetCursorPosition()
3399 clickTime = GetTime()
3400 if WorldFrame_OnMouseDown then
3401 WorldFrame_OnMouseDown(this, ...)
3402 end
3403 end)
3404
3405 WorldFrame:SetScript("OnMouseUp", function(this, ...)
3406 local x,y = GetCursorPosition()
3407 if not oldX or not oldY or not x or not y or not clickTime then
3408 self:Close()
3409 if WorldFrame_OnMouseUp then
3410 WorldFrame_OnMouseUp(this, ...)
3411 end
3412 return
3413 end
3414 local d = math.abs(x - oldX) + math.abs(y - oldY)
3415 if d <= 5 and GetTime() - clickTime < 0.5 then
3416 self:Close()
3417 end
3418 if WorldFrame_OnMouseUp then
3419 WorldFrame_OnMouseUp(this, ...)
3420 end
3421 end)
3422
3423 hooksecurefunc(DropDownList1, "Show", function()
3424 if levels[1] and levels[1]:IsVisible() then
3425 self:Close()
3426 end
3427 end)
3428
3429 hooksecurefunc("HideDropDownMenu", function()
3430 if levels[1] and levels[1]:IsVisible() then
3431 self:Close()
3432 end
3433 end)
3434
3435 hooksecurefunc("CloseDropDownMenus", function()
3436 if levels[1] and levels[1]:IsVisible() then
3437 local stack = debugstack()
3438 if not stack:find("`TargetFrame_OnHide'") then
3439 self:Close()
3440 end
3441 end
3442 end)
3443 end
3444 self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
3445 self.frame:UnregisterAllEvents()
3446 self.frame:RegisterEvent("PLAYER_REGEN_ENABLED")
3447 self.frame:RegisterEvent("PLAYER_REGEN_DISABLED")
3448 self.frame:Hide()
3449 self.frame:SetScript("OnEvent", function(this, event)
3450 this:Show()
3451 if event=="PLAYER_REGEN_ENABLED" then -- track combat state for secure frame operations
3452 self.combat = false
3453 elseif event=="PLAYER_REGEN_DISABLED" then
3454 self.combat = true
3455 end
3456 end)
3457 self.frame:SetScript("OnUpdate", function(this)
3458 this:Hide()
3459 self:Refresh(1)
3460 end)
3461 self.hookedTooltip = true
3462 if not oldLib or not oldLib.hookedTooltip then
3463 local OnTooltipHide = GameTooltip:GetScript("OnHide")
3464 GameTooltip:SetScript("OnHide", function(this, ...)
3465 if OnTooltipHide then
3466 OnTooltipHide(this, ...)
3467 end
3468 if type(self.OnTooltipHide) == "function" then
3469 self:OnTooltipHide()
3470 end
3471 end)
3472 end
3473 levels = {}
3474 buttons = {}
3475
3476 if oldDeactivate then
3477 oldDeactivate(oldLib)
3478 end
3479 end
3480
3481 local function external(lib, major, instance)
3482 if major == "SharedMedia-1.0" then
3483 SharedMedia = instance
3484 end
3485 end
3486
3487 AceLibrary:Register(Dewdrop, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)