Files
Bagshui/Components/Inventory.Menus.lua
T
OEVG 896f46f0d1
enUS Localization Update Announcement / localization-update-announcement (push) Canceled after 0s
Bagshui 1.6.4
2026-10-30 15:55:43 +00:00

1243 lines
49 KiB
Lua

-- Bagshui Inventory Prototype: Menus
--
-- Most non-Settings menu-related things for Inventory classes are here, except for
-- the calls to open menus - those are in OnClick functions.
Bagshui:AddComponent(function()
local Inventory = Bagshui.prototypes.Inventory
-- Group menu for removing categories should not allow removing the default category (Uncategorized).
local GROUP_CATEGORY_REMOVE_MENU_IDS_TO_DISABLE = { BS_DEFAULT_CATEGORY_ID }
--- Prepare the per-Inventory-class-instance Menus class instance.
--- This includes building all the menus.
function Inventory:InitMenus()
-- Passing self to set the Menus class' inventoryClassInstance property so that
-- it will call Inventory:GenerateSettingsMenuItem() from Menus:LoadMenu() when
-- the menu item has a _bagshuiSettingName property.
self.menus = Bagshui.prototypes.Menus:New(self)
--- All Category menu entries should display which group they're assigned to.
--- Attaching this function to the `tooltipTextFunc` property of the `value`
--- table of the initiating menu item makes that happen.
---@param categoryId any Category ID.
---@return string tooltipText
self._autoSplitMenuItem_Categories_TooltipTextFunc = function(categoryId)
-- If assigned, return `Category is active in <Inventory Type> group '<Group Name>'`.
-- If unassigned, `Category is not currently in a group`.
local tooltipText = L.EditMode_CategoryNotInGroup
if self.categoriesToGroups[categoryId] and self.groups[self.categoriesToGroups[categoryId]] then
local row, column = self:GroupIdToRowColumn(self.categoriesToGroups[categoryId])
local rowColumnString = string.format(
GRAY_FONT_COLOR_CODE .. " [%s %s, %s %s]" .. FONT_COLOR_CODE_CLOSE,
L.Row,
row,
L.Column,
column
)
tooltipText = string.format(
L.EditMode_CategoryInGroup,
self.groups[self.categoriesToGroups[categoryId]].name or L.Unnamed
) .. rowColumnString
end
return tooltipText
end
-- Character menu.
-- This MUST happen before the main menu is generated because the `generateMenuValueFunc`
-- property of the Character entry on the menu references it.
self.menus:AddMenu(
"Character",
-- Level 1 is an auto-split Characters menu full replacement.
{
autoSplitMenuType = BS_AUTO_SPLIT_MENU_TYPE.CHARACTERS,
func = function(_, character)
self:SetCharacter(character)
end,
-- `self._characterMenuActiveCharacter` is managed by `Inventory:SetCharacter()` instead
-- of the `OpenMenu()` callback so that it's always up to date for both the toolbar
-- and the main menu.
idsToCheck = self._characterMenuActiveCharacter,
}
)
-- Main Menu.
self.menus:AddMenu(
"Main",
-- 1
{
-- Bagshui [Inventory Type]
{
text = BS_FONT_COLOR.BAGSHUI .. "Bagshui " .. self.inventoryTypeLocalized .. FONT_COLOR_CODE_CLOSE,
isTitle = true,
checked = false,
notCheckable = true,
},
{
text = L.EditMode,
tooltipText = L.Menu_Main_EditMode_TooltipText,
icon = BsUtil.GetFullTexturePath("Icons\\Edit"),
_bagshuiCheckedFunc = function()
return self.editMode
end,
func = function()
self:ToggleEditMode()
end,
},
{
text = string.format(L.Symbol_Ellipsis, L.Settings),
tooltipTitle = L.Settings,
tooltipText = L.Menu_Main_Settings_TooltipText,
value = "Settings",
checked = false,
icon = BsUtil.GetFullTexturePath("Icons\\Settings"),
func = function()
self.menus:OpenMenu("Settings")
end,
},
-- Actions (built out below from self.toolbarAndMainMenuItems).
{
text = L.Actions,
isTitle = true,
notCheckable = true,
checked = false,
},
-- Manage
{
text = L.Manage,
isTitle = true,
notCheckable = true,
checked = false,
},
{
text = string.format(L.Symbol_Ellipsis, L.Categories),
tooltipTitle = L.Categories,
tooltipText = L.Menu_Main_ManageCategories_TooltipText,
checked = false,
func = function()
BsCategories:Open()
end,
icon = BsUtil.GetFullTexturePath("Icons\\Category"),
},
{
text = string.format(L.Symbol_Ellipsis, L.SortOrders),
tooltipTitle = L.SortOrders,
tooltipText = L.Menu_Main_ManageSortOrders_TooltipText,
checked = false,
func = function()
BsSortOrders:Open()
end,
icon = BsUtil.GetFullTexturePath("Icons\\SortOrder"),
},
{
text = string.format(L.Symbol_Ellipsis, L.Profiles),
tooltipTitle = L.Profiles,
tooltipText = L.Menu_Main_ManageProfiles_TooltipText,
checked = false,
func = function()
BsProfiles:Open()
end,
icon = BsUtil.GetFullTexturePath("Icons\\Characters"),
},
-- Toggle (built out below).
{
text = L.Toggle,
isTitle = true,
notCheckable = true,
},
}
)
-- Add toolbar buttons as menu items.
-- Find insertion point.
local toolbarInsertPoint
for i, entry in ipairs(self.menus.menuList.Main.levels[1]) do
if entry.text == L.Actions then
toolbarInsertPoint = i + 1
break
end
end
-- Add to menu.
if toolbarInsertPoint then
for i = table.getn(self.toolbarAndMainMenuItems), 1, -1 do
local toolbarItem = self.toolbarAndMainMenuItems[i]
local menuItem = {
text = toolbarItem.tooltipTitle,
tooltipText = toolbarItem.tooltipText,
checked = false,
icon = toolbarItem.texture,
hasArrow = toolbarItem.menuHasArrow,
_bagshuiHideArrow = toolbarItem.menuHideArrow,
_bagshuiDisabledFunc = toolbarItem._bagshuiDisabledFunc or function()
return (self.ui.buttons.toolbar[toolbarItem.id]:IsEnabled() == 0)
end,
_bagshuiCheckedFunc = toolbarItem._bagshuiCheckedFunc,
}
if not toolbarItem.menuIgnoreOnClick then
menuItem.func = toolbarItem.func
or function()
self.ui.buttons.toolbar[toolbarItem.id]:GetScript("OnClick")()
end
end
if toolbarItem.getMenuValueProp then
menuItem.value = toolbarItem.getMenuValueProp()
end
table.insert(self.menus.menuList.Main.levels[1], toolbarInsertPoint, menuItem)
end
else
self:PrintWarning("Failed to find main menu insertion point for actions")
end
-- Add Toggle entries for inventory all types.
-- If anything is ever added below the Toggle menu section, this will need to
-- be modified to find the insertion point like the toolbar buttons above.
for inventoryType, inventoryTypeLocalized in self:OtherInventoryTypesInToolbarIconOrder(false, true) do
local inventoryClass = Bagshui.components[inventoryType]
table.insert(self.menus.menuList.Main.levels[1], {
text = inventoryTypeLocalized,
tooltipText = string.format(L.Menu_Main_Toggle_TooltipText, inventoryTypeLocalized),
_bagshuiHideFunc = function()
-- Hide the entry for the current inventory type unless the top toolbar is hidden.
return (inventoryClass == self and self.settings.showHeader)
end,
_bagshuiCheckedFunc = function()
return inventoryClass:Visible()
end,
func = function()
inventoryClass:Toggle(true)
end,
icon = BsUtil.GetFullTexturePath("Icons\\" .. inventoryType),
})
end
-- Catalog.
table.insert(self.menus.menuList.Main.levels[1], {
text = L.Catalog,
tooltipTitle = L.Toolbar_Catalog_TooltipTitle,
tooltipText = L.Toolbar_Catalog_TooltipText,
_bagshuiCheckedFunc = function()
return BsCatalog:Visible()
end,
func = function()
BsCatalog:Toggle(true)
end,
icon = BsUtil.GetFullTexturePath("Icons\\Catalog"),
})
-- Settings Menu.
-- Start with an empty menu since it'll be populated by BuildSettingsMenuFromConfig().
self.menus:AddMenu(
"Settings",
nil,
nil,
nil,
--- Settings OpenMenu callback.
---@param menu table Menu table.
function(menu)
self:OpenSettingsMenu(menu)
-- Prevent a second call to ShowMenu() from Menus:OpenMenuCallback().
return false
end
)
-- Populate the Settings menu.
self:BuildSettingsMenuFromConfig(
Bagshui.config.Settings[BS_SETTING_APPLICABILITY.INVENTORY],
self.menus.menuList.Settings,
1
)
-- Group Menu.
local groupDefaults = Bagshui.config.Settings[BS_SETTING_APPLICABILITY.GROUP]
local groupMenuFakeItem = BsUtil.TableCopy(BS_ITEM_SKELETON)
groupMenuFakeItem._bagshuiCategoryConfigTrigger = true
-- The Group menu can only be opened when Edit Mode is active, so it makes
-- sense to use the Edit Mode window update function when a Group setting
-- is changed instead of directly calling the Inventory:Update() function.
local editModeWindowUpdate = function()
self:EditModeWindowUpdate(false)
end
self.menus:AddMenu(
"Group",
-- 1
{
-- Group (first item is updated to the group name by the OpenMenu callback).
{
_bagshuiMenuItemId = "GroupNameTitle",
text = L.Group,
icon = BsUtil.GetFullTexturePath("Icons\\Group"),
isTitle = true,
notCheckable = true,
},
{
text = L.Settings,
tooltipTitle = L.Menu_Group_Settings_TooltipTitle,
tooltipText = L.Menu_Group_Settings_TooltipText,
value = "Options",
hasArrow = true,
},
{
text = L.Rename,
tooltipTitle = L.Menu_Group_Rename_TooltipTitle,
tooltipText = L.Menu_Group_Rename_TooltipText,
func = function(groupId)
self:RenameGroup(groupId, false)
end,
},
{
text = L.Move,
tooltipTitle = L.Menu_Group_Move_TooltipTitle,
tooltipText = L.Menu_Group_Move_TooltipText,
func = function(groupId)
self:EditModePickUpGroup(groupId)
end,
},
{
text = L.Delete,
tooltipTitle = L.Menu_Group_Delete_TooltipTitle,
tooltipText = L.Menu_Group_Delete_TooltipText,
func = function(groupId)
self:DeleteGroup(groupId)
end,
},
-- Categories indented sub-header.
{
text = BS_MENU_SUBTITLE_INDENT .. L.Categories,
icon = BsUtil.GetFullTexturePath("Icons\\Category"),
isTitle = true,
notCheckable = true,
},
{
-- The Add/Remove menu is the Categories auto-split menu with nothing hidden.
_bagshuiMenuItemId = "CategoryAddRemove",
text = L.AddSlashRemove,
tooltipTitle = string.format(L.Prefix_Add, L.Category),
tooltipText = L.Menu_Group_Add_Category_TooltipText,
value = {
-- Categories auto-split menu trigger and parameters.
autoSplitMenuType = BS_AUTO_SPLIT_MENU_TYPE.CATEGORIES,
func = function(groupId, categoryId)
self:ToggleCategoryGroupAssignment(categoryId, groupId, true)
end,
tooltipTextFunc = self._autoSplitMenuItem_Categories_TooltipTextFunc,
-- Disable categories (currently just Uncategorized) when they're checked to prevent removal from the layout.
disableFunc = function(categoryId, menuItem, triggeringMenuValue)
if
menuItem.checked
and BsUtil.TableContainsValue(GROUP_CATEGORY_REMOVE_MENU_IDS_TO_DISABLE, categoryId) ~= nil
then
return true
end
return false
end,
-- New...
autoSplitMenuExtraItems = {
{
text = string.format(L.Symbol_Ellipsis, L.New),
tooltipTitle = L.Menu_Group_New_Category_TooltipTitle,
tooltipText = L.Menu_Group_New_Category_TooltipText,
checked = false,
value = {
func = function(groupId, _)
local template = BsUtil.TableCopy(BS_NEW_CATEGORY_TEMPLATE)
BsCategories:NewObject(
template,
-- onFirstSave function
function(categoryId)
if string.len(categoryId or "") > 0 and string.len(groupId or "") > 0 then
self:AssignCategoryToGroup(categoryId, groupId, true)
end
end
)
end,
},
},
},
},
hasArrow = true,
},
{
-- The Move menu is the Categories auto-split menu with only the assigned categories visible.
_bagshuiMenuItemId = "CategoryMove",
text = L.Move,
tooltipTitle = string.format(L.Prefix_Move, L.Category),
tooltipText = L.Menu_Group_Move_Category_TooltipText,
value = {
-- Categories auto-split menu trigger and parameters.
autoSplitMenuType = BS_AUTO_SPLIT_MENU_TYPE.CATEGORIES,
func = function(_, categoryId)
self:EditModePickUpCategory(categoryId, true)
end,
tooltipTextFunc = self._autoSplitMenuItem_Categories_TooltipTextFunc,
},
hasArrow = true,
},
{
-- The Configure menu is the Categories auto-split menu with only the assigned categories visible.
_bagshuiMenuItemId = "CategoryConfig",
text = L.Menu,
tooltipTitle = string.format(L.Prefix_OpenMenuFor, L.Category),
tooltipText = L.Menu_Group_Configure_Category_TooltipText,
value = {
-- Categories auto-split menu trigger and parameters.
autoSplitMenuType = BS_AUTO_SPLIT_MENU_TYPE.CATEGORIES,
func = function(_, categoryId)
groupMenuFakeItem.bagshuiCategoryId = categoryId
self.menus:OpenMenu(
"Item",
groupMenuFakeItem,
nil,
_G.UIParent,
_G.DropDownList1:GetLeft(),
_G.DropDownList1:GetTop()
)
end,
tooltipTextFunc = self._autoSplitMenuItem_Categories_TooltipTextFunc,
},
hasArrow = true,
},
-- Sorting indented sub-header.
{
text = BS_MENU_SUBTITLE_INDENT .. L.Sorting,
icon = BsUtil.GetFullTexturePath("Icons\\SortOrder"),
isTitle = true,
notCheckable = true,
},
{
-- The Sort Order menu is the Sort Order auto-split menu with the active sort order checked.
text = L.SortOrder,
tooltipTitle = L.Menu_Group_SortOrder_TooltipTitle,
tooltipText = L.Menu_Group_SortOrder_TooltipText,
value = {
-- Sort Order auto-split menu trigger and parameters.
autoSplitMenuType = BS_AUTO_SPLIT_MENU_TYPE.SORT_ORDERS,
func = function(groupId, sortOrderId)
self:AssignSortOrderToGroup(sortOrderId, groupId)
end,
-- Add [Structure Default] to the end of the default sort order's name.
nameRevisionFunc = function(itemId, itemName)
return (itemId == self.settings.defaultSortOrder) and string.format(L.Suffix_Default, itemName)
or itemName
end,
-- Add a (Use Default) item that will be checked by the OpenMenu callback
-- if this group doesn't have a defined sort order.
autoSplitMenuExtraItems = {
{
text = "(" .. L.UseDefault .. ")",
tooltipTitle = L.Menu_Group_DefaultSortOrder_TooltipTitle,
checked = false,
value = {
func = function(groupId, _)
self:AssignSortOrderToGroup(nil, groupId)
end,
tooltipTextFunc = function()
return string.format(
L.Menu_Group_DefaultSortOrder_TooltipText,
LIGHTYELLOW_FONT_COLOR_CODE
.. BsSortOrders.list[self.settings.defaultSortOrder].name
.. FONT_COLOR_CODE_CLOSE
)
end,
},
},
},
},
hasArrow = true,
},
},
-- 2
{
-- Per-group settings.
Options = {
{
text = L.Menu_Settings_View,
isTitle = true,
notCheckable = true,
keepShownOnClick = true,
},
{
text = L.Menu_Group_HideGroup,
tooltipText = L.Menu_Group_HideGroup_TooltipText,
_bagshuiCheckedFunc = function(groupId)
return (self.groups[groupId] and self.groups[groupId].hide) or not self.groups[groupId]
end,
func = function(groupId)
if not self.groups[groupId] then
return
end
self.groups[groupId].hide = not self.groups[groupId].hide
self:EditModeWindowUpdate(false)
end,
},
{
text = L.Menu_Group_HideStockBadge,
tooltipText = L.Menu_Group_HideStockBadge_TooltipText,
_bagshuiSettingName = "hideStockBadge",
_bagshuiSettingInfo = groupDefaults.hideStockBadge,
_bagshuiSettingUpdateFunction = editModeWindowUpdate,
},
{
text = L.Background,
isTitle = true,
notCheckable = true,
keepShownOnClick = true,
},
{
text = L.UseDefault,
tooltipTitle = string.format(L.Menu_Group_DefaultColor_TooltipTitle, L.Background),
tooltipText = string.format(L.Menu_Group_DefaultColor_TooltipText, string.lower(L.Background)),
_bagshuiCheckedFunc = function(groupId)
return (self.groups[groupId] and self.groups[groupId].background == nil) or not self.groups[groupId]
end,
func = function(groupId)
if not self.groups[groupId] then
return
end
self.groups[groupId].background = nil
self:EditModeWindowUpdate(false)
end,
},
{
text = L.Color,
tooltipTitle = string.format(L.Menu_Group_Color_TooltipTitle, L.Background),
tooltipText = string.format(L.Menu_Group_Color_TooltipText, string.lower(L.Background)),
_bagshuiSettingName = "background",
_bagshuiSettingInfo = groupDefaults.background,
_bagshuiSettingUpdateFunction = editModeWindowUpdate,
},
{
text = L.Border,
isTitle = true,
notCheckable = true,
keepShownOnClick = true,
},
{
text = L.UseDefault,
tooltipTitle = string.format(L.Menu_Group_DefaultColor_TooltipTitle, L.Border),
tooltipText = string.format(L.Menu_Group_DefaultColor_TooltipText, string.lower(L.Border)),
_bagshuiCheckedFunc = function(groupId)
return (self.groups[groupId] and self.groups[groupId].border == nil) or not self.groups[groupId]
end,
func = function(groupId)
if not self.groups[groupId] then
return
end
self.groups[groupId].border = nil
self:EditModeWindowUpdate(false)
end,
},
{
text = L.Color,
tooltipTitle = string.format(L.Menu_Group_Color_TooltipTitle, L.Border),
tooltipText = string.format(L.Menu_Group_Color_TooltipText, string.lower(L.Border)),
_bagshuiSettingName = "border",
_bagshuiSettingInfo = groupDefaults.border,
_bagshuiSettingUpdateFunction = editModeWindowUpdate,
},
{
text = L.Label,
isTitle = true,
notCheckable = true,
keepShownOnClick = true,
},
{
text = L.UseDefault,
tooltipTitle = string.format(L.Menu_Group_DefaultColor_TooltipTitle, L.Label),
tooltipText = string.format(L.Menu_Group_DefaultColor_TooltipText, string.lower(L.Label)),
_bagshuiCheckedFunc = function(groupId)
return (self.groups[groupId] and self.groups[groupId].label == nil) or not self.groups[groupId]
end,
func = function(groupId)
if not self.groups[groupId] then
return
end
self.groups[groupId].label = nil
self:EditModeWindowUpdate(false)
end,
},
{
text = L.Color,
tooltipTitle = string.format(L.Menu_Group_Color_TooltipTitle, L.Label),
tooltipText = string.format(L.Menu_Group_Color_TooltipText, string.lower(L.Label)),
_bagshuiSettingName = "label",
_bagshuiSettingInfo = groupDefaults.label,
_bagshuiSettingUpdateFunction = editModeWindowUpdate,
},
},
},
-- 3
{
-- Color history menu entries.
-- This is normally done automatically in BuildSettingsMenuFromConfig(),
-- but since the group settings menu is manually constructed, we need
-- to manually create these as well.
background = {
_initializeEmptyMenu = {
_bagshuiSettingName = "background",
_bagshuiSettingInfo = groupDefaults.background,
_bagshuiSettingUpdateFunction = editModeWindowUpdate,
},
},
border = {
_initializeEmptyMenu = {
_bagshuiSettingName = "border",
_bagshuiSettingInfo = groupDefaults.border,
_bagshuiSettingUpdateFunction = editModeWindowUpdate,
},
},
label = {
_initializeEmptyMenu = {
_bagshuiSettingName = "border",
_bagshuiSettingInfo = groupDefaults.label,
_bagshuiSettingUpdateFunction = editModeWindowUpdate,
},
},
},
--- Group OpenMenu callback.
---@param menu table Menu table.
---@param uiGroup table Group user interface object.
function(menu, uiGroup)
local groupId = uiGroup.bagshuiData.groupId
local group = self.groups[groupId]
local groupMenu = menu
for _, menuItem in ipairs(groupMenu.levels[1]) do
if menuItem._bagshuiMenuItemId == "GroupNameTitle" then
-- Update the menu title to the group name.
menuItem.text = string.len(group.name or "") > 0
and (string.format(L.Symbol_Colon, L.Group) .. " " .. LIGHTYELLOW_FONT_COLOR_CODE .. BsUtil.TruncateString(
group.name,
45
) .. FONT_COLOR_CODE_CLOSE)
or L.Group
elseif type(menuItem.value) == "table" and menuItem.value.autoSplitMenuType then
-- Category/SortOrder auto-split submenu updates.
-- Auto-split menus pass arg1 via value.objectId.
menuItem.value.objectId = groupId
if menuItem.value.autoSplitMenuType == BS_AUTO_SPLIT_MENU_TYPE.CATEGORIES then
if menuItem._bagshuiMenuItemId == "CategoryAddRemove" then
-- "Add" needs the full category list with already assigned categories checked.
menuItem.value.idsToCheck = group.categories
elseif
menuItem._bagshuiMenuItemId == "CategoryMove" or menuItem._bagshuiMenuItemId == "CategoryConfig"
then
-- "Remove" needs only the already assigned categories (if any).
if group.categories and table.getn(group.categories) > 0 then
menuItem.disabled = false
menuItem.hasArrow = true
menuItem.value.idList = group.categories
else
-- This will be hit when there are no assigned categories, disabling these options.
menuItem.disabled = true
menuItem.hasArrow = false
end
end
elseif menuItem.value.autoSplitMenuType == BS_AUTO_SPLIT_MENU_TYPE.SORT_ORDERS then
-- Control whether "(Use Default)" is checked.
menuItem.value.autoSplitMenuExtraItems[1].checked = (group.sortOrder == nil)
-- Check the currently selected item.
if group.sortOrder then
menuItem.value.idsToCheck = { group.sortOrder }
else
menuItem.value.idsToCheck = nil
end
end
end
end
-- Add properties to all menu items (this is fine to do for all of them even if it is a little brute force-y).
self.menus:SetPropertyOnAllMenuItems(groupMenu, "arg1", groupId)
self.menus:SetPropertyOnAllMenuItems(groupMenu, "_bagshuiSettingsStorage", group)
end
)
-- Item Menu.
-- Shared OnClick function for itemInfo submenu to display the itemInfo window.
local function showItemInfo(item)
Bagshui:CloseMenus()
BsItemInfo:Open(item)
end
-- Reusable tables for item/category relationships.
self._itemMenuDirectCategoryAssignmentList = {}
self._itemMenuMatchedCategoryList = {}
self._itemMenuAssignedCategoryList = { "Uncategorized" }
-- Generate menu items for equipping bags.
-- This needs to contain entries from all inventory classes that have equippable bags.
local bagSwapMenu = {}
for _, inventoryClassName in ipairs(BS_INVENTORY_TYPE_UI_ORDER) do
local inventoryClass = Bagshui.components[inventoryClassName]
if
inventoryClass
and inventoryClass.containerIds
-- Need more than a primary container.
and table.getn(inventoryClass.containerIds) > 1
then
table.insert(bagSwapMenu, {
text = inventoryClass.inventoryTypeLocalized,
isTitle = true,
notCheckable = true,
})
for _, containerId in ipairs(inventoryClass.containerIds) do
if containerId ~= inventoryClass.primaryContainer.id then
table.insert(bagSwapMenu, {
text = containerId,
notCheckable = true,
-- arg1 will be the item.
arg2 = {
containerId = containerId,
inventoryClass = inventoryClass,
},
_bagshuiTextureR = 1,
_bagshuiTextureG = 1,
_bagshuiTextureB = 1,
func = function(item, bagInfo)
Bagshui:CloseMenus()
bagInfo.inventoryClass:SwapBag(item, bagInfo.containerId)
end,
})
end
end
end
end
self.menus:AddMenu(
"Item",
-- 1
{
-- Category title (will be renamed by the OpenMenu callback.)
{
_bagshuiMenuItemId = "CategoryTitle",
_bagshuiItemMenuCategoryEntry = true,
text = L.Category,
isTitle = true,
notCheckable = true,
icon = BsUtil.GetFullTexturePath("Icons\\Category"),
_bagshuiTextureSize = 12,
_bagshuiTextureXOffset = -12,
},
-- arg1 and arg2 are assigned by the OpenMenu callback
{
_bagshuiItemMenuCategoryEntry = true,
text = L.Move,
tooltipTitle = string.format(L.Prefix_Move, L.Category),
tooltipText = L.Menu_Category_Move_TooltipText,
func = function(_, categoryId)
self:EditModePickUpCategory(categoryId, true)
end,
},
{
_bagshuiItemMenuCategoryEntry = true,
text = L.Remove,
tooltipTitle = string.format(L.Prefix_Remove, L.Category),
tooltipText = L.Menu_Category_Remove_TooltipText,
func = function(groupId, categoryId)
self:RemoveCategoryFromGroup(categoryId, groupId)
self:EditModeWindowUpdate(true)
end,
},
{
_bagshuiItemMenuCategoryEntry = true,
text = string.format(L.Symbol_Ellipsis, L.Edit),
tooltipTitle = string.format(L.Prefix_Edit, L.Category),
tooltipText = L.Menu_Category_Edit_TooltipText,
func = function(_, categoryId)
BsCategories:EditObject(categoryId)
self:CloseMenusAndClearFocuses()
end,
},
{
_bagshuiItemMenuCategoryEntry = true,
text = string.format(L.Symbol_Ellipsis, L.Manage),
tooltipTitle = string.format(L.Prefix_Manage, L.Categories),
tooltipText = L.Menu_Main_ManageCategories_TooltipText,
func = function()
BsCategories:Open()
self:CloseMenusAndClearFocuses()
end,
},
-- Item title (will be renamed by the OpenMenu callback.)
{
_bagshuiMenuItemId = "ItemNameTitle",
text = L.Item,
isTitle = true,
notCheckable = true,
_bagshuiTextureR = 1,
_bagshuiTextureG = 1,
_bagshuiTextureB = 1,
},
-- Tracked Item Bar.
{
_bagshuiMenuItemId = "ItemTrack",
text = L.Menu_Item_TrackItem,
tooltipText = L.Menu_Item_TrackItem_TooltipText,
disabled = true,
keepShownOnClick = false,
func = function(item)
Bagshui.components.TrackedItemBar:Toggle(item.id, item.texture)
self:CloseMenusAndClearFocuses()
end,
},
{
text = L.Information,
tooltipTitle = L.Menu_Item_Information_TooltipTitle,
tooltipText = L.Menu_Item_Information_TooltipText,
hasArrow = true,
value = {
value = "itemInfo",
},
},
{
text = L.Manage,
tooltipTitle = L.Menu_Item_Manage_TooltipTitle,
tooltipText = L.Menu_Item_Manage_TooltipText,
hasArrow = true,
value = {
value = "manage",
},
},
{
text = L.Move,
tooltipTitle = string.format(L.Prefix_Move, L.Item),
tooltipText = L.Menu_Item_Move_TooltipText,
_bagshuiMenuItemId = "ItemMove",
func = function(item)
self:EditModePickUpItem(item)
end,
},
-- Indented subheader for item categories menu.
{
text = BS_MENU_SUBTITLE_INDENT .. L.Categories,
isTitle = true,
notCheckable = true,
},
-- Matched Categories is a Categories auto-spilt menu that only contains the list of
-- categories that this item could potentially match, as produced by
-- Categories:GetAllMatchingCategoriesForItem(). The category that the item has
-- actually matched and is currently in will be checked. These updates are made
-- in the OpenMenu callback function.
{
text = L.Menu_Item_MatchedCategories,
tooltipTitle = L.Menu_Item_MatchedCategories_TooltipTitle,
tooltipText = L.Menu_Item_MatchedCategories_TooltipText,
value = {
autoSplitMenuType = BS_AUTO_SPLIT_MENU_TYPE.CATEGORIES,
func = function(itemId, categoryId)
BsCategories:EditObject(categoryId)
end,
sortFunc = function(categoryIds)
BsCategories:SortIdList(categoryIds, nil, "sequence")
end,
nameFunc = function(categoryId)
-- Prefix the category name with the sequence number.
-- Gray out the category name if it's not assigned to the current layout.
local fontColor = ""
if not (self.categoriesToGroups[categoryId] and self.groups[self.categoriesToGroups[categoryId]]) then
fontColor = GRAY_FONT_COLOR_CODE
elseif categoryId == self._itemMenuAssignedCategoryList[1] then
fontColor = NORMAL_FONT_COLOR_CODE
end
return fontColor
.. string.format(L.Symbol_Colon, BsCategories.list[categoryId].sequence)
.. " "
.. (BsCategories:GetName(categoryId, false, GRAY_FONT_COLOR_CODE) or tostring(categoryId))
.. ((fontColor ~= "") and FONT_COLOR_CODE_CLOSE or "")
end,
tooltipTextFunc = function(categoryId)
local layoutLocation = LIGHTYELLOW_FONT_COLOR_CODE
.. self._autoSplitMenuItem_Categories_TooltipTextFunc(categoryId)
.. FONT_COLOR_CODE_CLOSE
return NORMAL_FONT_COLOR_CODE
.. L.Menu_Item_MatchedCategory_TooltipText
.. FONT_COLOR_CODE_CLOSE
.. BS_NEWLINE
.. layoutLocation
end,
-- These tables are updated by the OpenMenu callback.
idList = self._itemMenuMatchedCategoryList,
idsToCheck = self._itemMenuAssignedCategoryList,
-- Never show the default category (Uncategorized) since every item matches it.
idsToOmit = { BS_DEFAULT_CATEGORY_ID },
},
hasArrow = true,
},
-- Direct Assignment is a Categories auto-split menu that contains all
-- editable (custom) NON-CLASS categories. Any categories the item is directly
-- assigned to will be checked. These updates are performed by the
-- OpenMenu callback function.
{
_bagshuiMenuItemId = "ItemDirectCategoryAssignment",
text = L.Menu_Item_AssignToCategory,
tooltipTitle = L.Menu_Item_AssignToCategory_TooltipTitle,
tooltipText = L.Menu_Item_AssignToCategory_TooltipText
.. BS_NEWLINE
.. LIGHTYELLOW_FONT_COLOR_CODE
.. L.Menu_Item_AssignToCategory_Hint_CustomOnly
.. FONT_COLOR_CODE_CLOSE,
value = {
autoSplitMenuType = BS_AUTO_SPLIT_MENU_TYPE.CATEGORIES,
func = function(itemAndGroupId, categoryId)
if BsCategories.list[categoryId].classes then
self.menus:OpenMenu("DirectAssignmentClasses", itemAndGroupId, categoryId, "cursor")
else
BsCategories:ToggleItemCategoryAssignment(categoryId, itemAndGroupId.item.id)
self:EditModeWindowUpdate(true)
end
end,
nameFunc = function(categoryId)
-- Need to override nameFunc to turn off duplicate suffix since we're only showing custom categories.
return BsCategories:GetName(categoryId, true) or tostring(categoryId)
end,
tooltipTextFunc = self._autoSplitMenuItem_Categories_TooltipTextFunc,
idsToCheck = self._itemMenuDirectCategoryAssignmentList,
idList = BsCategories.filteredIdLists.custom,
-- This will be passed as arg1 to the auto-split menu func()s.
objectId = {
item = nil,
groupId = nil,
},
autoSplitMenuExtraItems = {
{
text = string.format(L.Symbol_Ellipsis, L.New),
tooltipText = L.Menu_Item_AssignToCategory_CreateNew_TooltipText
.. BS_NEWLINE
.. LIGHTYELLOW_FONT_COLOR_CODE
.. L.Menu_Item_AssignToCategory_Hint_CustomOnly
.. FONT_COLOR_CODE_CLOSE,
checked = false,
value = {
func = function(itemAndGroupId, _)
local template = BsUtil.TableCopy(BS_NEW_CATEGORY_TEMPLATE)
table.insert(template.list, itemAndGroupId.item.id)
BsCategories:NewObject(
template,
-- onFirstSave callback.
function(categoryId)
if string.len(categoryId or "") > 0 and string.len(itemAndGroupId.groupId or "") > 0 then
self:AssignCategoryToGroup(categoryId, itemAndGroupId.groupId, true)
end
end
)
end,
},
},
},
},
hasArrow = true,
},
},
-- 2
{
-- Populated by the OpenMenu callback function.
itemInfo = {},
manage = {
-- Remove from worn gear list.
{
_bagshuiMenuItemId = "ItemRemoveFromWorn",
text = L.Menu_Item_RemoveFromEquippedGear,
tooltipText = L.Menu_Item_RemoveFromEquippedGear_TooltipText,
disabled = true,
keepShownOnClick = false,
func = function(item)
BsCharacter:RemoveFromEquippedGear(item.itemString)
self:CloseMenusAndClearFocuses()
end,
},
-- Reset item stock state.
{
_bagshuiMenuItemId = "ItemResetStockState",
text = L.Menu_Item_ResetStockState,
tooltipText = L.Menu_Item_ResetStockState_TooltipText,
disabled = true,
keepShownOnClick = false,
func = function(item)
self:ResetStockState()
end,
},
-- Bag menu for equipping.
{
_bagshuiMenuItemId = "ItemEquipBag",
text = L.Menu_Item_EquipBag,
tooltipText = L.Menu_Item_EquipBag_TooltipText,
hasArrow = true,
disabled = true,
value = {
value = "bags",
},
},
},
},
-- 3
{
bags = bagSwapMenu,
},
--- Item OpenMenu callback.
---@param menu table Menu table.
---@param item table Bagshui item table.
---@param groupId string Group to which the item is being assigned. When present, all other menu items will be suppressed and only the direct category assignment submenu will be available.
function(menu, item, groupId)
local itemMenu = menu
local directAssignmentOnlyMode = (groupId ~= nil)
local directAssignmentCategoryIds = (groupId and self.groups[groupId] and self.groups[groupId].categories)
or nil
local isMeaningfulItemId = ((item.id or 0) > 0)
-- Get list of categories to which this item is manually assigned.
-- This table is assigned to the idsToCheck property of the
-- Direct Assignment auto-split submenu. It's also used to decide
-- whether the parent Direct Assignment item should be checked.
BsCategories:GetDirectCategoryAssignmentsForItem(
item.id,
self._itemMenuDirectCategoryAssignmentList,
directAssignmentCategoryIds
)
-- Update menu titles and values.
for _, menuItem in pairs(itemMenu.levels[1]) do
menuItem._bagshuiHide = false
-- Category: Move/Remove/Edit/Configure need values to pass through to their func()s.
if menuItem._bagshuiItemMenuCategoryEntry then
menuItem.arg1 = self.categoriesToGroups[item.bagshuiCategoryId]
menuItem.arg2 = item.bagshuiCategoryId
if not self.editMode then
menuItem._bagshuiHide = true
end
elseif item._bagshuiCategoryConfigTrigger then
menuItem._bagshuiHide = true
end
if menuItem._bagshuiMenuItemId == "CategoryTitle" then
-- Category: Set category name.
menuItem.text = string.format(L.Symbol_Colon, L.Category)
.. " "
.. LIGHTYELLOW_FONT_COLOR_CODE
.. (BsCategories:GetName(item.bagshuiCategoryId) or L.Error_ItemCategoryUnknown)
.. FONT_COLOR_CODE_CLOSE
elseif menuItem._bagshuiMenuItemId == "ItemNameTitle" then
-- Item: Set item name and texture.
menuItem.text = string.format(L.Symbol_Colon, L.Item)
.. " "
.. LIGHTYELLOW_FONT_COLOR_CODE
.. BsUtil.TruncateString(item.name, 45)
.. FONT_COLOR_CODE_CLOSE
menuItem.icon = item.texture
if BsSkin.itemSlotIconTexCoord then
menuItem.tCoordLeft = BsSkin.itemSlotIconTexCoord[1]
menuItem.tCoordRight = BsSkin.itemSlotIconTexCoord[2]
menuItem.tCoordTop = BsSkin.itemSlotIconTexCoord[3]
menuItem.tCoordBottom = BsSkin.itemSlotIconTexCoord[4]
end
elseif menuItem._bagshuiMenuItemId == "ItemTrack" then
-- Item: Track/untrack on the Tracked Item Bar.
menuItem.arg1 = item
menuItem.disabled = not isMeaningfulItemId
menuItem.text = Bagshui.components.TrackedItemBar:IsTracked(item.id) and L.Menu_Item_StopTrackingItem
or L.Menu_Item_TrackItem
elseif menuItem._bagshuiMenuItemId == "ItemMove" then
-- Item: Disable the Move menu for empty slots since they can't be directly assigned to a category, only via a rule function.
menuItem.disabled = not isMeaningfulItemId
menuItem.arg1 = item
menuItem._bagshuiHide = not self.editMode
elseif menuItem._bagshuiMenuItemId == "ItemDirectCategoryAssignment" then
-- Item: Disable the Direct Assignment menu for empty slots since they can't be directly assigned to a category, only via a rule function.
menuItem.disabled = not isMeaningfulItemId
menuItem.value.objectId.item = item
menuItem.value.objectId.groupId = groupId or self.categoriesToGroups[item.bagshuiCategoryId]
-- Check it if any categories are manually assigned.
menuItem.checked = table.getn(self._itemMenuDirectCategoryAssignmentList) > 0
menuItem.value.idList = directAssignmentCategoryIds or BsCategories.filteredIdLists.custom
menuItem.value.idsToOmit = directAssignmentCategoryIds and BsCategories.filteredIdLists.builtin or nil
end
-- Item: Hide everything other than Direct Assignment
-- when an item is picked up in Edit Mode and dropped on a group.
if
menuItem._bagshuiHide == false -- Don't force anything to be shown that is already hidden.
and menuItem._bagshuiMenuItemId ~= "ItemNameTitle"
and menuItem._bagshuiMenuItemId ~= "ItemDirectCategoryAssignment"
and not item._bagshuiCategoryConfigTrigger
then
menuItem._bagshuiHide = (directAssignmentCategoryIds ~= nil)
end
end
-- Item: These submenus only need to be updated on a normal right-click,
-- not when an item has been dropped on a group in Edit Mode.
if not directAssignmentOnlyMode then
-- Update Manage submenu.
for _, menuItem in pairs(itemMenu.levels[2].manage) do
menuItem.arg1 = item
if menuItem._bagshuiMenuItemId == "ItemRemoveFromWorn" then
menuItem.disabled = not BsCharacter.equippedHistory[item.itemString]
elseif menuItem._bagshuiMenuItemId == "ItemResetStockState" then
menuItem.disabled = item.bagshuiStockState == BS_ITEM_STOCK_STATE.NO_CHANGE
elseif menuItem._bagshuiMenuItemId == "ItemEquipBag" then
menuItem.disabled = not BsItemInfo:IsContainer(item)
-- Update the bag slot menu to reflect current bags.
if not menuItem.disabled then
for index, bagMenuItem in pairs(itemMenu.levels[3].bags) do
if not bagMenuItem.isTitle then
bagMenuItem.arg1 = item
local inventoryClass = bagMenuItem.arg2.inventoryClass
local containerInfo = inventoryClass.containers[bagMenuItem.arg2.containerId]
if containerInfo.numSlots > 0 then
bagMenuItem.text = containerInfo.name
bagMenuItem.icon = containerInfo.texture
else
bagMenuItem.text = string.format(L.Symbol_Parentheses, L.EmptyBagSlot)
bagMenuItem.icon = BsUtil.GetFullTexturePath(BS_INVENTORY_EMPTY_SLOT_TEXTURE[L.Bag])
end
bagMenuItem.tooltipTitle = containerInfo.name
bagMenuItem.tooltipText = inventoryClass:GetBagSlotSwappableText(bagMenuItem.arg2.containerId)
bagMenuItem.disabled = (
not inventoryClass.online
-- Disable if the slot isn't usable. This is primarily for the Bank to disable unpurchased slots.
or containerInfo.usable == false
)
end
end
end
end
end
-- Add items to Information submenu.
local infoMenu = itemMenu.levels[2].itemInfo
local infoMenuItemCount = 0
for _, itemPropertyFriendly, _, itemPropertyDisplay in BsItemInfo:ItemPropertyValuesForDisplay(item) do
infoMenuItemCount = infoMenuItemCount + 1
infoMenu[infoMenuItemCount]._bagshuiHide = false
infoMenu[infoMenuItemCount].text = string.format(L.Symbol_Colon, itemPropertyFriendly)
.. " "
.. itemPropertyDisplay
infoMenu[infoMenuItemCount].tooltipText = LIGHTYELLOW_FONT_COLOR_CODE
.. L.Menu_Item_Information_Submenu_TooltipText
.. FONT_COLOR_CODE_CLOSE
infoMenu[infoMenuItemCount].arg1 = item
end
-- Hide unused menu items.
self.menus:HideMenuItems(infoMenu, infoMenuItemCount + 1)
-- Update Matched Categories submenu.
BsCategories:GetAllMatchingCategoriesForItem(item, nil, self._itemMenuMatchedCategoryList)
self._itemMenuAssignedCategoryList[1] = item.bagshuiCategoryId
end
-- Hide tooltips that sometimes get stuck.
Bagshui:HideTooltips(nil, true)
end
)
-- Fill the item info level 2 menus with placeholder entries that will be
-- updated by the OpenMenu callback.
self.menus:InitializeEmptyMenu(self.menus.menuList.Item.levels[2].itemInfo, {
text = "",
arg1 = "",
func = showItemInfo,
})
-- Direct Assignment Class Selection Menu.
-- Triggered when a Class Category is chosen from the Direct Assignment menu.
local function toggleClassCategoryDirectAssignment(arg1)
BsCategories:ToggleItemCategoryAssignment(arg1.categoryId, arg1.itemId, _G.this.value)
end
local classMenu = BsUtil.TableCopy(BsGameInfo.characterClassMenu)
-- Reusable table for passing values through to the callback function.
local classMenuArg1 = {}
for _, menuItem in ipairs(classMenu) do
menuItem.arg1 = classMenuArg1
menuItem.func = toggleClassCategoryDirectAssignment
end
table.insert(classMenu, 1, {
text = "Item placeholder",
isTitle = true,
notCheckable = true,
})
table.insert(classMenu, 2, {
text = LIGHTYELLOW_FONT_COLOR_CODE .. L.Menu_Item_AssignToClassCategory .. FONT_COLOR_CODE_CLOSE,
isTitle = true,
notCheckable = true,
})
table.insert(classMenu, 3, {
text = "Category placeholder",
isTitle = true,
notCheckable = true,
})
self.menus:AddMenu(
"DirectAssignmentClasses",
classMenu,
-- Nothing for level 2/3.
nil,
nil,
-- OpenMenu callback.
function(menu, itemAndGroupId, categoryId)
classMenuArg1.itemId = itemAndGroupId.item.id
classMenuArg1.categoryId = categoryId
menu.levels[1][1].text = itemAndGroupId.item.name
menu.levels[1][1].icon = itemAndGroupId.item.texture
menu.levels[1][1]._bagshuiTextureR = 1
menu.levels[1][1]._bagshuiTextureG = 1
menu.levels[1][1]._bagshuiTextureB = 1
if BsSkin.itemSlotIconTexCoord then
menu.levels[1][1].tCoordLeft = BsSkin.itemSlotIconTexCoord[1]
menu.levels[1][1].tCoordRight = BsSkin.itemSlotIconTexCoord[2]
menu.levels[1][1].tCoordTop = BsSkin.itemSlotIconTexCoord[3]
menu.levels[1][1].tCoordBottom = BsSkin.itemSlotIconTexCoord[4]
end
menu.levels[1][3].text = BsCategories:GetName(categoryId, true) or tostring(categoryId)
for i = 4, table.getn(menu.levels[1]) do
local menuItem = menu.levels[1][i]
menuItem.checked =
BsCategories:ItemInDirectAssignmentList(nil, categoryId, itemAndGroupId.item.id, nil, nil, menuItem.value)
end
end
)
end
end)