Merge branch 'master' of git://cams.pavlovian.net/questhelper
[QuestHelper.git] / menu.lua
blob3cd2d6ceb299f232db524554d3e21c8591008b70
1 QuestHelper_File["menu.lua"] = "Development Version"
2 QuestHelper_Loadtime["menu.lua"] = GetTime()
4 QuestHelper.active_menu = nil
6 local menuBorderInset = 4
7 local menuBackdrop = {
8 bgFile = "Interface/Tooltips/UI-Tooltip-Background",
9 edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
10 edgeSize = 16,
11 tile = true,
12 tileSize = 16,
13 insets = { left = menuBorderInset, right = menuBorderInset, top = menuBorderInset, bottom = menuBorderInset }}
14 local menuBackdropColor = { 0, 0, 0, 0.65 }
15 local menuBackdropBorderColor = { 1, 1, 1, 0.7 }
17 local function Menu_AddItem(self, item)
18 item:ClearAllPoints()
19 item:SetParent(self)
20 item:SetPoint("TOPLEFT", self, "TOPLEFT")
21 item.parent = self
22 table.insert(self.items, item)
23 end
25 local function Menu_SetCloseFunction(self, ...)
26 self.func_arg = {...}
27 self.func = table.remove(self.func_arg, 1)
28 end
30 local function Menu_OnUpdate(self, elapsed)
31 if self.showing then
32 self.show_phase = self.show_phase + elapsed * 5.0
33 if self.show_phase > 1 then
34 self.show_phase = 1
35 QH_Hook(self, "OnUpdate", nil)
36 end
37 else
38 self.show_phase = self.show_phase - elapsed * 3.0
39 if self.show_phase < 0 then
40 self.show_phase = 0
41 QH_Hook(self, "OnUpdate", nil)
42 self:Hide()
43 if self.func then
44 self.func(unpack(self.func_arg))
45 end
46 if self.auto_release then
47 QuestHelper:ReleaseMenu(self)
48 return
49 end
50 end
51 end
53 self:SetAlpha(self.show_phase)
54 end
56 local function Menu_DoShow(self)
57 self.showing = true
59 local w, h = 0, 0
61 for i, c in ipairs(self.items) do
62 local cw, ch = c:GetSize()
63 w = math.max(w, cw)
64 h = h + ch
65 end
67 local y = menuBorderInset
69 self:SetWidth(w+2*menuBorderInset)
70 self:SetHeight(h+2*menuBorderInset)
71 self:Show()
72 QH_Hook(self, "OnUpdate", self.OnUpdate)
74 for i, c in ipairs(self.items) do
75 local cw, ch = c:GetSize()
76 c:ClearAllPoints()
77 c:SetSize(w, ch)
78 c:SetPoint("TOPLEFT", self, "TOPLEFT", menuBorderInset, -y)
79 y = y + ch
80 end
82 if self.parent then
83 self.level = self.parent.parent.level + #self.parent.parent.items + 1
84 self:SetFrameStrata(self.parent:GetFrameStrata()) -- It should be sufficient to just set all to "TOOLTIP", but this seemed more versatile...
85 else
86 -- When there's no world map, or the world map is in a window, the menus
87 -- are un-parented. So make sure they're at a sufficient strata to be seen.
88 self:SetFrameStrata("TOOLTIP")
89 end
91 self:SetFrameLevel(self.level)
93 for i, n in ipairs(self.items) do
94 n.level = self.level+i
95 n:SetFrameLevel(n.level)
96 n:SetFrameStrata(self:GetFrameStrata())
97 n:DoShow()
98 end
99 end
101 local function Menu_DoHide(self)
102 self.showing = false
103 QH_Hook(self, "OnUpdate", self.OnUpdate)
105 if self.active_item then
106 self.active_item.highlighting = false
107 QH_Hook(self.active_item, "OnUpdate", self.active_item.OnUpdate)
110 for i, n in ipairs(self.items) do
111 n:DoHide()
115 local function Menu_ShowAtCursor(self, auto_release)
116 auto_release = auto_release == nil and true or auto_release
117 self.auto_release = auto_release
119 -- Add a 'Close Menu' item to the end of the menu, if it's not there already
120 if not self.close_item then
121 self.close_item = QuestHelper:CreateMenuItem(self, QHText("MENU_CLOSE"))
122 self.close_item:SetFunction( function() self:DoHide() end )
125 -- Set up the menu position, parentage, etc
126 local x, y = GetCursorPosition()
128 local parent = not UIParent:IsVisible() and QuestHelper.map_overlay or UIParent
129 self:SetParent(parent)
130 self.level = (parent or UIParent):GetFrameLevel()+10
131 self:ClearAllPoints()
133 -- Need to call DoShow before setting the position so that the width and height will have been calculated.
134 self:DoShow()
136 -- I declare this math horrible and convoluted.
137 local scale = parent and parent:GetEffectiveScale() or 1
138 x, y = math.max(0, math.min(x-self:GetWidth()/2*scale, UIParent:GetRight()*UIParent:GetEffectiveScale()-self:GetWidth()*scale)) / scale,
139 math.min(UIParent:GetTop()*UIParent:GetEffectiveScale(), math.max(self:GetHeight(), y+5)) / scale
141 self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x, y)
143 if QuestHelper.active_menu and QuestHelper.active_menu ~= self then
144 QuestHelper.active_menu:DoHide()
147 QuestHelper.active_menu = self
151 function QuestHelper:CreateMenu()
152 local menu = self:CreateFrame(UIParent)
153 menu:Hide()
155 menu.items = self:CreateTable()
156 menu:SetMovable(true)
157 menu:SetFrameStrata("TOOLTIP") -- A good default, but we usually re-parent the menus, which clobbers this.
158 menu:SetBackdrop(menuBackdrop)
159 menu:SetBackdropColor(unpack(menuBackdropColor))
160 menu:SetBackdropBorderColor(unpack(menuBackdropBorderColor))
162 menu.AddItem = Menu_AddItem
163 menu.SetCloseFunction = Menu_SetCloseFunction
164 menu.OnUpdate = Menu_OnUpdate
165 menu.DoShow = Menu_DoShow
166 menu.DoHide = Menu_DoHide
167 menu.ShowAtCursor = Menu_ShowAtCursor
169 menu.show_phase = 0
170 menu.showing = false
171 menu.level = 2
172 menu:SetFrameLevel(menu.level)
174 return menu
177 function QuestHelper:ReleaseMenu(menu)
178 while #menu.items > 0 do
179 local item = table.remove(menu.items)
180 self:ReleaseMenuItem(item)
183 if self.active_menu == menu then
184 self.active_menu = nil
187 self:ReleaseTable(menu.items)
188 self:ReleaseFrame(menu)
191 local function MenuItem_AddTexture(self, tex, before)
192 if before then
193 table.insert(self.lchildren, 1, tex)
194 else
195 table.insert(self.rchildren, tex)
199 local function MenuItem_DoShow(self)
200 self.showing = true
201 QH_Hook(self, "OnUpdate", self.OnUpdate)
202 self:Show()
205 local function MenuItem_DoHide(self)
206 if self.submenu then
207 self.submenu:DoHide()
210 self.showing = false
211 QH_Hook(self, "OnUpdate", self.OnUpdate)
214 local function MenuItem_OnUpdate(self, elapsed)
215 local done_update = true
217 if self.highlighting then
218 self.highlight_phase = self.highlight_phase + elapsed * 3.0
219 if self.highlight_phase > 1 then
220 self.highlight_phase = 1
221 else
222 done_update = false
224 else
225 self.highlight_phase = self.highlight_phase - elapsed
226 if self.highlight_phase < 0 then
227 self.highlight_phase = 0
228 else
229 done_update = false
233 if self.showing then
234 self.show_phase = self.show_phase + elapsed * 5.0
235 if self.show_phase > 1 then
236 self.show_phase = 1
237 else
238 done_update = false
240 else
241 self.show_phase = self.show_phase - elapsed * 5.0
242 if self.show_phase < 0 then
243 self.show_phase = 0
244 self.highlight_phase = 0
245 self:Hide()
246 done_update = true
247 else
248 done_update = false
252 self:Shade(self.show_phase, self.highlight_phase)
254 if done_update then
255 QH_Hook(self, "OnUpdate", nil)
259 local function MenuItem_Shade(self, s, h)
260 local theme = QuestHelper:GetColourTheme()
262 local ih = 1-h
264 self.text:SetTextColor(ih*theme.menu_text[1]+h*theme.menu_text_highlight[1],
265 ih*theme.menu_text[2]+h*theme.menu_text_highlight[2],
266 ih*theme.menu_text[3]+h*theme.menu_text_highlight[3], 1)
268 self.text:SetShadowColor(0, 0, 0, ih)
269 self.text:SetShadowOffset(1, -1)
271 self.background:SetVertexColor(ih*theme.menu[1]+h*theme.menu_highlight[1],
272 ih*theme.menu[2]+h*theme.menu_highlight[2],
273 ih*theme.menu[3]+h*theme.menu_highlight[3], h*0.3+0.4)
275 self:SetAlpha(s)
278 local function MenuItem_SetFunction(self, ...)
279 self.func_arg = {...}
280 self.func = table.remove(self.func_arg, 1)
283 local function MenuItem_SetSubmenu(self, menu)
284 assert(not self.submenu and menu)
285 menu:ClearAllPoints()
286 menu:SetParent(self)
287 menu:SetPoint("TOPLEFT", self, "TOPLEFT")
288 menu.parent = self
289 self.submenu = menu
290 self:AddTexture(QuestHelper:CreateIconTexture(self, 9))
293 local function MenuItem_OnEnter(self)
294 self.highlighting = true
295 QH_Hook(self, "OnUpdate", self.OnUpdate)
297 if self.parent.active_item and self.parent.active_item ~= self then
298 self.parent.active_item.highlighting = false
299 QH_Hook(self.parent.active_item, "OnUpdate", self.parent.active_item.OnUpdate)
302 self.parent.active_item = self
304 if self.parent.submenu and self.parent.submenu ~= self.submenu then
305 self.parent.submenu:DoHide()
306 self.parent.submenu = nil
309 if self.submenu then
310 self.parent.submenu = self.submenu
311 self.submenu:ClearAllPoints()
313 local v, h1, h2 = "TOP", "LEFT", "RIGHT"
315 self.submenu:DoShow()
317 if self:GetRight()+self.submenu:GetWidth() > UIParent:GetRight()*UIParent:GetEffectiveScale() then
318 h1, h2 = h2, h1
321 if self:GetBottom()-self.submenu:GetHeight() < 0 then
322 v = "BOTTOM"
325 self.submenu:SetPoint(v..h1, self, v..h2)
326 self.submenu:DoShow()
330 local function MenuItem_GetSize(self)
331 self.text:SetParent(UIParent) -- Remove the text's parent so that it doesn't inherit scaling and mess up the dimensions.
332 self.text:ClearAllPoints()
333 self.text:SetWidth(0)
334 self.text:SetHeight(0)
336 self.text_w = self.text:GetStringWidth()+20
337 if self.text_w >= 320 then
338 -- Clamp width to 320, then ballance the two rows (using a binary search)
339 self.text:SetWidth(320)
340 self.text_h = self.text:GetHeight()+1
341 local mn, mx = 100, 321
342 while mn ~= mx do
343 local w = math.floor((mn+mx)*0.5)
344 self.text:SetWidth(w-1)
345 if self.text:GetHeight() <= self.text_h then
346 mx = w
347 else
348 mn = w+1
352 self.text_w = mn+1
353 self.text:SetWidth(self.text_w)
354 else
355 self.text:SetWidth(self.text_w)
356 self.text_h = self.text:GetHeight()+1
359 self.text:SetParent(self)
361 local w, h = self.text_w+4, self.text_h+4
363 for i, f in ipairs(self.lchildren) do
364 w = w + f:GetWidth() + 4
365 h = math.max(h, f:GetHeight() + 4)
368 for i, f in ipairs(self.rchildren) do
369 w = w + f:GetWidth() + 4
370 h = math.max(h, f:GetHeight() + 4)
373 self.needed_width = w
375 return w, h
378 local function MenuItem_SetSize(self, w, h)
379 self:SetWidth(w)
380 self:SetHeight(h)
382 local x = 0
384 for i, f in ipairs(self.lchildren) do
385 local cw, ch = f:GetWidth(), f:GetHeight()
387 f:ClearAllPoints()
388 f:SetPoint("TOPLEFT", self, "TOPLEFT", x+2, -(h-ch)*0.5)
389 x = x + cw + 4
392 local x1 = x
393 x = w
395 for i, f in ipairs(self.rchildren) do
396 local cw, ch = f:GetWidth(), f:GetHeight()
397 f:ClearAllPoints()
398 x = x - cw - 4
399 f:SetPoint("TOPLEFT", self, "TOPLEFT", x+2, -(h-ch)*0.5)
402 self.text:ClearAllPoints()
403 self.text:SetPoint("TOPLEFT", self, "TOPLEFT", x1+((x-x1)-self.text_w)*0.5, -(h-self.text_h)*0.5)
406 local function MenuItem_OnClick(self, btn)
407 if btn == "RightButton" then
408 local parent = self.parent
409 while parent.parent do
410 parent = parent.parent
412 parent:DoHide()
413 elseif btn == "LeftButton" and self.func then
414 self.func(unpack(self.func_arg))
415 local parent = self.parent
416 while parent.parent do
417 parent = parent.parent
419 parent:DoHide()
423 function QuestHelper:CreateMenuItem(menu, text)
424 item = self:CreateFrame(menu)
425 item:Hide()
427 item.lchildren = self:CreateTable()
428 item.rchildren = self:CreateTable()
429 item.text = self:CreateText(item, text)
430 item.background = self:CreateTexture(item, 1, 1, 1, 1)
431 item.background:SetDrawLayer("BACKGROUND")
432 item.background:SetAllPoints()
433 item.background:SetVertexColor(0, 0, 0, 0)
435 item.showing = false
436 item.highlighting = false
437 item.show_phase = 0
438 item.highlight_phase = 0
440 item.AddTexture = MenuItem_AddTexture
441 item.DoShow = MenuItem_DoShow
442 item.DoHide = MenuItem_DoHide
443 item.OnUpdate = MenuItem_OnUpdate
444 item.Shade = MenuItem_Shade
445 item.SetFunction = MenuItem_SetFunction
446 item.SetSubmenu = MenuItem_SetSubmenu
447 item.OnEnter = MenuItem_OnEnter
448 item.GetSize = MenuItem_GetSize
449 item.SetSize = MenuItem_SetSize
450 item.OnClick = MenuItem_OnClick
452 item:RegisterForClicks("LeftButtonUp", "RightButtonDown")
453 QH_Hook(item, "OnEnter", item.OnEnter)
454 QH_Hook(item, "OnClick", item.OnClick)
456 menu:AddItem(item)
458 return item
461 function QuestHelper:ReleaseMenuItem(item)
462 item:Hide()
464 while #item.lchildren > 0 do
465 local child = table.remove(item.lchildren)
466 self:ReleaseTexture(child)
469 while #item.rchildren > 0 do
470 local child = table.remove(item.rchildren)
471 self:ReleaseTexture(child)
474 if item.submenu then
475 self:ReleaseMenu(item.submenu)
476 item.submenu = nil
479 self:ReleaseTable(item.lchildren)
480 self:ReleaseTable(item.rchildren)
481 self:ReleaseText(item.text)
482 self:ReleaseTexture(item.background)
483 self:ReleaseFrame(item)
486 local function MenuTitle_OnDragStart(self)
487 local parent = self.parent
489 while parent.parent do
490 parent = parent.parent
493 parent:StartMoving()
496 local function MenuTitle_OnDragStop(self)
497 local parent = self.parent
499 while parent.parent do
500 parent = parent.parent
503 parent:StopMovingOrSizing()
506 local function MenuTitle_Shade(self, s, h)
507 local theme = QuestHelper:GetColourTheme()
509 local ih = 1-h
511 self.text:SetTextColor(ih*theme.menu_title_text[1]+h*theme.menu_title_text_highlight[1],
512 ih*theme.menu_title_text[2]+h*theme.menu_title_text_highlight[2],
513 ih*theme.menu_title_text[3]+h*theme.menu_title_text_highlight[3], 1)
515 self.background:SetVertexColor(ih*theme.menu_title[1]+h*theme.menu_title_highlight[1],
516 ih*theme.menu_title[2]+h*theme.menu_title_highlight[2],
517 ih*theme.menu_title[3]+h*theme.menu_title_highlight[3], h*0.3+0.4)
519 self.text:SetShadowColor(0, 0, 0, 1)
520 self.text:SetShadowOffset(1, -1)
522 self:SetAlpha(s)
525 local function MenuTitle_OnClick(self)
526 local parent = self.parent
528 while parent.parent do
529 parent = parent.parent
532 parent:DoHide()
535 function QuestHelper:CreateMenuTitle(menu, title)
536 local item = self:CreateMenuItem(menu, title)
538 local f1, f2 = self:CreateTexture(item, "Interface\\AddOns\\QuestHelper\\Art\\Fluff.tga"),
539 self:CreateTexture(item, "Interface\\AddOns\\QuestHelper\\Art\\Fluff.tga")
541 f1:SetTexCoord(0, 1, 0, .5)
542 f1:SetWidth(20)
543 f1:SetHeight(10)
544 f2:SetTexCoord(0, 1, .5, 1)
545 f2:SetWidth(20)
546 f2:SetHeight(10)
548 item:AddTexture(f1, true)
549 item:AddTexture(f2, false)
551 item.OnDragStart = MenuTitle_OnDragStart
552 item.OnDragStop = MenuTitle_OnDragStop
553 item.Shade = MenuTitle_Shade
554 item.OnClick = MenuTitle_OnClick
556 item:RegisterForClicks("RightButtonDown")
557 QH_Hook(item, "OnClick", item.OnClick)
558 QH_Hook(item, "OnDragStart", item.OnDragStart)
559 QH_Hook(item, "OnDragStop", item.OnDragStop)
560 item:RegisterForDrag("LeftButton")
562 item.text:SetFont(QuestHelper.font.fancy, 11)