diff gui.lua @ 95:3546c7b55986

Move generation of the Help tab to the same file as the help text. Allow registration of arbitrary tab controls, not just text generation. Move some shared data out of the addon table into a 'gui' state table in preparation for more of the same. Clean some cruft out of the default itemfilter list.
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Thu, 26 Jul 2012 03:34:56 +0000
parents db1d5d09e5f5
children 780b7e0eeeeb
line wrap: on
line diff
--- a/gui.lua	Tue Jul 24 23:04:51 2012 +0000
+++ b/gui.lua	Thu Jul 26 03:34:56 2012 +0000
@@ -43,17 +43,33 @@
 	local disp = data[realrow].disposition
 	return eoi_st_lootrow_col3_colortable[disp or 'normal']
 end
-addon.time_column1_used_mt = { __index = {
+local time_column1_used_mt = { __index = {
 	[2] = {value=""},
 	[3] = {value=""},
 } }
-local time_column1_used_mt = addon.time_column1_used_mt
 
 
 ------ Globals
-local GUI = LibStub("AceGUI-3.0")
+local AceGUI = LibStub("AceGUI-3.0")
 local flib = LibStub("LibFarmbuyer")
 
+local gui = {
+	-- These are used to build the tabgroup_tabs array fed to TabGroup, and
+	-- for the official source of mouseover tab titles, etc.  Not completely
+	-- hidden because we need to reach in and fiddle too often to be worth it.
+	tabtexts = {
+		["eoi"] = {title=[[Loot]], desc=[[Observed loot, plus boss kills and other events of interest]]},
+		["hist"] = {title=[[History]], desc=[[A short semi-permanent record]]},
+		["opt"] = {title=[[Options]], desc=[[Options for fine-tuning behavior]]},
+	},
+	taborder        = { "eoi"                        },
+	taborder_APPEND = {        "hist", "help", "opt" },
+}
+addon.gui_state_pointer = gui    -- only during loading, then cleaned out
+if addon.author_debug then
+	_G.OLgui = gui
+end
+
 local g_loot			= nil
 local g_uniques			= nil
 local g_generated		= nil
@@ -73,7 +89,7 @@
 
 -- En masse forward decls of symbols defined inside local blocks
 local _generate_text, _populate_text_specials, _markup
-local eoi_dropdownfuncs, _tabtexts, _taborder -- filled out in gui block scope
+local eoi_dropdownfuncs -- filled out in gui block scope
 local _do_debugging_tooltip, _hide_debugging_tooltip, _build_debugging_tooltip
 local _new_rebroadcast_hyperlink
 
@@ -135,6 +151,36 @@
 	local text_gen_funcs, specials_gen_funcs = {}, {}
 	local accumulator = {}
 
+	local function _reg (tab_code, title, description, --[[unused]]generator,
+		opt_specgen, opt_noobtip, opt_cli
+	)
+		assert(type(tab_code)=='string')
+		assert(type(title)=='string')
+		assert(type(description)=='string')
+		gui.tabtexts[tab_code] = { title=title, desc=description }
+		if not gui.suppress_taborder then
+			gui:tabposition_insert (tab_code)
+		end
+		if opt_specgen then
+			assert(type(opt_specgen)=='function')
+			specials_gen_funcs[tab_code] = opt_specgen
+		end
+		if opt_noobtip then
+			if type(opt_noobtip) == 'string' then
+				noob_tips[tab_code] = _markup(opt_noobtip)
+			elseif type(opt_noobtip) == 'function' then
+				noob_tips[tab_code] = opt_noobtip
+			else
+				error(("Optional new user tip argument for '%s' must be a string or function!"):format(tab_code))
+			end
+		end
+		if opt_cli then
+			assert(type(opt_cli)=='function')
+			tabs_CLI_special[tab_code] = opt_cli
+		end
+		dirty_tabs = true
+	end
+
 	-- Can do clever things by passing other halting points as zero
 	function addon:zero_printed_fenceposts (zero)
 		for t in pairs(text_gen_funcs) do
@@ -147,15 +193,15 @@
 	end
 
 	-- This function is called during load, so be careful!
-	function addon:register_text_generator (text_type, title, description, generator, opt_specgen)
+	function addon:register_text_generator (text_type, title, description,
+		generator, opt_specgen, opt_noobtip, opt_cli
+	)
+		if self.NOLOAD then return end
 		if type(generator) ~= 'function' then
 			error(("Generator for text type '%s' must be a function!"):format(text_type))
 		end
-		_tabtexts[text_type] = { title=title, desc=description }
-		self:tabposition_insert (text_type)
+		_reg (text_type, title, description, generator, opt_specgen, opt_noobtip, opt_cli)
 		text_gen_funcs[text_type] = generator
-		specials_gen_funcs[text_type] = opt_specgen
-		dirty_tabs = true
 	end
 
 	-- These two called by tabs_generated_text_OGS
@@ -195,19 +241,19 @@
 
 	-- LOD tab has been clicked on.
 	local function _handle_LOD (tabs_container,specials,tabtitle)
-		-- "tabtitle" here is the name in _taborder, not the colorized string
-		local what = _tabtexts[tabtitle]
+		-- "tabtitle" here is the name in taborder, not the colorized string
+		local what = gui.tabtexts[tabtitle]
 		local addon_index = what.LOD
 		local function LOAD()
-			_tabtexts[tabtitle] = nil
-			addon:tabposition_remove_and_remember (tabtitle)
+			gui.tabtexts[tabtitle] = nil
+			gui:tabposition_remove_and_remember (tabtitle)
 			local loaded, whynot = LoadAddOn(addon_index)
-			local tabdelta = addon:tabposition_restore()
+			local tabdelta = gui:tabposition_restore()
 			if loaded then
 				addon:Print("%s loaded, %d |4tab:tabs; added.", tabtitle, tabdelta)
 			else
 				what.disabled = true
-				_tabtexts[tabtitle] = what -- restore this for mouseovers
+				gui.tabtexts[tabtitle] = what -- restore this for mouseovers
 				addon:Print("%s could not load (game client reason was '%s').", tabtitle, whynot)
 				DisableAddOn(addon_index)
 			end
@@ -243,7 +289,7 @@
 	-- already been called, we flag the dirty bit and let the main building
 	-- routine handle it like any other plugin.
 	function addon:_gui_add_LOD_tab (tabtitle, folder, addon_index, enabled_p, why_not)
-		_tabtexts[tabtitle] = {
+		gui.tabtexts[tabtitle] = {
 			title = ("|cffff0000(%s)|r"):format(tabtitle),
 			desc = ("Plugin '|cffff0000%s|r' is not loaded yet.  Click the tab to load it now."):format(folder),
 			LOD = addon_index,
@@ -251,9 +297,30 @@
 			LOD_why_not = why_not,
 		}
 		tabs_OnGroupSelected[tabtitle] = _handle_LOD
-		self:tabposition_insert (tabtitle)
+		gui:tabposition_insert (tabtitle)
 		dirty_tabs = true
 	end
+
+	-- Registering truly arbitrary tab controls, not just text generators.
+	-- (This is slightly out of place, but no more so than the LOD stuff.)
+	-- The arguments are nearly the same as those of :register_text_generator
+	-- but the "generator" function is the full-blown callback and there is
+	-- no "specgen" (since it's handled in the main generator).
+	function addon:register_tab_control (tab_code, title, description,
+		generator, opt_noobtip, opt_cli
+	)
+		if self.NOLOAD then return end
+		if type(generator) ~= 'function' then
+			error(("Generator for tab code '%s' must be a function!"):format(tab_code))
+		end
+		_reg (tab_code, title, description, generator, --[[opt_specgen=]]nil, opt_noobtip, opt_cli)
+		tabs_OnGroupSelected[tab_code] = generator
+	end
+	function addon:register_tab_control_AT_END (...)
+		gui.suppress_taborder = true
+		self:register_tab_control(...)
+		gui.suppress_taborder = nil
+	end
 end
 
 --[[
@@ -681,7 +748,7 @@
 
 
 ------ Main GUI Window
-local _d   -- display when it's open, eoiST when it's not
+local _d   -- display when it's open, nil when it's not
 local function setstatus(txt) _d:SetStatusText(txt) end
 local function statusy_OnLeave() setstatus("") end
 local tabgroup_tabs
@@ -690,24 +757,13 @@
 Controls for the tabs on the left side of the main display.
 ]]
 
--- The _tabtexts and _taborder tables have distressingly wide visibility.
--- They are used to build the tabgroup_tabs array fed to TabGroup, and for the
--- official source of mouseover tab titles, etc.  Not completely encapsulated
--- because we need to reach in and fiddle too often to be worth it.
-_tabtexts = {
-	["eoi"] = {title=[[Loot]], desc=[[Observed loot, plus boss kills and other events of interest]]},
-	["hist"] = {title=[[History]], desc=[[A short semi-permanent record]]},
-	["help"] = {title=[[Help]], desc=[[Instructions, reminders, and tips for non-obvious features]]},
-	["opt"] = {title=[[Options]], desc=[[Options for fine-tuning behavior]]},
-}
-_taborder = { "eoi", "hist", "help", "opt" }
-
 do
-	local next_insertion_position = 2   -- position in _taborder
+	--local next_insertion_position = 2   -- position in taborder
+	local next_insertion_position = #gui.taborder + 1
 	local removed, saved_offset
 
-	function addon:tabposition_insert (tabcode)
-		tinsert (_taborder, next_insertion_position, tabcode)
+	function gui:tabposition_insert (tabcode)
+		tinsert (gui.taborder, next_insertion_position, tabcode)
 		next_insertion_position = next_insertion_position + 1
 	end
 
@@ -715,11 +771,11 @@
 	-- a tab and prepares to insert more tab(s) in its place.  The second
 	-- returns the "next tab goes here" marker back to the proper end.  (And
 	-- doing all 3 adjustments below at once is amazingly hard to read.)
-	function addon:tabposition_remove_and_remember (tabcode)
+	function gui:tabposition_remove_and_remember (tabcode)
 		assert(not removed)   -- enforce stack-ish discipline
-		for i = 2, #_taborder do
-			if _taborder[i] == tabcode then
-				tremove (_taborder, i)
+		for i = 2, #gui.taborder do
+			if gui.taborder[i] == tabcode then
+				tremove (gui.taborder, i)
 				saved_offset = next_insertion_position - i - 1
 				removed, next_insertion_position = i, i
 				return
@@ -727,13 +783,23 @@
 		end
 		error(("'%s' not used as a tab-text code"):format(tabcode))
 	end
-	function addon:tabposition_restore()
+	function gui:tabposition_restore()
 		assert(removed)
 		local count = next_insertion_position - removed
 		next_insertion_position = next_insertion_position + saved_offset
 		removed, saved_offset = nil, nil
 		return count
 	end
+
+	function addon:FINISH_SPECIAL_TABS()
+		-- very carefully not touching next_insertion_position
+		for i,v in ipairs(gui.taborder_APPEND) do
+			gui.taborder[#gui.taborder+1] = v
+		end
+		gui.taborder_APPEND = nil
+		self.register_tab_control_AT_END = nil
+		self.FINISH_SPECIAL_TABS = nil
+	end
 end
 
 -- Done at startup, and whenever we've changed the population of tabs.
@@ -747,12 +813,12 @@
 	-- row is already full.  It turns out that not doing this looks like ass.
 	-- If we won't have enough tabs to trigger this on its own, pad out the tab
 	-- titles (not looking quite as nice, ah well) to force it to trigger.
-	local fmtstr = #_taborder > 6 and "%s" or "   %s   "
-	for i,name in ipairs(_taborder) do
+	local fmtstr = #gui.taborder > 6 and "%s" or "   %s   "
+	for i,name in ipairs(gui.taborder) do
 		tabgroup_tabs[i] = {
 			value = name,
-			text = fmtstr:format(_tabtexts[name].title),
-			disabled = _tabtexts[name].disabled,
+			text = fmtstr:format(gui.tabtexts[name].title),
+			disabled = gui.tabtexts[name].disabled,
 		}
 		-- By default, tabs are editboxes with generated text
 		if not tabs_OnGroupSelected[name] then
@@ -785,7 +851,7 @@
 	local i = _d and _d.GetUserData and _d:GetUserData("DD index")
 	if i then
 		subfunc(i,arg)
-		_d:GetUserData("which ST"):OuroLoot_Refresh(i)
+		gui.which_ST:OuroLoot_Refresh(i)
 	end
 end
 
@@ -886,7 +952,7 @@
 	end,
 
 	["Show only this player"] = function(rowi)
-		local st = _d:GetUserData("eoiST")
+		local st = assert(gui.eoiST)
 		_d:SetUserData("player filter name", g_loot[rowi].person)
 		st:SetFilter(_d:GetUserData("player filter by name"))
 		_d:GetUserData("eoi_filter_reset"):SetDisabled(false)
@@ -898,7 +964,7 @@
 	["Change from 'wipe' to 'kill'"] = function(rowi)
 		addon:_mark_boss_kill(rowi)
 		-- the fillout function called automatically will start too far down the list
-		_d:GetUserData("eoiST"):OuroLoot_Refresh()
+		gui.eoiST:OuroLoot_Refresh()
 	end,
 
 	["Edit note"] = function(rowi)
@@ -1144,7 +1210,7 @@
 	local e = g_loot[row_index]
 	if not e then return end   -- how the hell could we get this far?
 	local celldata = e.cols[3]
-	local box = GUI:Create("EditBox")
+	local box = AceGUI:Create("EditBox")
 	box:SetText(celldata.value)
 	box:SetUserData("old show", box.editbox:GetScript("OnShow"))
 	box:SetUserData("old escape", box.editbox:GetScript("OnEscapePressed"))
@@ -1161,7 +1227,7 @@
 		value = value and value:match("^(x%d+)")
 		if value then e.count = value end
 		_b:Release()
-		return _d:GetUserData("eoiST"):OuroLoot_Refresh(row_index)
+		return gui.eoiST:OuroLoot_Refresh(row_index)
 	end)
 	box:SetCallback("OnRelease", function(_b)
 		_b.editbox:ClearFocus()
@@ -1271,7 +1337,7 @@
 	-- this function replaces itself with a smaller, sleeker, sexier one.
 	-- This function will later be garbage collected.
 	local ST = LibStub("ScrollingTable"):CreateST(eoi_st_cols,eoi_st_displayed_rows,eoi_st_rowheight)
-	_d:SetUserData("eoiST",ST)
+	gui.eoiST = assert(ST)
 	if addon.author_debug then
 		_G.OLST = ST
 	end
@@ -1335,10 +1401,10 @@
 
 	-- Now set up the future drawing function...
 	tabs_OnGroupSelected["eoi"] = function(container,specials)
-		local st_widget = GUI:Create("lib-st")
-		local st = _d:GetUserData("eoiST")
+		local st_widget = AceGUI:Create("lib-st")
+		local st = assert(gui.eoiST)
 
-		_d:SetUserData("which ST",st)
+		gui.which_ST = st
 
 		-- This is actually required each time
 		_d:SetUserData ("player filter clear", player_filter_all)
@@ -1372,13 +1438,13 @@
 			[[Return to showing complete loot information.]])
 		b:SetFullWidth(true)
 		b:SetCallback("OnClick", function (_b)
-			local st = _d:GetUserData("eoiST")
-			st:SetFilter(player_filter_all)
+			gui.eoiST:SetFilter(player_filter_all)
 			_b:SetDisabled(true)
 		end)
 		b:SetDisabled(st.Filter == player_filter_all)
 		specials:AddChild(b)
 
+		-- FIXME iterate over the new raiders table instead
 		local people = { "<nobody>" }
 		for i = 1, GetNumRaidMembers() do
 			tinsert(people,(GetRaidRosterInfo(i)))
@@ -1427,7 +1493,7 @@
 -- Tab 2/3 (generated text)
 function tabs_generated_text_OGS (container, specials, text_kind)
 	container:SetLayout("Fill")
-	local box = GUI:Create("MultiLineEditBox")
+	local box = AceGUI:Create("MultiLineEditBox")
 	box:SetFullWidth(true)
 	box:SetFullHeight(true)
 	box:SetLabel("Pressing the Escape key while typing will return keystroke control to the usual chat window.")
@@ -1467,12 +1533,12 @@
 		g_loot.printed[text_kind] = 0
 		g_generated.last_instance = nil
 		g_generated[pos] = nil
-		addon:Print("'%s' has been regenerated.", _tabtexts[text_kind].title)
+		addon:Print("'%s' has been regenerated.", gui.tabtexts[text_kind].title)
 		return addon:redisplay()
 	end)
 	specials:AddChild(w)
-	if addon:is_plugin(text_kind) then
-		local pname = addon:is_plugin(text_kind):GetName()
+	if addon:get_plugin(text_kind) then
+		local pname = addon:get_plugin(text_kind):GetName()
 		w = mkbutton("Reset SVs & ReloadUI",
 			([[<DEBUG:> Reset savedvariables for %s plugin back to defaults, and trigger a UI reload.]]):format(pname))
 		w:SetFullWidth(true)
@@ -1516,7 +1582,6 @@
 				h.itemlink, addon:colorize(h.OLwho,h.OLclass))
 			if numleft < 1 then
 				history_filter_who = nil
-				--_d:GetUserData("histST"):SetFilter(history_filter_by_recent)
 				histST:SetFilter(history_filter_by_recent)
 				setstatus(hist_normal_status)
 			end
@@ -1663,7 +1728,7 @@
 -- Tab 4:  History (implementation)
 tabs_OnGroupSelected["hist"] = function(container,specials)
 	histST = LibStub("ScrollingTable"):CreateST(hist_st_cols,eoi_st_displayed_rows,eoi_st_rowheight)
-	_d:SetUserData("histST",histST)
+	gui.histST = histST
 	if addon.author_debug then
 		_G.OLHST = histST
 	end
@@ -1710,9 +1775,8 @@
 	end
 
 	tabs_OnGroupSelected["hist"] = function(container,specials)
-		local st_widget = GUI:Create("lib-st")
-		-- don't need _d:GetUserData("histST") here, as it's already a local
-		_d:SetUserData("which ST",histST)
+		local st_widget = AceGUI:Create("lib-st")
+		gui.which_ST = histST
 		histST:OuroLoot_Refresh()
 		st_widget:WrapST(histST)
 		st_widget.head_offset = 15
@@ -1750,7 +1814,7 @@
 			specials:AddChild(b)
 		end
 
-		--[[ b = GUI:Create("Spacer") b:SetFullWidth(true) b:SetHeight(10) specials:AddChild(b) ]]
+		--[[ b = AceGUI:Create("Spacer") b:SetFullWidth(true) b:SetHeight(10) specials:AddChild(b) ]]
 
 		b = mkbutton("Regenerate",
 			[[Erases all history entries from the displayed realm, and regenerates it from current loot information.]])
@@ -1814,13 +1878,12 @@
 
 <Shift-Left> while over an item link to paste it into chat.  <Shift-Right>
 any row to display a dropdown menu.]]
