Files
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

307 lines
12 KiB
Lua

-- Bagshui UI Class: Window Frames
Bagshui:AddComponent(function()
local Ui = Bagshui.prototypes.Ui
--- Create a frame to be used as the basis of a window.
--- Adding a title will automatically create a title bar with close button.
---@param name string Unique name for the window (will be passed to `Ui:CreateElementName()`).
---@param parent table? Parent frame (default: `UIParent`).
---@param height number? Default: 500.
---@param width number? Default: 500.
---@param title string? Text to display in the title bar.
---@param noCloseOnEscape boolean? Don't set this frame to be closed when the escape key is pressed.
---@return table window
function Ui:CreateWindowFrame(name, parent, height, width, title, noCloseOnEscape)
local frameParent = parent or _G.UIParent
local frame = _G.CreateFrame("Frame", self:CreateElementName(name), frameParent)
frame.bagshuiData = {
isWindow = true, -- Used in a couple of places to differentiate "window" frames from all other types.
}
-- Set required properties.
frame:SetToplevel(true) -- The frame won't come to the front when clicked without this.
frame:SetMovable(true)
frame:EnableMouse(true)
frame:RegisterForDrag("LeftButton")
frame:SetWidth((height or 500))
frame:SetHeight((width or 500))
-- Make draggable.
frame:SetScript("OnDragStart", function()
if frame:IsMovable() then
frame:StartMoving()
end
end)
frame:SetScript("OnDragStop", function()
frame:StopMovingOrSizing()
end)
-- Open/Close events.
frame:SetScript("OnShow", function()
Bagshui:RaiseEvent("BAGSHUI_WINDOW_OPENED", false, frame)
end)
frame:SetScript("OnHide", function()
Bagshui:RaiseEvent("BAGSHUI_WINDOW_CLOSED", false, frame)
end)
-- Set default style and color.
self:SetFrameBackdrop(frame)
frame:SetBackdropColor(
BsSkin.frameBackgroundColor[1],
BsSkin.frameBackgroundColor[2],
BsSkin.frameBackgroundColor[3],
BsSkin.frameBackgroundColor[4] or 1
)
if BsSkin.frameBorderColor then
frame:SetBackdropBorderColor(
BsSkin.frameBorderColor[1],
BsSkin.frameBorderColor[2],
BsSkin.frameBorderColor[3],
BsSkin.frameBorderColor[4] or 1
)
end
-- Add title bar and close button.
if title then
frame.bagshuiData.header, frame.bagshuiData.title, frame.bagshuiData.closeButton =
self:CreateTitleBar(frame, title)
end
-- Shouldn't be visible by default.
frame:Hide()
-- Close when escape is pressed.
if not noCloseOnEscape then
Bagshui:RegisterFrameForCloseOnEscape(frame)
end
return frame
end
--- Create a frame whose only contents are a full-size scrollable edit box.
--- Always generates a title bar.
---@param name string Parameter for `Ui:CreateWindowFrame()`.
---@param parent table? Parameter for `Ui:CreateWindowFrame()`.
---@param height number? Parameter for `Ui:CreateWindowFrame()`.
---@param width number? Parameter for `Ui:CreateWindowFrame()`.
---@param title string? Parameter for `Ui:CreateWindowFrame()` (default: `Edit Window`).
---@param noCloseOnEscape boolean? Parameter for `Ui:CreateWindowFrame()`.
---@param fontObject string|table? Parameter for `Ui:CreateEditBox()`.
---@param selectAllOnFocus boolean? Parameter for `Ui:CreateEditBox()`.
---@return table window
---@return table scrollFrame
---@return table editBox
function Ui:CreateScrollableEditBoxWindow(
name,
parent,
height,
width,
title,
noCloseOnEscape,
fontObject,
selectAllOnFocus
)
local frame = self:CreateWindowFrame(name, parent, height, width, title, noCloseOnEscape)
local scrollFrame, editBox = self:CreateScrollableEditBox(
"EditScroll",
frame,
nil, -- borderStyle
nil, -- editBoxInherits
fontObject,
selectAllOnFocus
)
if frame.bagshuiData.header then
self:SetPoint(scrollFrame, "TOPLEFT", frame.bagshuiData.header, "BOTTOMLEFT", 0, -2)
else
self:SetPoint(scrollFrame, "TOPLEFT", frame, "TOPLEFT", BsSkin.windowPadding, -BsSkin.windowPadding)
end
self:SetPoint(scrollFrame, "BOTTOMRIGHT", frame, "BOTTOMRIGHT", -BsSkin.windowPadding, BsSkin.windowPadding)
-- Manually setting width so that the EditBox can be properly sized.
self:SetWidth(scrollFrame, frame:GetWidth() - (BsSkin.windowPadding * 2))
frame.bagshuiData.scrollFrame = scrollFrame
frame.bagshuiData.editBox = editBox
return frame, scrollFrame, editBox
end
--- Add a title bar (header frame, window title, and close button) to a window frame.
---@param windowFrame table Frame created by `Ui:CreateWindowFrame()`.
---@param windowTitleText string Parameter for `Ui:CreateWindowTitle()`.
---@param closeFunction function? `clickFunction` parameter for `Ui:CreateWindowTitle()`.
---@return table header Frame
---@return table title FontString
---@return table closeButton Button
function Ui:CreateTitleBar(windowFrame, windowTitleText, closeFunction)
local header = self:CreateHeaderFooter("Header", windowFrame, "TOP", BsSkin.windowTitleBarHeight)
local title = self:CreateWindowTitle("Title", header, windowTitleText, "LEFT", 0, 0)
local closeButton = self:CreateCloseButton("CloseButton", header, "RIGHT", 0, 0, closeFunction)
return header, title, closeButton
end
--- Create a header or footer frame for a window.
--- By default will create a header anchored to the top, but if `anchorTopBottom == "BOTTOM"` a footer will be created.
---@param name string Unique name for the element (will be passed to `Ui:CreateElementName()`).
---@param windowFrame table Parent frame.
---@param anchorTopBottom string? `"TOP"` (header) or `"BOTTOM"` (footer).
---@param height number? Defaults to `BsSkin.windowHeaderFooterHeight`.
---@param xOffset number? See code for default.
---@param yOffset number? See code for default.
---@return table headerOrFooter
function Ui:CreateHeaderFooter(name, windowFrame, anchorTopBottom, height, xOffset, yOffset)
-- Figure out positioning.
anchorTopBottom = (anchorTopBottom == "BOTTOM") and "BOTTOM" or "TOP"
local invertY = (anchorTopBottom == "TOP") and -1 or 1
xOffset = xOffset or BsSkin.windowPadding
yOffset = yOffset or ((BsSkin.windowPadding - BsSkin.windowHeaderFooterYAdjustment) * invertY)
-- Create header/footer.
local headerFooter = _G.CreateFrame("Frame", self:CreateElementName(name), windowFrame)
headerFooter:SetHeight(height or BsSkin.windowHeaderFooterHeight)
-- Set position.
headerFooter:SetPoint(anchorTopBottom .. "LEFT", windowFrame, xOffset, yOffset)
headerFooter:SetPoint(anchorTopBottom .. "RIGHT", windowFrame, -xOffset, yOffset)
return headerFooter
end
--- Add a window title to the given window frame.
---@param name string Unique name for the element (will be passed to `Ui:CreateElementName()`).
---@param windowFrame table Parent frame.
---@param text string Title text.
---@param anchor string? Default: `TOPLEFT`.
---@param xOffset number? Default: `BsSkin.windowPadding`.
---@param yOffset number? Default: `-BsSkin.windowPadding`.
---@return table titleText FontString
function Ui:CreateWindowTitle(name, windowFrame, text, anchor, xOffset, yOffset)
local titleText = self:CreateShadowedFontString(windowFrame, self:CreateElementName(name), "GameFontNormal")
titleText:SetPoint(
anchor or "TOPLEFT",
windowFrame,
xOffset or BsSkin.windowPadding,
yOffset or -BsSkin.windowPadding
)
titleText:SetJustifyH("LEFT")
titleText:SetJustifyV("TOP")
titleText:SetText(text)
return titleText
end
--- Add a close button to the given window frame.
---@param name string Unique name for the element (will be passed to `Ui:CreateElementName()`).
---@param windowFrame table Parent frame.
---@param anchor string? Default: `TOPRIGHT`.
---@param xOffset number? Default: `BsSkin.windowPadding`. Always is adjusted by `BsSkin.closeButtonXOffsetAdjustment`.
---@param yOffset number? Default: `-BsSkin.windowPadding`. Always is adjusted by `BsSkin.closeButtonYOffsetAdjustment`.
---@param clickFunction function|boolean? OnClick event handler. If not provided, one will be created automatically. Pass `false` to not add a click handler at all.
---@return table closeButton Button.
function Ui:CreateCloseButton(name, windowFrame, anchor, xOffset, yOffset, clickFunction)
local closeButton = _G.CreateFrame("Button", self:CreateElementName(name), windowFrame)
closeButton.bagshuiData = {}
-- Add textures, set size and position.
BsSkin.closeButtonSkinFunc(closeButton)
closeButton:SetWidth(BsSkin.closeButtonSize)
closeButton:SetHeight(BsSkin.closeButtonSize)
closeButton:SetPoint(
anchor or "TOPRIGHT",
(xOffset or -BsSkin.windowPadding) + BsSkin.closeButtonXOffsetAdjustment,
(yOffset or -BsSkin.windowPadding) + BsSkin.closeButtonYOffsetAdjustment
)
closeButton:SetHitRectInsets(
BsSkin.closeButtonHitRectInsets,
BsSkin.closeButtonHitRectInsets,
BsSkin.closeButtonHitRectInsets,
BsSkin.closeButtonHitRectInsets
)
-- Add close function.
if clickFunction ~= false then
-- When an explicit function isn't passed, we need to create the close function.
if type(clickFunction) ~= "function" then
clickFunction = nil
local parentWindow = self:FindWindowFrame(windowFrame)
if parentWindow then
-- We found a window frame, so create the function to hide it.
clickFunction = function()
Bagshui:CloseMenus()
parentWindow:Hide()
end
else
Bagshui:PrintWarning(
"UI WARNING: No parent frame found for close button '"
.. tostring(name)
.. "' (child of frame "
.. tostring(windowFrame:GetName())
.. ") -- frame may not be closable!"
)
end
end
if clickFunction then
closeButton:SetScript("OnClick", clickFunction)
end
end
return closeButton
end
--- Obscure and disable the contents of a window frame so a modal dialog can be displayed on top.
---@param window table Bagshui window frame.
---@param shadeOn boolean Whether to show or hide the shade.
---@param text string? Text to display in the middle of the shade.
function Ui:SetWindowShade(window, shadeOn, text)
if not window.bagshuiData and window.bagshuiData.isWindow then
return
end
if shadeOn then
if not window.bagshuiData.shadeFrame then
window.bagshuiData.shadeFrame = _G.CreateFrame("Frame", nil, window)
local inset = (type(BsSkin.frameDefaultBorderStyle) == "table" and BsSkin.frameDefaultBorderStyle.insets)
or BsSkin.frameBorderInsets
or BS_BORDER.CURVED.insets
window.bagshuiData.shadeFrame:SetPoint("TOPLEFT", window, "TOPLEFT", inset, -inset)
window.bagshuiData.shadeFrame:SetPoint("BOTTOMRIGHT", window, "BOTTOMRIGHT", -inset, inset)
self:SetFrameBackdrop(window.bagshuiData.shadeFrame, BsSkin.frameDefaultBorderStyle)
window.bagshuiData.shadeFrame:SetBackdropColor(0.1, 0.1, 0.1, 0.65)
window.bagshuiData.shadeFrame:SetBackdropBorderColor(0, 0, 0, 0.1)
window.bagshuiData.shadeFrameText = self:CreateShadowedFontString(window.bagshuiData.shadeFrame)
window.bagshuiData.shadeFrameText:SetPoint("CENTER", window.bagshuiData.shadeFrame)
window.bagshuiData.shadeFrameText:SetJustifyH("CENTER")
-- Need to keep the window's dragability.
window.bagshuiData.shadeFrame:EnableMouse(true)
window.bagshuiData.shadeFrame:RegisterForDrag("LeftButton")
self:PassMouseEventsThrough(window.bagshuiData.shadeFrame, window)
end
if text then
window.bagshuiData.shadeFrameText:SetText(tostring(text))
window.bagshuiData.shadeFrameText:Show()
else
window.bagshuiData.shadeFrameText:Hide()
end
window.bagshuiData.shadeFrame:SetFrameLevel(window:GetFrameLevel() + 10)
window.bagshuiData.shadeFrame:Show()
else
if window.bagshuiData.shadeFrame then
window.bagshuiData.shadeFrame:Hide()
end
end
end
end)