Files
Bagshui/Components/Inventory.Ui.Group.lua
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

490 lines
18 KiB
Lua

-- Bagshui Inventory Prototype: Groups
Bagshui:AddComponent(function()
local Inventory = Bagshui.prototypes.Inventory
local InventoryUi = Bagshui.prototypes.InventoryUi
--#region Groups
--- Trigger Edit Mode colors and tooltips.
--- This is part of the Inventory class instead of a local function like the other
--- group script functions so that it's available to call from Inventory:ItemSlotAndGroupMouseOverCheck().
---@param group table Group UI frame.
function Inventory:Group_OnEnter(group)
local this = group or _G.this
local ui = this.bagshuiData.ui
local inventory = ui.inventory
-- It feels weird to have colors flashing under the cursor and tooltips appearing when menus are open.
-- (The IsMouseEnabled() check is just a safeguard.)
if inventory.menus:IsMenuOpen() or not this:IsMouseEnabled() then
return
end
-- Actual mouseover status tracking, since MouseIsOver() returns true even
-- when other frames are in front of this one.
this.bagshuiData.mouseIsOver = true
-- Update colors and borders for mouseover.
ui:SetGroupColors(this, false)
-- Show tooltip in Edit Mode when the user hasn't picked up an object.
if inventory.editMode and not inventory:EditModeCursorHasItem() then
local groupId = this.bagshuiData.groupId
local groupName = string.len(inventory.groups[groupId].name or "") > 0 and inventory.groups[groupId].name
or L.UnnamedGroup
-- Prepare tooltips.
_G.GameTooltip:SetOwner(this, "ANCHOR_" .. BsUtil.FlipAnchorPoint(inventory.settings.windowAnchorXPoint))
-- Format the group name.
_G.GameTooltip:AddLine(Bagshui:FormatTooltipLine(groupName, string.format(L.Symbol_Colon, L.Group), true))
-- Group is hidden.
if inventory.groups[groupId] and inventory.groups[groupId].hide then
_G.GameTooltip:AddLine(GRAY_FONT_COLOR_CODE .. "«" .. L.Hidden .. "»" .. FONT_COLOR_CODE_CLOSE)
end
-- Build bulleted category list.
_G.GameTooltip:AddLine(string.format(L.Symbol_Colon, L.Categories), nil, nil, nil, true) -- "Categories:"
local categoryList = inventory.groups[groupId] and inventory.groups[groupId].categories
if type(categoryList) == "table" and table.getn(categoryList) > 0 then
-- Need to smash them all into one line to circumvent the 30 line tooltip limit.
local categories = ""
BsCategories:SortIdList(categoryList)
for _, categoryId in ipairs(categoryList) do
if BsCategories.list[categoryId] then
categories = categories
.. GRAY_FONT_COLOR_CODE
.. "• "
.. FONT_COLOR_CODE_CLOSE
.. HIGHLIGHT_FONT_COLOR_CODE
.. (BsCategories:GetName(categoryId) or tostring(categoryId))
.. FONT_COLOR_CODE_CLOSE
.. BS_NEWLINE
end
end
_G.GameTooltip:AddLine(BsUtil.Trim(categories), nil, nil, nil, true)
else
-- No categories assigned.
_G.GameTooltip:AddLine(GRAY_FONT_COLOR_CODE .. L.NoneAssigned .. FONT_COLOR_CODE_CLOSE, nil, nil, nil, true)
end
-- Put instructions in Bagshui info tooltip.
Bagshui:SetInfoTooltipPosition(this, true, true)
inventory:AddBagshuiInfoTooltipLine(L.Click, string.format(L.Symbol_Colon, string.format(L.Prefix_Move, L.Group)))
inventory:AddBagshuiInfoTooltipLine(
L.RightClick,
string.format(L.Symbol_Colon, string.format(L.Suffix_Menu, L.Group))
)
_G.GameTooltip:Show()
Bagshui:ShowInfoTooltip()
end
end
--- Reset colors, hide tooltips.
local function Group_OnLeave()
local this = _G.this
local ui = this.bagshuiData.ui
this.bagshuiData.mouseIsOver = false
Bagshui:HideTooltips()
if not this:IsMouseEnabled() then
return
end
ui:SetGroupColors(this, false)
end
--- Edit Mode interactions for groups.
local function Group_OnClick()
local this = _G.this
local ui = this.bagshuiData.ui
local inventory = ui.inventory
-- This isn't really necessary because group interactivity is disabled outside Edit Mode, but let's have a safeguard.
if not inventory.editMode then
return
end
-- Left/Right click.
if _G.arg1 == "LeftButton" or inventory:EditModeCursorHasItem() then
-- When a menu is open, just close it and don't do anything else.
-- Without this, you can accidentally pick up a group when trying to close a menu.
if inventory.menus:IsMenuOpen() then
Bagshui:CloseMenus()
this:GetScript("OnEnter")()
-- Tooltips and highlighting may need to reappear.
inventory:ItemSlotAndGroupMouseOverCheck()
return
end
-- Something is on the cursor, so let's take it off.
if inventory:EditModeCursorHasItem() then
if inventory.editState.cursorItemType == BS_INVENTORY_OBJECT_TYPE.GROUP then
inventory:AssignGroupToLayout(
inventory.editState.cursorItem,
BS_INVENTORY_OBJECT_TYPE.GROUP,
BS_INVENTORY_LAYOUT_DIRECTION.COLUMN,
this.bagshuiData.rowNum,
this.bagshuiData.columnNum
)
elseif inventory.editState.cursorItemType == BS_INVENTORY_OBJECT_TYPE.CATEGORY then
inventory:AssignCategoryToGroup(inventory.editState.cursorItem, this.bagshuiData.groupId)
elseif inventory.editState.cursorItemType == BS_INVENTORY_OBJECT_TYPE.ITEM then
inventory:ShowItemCategoryAssignmentMenuForGroup(inventory.editState.cursorItem, this)
else
-- Failsafe
inventory:ClearEditModeCursor()
end
_G.PlaySound("INTERFACESOUND_CURSORDROPOBJECT")
else
-- Nothing on the cursor - pick up the group.
-- _G.PlaySound is handled by PickUpGroup().
this:GetScript("OnLeave")()
inventory:EditModePickUpGroup(this.bagshuiData.groupId)
ui:SetGroupColors(this, false)
end
elseif _G.arg1 == "RightButton" then
inventory.menus:OpenMenu("Group", this, nil, "cursor")
end
end
--- Highlight group when the mouse button is pressed to indicate that it will be dragged.
local function Group_OnMouseDown()
if not _G.this:IsEnabled() then
return
end
_G.this.bagshuiData.ui:SetGroupColors(_G.this, true)
end
--- Remove group highlight.
local function Group_OnMouseUp()
_G.this.bagshuiData.ui:SetGroupColors(_G.this, false)
end
--- Pick up the group.
local function Group_OnDragStart()
_G.this:Click()
end
---Create a new group frame that holds item slot buttons.
---@param groupNum number? When provided, only create the group if it doesn't exist.
function InventoryUi:CreateGroup(groupNum)
local ui = self
-- Declare reusable group creation function.
if not ui.CreateGroupIfNotExists then
-- Wrapper function.
local function Group_OnEnter()
ui.inventory:Group_OnEnter()
end
function ui.CreateGroupIfNotExists(elementNum)
-- Group frame is actually a button so it can have an OnClick event.
ui.frames.groups[elementNum] =
_G.CreateFrame("Button", ui:CreateElementName("Group" .. elementNum), ui.frames.main)
local group = ui.frames.groups[elementNum]
group.bagshuiData = {
type = BS_INVENTORY_OBJECT_TYPE.GROUP,
text = nil,
mouseIsOver = false,
groupId = nil,
rowNum = nil,
ColumNum = nil,
ui = ui,
}
-- It feels better when the group is a little bigger than the visual would indicate.
group:SetHitRectInsets(2, 2, 2, 2)
-- Set default appearance.
ui:SetFrameBackdrop(group)
-- Add label text in top left corner.
-- This has to be in a frame so the full label can be revealed on mouseover.
group.bagshuiData.labelFrame, group.bagshuiData.text = ui:CreateLabel(group, ui.frames.main)
-- Add hidden indicator.
group.bagshuiData.hidden = group:CreateTexture(nil, "OVERLAY")
group.bagshuiData.hidden:SetTexture(BsUtil.GetFullTexturePath("Icons\\Mask"))
group.bagshuiData.hidden:SetHeight(10)
group.bagshuiData.hidden:SetWidth(10)
-- Adjust position based on border inset.
local backdrop = group:GetBackdrop()
local inset = ((backdrop.insets and backdrop.insets.top) or 0) + 1
group.bagshuiData.hidden:SetPoint("TOPRIGHT", group, "TOPRIGHT", -inset, 2 - inset)
-- Will be displayed by `Inventory:SetGroupColors()` when appropriate.
group.bagshuiData.hidden:Hide()
-- Add group behaviors
group:RegisterForClicks("LeftButtonUp", "RightButtonUp")
group:RegisterForDrag("LeftButton")
group:SetScript("OnEnter", Group_OnEnter)
group:SetScript("OnLeave", Group_OnLeave)
group:SetScript("OnClick", Group_OnClick)
group:SetScript("OnMouseDown", Group_OnMouseDown)
group:SetScript("OnMouseUp", Group_OnMouseUp)
group:SetScript("OnDragStart", Group_OnDragStart)
end
end
-- Perform group creation.
ui:CreateIfNotExists(groupNum, ui.frames.groups, ui.CreateGroupIfNotExists)
-- Create a corresponding group move target.
-- We'll need at least `<number of groups> + 2` to complete
-- the layout, but UpdateWindow() will take care of that.
ui:CreateGroupMoveTarget(groupNum)
end
--- Set the colors of a group / group move target.
---@param uiGroup table Group or group move target frame (type will be determined via `uiGroup.bagshuiData.type`).
---@param mouseDown boolean? Control whether the extra highlighting to indicate mouse button press should be applied.
function InventoryUi:SetGroupColors(uiGroup, mouseDown)
local inventory = self.inventory
-- Default values.
local backdropOpacity = 0.1
local backdropColor = BS_COLOR.WHITE
local borderOpacity = 0.5
local borderColor = BS_COLOR.GRAY
local textOpacity = 0.5 -- This will be the base opacity in Edit Mode.
local textColor = BS_COLOR.WHITE
-- Track whether this Group has been picked up.
local groupIsOnCursor = false
local highlightThisGroup = false
-- These are only applicable to groups, not group move targets.
if uiGroup.bagshuiData.type == BS_INVENTORY_OBJECT_TYPE.GROUP then
local groupId = uiGroup.bagshuiData.groupId
-- Group color priority:
-- 1. Per-group setting.
-- 2. Skin colors, if groupUseSkinColors is enabled and applicable for the setting.
-- 3. Default colors.
backdropColor = inventory.groups[groupId] and inventory.groups[groupId].background
or inventory.settings.groupUseSkinColors and BsSkin.skinBackgroundColor
or inventory.settings.groupBackgroundDefault
backdropOpacity = backdropColor[4]
borderColor = inventory.groups[groupId] and inventory.groups[groupId].border
or inventory.settings.groupUseSkinColors and BsSkin.skinBorderColor
or inventory.settings.groupBorderDefault
borderOpacity = borderColor[4]
-- Outline the group in yellow if it's on the cursor or highlighting is requested.
groupIsOnCursor = inventory.editState.cursorItem == groupId
highlightThisGroup = inventory.editModeGroupHighlight == groupId
textColor = inventory.groups[groupId] and inventory.groups[groupId].label or inventory.settings.groupLabelDefault
-- Use configured label opacity outside Edit Mode.
if not inventory.editMode then
textOpacity = textColor[4]
end
-- Display hidden indicator in Edit Mode.
if inventory.editMode and inventory.groups[groupId].hide then
uiGroup.bagshuiData.hidden:Show()
uiGroup.bagshuiData.hidden:SetAlpha(uiGroup.bagshuiData.mouseIsOver and 0.8 or 0.5)
else
uiGroup.bagshuiData.hidden:Hide()
end
end
-- Highlight the group.
if
-- In Edit Mode when the cursor has an item and one of the following is true:
-- - This is a group move target.
-- - This group is on the cursor.
-- - There is another group on the cursor and this group mis moused over.
(
inventory:EditModeCursorHasItem()
and (
uiGroup.bagshuiData.type == BS_INVENTORY_OBJECT_TYPE.GROUP_MOVE_TARGET
or groupIsOnCursor
or (inventory.editState.cursorItemType == BS_INVENTORY_OBJECT_TYPE.GROUP and uiGroup.bagshuiData.mouseIsOver)
)
)
-- When this group is being targeted in Edit Mode via self.editModeGroupHighlight.
or highlightThisGroup
then
borderOpacity = (groupIsOnCursor or highlightThisGroup) and 0.8 or 0.6
borderColor = BS_COLOR.YELLOW
textOpacity = 0.8
textColor = BS_COLOR.YELLOW
end
-- Apply mouse button down/mouseover opacity changes.
if mouseDown then
backdropOpacity = math.max(backdropOpacity, 0.3)
borderOpacity = math.max(borderOpacity, 1)
textOpacity = 1
elseif uiGroup.bagshuiData.mouseIsOver then
backdropOpacity = math.max(backdropOpacity, 0.2)
borderOpacity = math.max(borderOpacity, 0.9)
textOpacity = 0.9
end
-- Apply special backdrop color for highlighted group.
if highlightThisGroup or groupIsOnCursor then
backdropColor = BS_COLOR.YELLOW
backdropOpacity = 0.25
end
-- Set colors.
uiGroup:SetBackdropColor(backdropColor[1], backdropColor[2], backdropColor[3], backdropOpacity)
uiGroup:SetBackdropBorderColor(borderColor[1], borderColor[2], borderColor[3], borderOpacity)
if uiGroup.bagshuiData.text then
uiGroup.bagshuiData.text:SetTextColor(textColor[1], textColor[2], textColor[3], textOpacity)
end
end
--- Control whether a group has interactivity enabled. Used when entering/exiting Edit Mode.
---@param uiGroup table Group frame.
---@param enabled boolean Whether the group should receive mouse events.
function InventoryUi:SetGroupInteractivityEnabled(uiGroup, enabled)
uiGroup:EnableMouse(enabled)
end
--#endregion Groups
--#region Groups Move Targets
--- Trigger color changes and tooltips.
local function GroupMoveTarget_OnEnter()
local this = _G.this
local ui = this.bagshuiData.ui
local inventory = ui.inventory
-- It feels weird to have colors flashing under the cursor and tooltips appearing when menus are open.
if inventory.menus:IsMenuOpen() then
return
end
this.bagshuiData.mouseIsOver = true
ui:SetGroupColors(this, false)
-- Don't show tooltip if there's an item held on the Edit Mode cursor.
if inventory:EditModeCursorHasItem() then
return
end
-- Place tooltip in bottom right of the screen.
_G.GameTooltip_SetDefaultAnchor(_G.GameTooltip, this)
_G.GameTooltip:ClearLines()
_G.GameTooltip:SetText(string.format(L.Prefix_New, L.Group)) -- "Add Group"
_G.GameTooltip:Show()
end
--- Hide tooltips and reset colors.
local function GroupMoveTarget_OnLeave()
local this = _G.this
local ui = this.bagshuiData.ui
this.bagshuiData.mouseIsOver = false
ui:SetGroupColors(this, false)
Bagshui:HideTooltips()
end
--- Create a new group and move the object on the cursor into it if applicable.
local function GroupMoveTarget_OnClick()
local this = _G.this
local inventory = this.bagshuiData.ui.inventory
this.bagshuiData.ui.inventory:AssignGroupToLayout(
inventory.editState.cursorItem,
inventory.editState.cursorItemType,
this.bagshuiData.insert,
this.bagshuiData.rowNum,
this.bagshuiData.columnNum
)
end
--- Highlight.
local function GroupMoveTarget_OnMouseDown()
_G.this.bagshuiData.ui:SetGroupColors(_G.this, true)
end
--- Un-highlight.
local function GroupMoveTarget_OnMouseUp()
_G.this.bagshuiData.ui:SetGroupColors(_G.this, false)
end
---Create a new UI group move target button.
---@param groupMoveTargetNum number? When provided, only create the group move target if it doesn't exist.
function InventoryUi:CreateGroupMoveTarget(groupMoveTargetNum)
local ui = self
local inventory = self.inventory
-- Declare reusable group move target creation function.
if not ui.CreateGroupMoveTargetIfNotExists then
function ui.CreateGroupMoveTargetIfNotExists(elementNum)
ui.frames.groupMoveTargets[elementNum] =
_G.CreateFrame("Frame", ui:CreateElementName("GroupMoveTarget" .. elementNum), ui.frames.main)
local button = _G.CreateFrame(
"Button",
ui:CreateElementName("GroupMoveTarget" .. elementNum),
ui.frames.groupMoveTargets[elementNum]
)
-- Share Bagshui table between outer frame and actual button
ui.frames.groupMoveTargets[elementNum].bagshuiData = {
type = BS_INVENTORY_OBJECT_TYPE.GROUP_MOVE_TARGET,
button = button,
mouseIsOver = false,
insert = nil,
rowNum = nil,
columnNum = nil,
ui = ui,
}
button.bagshuiData = ui.frames.groupMoveTargets[elementNum].bagshuiData
button:EnableMouse(true)
button:SetHitRectInsets(-4, -4, -4, -4)
ui:SetFrameBackdrop(
button,
"SOLID", -- borderStyle
nil, -- bgFile
nil, -- edgeFile
nil, -- tileSize
nil, -- edgeSize
0 -- insets
)
local text = button:CreateFontString(nil, nil, "GameFontNormal")
button.bagshuiData.text = text
text:SetJustifyH("CENTER")
text:SetJustifyV("MIDDLE")
text:SetPoint("TOPLEFT", button, -10, 10)
text:SetPoint("BOTTOMRIGHT", button, 10, -12)
text:SetShadowColor(0, 0, 0, 0)
text:SetShadowOffset(0, 0)
text:SetFont(text:GetFont(), 13, "THICKOUTLINE, MONOCHROME")
text:SetText("+")
-- Set initial colors.
ui:SetGroupColors(button, false)
-- Assign behaviors.
button:SetScript("OnEnter", GroupMoveTarget_OnEnter)
button:SetScript("OnLeave", GroupMoveTarget_OnLeave)
button:SetScript("OnClick", GroupMoveTarget_OnClick)
button:SetScript("OnMouseDown", GroupMoveTarget_OnMouseDown)
button:SetScript("OnMouseUp", GroupMoveTarget_OnMouseUp)
end
end
-- Perform group move target creation.
ui:CreateIfNotExists(groupMoveTargetNum, ui.frames.groupMoveTargets, ui.CreateGroupMoveTargetIfNotExists)
end
--#endregion Group Move Targets
end)