--- '/loot hi pla' -> set filter on Playername
+-- '/ol hi pla' -> set filter on Playername
 tabs_CLI_special["hist"] = function (name)
 	name = '^'..name   -- already tolower'd by onslash
 	for _,player in ipairs(addon.history) do
 		if player.name:lower():find(name) then
 			history_filter_who = player.name
-			--_d:GetUserData("histST"):SetFilter(history_filter_by_name)
 			histST:SetFilter(history_filter_by_name)
 			setstatus(hist_name_status)
 			break
@@ -1831,58 +1894,6 @@
 
 
 -- Tab 5:  Help (content in verbage.lua)
-do
-	local tabs_help_OnGroupSelected_func = function (treeg,event,category)
-		treeg:ReleaseChildren()
-		local txt = GUI:Create("Label")
-		txt:SetFullWidth(true)
-		txt:SetFontObject(GameFontNormal)--Highlight)
-		txt:SetText(addon.helptext[category])
-		local sf = GUI:Create("ScrollFrame")
-		local sfstat = _d:GetUserData("help tab scroll status") or {}
-		sf:SetStatusTable(sfstat)
-		_d:SetUserData("help tab scroll status",sfstat)
-		sf:SetLayout("Fill")
-		-- This forces the scrolling area to be bigger than the visible area; else
-		-- some of the text gets cut off.
-		sf.content:SetHeight(700)
-		sf:AddChild(txt)
-		treeg:AddChild(sf)
-		if treeg:GetUserData("help restore scroll") then
-			sfstat = sfstat.scrollvalue
-			if sfstat then sf:SetScroll(sfstat) end
-			treeg:SetUserData("help restore scroll", false)
-		else
-			sf:SetScroll(0)
-		end
-	end
-	tabs_OnGroupSelected["help"] = function(container,specials)
-		container:SetLayout("Fill")
-		local left = GUI:Create("TreeGroup")
-		local leftstat = _d:GetUserData("help tab select status")
-						 or {treewidth=145}
-		left:SetStatusTable(leftstat)
-		_d:SetUserData("help tab select status",leftstat)
-		left:SetLayout("Fill")
-		left:SetFullWidth(true)
-		left:SetFullHeight(true)
-		left:EnableButtonTooltips(false)
-		left:SetTree(addon.helptree)
-		left:SetCallback("OnGroupSelected", tabs_help_OnGroupSelected_func)
-		container:AddChild(left)
-		leftstat = leftstat.selected
-		if leftstat then
-			left:SetUserData("help restore scroll", true)
-			left:SelectByValue(leftstat)
-		else
-			left:SelectByValue("basic")
-		end
-	end
-	noob_tips["help"] = _markup[[
-Every tab across the top also gets its own special buttons here in the lower
-right, under the same name as the tab.  Some controls do not become visible
-until that tab has data to work with.]]
-end
 
 
 -- Tab 6:  Options / Advanced
