Added hooks for AddObjectiveWatch and RemoveObjectiveWatch, so we can update the...
[QuestHelper.git] / menu.lua
blob6cfb9c644a7f567c360bf9606ec1f95142ff95c7
1 QuestHelper_File["menu.lua"] = "Development Version"
3 QuestHelper.active_menu = nil
5 local menuBorderInset = 4
6 local menuBackdrop = {
7 bgFile = "Interface/Tooltips/UI-Tooltip-Background",
8 edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
9 edgeSize = 16,
10 tile = true,
11 tileSize = 16,
12 insets = { left = menuBorderInset, right = menuBorderInset, top = menuBorderInset, bottom = menuBorderInset }}
13 local menuBackdropColor = { 0, 0, 0, 0.65 }
14 local menuBackdropBorderColor = { 1, 1, 1, 0.7 }
16 local function Menu_AddItem(self, item)
17 item:ClearAllPoints()
18 item:SetParent(self)
19 item:SetPoint("TOPLEFT", self, "TOPLEFT")
20 item.parent = self
21 table.insert(self.items, item)
22 end
24 local function Menu_SetCloseFunction(self, ...)
25 self.func_arg = {...}
26 self.func = table.remove(self.func_arg, 1)
27 end
29 local function Menu_OnUpdate(self, elapsed)
30 if self.showing then
31 self.show_phase = self.show_phase + elapsed * 5.0
32 if self.show_phase > 1 then
33 self.show_phase = 1
34 self:SetScript("OnUpdate", nil)
35 end
36 else
37 self.show_phase = self.show_phase - elapsed * 3.0
38 if self.show_phase < 0 then
39 self.show_phase = 0
40 self:SetScript("OnUpdate", nil)
41 self:Hide()
42 if self.func then
43 self.func(unpack(self.func_arg))
44 end
45 if self.auto_release then
46 QuestHelper:ReleaseMenu(self)
47 return
48 end
49 end
50 end
52 self:SetAlpha(self.show_phase)
53 end
55 local function Menu_DoShow(self)
56 self.showing = true
58 local w, h = 0, 0
60 for i, c in ipairs(self.items) do
61 local cw, ch = c:GetSize()
62 w = math.max(w, cw)
63 h = h + ch
64 end
66 local y = menuBorderInset
68 self:SetWidth(w+2*menuBorderInset)
69 self:SetHeight(h+2*menuBorderInset)
70 self:Show()
71 self:SetScript("OnUpdate", self.OnUpdate)
73 for i, c in ipairs(self.items) do
74 local cw, ch = c:GetSize()
75 c:ClearAllPoints()
76 c:SetSize(w, ch)
77 c:SetPoint("TOPLEFT", self, "TOPLEFT", menuBorderInset, -y)
78 y = y + ch
79 end
81 if self.parent then
82 self.level = self.parent.parent.level + #self.parent.parent.items + 1
83 self:SetFrameStrata(self.parent:GetFrameStrata()) -- It should be sufficient to just set all to "TOOLTIP", but this seemed more versatile...
84 else
85 -- When there's no world map, or the world map is in a window, the menus
86 -- are un-parented. So make sure they're at a sufficient strata to be seen.
87 self:SetFrameStrata("TOOLTIP")
88 end
90 self:SetFrameLevel(self.level)
92 for i, n in ipairs(self.items) do
93 n.level = self.level+i
94 n:SetFrameLevel(n.level)
95 n:SetFrameStrata(self:GetFrameStrata())
96 n:DoShow()
97 end
98 end
100 local function Menu_DoHide(self)
101 self.showing = false
102 self:SetScript("OnUpdate", self.OnUpdate)
104 if self.active_item then
105 self.active_item.highlighting = false
106 self.active_item:SetScript("OnUpdate", self.active_item.OnUpdate)
109 for i, n in ipairs(self.items) do
110 n:DoHide()
114 local function Menu_ShowAtCursor(self, auto_release)
115 auto_release = auto_release == nil and true or auto_release
116 self.auto_release = auto_release
118 -- Add a 'Close Menu' item to the end of the menu, if it's not there already
119 if not self.close_item then
120 self.close_item = QuestHelper:CreateMenuItem(self, QHText("MENU_CLOSE"))
121 self.close_item:SetFunction( function() self:DoHide() end )
124 -- Set up the menu position, parentage, etc
125 local x, y = GetCursorPosition()
127 local parent = not UIParent:IsVisible() and QuestHelper.map_overlay or UIParent
128 self:SetParent(parent)
129 self.level = (parent or UIParent):GetFrameLevel()+10
130 self:ClearAllPoints()
132 -- Need to call DoShow before setting the position so that the width and height will have been calculated.
133 self:DoShow()
135 -- I declare this math horrible and convoluted.
136 local scale = parent and parent:GetEffectiveScale() or 1
137 x, y = math.max(0, math.min(x-self:GetWidth()/2*scale, UIParent:GetRight()*UIParent:GetEffectiveScale()-self:GetWidth()*scale)) / scale,
138 math.min(UIParent:GetTop()*UIParent:GetEffectiveScale(), math.max(self:GetHeight(), y+5)) / scale
140 self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x, y)
142 if QuestHelper.active_menu and QuestHelper.active_menu ~= self then
143 QuestHelper.active_menu:DoHide()
146 QuestHelper.active_menu = self
150 function QuestHelper:CreateMenu()
151 local menu = self:CreateFrame(UIParent)
152 menu:Hide()
154 menu.items = self:CreateTable()
155 menu:SetMovable(true)
156 menu:SetFrameStrata("TOOLTIP") -- A good default, but we usually re-parent the menus, which clobbers this.
157 menu:SetBackdrop(menuBackdrop)
158 menu:SetBackdropColor(unpack(menuBackdropColor))
159 menu:SetBackdropBorderColor(unpack(menuBackdropBorderColor))
161 menu.AddItem = Menu_AddItem
162 menu.SetCloseFunction = Menu_SetCloseFunction
163 menu.OnUpdate = Menu_OnUpdate
164 menu.DoShow = Menu_DoShow
165 menu.DoHide = Menu_DoHide
166 menu.ShowAtCursor = Menu_ShowAtCursor
168 menu.show_phase = 0
169 menu.showing = false
170 menu.level = 2
171 menu:SetFrameLevel(menu.level)
173 return menu
176 function QuestHelper:ReleaseMenu(menu)
177 while #menu.items > 0 do
178 local item = table.remove(menu.items)
179 self:ReleaseMenuItem(item)
182 if self.active_menu == menu then
183 self.active_menu = nil
186 self:ReleaseTable(menu.items)
187 self:ReleaseFrame(menu)
190 local function MenuItem_AddTexture(self, tex, before)
191 if before then
192 table.insert(self.lchildren, 1, tex)
193 else
194 table.insert(self.rchildren, tex)
198 local function MenuItem_DoShow(self)
199 self.showing = true
200 self:SetScript("OnUpdate", self.OnUpdate)
201 self:Show()
204 local function MenuItem_DoHide(self)
205 if self.submenu then
206 self.submenu:DoHide()
209 self.showing = false
210 self:SetScript("OnUpdate", self.OnUpdate)
213 local function MenuItem_OnUpdate(self, elapsed)
214 local done_update = true
216 if self.highlighting then
217 self.highlight_phase = self.highlight_phase + elapsed * 3.0
218 if self.highlight_phase > 1 then
219 self.highlight_phase = 1
220 else
221 done_update = false
223 else
224 self.highlight_phase = self.highlight_phase - elapsed
225 if self.highlight_phase < 0 then
226 self.highlight_phase = 0
227 else
228 done_update = false
232 if self.showing then
233 self.show_phase = self.show_phase + elapsed * 5.0
234 if self.show_phase > 1 then
235 self.show_phase = 1
236 else
237 done_update = false
239 else
240 self.show_phase = self.show_phase - elapsed * 5.0
241 if self.show_phase < 0 then
242 self.show_phase = 0
243 self.highlight_phase = 0
244 self:Hide()
245 done_update = true
246 else
247 done_update = false
251 self:Shade(self.show_phase, self.highlight_phase)
253 if done_update then
254 self:SetScript("OnUpdate", nil)
258 local function MenuItem_Shade(self, s, h)
259 local theme = QuestHelper:GetColourTheme()
261 local ih = 1-h
263 self.text:SetTextColor(ih*theme.menu_text[1]+h*theme.menu_text_highlight[1],
264 ih*theme.menu_text[2]+h*theme.menu_text_highlight[2],
265 ih*theme.menu_text[3]+h*theme.menu_text_highlight[3], 1)
267 self.text:SetShadowColor(0, 0, 0, ih)
268 self.text:SetShadowOffset(1, -1)
270 self.background:SetVertexColor(ih*theme.menu[1]+h*theme.menu_highlight[1],
271 ih*theme.menu[2]+h*theme.menu_highlight[2],
272 ih*theme.menu[3]+h*theme.menu_highlight[3], h*0.3+0.4)
274 self:SetAlpha(s)
277 local function MenuItem_SetFunction(self, ...)
278 self.func_arg = {...}
279 self.func = table.remove(self.func_arg, 1)
282 local function MenuItem_SetSubmenu(self, menu)
283 assert(not self.submenu and menu)
284 menu:ClearAllPoints()
285 menu:SetParent(self)
286 menu:SetPoint("TOPLEFT", self, "TOPLEFT")
287 menu.parent = self
288 self.submenu = menu
289 self:AddTexture(QuestHelper:CreateIconTexture(self, 9))
292 local function MenuItem_OnEnter(self)
293 self.highlighting = true
294 self:SetScript("OnUpdate", self.OnUpdate)
296 if self.parent.active_item and self.parent.active_item ~= self then
297 self.parent.active_item.highlighting = false
298 self.parent.active_item:SetScript("OnUpdate", self.parent.active_item.OnUpdate)
301 self.parent.active_item = self
303 if self.parent.submenu and self.parent.submenu ~= self.submenu then
304 self.parent.submenu:DoHide()
305 self.parent.submenu = nil
308 if self.submenu then
309 self.parent.submenu = self.submenu
310 self.submenu:ClearAllPoints()
312 local v, h1, h2 = "TOP", "LEFT", "RIGHT"
314 self.submenu:DoShow()
316 if self:GetRight()+self.submenu:GetWidth() > UIParent:GetRight()*UIParent:GetEffectiveScale() then
317 h1, h2 = h2, h1
320 if self:GetBottom()-self.submenu:GetHeight() < 0 then
321 v = "BOTTOM"
324 self.submenu:SetPoint(v..h1, self, v..h2)
325 self.submenu:DoShow()
329 local function MenuItem_GetSize(self)
330 self.text:SetParent(nil) -- Remove the text's parent so that it doesn't inherit scaling and mess up the dimensions.
331 self.text:ClearAllPoints()
332 self.text:SetWidth(0)
333 self.text:SetHeight(0)
335 self.text_w = self.text:GetStringWidth()+20
336 if self.text_w >= 320 then
337 -- Clamp width to 320, then ballance the two rows (using a binary search)
338 self.text:SetWidth(320)
339 self.text_h = self.text:GetHeight()+1
340 local mn, mx = 100, 321
341 while mn ~= mx do
342 local w = math.floor((mn+mx)*0.5)
343 self.text:SetWidth(w-1)
344 if self.text:GetHeight() <= self.text_h then
345 mx = w
346 else
347 mn = w+1
351 self.text_w = mn+1
352 self.text:SetWidth(self.text_w)
353 else
354 self.text:SetWidth(self.text_w)
355 self.text_h = self.text:GetHeight()+1
358 self.text:SetParent(self)
360 local w, h = self.text_w+4, self.text_h+4
362 for i, f in ipairs(self.lchildren) do
363 w = w + f:GetWidth() + 4
364 h = math.max(h, f:GetHeight() + 4)
367 for i, f in ipairs(self.rchildren) do
368 w = w + f:GetWidth() + 4
369 h = math.max(h, f:GetHeight() + 4)
372 self.needed_width = w
374 return w, h
377 local function MenuItem_SetSize(self, w, h)
378 self:SetWidth(w)
379 self:SetHeight(h)
381 local x = 0
383 for i, f in ipairs(self.lchildren) do
384 local cw, ch = f:GetWidth(), f:GetHeight()
386 f:ClearAllPoints()
387 f:SetPoint("TOPLEFT", self, "TOPLEFT", x+2, -(h-ch)*0.5)
388 x = x + cw + 4
391 local x1 = x
392 x = w
394 for i, f in ipairs(self.rchildren) do
395 local cw, ch = f:GetWidth(), f:GetHeight()
396 f:ClearAllPoints()
397 x = x - cw - 4
398 f:SetPoint("TOPLEFT", self, "TOPLEFT", x+2, -(h-ch)*0.5)
401 self.text:ClearAllPoints()
402 self.text:SetPoint("TOPLEFT", self, "TOPLEFT", x1+((x-x1)-self.text_w)*0.5, -(h-self.text_h)*0.5)
405 local function MenuItem_OnClick(self, btn)
406 if btn == "RightButton" then
407 local parent = self.parent
408 while parent.parent do
409 parent = parent.parent
411 parent:DoHide()
412 elseif btn == "LeftButton" and self.func then
413 self.func(unpack(self.func_arg))
414 local parent = self.parent
415 while parent.parent do
416 parent = parent.parent
418 parent:DoHide()
422 function QuestHelper:CreateMenuItem(menu, text)
423 item = self:CreateFrame(menu)
424 item:Hide()
426 item.lchildren = self:CreateTable()
427 item.rchildren = self:CreateTable()
428 item.text = self:CreateText(item, text)
429 item.background = self:CreateTexture(item, 1, 1, 1, 1)
430 item.background:SetDrawLayer("BACKGROUND")
431 item.background:SetAllPoints()
432 item.background:SetVertexColor(0, 0, 0, 0)
434 item.showing = false
435 item.highlighting = false
436 item.show_phase = 0
437 item.highlight_phase = 0
439 item.AddTexture = MenuItem_AddTexture
440 item.DoShow = MenuItem_DoShow
441 item.DoHide = MenuItem_DoHide
442 item.OnUpdate = MenuItem_OnUpdate
443 item.Shade = MenuItem_Shade
444 item.SetFunction = MenuItem_SetFunction
445 item.SetSubmenu = MenuItem_SetSubmenu
446 item.OnEnter = MenuItem_OnEnter
447 item.GetSize = MenuItem_GetSize
448 item.SetSize = MenuItem_SetSize
449 item.OnClick = MenuItem_OnClick
451 item:RegisterForClicks("LeftButtonUp", "RightButtonDown")
452 item:SetScript("OnEnter", item.OnEnter)
453 item:SetScript("OnClick", item.OnClick)
455 menu:AddItem(item)
457 return item
460 function QuestHelper:ReleaseMenuItem(item)
461 item:Hide()
463 while #item.lchildren > 0 do
464 local child = table.remove(item.lchildren)
465 self:ReleaseTexture(child)
468 while #item.rchildren > 0 do
469 local child = table.remove(item.rchildren)
470 self:ReleaseTexture(child)
473 if item.submenu then
474 self:ReleaseMenu(item.submenu)
475 item.submenu = nil
478 self:ReleaseTable(item.lchildren)
479 self:ReleaseTable(item.rchildren)
480 self:ReleaseText(item.text)
481 self:ReleaseTexture(item.background)
482 self:ReleaseFrame(item)
485 local function MenuTitle_OnDragStart(self)
486 local parent = self.parent
488 while parent.parent do
489 parent = parent.parent
492 parent:StartMoving()
495 local function MenuTitle_OnDragStop(self)
496 local parent = self.parent
498 while parent.parent do
499 parent = parent.parent
502 parent:StopMovingOrSizing()
505 local function MenuTitle_Shade(self, s, h)
506 local theme = QuestHelper:GetColourTheme()
508 local ih = 1-h
510 self.text:SetTextColor(ih*theme.menu_title_text[1]+h*theme.menu_title_text_highlight[1],
511 ih*theme.menu_title_text[2]+h*theme.menu_title_text_highlight[2],
512 ih*theme.menu_title_text[3]+h*theme.menu_title_text_highlight[3], 1)
514 self.background:SetVertexColor(ih*theme.menu_title[1]+h*theme.menu_title_highlight[1],
515 ih*theme.menu_title[2]+h*theme.menu_title_highlight[2],
516 ih*theme.menu_title[3]+h*theme.menu_title_highlight[3], h*0.3+0.4)
518 self.text:SetShadowColor(0, 0, 0, 1)
519 self.text:SetShadowOffset(1, -1)
521 self:SetAlpha(s)
524 local function MenuTitle_OnClick(self)
525 local parent = self.parent
527 while parent.parent do
528 parent = parent.parent
531 parent:DoHide()
534 function QuestHelper:CreateMenuTitle(menu, title)
535 local item = self:CreateMenuItem(menu, title)
537 local f1, f2 = self:CreateTexture(item, "Interface\\AddOns\\QuestHelper\\Art\\Fluff.tga"),
538 self:CreateTexture(item, "Interface\\AddOns\\QuestHelper\\Art\\Fluff.tga")
540 f1:SetTexCoord(0, 1, 0, .5)
541 f1:SetWidth(20)
542 f1:SetHeight(10)
543 f2:SetTexCoord(0, 1, .5, 1)
544 f2:SetWidth(20)
545 f2:SetHeight(10)
547 item:AddTexture(f1, true)
548 item:AddTexture(f2, false)
550 item.OnDragStart = MenuTitle_OnDragStart
551 item.OnDragStop = MenuTitle_OnDragStop
552 item.Shade = MenuTitle_Shade
553 item.OnClick = MenuTitle_OnClick
555 item:RegisterForClicks("RightButtonDown")
556 item:SetScript("OnClick", item.OnClick)
557 item:SetScript("OnDragStart", item.OnDragStart)
558 item:SetScript("OnDragStop", item.OnDragStop)
559 item:RegisterForDrag("LeftButton")
561 item.text:SetFont(QuestHelper.font.fancy, 11)