From 796f20eadf4799c0f688f3a2a75b6d4a2cc0550d Mon Sep 17 00:00:00 2001
From: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri, 30 Oct 2026 00:38:54 -0500
Subject: [PATCH] Show chat copy and the share box in ClassicAPI's scrolling
edit box
pfUIScrollingEditBoxTemplate (api/ui-widgets.xml) is ScrollingEditBoxTemplate
with pfUIScrollBarTemplate beside the text. Its mixin's SetFont gives the
box a font object of its own, since the edit box sets its font by object on
every SetText and pfUI fonts are paths. ui-widgets.lua now loads before the
XML, which needs the mixin.
The share box no longer recomputes its scroll child on every text change.
Chat copy reads each chat frame's messages with ClassicAPI's
GetMessageInfo when it opens, so the AddMessage hook, its 100-line copy of
the history and the skip of combat frames are gone. It shows every line
the frame keeps, lines from before login included. It puts the caret at
the end to scroll to the newest line instead of three OnUpdate passes, and
Escape now leaves copy mode on every chat frame.
---
api/ui-widgets.lua | 24 +++++++
api/ui-widgets.xml | 22 +++++++
init/api.xml | 2 +-
modules/chatcopy.lua | 153 +++++++++++++------------------------------
modules/share.lua | 50 +++++---------
5 files changed, 111 insertions(+), 140 deletions(-)
diff --git a/api/ui-widgets.lua b/api/ui-widgets.lua
index 7b7c3165..f431b99b 100644
--- a/api/ui-widgets.lua
+++ b/api/ui-widgets.lua
@@ -427,6 +427,30 @@ function pfUI.api.CreateTabFrame(parent, align, outside)
return f
end
+-- [ pfUIScrollingEditBoxTemplate ]
+-- Mixin of the template in ui-widgets.xml: ClassicAPI's scrolling edit box
+-- with pfUI's scroll bar beside it.
+_G.pfUIScrollingEditBoxMixin = {}
+
+function pfUIScrollingEditBoxMixin:InitScrollBar()
+ local scrollBox = self:GetScrollBox()
+ scrollBox:ClearAllPoints()
+ scrollBox:SetPoint("TOPLEFT")
+ scrollBox:SetPoint("BOTTOMRIGHT", self.ScrollBar, "BOTTOMLEFT")
+ ScrollUtil.RegisterScrollBoxWithScrollBar(scrollBox, self.ScrollBar)
+end
+
+-- The edit box takes its font from a font object and sets that object again
+-- on every SetText, so a font path goes through a font object of the frame's own.
+function pfUIScrollingEditBoxMixin:SetFont(font, size, flags)
+ if not self.font then
+ self.font = CreateFont(self:GetName() .. "Font")
+ self:GetEditBox().fontName = self.font
+ end
+ self.font:SetFont(font, size, flags)
+ self:SetFontObject(self.font)
+end
+
function pfUI.api.CreateScrollFrame(name, parent)
local f = CreateFrame("ScrollFrame", name, parent)
diff --git a/api/ui-widgets.xml b/api/ui-widgets.xml
index 0d7b3940..a9a02407 100644
--- a/api/ui-widgets.xml
+++ b/api/ui-widgets.xml
@@ -28,4 +28,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/init/api.xml b/init/api.xml
index acc19d0c..fd647af5 100644
--- a/init/api.xml
+++ b/init/api.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/modules/chatcopy.lua b/modules/chatcopy.lua
index dfa21925..da16bc40 100644
--- a/modules/chatcopy.lua
+++ b/modules/chatcopy.lua
@@ -1,5 +1,4 @@
pfUI:RegisterModule("chatcopy", function ()
- local limit = 100
local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:SetScript("OnEvent", function()
@@ -15,26 +14,27 @@ pfUI:RegisterModule("chatcopy", function ()
button.icon:SetTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Disabled")
button.icon:SetAllPoints()
+ -- show or hide the copy box of every chat frame
+ local function SetCopyMode(state)
+ button.state = state
+
+ for i=1, _G.NUM_CHAT_WINDOWS do
+ local scroll = _G["ChatFrame"..i].scroll
+ if scroll then
+ scroll:SetShown(state)
+ end
+ end
+
+ if state then
+ button.icon:SetTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Up")
+ else
+ button.icon:SetTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Disabled")
+ end
+ end
+
button:SetScript("OnClick", function()
if arg1 == "LeftButton" then
- this.state = not this.state
-
- for i=1, _G.NUM_CHAT_WINDOWS do
- local frame = _G["ChatFrame"..i]
- local scroll = frame.scroll
-
- if scroll and this.state then
- scroll:Show()
- elseif scroll then
- scroll:Hide()
- end
- end
-
- if this.state then
- button.icon:SetTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Up")
- else
- button.icon:SetTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Disabled")
- end
+ SetCopyMode(not this.state)
elseif arg1 == "RightButton" then
if ChatMenu:IsShown() then
ChatMenu:Hide()
@@ -56,98 +56,39 @@ pfUI:RegisterModule("chatcopy", function ()
for i=1, _G.NUM_CHAT_WINDOWS do
local frame = _G["ChatFrame"..i]
- local name = frame:GetName()
- local combat = 0
- for i, msg in pairs(frame.messageTypeList) do
- if strfind(msg, "SPELL", 1) or strfind(msg, "COMBAT", 1) then
- combat = combat + 1
+ -- the chat frame's messages in an edit box to copy from
+ local scroll = CreateFrame("Frame", "pfChatCopy" .. i, frame, "pfUIScrollingEditBoxTemplate")
+ scroll:SetAllPoints(frame)
+ scroll.tex = scroll:CreateTexture(nil, "BACKGROUND")
+ scroll.tex:SetTexture(0,0,0,.95)
+ scroll.tex:SetPoint("TOPLEFT", -3, 3)
+ scroll.tex:SetPoint("BOTTOMRIGHT", 3, -3)
+ scroll:Hide()
+
+ scroll:RegisterCallback("OnEscapePressed", function()
+ SetCopyMode(false)
+ end, scroll)
+
+ scroll:HookScript("OnShow", function()
+ -- each line in its own color, which also replaces the |r resets inside it
+ local lines = {}
+ for index=1, frame:GetNumMessages() do
+ local text, r, g, b = frame:GetMessageInfo(index)
+ local color = rgbhex(r, g, b)
+ table.insert(lines, color .. text:gsub("|r", color))
end
- end
+ local text = table.concat(lines, "\n")
- if combat < 5 then
- -- initialize message cache
- local history = {}
- local AddMessage = frame.AddMessage
- frame.AddMessage = function(self,msg,r,g,b)
- if msg and r and g and b then
- local col = rgbhex(r,g,b)
- table.insert(history, 1, col..string.gsub(msg, "|r", col))
- elseif msg then
- table.insert(history, 1, "|cffffffff"..msg)
- end
+ scroll:SetFont(frame:GetFont())
+ scroll:SetText(text)
- if history[limit] then
- table.remove(history, limit)
- end
- AddMessage(self,msg,r,g,b)
- end
+ -- the cursor at the end scrolls the box to the newest line
+ scroll:SetFocus()
+ scroll:SetCursorPosition(#text)
+ end)
- -- read chat defaults
- local font, size, flags = frame:GetFont()
-
- -- build the frames
- local scroll = CreateScrollFrame("ChatFrameScroll"..i, frame)
- scroll:SetAllPoints(frame)
- scroll.tex = scroll:CreateTexture(nil, "BACKGROUND")
- scroll.tex:SetTexture(0,0,0,.95)
- scroll.tex:SetPoint("TOPLEFT", -3, 3)
- scroll.tex:SetPoint("BOTTOMRIGHT", 3, -3)
- scroll:Hide()
-
- local editbox = CreateFrame("EditBox", "pfChatCopyBox" .. i, scroll)
- editbox:SetSize(frame:GetSize())
- editbox:SetAllPoints(scroll)
- editbox:SetTextColor(1,1,1,1)
- editbox:SetFontObject(ChatFontNormal)
- editbox:SetFont(font, size, flags)
- editbox:SetAutoFocus(true)
- editbox:SetMultiLine(true)
- editbox:SetMaxLetters(0)
-
- editbox:SetScript("OnMouseDown", function()
- editbox.hasFocus = true
- end)
-
- editbox:SetScript("OnEscapePressed", function()
- if editbox.hasFocus then
- editbox:HighlightText(0, 0)
- editbox:ClearFocus()
- editbox.hasFocus = false
- else
- editbox:GetParent():Hide()
- pfChatCopyButton.icon:SetTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Disabled")
- pfChatCopyButton.state = false
- end
- end)
-
- scroll:SetScrollChild(editbox)
- scroll:SetScript("OnShow", function()
- editbox:SetText("")
- for i=limit,0,-1 do
- if history[i] then
- editbox:Insert("\n" .. history[i])
- end
- end
-
- editbox:SetScript("OnUpdate", function()
- if not this.count then this.count = 0 end
-
- scroll:UpdateScrollChildRect()
- scroll:SetVerticalScroll(scroll:GetVerticalScrollRange())
- scroll:Scroll()
- this.count = this.count + 1
-
- -- the scroll frame takes a while to update
- if this.count >= 3 then
- this:SetScript("OnUpdate", nil)
- this.count = 0
- end
- end)
- end)
-
- frame.scroll = scroll
- end
+ frame.scroll = scroll
end
this:UnregisterAllEvents()
diff --git a/modules/share.lua b/modules/share.lua
index 2cd9f2f4..f2c42494 100644
--- a/modules/share.lua
+++ b/modules/share.lua
@@ -205,36 +205,23 @@ pfUI:RegisterModule("share", function ()
table.insert(UISpecialFrames, "pfShare")
do -- Edit Box
- f.scroll = pfUI.api.CreateScrollFrame("pfShareScroll", f)
+ f.scroll = CreateFrame("Frame", "pfShareScroll", f, "pfUIScrollingEditBoxTemplate")
f.scroll:SetPoint("TOPLEFT", f, "TOPLEFT", 10, -30)
f.scroll:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -10, 50)
- f.scroll:SetWidth(560)
- f.scroll:SetHeight(400)
+ f.scroll:SetFont(pfUI.media["font:RobotoMono.ttf"], 9)
+ f.scroll:SetTextInsets(15,15,15,15)
- f.scroll.backdrop = CreateFrame("Frame", "pfShareScrollBackdrop", f.scroll)
- f.scroll.backdrop:SetFrameLevel(1)
+ -- the ScrollBox clips its children, so the backdrop is a sibling
+ f.scroll.backdrop = CreateFrame("Frame", "pfShareScrollBackdrop", f)
f.scroll.backdrop:SetPoint("TOPLEFT", f.scroll, "TOPLEFT", -5, 5)
f.scroll.backdrop:SetPoint("BOTTOMRIGHT", f.scroll, "BOTTOMRIGHT", 5, -5)
pfUI.api.CreateBackdrop(f.scroll.backdrop, nil, true)
+ f.scroll.backdrop.tint = f.scroll.backdrop:CreateTexture(nil, "ARTWORK")
+ f.scroll.backdrop.tint:SetAllPoints(f.scroll)
+ f.scroll.backdrop.tint:SetTexture(1,1,1,.05)
- f.scroll.text = CreateFrame("EditBox", "pfShareEditBox", f.scroll)
- f.scroll.text.bg = f.scroll.text:CreateTexture(nil, "OVERLAY")
- f.scroll.text.bg:SetAllPoints(f.scroll.text)
- f.scroll.text.bg:SetTexture(1,1,1,.05)
- f.scroll.text:SetMultiLine(true)
- f.scroll.text:SetWidth(560)
- f.scroll.text:SetHeight(400)
- f.scroll.text:SetAllPoints(f.scroll)
- f.scroll.text:SetTextInsets(15,15,15,15)
- f.scroll.text:SetFont(pfUI.media["font:RobotoMono.ttf"], 9)
- f.scroll.text:SetAutoFocus(false)
- f.scroll.text:SetJustifyH("LEFT")
- f.scroll.text:SetScript("OnEscapePressed", function() this:ClearFocus() end)
- f.scroll.text:SetScript("OnTextChanged", function()
- this:GetParent():UpdateScrollChildRect()
- this:GetParent():UpdateScrollState()
-
- local text = this:GetText()
+ f.scroll:RegisterCallback("OnTextChanged", function()
+ local text = f.scroll:GetText()
local blob = Decode(text)
local json = not blob and DecodeJSON(text) or nil
local legacy = not blob and not json and DecodeLegacy(text) or nil
@@ -253,27 +240,26 @@ pfUI:RegisterModule("share", function ()
f.readButton:Enable()
f.readButton.text:SetText(T["Decode"])
f.readButton.func = function()
- local current = f.scroll.text:GetText()
+ local current = f.scroll:GetText()
local config = Decode(current) or DecodeLegacy(current)
if config then
- f.scroll.text:SetText(C_EncodingUtil.SerializeJSON(config))
+ f.scroll:SetText(C_EncodingUtil.SerializeJSON(config))
end
end
elseif json then
f.readButton:Enable()
f.readButton.text:SetText(T["Encode"])
f.readButton.func = function()
- local config = DecodeJSON(f.scroll.text:GetText())
+ local config = DecodeJSON(f.scroll:GetText())
if config then
- f.scroll.text:SetText(Encode(config))
+ f.scroll:SetText(Encode(config))
end
end
else
f.readButton:Disable()
f.readButton.text:SetText(T["N/A"])
end
- end)
- f.scroll:SetScrollChild(f.scroll.text)
+ end, f)
end
do -- button: close
@@ -322,7 +308,7 @@ pfUI:RegisterModule("share", function ()
f.loadButton.text:SetFont(pfUI.font_default, pfUI_config.global.font_size, "OUTLINE")
f.loadButton.text:SetText(T["Import"])
f.loadButton:SetScript("OnClick", function()
- local text = f.scroll.text:GetText()
+ local text = f.scroll:GetText()
local config = Decode(text) or DecodeJSON(text) or DecodeLegacy(text)
if not config then return end
@@ -383,9 +369,7 @@ pfUI:RegisterModule("share", function ()
ignored["disabled"] = f.ignorePosition:GetChecked()
local encoded = Encode(DiffConfig(myconfig, defconfig, ignored) or {})
- f.scroll.text:SetText(encoded)
- f.scroll.text.value = encoded
- f.scroll:SetVerticalScroll(0)
+ f.scroll:SetText(encoded)
end)
end