@@ -1914,7 +1925,7 @@
 	local function adv_real (container, specials)
 		local grp, w
 
-		grp = GUI:Create("InlineGroup")
+		grp = AceGUI:Create("InlineGroup")
 		grp:SetLayout("Flow")
 		grp:PauseLayout()
 		grp:SetFullWidth(true)
@@ -1976,9 +1987,9 @@
 		end)
 		grp:AddChild(w)
 
-		w = GUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(1) grp:AddChild(w)
+		w = AceGUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(1) grp:AddChild(w)
 
-		local simple = GUI:Create("SimpleGroup")
+		local simple = AceGUI:Create("SimpleGroup")
 		simple:SetLayout("List")
 		simple:SetRelativeWidth(0.3)
 		w = mkbutton("Dropdown", nil, "", [[hovering over Item column only]])
@@ -1995,9 +2006,9 @@
 		end)
 		simple:AddChild(w)
 
-		w = GUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(10) simple:AddChild(w)
+		w = AceGUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(10) simple:AddChild(w)
 
-		w = GUI:Create("CheckBoxSmallLabel")
+		w = AceGUI:Create("CheckBoxSmallLabel")
 		w:SetFullWidth(true)
 		w:SetType("checkbox")
 		w:SetLabel("debug toggle  --->")
