ca2b78e217
/fui keeps edit mode, reset and list. The thirty diagnostic and tuning functions behind the other subcommands (bar, bags, character, tabs, close, model, minimap, mouse, map, questlog, chat, chatlines, tabard...) go with them, with Panes.Report, the bags read-back and the 205 texts they alone used. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
214 lines
6.1 KiB
Lua
214 lines
6.1 KiB
Lua
-- Hosts and their contents: the single place that decides what a window shows.
|
|
-- A host is a fixed surface (a pane, a column) in which contents replace each other, like
|
|
-- LeftPaneHost / RightPaneHost in camelot CharacterFrame.xml. A content is built once, on
|
|
-- first show, then only shown or hidden; nothing is recomputed.
|
|
|
|
local ForeverUI = ForeverUI or {}
|
|
_G.ForeverUI = ForeverUI
|
|
|
|
local Panes = {}
|
|
ForeverUI.Panes = Panes
|
|
|
|
local hosts = {} -- name -> host (frame, contents, pages, furniture, group, current)
|
|
local hostOrder = {}
|
|
local L = ForeverUI.L
|
|
|
|
-- ---------------------------------------------------------------- hosts
|
|
|
|
-- Declares a host on an existing frame; the library only records where contents go.
|
|
function Panes.NewHost(name, frame)
|
|
local host = hosts[name]
|
|
if not host then
|
|
host = { name = name, contents = {}, order = {}, pages = {},
|
|
furniture = {}, displayed = true }
|
|
hosts[name] = host
|
|
hostOrder[#hostOrder + 1] = name
|
|
end
|
|
host.frame = frame
|
|
return host
|
|
end
|
|
|
|
-- ------------------------------------------------------------ contents
|
|
|
|
-- def = { host, group, id, build }: group is the screen a tab opens (it sets every host);
|
|
-- id names the page. Pages of the same host and group replace each other.
|
|
-- build(hostFrame) runs once, on first show, and returns our root frame (or nil) and the
|
|
-- list of client frames the content owns. Client frames are not reparented (they would
|
|
-- lose their level and strata), so they are shown and hidden with the root.
|
|
function Panes.Register(def)
|
|
local host = hosts[def.host]
|
|
if not host then
|
|
error(ForeverUI.L.PANES_ERROR_UNKNOWN_HOST .. tostring(def.host))
|
|
end
|
|
|
|
local content = {
|
|
id = def.id,
|
|
group = def.group,
|
|
build = def.build,
|
|
host = host,
|
|
built = false,
|
|
frames = {},
|
|
}
|
|
host.contents[def.id] = content
|
|
host.order[#host.order + 1] = def.id
|
|
|
|
-- The first page declared for a group becomes its default page.
|
|
if host.pages[def.group] == nil then
|
|
host.pages[def.group] = def.id
|
|
end
|
|
return content
|
|
end
|
|
|
|
local function assemble(content)
|
|
if content.built then
|
|
return
|
|
end
|
|
content.built = true -- set before the call: no re-entry
|
|
|
|
if content.build then
|
|
local root, frames = content.build(content.host.frame)
|
|
content.root = root
|
|
content.frames = frames or {}
|
|
end
|
|
end
|
|
|
|
-- Furniture: frames a host shows for a group whatever the page (stone band, page tabs),
|
|
-- like camelot UpdateRightPaneHeader. Can be called several times; frames add up.
|
|
function Panes.Furniture(name, group, frames)
|
|
local host = hosts[name]
|
|
if not host then
|
|
return
|
|
end
|
|
local list = host.furniture[group] or {}
|
|
host.furniture[group] = list
|
|
for _, frame in ipairs(frames or {}) do
|
|
if frame then
|
|
list[#list + 1] = frame
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Shows or hides a frame only on a change: a protected client frame (PetPaperDollFrame holds
|
|
-- secure companion buttons) refuses even a call that changes nothing, in combat
|
|
local function setShown(frame, visible)
|
|
if (frame:IsShown() and true or false) ~= visible then
|
|
if visible then frame:Show() else frame:Hide() end
|
|
end
|
|
end
|
|
|
|
-- Shows or hides a content and the client frames it owns; builds it on first show
|
|
local function place(content, visible)
|
|
if visible then
|
|
assemble(content)
|
|
elseif not content.built then
|
|
-- Never shown, so nothing to hide: do not build it just for that.
|
|
return
|
|
end
|
|
|
|
if content.root then
|
|
setShown(content.root, visible)
|
|
end
|
|
for _, frame in ipairs(content.frames) do
|
|
if frame and frame.Show then
|
|
setShown(frame, visible)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- ------------------------------------------------------------- display
|
|
|
|
-- Applies a host's group and page: shows the wanted content and furniture, hides the rest
|
|
local function apply(host)
|
|
local wanted = host.displayed and host.pages[host.group or ""] or nil
|
|
|
|
for _, id in ipairs(host.order) do
|
|
place(host.contents[id], id == wanted)
|
|
end
|
|
host.current = wanted
|
|
|
|
-- Furniture: hide everything declared, then show the current group's, so a frame shared by
|
|
-- two groups does not depend on the loop order.
|
|
local activeFrames = {}
|
|
local ownScreen = host.furniture[host.group or ""]
|
|
if host.displayed and wanted and ownScreen then
|
|
for _, frame in ipairs(ownScreen) do
|
|
activeFrames[frame] = true
|
|
end
|
|
end
|
|
for _, list in pairs(host.furniture) do
|
|
for _, frame in ipairs(list) do
|
|
if frame and frame.Hide and not activeFrames[frame] then
|
|
frame:Hide()
|
|
end
|
|
end
|
|
end
|
|
for frame in pairs(activeFrames) do
|
|
if frame.Show then
|
|
frame:Show()
|
|
end
|
|
end
|
|
|
|
-- A host with no content is hidden, so an empty right pane (with its background and stone
|
|
-- band) does not stay on a tab that has nothing to put there.
|
|
if host.frame then
|
|
local needed = host.displayed and wanted ~= nil
|
|
if needed then host.frame:Show() else host.frame:Hide() end
|
|
end
|
|
end
|
|
|
|
-- Opens a group: a whole screen, all hosts at once.
|
|
function Panes.ShowGroup(group)
|
|
for _, name in ipairs(hostOrder) do
|
|
local host = hosts[name]
|
|
host.group = group
|
|
apply(host)
|
|
end
|
|
Panes.group = group
|
|
end
|
|
|
|
function Panes.CurrentGroup()
|
|
return Panes.group
|
|
end
|
|
|
|
-- Switches the page of a host without changing the group (two buttons sharing one surface).
|
|
function Panes.ShowPage(name, id)
|
|
local host = hosts[name]
|
|
if not host or not host.contents[id] then
|
|
return
|
|
end
|
|
host.pages[host.group or ""] = id
|
|
apply(host)
|
|
end
|
|
|
|
function Panes.CurrentPage(name)
|
|
local host = hosts[name]
|
|
return host and host.current
|
|
end
|
|
|
|
-- Hides a whole host (a collapsed pane). The group and the chosen page are kept for when it
|
|
-- is expanded again.
|
|
function Panes.SetHostShown(name, state)
|
|
local host = hosts[name]
|
|
if not host then
|
|
return
|
|
end
|
|
host.displayed = state and true or false
|
|
apply(host)
|
|
end
|
|
|
|
-- Tells whether a host has something to show for this group, without building anything
|
|
-- (e.g. whether the collapse button makes sense).
|
|
function Panes.HasContent(name, group)
|
|
local host = hosts[name]
|
|
return host ~= nil and host.pages[group or host.group or ""] ~= nil
|
|
end
|
|
|
|
-- Re-applies the current state. Call it when a content is registered after its group was
|
|
-- opened, never from a display loop.
|
|
function Panes.Refresh()
|
|
for _, name in ipairs(hostOrder) do
|
|
apply(hosts[name])
|
|
end
|
|
end
|
|
|