vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Name: Dewdrop-2.0
3 Revision: $Rev: 11598 $
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 ]]
11  
12 local MAJOR_VERSION = "Dewdrop-2.0"
13 local MINOR_VERSION = "$Revision: 11598 $"
14  
15 if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
16 if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
17  
18 local Dewdrop = {}
19  
20 local table_setn
21 do
22 local version = GetBuildInfo()
23 if string.find(version, "^2%.") then
24 -- 2.0.0
25 table_setn = function() end
26 else
27 table_setn = table.setn
28 end
29 end
30  
31 local function new(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
32 local t = {}
33 if k1 then t[k1] = v1
34 if k2 then t[k2] = v2
35 if k3 then t[k3] = v3
36 if k4 then t[k4] = v4
37 if k5 then t[k5] = v5
38 if k6 then t[k6] = v6
39 if k7 then t[k7] = v7
40 if k8 then t[k8] = v8
41 if k9 then t[k9] = v9
42 if k10 then t[k10] = v10
43 if k11 then t[k11] = v11
44 if k12 then t[k12] = v12
45 if k13 then t[k13] = v13
46 if k14 then t[k14] = v14
47 if k15 then t[k15] = v15
48 if k16 then t[k16] = v16
49 if k17 then t[k17] = v17
50 if k18 then t[k18] = v18
51 if k19 then t[k19] = v19
52 if k20 then t[k20] = v20
53 end end end end end end end end end end end end end end end end end end end end
54 return t
55 end
56 local tmp
57 do
58 local t
59 function tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
60 for k in pairs(t) do
61 t[k] = nil
62 end
63 if type(k1) == "table" then
64 for k,v in pairs(k1) do
65 t[k] = v
66 end
67 else
68 if k1 then t[k1] = v1
69 if k2 then t[k2] = v2
70 if k3 then t[k3] = v3
71 if k4 then t[k4] = v4
72 if k5 then t[k5] = v5
73 if k6 then t[k6] = v6
74 if k7 then t[k7] = v7
75 if k8 then t[k8] = v8
76 if k9 then t[k9] = v9
77 if k10 then t[k10] = v10
78 if k11 then t[k11] = v11
79 if k12 then t[k12] = v12
80 if k13 then t[k13] = v13
81 if k14 then t[k14] = v14
82 if k15 then t[k15] = v15
83 if k16 then t[k16] = v16
84 if k17 then t[k17] = v17
85 if k18 then t[k18] = v18
86 if k19 then t[k19] = v19
87 if k20 then t[k20] = v20
88 end end end end end end end end end end end end end end end end end end end end
89 end
90 return t
91 end
92 local x = tmp
93 function tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
94 t = {}
95 tmp = x
96 x = nil
97 return tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
98 end
99 end
100 local tmp2
101 do
102 local t
103 function tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
104 for k in pairs(t) do
105 t[k] = nil
106 end
107 if k1 then t[k1] = v1
108 if k2 then t[k2] = v2
109 if k3 then t[k3] = v3
110 if k4 then t[k4] = v4
111 if k5 then t[k5] = v5
112 if k6 then t[k6] = v6
113 if k7 then t[k7] = v7
114 if k8 then t[k8] = v8
115 if k9 then t[k9] = v9
116 if k10 then t[k10] = v10
117 if k11 then t[k11] = v11
118 if k12 then t[k12] = v12
119 if k13 then t[k13] = v13
120 if k14 then t[k14] = v14
121 if k15 then t[k15] = v15
122 if k16 then t[k16] = v16
123 if k17 then t[k17] = v17
124 if k18 then t[k18] = v18
125 if k19 then t[k19] = v19
126 if k20 then t[k20] = v20
127 end end end end end end end end end end end end end end end end end end end end
128 return t
129 end
130 local x = tmp2
131 function tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
132 t = {}
133 tmp2 = x
134 x = nil
135 return tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
136 end
137 end
138 local levels
139 local buttons
140  
141 local function GetScaledCursorPosition()
142 local x, y = GetCursorPosition()
143 local scale = UIParent:GetEffectiveScale()
144 return x / scale, y / scale
145 end
146  
147 local function StartCounting(self, levelNum)
148 for i = levelNum, table.getn(levels) do
149 if levels[i] then
150 levels[i].count = 3
151 end
152 end
153 end
154  
155 local function StopCounting(self, level)
156 for i = level, 1, -1 do
157 if levels[i] then
158 levels[i].count = nil
159 end
160 end
161 end
162  
163 local function OnUpdate(self, arg1)
164 for _,level in ipairs(levels) do
165 if level.count then
166 level.count = level.count - arg1
167 if level.count < 0 then
168 level.count = nil
169 self:Close(level.num)
170 end
171 end
172 end
173 end
174  
175 local function CheckDualMonitor(self, frame)
176 local ratio = GetScreenWidth() / GetScreenHeight()
177 if ratio >= 2.4 and frame:GetRight() > GetScreenWidth() / 2 and frame:GetLeft() < GetScreenWidth() / 2 then
178 local offsetx
179 if GetCursorPosition() / GetScreenHeight() * 768 < GetScreenWidth() / 2 then
180 offsetx = GetScreenWidth() / 2 - frame:GetRight()
181 else
182 offsetx = GetScreenWidth() / 2 - frame:GetLeft()
183 end
184 local point, parent, relativePoint, x, y = frame:GetPoint(1)
185 frame:SetPoint(point, parent, relativePoint, (x or 0) + offsetx, y or 0)
186 end
187 end
188  
189 local function CheckSize(self, level)
190 if not level.buttons then
191 return
192 end
193 local height = 20
194 for _, button in ipairs(level.buttons) do
195 height = height + button:GetHeight()
196 end
197 level:SetHeight(height)
198 local width = 160
199 for _, button in ipairs(level.buttons) do
200 local extra = 1
201 if button.hasArrow or button.hasColorSwatch then
202 extra = extra + 16
203 end
204 if not button.notCheckable then
205 extra = extra + 24
206 end
207 button.text:SetFont(STANDARD_TEXT_FONT, button.textHeight)
208 if button.text:GetWidth() + extra > width then
209 width = button.text:GetWidth() + extra
210 end
211 end
212 level:SetWidth(width + 20)
213 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
214 level:ClearAllPoints()
215 if level.lastDirection == "RIGHT" then
216 if level.lastVDirection == "DOWN" then
217 level:SetPoint("TOPLEFT", level.parent or level:GetParent(), "TOPRIGHT", 5, 10)
218 else
219 level:SetPoint("BOTTOMLEFT", level.parent or level:GetParent(), "BOTTOMRIGHT", 5, -10)
220 end
221 else
222 if level.lastVDirection == "DOWN" then
223 level:SetPoint("TOPRIGHT", level.parent or level:GetParent(), "TOPLEFT", -5, 10)
224 else
225 level:SetPoint("BOTTOMRIGHT", level.parent or level:GetParent(), "BOTTOMLEFT", -5, -10)
226 end
227 end
228 end
229 local dirty = false
230 if not level:GetRight() then
231 self:Close()
232 return
233 end
234 if level:GetRight() > GetScreenWidth() and level.lastDirection == "RIGHT" then
235 level.lastDirection = "LEFT"
236 dirty = true
237 elseif level:GetLeft() < 0 and level.lastDirection == "LEFT" then
238 level.lastDirection = "RIGHT"
239 dirty = true
240 end
241 if level:GetTop() > GetScreenHeight() and level.lastVDirection == "UP" then
242 level.lastVDirection = "DOWN"
243 dirty = true
244 elseif level:GetBottom() < 0 and level.lastVDirection == "DOWN" then
245 level.lastVDirection = "UP"
246 dirty = true
247 end
248 if dirty then
249 level:ClearAllPoints()
250 if level.lastDirection == "RIGHT" then
251 if level.lastVDirection == "DOWN" then
252 level:SetPoint("TOPLEFT", level.parent or level:GetParent(), "TOPRIGHT", 5, 10)
253 else
254 level:SetPoint("BOTTOMLEFT", level.parent or level:GetParent(), "BOTTOMRIGHT", 5, -10)
255 end
256 else
257 if level.lastVDirection == "DOWN" then
258 level:SetPoint("TOPRIGHT", level.parent or level:GetParent(), "TOPLEFT", -5, 10)
259 else
260 level:SetPoint("BOTTOMRIGHT", level.parent or level:GetParent(), "BOTTOMLEFT", -5, -10)
261 end
262 end
263 end
264 if level:GetTop() > GetScreenHeight() then
265 local top = level:GetTop()
266 local point, parent, relativePoint, x, y = level:GetPoint(1)
267 level:ClearAllPoints()
268 level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) + GetScreenHeight() - top)
269 elseif level:GetBottom() < 0 then
270 local bottom = level:GetBottom()
271 local point, parent, relativePoint, x, y = level:GetPoint(1)
272 level:ClearAllPoints()
273 level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) - bottom)
274 end
275 CheckDualMonitor(self, level)
276 if mod(level.num, 5) == 0 then
277 local left, bottom = level:GetLeft(), level:GetBottom()
278 level:ClearAllPoints()
279 level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
280 end
281 end
282  
283 local Open
284 local OpenSlider
285 local OpenEditBox
286 local Refresh
287 local Clear
288 local function ReleaseButton(self, level, index)
289 if not level.buttons then
290 return
291 end
292 if not level.buttons[index] then
293 return
294 end
295 local button = level.buttons[index]
296 button:Hide()
297 if button.highlight then
298 button.highlight:Hide()
299 end
300 table.remove(level.buttons, index)
301 table.insert(buttons, button)
302 for k in pairs(button) do
303 if k ~= 0 and k ~= "text" and k ~= "check" and k ~= "arrow" and k ~= "colorSwatch" and k ~= "highlight" and k ~= "radioHighlight" then
304 button[k] = nil
305 end
306 end
307 return true
308 end
309  
310 local function Scroll(self, level, down)
311 if down then
312 if level:GetBottom() < 0 then
313 local point, parent, relativePoint, x, y = level:GetPoint(1)
314 level:SetPoint(point, parent, relativePoint, x, y + 50)
315 if level:GetBottom() > 0 then
316 level:SetPoint(point, parent, relativePoint, x, y + 50 - level:GetBottom())
317 end
318 end
319 else
320 if level:GetTop() > GetScreenHeight() then
321 local point, parent, relativePoint, x, y = level:GetPoint(1)
322 level:SetPoint(point, parent, relativePoint, x, y - 50)
323 if level:GetTop() < GetScreenHeight() then
324 level:SetPoint(point, parent, relativePoint, x, y - 50 + GetScreenHeight() - level:GetTop())
325 end
326 end
327 end
328 end
329  
330 local sliderFrame
331 local editBoxFrame
332  
333 local function AcquireButton(self, level)
334 if not levels[level] then
335 return
336 end
337 level = levels[level]
338 if not level.buttons then
339 level.buttons = {}
340 end
341 local button
342 if table.getn(buttons) == 0 then
343 button = CreateFrame("Button")
344 button:SetFrameStrata("FULLSCREEN_DIALOG")
345 button:SetHeight(16)
346 local highlight = button:CreateTexture(nil, "BACKGROUND")
347 highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
348 button.highlight = highlight
349 highlight:SetBlendMode("ADD")
350 highlight:SetAllPoints(button)
351 highlight:Hide()
352 local check = button:CreateTexture(nil, "ARTWORK")
353 button.check = check
354 check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
355 check:SetPoint("CENTER", button, "LEFT", 12, 0)
356 check:SetWidth(24)
357 check:SetHeight(24)
358 local radioHighlight = button:CreateTexture(nil, "ARTWORK")
359 button.radioHighlight = radioHighlight
360 radioHighlight:SetTexture("Interface\\Buttons\\UI-RadioButton")
361 radioHighlight:SetAllPoints(check)
362 radioHighlight:SetBlendMode("ADD")
363 radioHighlight:SetTexCoord(0.5, 0.75, 0, 1)
364 radioHighlight:Hide()
365 button:SetScript("OnEnter", function()
366 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
367 for i = 1, this.level.num do
368 Refresh(self, levels[i])
369 end
370 return
371 end
372 self:Close(this.level.num + 1)
373 if not this.disabled then
374 if this.hasSlider then
375 OpenSlider(self, this)
376 elseif this.hasEditBox then
377 OpenEditBox(self, this)
378 elseif this.hasArrow then
379 Open(self, this, nil, this.level.num + 1, this.value)
380 end
381 end
382 if not this.level then -- button reclaimed
383 return
384 end
385 StopCounting(self, this.level.num + 1)
386 if not this.disabled then
387 highlight:Show()
388 if this.isRadio then
389 button.radioHighlight:Show()
390 end
391 end
392 if this.tooltipTitle or this.tooltipText then
393 GameTooltip_SetDefaultAnchor(GameTooltip, this)
394 local disabled = this.disabled
395 if this.tooltipTitle then
396 if disabled then
397 GameTooltip:SetText(this.tooltipTitle, 0.5, 0.5, 0.5, 1)
398 else
399 GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1)
400 end
401 if this.tooltipText then
402 if disabled then
403 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)
404 else
405 GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
406 end
407 end
408 else
409 if disabled then
410 GameTooltip:SetText(this.tooltipText, 0.5, 0.5, 0.5, 1)
411 else
412 GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1)
413 end
414 end
415 GameTooltip:Show()
416 end
417 if this.tooltipFunc then
418 GameTooltip:SetOwner(this, "ANCHOR_NONE")
419 GameTooltip:SetPoint("TOPLEFT", this, "TOPRIGHT", 5, 0)
420 this.tooltipFunc(this.tooltipArg1, this.tooltipArg2, this.tooltipArg3, this.tooltipArg4)
421 GameTooltip:Show()
422 end
423 end)
424 button:SetScript("OnLeave", function()
425 highlight:Hide()
426 button.radioHighlight:Hide()
427 if this.level then
428 StartCounting(self, this.level.num)
429 end
430 GameTooltip:Hide()
431 end)
432 button:SetScript("OnClick", function()
433 if not this.disabled then
434 if this.hasColorSwatch then
435 local func = button.colorFunc
436 local a1,a2,a3,a4 = button.colorArg1, button.colorArg2, button.colorArg3, button.colorArg4
437 local hasOpacity = this.hasOpacity
438 ColorPickerFrame.func = function()
439 if func then
440 local r,g,b = ColorPickerFrame:GetColorRGB()
441 local a = hasOpacity and 1 - OpacitySliderFrame:GetValue() or nil
442 if a1 == nil then
443 func(r, g, b, a)
444 elseif a2 == nil then
445 func(a1, r, g, b, a)
446 elseif a3 == nil then
447 func(a1, a2, r, g, b, a)
448 elseif a4 == nil then
449 func(a1, a2, a3, r, g, b, a)
450 else
451 func(a1, a2, a3, a4, r, g, b, a)
452 end
453 end
454 end
455 ColorPickerFrame.hasOpacity = this.hasOpacity
456 ColorPickerFrame.opacityFunc = ColorPickerFrame.func
457 ColorPickerFrame.opacity = 1 - this.opacity
458 ColorPickerFrame:SetColorRGB(this.r, this.g, this.b)
459 local r, g, b, a = this.r, this.g, this.b, this.opacity
460 ColorPickerFrame.cancelFunc = function()
461 if a1 == nil then
462 func(r, g, b, a)
463 elseif a2 == nil then
464 func(a1, r, g, b, a)
465 elseif a3 == nil then
466 func(a1, a2, r, g, b, a)
467 else
468 func(a1, a2, a3, r, g, b, a)
469 end
470 end
471 self:Close(1)
472 ShowUIPanel(ColorPickerFrame)
473 elseif this.func then
474 local level = button.level
475 if type(this.func) == "string" then
476 self:assert(type(this.arg1[this.func]) == "function", "Cannot call method " .. this.func)
477 this.arg1[this.func](this.arg1, this.arg2, this.arg3, this.arg4)
478 else
479 this.func(this.arg1, this.arg2, this.arg3, this.arg4)
480 end
481 if this.closeWhenClicked then
482 self:Close()
483 elseif level:IsShown() then
484 for i = 1, level.num do
485 Refresh(self, levels[i])
486 end
487 end
488 elseif this.closeWhenClicked then
489 self:Close()
490 end
491 end
492 end)
493 local text = button:CreateFontString(nil, "ARTWORK")
494 button.text = text
495 text:SetFontObject(GameFontHighlightSmall)
496 button.text:SetFont(STANDARD_TEXT_FONT, UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT)
497 button:SetScript("OnMouseDown", function()
498 if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
499 text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 1 or 25, -1)
500 end
501 end)
502 button:SetScript("OnMouseUp", function()
503 if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
504 text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 0 or 24, 0)
505 end
506 end)
507 local arrow = button:CreateTexture(nil, "ARTWORK")
508 button.arrow = arrow
509 arrow:SetPoint("RIGHT", button, "RIGHT", 0, 0)
510 arrow:SetWidth(16)
511 arrow:SetHeight(16)
512 arrow:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
513 local colorSwatch = button:CreateTexture(nil, "OVERLAY")
514 button.colorSwatch = colorSwatch
515 colorSwatch:SetWidth(20)
516 colorSwatch:SetHeight(20)
517 colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
518 local texture = button:CreateTexture(nil, "OVERLAY")
519 colorSwatch.texture = texture
520 texture:SetTexture(1, 1, 1)
521 texture:SetWidth(11.5)
522 texture:SetHeight(11.5)
523 texture:Show()
524 texture:SetPoint("CENTER", colorSwatch, "CENTER")
525 colorSwatch:SetPoint("RIGHT", button, "RIGHT", 0, 0)
526 else
527 button = buttons[table.getn(buttons)]
528 table.remove(buttons, table.getn(buttons))
529 end
530 button:ClearAllPoints()
531 button:SetParent(level)
532 button:SetFrameStrata(level:GetFrameStrata())
533 button:SetFrameLevel(level:GetFrameLevel() + 1)
534 button:SetPoint("LEFT", level, "LEFT", 10, 0)
535 button:SetPoint("RIGHT", level, "RIGHT", -10, 0)
536 if table.getn(level.buttons) == 0 then
537 button:SetPoint("TOP", level, "TOP", 0, -10)
538 else
539 button:SetPoint("TOP", level.buttons[table.getn(level.buttons)], "BOTTOM", 0, 0)
540 end
541 button.text:SetPoint("LEFT", button, "LEFT", 24, 0)
542 button:Show()
543 button.level = level
544 table.insert(level.buttons, button)
545 if not level.parented then
546 level.parented = true
547 level:ClearAllPoints()
548 if level.num == 1 then
549 if level.parent ~= UIParent then
550 level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT")
551 else
552 level:SetPoint("CENTER", level.parent, "CENTER")
553 end
554 else
555 if level.lastDirection == "RIGHT" then
556 if level.lastVDirection == "DOWN" then
557 level:SetPoint("TOPLEFT", level.parent, "TOPRIGHT", 5, 10)
558 else
559 level:SetPoint("BOTTOMLEFT", level.parent, "BOTTOMRIGHT", 5, -10)
560 end
561 else
562 if level.lastVDirection == "DOWN" then
563 level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT", -5, 10)
564 else
565 level:SetPoint("BOTTOMRIGHT", level.parent, "BOTTOMLEFT", -5, -10)
566 end
567 end
568 end
569 level:SetFrameStrata("FULLSCREEN_DIALOG")
570 end
571 return button
572 end
573  
574 local function AcquireLevel(self, level)
575 if not levels[level] then
576 for i = table.getn(levels) + 1, level, -1 do
577 local i = i
578 local frame = CreateFrame("Button")
579 if i == 1 then
580 local old_CloseWindows = CloseWindows
581 function CloseWindows(ignoreCenter)
582 local found = old_CloseWindows(ignoreCenter)
583 if levels[1]:IsShown() then
584 self:Close()
585 return 1
586 end
587 return found
588 end
589 end
590 levels[i] = frame
591 frame.num = i
592 frame:SetParent(UIParent)
593 frame:SetFrameStrata("FULLSCREEN_DIALOG")
594 frame:Hide()
595 frame:SetWidth(180)
596 frame:SetHeight(10)
597 frame:SetFrameLevel(i * 3)
598 frame:SetScript("OnHide", function()
599 self:Close(level + 1)
600 end)
601 frame:SetFrameStrata("FULLSCREEN_DIALOG")
602 if frame.SetTopLevel then
603 frame:SetTopLevel(true)
604 end
605 frame:EnableMouse(true)
606 frame:EnableMouseWheel(true)
607 local backdrop = CreateFrame("Frame", nil, frame)
608 backdrop:SetAllPoints(frame)
609 backdrop:SetBackdrop(tmp(
610 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
611 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
612 'tile', true,
613 'insets', tmp2(
614 'left', 5,
615 'right', 5,
616 'top', 5,
617 'bottom', 5
618 ),
619 'tileSize', 16,
620 'edgeSize', 16
621 ))
622 backdrop:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
623 backdrop:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
624 frame:SetScript("OnClick", function()
625 self:Close(i)
626 end)
627 frame:SetScript("OnEnter", function()
628 StopCounting(self, i)
629 end)
630 frame:SetScript("OnLeave", function()
631 StartCounting(self, i)
632 end)
633 frame:SetScript("OnMouseWheel", function()
634 Scroll(self, frame, arg1 < 0)
635 end)
636 if i == 1 then
637 frame:SetScript("OnUpdate", function()
638 OnUpdate(self, arg1)
639 end)
640 levels[1].lastDirection = "RIGHT"
641 levels[1].lastVDirection = "DOWN"
642 else
643 levels[i].lastDirection = levels[i - 1].lastDirection
644 levels[i].lastVDirection = levels[i - 1].lastVDirection
645 end
646 end
647 end
648 return levels[level]
649 end
650  
651 local function checkValidate(validateFunc, func, arg1, arg2, arg3)
652 local text
653 if arg3 ~= nil then
654 text = arg3
655 elseif arg2 ~= nil then
656 text = arg2
657 else
658 text = arg1
659 end
660 if not validateFunc(text) then
661 DEFAULT_CHAT_FRAME:AddMessage("|cffffff7fValidation error: [|r" .. tostring(text) .. "|cffffff7f]|r")
662 else
663 func(arg1, arg2, arg3)
664 end
665 end
666  
667 local function validateOptions(options, position, baseOptions, fromPass)
668 if not baseOptions then
669 baseOptions = options
670 end
671 if type(options) ~= "table" then
672 return "Options must be a table.", position
673 end
674 local kind = options.type
675 if type(kind) ~= "string" then
676 return '"type" must be a string.', position
677 elseif kind ~= "group" and kind ~= "range" and kind ~= "text" and kind ~= "execute" and kind ~= "toggle" and kind ~= "color" and kind ~= "header" then
678 return '"type" must either be "range", "text", "group", "toggle", "execute", "color", or "header".', position
679 end
680 if options.aliases then
681 if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then
682 return '"alias" must be a table or string', position
683 end
684 end
685 if not fromPass then
686 if kind == "execute" then
687 if type(options.func) ~= "string" and type(options.func) ~= "function" then
688 return '"func" must be a string or function', position
689 end
690 elseif kind == "range" or kind == "text" or kind == "toggle" then
691 if type(options.set) ~= "string" and type(options.set) ~= "function" then
692 return '"set" must be a string or function', position
693 end
694 if kind == "text" and options.get == false then
695 elseif type(options.get) ~= "string" and type(options.get) ~= "function" then
696 return '"get" must be a string or function', position
697 end
698 elseif kind == "group" and options.pass then
699 if options.pass ~= true then
700 return '"pass" must be either nil, true, or false', position
701 end
702 if not options.func then
703 if type(options.set) ~= "string" and type(options.set) ~= "function" then
704 return '"set" must be a string or function', position
705 end
706 if type(options.get) ~= "string" and type(options.get) ~= "function" then
707 return '"get" must be a string or function', position
708 end
709 elseif type(options.func) ~= "string" and type(options.func) ~= "function" then
710 return '"func" must be a string or function', position
711 end
712 end
713 else
714 if kind == "group" then
715 return 'cannot have "type" = "group" as a subgroup of a passing group', position
716 end
717 end
718 if options ~= baseOptions then
719 if kind == "header" then
720 elseif type(options.desc) ~= "string" then
721 return '"desc" must be a string', position
722 elseif string.len(options.desc) == 0 then
723 return '"desc" cannot be a 0-length string', position
724 end
725 end
726 if options ~= baseOptions or kind == "range" or kind == "text" or kind == "toggle" or kind == "color" then
727 if options.type == "header" and not options.cmdName and not options.name then
728 elseif options.cmdName then
729 if type(options.cmdName) ~= "string" then
730 return '"cmdName" must be a string or nil', position
731 elseif string.len(options.cmdName) == 0 then
732 return '"cmdName" cannot be a 0-length string', position
733 end
734 if type(options.guiName) ~= "string" then
735 if not options.guiNameIsMap then
736 return '"guiName" must be a string or nil', position
737 end
738 elseif string.len(options.guiName) == 0 then
739 return '"guiName" cannot be a 0-length string', position
740 end
741 else
742 if type(options.name) ~= "string" then
743 return '"name" must be a string', position
744 elseif string.len(options.name) == 0 then
745 return '"name" cannot be a 0-length string', position
746 end
747 end
748 end
749 if options.guiNameIsMap then
750 if type(options.guiNameIsMap) ~= "boolean" then
751 return '"guiNameIsMap" must be a boolean or nil', position
752 elseif options.type ~= "toggle" then
753 return 'if "guiNameIsMap" is true, then "type" must be set to \'toggle\'', position
754 elseif type(options.map) ~= "table" then
755 return '"map" must be a table', position
756 end
757 end
758 if options.message and type(options.message) ~= "string" then
759 return '"message" must be a string or nil', position
760 end
761 if options.error and type(options.error) ~= "string" then
762 return '"error" must be a string or nil', position
763 end
764 if options.current and type(options.current) ~= "string" then
765 return '"current" must be a string or nil', position
766 end
767 if options.order then
768 if type(options.order) ~= "number" or (-1 < options.order and options.order < 0.999) then
769 return '"order" must be a non-zero number or nil', position
770 end
771 end
772 if options.disabled then
773 if type(options.disabled) ~= "function" and type(options.disabled) ~= "string" and options.disabled ~= true then
774 return '"disabled" must be a function, string, or boolean', position
775 end
776 end
777 if options.cmdHidden then
778 if type(options.cmdHidden) ~= "function" and type(options.cmdHidden) ~= "string" and options.cmdHidden ~= true then
779 return '"cmdHidden" must be a function, string, or boolean', position
780 end
781 end
782 if options.guiHidden then
783 if type(options.guiHidden) ~= "function" and type(options.guiHidden) ~= "string" and options.guiHidden ~= true then
784 return '"guiHidden" must be a function, string, or boolean', position
785 end
786 end
787 if options.hidden then
788 if type(options.hidden) ~= "function" and type(options.hidden) ~= "string" and options.hidden ~= true then
789 return '"hidden" must be a function, string, or boolean', position
790 end
791 end
792 if kind == "text" then
793 if type(options.validate) == "table" then
794 local t = options.validate
795 local iTable = nil
796 for k,v in pairs(t) do
797 if type(k) == "number" then
798 if iTable == nil then
799 iTable = true
800 elseif not iTable then
801 return '"validate" must either have all keys be indexed numbers or strings', position
802 elseif k < 1 or k > table.getn(t) then
803 return '"validate" numeric keys must be indexed properly. >= 1 and <= table.getn', position
804 end
805 else
806 if iTable == nil then
807 iTable = false
808 elseif iTable then
809 return '"validate" must either have all keys be indexed numbers or strings', position
810 end
811 end
812 if type(v) ~= "string" then
813 return '"validate" values must all be strings', position
814 end
815 end
816 else
817 if type(options.usage) ~= "string" then
818 return '"usage" must be a string', position
819 elseif options.validate and type(options.validate) ~= "string" and type(options.validate) ~= "function" then
820 return '"validate" must be a string, function, or table', position
821 end
822 end
823 elseif kind == "range" then
824 if options.min or options.max then
825 if type(options.min) ~= "number" then
826 return '"min" must be a number', position
827 elseif type(options.max) ~= "number" then
828 return '"max" must be a number', position
829 elseif options.min >= options.max then
830 return '"min" must be less than "max"', position
831 end
832 end
833 if options.step then
834 if type(options.step) ~= "number" then
835 return '"step" must be a number', position
836 elseif options.step < 0 then
837 return '"step" must be nonnegative', position
838 end
839 end
840 if options.isPercent and options.isPercent ~= true then
841 return '"isPercent" must either be nil, true, or false', position
842 end
843 elseif kind == "toggle" then
844 if options.map then
845 if type(options.map) ~= "table" then
846 return '"map" must be a table', position
847 elseif type(options.map[true]) ~= "string" then
848 return '"map[true]" must be a string', position
849 elseif type(options.map[false]) ~= "string" then
850 return '"map[false]" must be a string', position
851 end
852 end
853 elseif kind == "color" then
854 if options.hasAlpha and options.hasAlpha ~= true then
855 return '"hasAlpha" must be nil, true, or false', position
856 end
857 elseif kind == "group" then
858 if options.pass and options.pass ~= true then
859 return '"pass" must be nil, true, or false', position
860 end
861 if type(options.args) ~= "table" then
862 return '"args" must be a table', position
863 end
864 for k,v in pairs(options.args) do
865 if type(k) ~= "string" then
866 return '"args" keys must be strings', position
867 elseif string.find(k, "%s") then
868 return string.format('"args" keys must not include spaces. %q is not appropriate.', k), position
869 elseif string.len(k) == 0 then
870 return '"args" keys must not be 0-length strings.', position
871 end
872 if type(v) ~= "table" then
873 return '"args" values must be tables', position and position .. "." .. k or k
874 end
875 local newposition
876 if position then
877 newposition = position .. ".args." .. k
878 else
879 newposition = "args." .. k
880 end
881 local err, pos = validateOptions(v, newposition, baseOptions, options.pass)
882 if err then
883 return err, pos
884 end
885 end
886 end
887 end
888  
889 local validatedOptions
890  
891 local values
892 local mysort_args
893 local mysort
894 local othersort
895 local othersort_validate
896  
897 local baseFunc, currentLevel
898  
899 function Dewdrop:FeedAceOptionsTable(options, difference)
900 self:argCheck(options, 2, "table")
901 self:argCheck(difference, 3, "nil", "number")
902 self:assert(currentLevel, "Cannot call `FeedAceOptionsTable' outside of a Dewdrop declaration")
903 if not difference then
904 difference = 0
905 end
906 if not validatedOptions then
907 validatedOptions = {}
908 end
909 if not validatedOptions[options] then
910 local err, position = validateOptions(options)
911  
912 if err then
913 if position then
914 Dewdrop:error(position .. ": " .. err)
915 else
916 Dewdrop:error(err)
917 end
918 end
919  
920 validatedOptions[options] = true
921 end
922 local level = levels[currentLevel]
923 self:assert(level, "Improper level given")
924 if not values then
925 values = {}
926 else
927 for k,v in pairs(values) do
928 values[k] = nil
929 end
930 table_setn(values, 0)
931 end
932  
933 local current = level
934 while current do
935 if current.num == difference + 1 then
936 break
937 end
938 table.insert(values, current.value)
939 current = levels[current.num - 1]
940 end
941  
942 local realOptions = options
943 local handler = options.handler
944 local passTable
945 local passValue
946 while table.getn(values) > 0 do
947 passTable = options.pass and current or nil
948 local value = table.remove(values)
949 options = options.args and options.args[value]
950 if not options then
951 return
952 end
953 handler = options.handler or handler
954 passValue = passTable and value or nil
955 end
956  
957 if options.type == "group" then
958 for k in pairs(options.args) do
959 table.insert(values, k)
960 end
961 if not mysort then
962 mysort = function(a, b)
963 local alpha, bravo = mysort_args[a], mysort_args[b]
964 local alpha_order = alpha.order or 100
965 local bravo_order = bravo.order or 100
966 local alpha_name = alpha.guiName or alpha.name
967 local bravo_name = bravo.guiName or bravo.name
968 if alpha_order == bravo_order then
969 if not alpha_name then
970 return true
971 elseif not bravo_name then
972 return false
973 else
974 return alpha_name < bravo_name
975 end
976 else
977 if alpha_order < 0 then
978 if bravo_order > 0 then
979 return false
980 end
981 else
982 if bravo_order < 0 then
983 return true
984 end
985 end
986 return alpha_order < bravo_order
987 end
988 end
989 end
990 mysort_args = options.args
991 table.sort(values, mysort)
992 mysort_args = nil
993 local hasBoth = table.getn(values) >= 1 and (options.args[values[1]].order or 100) > 0 and (options.args[values[table.getn(values)]].order or 100) < 0
994 local last_order = 1
995 for _,k in ipairs(values) do
996 local v = options.args[k]
997 local handler = v.handler or handler
998 if hasBoth and last_order > 0 and (v.order or 100) < 0 then
999 hasBoth = false
1000 self:AddLine()
1001 end
1002 local hidden, disabled = v.guiHidden or v.hidden, v.disabled
1003 if type(hidden) == "function" then
1004 hidden = hidden()
1005 elseif type(hidden) == "string" then
1006 hidden = handler[hidden](handler)
1007 end
1008 if not hidden then
1009 if type(disabled) == "function" then
1010 disabled = disabled()
1011 elseif type(disabled) == "string" then
1012 disabled = handler[disabled](handler)
1013 end
1014 local name = v.guiName or v.name
1015 local desc = v.desc
1016 local tooltipTitle, tooltipText
1017 tooltipTitle = name
1018 if name ~= desc then
1019 tooltipText = desc
1020 end
1021 if v.type == "toggle" then
1022 local checked
1023 if type(v.get) == "function" then
1024 checked = v.get(passValue)
1025 else
1026 if not handler[v.get] then
1027 Dewdrop:error("Handler %q not available", v.get)
1028 end
1029 checked = handler[v.get](handler, passValue)
1030 end
1031 local func, arg1, arg2, arg3
1032 if type(v.set) == "function" then
1033 func = v.set
1034 if passValue ~= nil then
1035 arg1 = passValue
1036 arg2 = not checked
1037 else
1038 arg1 = not checked
1039 end
1040 else
1041 if not handler[v.set] then
1042 Dewdrop:error("Handler %q not available", v.set)
1043 end
1044 func = handler[v.set]
1045 arg1 = handler
1046 if passValue ~= nil then
1047 arg2 = passValue
1048 arg3 = not checked
1049 else
1050 arg2 = not checked
1051 end
1052 end
1053 if v.guiNameIsMap then
1054 checked = checked and true or false
1055 name = string.gsub(tostring(v.map and v.map[checked]), "|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
1056 checked = nil
1057 end
1058 self:AddLine(
1059 'text', name,
1060 'checked', checked,
1061 'func', func,
1062 'arg1', arg1,
1063 'arg2', arg2,
1064 'arg3', arg3,
1065 'disabled', disabled,
1066 'tooltipTitle', tooltipTitle,
1067 'tooltipText', tooltipText
1068 )
1069 elseif v.type == "execute" then
1070 local func, arg1, arg2
1071 if type(v.func) == "function" then
1072 func = v.func
1073 arg1 = passValue
1074 else
1075 if not handler[v.func] then
1076 Dewdrop:error("Handler %q not available", v.func)
1077 end
1078 func = handler[v.func]
1079 arg1 = handler
1080 arg2 = passValue
1081 end
1082 self:AddLine(
1083 'text', name,
1084 'checked', checked,
1085 'func', func,
1086 'arg1', arg1,
1087 'arg2', arg2,
1088 'disabled', disabled,
1089 'tooltipTitle', tooltipTitle,
1090 'tooltipText', tooltipText
1091 )
1092 elseif v.type == "range" then
1093 local sliderValue
1094 if type(v.get) == "function" then
1095 sliderValue = v.get(passValue)
1096 else
1097 if not handler[v.get] then
1098 Dewdrop:error("Handler %q not available", v.get)
1099 end
1100 sliderValue = handler[v.get](handler, passValue)
1101 end
1102 local sliderFunc, sliderArg1, sliderArg2
1103 if type(v.set) == "function" then
1104 sliderFunc = v.set
1105 sliderArg1 = passValue
1106 else
1107 if not handler[v.set] then
1108 Dewdrop:error("Handler %q not available", v.set)
1109 end
1110 sliderFunc = handler[v.set]
1111 sliderArg1 = handler
1112 sliderArg2 = passValue
1113 end
1114 self:AddLine(
1115 'text', name,
1116 'hasArrow', true,
1117 'hasSlider', true,
1118 'sliderMin', v.min or 0,
1119 'sliderMax', v.max or 1,
1120 'sliderStep', v.step or 0,
1121 'sliderIsPercent', v.isPercent or false,
1122 'sliderValue', sliderValue,
1123 'sliderFunc', sliderFunc,
1124 'sliderArg1', sliderArg1,
1125 'sliderArg2', sliderArg2,
1126 'disabled', disabled,
1127 'tooltipTitle', tooltipTitle,
1128 'tooltipText', tooltipText
1129 )
1130 elseif v.type == "color" then
1131 local r,g,b,a
1132 if type(v.get) == "function" then
1133 r,g,b,a = v.get(passValue)
1134 else
1135 if not handler[v.get] then
1136 Dewdrop:error("Handler %q not available", v.get)
1137 end
1138 r,g,b,a = handler[v.get](handler, passValue)
1139 end
1140 local colorFunc, colorArg1, colorArg2
1141 if type(v.set) == "function" then
1142 colorFunc = v.set
1143 colorArg1 = passValue
1144 else
1145 if not handler[v.set] then
1146 Dewdrop:error("Handler %q not available", v.set)
1147 end
1148 colorFunc = handler[v.set]
1149 colorArg1 = handler
1150 colorArg2 = passValue
1151 end
1152 self:AddLine(
1153 'text', name,
1154 'hasArrow', true,
1155 'hasColorSwatch', true,
1156 'r', r,
1157 'g', g,
1158 'b', b,
1159 'opacity', v.hasAlpha and a or nil,
1160 'hasOpacity', v.hasAlpha,
1161 'colorFunc', colorFunc,
1162 'colorArg1', colorArg1,
1163 'colorArg2', colorArg2,
1164 'disabled', disabled,
1165 'tooltipTitle', tooltipTitle,
1166 'tooltipText', tooltipText
1167 )
1168 elseif v.type == "text" then
1169 if type(v.validate) == "table" then
1170 self:AddLine(
1171 'text', name,
1172 'hasArrow', true,
1173 'value', k,
1174 'disabled', disabled,
1175 'tooltipTitle', tooltipTitle,
1176 'tooltipText', tooltipText
1177 )
1178 else
1179 local editBoxText
1180 if type(v.get) == "function" then
1181 editBoxText = v.get(passValue)
1182 elseif v.get == false then
1183 editBoxText = nil
1184 else
1185 if not handler[v.get] then
1186 Dewdrop:error("Handler %q not available", v.get)
1187 end
1188 editBoxText = handler[v.get](handler, passValue)
1189 end
1190 local editBoxFunc, editBoxArg1, editBoxArg2
1191 if type(v.set) == "function" then
1192 editBoxFunc = v.set
1193 editBoxArg1 = passValue
1194 else
1195 if not handler[v.set] then
1196 Dewdrop:error("Handler %q not available", v.set)
1197 end
1198 editBoxFunc = handler[v.set]
1199 editBoxArg1 = handler
1200 editBoxArg2 = passValue
1201 end
1202  
1203 local editBoxValidateFunc, editBoxValidateArg1
1204  
1205 if v.validate then
1206 if type(v.validate) == "function" then
1207 editBoxValidateFunc = v.validate
1208 else
1209 if not handler[v.validate] then
1210 Dewdrop:error("Handler %q not available", v.validate)
1211 end
1212 editBoxValidateFunc = handler[v.validate]
1213 editBoxValidateArg1 = handler
1214 end
1215 end
1216  
1217 self:AddLine(
1218 'text', name,
1219 'hasArrow', true,
1220 'hasEditBox', true,
1221 'editBoxText', editBoxText,
1222 'editBoxFunc', editBoxFunc,
1223 'editBoxArg1', editBoxArg1,
1224 'editBoxArg2', editBoxArg2,
1225 'editBoxValidateFunc', editBoxValidateFunc,
1226 'editBoxValidateArg1', editBoxValidateArg1,
1227 'disabled', disabled,
1228 'tooltipTitle', tooltipTitle,
1229 'tooltipText', tooltipText
1230 )
1231 end
1232 elseif v.type == "group" then
1233 self:AddLine(
1234 'text', name,
1235 'hasArrow', true,
1236 'value', k,
1237 'disabled', disabled,
1238 'tooltipTitle', tooltipTitle,
1239 'tooltipText', tooltipText
1240 )
1241 elseif v.type == "header" then
1242 if name == "" or not name then
1243 self:AddLine()
1244 else
1245 self:AddLine(
1246 'text', name,
1247 'isTitle', true
1248 )
1249 end
1250 end
1251 end
1252 last_order = v.order or 100
1253 end
1254 elseif options.type == "text" and type(options.validate) == "table" then
1255 local current
1256 if type(options.get) == "function" then
1257 current = options.get(passValue)
1258 else
1259 if not handler[options.get] then
1260 Dewdrop:error("Handler %q not available", options.get)
1261 end
1262 current = handler[options.get](handler, passValue)
1263 end
1264 local indexed = true
1265 for k,v in pairs(options.validate) do
1266 if type(k) ~= "number" then
1267 indexed = false
1268 end
1269 table.insert(values, k)
1270 end
1271 if not indexed then
1272 if not othersort then
1273 othersort = function(alpha, bravo)
1274 return othersort_validate[alpha] < othersort_validate[bravo]
1275 end
1276 end
1277 othersort_validate = options.validate
1278 table.sort(values, othersort)
1279 othersort_validate = nil
1280 end
1281 for _,k in ipairs(values) do
1282 local v = options.validate[k]
1283 if type(k) == "number" then
1284 k = v
1285 end
1286 local func, arg1, arg2
1287 if type(options.set) == "function" then
1288 func = options.set
1289 if passValue ~= nil then
1290 arg1 = passValue
1291 arg2 = k
1292 else
1293 arg1 = k
1294 end
1295 else
1296 if not handler[options.set] then
1297 Dewdrop:error("Handler %q not available", options.set)
1298 end
1299 func = handler[options.set]
1300 arg1 = handler
1301 if passValue ~= nil then
1302 arg2 = passValue
1303 arg3 = k
1304 else
1305 arg2 = k
1306 end
1307 end
1308 local checked = (k == current or (type(k) == "string" and type(current) == "string" and string.lower(k) == string.lower(current)))
1309 self:AddLine(
1310 'text', v,
1311 'func', not checked and func or nil,
1312 'arg1', not checked and arg1 or nil,
1313 'arg2', not checked and arg2 or nil,
1314 'arg3', not checked and arg3 or nil,
1315 'isRadio', true,
1316 'checked', checked,
1317 'tooltipTitle', options.guiName or options.name,
1318 'tooltipText', v
1319 )
1320 end
1321 for k in pairs(values) do
1322 values[k] = nil
1323 end
1324 table_setn(values, 0)
1325 else
1326 return false
1327 end
1328 return true
1329 end
1330  
1331 function Refresh(self, level)
1332 if type(level) == "number" then
1333 level = levels[level]
1334 end
1335 if not level then
1336 return
1337 end
1338 if baseFunc then
1339 Clear(self, level)
1340 currentLevel = level.num
1341 if type(baseFunc) == "table" then
1342 if currentLevel == 1 then
1343 local handler = baseFunc.handler
1344 if handler then
1345 local name = tostring(handler)
1346 if not string.find(name, '^table:') then
1347 name = string.gsub(name, "|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
1348 self:AddLine(
1349 'text', name,
1350 'isTitle', true
1351 )
1352 end
1353 end
1354 end
1355 self:FeedAceOptionsTable(baseFunc)
1356 if currentLevel == 1 then
1357 self:AddLine(
1358 'text', CLOSE,
1359 'closeWhenClicked', true
1360 )
1361 end
1362 else
1363 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)
1364 end
1365 currentLevel = nil
1366 CheckSize(self, level)
1367 end
1368 end
1369  
1370 function Dewdrop:Refresh(level)
1371 self:argCheck(level, 2, "number")
1372 Refresh(self, levels[level])
1373 end
1374  
1375 function OpenSlider(self, parent)
1376 if not sliderFrame then
1377 sliderFrame = CreateFrame("Frame", nil, UIParent)
1378 sliderFrame:SetWidth(80)
1379 sliderFrame:SetHeight(170)
1380 sliderFrame:SetBackdrop(tmp(
1381 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
1382 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
1383 'tile', true,
1384 'insets', tmp2(
1385 'left', 5,
1386 'right', 5,
1387 'top', 5,
1388 'bottom', 5
1389 ),
1390 'tileSize', 16,
1391 'edgeSize', 16
1392 ))
1393 sliderFrame:SetFrameStrata("FULLSCREEN_DIALOG")
1394 if sliderFrame.SetTopLevel then
1395 sliderFrame:SetTopLevel(true)
1396 end
1397 sliderFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
1398 sliderFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
1399 sliderFrame:EnableMouse(true)
1400 sliderFrame:Hide()
1401 sliderFrame:SetPoint("CENTER", UIParent, "CENTER")
1402 local slider = CreateFrame("Slider", nil, sliderFrame)
1403 sliderFrame.slider = slider
1404 slider:SetOrientation("VERTICAL")
1405 slider:SetMinMaxValues(0, 1)
1406 slider:SetValueStep(0.01)
1407 slider:SetValue(0.5)
1408 slider:SetWidth(16)
1409 slider:SetHeight(128)
1410 slider:SetPoint("LEFT", sliderFrame, "LEFT", 15, 0)
1411 slider:SetBackdrop(tmp(
1412 'bgFile', "Interface\\Buttons\\UI-SliderBar-Background",
1413 'edgeFile', "Interface\\Buttons\\UI-SliderBar-Border",
1414 'tile', true,
1415 'edgeSize', 8,
1416 'tileSize', 8,
1417 'insets', tmp2(
1418 'left', 3,
1419 'right', 3,
1420 'top', 3,
1421 'bottom', 3
1422 )
1423 ))
1424 local texture = slider:CreateTexture()
1425 slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
1426 local text = slider:CreateFontString(nil, "ARTWORK")
1427 sliderFrame.topText = text
1428 text:SetFontObject(GameFontGreenSmall)
1429 text:SetText("100%")
1430 text:SetPoint("BOTTOM", slider, "TOP")
1431 local text = slider:CreateFontString(nil, "ARTWORK")
1432 sliderFrame.bottomText = text
1433 text:SetFontObject(GameFontGreenSmall)
1434 text:SetText("0%")
1435 text:SetPoint("TOP", slider, "BOTTOM")
1436 local text = slider:CreateFontString(nil, "ARTWORK")
1437 sliderFrame.currentText = text
1438 text:SetFontObject(GameFontHighlightSmall)
1439 text:SetText("50%")
1440 text:SetPoint("LEFT", slider, "RIGHT")
1441 text:SetPoint("RIGHT", sliderFrame, "RIGHT", -6, 0)
1442 text:SetJustifyH("CENTER")
1443 local changed = false
1444 local inside = false
1445 slider:SetScript("OnValueChanged", function()
1446 if sliderFrame.changing then
1447 return
1448 end
1449 changed = true
1450 local done = false
1451 if sliderFrame.parent then
1452 if sliderFrame.parent.sliderFunc then
1453 local min = sliderFrame.parent.sliderMin or 0
1454 local max = sliderFrame.parent.sliderMax or 1
1455 local step = sliderFrame.parent.sliderStep or (max - min) / 100
1456 local a1,a2,a3,a4 = sliderFrame.parent.sliderArg1, sliderFrame.parent.sliderArg2, sliderFrame.parent.sliderArg3, sliderFrame.parent.sliderArg4
1457 local value = (1 - slider:GetValue()) * (max - min) + min
1458 if step > 0 then
1459 value = math.floor((value - min) / step + 0.5) * step + min
1460 if value > max then
1461 value = max
1462 elseif value < min then
1463 value = min
1464 end
1465 end
1466 local text
1467 if a1 == nil then
1468 text = sliderFrame.parent.sliderFunc(value)
1469 elseif a2 == nil then
1470 text = sliderFrame.parent.sliderFunc(a1, value)
1471 elseif a3 == nil then
1472 text = sliderFrame.parent.sliderFunc(a1, a2, value)
1473 elseif a4 == nil then
1474 text = sliderFrame.parent.sliderFunc(a1, a2, a3, value)
1475 else
1476 text = sliderFrame.parent.sliderFunc(a1, a2, a3, a4, value)
1477 end
1478 if text then
1479 sliderFrame.currentText:SetText(text)
1480 done = true
1481 end
1482 end
1483 end
1484 if not done then
1485 local min = sliderFrame.parent.sliderMin or 0
1486 local max = sliderFrame.parent.sliderMax or 1
1487 local step = sliderFrame.parent.sliderStep or (max - min) / 100
1488 local value = (1 - slider:GetValue()) * (max - min) + min
1489 if step > 0 then
1490 value = math.floor((value - min) / step + 0.5) * step + min
1491 if value > max then
1492 value = max
1493 elseif value < min then
1494 value = min
1495 end
1496 end
1497 if sliderFrame.parent.sliderIsPercent then
1498 sliderFrame.currentText:SetText(string.format("%.0f%%", value * 100))
1499 else
1500 if step < 0.1 then
1501 sliderFrame.currentText:SetText(string.format("%.2f", value))
1502 elseif step < 1 then
1503 sliderFrame.currentText:SetText(string.format("%.1f", value))
1504 else
1505 sliderFrame.currentText:SetText(string.format("%.0f", value))
1506 end
1507 end
1508 end
1509 end)
1510 sliderFrame:SetScript("OnEnter", function()
1511 StopCounting(self, sliderFrame.level)
1512 end)
1513 sliderFrame:SetScript("OnLeave", function()
1514 StartCounting(self, sliderFrame.level)
1515 end)
1516 slider:SetScript("OnMouseDown", function()
1517 sliderFrame.mouseDown = true
1518 end)
1519 slider:SetScript("OnMouseUp", function()
1520 sliderFrame.mouseDown = false
1521 if changed and not inside then
1522 local parent = sliderFrame.parent
1523 for i = 1, sliderFrame.level - 1 do
1524 Refresh(self, levels[i])
1525 end
1526 OpenSlider(self, parent)
1527 end
1528 end)
1529 slider:SetScript("OnEnter", function()
1530 inside = true
1531 StopCounting(self, sliderFrame.level)
1532 end)
1533 slider:SetScript("OnLeave", function()
1534 inside = false
1535 StartCounting(self, sliderFrame.level)
1536 if changed and not sliderFrame.mouseDown then
1537 local parent = sliderFrame.parent
1538 for i = 1, sliderFrame.level - 1 do
1539 Refresh(self, levels[i])
1540 end
1541 OpenSlider(self, parent)
1542 end
1543 end)
1544 end
1545 sliderFrame.parent = parent
1546 sliderFrame.level = parent.level.num + 1
1547 sliderFrame.parentValue = parent.level.value
1548 sliderFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
1549 sliderFrame.slider:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
1550 sliderFrame.changing = true
1551 if not parent.sliderMin or not parent.sliderMax then
1552 return
1553 end
1554 if not parent.sliderValue then
1555 parent.sliderValue = (parent.sliderMin + parent.sliderMax) / 2
1556 end
1557 sliderFrame.slider:SetValue(1 - (parent.sliderValue - parent.sliderMin) / (parent.sliderMax - parent.sliderMin))
1558 sliderFrame.changing = false
1559 sliderFrame.bottomText:SetText(parent.sliderMinText or "0")
1560 sliderFrame.topText:SetText(parent.sliderMaxText or "1")
1561 local text
1562 if parent.sliderFunc then
1563 local a1,a2,a3,a4 = parent.sliderArg1, parent.sliderArg2, parent.sliderArg3, parent.sliderArg4
1564 if a1 == nil then
1565 text = parent.sliderFunc(parent.sliderValue)
1566 elseif a2 == nil then
1567 text = parent.sliderFunc(a1, parent.sliderValue)
1568 elseif a3 == nil then
1569 text = parent.sliderFunc(a1, a2, parent.sliderValue)
1570 elseif a4 == nil then
1571 text = parent.sliderFunc(a1, a2, a3, parent.sliderValue)
1572 else
1573 text = parent.sliderFunc(a1, a2, a3, a4, parent.sliderValue)
1574 end
1575 end
1576 if text then
1577 sliderFrame.currentText:SetText(text)
1578 elseif parent.sliderIsPercent then
1579 sliderFrame.currentText:SetText(string.format("%.0f%%", parent.sliderValue * 100))
1580 else
1581 sliderFrame.currentText:SetText(parent.sliderValue)
1582 end
1583  
1584 local level = parent.level
1585 sliderFrame:Show()
1586 sliderFrame:ClearAllPoints()
1587 if level.lastDirection == "RIGHT" then
1588 if level.lastVDirection == "DOWN" then
1589 sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
1590 else
1591 sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
1592 end
1593 else
1594 if level.lastVDirection == "DOWN" then
1595 sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
1596 else
1597 sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
1598 end
1599 end
1600 local dirty
1601 if level.lastDirection == "RIGHT" then
1602 if sliderFrame:GetRight() > GetScreenWidth() then
1603 level.lastDirection = "LEFT"
1604 dirty = true
1605 end
1606 elseif sliderFrame:GetLeft() < 0 then
1607 level.lastDirection = "RIGHT"
1608 dirty = true
1609 end
1610 if level.lastVDirection == "DOWN" then
1611 if sliderFrame:GetBottom() < 0 then
1612 level.lastVDirection = "UP"
1613 dirty = true
1614 end
1615 elseif sliderFrame:GetTop() > GetScreenWidth() then
1616 level.lastVDirection = "DOWN"
1617 dirty = true
1618 end
1619 if dirty then
1620 sliderFrame:ClearAllPoints()
1621 if level.lastDirection == "RIGHT" then
1622 if level.lastVDirection == "DOWN" then
1623 sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
1624 else
1625 sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
1626 end
1627 else
1628 if level.lastVDirection == "DOWN" then
1629 sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
1630 else
1631 sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
1632 end
1633 end
1634 end
1635 local left, bottom = sliderFrame:GetLeft(), sliderFrame:GetBottom()
1636 sliderFrame:ClearAllPoints()
1637 sliderFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
1638 if mod(level.num, 5) == 0 then
1639 local left, bottom = level:GetLeft(), level:GetBottom()
1640 level:ClearAllPoints()
1641 level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
1642 end
1643 end
1644  
1645 function OpenEditBox(self, parent)
1646 if not editBoxFrame then
1647 editBoxFrame = CreateFrame("Frame", nil, UIParent)
1648 editBoxFrame:SetWidth(200)
1649 editBoxFrame:SetHeight(40)
1650 editBoxFrame:SetBackdrop(tmp(
1651 'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
1652 'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
1653 'tile', true,
1654 'insets', tmp2(
1655 'left', 5,
1656 'right', 5,
1657 'top', 5,
1658 'bottom', 5
1659 ),
1660 'tileSize', 16,
1661 'edgeSize', 16
1662 ))
1663 editBoxFrame:SetFrameStrata("FULLSCREEN_DIALOG")
1664 if editBoxFrame.SetTopLevel then
1665 editBoxFrame:SetTopLevel(true)
1666 end
1667 editBoxFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
1668 editBoxFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
1669 editBoxFrame:EnableMouse(true)
1670 editBoxFrame:Hide()
1671 editBoxFrame:SetPoint("CENTER", UIParent, "CENTER")
1672  
1673 local editBox = CreateFrame("EditBox", nil, editBoxFrame)
1674 editBoxFrame.editBox = editBox
1675 editBox:SetFontObject(ChatFontNormal)
1676 editBox:SetWidth(160)
1677 editBox:SetHeight(13)
1678 editBox:SetPoint("CENTER", editBoxFrame, "CENTER", 0, 0)
1679  
1680 local left = editBox:CreateTexture(nil, "BACKGROUND")
1681 left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
1682 left:SetTexCoord(0, 100 / 256, 0, 1)
1683 left:SetWidth(100)
1684 left:SetHeight(32)
1685 left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
1686 local right = editBox:CreateTexture(nil, "BACKGROUND")
1687 right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
1688 right:SetTexCoord(156/256, 1, 0, 1)
1689 right:SetWidth(100)
1690 right:SetHeight(32)
1691 right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
1692  
1693 editBox:SetScript("OnEnterPressed", function()
1694 if editBoxFrame.parent and editBoxFrame.parent.editBoxValidateFunc then
1695 local a1,a2,a3,a4 = editBoxFrame.parent.editBoxValidateArg1, editBoxFrame.parent.editBoxValidateArg2, editBoxFrame.parent.editBoxValidateArg3, editBoxFrame.parent.editBoxValidateArg4
1696  
1697 local result
1698 if a1 == nil then
1699 result = editBoxFrame.parent.editBoxValidateFunc(editBox:GetText() or "")
1700 elseif a2 == nil then
1701 result = editBoxFrame.parent.editBoxValidateFunc(a1, editBox:GetText() or "")
1702 elseif a3 == nil then
1703 result = editBoxFrame.parent.editBoxValidateFunc(a1, a2, editBox:GetText() or "")
1704 elseif a4 == nil then
1705 result = editBoxFrame.parent.editBoxValidateFunc(a1, a2, a3, editBox:GetText() or "")
1706 else
1707 result = editBoxFrame.parent.editBoxValidateFunc(a1, a2, a3, a4, editBox:GetText() or "")
1708 end
1709 if not result then
1710 message("Validation error: [" .. tostring(text) .. "]")
1711 return
1712 end
1713 end
1714 if editBoxFrame.parent and editBoxFrame.parent.editBoxFunc then
1715 local a1,a2,a3,a4 = editBoxFrame.parent.editBoxArg1, editBoxFrame.parent.editBoxArg2, editBoxFrame.parent.editBoxArg3, editBoxFrame.parent.editBoxArg4
1716 if a1 == nil then
1717 editBoxFrame.parent.editBoxFunc(editBox:GetText() or "")
1718 elseif a2 == nil then
1719 editBoxFrame.parent.editBoxFunc(a1, editBox:GetText() or "")
1720 elseif a3 == nil then
1721 editBoxFrame.parent.editBoxFunc(a1, a2, editBox:GetText() or "")
1722 elseif a4 == nil then
1723 editBoxFrame.parent.editBoxFunc(a1, a2, a3, editBox:GetText() or "")
1724 else
1725 editBoxFrame.parent.editBoxFunc(a1, a2, a3, a4, editBox:GetText() or "")
1726 end
1727 end
1728 self:Close(editBoxFrame.level)
1729 for i = 1, editBoxFrame.level - 1 do
1730 Refresh(self, levels[i])
1731 end
1732 end)
1733 editBox:SetScript("OnEscapePressed", function()
1734 self:Close(editBoxFrame.level)
1735 end)
1736 local changing = false
1737 local skipNext = false
1738  
1739 function editBox:SpecialSetText(text)
1740 local oldText = editBox:GetText() or ""
1741 if not text then
1742 text = ""
1743 end
1744 if text ~= oldText then
1745 changing = true
1746 self:SetText(text)
1747 changing = false
1748 skipNext = true
1749 end
1750 end
1751  
1752 editBox:SetScript("OnTextChanged", function()
1753 if skipNext then
1754 skipNext = false
1755 elseif not changing and editBoxFrame.parent and editBoxFrame.parent.editBoxChangeFunc then
1756 local a1,a2,a3,a4 = editBoxFrame.parent.editBoxChangeArg1, editBoxFrame.parent.editBoxChangeArg2, editBoxFrame.parent.editBoxChangeArg3, editBoxFrame.parent.editBoxChangeArg4
1757 local text
1758 if a1 == nil then
1759 text = editBoxFrame.parent.editBoxChangeFunc(editBox:GetText() or "")
1760 elseif a2 == nil then
1761 text = editBoxFrame.parent.editBoxChangeFunc(a1, editBox:GetText() or "")
1762 elseif a3 == nil then
1763 text = editBoxFrame.parent.editBoxChangeFunc(a1, a2, editBox:GetText() or "")
1764 elseif a4 == nil then
1765 text = editBoxFrame.parent.editBoxChangeFunc(a1, a2, a3, editBox:GetText() or "")
1766 else
1767 text = editBoxFrame.parent.editBoxChangeFunc(a1, a2, a3, a4, editBox:GetText() or "")
1768 end
1769 if text then
1770 editBox:SpecialSetText(text)
1771 end
1772 end
1773 end)
1774 editBoxFrame:SetScript("OnEnter", function()
1775 StopCounting(self, editBoxFrame.level)
1776 end)
1777 editBoxFrame:SetScript("OnLeave", function()
1778 StartCounting(self, editBoxFrame.level)
1779 end)
1780 editBox:SetScript("OnEnter", function()
1781 StopCounting(self, editBoxFrame.level)
1782 end)
1783 editBox:SetScript("OnLeave", function()
1784 StartCounting(self, editBoxFrame.level)
1785 end)
1786 end
1787 editBoxFrame.parent = parent
1788 editBoxFrame.level = parent.level.num + 1
1789 editBoxFrame.parentValue = parent.level.value
1790 editBoxFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
1791 editBoxFrame.editBox:SetFrameLevel(editBoxFrame:GetFrameLevel() + 1)
1792 editBoxFrame.editBox:SpecialSetText(parent.editBoxText)
1793  
1794 local level = parent.level
1795 editBoxFrame:Show()
1796 editBoxFrame:ClearAllPoints()
1797 if level.lastDirection == "RIGHT" then
1798 if level.lastVDirection == "DOWN" then
1799 editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
1800 else
1801 editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
1802 end
1803 else
1804 if level.lastVDirection == "DOWN" then
1805 editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
1806 else
1807 editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
1808 end
1809 end
1810 local dirty
1811 if level.lastDirection == "RIGHT" then
1812 if editBoxFrame:GetRight() > GetScreenWidth() then
1813 level.lastDirection = "LEFT"
1814 dirty = true
1815 end
1816 elseif editBoxFrame:GetLeft() < 0 then
1817 level.lastDirection = "RIGHT"
1818 dirty = true
1819 end
1820 if level.lastVDirection == "DOWN" then
1821 if editBoxFrame:GetBottom() < 0 then
1822 level.lastVDirection = "UP"
1823 dirty = true
1824 end
1825 elseif editBoxFrame:GetTop() > GetScreenWidth() then
1826 level.lastVDirection = "DOWN"
1827 dirty = true
1828 end
1829 if dirty then
1830 editBoxFrame:ClearAllPoints()
1831 if level.lastDirection == "RIGHT" then
1832 if level.lastVDirection == "DOWN" then
1833 editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
1834 else
1835 editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
1836 end
1837 else
1838 if level.lastVDirection == "DOWN" then
1839 editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
1840 else
1841 editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
1842 end
1843 end
1844 end
1845 local left, bottom = editBoxFrame:GetLeft(), editBoxFrame:GetBottom()
1846 editBoxFrame:ClearAllPoints()
1847 editBoxFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
1848 if mod(level.num, 5) == 0 then
1849 local left, bottom = level:GetLeft(), level:GetBottom()
1850 level:ClearAllPoints()
1851 level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
1852 end
1853 end
1854  
1855 function Dewdrop:IsOpen(parent)
1856 self:argCheck(parent, 2, "table", "nil")
1857 return levels[1] and levels[1]:IsShown() and (not parent or parent == levels[1].parent or parent == levels[1]:GetParent())
1858 end
1859  
1860 function Dewdrop:GetOpenedParent()
1861 return (levels[1] and levels[1]:IsShown()) and (levels[1].parent or levels[1]:GetParent())
1862 end
1863  
1864 function Open(self, parent, func, level, value, point, relativePoint, cursorX, cursorY)
1865 self:Close(level)
1866 if DewdropLib then
1867 local d = DewdropLib:GetInstance('1.0')
1868 local ret, val = pcall(d, IsOpen, d)
1869 if ret and val then
1870 DewdropLib:GetInstance('1.0'):Close()
1871 end
1872 end
1873 parent:GetCenter()
1874 local frame = AcquireLevel(self, level)
1875 if level == 1 then
1876 frame.lastDirection = "RIGHT"
1877 frame.lastVDirection = "DOWN"
1878 else
1879 frame.lastDirection = levels[level - 1].lastDirection
1880 frame.lastVDirection = levels[level - 1].lastVDirection
1881 end
1882 frame:SetFrameStrata("FULLSCREEN_DIALOG")
1883 frame:ClearAllPoints()
1884 frame.parent = parent
1885 frame:SetPoint("LEFT", UIParent, "RIGHT", 10000, 0)
1886 frame:Show()
1887 if level == 1 then
1888 baseFunc = func
1889 end
1890 levels[level].value = value
1891 relativePoint = relativePoint or point
1892 Refresh(self, levels[level])
1893 if point or (cursorX and cursorY) then
1894 frame:ClearAllPoints()
1895 if cursorX and cursorY then
1896 local curX, curY = GetScaledCursorPosition()
1897 if curY < GetScreenHeight() / 2 then
1898 point, relativePoint = "BOTTOM", "BOTTOM"
1899 else
1900 point, relativePoint = "TOP", "TOP"
1901 end
1902 if curX < GetScreenWidth() / 2 then
1903 point, relativePoint = point .. "LEFT", relativePoint .. "RIGHT"
1904 else
1905 point, relativePoint = point .. "RIGHT", relativePoint .. "LEFT"
1906 end
1907 end
1908 frame:SetPoint(point, parent, relativePoint)
1909 if cursorX and cursorY then
1910 local left = frame:GetLeft()
1911 local width = frame:GetWidth()
1912 local bottom = frame:GetBottom()
1913 local height = frame:GetHeight()
1914 local curX, curY = GetScaledCursorPosition()
1915 frame:ClearAllPoints()
1916 relativePoint = relativePoint or point
1917 if point == "BOTTOM" or point == "TOP" then
1918 if curX < GetScreenWidth() / 2 then
1919 point = point .. "LEFT"
1920 else
1921 point = point .. "RIGHT"
1922 end
1923 elseif point == "CENTER" then
1924 if curX < GetScreenWidth() / 2 then
1925 point = "LEFT"
1926 else
1927 point = "RIGHT"
1928 end
1929 end
1930 local xOffset, yOffset = 0, 0
1931 if curY > GetScreenHeight() / 2 then
1932 yOffset = -height
1933 end
1934 if curX > GetScreenWidth() / 2 then
1935 xOffset = -width
1936 end
1937 frame:SetPoint(point, parent, relativePoint, curX - left + xOffset, curY - bottom + yOffset)
1938 if level == 1 then
1939 frame.lastDirection = "RIGHT"
1940 end
1941 elseif cursorX then
1942 local left = frame:GetLeft()
1943 local width = frame:GetWidth()
1944 local curX, curY = GetScaledCursorPosition()
1945 frame:ClearAllPoints()
1946 relativePoint = relativePoint or point
1947 if point == "BOTTOM" or point == "TOP" then
1948 if curX < GetScreenWidth() / 2 then
1949 point = point .. "LEFT"
1950 else
1951 point = point .. "RIGHT"
1952 end
1953 elseif point == "CENTER" then
1954 if curX < GetScreenWidth() / 2 then
1955 point = "LEFT"
1956 else
1957 point = "RIGHT"
1958 end
1959 end
1960 frame:SetPoint(point, parent, relativePoint, curX - left - width / 2, 0)
1961 if level == 1 then
1962 frame.lastDirection = "RIGHT"
1963 end
1964 elseif cursorY then
1965 local bottom = frame:GetBottom()
1966 local height = frame:GetHeight()
1967 local curX, curY = GetScaledCursorPosition()
1968 frame:ClearAllPoints()
1969 relativePoint = relativePoint or point
1970 if point == "LEFT" or point == "RIGHT" then
1971 if curX < GetScreenHeight() / 2 then
1972 point = point .. "BOTTOM"
1973 else
1974 point = point .. "TOP"
1975 end
1976 elseif point == "CENTER" then
1977 if curX < GetScreenHeight() / 2 then
1978 point = "BOTTOM"
1979 else
1980 point = "TOP"
1981 end
1982 end
1983 frame:SetPoint(point, parent, relativePoint, 0, curY - bottom - height / 2)
1984 if level == 1 then
1985 frame.lastDirection = "DOWN"
1986 end
1987 end
1988 if (strsub(point, 1, 3) ~= strsub(relativePoint, 1, 3)) then
1989 if frame:GetBottom() < 0 then
1990 local point, parent, relativePoint, x, y = frame:GetPoint(1)
1991 local change = GetScreenHeight() - frame:GetTop()
1992 local otherChange = -frame:GetBottom()
1993 if otherChange < change then
1994 change = otherChange
1995 end
1996 frame:SetPoint(point, parent, relativePoint, x, y + change)
1997 elseif frame:GetTop() > GetScreenHeight() then
1998 local point, parent, relativePoint, x, y = frame:GetPoint(1)
1999 local change = GetScreenHeight() - frame:GetTop()
2000 local otherChange = -frame:GetBottom()
2001 if otherChange < change then
2002 change = otherChange
2003 end
2004 frame:SetPoint(point, parent, relativePoint, x, y + change)
2005 end
2006 end
2007 end
2008 CheckDualMonitor(self, frame)
2009 StartCounting(self, level)
2010 end
2011  
2012 function Dewdrop:IsRegistered(parent)
2013 self:argCheck(parent, 2, "table")
2014 return not not self.registry[parent]
2015 end
2016  
2017 function Dewdrop:Register(parent, k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2018 self:argCheck(parent, 2, "table")
2019 if self.registry[parent] then
2020 self:Unregister(parent)
2021 end
2022 local info = new(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2023 if type(info.children) == "table" then
2024 local err, position = validateOptions(info.children)
2025  
2026 if err then
2027 if position then
2028 Dewdrop:error(position .. ": " .. err)
2029 else
2030 Dewdrop:error(err)
2031 end
2032 end
2033 end
2034 self.registry[parent] = info
2035 if not info.dontHook and not self.onceRegistered[parent] then
2036 if parent:HasScript("OnMouseUp") then
2037 local script = parent:GetScript("OnMouseUp")
2038 parent:SetScript("OnMouseUp", function()
2039 if script then
2040 script()
2041 end
2042 if arg1 == "RightButton" and self.registry[parent] then
2043 if self:IsOpen(parent) then
2044 self:Close()
2045 else
2046 self:Open(parent)
2047 end
2048 end
2049 end)
2050 end
2051 if parent:HasScript("OnMouseDown") then
2052 local script = parent:GetScript("OnMouseDown")
2053 parent:SetScript("OnMouseDown", function()
2054 if script then
2055 script()
2056 end
2057 if self.registry[parent] then
2058 self:Close()
2059 end
2060 end)
2061 end
2062 end
2063 self.onceRegistered[parent] = true
2064 end
2065  
2066 function Dewdrop:Unregister(parent)
2067 self:argCheck(parent, 2, "table")
2068 self.registry[parent] = nil
2069 end
2070  
2071 function Dewdrop:Open(parent, k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2072 self:argCheck(parent, 2, "table")
2073 local info
2074 if type(k1) == "table" and k1[0] and k1.IsFrameType and self.registry[k1] then
2075 info = tmp()
2076 for k,v in pairs(self.registry[k1]) do
2077 info[k] = v
2078 end
2079 else
2080 info = tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2081 if self.registry[parent] then
2082 for k,v in pairs(self.registry[parent]) do
2083 if info[k] == nil then
2084 info[k] = v
2085 end
2086 end
2087 end
2088 end
2089 local point = info.point
2090 local relativePoint = info.relativePoint
2091 local cursorX = info.cursorX
2092 local cursorY = info.cursorY
2093 if type(point) == "function" then
2094 local b
2095 point, b = point(parent)
2096 if b then
2097 relativePoint = b
2098 end
2099 end
2100 if type(relativePoint) == "function" then
2101 relativePoint = relativePoint(parent)
2102 end
2103 Open(self, parent, info.children, 1, nil, point, relativePoint, cursorX, cursorY)
2104 end
2105  
2106 function Clear(self, level)
2107 if level then
2108 if level.buttons then
2109 for i = table.getn(level.buttons), 1, -1 do
2110 ReleaseButton(self, level, i)
2111 end
2112 end
2113 end
2114 end
2115  
2116 function Dewdrop:Close(level)
2117 if DropDownList1:IsShown() then
2118 DropDownList1:Hide()
2119 end
2120 if DewdropLib then
2121 local d = DewdropLib:GetInstance('1.0')
2122 local ret, val = pcall(d, IsOpen, d)
2123 if ret and val then
2124 DewdropLib:GetInstance('1.0'):Close()
2125 end
2126 end
2127 self:argCheck(level, 2, "number", "nil")
2128 if not level then
2129 level = 1
2130 end
2131 if level == 1 and levels[level] then
2132 levels[level].parented = false
2133 end
2134 if sliderFrame and sliderFrame.level >= level then
2135 sliderFrame:Hide()
2136 end
2137 if editBoxFrame and editBoxFrame.level >= level then
2138 editBoxFrame:Hide()
2139 end
2140 for i = level, table.getn(levels) do
2141 Clear(self, levels[level])
2142 levels[level]:Hide()
2143 levels[i]:ClearAllPoints()
2144 levels[i]:SetPoint("CENTER", UIParent, "CENTER")
2145 end
2146 end
2147  
2148 function Dewdrop:AddLine(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2149 local info = tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20)
2150 local level = info.level or currentLevel
2151 info.level = nil
2152 local button = AcquireButton(self, level)
2153 if not next(info) then
2154 info.disabled = true
2155 end
2156 button.disabled = info.isTitle or info.notClickable or info.disabled
2157 button.isTitle = info.isTitle
2158 button.notClickable = info.notClickable
2159 if button.isTitle then
2160 button.text:SetFontObject(GameFontNormalSmall)
2161 elseif button.notClickable then
2162 button.text:SetFontObject(GameFontHighlightSmall)
2163 elseif button.disabled then
2164 button.text:SetFontObject(GameFontDisableSmall)
2165 else
2166 button.text:SetFontObject(GameFontHighlightSmall)
2167 end
2168 if info.disabled then
2169 button.arrow:SetDesaturated(true)
2170 button.check:SetDesaturated(true)
2171 else
2172 button.arrow:SetDesaturated(false)
2173 button.check:SetDesaturated(false)
2174 end
2175 if info.textR and info.textG and info.textB then
2176 button.textR = info.textR
2177 button.textG = info.textG
2178 button.textB = info.textB
2179 button.text:SetTextColor(button.textR, button.textG, button.textB)
2180 else
2181 button.text:SetTextColor(button.text:GetFontObject():GetTextColor())
2182 end
2183 button.notCheckable = info.notCheckable
2184 button.text:SetPoint("LEFT", button, "LEFT", button.notCheckable and 0 or 24, 0)
2185 button.checked = not info.notCheckable and info.checked
2186 button.isRadio = not info.notCheckable and info.isRadio
2187 if info.isRadio then
2188 button.check:Show()
2189 button.check:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
2190 if button.checked then
2191 button.check:SetTexCoord(0.25, 0.5, 0, 1)
2192 button.check:SetVertexColor(1, 1, 1, 1)
2193 else
2194 button.check:SetTexCoord(0, 0.25, 0, 1)
2195 button.check:SetVertexColor(1, 1, 1, 0.5)
2196 end
2197 button.radioHighlight:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
2198 button.check:SetWidth(16)
2199 button.check:SetHeight(16)
2200 else
2201 if button.checked then
2202 if info.checkIcon then
2203 button.check:SetWidth(16)
2204 button.check:SetHeight(16)
2205 button.check:SetTexture(info.checkIcon)
2206 if string.sub(info.checkIcon, 1, 16) == "Interface\\Icons\\" then
2207 button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
2208 else
2209 button.check:SetTexCoord(0, 1, 0, 1)
2210 end
2211 else
2212 button.check:SetWidth(24)
2213 button.check:SetHeight(24)
2214 button.check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
2215 button.check:SetTexCoord(0, 1, 0, 1)
2216 end
2217 button.check:SetVertexColor(1, 1, 1, 1)
2218 else
2219 button.check:SetVertexColor(1, 1, 1, 0)
2220 end
2221 end
2222 if not button.disabled then
2223 button.func = info.func
2224 end
2225 button.hasColorSwatch = info.hasColorSwatch
2226 if button.hasColorSwatch then
2227 button.colorSwatch:Show()
2228 button.colorSwatch.texture:Show()
2229 button.r = info.r or 1
2230 button.g = info.g or 1
2231 button.b = info.b or 1
2232 button.colorSwatch.texture:SetTexture(button.r, button.g, button.b)
2233 button.checked = false
2234 button.func = nil
2235 button.colorFunc = info.colorFunc
2236 button.colorArg1 = info.colorArg1
2237 button.colorArg2 = info.colorArg2
2238 button.colorArg3 = info.colorArg3
2239 button.colorArg4 = info.colorArg4
2240 button.hasOpacity = info.hasOpacity
2241 button.opacity = info.opacity or 1
2242 else
2243 button.colorSwatch:Hide()
2244 button.colorSwatch.texture:Hide()
2245 end
2246 button.hasArrow = not button.hasColorSwatch and (info.value or info.hasSlider or info.hasEditBox) and info.hasArrow
2247 if button.hasArrow then
2248 button.arrow:SetAlpha(1)
2249 if info.hasSlider then
2250 button.hasSlider = true
2251 button.sliderMin = info.sliderMin or 0
2252 button.sliderMax = info.sliderMax or 1
2253 button.sliderStep = info.sliderStep or 0
2254 button.sliderIsPercent = info.sliderIsPercent and true or false
2255 button.sliderMinText = info.sliderMinText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMin * 100) or button.sliderMin
2256 button.sliderMaxText = info.sliderMaxText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMax * 100) or button.sliderMax
2257 button.sliderFunc = info.sliderFunc
2258 button.sliderValue = info.sliderValue
2259 button.sliderArg1 = info.sliderArg1
2260 button.sliderArg2 = info.sliderArg2
2261 button.sliderArg3 = info.sliderArg3
2262 button.sliderArg4 = info.sliderArg4
2263 elseif info.hasEditBox then
2264 button.hasEditBox = true
2265 button.editBoxText = info.editBoxText or ""
2266 button.editBoxFunc = info.editBoxFunc
2267 button.editBoxArg1 = info.editBoxArg1
2268 button.editBoxArg2 = info.editBoxArg2
2269 button.editBoxArg3 = info.editBoxArg3
2270 button.editBoxArg4 = info.editBoxArg4
2271 button.editBoxChangeFunc = info.editBoxChangeFunc
2272 button.editBoxChangeArg1 = info.editBoxChangeArg1
2273 button.editBoxChangeArg2 = info.editBoxChangeArg2
2274 button.editBoxChangeArg3 = info.editBoxChangeArg3
2275 button.editBoxChangeArg4 = info.editBoxChangeArg4
2276 button.editBoxValidateFunc = info.editBoxValidateFunc
2277 button.editBoxValidateArg1 = info.editBoxValidateArg1
2278 button.editBoxValidateArg2 = info.editBoxValidateArg2
2279 button.editBoxValidateArg3 = info.editBoxValidateArg3
2280 button.editBoxValidateArg4 = info.editBoxValidateArg4
2281 else
2282 button.value = info.value
2283 end
2284 else
2285 button.arrow:SetAlpha(0)
2286 end
2287 button.arg1 = info.arg1
2288 button.arg2 = info.arg2
2289 button.arg3 = info.arg3
2290 button.arg4 = info.arg4
2291 button.closeWhenClicked = info.closeWhenClicked
2292 button.textHeight = info.textHeight or UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT or 10
2293 local font,_ = button.text:GetFont()
2294 button.text:SetFont(STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF", button.textHeight)
2295 button:SetHeight(button.textHeight + 6)
2296 button.text:SetPoint("RIGHT", button.arrow, (button.hasColorSwatch or button.hasArrow) and "LEFT" or "RIGHT")
2297 button.text:SetJustifyH(info.justifyH or "LEFT")
2298 button.text:SetText(info.text)
2299 button.tooltipTitle = info.tooltipTitle
2300 button.tooltipText = info.tooltipText
2301 button.tooltipFunc = info.tooltipFunc
2302 button.tooltipArg1 = info.tooltipArg1
2303 button.tooltipArg2 = info.tooltipArg2
2304 button.tooltipArg3 = info.tooltipArg3
2305 button.tooltipArg4 = info.tooltipArg4
2306 if not button.tooltipTitle and not button.tooltipText and not button.tooltipFunc and not info.isTitle then
2307 button.tooltipTitle = info.text
2308 end
2309 if type(button.func) == "string" then
2310 self:assert(type(button.arg1) == "table", "Cannot call method " .. button.func .. " on a non-table")
2311 self:assert(type(button.arg1[button.func]) == "function", "Method " .. button.func .. " nonexistant.")
2312 end
2313 end
2314  
2315 function Dewdrop:InjectAceOptionsTable(handler, options)
2316 self:argCheck(handler, 2, "table")
2317 self:argCheck(options, 3, "table")
2318 if string.lower(tostring(options.type)) ~= "group" then
2319 self:error('Cannot inject into options table argument #3 if its type is not "group"')
2320 end
2321 if options.handler ~= nil and options.handler ~= handler then
2322 self:error("Cannot inject into options table argument #3 if it has a different handler than argument #2")
2323 end
2324 options.handler = handler
2325 local class = handler.class
2326 if not AceLibrary:HasInstance("AceOO-2.0") or not class then
2327 self:error("Cannot retrieve AceOptions tables from a non-object argument #2")
2328 end
2329 while class and class ~= AceLibrary("AceOO-2.0").Class do
2330 if type(class.GetAceOptionsDataTable) == "function" then
2331 local t = class:GetAceOptionsDataTable(handler)
2332 for k,v in pairs(t) do
2333 if type(options.args) ~= "table" then
2334 options.args = {}
2335 end
2336 if options.args[k] == nil then
2337 options.args[k] = v
2338 end
2339 end
2340 end
2341 local mixins = class.mixins
2342 if mixins then
2343 for mixin in pairs(mixins) do
2344 if type(mixin.GetAceOptionsDataTable) == "function" then
2345 local t = mixin:GetAceOptionsDataTable(handler)
2346 for k,v in pairs(t) do
2347 if type(options.args) ~= "table" then
2348 options.args = {}
2349 end
2350 if options.args[k] == nil then
2351 options.args[k] = v
2352 end
2353 end
2354 end
2355 end
2356 end
2357 class = class.super
2358 end
2359 return options
2360 end
2361  
2362 local function activate(self, oldLib, oldDeactivate)
2363 Dewdrop = self
2364 if oldLib and oldLib.registry then
2365 self.registry = oldLib.registry
2366 self.onceRegistered = oldLib.onceRegistered
2367 else
2368 self.registry = {}
2369 self.onceRegistered = {}
2370  
2371 local WorldFrame_OnMouseDown = WorldFrame:GetScript("OnMouseDown")
2372 local WorldFrame_OnMouseUp = WorldFrame:GetScript("OnMouseUp")
2373 local oldX, oldY, clickTime
2374 WorldFrame:SetScript("OnMouseDown", function()
2375 oldX,oldY = GetCursorPosition()
2376 clickTime = GetTime()
2377 if WorldFrame_OnMouseDown then
2378 WorldFrame_OnMouseDown()
2379 end
2380 end)
2381  
2382 WorldFrame:SetScript("OnMouseUp", function()
2383 local x,y = GetCursorPosition()
2384 if not oldX or not oldY or not x or not y or not clickTime then
2385 self:Close()
2386 if WorldFrame_OnMouseUp then
2387 WorldFrame_OnMouseUp()
2388 end
2389 return
2390 end
2391 local d = math.abs(x - oldX) + math.abs(y - oldY)
2392 if d <= 5 and GetTime() - clickTime < 0.5 then
2393 self:Close()
2394 end
2395 if WorldFrame_OnMouseUp then
2396 WorldFrame_OnMouseUp()
2397 end
2398 end)
2399  
2400 local DropDownList1_Show = DropDownList1.Show
2401 function DropDownList1.Show(DropDownList1)
2402 if levels[1] and levels[1]:IsVisible() then
2403 self:Close()
2404 end
2405 DropDownList1_Show(DropDownList1)
2406 end
2407  
2408 local old_HideDropDownMenu = HideDropDownMenu
2409 function HideDropDownMenu(num)
2410 if levels[1] and levels[1]:IsVisible() then
2411 self:Close()
2412 end
2413 old_HideDropDownMenu(num)
2414 end
2415  
2416 local old_CloseDropDownMenus = CloseDropDownMenus
2417 function CloseDropDownMenus(num)
2418 if levels[1] and levels[1]:IsVisible() then
2419 self:Close()
2420 end
2421 old_CloseDropDownMenus(num)
2422 end
2423 end
2424 levels = {}
2425 buttons = {}
2426  
2427 if oldDeactivate then
2428 oldDeactivate(oldLib)
2429 end
2430 end
2431  
2432 AceLibrary:Register(Dewdrop, MAJOR_VERSION, MINOR_VERSION, activate)