@@ -2007,7 +2018,7 @@
 			addon:redisplay()
 		end)
 		simple:AddChild(w)
-		w = GUI:Create("CheckBoxSmallLabel")
+		w = AceGUI:Create("CheckBoxSmallLabel")
 		w:SetFullWidth(true)
 		w:SetType("checkbox")
 		w:SetLabel("GOP history mode")
@@ -2023,11 +2034,11 @@
 		simple:AddChild(w)
 		grp:AddChild(simple)
 
-		simple = GUI:Create("SimpleGroup")
+		simple = AceGUI:Create("SimpleGroup")
 		simple:SetLayout("List")
 		simple:SetRelativeWidth(0.5)
 		for d,v in pairs(addon.debug) do
-			w = GUI:Create("CheckBoxSmallLabel")
+			w = AceGUI:Create("CheckBoxSmallLabel")
 			w:SetFullWidth(true)
 			w:SetType("checkbox")
 			w:SetLabel(d)
@@ -2045,7 +2056,7 @@
 		end
 		grp:AddChild(simple)
 
-		simple = GUI:Create("SimpleGroup")
+		simple = AceGUI:Create("SimpleGroup")
 		simple:SetLayout("Flow")
 		simple:SetRelativeWidth(0.85)
 		w = mkbutton("MidS-H", [[not exactly an Easter egg, with sound]])
