Merge branch 'master' of git.esoui.com:eso-SousChef-15

simenon [06-27-14 - 23:07]
Merge branch 'master' of git.esoui.com:eso-SousChef-15

Conflicts:
	Inventory.lua
	Provisioning.lua
	SousChef.lua
	SousChef.txt
	Utility.lua
	libs/LibSort/LibSort-1.0.lua
Filename
Provisioning.lua
SousChef.lua
SousChef.txt
Utility.lua
libs/LibAddonMenu-1.0/LibAddonMenu-1.0.lua
libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua
libs/LibAddonMenu-2.0/controls/button.lua
libs/LibAddonMenu-2.0/controls/checkbox.lua
libs/LibAddonMenu-2.0/controls/colorpicker.lua
libs/LibAddonMenu-2.0/controls/custom.lua
libs/LibAddonMenu-2.0/controls/description.lua
libs/LibAddonMenu-2.0/controls/dropdown.lua
libs/LibAddonMenu-2.0/controls/editbox.lua
libs/LibAddonMenu-2.0/controls/header.lua
libs/LibAddonMenu-2.0/controls/panel.lua
libs/LibAddonMenu-2.0/controls/slider.lua
libs/LibAddonMenu-2.0/controls/submenu.lua
libs/LibAddonMenu-2.0/controls/texture.lua
diff --git a/Provisioning.lua b/Provisioning.lua
index c0a8ce4..1d24859 100644
--- a/Provisioning.lua
+++ b/Provisioning.lua
@@ -2,25 +2,26 @@ local SousChef = SousChef
 local u = SousChef.Utility
 local m = SousChef.Media

+-- SousChef:UpdateProvisioningTable() adds the Quality checkbox to the provisioner window and overrides the filtering function to incorporate it
 function SousChef:UpdateProvisioningTable()
-    self.qualityCheckBox = SousChef_ControllerOnlyQuality
-
     local function OnFilterChanged()
         self.settings.qualityChecked = ZO_CheckButton_IsChecked(self.qualityCheckBox)
         PROVISIONER:DirtyRecipeTree()
     end
+	-- set up the quality checkbox
+    self.qualityCheckBox = SousChef_ControllerOnlyQuality
     self.qualityCheckBox:SetParent(ZO_ProvisionerTopLevel)
     ZO_CheckButton_SetLabelText(self.qualityCheckBox, "Quality")
     ZO_CheckButton_SetToggleFunction(self.qualityCheckBox, OnFilterChanged)
     ZO_CraftingUtils_ConnectCheckBoxToCraftingProcess(self.qualityCheckBox)
     ZO_CheckButton_SetCheckState(self.qualityCheckBox, self.settings.qualityChecked)

-    -- Nudge to the left
+    -- make room for the checkbox
     ZO_ProvisionerTopLevelHaveSkills:ClearAnchors()
     ZO_ProvisionerTopLevelHaveSkills:SetAnchor( LEFT, ZO_ProvisionerTopLevelHaveIngredients, RIGHT, 180 )

+	-- incorporate new checkbox into the filter
     SousChef.filter = PROVISIONER.DoesRecipePassFilter
-
     PROVISIONER.DoesRecipePassFilter =
         function(control, specialIngredientType, numCreatable, provisionerLevelReq, qualityReq)
             if ZO_CheckButton_IsChecked(self.qualityCheckBox) and qualityReq < 2 then return false end
@@ -28,7 +29,7 @@ function SousChef:UpdateProvisioningTable()
         end
 end

-
+-- this is our "Mark Recipe" button definition
 SousChef.ProvisioningButton=
 {{
     name = "Mark Recipe", -- or function that returns a name
@@ -38,9 +39,9 @@ SousChef.ProvisioningButton=
     visible = function(descriptor) return PROVISIONER:GetSelectedRecipeIndex() ~= nil end,
     alignment = KEYBIND_STRIP_ALIGN_RIGHT,
 }}
-
 local bGroup = SousChef.ProvisioningButton

+-- SousChef:HookRecipeTreeFunction() adds the "Mark Recipe" button to the keybind bar, and set the provisioning window to display checkmarks by marked recipes
 function SousChef:HookRecipeTreeFunction()
     if not KEYBIND_STRIP:HasKeybindButtonGroup(bGroup) then
         KEYBIND_STRIP:AddKeybindButtonGroup(bGroup)
@@ -67,8 +68,10 @@ function SousChef:HookRecipeTreeFunction()
     PROVISIONER:DirtyRecipeTree()
 end

+-- SousChef:getMarkedIcon(row, marked) adds the checkmark next to each marked line in the provisioning window
 function SousChef:getMarkedIcon(row, marked)
     local rankIcon = SousChef.slotLines[row:GetName()]
+	-- first, if the control for the checkmark doesn't exist, create it
     if(not rankIcon) then
         rankIcon =  WINDOW_MANAGER:CreateControl(row:GetName() .. "SousChef", row, CT_TEXTURE)
         SousChef.slotLines[row:GetName()] = rankIcon
@@ -80,7 +83,7 @@ function SousChef:getMarkedIcon(row, marked)
                                                 ClearTooltip(InformationTooltip)
                                             end)
     end