@@ -2070,7 +2081,7 @@
 
 		grp:ResumeLayout()
 		container:AddChild(grp)
-		GUI:ClearFocus()
+		AceGUI:ClearFocus()
 		container:SetScroll(1000)  -- scrollframe's max value
 	end
 
@@ -2082,11 +2093,11 @@
 		adv_lower = adv_real
 	else
 		function adv_lower (container, specials)
-			local spacer = GUI:Create("Spacer")
+			local spacer = AceGUI:Create("Spacer")
 			spacer:SetFullWidth(true)
 			spacer:SetHeight(5)
 			container:AddChild(spacer)
-			local speedbump = GUI:Create("InteractiveLabel")
+			local speedbump = AceGUI:Create("InteractiveLabel")
 			speedbump:SetFullWidth(true)
 			speedbump:SetFontObject(GameFontHighlightLarge)
 			speedbump:SetImage[[Interface\DialogFrame\DialogAlertIcon]]
@@ -2097,7 +2108,7 @@
 				return addon:redisplay()
 			end)
 			container:AddChild(speedbump)
-			spacer = GUI:Create("Spacer")
+			spacer = AceGUI:Create("Spacer")
 			spacer:SetFullWidth(true)
 			spacer:SetHeight(5)
 			container:AddChild(spacer)
@@ -2110,10 +2121,10 @@
 		container:SetLayout("Fill")
 		local scroll, grp, w
 
-		scroll = GUI:Create("ScrollFrame")
+		scroll = AceGUI:Create("ScrollFrame")
 		scroll:SetLayout("Flow")
 
-		grp = GUI:Create("InlineGroup")
+		grp = AceGUI:Create("InlineGroup")
 		grp:SetLayout("Flow")
 		grp:SetFullWidth(true)
 		grp:SetTitle("User Options     [these are saved across sessions]")
@@ -2171,11 +2182,11 @@
 			[[See description under +Help -- Handy Tips -- Prescanning> for instructions.]])
 		grp:AddChild(w)
 
-		w = GUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(5) grp:AddChild(w)
+		w = AceGUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(5) grp:AddChild(w)
 
 		-- possible keybindings
 		do
-			local pair = GUI:Create("InlineGroup")
+			local pair = AceGUI:Create("InlineGroup")
 			pair:SetLayout("List")
 			pair:SetRelativeWidth(0.49)
 			pair:SetTitle("Keybinding for '/ouroloot'")
@@ -2202,7 +2213,7 @@
 
 		-- replacement for slashloot
 		do
-			local pair = GUI:Create("InlineGroup")
+			local pair = AceGUI:Create("InlineGroup")
 			pair:SetLayout("List")
 			pair:SetRelativeWidth(0.49)
 			pair:SetTitle('Synonyms for "/ouroloot"')
@@ -2241,7 +2252,7 @@
 
 		-- chatty disposition/assignment changes
 		do
-			local chatgroup = GUI:Create("InlineGroup")
+			local chatgroup = AceGUI:Create("InlineGroup")
 			chatgroup:SetLayout("List")
 			chatgroup:SetRelativeWidth(0.49)
 			chatgroup:SetTitle("Remote Changes Chat")
@@ -2254,7 +2265,7 @@
 				end)
 			toggle:SetFullWidth(true)
 			chatgroup:AddChild(toggle)
-			w = GUI:Create("Label")
+			w = AceGUI:Create("Label")
 			w:SetFullWidth(true)
 			w:SetText("This controls the output of the |cff00ffff'Be chatty on remote changes'|r option.  If this field is a number, it designates which chat frame to use.  Otherwise it is the Lua variable name of a frame with AddMessage capability.")
 			chatgroup:AddChild(w)