-
+	-- then, if the recipe is marked by someone, show who, and set the checkmark's alpha accordingly
     if marked then
         rankIcon:SetHandler("OnMouseEnter", function()
                                         InitializeTooltip(InformationTooltip, rankIcon, RIGHT, -15, 0)
@@ -95,6 +98,7 @@ function SousChef:getMarkedIcon(row, marked)
     return rankIcon
 end

+-- SousChef:AddNotifier(control, marked) shows or hides the checkmark next to each recipe according to whether it's marked by anyone
 function SousChef:AddNotifier(control, marked)
     local icon = self:getMarkedIcon(control, marked)
     if marked then
@@ -120,6 +124,10 @@ function SousChef:MarkRecipe()
         SousChef.settings.shoppingList[link][GetUnitName("player")] = true -- we're now marked
     else
         SousChef.settings.shoppingList[link][GetUnitName("player")] = nil -- we're now unmarked
+		-- if that was the last mark we just got rid of, then nil out the entire recipe entry
+		if u.TableLength(SousChef.settings.shoppingList[link]) == 0 then
+			SousChef.settings.shoppingList[link] = nil
+		end
     end

     PROVISIONER:DirtyRecipeTree()
@@ -206,7 +214,6 @@ function SousChef:RefreshRecipeTree()
                     local data = {
                         recipeListIndex = recipeListIndex,
                         recipeIndex = recipeIndex,
-
                         name = recipeName,
                         provisionerLevelReq = provisionerLevelReq,
                         qualityReq = qualityReq,
diff --git a/SousChef.lua b/SousChef.lua
index 61a33e1..b64472b 100644
--- a/SousChef.lua
+++ b/SousChef.lua
@@ -13,7 +13,7 @@ SousChef.Media = {}
 local SousChef = SousChef
 local u = SousChef.Utility

-local LAM = LibStub:GetLibrary("LibAddonMenu-1.0")
+local LAM = LibStub:GetLibrary("LibAddonMenu-2.0")

 local BACKPACK = ZO_PlayerInventoryBackpack
 local BANK = ZO_PlayerBankBackpack
@@ -30,13 +30,6 @@ SousChef.hookedDataFunction = nil

 local rowHandler = {}

-local function AddRecipe(Cookbook, link)
-	for _,v in pairs(Cookbook) do
-		if v == link then return end
-	end
-	table.insert(Cookbook, link)
-end
-
 local typeIconLookup = {
 	["Grilled"] = 9,
 	["Bread and Pies"] = 10,
@@ -46,12 +39,24 @@ local typeIconLookup = {
 	["Wine"] = 14
 }

+
+-- FUNCTIONS
+-- AddRecipe(Cookbook, link) adds the linked recipe to the player cookbook if it isn't there already.
+local function AddRecipe(Cookbook, link)
+	for _,v in pairs(Cookbook) do
+		if v == link then return end
+	end
+	table.insert(Cookbook, link)
+end
+
+-- RefreshViews() incorporates changes to the rendered inventory if it is visible.
 function SousChef:RefreshViews()
 	ZO_ScrollList_RefreshVisible(BACKPACK)
 	ZO_ScrollList_RefreshVisible(BANK)
 	ZO_ScrollList_RefreshVisible(GUILD_BANK)
 end

+-- ParseRecipes() goes through the player's known recipes and records their info.
 function SousChef:ParseRecipes()
 	local lists = GetNumRecipeLists()

@@ -59,163 +64,242 @@ function SousChef:ParseRecipes()
 		local name, count = GetRecipeListInfo(listIndex)
 		for recipeIndex = 1, count do
 			if GetRecipeInfo(listIndex, recipeIndex) then
-				-- Store the recipes known
+				-- Store the recipes known:
 				local recipeName = u.CleanString((GetRecipeResultItemInfo(listIndex, recipeIndex)))
+				-- in this character's cookbook
                 SousChef.Cookbook[recipeName] = true
+				-- and in the settings, so other characters can see what recipes this character knows
 				if not SousChef.settings.Cookbook[recipeName] then
                     SousChef.settings.Cookbook[recipeName] = {}
 				end
                 SousChef.settings.Cookbook[recipeName][GetUnitName("player")] = true
+
+				-- now record information about the recipe's ingregients
 				local _, _, ingredientCount, level, _, specialType = GetRecipeInfo(listIndex, recipeIndex)
-				-- Lookup for the recipe result count
+				-- store the recipe's index numbers and number of ingredients
+				-- XXX: why does this table get indexed with the formatted item name, but the Pantry table uses the whole link?
 				SousChef.CookbookIndex[zo_strformat(SI_TOOLTIP_ITEM_NAME, GetRecipeResultItemLink(listIndex, recipeIndex, LINK_STYLE_BRACKETS))] =
 					{listIndex = listIndex, recipeIndex = recipeIndex, numIngredients = ingredientCount}
-
+				-- now, for every ingredient in the current recipe...
 				for ingredientIndex = 1, ingredientCount do
 					local link = u.GetItemID(GetRecipeIngredientItemLink(listIndex, recipeIndex, ingredientIndex, LINK_STYLE_DEFAULT))
 					-- Store the fact that the ingredient is used
                     if ingredientIndex < 3 or not SousChef.settings.showSpecialIngredients then
+						-- if this isn't a special ingredient, or if the user doesn't want special ingredients marked as special, just record the highest-tier recipe this ingredient appears in
+						-- for this character,
                         SousChef.Pantry[link] = math.max(level, SousChef.Pantry[link] or 0)
+						-- and across characters
                         SousChef.settings.Pantry[link] = math.max(level, SousChef.Pantry[link] or 0)
                     else
+						-- if this is a special ingredient, record:
+						-- 						type icon number if user wants type icons,								7 if ingredient is a flavoring,																8 otherwise (ie, ingredient is a spice)
                         SousChef.Pantry[link] = SousChef.settings.typeIcon and typeIconLookup[name] or specialType == PROVISIONER_SPECIAL_INGREDIENT_TYPE_FLAVORING and 7 or 8
                         SousChef.settings.Pantry[link] = SousChef.settings.typeIcon and typeIconLookup[name] or specialType == PROVISIONER_SPECIAL_INGREDIENT_TYPE_FLAVORING and 7 or 8
                     end
-					-- Store the recipe it's used in
+					-- Store the recipe it's used in to the character reverseCookbook
 					if not SousChef.ReverseCookbook[link] then SousChef.ReverseCookbook[link] = {} end
 					AddRecipe(SousChef.ReverseCookbook[link], zo_strformat(SI_TOOLTIP_ITEM_NAME, GetRecipeResultItemLink(listIndex, recipeIndex, LINK_STYLE_BRACKETS)))
-					-- Store the global reference
+					-- ...and to the account-wide reverseCookbook
 					if not SousChef.settings.ReverseCookbook[link] then SousChef.settings.ReverseCookbook[link] = {} end
 					AddRecipe(SousChef.settings.ReverseCookbook[link], zo_strformat(SI_TOOLTIP_ITEM_NAME, GetRecipeResultItemLink(listIndex, recipeIndex, LINK_STYLE_BRACKETS)))
 				end
 			end
-
 		end
 	end
 	SousChef:RefreshViews()
 end

+-- SousChefCreateSettings() creates the configuration menu for the add-on
 local function SousChefCreateSettings()
-	local panel = LAM:CreateControlPanel("SousChefMenu", "Sous Chef")
+	local panelData = {
+		type = "panel",
+		name = "Sous Chef",
+		displayName = "Sous Chef",
+		author = "Wobin & KatKat42 & CrazyDutchGuy",
+		version = "1.18",
+		registerForRefresh = true,
+	}

-	LAM:AddHeader(panel, "SousChef_General", "Recipe Options")
-
-    LAM:AddCheckbox(panel, "processRecipes", "Have Sous Chef check for recipes", "Non English clients may want to untick this if the experimental matching isn't sufficient",
-                        function() return SousChef.settings.processRecipes end,
-                        function(value)
-                            SousChef.settings.processRecipes = not SousChef.settings.processRecipes
-                            if not SousChef.settings.processRecipes then
-                                ZO_Options_SetOptionInactive(markLearnt)
-                                ZO_Options_SetOptionInactive(markAltKnows)
-                                ZO_Options_SetOptionInactive(showAltKnows)
-                                ZO_Options_SetOptionInactive(experimental)
-                            else
-                                ZO_Options_SetOptionActive(markLearnt)
-                                ZO_Options_SetOptionActive(markAltKnows)
-                                ZO_Options_SetOptionActive(showAltKnows)
-                                ZO_Options_SetOptionActive(experimental)
-                            end
-                        end)
-  	LAM:AddDropdown(panel, "markLearnt", "Mark if recipes are ",
-						"How do you want Sous Chef to indicate your knowledge of a recipe?",
-						{"known", "unknown"}, function() return SousChef.settings.checkKnown end,
-						function(valueString) SousChef.settings.checkKnown = valueString end)
-	LAM:AddCheckbox(panel, "markAltKnows", "Alternate Character Check", "Indicate if an alt knows the recipe on unknown recipes. Will only work if the above setting is set to 'unknown'",
-						function() return SousChef.settings.markAlt end,
-						function(value) SousChef.settings.markAlt = not SousChef.settings.markAlt SousChef:RefreshViews() end)
-	LAM:AddCheckbox(panel, "showAltKnows", "Alternate Character Recipe Knowledge", "Show rank indicators on alts for all recipe knowledge of all alternate characters",
-						function() return SousChef.settings.showAltKnowledge end,
-						function(value) SousChef.settings.showAltKnowledge = not SousChef.settings.showAltKnowledge SousChef:RefreshViews() end)
-    LAM:AddCheckbox(panel, "experimental", "Use the experimental recipe matcher", [[Currently Sous Chef cannot match reliably in other languages. This will attempt to match as best it can by stripping out common prepositions so that the recipe and result match better.
+	LAM:RegisterAddonPanel("SousChefSettings", panelData)

-    (NOTE: please file a typo bug report with Zenimax if you find recipes that don't match the results exactly, with the recipe/result names)]],
-                        function() return SousChef.settings.experimentalMatch end,
-                        function(value) SousChef.settings.experimentalMatch = not SousChef.settings.experimentalMatch end)
-	LAM:AddCheckbox(panel, "sortProvisioning", "Sort the Provisioning Table", "Will sort the provisioning table by Rank, Quality, then Name",
-						function() return SousChef.settings.sortProvisioningTable end,
-						function(value) SousChef.settings.sortProvisioningTable = not SousChef.settings.sortProvisioningTable end, true, "Will not sort within ranks, eg 1-20 will be somewhat jumbled")
+	local optionsMenu = {
+		[1] = {
+			type = "header",
+			name = "Recipe Options",
+			width = "full",
+		},
+
+		[2] = {
+			type = "checkbox",
+			name = "Have Sous Chef check for recipes",
+			tooltip = "Non English clients may want to untick this if the experimental matching isn't sufficient",
+			getFunc = function() return SousChef.settings.processRecipes end,
+			setFunc = function(value)
+								SousChef.settings.processRecipes = not SousChef.settings.processRecipes
+							end,
+			width = "full",
+		},
+
+		[3] = {
+			type = "dropdown",
+			name = "Mark if recipes are ",
+			tooltip = "How do you want Sous Chef to indicate your knowledge of a recipe?",
+			choices = {"known", "unknown"},
+			getFunc = function() return SousChef.settings.checkKnown end,
+			setFunc = function(valueString) SousChef.settings.checkKnown = valueString end,
+			disabled = function() return not SousChef.settings.processRecipes end,
+		},
+
+		[4] = {
+			type = "checkbox",
+			name = "Alternate Character Check",
+			tooltip = "Indicate if an alt knows the recipe on unknown recipes. Will only work if the above setting is set to 'unknown'",
+			getFunc = function() return SousChef.settings.markAlt end,
+			setFunc = function(value) SousChef.settings.markAlt = not SousChef.settings.markAlt SousChef:RefreshViews() end,
+			disabled = function() return not SousChef.settings.processRecipes end,
+		},
+
+		[5] = {
+			type = "checkbox",
+			name = "Alternate Character Recipe Knowledge",
+			tooltip = "Show in tooltips which characters know a recipe",
+			getFunc = function() return SousChef.settings.showAltKnowledge end,
+			setFunc = function(value) SousChef.settings.showAltKnowledge = not SousChef.settings.showAltKnowledge SousChef:RefreshViews() end,
+			disabled = function() return not SousChef.settings.processRecipes end,
+		},
+
+		[6] = {
+			type = "checkbox",
+			name = "Use the Experimental Recipe Matcher",
+			tooltip = "Currently Sous Chef cannot match reliably in other languages. This will attempt to match as best it can by stripping out common prepositions so that the recipe and result match better. (NOTE: please file a typo bug report with Zenimax if you find recipe names that don't match their results exactly)",
+			getFunc = function() return SousChef.settings.experimentalMatch end,
+			setFunc = function(value) SousChef.settings.experimentalMatch = not SousChef.settings.experimentalMatch end,
+			disabled = function() return not SousChef.settings.processRecipes end,
+		},
+
+		[7] = {
+			type = "checkbox",
+			name = "Sort the Provisioning Table",
+			tooltip = "Will sort the provisioning table by Rank, Quality, then Name (but not by required level)",
+			getFunc = function() return SousChef.settings.sortProvisioningTable end,
+			setFunc = function(value) SousChef.settings.sortProvisioningTable = not SousChef.settings.sortProvisioningTable end,
+		},
+
+		[8] = {
+			type = "header",
+			name = "Tooltip Options",
+			width = "full",
+		},

-	LAM:AddHeader(panel, "SousChef_Tooltip", "Tooltip Options")
+		[9] = {
+			type = "checkbox",
+			name = "Show Sous Chef in Ingredient tooltips after Mouse Click",
+			tooltip = "Only show Sous Chef information in ingredient tooltips after a mouse click, to save space",
+			warning = "Requires UI Reload",
+			getFunc = function() return SousChef.settings.showOnClick end,
+			setFunc = function(value) SousChef.settings.showOnClick = not SousChef.settings.showOnClick end,
+		},

-	LAM:AddCheckbox(panel, "showOnClickOnly", "Show in Tooltip only on click", "Only show details on a left click to save space (requires /reloadui)",
-						function() return SousChef.settings.showOnClick end,
-						function(value) SousChef.settings.showOnClick = not SousChef.settings.showOnClick end, true, "Requires /reloadui")
-	LAM:AddCheckbox(panel, "showResultCount", "Show recipe result counts", "Show how many of a particular recipe can be made next to the link in the Ingredient tooltip",
-						function() return SousChef.settings.showCounts end,
-						function(value) SousChef.settings.showCounts = not SousChef.settings.showCounts end)
-	LAM:AddCheckbox(panel, "markAltIngKnows", "Alternate Character Ingredient Knowledge", "Indicate if an alt uses the ingredients",
-						function() return SousChef.settings.showAltIngredientKnowledge end,
-						function(value) SousChef.settings.showAltIngredientKnowledge = not SousChef.settings.showAltIngredientKnowledge SousChef:RefreshViews() end)
-
-	LAM:AddHeader(panel, "SousChef_Indicator", "Indicator Options")
-	LAM:AddCheckbox(panel, "useBold", "Use bolder icons", "Swap out rank icon to a more flat display",
-						function() return SousChef.settings.boldIcon end,
-						function(value) SousChef.settings.boldIcon = not SousChef.settings.boldIcon SousChef:RefreshViews() end)
-	LAM:AddCheckbox(panel, "specialIngredients", "Show special ingredients", "Indicate if an ingredient is considered a Spice/Flavour rather than the tier of the max ranked recipe you have that uses it",
-						function() return SousChef.settings.showSpecialIngredients end,
-						function(value)
+		[10] = {
+			type = "checkbox",
+			name = "Show Recipe Result Counts",
+			tooltip = "Show how many of each recipe can be made in the Ingredient tooltip",
+			getFunc = function() return SousChef.settings.showCounts end,
+			setFunc = function(value) SousChef.settings.showCounts = not SousChef.settings.showCounts end,
+		},
+
+		[11] = {
+			type = "checkbox",
+			name = "Alternate Character Ingredient Knowledge",
+			tooltip = "Indicte if an alt uses the ingredients",
+			getFunc = function() return SousChef.settings.showAltIngredientKnowledge end,
+			setFunc = function(value) SousChef.settings.showAltIngredientKnowledge = not SousChef.settings.showAltIngredientKnowledge SousChef:RefreshViews() end,
+		},
+
+		[12] = {
+			type = "header",
+			name = "Indicator Options",
+			width = "full",
+		},
+
+		[13] = {
+			type = "checkbox",
+			name = "Use bolder icons",
+			tooltip = "Swap out rank icons to a more flat display",
+			getFunc = function() return SousChef.settings.boldIcon end,
+			setFunc = function(value) SousChef.settings.boldIcon = not SousChef.settings.boldIcon SousChef:RefreshViews() end,
+		},
+
+		[14] = {
+			type = "checkbox",
+			name = "Show Special Ingredients",
+			tooltip = "If an ingredient is considered a Spice/Flavour, indicate that rather than the tier of the highest tier recipe that uses it",
+			getFunc = function() return SousChef.settings.showSpecialIngredients end,
+			setFunc = function(value)
 							SousChef.settings.showSpecialIngredients = not SousChef.settings.showSpecialIngredients
-							if SousChef.settings.showSpecialIngredients then
-								ZO_Options_SetOptionActive(typeIcons)
-							else
-								ZO_Options_SetOptionInactive(typeIcons)
-							end
 							SousChef.ParseRecipes()
-						end)
-	LAM:AddCheckbox(panel, "typeIcons", "Show special ingredients as types", "Use the icon for the type of food it's a special ingredient for, eg Grilled",
-						function() return SousChef.settings.typeIcon end,
-						function(value) SousChef.settings.typeIcon = not SousChef.settings.typeIcon SousChef.ParseRecipes() SousChef:RefreshViews() end)
-	LAM:AddColorPicker(panel, "setColour", "Indicator colour",
-						"Allows you to set the colour of the indicator",
-						function() return SousChef.settings.colour[1], SousChef.settings.colour[2], SousChef.settings.colour[3] end,
-						function(r,g,b) SousChef.settings.colour[1] = r; SousChef.settings.colour[2] = g; SousChef.settings.colour[3] = b SousChef:RefreshViews() end)
-	LAM:AddColorPicker(panel, "setShoppingListColour", "Shopping List indicator colour",
-						"Allows you to set the colour of the indicator for ingredients in your Shopping List",
-						function() return SousChef.settings.shoppingColour[1], SousChef.settings.shoppingColour[2], SousChef.settings.shoppingColour[3] end,
-						function(r,g,b) SousChef.settings.shoppingColour[1] = r; SousChef.settings.shoppingColour[2] = g; SousChef.settings.shoppingColour[3] = b SousChef:RefreshViews() end)
-	LAM:AddCheckbox(panel, "onlyShoppingList", "Only show Shopping List ingredients", "Only mark ingredients on your Shopping List",
-						function() return SousChef.settings.onlyShowShopping end,
-						function(value) SousChef.settings.onlyShowShopping = not SousChef.settings.onlyShowShopping SousChef:RefreshViews() end)
-	LAM:AddCheckbox(panel, "sortKnownInventoryIngredients", "Sort ingredients in the inventory", "Will sort known ingredients by rank",
-						function() return SousChef.settings.sortKnownIngredients end,
-						function(value)
-							SousChef.settings.sortKnownIngredients = not SousChef.settings.sortKnownIngredients
-							if not SousChef.settings.sortKnownIngredients then
-								SousChef.UnregisterSort()
-							else
-								SousChef.SetupSort()
-							end
-						end)
+						end,
+		},
+
+		[15] = {
+			type = "checkbox",
+			name = "Show Special Ingredient Types",
+			tooltip = "Use the icon for the type of food it's a special ingredient for, eg Grilled",
+			getFunc = function() return SousChef.settings.typeIcon end,
+			setFunc = function(value) SousChef.settings.typeIcon = not SousChef.settings.typeIcon SousChef.ParseRecipes() SousChef:RefreshViews() end,
+			disabled = function() return not SousChef.settings.showSpecialIngredients end,
+		},
+
+		[16] = {
+			type = "colorpicker",
+			name = "Indicator Colour",
+			tooltip = "Allows you to set the colour of the recipe indicator",
+			getFunc = function() return SousChef.settings.colour[1], SousChef.settings.colour[2], SousChef.settings.colour[3] end,
+			setFunc = function(r,g,b) SousChef.settings.colour[1] = r; SousChef.settings.colour[2] = g; SousChef.settings.colour[3] = b; SousChef:RefreshViews() end,
+		},
+
+		[17] = {
+			type = "colorpicker",
+			name = "Shopping List Indicator Colour",
+			tooltip = "Allows you to set the colour of hte indicator for ingredients in your Shopping List",
+			getFunc = function() return SousChef.settings.shoppingColour[1], SousChef.settings.shoppingColour[2], SousChef.settings.shoppingColour[3] end,
+			setFunc = function(r,g,b) SousChef.settings.shoppingColour[1] = r; SousChef.settings.shoppingColour[2] = g; SousChef.settings.shoppingColour[3] = b; SousChef:RefreshViews() end,
+		},
+
+		[18] = {
+			type = "checkbox",
+			name = "Only Mark Shopping List Ingredients",
+			tooltip = "Only mark ingredients on your Shopping List",
+			getFunc = function() return SousChef.settings.onlyShowShopping end,
+			setFunc = function(value) SousChef.settings.onlyShowShopping = not SousChef.settings.onlyShowShopping SousChef:RefreshViews() end,
+		},
+
+		[19] = {
+			type = "checkbox",
+			name = "Sort Ingredients in Inventory",
+			tooltip = "Will sort known ingredients by rank",
+			getFunc = function() return SousChef.settings.sortKnownIngredients end,
+			setFunc = function(value)
+								SousChef.settings.sortKnownIngredients = not SousChef.settings.sortKnownIngredients
+								if not SousChef.settings.sortKnownIngredients then
+									SousChef.UnregisterSort()
+								else
+									SousChef.SetupSort()
+								end
+							end,
+		},
+	}
+
+	LAM:RegisterOptionControls("SousChefSettings", optionsMenu)

 	ZO_CreateStringId("SI_BINDING_NAME_SC_MARK_RECIPE", "Mark Recipe")
-
-    if not SousChef.settings.processRecipes then
-        ZO_Options_SetOptionInactive(markLearnt)
-        ZO_Options_SetOptionInactive(markAltKnows)
-        ZO_Options_SetOptionInactive(showAltKnows)
-        ZO_Options_SetOptionInactive(experimental)
-    else
-        ZO_Options_SetOptionActive(markLearnt)
-        ZO_Options_SetOptionActive(markAltKnows)
-        ZO_Options_SetOptionActive(showAltKnows)
-        ZO_Options_SetOptionActive(experimental)
-    end
-
-    if SousChef.settings.showSpecialIngredients then
-		ZO_Options_SetOptionActive(typeIcons)
-	else
-		ZO_Options_SetOptionInactive(typeIcons)
-	end
 end

-
-
+-- SousChef_Loaded(eventCode, addOnName) runs when the EVENT_ADD_ON_LOADED event fires.
 local function SousChef_Loaded(eventCode, addOnName)
+	if(addOnName ~= "SousChef") then return end

-	if(addOnName ~= "SousChef") then
-        return
-    end
-
+	-- default config settings
 	local defaults = {
 		watching = true,
 		checkKnown = "unknown",
@@ -242,6 +326,7 @@ local function SousChef_Loaded(eventCode, addOnName)
         sortKnownIngredients = false
 	}

+	-- Fetch the saved variables
     SousChef.settings = ZO_SavedVars:NewAccountWide("SousChef_Settings", 9, GetCVar("Language.2"), defaults)

 	local function tablelength(T)
@@ -250,35 +335,40 @@ local function SousChef_Loaded(eventCode, addOnName)
 		return count
 	end

+	-- define some slash commands
 	SLASH_COMMANDS['/scstats'] = function()
 		d("Number of recipes known: ".. tablelength(SousChef.settings.Cookbook))
 		d("Number of ingredients tracked: "..tablelength(SousChef.settings.Pantry))
 	end
+	SLASH_COMMANDS['/sciadd'] = SousChef.AddRecipeToIgnoreList
+	SLASH_COMMANDS['/sciremove'] = SousChef.RemoveRecipeFromIgnoreList
+	SLASH_COMMANDS['/scilist'] = SousChef.ListIgnoredRecipes

+	-- initialize the configuration menu
 	SousChefCreateSettings()

+	-- parse the recipes this character knows, in a second
 	zo_callLater(SousChef.ParseRecipes, 1000)

-	SLASH_COMMANDS['/sciadd'] = SousChef.AddRecipeToIgnoreList
-	SLASH_COMMANDS['/sciremove'] = SousChef.RemoveRecipeFromIgnoreList
-	SLASH_COMMANDS['/scilist'] = SousChef.ListIgnoredRecipes
-
 	SousChef:UpdateProvisioningTable()
 	SousChef:HookGetRecipeInfo()

 	if SousChef.settings.sortKnownIngredients then SousChef.SetupSort() end

-	-- Now we want to hook into the function that sets the details on the inventory slot
+	-- Now we register for some events, and hook into the function that sets the details on the inventory slot
 	zo_callLater(SousChef.HookEvents, 3000)
 end

-
-function SousChef.HookEvents()
+-- HookEvents() registers the add-on for some events
+function SousChef.HookEvents()
+	-- let us know if we're opening a trading house, so we can look at its contents
 	EVENT_MANAGER:RegisterForEvent("SousChefTrading", EVENT_TRADING_HOUSE_RESPONSE_RECEIVED, SousChef.HookTrading)
+	-- let us know if we've learned a new recipe, so we can integrate it into our cookbook
 	EVENT_MANAGER:RegisterForEvent("SousChefLearnt", EVENT_RECIPE_LEARNED, SousChef.ParseRecipes)
+	-- let us know when we open a crafting station, so we can sort the recipe tree
     EVENT_MANAGER:RegisterForEvent("SousChefProvi", EVENT_CRAFTING_STATION_INTERACT, function(...) SousChef:HookRecipeTree(...) end)
     EVENT_MANAGER:RegisterForEvent("SousChefProviEnd", EVENT_END_CRAFTING_STATION_INTERACT, function() SousChef:UnhookRecipeTree() end)
-
+    -- and finally, hook the opening of the inventory, bank, guild bank, and loot windows so we can add icons and hook the tooltips
     SousChef.HookInventory()
 end

diff --git a/SousChef.txt b/SousChef.txt
index 8839dfc..36b08e6 100644
--- a/SousChef.txt
+++ b/SousChef.txt
@@ -1,12 +1,25 @@
-## Title: |cFFFFB0Sous Chef|r by |c00C000CrazyDutchGuy & Wobin|r
-## Author: CrazyDutchGuy & Wobin
-## Version: v2.1
+## Title: |cFFFFB0Sous Chef|r by |c00C000Wobin & CrazyDutchGuy & KatKat42|r
+## Author: Wobin & CrazyDutchGuy & KatKat42
+## Version: @project-version@
 ## APIVersion: 100007
 ## SavedVariables: SousChef_Settings
-## OptionalDependsOn: LibStub LibAddonMenu-1.0 LibSort-1.0

 libs\LibStub\LibStub.lua
-libs\LibAddonMenu-1.0\LibAddonMenu-1.0.lua
+
+libs\LibAddonMenu-2.0\LibAddonMenu-2.0.lua
+libs\LibAddonMenu-2.0\controls\panel.lua
+libs\LibAddonMenu-2.0\controls\submenu.lua
+libs\LibAddonMenu-2.0\controls\button.lua
+libs\LibAddonMenu-2.0\controls\checkbox.lua
+libs\LibAddonMenu-2.0\controls\colorpicker.lua
+libs\LibAddonMenu-2.0\controls\custom.lua
+libs\LibAddonMenu-2.0\controls\description.lua
+libs\LibAddonMenu-2.0\controls\dropdown.lua
+libs\LibAddonMenu-2.0\controls\editbox.lua
+libs\LibAddonMenu-2.0\controls\header.lua
+libs\LibAddonMenu-2.0\controls\slider.lua
+libs\LibAddonMenu-2.0\controls\texture.lua
+
 libs\LibSort\LibSort-1.0.lua

 SousChef.lua
diff --git a/Utility.lua b/Utility.lua
index 23fbf6f..51e253b 100644
--- a/Utility.lua
+++ b/Utility.lua
@@ -1,4 +1,3 @@
-
 local SousChef = SousChef

 local u =  SousChef.Utility
@@ -86,4 +85,13 @@ function u.MatchInIgnoreList(name)
         if u.CleanString(recipe) == name then return true end
     end
     return false
+end
+
+function u.TableLength(t)
+	if type(t) ~= "table" then return #t end
+	local n = 0
+	for k, v in pairs(t) do
+		n = n + 1
+	end
+	return n
 end
\ No newline at end of file
diff --git a/libs/LibAddonMenu-1.0/LibAddonMenu-1.0.lua b/libs/LibAddonMenu-1.0/LibAddonMenu-1.0.lua
deleted file mode 100644
index e09debc..0000000
--- a/libs/LibAddonMenu-1.0/LibAddonMenu-1.0.lua
+++ /dev/null
@@ -1,485 +0,0 @@
-local MAJOR, MINOR = "LibAddonMenu-1.0", 7
-local lam, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
-if not lam then return end	--the same or newer version of this lib is already loaded into memory
-
---UPVALUES--
-lam.lastAddedControl = {}
-local lastAddedControl = lam.lastAddedControl
-local wm = GetWindowManager()
-local strformat = string.format
-local tostring = tostring
-local round = zo_round
-local optionsWindow = ZO_OptionsWindowSettingsScrollChild
-local openSubMenu
-
-
-function lam:CreateControlPanel(controlPanelID, controlPanelName)
-	local panelID
-
-	if _G[controlPanelID] then
-		panelID = _G[controlPanelID]
-		return panelID
-	end
-
-	ZO_OptionsWindow_AddUserPanel(controlPanelID, controlPanelName)
-
-	--disables Defaults button because we don't need it, but keybind still works :/ ...
-	panelID = _G[controlPanelID]
-
-	return panelID
-end
-
-function lam:AddHeader(panelID, controlName, text)
-	local isSubMenu = type(panelID) == "userdata"
-	--local header = wm:CreateControlFromVirtual(controlName, optionsWindow, lastAddedControl[panelID] and "ZO_Options_SectionTitle_WithDivider" or "ZO_Options_SectionTitle")
-	local header = wm:CreateControlFromVirtual(controlName, isSubMenu and panelID or optionsWindow, lastAddedControl[panelID] and "ZO_Options_SectionTitle_WithDivider" or "ZO_Options_SectionTitle")
-	if lastAddedControl[panelID] then
-		header:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 15)
-	else
-		header:SetAnchor(TOPLEFT)
-		if not isSubMenu then
-			header:SetHandler("OnShow", function()
-				ZO_OptionsWindowResetToDefaultButton:SetEnabled(false)
-				ZO_OptionsWindowResetToDefaultButton:SetKeybindEnabled(false)
-				ZO_OptionsWindowResetToDefaultButton:SetHidden(true)
-				--ZO_OptionsWindowResetToDefaultButton:SetAlpha(0)
-			end)
-			header:SetHandler("OnHide", function()
-				ZO_OptionsWindowResetToDefaultButton:SetEnabled(true)
-				ZO_OptionsWindowResetToDefaultButton:SetKeybindEnabled(true)
-				ZO_OptionsWindowResetToDefaultButton:SetHidden(false)
-				--ZO_OptionsWindowResetToDefaultButton:SetAlpha(1)
-			end)
-		end
-	end
-	header.controlType = OPTIONS_SECTION_TITLE
-	header.panel = isSubMenu and panelID.panel or panelID
-	header.text = text
-
-	ZO_OptionsWindow_InitializeControl(header)
-	if isSubMenu then header:SetParent(panelID) end
-
-	lastAddedControl[panelID] = header
-
-	return header
-end
-
-
---To-Do list:
---extra sub-options window out to the right?? (or maybe addon list?)
---find alternatives to handler hooks
-
-function lam:AddSlider(panelID, controlName, text, tooltip, minValue, maxValue, step, getFunc, setFunc, warning, warningText)
-	local isSubMenu = type(panelID) == "userdata"
-	local slider = wm:CreateControlFromVirtual(controlName, isSubMenu and panelID or optionsWindow, "ZO_Options_Slider")
-	slider:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 6)
-	slider.controlType = OPTIONS_SLIDER
-	slider.system = SETTING_TYPE_UI
-	slider.panel = isSubMenu and panelID.panel or panelID
-	slider.text = text
-	slider.tooltipText = tooltip
-	slider.showValue = true
-	slider.showValueMin = minValue
-	slider.showValueMax = maxValue
-	local range = maxValue - minValue
-	local slidercontrol = slider:GetNamedChild("Slider")
-	local slidervalue = slider:GetNamedChild("ValueLabel")
-	slidercontrol:SetValueStep(1/range * step)
-	slider:SetHandler("OnShow", function()
-			local curValue = getFunc()
-			slidercontrol:SetValue((curValue - minValue)/range)
-			slidervalue:SetText(tostring(curValue))
-		end)
-	slidercontrol:SetHandler("OnValueChanged", function (self, value)
-			self:SetValue(value)
-			value = round(value*range + minValue)
-			slidervalue:SetText(strformat("%d", value))
-		end)
-	slidercontrol:SetHandler("OnSliderReleased", function(self, value)
-			value = round(value*range + minValue)
-			setFunc(value)
-		end)
-
-	if warning then
-		slider.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", slider, "ZO_Options_WarningIcon")
-		slider.warning:SetAnchor(RIGHT, slidercontrol, LEFT, -5, 0)
-		slider.warning.tooltipText = warningText
-	end
-
-	ZO_OptionsWindow_InitializeControl(slider)
-	if isSubMenu then slider:SetParent(panelID) end
-
-	lastAddedControl[panelID] = slider
-
-	return slider
-end
-
-function lam:AddDropdown(panelID, controlName, text, tooltip, validChoices, getFunc, setFunc, warning, warningText)
-	local isSubMenu = type(panelID) == "userdata"
-	local dropdown = wm:CreateControlFromVirtual(controlName, isSubMenu and panelID or optionsWindow, "ZO_Options_Dropdown")
-	dropdown:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 6)
-	dropdown.controlType = OPTIONS_DROPDOWN
-	dropdown.system = SETTING_TYPE_UI
-	dropdown.panel = isSubMenu and panelID.panel or panelID
-	dropdown.text = text
-	dropdown.tooltipText = tooltip
-	dropdown.valid = validChoices
-	local dropmenu = ZO_ComboBox_ObjectFromContainer(GetControl(dropdown, "Dropdown"))
-	local setText = dropmenu.m_selectedItemText.SetText
-	local selectedName
-	ZO_PreHookHandler(dropmenu.m_selectedItemText, "OnTextChanged", function(self)
-			if dropmenu.m_selectedItemData then
-				selectedName = dropmenu.m_selectedItemData.name
-				setText(self, selectedName)
-				setFunc(selectedName)
-			end
-		end)
-	dropdown:SetHandler("OnShow", function()
-			dropmenu:SetSelectedItem(getFunc())
-		end)
-
-	if warning then
-		dropdown.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", dropdown, "ZO_Options_WarningIcon")
-		dropdown.warning:SetAnchor(RIGHT, dropdown:GetNamedChild("Dropdown"), LEFT, -5, 0)
-		dropdown.warning.tooltipText = warningText
-	end
-
-	ZO_OptionsWindow_InitializeControl(dropdown)
-	if isSubMenu then dropdown:SetParent(panelID) end
-
-	lastAddedControl[panelID] = dropdown
-
-	return dropdown
-end
-
-function lam:AddCheckbox(panelID, controlName, text, tooltip, getFunc, setFunc, warning, warningText)
-	local isSubMenu = type(panelID) == "userdata"
-	local checkbox = wm:CreateControlFromVirtual(controlName, isSubMenu and panelID or optionsWindow, "ZO_Options_Checkbox")
-	checkbox:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 6)
-	checkbox.controlType = OPTIONS_CHECKBOX
-	checkbox.system = SETTING_TYPE_UI
-	checkbox.settingId = _G[strformat("SETTING_%s", controlName)]
-	checkbox.panel = isSubMenu and panelID.panel or panelID
-	checkbox.text = text
-	checkbox.tooltipText = tooltip
-
-	local checkboxButton = checkbox:GetNamedChild("Checkbox")
-
-	ZO_PreHookHandler(checkbox, "OnShow", function()
-			checkboxButton:SetState(getFunc() and 1 or 0)
-			checkboxButton:toggleFunction(getFunc())
-		end)
-	ZO_PreHookHandler(checkboxButton, "OnClicked", function() setFunc(not getFunc()) end)
-
-	if warning then
-		checkbox.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", checkbox, "ZO_Options_WarningIcon")
-		checkbox.warning:SetAnchor(RIGHT, checkboxButton, LEFT, -5, 0)
-		checkbox.warning.tooltipText = warningText
-	end
-
-	ZO_OptionsWindow_InitializeControl(checkbox)
-	if isSubMenu then checkbox:SetParent(panelID) end
-
-	lastAddedControl[panelID] = checkbox
-
-	return checkbox
-end
-
-function lam:AddColorPicker(panelID, controlName, text, tooltip, getFunc, setFunc, warning, warningText)
-	local isSubMenu = type(panelID) == "userdata"
-	local colorpicker = wm:CreateTopLevelWindow(controlName)
-	colorpicker:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 10)
-	colorpicker:SetParent(isSubMenu and panelID or optionsWindow)
-	colorpicker:SetResizeToFitDescendents(true)
-	colorpicker:SetWidth(510)
-	colorpicker:SetMouseEnabled(true)
-
-	colorpicker.label = wm:CreateControl(controlName.."Label", colorpicker, CT_LABEL)
-	local label = colorpicker.label
-	label:SetDimensions(300, 26)
-	label:SetAnchor(TOPLEFT)
-	label:SetFont("ZoFontWinH4")
-	label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
-	label:SetText(text)
-
-	colorpicker.color = wm:CreateControl(controlName.."Color", colorpicker, CT_CONTROL)
-	local color = colorpicker.color
-	color:SetDimensions(200,26)
-	color:SetAnchor(RIGHT)
-
-	color.thumb = wm:CreateControl(controlName.."ColorThumb", color, CT_TEXTURE)
-	local thumb = color.thumb
-	thumb:SetDimensions(36, 18)
-	thumb:SetAnchor(LEFT, color, LEFT, 4, 0)
-	local r, g, b, a = getFunc()
-	thumb:SetColor(r, g, b, a or 1)
-
-	color.border = wm:CreateControl(controlName.."ColorBorder", color, CT_TEXTURE)
-	local border = color.border
-	border:SetTexture("EsoUI\\Art\\ChatWindow\\chatOptions_bgColSwatch_frame.dds")
-	border:SetTextureCoords(0, .625, 0, .8125)
-	border:SetDimensions(40, 22)
-	border:SetAnchor(CENTER, thumb, CENTER, 0, 0)
-
-	local ColorPickerCallback
-	if not ColorPickerCallback then
-		ColorPickerCallback = function(r, g, b, a)
-			thumb:SetColor(r, g, b, a or 1)
-			setFunc(r, g, b, a)
-		end
-	end
-
-	colorpicker.controlType = OPTIONS_CUSTOM
-	colorpicker.customSetupFunction = function(colorpicker)
-			colorpicker:SetHandler("OnMouseUp", function(self, btn, upInside)
-					if upInside then
-						local r, g, b, a = getFunc()
-						COLOR_PICKER:Show(ColorPickerCallback, r, g, b, a, text)
-					end
-				end)
-		end
-	colorpicker.panel = isSubMenu and panelID.panel or panelID
-	colorpicker.tooltipText = tooltip
-	colorpicker:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
-	colorpicker:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
-
-	if warning then
-		colorpicker.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", colorpicker, "ZO_Options_WarningIcon")
-		colorpicker.warning:SetAnchor(RIGHT, colorpicker:GetNamedChild("Color"), LEFT, -5, 0)
-		colorpicker.warning.tooltipText = warningText
-	end
-
-	ZO_OptionsWindow_InitializeControl(colorpicker)
-	if isSubMenu then colorpicker:SetParent(panelID) end
-
-	lastAddedControl[panelID] = colorpicker
-
-	return colorpicker
-end
-
-function lam:AddEditBox(panelID, controlName, text, tooltip, isMultiLine, getFunc, setFunc, warning, warningText)
-	local isSubMenu = type(panelID) == "userdata"
-	local editbox = wm:CreateTopLevelWindow(controlName)
-	editbox:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 10)
-	editbox:SetResizeToFitDescendents(true)
-	editbox:SetWidth(510)
-	editbox:SetMouseEnabled(true)
-
-	editbox.label = wm:CreateControl(controlName.."Label", editbox, CT_LABEL)
-	local label = editbox.label
-	label:SetDimensions(300, 26)
-	label:SetAnchor(TOPLEFT)
-	label:SetFont("ZoFontWinH4")
-	label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
-	label:SetText(text)
-
-	editbox.bg = wm:CreateControlFromVirtual(controlName.."BG", editbox, "ZO_EditBackdrop")
-	local bg = editbox.bg
-	bg:SetDimensions(200,isMultiLine and 100 or 24)
-	bg:SetAnchor(RIGHT)
-	editbox.edit = wm:CreateControlFromVirtual(controlName.."Edit", bg, isMultiLine and "ZO_DefaultEditMultiLineForBackdrop" or "ZO_DefaultEditForBackdrop")
-	editbox.edit:SetText(getFunc())
-	editbox.edit:SetHandler("OnFocusLost", function(self) setFunc(self:GetText()) end)
-	editbox.edit:SetHandler("OnEscape", function(self) self:LoseFocus() end)
-	editbox.edit:SetMaxInputChars(1040)
-
-	editbox.panel = isSubMenu and panelID.panel or panelID
-	editbox.tooltipText = tooltip
-	editbox:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
-	editbox:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
-
-	if warning then
-		editbox.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", editbox, "ZO_Options_WarningIcon")
-		editbox.warning:SetAnchor(TOPRIGHT, editbox:GetNamedChild("BG"), TOPLEFT, -5, 0)
-		editbox.warning.tooltipText = warningText
-	end
-
-	ZO_OptionsWindow_InitializeControl(editbox)
-	editbox:SetParent(isSubMenu and panelID or optionsWindow)
-
-	lastAddedControl[panelID] = editbox
-
-	return editbox
-end
-
-function lam:AddButton(panelID, controlName, text, tooltip, onClick, warning, warningText)
-	local isSubMenu = type(panelID) == "userdata"
-	local button = wm:CreateTopLevelWindow(controlName)
-	button:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 6)
-	button:SetDimensions(510, 28)
-	button:SetMouseEnabled(true)
-
-	button.btn = wm:CreateControlFromVirtual(controlName.."Button", button, "ZO_DefaultButton")
-	local btn = button.btn
-	btn:SetAnchor(TOPRIGHT)
-	btn:SetWidth(200)
-	btn:SetText(text)
-	btn:SetHandler("OnClicked", onClick)
-
-	button.controlType = OPTIONS_CUSTOM
-	button.customSetupFunction = function() end	--move handlers into this function? (since I created a function...)
-	button.panel = isSubMenu and panelID.panel or panelID
-	btn.tooltipText = tooltip
-	btn:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
-	btn:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
-
-	if warning then
-		button.warning = wm:CreateControlFromVirtual(controlName.."WarningIcon", button, "ZO_Options_WarningIcon")
-		button.warning:SetAnchor(RIGHT, btn, LEFT, -5, 0)
-		button.warning.tooltipText = warningText
-	end
-
-	ZO_OptionsWindow_InitializeControl(button)
-	button:SetParent(isSubMenu and panelID or optionsWindow)
-
-	lastAddedControl[panelID] = button
-
-	return button
-end
-
-function lam:AddDescription(panelID, controlName, text, titleText)
-	local isSubMenu = type(panelID) == "userdata"
-	local textBox = wm:CreateTopLevelWindow(controlName)
-	textBox:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 10)
-	textBox:SetResizeToFitDescendents(true)
-	textBox:SetWidth(510)
-
-	if titleText then
-		textBox.title = wm:CreateControl(controlName.."Title", textBox, CT_LABEL)
-		local title = textBox.title
-		title:SetWidth(510)
-		title:SetAnchor(TOPLEFT, textBox, TOPLEFT)
-		title:SetFont("ZoFontWinH4")
-		title:SetText(titleText)
-	end
-
-	textBox.desc = wm:CreateControl(controlName.."Text", textBox, CT_LABEL)
-	local desc = textBox.desc
-	desc:SetWidth(510)
-	if titleText then
-		desc:SetAnchor(TOPLEFT, textBox.title, BOTTOMLEFT)
-	else
-		desc:SetAnchor(TOPLEFT)
-	end
-	desc:SetVerticalAlignment(TEXT_ALIGN_TOP)
-	desc:SetFont("ZoFontGame")
-	desc:SetText(text)
-
-	textBox.controlType = OPTIONS_CUSTOM
-	textBox.panel = isSubMenu and panelID.panel or panelID
-
-	ZO_OptionsWindow_InitializeControl(textBox)
-	textBox:SetParent(isSubMenu and panelID or optionsWindow)
-
-	lastAddedControl[panelID] = textBox
-
-	return textBox
-end
-
---window doesn't hide when escape is pressed
---color-picker is hidden for some reason
-function lam:AddSubMenu(panelID, controlName, text, tooltip)
-	local menubtn = wm:CreateTopLevelWindow(controlName)
-	menubtn:SetParent(optionsWindow)
-	menubtn:SetAnchor(TOPLEFT, lastAddedControl[panelID], BOTTOMLEFT, 0, 6)
-	menubtn:SetDimensions(510, 28)
-	menubtn:SetMouseEnabled(true)
-
-	menubtn.label = wm:CreateControl(controlName.."Text", menubtn, CT_LABEL)
-	local label = menubtn.label
-	label:SetAnchor(TOPLEFT, textBox, TOPLEFT)
-	label:SetDimensions(300, 28)
-	label:SetFont("ZoFontWinH4")
-	label:SetText(text)
-
-	menubtn.btn = wm:CreateControlFromVirtual(controlName.."Button", menubtn, "ZO_DefaultButton")
-	local btn = menubtn.btn
-	btn:SetAnchor(TOPRIGHT)
-	btn:SetWidth(200)
-	btn:SetText(GetString("SI_GAMECAMERAACTIONTYPE", 13).." |t32:32:esoui\\art\\crafting\\smithing_rightarrow_up.dds|t")
-	--btn:SetText("Open -->")
-
-	menubtn.window = wm:CreateTopLevelWindow(controlName.."Window")
-	local window = menubtn.window
-	--window:SetParent(menubtn)
-	window:SetAnchor(TOPLEFT, menubtn, TOPRIGHT)
-	window:SetDimensions(555, 300)
-	window:SetClampedToScreen(true)
-	window:SetClampedToScreenInsets(-5, -20, 5, 5)
-	window.bg = wm:CreateControlFromVirtual(controlName.."WindowBG", window, "ZO_DefaultBackdrop")
-	window:SetHidden(true)
-
-	window.settings = wm:CreateControlFromVirtual(controlName.."WindowSettings", window, "ZO_ScrollContainer")
-	local settings = window.settings
-	settings:SetAnchor(TOPLEFT, window, TOPLEFT, 10, 10)
-	settings:SetAnchor(BOTTOMRIGHT, window, BOTTOMRIGHT, -5, -5)
-	local scroll = settings:GetNamedChild("ScrollChild")
-	scroll.panel = panelID
-
-	btn:SetHandler("OnClicked", function()
-			if window:IsHidden() then	--if this submenu isn't open yet, then...
-				if openSubMenu then openSubMenu:SetHidden(true) end	--if a submenu was already open, then close it
-				window:SetHidden(false)	--show this submenu
-				openSubMenu = window	--this submenu is now our open menu
-			else
-				window:SetHidden(true)
-				openSubMenu = nil	--no more open submenus
-			end
-		end)
-	menubtn:SetHandler("OnHide", function()
-			if openSubMenu then
-				openSubMenu:SetHidden(true)
-			end
-		end)
-	ZO_OptionsWindow:SetHandler("OnHide", function()
-			if openSubMenu then
-				openSubMenu:SetHidden(true)
-			end
-		end)
-
-	menubtn.controlType = OPTIONS_CUSTOM
-	menubtn.customSetupFunction = function() end	--move handlers into this function? (since I created a function...)
-	menubtn.panel = panelID
-	btn.tooltipText = tooltip
-	btn:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
-	btn:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
-
-	ZO_OptionsWindow_InitializeControl(menubtn)
-
-	lastAddedControl[panelID] = menubtn
-
-	lam:AddHeader(scroll, controlName.."MenuHeader", text)	--create the header for our menu
-
-	return scroll
-end
-
-
-
---test controls & examples--
---[[local controlPanelID = lam:CreateControlPanel("ZAM_TEST_ADDON_OPTIONS", "ZAM Test")
-lam:AddHeader(controlPanelID, "ZAM_Addons_TESTADDON", "TEST ADDON")
-lam:AddDescription(controlPanelID, "ZAM_Addons_TESTDESC", "This is a test description.", "Header")
-local window = lam:AddSubMenu(controlPanelID, "ZAM_Addons_TESTMENU", "Click for more options.", "This is some tooltip text.")
-lam:AddHeader(window, "ZAM_Addons_TESTMENU_HEADER", "Header")
-lam:AddSlider(window, "ZAM_Addons_TESTMENU_SLIDER", "Test slider", "Adjust the slider.", 1, 10, 1, function() return 7 end, function(value) end, true, "needs UI reload")
-lam:AddHeader(window, "ZAM_Addons_TESTMENU_HEADER2", "Header 2")
-lam:AddColorPicker(window, "ZAM_Addons_TESTMENU_COLORPICKER", "Test color picker", "What's your favorite color?", function() return 1, 1, 0 end, function(r,g,b) print(r,g,b) end)
-local window2 = lam:AddSubMenu(controlPanelID, "ZAM_Addons_TESTMENU2", "Click for more options.", "This is some tooltip text.")
-lam:AddHeader(window2, "ZAM_Addons_TESTMENU2_HEADER", "Header")
-lam:AddSlider(window2, "ZAM_Addons_TESTMENU2_SLIDER", "Test slider", "Adjust the slider.", 1, 10, 1, function() return 7 end, function(value) end, true, "needs UI reload")
-lam:AddHeader(window2, "ZAM_Addons_TESTMENU2_HEADER2", "Header 2")
-lam:AddColorPicker(window2, "ZAM_Addons_TESTMENU2_COLORPICKER", "Test color picker", "What's your favorite color?", function() return 1, 1, 0 end, function(r,g,b) print(r,g,b) end)
-lam:AddSlider(controlPanelID, "ZAM_TESTSLIDER", "Test slider", "Adjust the slider.", 1, 10, 1, function() return 7 end, function(value) end, true, "needs UI reload")
-lam:AddDropdown(controlPanelID, "ZAM_TESTDROPDOWN", "Test Dropdown", "Pick something!", {"thing 1", "thing 2", "thing 3"}, function() return "thing 2" end, function(self,valueString) print(valueString) end)
-local checkbox1 = true
-lam:AddCheckbox(controlPanelID, "ZAM_TESTCHECKBOX", "Test Checkbox", "On or off?", function() return checkbox1 end, function(value) checkbox1 = not checkbox1 print(value, checkbox1) end)
-lam:AddColorPicker(controlPanelID, "ZAM_TESTCOLORPICKER", "Test color picker", "What's your favorite color?", function() return 1, 1, 0 end, function(r,g,b) print(r,g,b) end)
-lam:AddEditBox(controlPanelID, "ZAM_TESTEDITBOX", "Test Edit Box", "This is a tooltip!", false, function() return "hi" end, function(text) print(text) end)
-lam:AddHeader(controlPanelID, "ZAM_Addons_TESTADDON2", "TEST ADDON 2")
-local checkbox2 = false
-lam:AddCheckbox(controlPanelID, "ZAM_TESTCHECKBOX2", "Test Checkbox 2", "On or off?", function() return checkbox2 end, function(value) checkbox2 = not checkbox2 print(value, checkbox2) end)
-lam:AddButton(controlPanelID, "ZAM_TESTBUTTON", "Test Button", "Click me", function() print("hi") end, true, "oh noez!")
-lam:AddEditBox(controlPanelID, "ZAM_TESTEDITBOX2", "Test Edit Box 2", "This is a tooltip!", true, function() return "hi" end, function(text) print(text) end, true, "warning text")
-lam:AddSlider(controlPanelID, "ZAM_TESTSLIDER2", "Test slider 2", "Adjust the slider.", 50, 100, 10, function() return 80 end, function(value) end)
-lam:AddDropdown(controlPanelID, "ZAM_TESTDROPDOWN2", "Test Dropdown 2", "Pick something!", {"thing 4", "thing 5", "thing 6"}, function() return "thing 6" end, function(self,valueString) print(valueString) end)
-]]--
\ No newline at end of file
diff --git a/libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua b/libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua
new file mode 100644
index 0000000..b68d516
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua
@@ -0,0 +1,299 @@
+--	LibAddonMenu-2.0 & its files © Ryan Lakanen (Seerah)	--
+--	All Rights Reserved										--
+--	Permission is granted to use Seerah's LibAddonMenu-2.0	--
+--	in your project. Any modifications to LibAddonMenu-2.0	--
+--	may not be redistributed.								--
+--------------------------------------------------------------
+
+
+--Register LAM with LibStub
+local MAJOR, MINOR = "LibAddonMenu-2.0", 7
+local lam, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+if not lam then return end	--the same or newer version of this lib is already loaded into memory
+
+
+--UPVALUES--
+local wm = WINDOW_MANAGER
+local cm = CALLBACK_MANAGER
+local tinsert = table.insert
+local optionsWindow = ZO_OptionsWindowSettingsScrollChild
+local _
+
+local addonsForList = {}
+local addonToOptionsMap = {}
+local optionsCreated = {}
+local widgets = {}
+
+
+--METHOD: REGISTER WIDGET--
+--each widget has its version checked before loading,
+--so we only have the most recent one in memory
+--Usage:
+--	widgetType = "string"; the type of widget being registered
+--	widgetVersion = integer; the widget's version number
+LAMCreateControl = LAMCreateControl or {}
+local lamcc = LAMCreateControl
+
+function lam:RegisterWidget(widgetType, widgetVersion)
+	if widgets[widgetType] and widgets[widgetType] >= widgetVersion then
+		return false
+	else
+		widgets[widgetType] = widgetVersion
+		return true
+	end
+end
+
+
+--METHOD: OPEN TO ADDON PANEL--
+--opens to a specific addon's option panel
+--Usage:
+--	panel = userdata; the panel returned by the :RegisterOptionsPanel method
+--local settings = {en = "Settings", de = "Einstellungen", fr = "Réglages"}
+--local locSettings = settings[GetCVar("Language.2")]
+local locSettings = GetString(SI_GAME_MENU_SETTINGS)
+function lam:OpenToPanel(panel)
+	SCENE_MANAGER:Show("gameMenuInGame")
+	zo_callLater(function()
+			ZO_GameMenu_InGame.gameMenu.headerControls[locSettings]:SetOpen(true)
+			SCENE_MANAGER:AddFragment(OPTIONS_WINDOW_FRAGMENT)
+			ZO_OptionsWindow_ChangePanels(lam.panelID)
+			panel:SetHidden(false)
+		end, 200)
+end
+
+
+--INTERNAL FUNCTION
+--creates controls when options panel is first shown
+--controls anchoring of these controls in the panel
+local function CreateOptionsControls(panel)
+	local addonID = panel:GetName()
+	local optionsTable = addonToOptionsMap[addonID]
+
+	if optionsTable then
+		local lastAddedControl, lacAtHalfRow
+		for _, widgetData in ipairs(optionsTable) do
+			local widgetType = widgetData.type
+			if widgetType == "submenu" then
+				local submenu = LAMCreateControl[widgetType](panel, widgetData)
+				if lastAddedControl then
+					submenu:SetAnchor(TOPLEFT, lastAddedControl, BOTTOMLEFT, 0, 15)
+				else
+					submenu:SetAnchor(TOPLEFT)
+				end
+				lastAddedControl = submenu
+				lacAtHalfRow = false
+
+				local lastAddedControlSub, lacAtHalfRowSub
+				for _, subWidgetData in ipairs(widgetData.controls) do
+					local subWidgetType = subWidgetData.type
+					local subWidget = LAMCreateControl[subWidgetType](submenu, subWidgetData)
+					local isHalf = subWidgetData.width == "half"
+					if lastAddedControlSub then
+						if lacAtHalfRowSub and isHalf then
+							subWidget:SetAnchor(TOPLEFT, lastAddedControlSub, TOPRIGHT, 5, 0)
+							lacAtHalfRowSub = false
+						else
+							subWidget:SetAnchor(TOPLEFT, lastAddedControlSub, BOTTOMLEFT, 0, 15)
+							lacAtHalfRowSub = isHalf and true or false
+							lastAddedControlSub = subWidget
+						end
+					else
+						subWidget:SetAnchor(TOPLEFT)
+						lacAtHalfRowSub = isHalf and true or false
+						lastAddedControlSub = subWidget
+					end
+				end
+			else
+				local widget = LAMCreateControl[widgetType](panel, widgetData)
+				local isHalf = widgetData.width == "half"
+				if lastAddedControl then
+					if lacAtHalfRow and isHalf then
+						widget:SetAnchor(TOPLEFT, lastAddedControl, TOPRIGHT, 10, 0)
+						lacAtHalfRow = false
+					else
+						widget:SetAnchor(TOPLEFT, lastAddedControl, BOTTOMLEFT, 0, 15)
+						lacAtHalfRow = isHalf and true or false
+						lastAddedControl = widget
+					end
+				else
+					widget:SetAnchor(TOPLEFT)
+					lacAtHalfRow = isHalf and true or false
+					lastAddedControl = widget
+				end
+			end
+		end
+	end
+
+	optionsCreated[addonID] = true
+	cm:FireCallbacks("LAM-PanelControlsCreated", panel)
+end
+
+
+--INTERNAL FUNCTION
+--handles switching between panels
+local function ToggleAddonPanels(panel)	--called in OnShow of newly shown panel
+	local currentlySelected = LAMAddonPanelsMenu.currentlySelected
+	if currentlySelected and currentlySelected ~= panel then
+		currentlySelected:SetHidden(true)
+	end
+	LAMAddonPanelsMenu.currentlySelected = panel
+
+	if not optionsCreated[panel:GetName()] then	--if this is the first time opening this panel, create these options
+		CreateOptionsControls(panel)
+	end
+
+	cm:FireCallbacks("LAM-RefreshPanel", panel)
+end
+
+
+--METHOD: REGISTER ADDON PANEL
+--registers your addon with LibAddonMenu and creates a panel
+--Usage:
+--	addonID = "string"; unique ID which will be the global name of your panel
+--	panelData = table; data object for your panel - see controls\panel.lua
+function lam:RegisterAddonPanel(addonID, panelData)
+	local panel = lamcc.panel(nil, panelData, addonID)	--addonID==global name of panel
+	panel:SetHidden(true)
+	panel:SetAnchor(TOPLEFT, LAMAddonPanelsMenu, TOPRIGHT, 10, 0)
+	panel:SetAnchor(BOTTOMLEFT, LAMAddonPanelsMenu, BOTTOMRIGHT, 10, 0)
+	panel:SetWidth(549)
+	panel:SetDrawLayer(DL_OVERLAY)
+	tinsert(addonsForList, {panel = addonID, name = panelData.name})
+	panel:SetHandler("OnShow", ToggleAddonPanels)
+	if panelData.slashCommand then
+		SLASH_COMMANDS[panelData.slashCommand] = function()
+			lam:OpenToPanel(panel)
+		end
+	end
+
+	return panel	--return for authors creating options manually
+end
+
+
+--METHOD: REGISTER OPTION CONTROLS
+--registers the options you want shown for your addon
+--these are stored in a table where each key-value pair is the order
+--of the options in the panel and the data for that control, respectively
+--see exampleoptions.lua for an example
+--see controls\<widget>.lua for each widget type
+--Usage:
+--	addonID = "string"; the same string passed to :RegisterAddonPanel
+--	optionsTable = table; the table containing all of the options controls and their data
+function lam:RegisterOptionControls(addonID, optionsTable)	--optionsTable = {sliderData, buttonData, etc}
+	addonToOptionsMap[addonID] = optionsTable
+end
+
+
+--INTERNAL FUNCTION
+--handles switching between LAM's Addon Settings panel and other panels in the Settings menu
+local oldDefaultButton = ZO_OptionsWindowResetToDefaultButton
+local oldCallback = oldDefaultButton.callback
+local dummyFunc = function() end
+local panelWindow = ZO_OptionsWindow
+local bgL = ZO_OptionsWindowBGLeft
+local bgR = ZO_OptionsWindowBGLeftBGRight
+local function HandlePanelSwitching(panel)
+	if panel == lam.panelID then	--our addon settings panel
+		oldDefaultButton:SetCallback(dummyFunc)
+		oldDefaultButton:SetHidden(true)
+		oldDefaultButton:SetAlpha(0)	--just because it still bugs out
+		panelWindow:SetDimensions(999, 960)
+		bgL:SetWidth(666)
+		bgR:SetWidth(333)
+	else
+		local shown = LAMAddonPanelsMenu.currentlySelected
+		if shown then shown:SetHidden(true) end
+		oldDefaultButton:SetCallback(oldCallback)
+		oldDefaultButton:SetHidden(false)
+		oldDefaultButton:SetAlpha(1)
+		panelWindow:SetDimensions(768, 914)
+		bgL:SetWidth(512)
+		bgR:SetWidth(256)
+	end
+end
+
+
+--INTERNAL FUNCTION
+--creates LAM's Addon Settings panel
+local function CreateAddonSettingsPanel()
+	if not LAMSettingsPanelCreated then
+		local controlPanelID = "LAM_ADDON_SETTINGS_PANEL"
+		local controlPanelNames = {en = "Addon Settings", fr = "Extensions", de = "Erweiterungen"}
+
+		ZO_OptionsWindow_AddUserPanel(controlPanelID, controlPanelNames[GetCVar("Language.2")])
+
+		lam.panelID = _G[controlPanelID]
+
+		ZO_PreHook("ZO_OptionsWindow_ChangePanels", HandlePanelSwitching)
+
+		LAMSettingsPanelCreated = true
+	end
+end
+
+
+--INTERNAL FUNCTION
+--adds each registered addon to the menu in LAM's panel
+local function CreateAddonButtons(list, addons)
+	for i = 1, #addons do
+		local button = wm:CreateControlFromVirtual("LAMAddonMenuButton"..i, list, "ZO_DefaultTextButton")
+		button.name = addons[i].name
+		button.panel = _G[addons[i].panel]
+		button:SetText(button.name)
+		button:SetHorizontalAlignment(TEXT_ALIGN_LEFT)
+		button:SetWidth(190)
+		if i == 1 then
+			button:SetAnchor(TOPLEFT, list, TOPLEFT, 5, 5)
+		else
+			button:SetAnchor(TOPLEFT, _G["LAMAddonMenuButton"..i-1], BOTTOMLEFT)
+		end
+		button:SetHandler("OnClicked", function(self) self.panel:SetHidden(false) end)
+	end
+end
+
+
+--INTERNAL FUNCTION
+--creates the left-hand menu in LAM's panel
+local function CreateAddonList()
+	local list
+	--check if an earlier loaded copy of LAM created it already
+	list = LAMAddonPanelsMenu or wm:CreateControlFromVirtual("LAMAddonPanelsMenu", optionsWindow, "ZO_ScrollContainer")
+	list:ClearAnchors()
+	list:SetAnchor(TOPLEFT)
+	list:SetHeight(675)
+	list:SetWidth(200)
+
+	list.bg = list.bg or wm:CreateControl(nil, list, CT_BACKDROP)
+	local bg = list.bg
+	bg:SetAnchorFill()	--offsets of 8?
+	bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16)
+	bg:SetCenterColor(0, 0, 0, 0)
+
+	local generatedButtons
+	list:SetHandler("OnShow", function(self)
+			if not generatedButtons and #addonsForList > 0 then
+				--we're about to show our list for the first time - let's sort the buttons before creating them
+				table.sort(addonsForList, function(a, b)
+						return a.name < b.name
+					end)
+				CreateAddonButtons(list, addonsForList)
+				self.currentlySelected = LAMAddonMenuButton1 and LAMAddonMenuButton1.panel
+				--since our addon panels don't have a parent, let's make sure they hide when we're done with them
+				ZO_PreHookHandler(ZO_OptionsWindow, "OnHide", function() self.currentlySelected:SetHidden(true) end)
+				generatedButtons = true
+			end
+			if self.currentlySelected then self.currentlySelected:SetHidden(false) end
+		end)
+
+	list.controlType = OPTIONS_CUSTOM
+	list.panel = lam.panelID
+
+	ZO_OptionsWindow_InitializeControl(list)
+
+	return list
+end
+
+
+--INITIALIZING
+CreateAddonSettingsPanel()
+CreateAddonList()
+
diff --git a/libs/LibAddonMenu-2.0/controls/button.lua b/libs/LibAddonMenu-2.0/controls/button.lua
new file mode 100644
index 0000000..9954a1e
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/button.lua
@@ -0,0 +1,87 @@
+--[[buttonData = {
+	type = "button",
+	name = "My Button",
+	tooltip = "Button's tooltip text.",
+	func = function() end,
+	width = "full",	--or "half" (optional)
+	disabled = function() return db.someBooleanSetting end,	--or boolean (optional)
+	icon = "icon\\path.dds",	--(optional)
+	warning = "Will need to reload the UI.",	--(optional)
+	reference = "MyAddonButton"	--(optional) unique global reference to control
+}	]]
+
+
+local widgetVersion = 2
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("button", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local cm = CALLBACK_MANAGER
+local tinsert = table.insert
+
+local function UpdateDisabled(control)
+	local disable
+	if type(control.data.disabled) == "function" then
+		disable = control.data.disabled()
+	else
+		disable = control.data.disabled
+	end
+
+	control.button:SetEnabled(not disable)
+end
+
+
+--controlName is optional
+function LAMCreateControl.button(parent, buttonData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName or buttonData.reference)
+	control:SetParent(parent.scroll)
+
+	local isHalfWidth = buttonData.width == "half"
+	control:SetDimensions(isHalfWidth and 250 or 510, isHalfWidth and 55 or 28)
+	control:SetMouseEnabled(true)
+
+	if buttonData.icon then
+		control.button = wm:CreateControl(nil, control, CT_BUTTON)
+		control.button:SetDimensions(26, 26)
+		control.button:SetNormalTexture(buttonData.icon)
+		control.button:SetPressedOffset(2, 2)
+	else
+		--control.button = wm:CreateControlFromVirtual(controlName.."Button", control, "ZO_DefaultButton")
+		control.button = wm:CreateControlFromVirtual(nil, control, "ZO_DefaultButton")
+		control.button:SetWidth(isHalfWidth and 180 or 200)
+		control.button:SetText(buttonData.name)
+	end
+	local button = control.button
+	button:SetAnchor(isHalfWidth and CENTER or RIGHT)
+	button:SetClickSound("Click")
+	button.tooltipText = buttonData.tooltip
+	button:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+	button:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+	button:SetHandler("OnClicked", function(self, ...)
+			buttonData.func(self, ...)
+			if control.panel.data.registerForRefresh then
+				cm:FireCallbacks("LAM-RefreshPanel", control)
+			end
+		end)
+
+	if buttonData.warning then
+		control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+		control.warning:SetAnchor(RIGHT, button, LEFT, -5, 0)
+		control.warning.tooltipText = buttonData.warning
+	end
+
+	control.panel = parent.panel or parent	--if this is in a submenu, panel is its parent
+	control.data = buttonData
+
+	if buttonData.disabled then
+		control.UpdateDisabled = UpdateDisabled
+		control:UpdateDisabled()
+
+		--this is here because buttons don't have an UpdateValue method
+		if control.panel.data.registerForRefresh then	--if our parent window wants to refresh controls, then add this to the list
+			tinsert(control.panel.controlsToRefresh, control)
+		end
+	end
+
+	return control
+end
\ No newline at end of file
diff --git a/libs/LibAddonMenu-2.0/controls/checkbox.lua b/libs/LibAddonMenu-2.0/controls/checkbox.lua
new file mode 100644
index 0000000..8432109
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/checkbox.lua
@@ -0,0 +1,170 @@
+--[[checkboxData = {
+	type = "checkbox",
+	name = "My Checkbox",
+	tooltip = "Checkbox's tooltip text.",
+	getFunc = function() return db.var end,
+	setFunc = function(value) db.var = value doStuff() end,
+	width = "full",	--or "half" (optional)
+	disabled = function() return db.someBooleanSetting end,	--or boolean (optional)
+	warning = "Will need to reload the UI.",	--(optional)
+	default = defaults.var,	--(optional)
+	reference = "MyAddonCheckbox"	--(optional) unique global reference to control
+}	]]
+
+
+local widgetVersion = 4
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("checkbox", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local cm = CALLBACK_MANAGER
+local tinsert = table.insert
+--label
+local enabledColor = ZO_DEFAULT_ENABLED_COLOR
+local enabledHLcolor = ZO_HIGHLIGHT_TEXT
+local disabledColor = ZO_DEFAULT_DISABLED_COLOR
+local disabledHLcolor = ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR
+--checkbox
+local checkboxColor = ZO_NORMAL_TEXT
+local checkboxHLcolor = ZO_HIGHLIGHT_TEXT
+
+
+local function UpdateDisabled(control)
+	local disable
+	if type(control.data.disabled) == "function" then
+		disable = control.data.disabled()
+	else
+		disable = control.data.disabled
+	end
+
+	control.label:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or control.value and ZO_DEFAULT_ENABLED_COLOR or ZO_DEFAULT_DISABLED_COLOR):UnpackRGBA())
+	control.checkbox:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or ZO_NORMAL_TEXT):UnpackRGBA())
+	--control:SetMouseEnabled(not disable)
+	--control:SetMouseEnabled(true)
+
+	control.isDisabled = disable
+end
+
+local function ToggleCheckbox(control)
+	if control.value then
+		control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+		control.checkbox:SetText(control.checkedText)
+	else
+		control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+		control.checkbox:SetText(control.uncheckedText)
+	end
+end
+
+local function UpdateValue(control, forceDefault, value)
+	if forceDefault then	--if we are forcing defaults
+		value = control.data.default
+		control.data.setFunc(value)
+	elseif value ~= nil then	--our value could be false
+		control.data.setFunc(value)
+		--after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
+		if control.panel.data.registerForRefresh then
+			cm:FireCallbacks("LAM-RefreshPanel", control)
+		end
+	else
+		value = control.data.getFunc()
+	end
+	control.value = value
+
+	ToggleCheckbox(control)
+end
+
+local function OnMouseEnter(control)
+	ZO_Options_OnMouseEnter(control)
+
+	if control.isDisabled then return end
+
+	local label = control.label
+	if control.value then
+		label:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA())
+	else
+		label:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA())
+	end
+	control.checkbox:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA())
+end
+
+local function OnMouseExit(control)
+    ZO_Options_OnMouseExit(control)
+
+	if control.isDisabled then return end
+
+	local label = control.label
+	if control.value then
+		label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+	else
+		label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+	end
+	control.checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA())
+end
+
+
+--controlName is optional
+function LAMCreateControl.checkbox(parent, checkboxData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName or checkboxData.reference)
+	control:SetParent(parent.scroll)
+	control:SetMouseEnabled(true)
+	control.tooltipText = checkboxData.tooltip
+	control:SetHandler("OnMouseEnter", OnMouseEnter)
+	control:SetHandler("OnMouseExit", OnMouseExit)
+	control:SetHandler("OnMouseUp", function(control)
+			if control.isDisabled then return end
+			PlaySound(SOUNDS.DEFAULT_CLICK)
+			control.value = not control.value
+			control:UpdateValue(false, control.value)
+		end)
+
+	control.label = wm:CreateControl(nil, control, CT_LABEL)
+	local label = control.label
+	label:SetFont("ZoFontWinH4")
+	label:SetText(checkboxData.name)
+	label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+	label:SetHeight(26)
+
+	control.checkbox = wm:CreateControl(nil, control, CT_LABEL)
+	local checkbox = control.checkbox
+	checkbox:SetFont("ZoFontGameBold")
+	checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA())
+	control.checkedText = GetString(SI_CHECK_BUTTON_ON):upper()
+	control.uncheckedText = GetString(SI_CHECK_BUTTON_OFF):upper()
+
+	local isHalfWidth = checkboxData.width == "half"
+	if isHalfWidth then
+		control:SetDimensions(250, 55)
+		checkbox:SetDimensions(100, 26)
+		checkbox:SetAnchor(BOTTOMRIGHT)
+		label:SetAnchor(TOPLEFT)
+		label:SetAnchor(TOPRIGHT)
+	else
+		control:SetDimensions(510, 30)
+		checkbox:SetDimensions(200, 26)
+		checkbox:SetAnchor(RIGHT)
+		label:SetAnchor(LEFT)
+		label:SetAnchor(RIGHT, checkbox, LEFT, -5, 0)
+	end
+
+	if checkboxData.warning then
+		control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+		control.warning:SetAnchor(RIGHT, checkbox, LEFT, -5, 0)
+		control.warning.tooltipText = checkboxData.warning
+	end
+
+	control.panel = parent.panel or parent	--if this is in a submenu, panel is its parent
+	control.data = checkboxData
+
+	if checkboxData.disabled then
+		control.UpdateDisabled = UpdateDisabled
+		control:UpdateDisabled()
+	end
+	control.UpdateValue = UpdateValue
+	control:UpdateValue()
+
+	if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then	--if our parent window wants to refresh controls, then add this to the list
+		tinsert(control.panel.controlsToRefresh, control)
+	end
+
+	return control
+end
\ No newline at end of file
diff --git a/libs/LibAddonMenu-2.0/controls/colorpicker.lua b/libs/LibAddonMenu-2.0/controls/colorpicker.lua
new file mode 100644
index 0000000..af645ba
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/colorpicker.lua
@@ -0,0 +1,138 @@
+--[[colorpickerData = {
+	type = "colorpicker",
+	name = "My Color Picker",
+	tooltip = "Color Picker's tooltip text.",
+	getFunc = function() return db.r, db.g, db.b, db.a end,	--(alpha is optional)
+	setFunc = function(r,g,b,a) db.r=r, db.g=g, db.b=b, db.a=a end,	--(alpha is optional)
+	width = "full",	--or "half" (optional)
+	disabled = function() return db.someBooleanSetting end,	--or boolean (optional)
+	warning = "Will need to reload the UI.",	--(optional)
+	default = {defaults.r, defaults.g, defaults.b, defaults.a},	--(optional) table of default color values
+	reference = "MyAddonColorpicker"	--(optional) unique global reference to control
+}	]]
+
+
+local widgetVersion = 2
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("colorpicker", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local cm = CALLBACK_MANAGER
+local tinsert = table.insert
+
+
+local function UpdateDisabled(control)
+	local disable
+	if type(control.data.disabled) == "function" then
+		disable = control.data.disabled()
+	else
+		disable = control.data.disabled
+	end
+
+	if disable then
+		control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+	else
+		control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+	end
+
+	control.isDisabled = disable
+end
+
+local function UpdateValue(control, forceDefault, valueR, valueG, valueB, valueA)
+	if forceDefault then	--if we are forcing defaults
+		local color = control.data.default
+		valueR, valueG, valueB, valueA = color.r, color.g, color.b, color.a
+		control.data.setFunc(valueR, valueG, valueB, valueA)
+	elseif valueR and valueG and valueB then
+		control.data.setFunc(valueR, valueG, valueB, valueA or 1)
+		--after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
+		if control.panel.data.registerForRefresh then
+			cm:FireCallbacks("LAM-RefreshPanel", control)
+		end
+	else
+		valueR, valueG, valueB, valueA = control.data.getFunc()
+	end
+
+	control.thumb:SetColor(valueR, valueG, valueB, valueA or 1)
+end
+
+
+function LAMCreateControl.colorpicker(parent, colorpickerData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName or colorpickerData.reference)
+	control:SetParent(parent.scroll)
+	control:SetMouseEnabled(true)
+	control.tooltipText = colorpickerData.tooltip
+	control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+	control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+
+	control.label = wm:CreateControl(nil, control, CT_LABEL)
+	local label = control.label
+	label:SetDimensions(300, 26)
+	label:SetAnchor(TOPLEFT)
+	label:SetFont("ZoFontWinH4")
+	label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+	label:SetText(colorpickerData.name)
+
+	control.color = wm:CreateControl(nil, control, CT_CONTROL)
+	local color = control.color
+
+	local isHalfWidth = colorpickerData.width == "half"
+	if isHalfWidth then
+		control:SetDimensions(250, 55)
+		label:SetDimensions(250, 26)
+		color:SetDimensions(100, 24)
+		color:SetAnchor(TOPRIGHT, label, BOTTOMRIGHT)
+	else
+		control:SetDimensions(510, 30)
+		label:SetDimensions(300, 26)
+		color:SetDimensions(200, 24)
+		color:SetAnchor(TOPRIGHT)
+	end
+
+	control.thumb = wm:CreateControl(nil, color, CT_TEXTURE)
+	local thumb = control.thumb
+	thumb:SetDimensions(36, 18)
+	thumb:SetAnchor(LEFT, color, LEFT, 4, 0)
+
+	color.border = wm:CreateControl(nil, color, CT_TEXTURE)
+	local border = color.border
+	border:SetTexture("EsoUI\\Art\\ChatWindow\\chatOptions_bgColSwatch_frame.dds")
+	border:SetTextureCoords(0, .625, 0, .8125)
+	border:SetDimensions(40, 22)
+	border:SetAnchor(CENTER, thumb, CENTER, 0, 0)
+
+	local function ColorPickerCallback(r, g, b, a)
+			control:UpdateValue(false, r, g, b, a)
+		end
+
+	control:SetHandler("OnMouseUp", function(self, btn, upInside)
+			if self.isDisabled then return end
+
+			if upInside then
+				local r, g, b, a = colorpickerData.getFunc()
+				COLOR_PICKER:Show(ColorPickerCallback, r, g, b, a, colorpickerData.name)
+			end
+		end)
+
+	if colorpickerData.warning then
+		control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+		control.warning:SetAnchor(RIGHT, control.color, LEFT, -5, 0)
+		control.warning.tooltipText = colorpickerData.warning
+	end
+
+	control.panel = parent.panel or parent	--if this is in a submenu, panel is its parent
+	control.data = colorpickerData
+
+	if colorpickerData.disabled then
+		control.UpdateDisabled = UpdateDisabled
+		control:UpdateDisabled()
+	end
+	control.UpdateValue = UpdateValue
+	control:UpdateValue()
+
+	if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then	--if our parent window wants to refresh controls, then add this to the list
+		tinsert(control.panel.controlsToRefresh, control)
+	end
+
+	return control
+end
\ No newline at end of file
diff --git a/libs/LibAddonMenu-2.0/controls/custom.lua b/libs/LibAddonMenu-2.0/controls/custom.lua
new file mode 100644
index 0000000..ae7cdb2
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/custom.lua
@@ -0,0 +1,31 @@
+--[[customData = {
+	type = "custom",
+	reference = "MyAddonCustomControl",	--unique name for your control to use as reference
+	width = "full",	--or "half" (optional)
+}	]]
+
+local widgetVersion = 2
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("custom", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+function LAMCreateControl.custom(parent, customData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName or customData.reference)
+	control:SetResizeToFitDescendents(true)
+	control:SetParent(parent.scroll)
+
+	local isHalfWidth = customData.width == "half"
+	if isHalfWidth then	--note these restrictions
+		control:SetDimensionConstraints(250, 55, 250, 100)
+		control:SetDimensions(250, 55)
+	else
+		control:SetDimensionConstraints(510, 30, 510, 100)
+		control:SetDimensions(510, 30)
+	end
+
+	control.panel = parent.panel or parent	--if this is in a submenu, panel is its parent
+	control.data = customData
+
+	return control
+end
\ No newline at end of file
diff --git a/libs/LibAddonMenu-2.0/controls/description.lua b/libs/LibAddonMenu-2.0/controls/description.lua
new file mode 100644
index 0000000..2db253b
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/description.lua
@@ -0,0 +1,53 @@
+--[[descriptionData = {
+	type = "description",
+	title = "My Title",	--(optional)
+	text = "My description text to display.",
+	width = "full",	--or "half" (optional)
+	reference = "MyAddonDescription"	--(optional) unique global reference to control
+}	]]
+
+
+local widgetVersion = 2
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("description", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+function LAMCreateControl.description(parent, descriptionData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName or descriptionData.reference)
+	control:SetResizeToFitDescendents(true)
+	control:SetParent(parent.scroll)
+	local isHalfWidth = descriptionData.width == "half"
+	if isHalfWidth then
+		control:SetDimensionConstraints(250, 55, 250, 100)
+		control:SetDimensions(250, 55)
+	else
+		control:SetDimensionConstraints(510, 40, 510, 100)
+		control:SetDimensions(510, 30)
+	end
+
+	control.desc = wm:CreateControl(nil, control, CT_LABEL)
+	local desc = control.desc
+	desc:SetVerticalAlignment(TEXT_ALIGN_TOP)
+	desc:SetFont("ZoFontGame")
+	desc:SetText(descriptionData.text)
+	desc:SetWidth(isHalfWidth and 250 or 510)
+
+	if descriptionData.title then
+		control.title = wm:CreateControl(nil, control, CT_LABEL)
+		local title = control.title
+		title:SetWidth(isHalfWidth and 250 or 510)
+		title:SetAnchor(TOPLEFT, control, TOPLEFT)
+		title:SetFont("ZoFontWinH4")
+		title:SetText(descriptionData.title)
+		desc:SetAnchor(TOPLEFT, title, BOTTOMLEFT)
+	else
+		desc:SetAnchor(TOPLEFT)
+	end
+
+	control.panel = parent.panel or parent	--if this is in a submenu, panel is its parent
+	control.data = descriptionData
+
+	return control
+
+end
\ No newline at end of file
diff --git a/libs/LibAddonMenu-2.0/controls/dropdown.lua b/libs/LibAddonMenu-2.0/controls/dropdown.lua
new file mode 100644
index 0000000..e338db6
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/dropdown.lua
@@ -0,0 +1,153 @@
+--[[dropdownData = {
+	type = "dropdown",
+	name = "My Dropdown",
+	tooltip = "Dropdown's tooltip text.",
+	choices = {"table", "of", "choices"},
+	sort = "name-up", --or "name-down", "numeric-up", "numeric-down" (optional) - if not provided, list will not be sorted
+	getFunc = function() return db.var end,
+	setFunc = function(var) db.var = var doStuff() end,
+	width = "full",	--or "half" (optional)
+	disabled = function() return db.someBooleanSetting end,	--or boolean (optional)
+	warning = "Will need to reload the UI.",	--(optional)
+	default = defaults.var,	--(optional)
+	reference = "MyAddonDropdown"	--(optional) unique global reference to control
+}	]]
+
+
+local widgetVersion = 3
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("dropdown", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local cm = CALLBACK_MANAGER
+local tinsert = table.insert
+
+
+local function UpdateDisabled(control)
+	local disable
+	if type(control.data.disabled) == "function" then
+		disable = control.data.disabled()
+	else
+		disable = control.data.disabled
+	end
+
+	control.dropdown:SetEnabled(not disable)
+	if disable then
+		control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+	else
+		control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+	end
+end
+
+local function UpdateValue(control, forceDefault, value)
+	if forceDefault then	--if we are forcing defaults
+		value = control.data.default
+		control.data.setFunc(value)
+		control.dropdown:SetSelectedItem(value)
+	elseif value then
+		control.data.setFunc(value)
+		--after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
+		if control.panel.data.registerForRefresh then
+			cm:FireCallbacks("LAM-RefreshPanel", control)
+		end
+	else
+		value = control.data.getFunc()
+		control.dropdown:SetSelectedItem(value)
+	end
+end
+
+local function DropdownCallback(choice, choiceText, choice)
+	choice.control:UpdateValue(false, choiceText)
+end
+
+local function UpdateChoices(control, choices)
+	control.dropdown:ClearItems()	--remove previous choices	--(need to call :SetSelectedItem()?)
+
+	--build new list of choices
+	local choices = choices or control.data.choices
+	for i = 1, #choices do
+		local entry = control.dropdown:CreateItemEntry(choices[i], DropdownCallback)
+		entry.control = control
+		control.dropdown:AddItem(entry, not control.data.sort and ZO_COMBOBOX_SUPRESS_UPDATE)	--if sort type/order isn't specified, then don't sort
+	end
+end
+
+local function GrabSortingInfo(sortInfo)
+	local t, i = {}, 1
+	for info in string.gmatch(sortInfo, "([^%-]+)") do
+		t[i] = info
+		i = i + 1
+	end
+
+	return t
+end
+
+
+local comboboxCount = 1
+function LAMCreateControl.dropdown(parent, dropdownData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName or dropdownData.reference)
+	control:SetParent(parent.scroll)
+	control:SetMouseEnabled(true)
+	control.tooltipText = dropdownData.tooltip
+	control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+	control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+
+	control.label = wm:CreateControl(nil, control, CT_LABEL)
+	local label = control.label
+	label:SetAnchor(TOPLEFT)
+	label:SetFont("ZoFontWinH4")
+	label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+	label:SetText(dropdownData.name)
+
+	control.combobox = wm:CreateControlFromVirtual(parent:GetName().."Combobox"..comboboxCount, control, "ZO_ComboBox")
+	comboboxCount = comboboxCount + 1
+	local combobox = control.combobox
+	combobox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end)
+	combobox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end)
+	control.dropdown = ZO_ComboBox_ObjectFromContainer(combobox)
+	local dropdown = control.dropdown
+	if dropdownData.sort then
+		local sortInfo = GrabSortingInfo(dropdownData.sort)
+		local sortType, sortOrder = sortInfo[1], sortInfo[2]
+		dropdown:SetSortOrder(sortOrder == "up" and ZO_SORT_ORDER_UP or ZO_SORT_ORDER_DOWN, sortType == "name" and ZO_SORT_BY_NAME or ZO_SORT_BY_NAME_NUMERIC)
+	end
+
+	local isHalfWidth = dropdownData.width == "half"
+	if isHalfWidth then
+		control:SetDimensions(250, 55)
+		label:SetDimensions(250, 26)
+		combobox:SetDimensions(240, 26)
+		--dropdown:SetWidth(240)
+		combobox:SetAnchor(TOPRIGHT, label, BOTTOMRIGHT)
+	else
+		control:SetDimensions(510, 30)
+		label:SetDimensions(300, 26)
+		combobox:SetDimensions(200, 26)
+		--dropdown:SetWidth(200)
+		combobox:SetAnchor(TOPRIGHT)
+	end
+
+	if warning then
+		control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+		control.warning:SetAnchor(RIGHT, combobox, LEFT, -5, 0)
+		control.warning.tooltipText = warningText
+	end
+
+	control.panel = parent.panel or parent	--if this is in a submenu, panel is its parent
+	control.data = dropdownData
+
+	if dropdownData.disabled then
+		control.UpdateDisabled = UpdateDisabled
+		control:UpdateDisabled()
+	end
+	control.UpdateChoices = UpdateChoices
+	control:UpdateChoices(dropdownData.choices)
+	control.UpdateValue = UpdateValue
+	control:UpdateValue()
+
+	if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then	--if our parent window wants to refresh controls, then add this to the list
+		tinsert(control.panel.controlsToRefresh, control)
+	end
+
+	return control
+end
\ No newline at end of file
diff --git a/libs/LibAddonMenu-2.0/controls/editbox.lua b/libs/LibAddonMenu-2.0/controls/editbox.lua
new file mode 100644
index 0000000..f2b4ded
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/editbox.lua
@@ -0,0 +1,153 @@
+--[[editboxData = {
+	type = "editbox",
+	name = "My Editbox",
+	tooltip = "Editbox's tooltip text.",
+	getFunc = function() return db.text end,
+	setFunc = function(text) db.text = text doStuff() end,
+	isMultiline = true,	--boolean
+	width = "full",	--or "half" (optional)
+	disabled = function() return db.someBooleanSetting end,	--or boolean (optional)
+	warning = "Will need to reload the UI.",	--(optional)
+	default = defaults.text,	--(optional)
+	reference = "MyAddonEditbox"	--(optional) unique global reference to control
+}	]]
+
+
+local widgetVersion = 3
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("editbox", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local cm = CALLBACK_MANAGER
+local tinsert = table.insert
+
+
+local function UpdateDisabled(control)
+	local disable
+	if type(control.data.disabled) == "function" then
+		disable = control.data.disabled()
+	else
+		disable = control.data.disabled
+	end
+
+	if disable then
+		control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+		control.editbox:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA())
+	else
+		control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+		control.editbox:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+	end
+	--control.editbox:SetEditEnabled(not disable)
+	control.editbox:SetMouseEnabled(not disable)
+end
+
+local function UpdateValue(control, forceDefault, value)
+	if forceDefault then	--if we are forcing defaults
+		value = control.data.default
+		control.data.setFunc(value)
+		control.editbox:SetText(value)
+	elseif value then
+		control.data.setFunc(value)
+		--after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
+		if control.panel.data.registerForRefresh then
+			cm:FireCallbacks("LAM-RefreshPanel", control)
+		end
+	else
+		value = control.data.getFunc()
+		control.editbox:SetText(value)
+	end
+end
+
+
+function LAMCreateControl.editbox(parent, editboxData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName or editboxData.reference)
+	control:SetParent(parent.scroll)
+	control:SetMouseEnabled(true)
+	control:SetResizeToFitDescendents(true)
+	control.tooltipText = editboxData.tooltip
+	control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+	control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+
+	control.label = wm:CreateControl(nil, control, CT_LABEL)
+	local label = control.label
+	label:SetAnchor(TOPLEFT)
+	label:SetFont("ZoFontWinH4")
+	label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+	label:SetText(editboxData.name)
+
+	control.bg = wm:CreateControlFromVirtual(nil, control, "ZO_EditBackdrop")
+	local bg = control.bg
+
+	if editboxData.isMultiline then
+		control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditMultiLineForBackdrop")
+		control.editbox:SetHandler("OnMouseWheel", function(self, delta)
+				if self:HasFocus() then	--only set focus to new spots if the editbox is currently in use
+					local cursorPos = self:GetCursorPosition()
+					local text = self:GetText()
+					local textLen = text:len()
+					local newPos
+					if delta > 0 then	--scrolling up
+						local reverseText = text:reverse()
+						local revCursorPos = textLen - cursorPos
+						local revPos = reverseText:find("\n", revCursorPos+1)
+						newPos = revPos and textLen - revPos
+					else	--scrolling down
+						newPos = text:find("\n", cursorPos+1)
+					end
+					if newPos then	--if we found a new line, then scroll, otherwise don't
+						self:SetCursorPosition(newPos)
+					end
+				end
+			end)
+	else
+		control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditForBackdrop")
+	end
+	local editbox = control.editbox
+	editbox:SetText(editboxData.getFunc())
+	editbox:SetMaxInputChars(3000)
+	editbox:SetHandler("OnFocusLost", function(self) control:UpdateValue(false, self:GetText()) end)
+	editbox:SetHandler("OnEscape", function(self) self:LoseFocus() control:UpdateValue(false, self:GetText()) end)
+	editbox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end)
+	editbox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end)
+
+	local isHalfWidth = editboxData.width == "half"
+	if isHalfWidth then
+		control:SetDimensions(250, 55)
+		label:SetDimensions(250, 26)
+		bg:SetDimensions(225, editboxData.isMultiline and 74 or 24)
+		bg:SetAnchor(TOPRIGHT, label, BOTTOMRIGHT)
+		if editboxData.isMultiline then
+			editbox:SetDimensionConstraints(210, 74, 210, 500)
+		end
+	else
+		control:SetDimensions(510, 30)
+		label:SetDimensions(300, 26)
+		bg:SetDimensions(200, editboxData.isMultiline and 100 or 24)
+		bg:SetAnchor(TOPRIGHT)
+		if editboxData.isMultiline then
+			editbox:SetDimensionConstraints(185, 100, 185, 500)
+		end
+	end
+
+	if editboxData.warning then
+		control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+		control.warning:SetAnchor(TOPRIGHT, control.bg, TOPLEFT, -5, 0)
+		control.warning.tooltipText = editboxData.warning
+	end
+
+	control.panel = parent.panel or parent	--if this is in a submenu, panel is its parent
+	control.data = editboxData
+
+	if editboxData.disabled then
+		control.UpdateDisabled = UpdateDisabled
+		control:UpdateDisabled()
+	end
+	control.UpdateValue = UpdateValue
+	control:UpdateValue()
+
+	if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then	--if our parent window wants to refresh controls, then add this to the list
+		tinsert(control.panel.controlsToRefresh, control)
+	end
+
+	return control
+end
\ No newline at end of file
diff --git a/libs/LibAddonMenu-2.0/controls/header.lua b/libs/LibAddonMenu-2.0/controls/header.lua
new file mode 100644
index 0000000..46250ca
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/header.lua
@@ -0,0 +1,36 @@
+--[[headerData = {
+	type = "header",
+	name = "My Header",
+	width = "full",	--or "half" (optional)
+	reference = "MyAddonHeader"	--(optional) unique global reference to control
+}	]]
+
+
+local widgetVersion = 2
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("header", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+function LAMCreateControl.header(parent, headerData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName or headerData.reference)
+	control:SetParent(parent.scroll)
+	local isHalfWidth = headerData.width == "half"
+	control:SetDimensions(isHalfWidth and 250 or 510, 30)
+
+	control.divider = wm:CreateControlFromVirtual(nil, control, "ZO_Options_Divider")
+	local divider = control.divider
+	divider:SetWidth(isHalfWidth and 250 or 510)
+	divider:SetAnchor(TOPLEFT)
+
+	control.header = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel")
+	local header = control.header
+	header:SetAnchor(TOPLEFT, divider, BOTTOMLEFT)
+	header:SetAnchor(BOTTOMRIGHT)
+	header:SetText(headerData.name)
+
+	control.panel = parent.panel or parent	--if this is in a submenu, panel is its parent
+	control.data = headerData
+
+	return control
+end
\ No newline at end of file
diff --git a/libs/LibAddonMenu-2.0/controls/panel.lua b/libs/LibAddonMenu-2.0/controls/panel.lua
new file mode 100644
index 0000000..de1beb1
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/panel.lua
@@ -0,0 +1,136 @@
+--[[panelData = {
+	type = "panel",
+	name = "Window Title",
+	displayName = "My Longer Window Title",	--(optional) (can be useful for long addon names or if you want to colorize it)
+	author = "Seerah",	--(optional)
+	version = "2.0",	--(optional)
+	slashCommand = "/myaddon",	--(optional) will register a keybind to open to this panel (don't forget to include the slash!)
+	registerForRefresh = true,	--boolean (optional) (will refresh all options controls when a setting is changed and when the panel is shown)
+	registerForDefaults = true,	--boolean (optional) (will set all options controls back to default values)
+	resetFunc = function() print("defaults reset") end,	--(optional) custom function to run after settings are reset to defaults
+}	]]
+
+
+local widgetVersion = 4
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("panel", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local cm = CALLBACK_MANAGER
+
+local function RefreshPanel(control)
+	local panel = control.panel or control	--callback can be fired by a single control or by the panel showing
+	local panelControls = panel.controlsToRefresh
+
+	for i = 1, #panelControls do
+		local updateControl = panelControls[i]
+		if  updateControl ~= control then
+			if updateControl.UpdateValue then
+				updateControl:UpdateValue()
+			end
+			if updateControl.UpdateDisabled then
+				updateControl:UpdateDisabled()
+			end
+		end
+	end
+end
+
+local function ForceDefaults(panel)
+	local panelControls = panel.controlsToRefresh
+
+	for i = 1, #panelControls do
+		local updateControl = panelControls[i]
+		if updateControl.UpdateValue and updateControl.data.default ~= nil then
+			updateControl:UpdateValue(true)
+		end
+	end
+
+	if panel.data.resetFunc then
+		panel.data.resetFunc()
+	end
+
+	cm:FireCallbacks("LAM-RefreshPanel", panel)
+end
+ESO_Dialogs["LAM_DEFAULTS"] = {
+	title = {
+		text = SI_OPTIONS_RESET_TITLE,
+	},
+	mainText = {
+		text = SI_OPTIONS_RESET_PROMPT,
+		align = TEXT_ALIGN_CENTER,
+	},
+	buttons = {
+		[1] = {
+			text = SI_OPTIONS_RESET,
+			callback = function(dialog) ForceDefaults(dialog.data[1]) end,
+		},
+		[2] = {
+			text = SI_DIALOG_CANCEL,
+		},
+	},
+}
+
+local scrollCount = 1
+function LAMCreateControl.panel(parent, panelData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName)
+	control:SetParent(parent)
+
+	control.bg = wm:CreateControl(nil, control, CT_BACKDROP)
+	local bg = control.bg
+	bg:SetAnchorFill()
+	bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16)
+	bg:SetCenterColor(0, 0, 0, 0)
+
+	control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel")
+	local label = control.label
+	label:SetAnchor(TOPLEFT, control, TOPLEFT, 10, 10)
+	label:SetText(panelData.displayName and panelData.displayName or panelData.name)
+
+	if panelData.author or panelData.version then
+		control.info = wm:CreateControl(nil, control, CT_LABEL)
+		local info = control.info
+		info:SetFont("$(CHAT_FONT)|14|soft-shadow-thin")
+		info:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA())
+		info:SetHeight(13)
+		info:SetAnchor(TOPRIGHT, control, BOTTOMRIGHT, -5, 2)
+		if panelData.author and panelData.version then
+			--info:SetText("Version: "..panelData.version.."  -  "..GetString(SI_ADDON_MANAGER_AUTHOR)..": "..panelData.author)
+			info:SetText(string.format("Version: %s  -  %s: %s", panelData.version, GetString(SI_ADDON_MANAGER_AUTHOR), panelData.author))
+		elseif panelData.author then
+			info:SetText(string.format("%s: %s", GetString(SI_ADDON_MANAGER_AUTHOR), panelData.author))
+		else
+			info:SetText("Version: "..panelData.version)
+		end
+	end
+
+	control.container = wm:CreateControlFromVirtual("LAMAddonPanelContainer"..scrollCount, control, "ZO_ScrollContainer")
+	scrollCount = scrollCount + 1
+	local container = control.container
+	container:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, 20)
+	container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, -3, -3)
+	control.scroll = GetControl(control.container, "ScrollChild")
+	control.scroll:SetResizeToFitPadding(0, 20)
+
+	if panelData.registerForDefaults then
+		control.defaultButton = wm:CreateControlFromVirtual(nil, control, "ZO_DefaultTextButton")
+		local defaultButton = control.defaultButton
+		defaultButton:SetFont("ZoFontDialogKeybindDescription")
+		defaultButton:SetHorizontalAlignment(TEXT_ALIGN_LEFT)
+		--defaultButton:SetText("Reset To Defaults")
+		defaultButton:SetText(GetString(SI_OPTIONS_RESET_TITLE))
+		defaultButton:SetDimensions(200, 30)
+		defaultButton:SetAnchor(TOPLEFT, control, BOTTOMLEFT, 0, 2)
+		defaultButton:SetHandler("OnClicked", function()
+				ZO_Dialogs_ShowDialog("LAM_DEFAULTS", {control})
+			end)
+	end
+
+	if panelData.registerForRefresh then
+		cm:RegisterCallback("LAM-RefreshPanel", RefreshPanel)
+	end
+
+	control.data = panelData
+	control.controlsToRefresh = {}
+
+	return control
+end
\ No newline at end of file
diff --git a/libs/LibAddonMenu-2.0/controls/slider.lua b/libs/LibAddonMenu-2.0/controls/slider.lua
new file mode 100644
index 0000000..b28e1b8
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/slider.lua
@@ -0,0 +1,182 @@
+--[[sliderData = {
+	type = "slider",
+	name = "My Slider",
+	tooltip = "Slider's tooltip text.",
+	min = 0,
+	max = 20,
+	step = 1,	--(optional)
+	getFunc = function() return db.var end,
+	setFunc = function(value) db.var = value doStuff() end,
+	width = "full",	--or "half" (optional)
+	disabled = function() return db.someBooleanSetting end,	--or boolean (optional)
+	warning = "Will need to reload the UI.",	--(optional)
+	default = defaults.var,	--(optional)
+	reference = "MyAddonSlider"	--(optional) unique global reference to control
+}	]]
+
+
+local widgetVersion = 2
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("slider", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local cm = CALLBACK_MANAGER
+local round = zo_round
+local strformat = string.format
+local tinsert = table.insert
+
+local function UpdateDisabled(control)
+	local disable
+	if type(control.data.disabled) == "function" then
+		disable = control.data.disabled()
+	else
+		disable = control.data.disabled
+	end
+
+	control.slider:SetEnabled(not disable)
+	control.slidervalue:SetEditEnabled(not disable)
+	if disable then
+		control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+		control.minText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+		control.maxText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA())
+		control.slidervalue:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA())
+	else
+		control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+		control.minText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+		control.maxText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+		control.slidervalue:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA())
+	end
+end
+
+local function UpdateValue(control, forceDefault, value)
+	if forceDefault then	--if we are forcing defaults
+		value = control.data.default
+		control.data.setFunc(value)
+	elseif value and value >= control.data.min and value <= control.data.max then
+		control.data.setFunc(value)
+		--after setting this value, let's refresh the others to see if any should be disabled or have their settings changed
+		if control.panel.data.registerForRefresh then
+			cm:FireCallbacks("LAM-RefreshPanel", control)
+		end
+	else
+		value = control.data.getFunc()
+	end
+
+	control.slider:SetValue(value)
+	control.slidervalue:SetText(value)
+end
+
+
+function LAMCreateControl.slider(parent, sliderData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName or sliderData.reference)
+	control:SetParent(parent.scroll)
+	local isHalfWidth = sliderData.width == "half"
+	if isHalfWidth then
+		control:SetDimensions(250, 55)
+	else
+		control:SetDimensions(510, 40)
+	end
+	control:SetMouseEnabled(true)
+	control.tooltipText = sliderData.tooltip
+	control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+	control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+
+	control.label = wm:CreateControl(nil, control, CT_LABEL)
+	local label = control.label
+	label:SetFont("ZoFontWinH4")
+	label:SetDimensions(isHalfWidth and 250 or 300, 26)
+	label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+	label:SetAnchor(isHalfWidth and TOPLEFT or LEFT)
+	label:SetText(sliderData.name)
+
+	--skipping creating the backdrop...  Is this the actual slider texture?
+	control.slider = wm:CreateControl(nil, control, CT_SLIDER)
+	local slider = control.slider
+	slider:SetDimensions(190, 14)
+	if isHalfWidth then
+		slider:SetAnchor(TOPRIGHT, label, BOTTOMRIGHT, -5, 2)
+	else
+		slider:SetAnchor(RIGHT, control, RIGHT, -5, -5)
+	end
+	slider:SetMouseEnabled(true)
+	slider:SetOrientation(ORIENTATION_HORIZONTAL)
+	--put nil for highlighted texture file path, and what look to be texture coords
+	slider:SetThumbTexture("EsoUI\\Art\\Miscellaneous\\scrollbox_elevator.dds", "EsoUI\\Art\\Miscellaneous\\scrollbox_elevator_disabled.dds", nil, 8, 16)
+	local minValue = sliderData.min
+	local maxValue = sliderData.max
+	slider:SetMinMax(minValue, maxValue)
+	slider:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end)
+	slider:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseExit(control) end)
+
+	slider.bg = wm:CreateControl(nil, slider, CT_BACKDROP)
+	local bg = slider.bg
+	bg:SetCenterColor(0, 0, 0)
+	bg:SetAnchor(TOPLEFT, slider, TOPLEFT, 0, 4)
+	bg:SetAnchor(BOTTOMRIGHT, slider, BOTTOMRIGHT, 0, -4)
+	bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-SliderBackdrop.dds", 32, 4)
+
+	control.minText = wm:CreateControl(nil, slider, CT_LABEL)
+	local minText = control.minText
+	minText:SetFont("ZoFontGameSmall")
+	minText:SetAnchor(TOPLEFT, slider, BOTTOMLEFT)
+	minText:SetText(sliderData.min)
+
+	control.maxText = wm:CreateControl(nil, slider, CT_LABEL)
+	local maxText = control.maxText
+	maxText:SetFont("ZoFontGameSmall")
+	maxText:SetAnchor(TOPRIGHT, slider, BOTTOMRIGHT)
+	maxText:SetText(sliderData.max)
+
+	control.slidervalueBG = wm:CreateControlFromVirtual(nil, slider, "ZO_EditBackdrop")
+	control.slidervalueBG:SetDimensions(50, 16)
+	control.slidervalueBG:SetAnchor(TOP, slider, BOTTOM, 0, 0)
+	control.slidervalue = wm:CreateControlFromVirtual(nil, control.slidervalueBG, "ZO_DefaultEditForBackdrop")
+	local slidervalue = control.slidervalue
+	slidervalue:ClearAnchors()
+	slidervalue:SetAnchor(TOPLEFT, slidervaluebg, TOPLEFT, 3, 1)
+	slidervalue:SetAnchor(BOTTOMRIGHT, slidervaluebg, BOTTOMRIGHT, -3, -1)
+	slidervalue:SetTextType(TEXT_TYPE_NUMERIC)
+	slidervalue:SetFont("ZoFontGameSmall")
+	slidervalue:SetHandler("OnEscape", function(self)
+			self:LoseFocus()
+			control:UpdateValue()
+		end)
+	slidervalue:SetHandler("OnEnter", function(self)
+			self:LoseFocus()
+			control:UpdateValue(false, tonumber(self:GetText()))
+		end)
+
+	local range = maxValue - minValue
+	slider:SetValueStep(sliderData.step or 1)
+	slider:SetHandler("OnValueChanged", function(self, value, eventReason)
+			if eventReason == EVENT_REASON_SOFTWARE then return end
+			self:SetValue(value)	--do we actually need this line?
+			slidervalue:SetText(value)
+		end)
+	slider:SetHandler("OnSliderReleased", function(self, value)
+			--sliderData.setFunc(value)
+			control:UpdateValue(false, value)	--does this work here instead?
+		end)
+
+	if sliderData.warning then
+		control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon")
+		control.warning:SetAnchor(RIGHT, slider, LEFT, -5, 0)
+		control.warning.tooltipText = sliderData.warning
+	end
+
+	control.panel = parent.panel or parent	--if this is in a submenu, panel is its parent
+	control.data = sliderData
+
+	if sliderData.disabled then
+		control.UpdateDisabled = UpdateDisabled
+		control:UpdateDisabled()
+	end
+	control.UpdateValue = UpdateValue
+	control:UpdateValue()
+
+	if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then	--if our parent window wants to refresh controls, then add this to the list
+		tinsert(control.panel.controlsToRefresh, control)
+	end
+
+	return control
+end
\ No newline at end of file
diff --git a/libs/LibAddonMenu-2.0/controls/submenu.lua b/libs/LibAddonMenu-2.0/controls/submenu.lua
new file mode 100644
index 0000000..b2b399d
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/submenu.lua
@@ -0,0 +1,97 @@
+--[[submenuData = {
+	type = "submenu",
+	name = "Submenu Title",
+	tooltip = "My submenu tooltip",	--(optional)
+	controls = {sliderData, buttonData}	--(optional) used by LAM
+	reference = "MyAddonSubmenu"	--(optional) unique global reference to control
+}	]]
+
+local widgetVersion = 2
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("submenu", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+local am = ANIMATION_MANAGER
+
+
+local function AnimateSubmenu(label)
+	local control = label:GetParent()
+	control.open = not control.open
+
+	if control.open then
+		control.animation:PlayFromStart()
+	else
+		control.animation:PlayFromEnd()
+	end
+end
+
+
+function LAMCreateControl.submenu(parent, submenuData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName or submenuData.reference)
+	control:SetParent(parent.scroll)
+	control.panel = parent
+	control:SetDimensions(523, 40)
+
+	control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel")
+	local label = control.label
+	label:SetAnchor(TOPLEFT, control, TOPLEFT, 5, 5)
+	label:SetDimensions(520, 30)
+	label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS)
+	label:SetText(submenuData.name)
+	label:SetMouseEnabled(true)
+	if submenuData.tooltip then
+		label.tooltipText = submenuData.tooltip
+		label:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+		label:SetHandler("OnMouseExit", ZO_Options_OnMouseExit)
+	end
+
+	control.scroll = wm:CreateControl(nil, control, CT_SCROLL)
+	local scroll = control.scroll
+	scroll:SetParent(control)
+	scroll:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, 10)
+	scroll:SetDimensionConstraints(525, 0, 525, 1500)
+
+	control.bg = wm:CreateControl(nil, label, CT_BACKDROP)
+	local bg = control.bg
+	bg:SetAnchor(TOPLEFT, label, TOPLEFT, -5, -5)
+	bg:SetAnchor(BOTTOMRIGHT, scroll, BOTTOMRIGHT, -7, 0)
+	bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16)
+	bg:SetCenterTexture("EsoUI\\Art\\Tooltips\\UI-TooltipCenter.dds")
+	bg:SetInsets(16, 16, -16, -16)
+
+	control.arrow = wm:CreateControl(nil, bg, CT_TEXTURE)
+	local arrow = control.arrow
+	arrow:SetDimensions(28, 28)
+	arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds")	--list_sortup for the other way
+	arrow:SetAnchor(TOPRIGHT, bg, TOPRIGHT, -5, 5)
+
+	--figure out the cool animation later...
+	control.animation = am:CreateTimeline()
+	local animation = control.animation
+	animation:SetPlaybackType(ANIMATION_SIZE, 0)	--2nd arg = loop count
+	--animation:SetDuration(1)
+	--animation:SetEasingFunction(ZO_LinearEase)	--is this needed?
+	--animation:SetHeightStartAndEnd(40, 80)	--SetStartAndEndHeight
+	--animation:SetStartAndEndHeight(40, 80)	--SetStartAndEndHeight
+	--animation:SetAnimatedControl(control)
+
+	control:SetResizeToFitDescendents(true)
+	control.open = false
+	label:SetHandler("OnMouseUp", AnimateSubmenu)
+	animation:SetHandler("OnStop", function(self, completedPlaying)
+			scroll:SetResizeToFitDescendents(control.open)
+			if control.open then
+				control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortup.dds")
+				scroll:SetResizeToFitPadding(5, 20)
+			else
+				control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds")
+				scroll:SetResizeToFitPadding(5, 0)
+				scroll:SetHeight(0)
+			end
+		end)
+
+	control.data = submenuData
+
+	return control
+end
+
diff --git a/libs/LibAddonMenu-2.0/controls/texture.lua b/libs/LibAddonMenu-2.0/controls/texture.lua
new file mode 100644
index 0000000..fc4be69
--- /dev/null
+++ b/libs/LibAddonMenu-2.0/controls/texture.lua
@@ -0,0 +1,50 @@
+--[[textureData = {
+	type = "texture",
+	image = "file/path.dds",
+	imageWidth = 64,	--max of 250 for half width, 510 for full
+	imageHeight = 32,	--max of 100
+	tooltip = "Image's tooltip text.",	--(optional)
+	width = "full",	--or "half" (optional)
+	reference = "MyAddonTexture"	--(optional) unique global reference to control
+}	]]
+
+--add texture coords support?
+
+local widgetVersion = 2
+local LAM = LibStub("LibAddonMenu-2.0")
+if not LAM:RegisterWidget("texture", widgetVersion) then return end
+
+local wm = WINDOW_MANAGER
+
+function LAMCreateControl.texture(parent, textureData, controlName)
+	local control = wm:CreateTopLevelWindow(controlName or textureData.reference)
+	control:SetResizeToFitDescendents(true)
+	control:SetParent(parent.scroll)
+
+	local isHalfWidth = textureData.width == "half"
+	if isHalfWidth then
+		control:SetDimensionConstraints(250, 55, 250, 100)
+		control:SetDimensions(250, 55)
+	else
+		control:SetDimensionConstraints(510, 30, 510, 100)
+		control:SetDimensions(510, 30)
+	end
+
+	control.texture = wm:CreateControl(nil, control, CT_TEXTURE)
+	local texture = control.texture
+	texture:SetAnchor(CENTER)
+	texture:SetDimensions(textureData.imageWidth, textureData.imageHeight)
+	texture:SetTexture(textureData.image)
+
+	if textureData.tooltip then
+		texture:SetMouseEnabled(true)
+		texture.tooltipText = textureData.tooltip
+		texture:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter)
+		texture:SetHandler("OnMouseEnter", ZO_Options_OnMouseExit)
+	end
+
+	control.panel = parent.panel or parent	--if this is in a submenu, panel is its parent
+	control.data = textureData
+
+	return control
+end
\ No newline at end of file