@@ -2292,7 +2303,7 @@
 		end
 
 		-- boss mod selection
-		w = GUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(2) grp:AddChild(w)
+		w = AceGUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(2) grp:AddChild(w)
 		do
 			local list = {}
 			local current
@@ -2314,7 +2325,7 @@
 		end
 
 		-- item filters
-		w = GUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(2) grp:AddChild(w)
+		w = AceGUI:Create("Spacer") w:SetFullWidth(true) w:SetHeight(2) grp:AddChild(w)
 		do
 			local warntext = "At least one of the items in the filter list was not in your game client's cache.  This is okay.  Just wait a few seconds, display some other Ouro Loot tab, and then display Options again."
 			local cache_warn, cache_warned = false, false
@@ -2343,7 +2354,7 @@
 				end
 			end
 
-			w = GUI:Create("EditBoxDropDown")
+			w = AceGUI:Create("EditBoxDropDown")
 			w:SetRelativeWidth(0.4)
 			w:SetText("Item filter")
 			w:SetEditBoxTooltip("Link items which should no longer be tracked.")
@@ -2367,12 +2378,12 @@
 			w:SetCallback("OnDropdownShown",do_warning)
 			grp:AddChild(w)
 
-			w = GUI:Create("Spacer")
+			w = AceGUI:Create("Spacer")
 			w:SetRelativeWidth(0.1)
 			w:SetHeight(2)
 			grp:AddChild(w)
 
-			w = GUI:Create("EditBoxDropDown")
+			w = AceGUI:Create("EditBoxDropDown")
 			w:SetRelativeWidth(0.4)
 			w:SetText("Vault items")
 			w:SetEditBoxTooltip("Link items which should be automatically marked as guild vault.")
@@ -2402,11 +2413,11 @@
 			local senders = table.concat(addon.sender_list.namesI,'\n')   -- sigh
 			-- If 39 other people in the raid are running this, the label will
 			-- explode... is it likely enough to care about?  No.
-			w = GUI:Create("Spacer")
+			w = AceGUI:Create("Spacer")
 			w:SetFullWidth(true)
 			w:SetHeight(20)
 			grp:AddChild(w)
-			w = GUI:Create("Label")
+			w = AceGUI:Create("Label")
 			w:SetRelativeWidth(0.4)
 			w:SetText(ITEM_QUALITY_COLORS[3].hex .."Echo from latest ping:|r\n"..senders)
 			grp:AddChild(w)
@@ -2452,9 +2463,9 @@
 	tabs:ReleaseChildren()
 	local spec = tabs:GetUserData("special buttons group")
 	spec:ReleaseChildren()
-	local h = GUI:Create("Heading")
+	local h = AceGUI:Create("Heading")
 	h:SetFullWidth(true)
-	h:SetText(_tabtexts[group].title)
+	h:SetText(gui.tabtexts[group].title)
 	spec:AddChild(h)
 	do
 		addon.sender_list.sort()
@@ -2513,7 +2524,7 @@
 	elseif not status then
 		opt_widget_type, opt_key, label, status = "Button", opt_widget_type, opt_key, label
 	end
-	local button = assert(GUI:Create(opt_widget_type))
+	local button = assert(AceGUI:Create(opt_widget_type))
 	if button.SetText then button:SetText(tostring(label)) end
 	status = _markup(status)
 	button:SetCallback("OnEnter", function() setstatus(status) end) -- maybe factor that closure out
@@ -2546,10 +2557,7 @@
 		self:zero_printed_fenceposts()
 	end
 
-	local display = GUI:Create("Frame")
-	if _d then
-		display:SetUserData("eoiST",_d)    -- warning! warning! kludge detected!
-	end
+	local display = AceGUI:Create("Frame")
 	_d = display
 	self.display = display
 	display:SetTitle(window_title)
@@ -2560,16 +2568,16 @@
 	display:SetCallback("OnClose", function(_display)
 		UIDROPDOWNMENU_SHOW_TIME = prev_fade_time 
 		hide_noobtips_frame()
-		_d = _display:GetUserData("eoiST")
+		_d = nil
 		self.display = nil
-		GUI:Release(_display)
+		AceGUI:Release(_display)
 		flib.clear()
 		collectgarbage()
 	end)
 
 	----- Right-hand panel
 	local rhs_width = 0.20
-	local control = GUI:Create("SimpleGroup")
+	local control = AceGUI:Create("SimpleGroup")
 	control:SetLayout("Flow")
 	control:SetRelativeWidth(rhs_width)
 	control.alignoffset = 25
@@ -2577,7 +2585,7 @@
 	local h,b
 
 	--- Main ---
-	h = GUI:Create("Heading")
+	h = AceGUI:Create("Heading")
 	h:SetFullWidth(true)
 	h:SetText("Main")
 	control:AddChild(h)
@@ -2625,7 +2633,7 @@
 	end)
 	control:AddChild(b)
 
-	b = GUI:Create("Spacer")
+	b = AceGUI:Create("Spacer")
 	b:SetFullWidth(true)
 	b:SetHeight(10)
 	control:AddChild(b)
@@ -2638,7 +2646,7 @@
 	   ...
 	 [ Load ]  [ Delete ]
 	]]
-	h = GUI:Create("Heading")
+	h = AceGUI:Create("Heading")
 	h:SetFullWidth(true)
 	h:SetText("Saved Texts")
 	control:AddChild(h)
@@ -2652,17 +2660,17 @@
 	control:AddChild(b)
 
 	do
-		local scontainer = GUI:Create("SimpleGroup")
+		local scontainer = AceGUI:Create("SimpleGroup")
 		scontainer:SetFullWidth(true)
 		scontainer:SetFullHeight(false)
 		scontainer:SetAutoAdjustHeight(false)
 		scontainer:SetHeight(40)  -- no relative height available anymore
 		scontainer:SetLayout("Fill")
-		local scroll = GUI:Create("ScrollFrame")
+		local scroll = AceGUI:Create("ScrollFrame")
 		scroll:SetLayout("List")
 		local saved = self:check_saved_table(--[[silent_on_empty=]]true)
 		if saved then for i,s in ipairs(saved) do
-			local il = GUI:Create("InteractiveLabel")
+			local il = AceGUI:Create("InteractiveLabel")
 			il:SetFullWidth(true)
 			il:SetText(s.name)
 			il:SetUserData("num",i)
@@ -2709,20 +2717,20 @@
 	b:SetDisabled(true)
 	control:AddChild(b)
 
-	b = GUI:Create("Spacer")
+	b = AceGUI:Create("Spacer")
 	b:SetFullWidth(true)
 	b:SetHeight(10)
 	control:AddChild(b)
 
 	-- Other stuff on right-hand side
-	local tab_specials = GUI:Create("SimpleGroup")
+	local tab_specials = AceGUI:Create("SimpleGroup")
 	tab_specials:SetLayout("Flow")
 	tab_specials:SetFullWidth(true)
 	control:AddChild(tab_specials)
 	control:ResumeLayout()
 
 	----- Left-hand group
-	local tabs = GUI:Create("TabGroup")
+	local tabs = AceGUI:Create("TabGroup")
 	tabs:SetLayout("Flow")
 	tabs.alignoffset = 25
 	local titletext_orig_fo = tabs.titletext:GetFontObject()
@@ -2735,7 +2743,7 @@
 	tabs:SetTabs(tabgroup_tabs)
 	tabs:SetCallback("OnGroupSelected", tabs_OnGroupSelected_func)
 	tabs:SetCallback("OnTabEnter", function(_tabs,event,value,tab)
-		setstatus(_tabtexts[value].desc)
+		setstatus(gui.tabtexts[value].desc)
 	end)
 	tabs:SetCallback("OnTabLeave", statusy_OnLeave)
 	tabs:SetUserData("special buttons group",tab_specials)
@@ -2749,10 +2757,12 @@
 	return display
 end
 
+-- Searches tab titles from left to right.
 function addon:OpenMainDisplayToTab (text, opt_arg)
 	text = '^'..text:lower()
-	for tab,v in pairs(_tabtexts) do
-		if v.title:lower():find(text) then
+	for _,tab in ipairs(gui.taborder) do
+		local v = gui.tabtexts[tab]
+		if v and v.title:lower():find(text) then
 			self:BuildMainDisplay(tab)
 			if opt_arg and tabs_CLI_special[tab] then
 				tabs_CLI_special[tab](opt_arg)
@@ -2947,7 +2957,7 @@
 		local entry = tremove(g_loot,boss_index)
 		tinsert(g_loot,data.rowindex,entry)
 		addon:_mark_boss_kill(data.rowindex)
-		data.display:GetUserData("eoiST"):OuroLoot_Refresh(data.rowindex)
+		gui.eoiST:OuroLoot_Refresh(data.rowindex)
 		dialog.data = nil   -- free up memory
 		addon:Print("Inserted %s %s at entry %d.", data.kind, data.name, data.rowindex)
 		return
@@ -3112,7 +3122,7 @@
 	OnAccept = function(dialog, data)
 		local name = dialog.usertext --editBox:GetText()
 		addon:reassign_loot ("local", data.index, name)
-		data.display:GetUserData("eoiST"):OuroLoot_Refresh(data.index)
+		gui.eoiST:OuroLoot_Refresh(data.index)
 	end,
 }