enUS Localization Update Announcement / localization-update-announcement (push) Canceled after 0s
215 lines
9.1 KiB
Lua
215 lines
9.1 KiB
Lua
-- Bagshui UI Class: Edit Boxes
|
|
|
|
Bagshui:AddComponent(function()
|
|
local Ui = Bagshui.prototypes.Ui
|
|
|
|
--- Create an EditBox for entering text.
|
|
--- Bagshui EditBox support a fake "read-only" mode -- see `Ui:ResetEditBox()` for details.
|
|
---@param name string Unique name for the button (will be passed to `Ui:CreateElementName()`).
|
|
---@param parent table Parent frame.
|
|
---@param inherits string? Widget template.
|
|
---@param fontObject string|table? Font object or identifier string (defaults to ChatFontNormal).
|
|
---@param multiLine boolean? Edit box should be multiline.
|
|
---@param noBackgroundOrBorder boolean? Don't apply any styling.
|
|
---@param selectAllOnFocus boolean? Highlight text when focus is gained.
|
|
---@return table editBox
|
|
function Ui:CreateEditBox(name, parent, inherits, fontObject, multiLine, noBackgroundOrBorder, selectAllOnFocus)
|
|
local editBox = _G.CreateFrame("EditBox", self:CreateElementName(name), parent, inherits)
|
|
|
|
editBox.bagshuiData = {
|
|
selectAllOnFocus = selectAllOnFocus,
|
|
-- There doesn't seem to be a built-in way to check whether an edit box
|
|
-- currently has focus, so we'll track it.
|
|
hasFocus = false,
|
|
}
|
|
|
|
editBox:SetFontObject(fontObject or "ChatFontNormal")
|
|
editBox:SetMultiLine(multiLine)
|
|
editBox:SetAutoFocus(false)
|
|
editBox:SetTextInsets(5, 5, 5, 5)
|
|
|
|
if not noBackgroundOrBorder then
|
|
self:SetFrameBackdropAndBorderForEditWidgets(editBox)
|
|
end
|
|
|
|
-- Create reusable functions for EditBox script handlers.
|
|
-- RestReadOnly needs to capture self, so they're all just being done
|
|
-- here instead of as local functions.
|
|
if not self._createEditBox_OnEscape then
|
|
-- Clear focus.
|
|
function self._createEditBox_OnEscape()
|
|
_G.this:ClearFocus()
|
|
end
|
|
|
|
-- Focus tracking.
|
|
function self._createEditBox_OnFocusGained()
|
|
_G.this.bagshuiData.hasFocus = true
|
|
if _G.this.bagshuiData.selectAllOnFocus then
|
|
_G.this:HighlightText()
|
|
end
|
|
end
|
|
|
|
-- Focus tracking.
|
|
function self._createEditBox_OnFocusLost()
|
|
_G.this.bagshuiData.hasFocus = false
|
|
end
|
|
|
|
-- Reset read-only EditBoxes.
|
|
function self._createEditBox_ResetReadOnly()
|
|
self:ResetEditBox(_G.this)
|
|
end
|
|
end
|
|
|
|
-- Escape key should of course remove the cursor from the EditBox.
|
|
editBox:SetScript("OnEscapePressed", self._createEditBox_OnEscape)
|
|
|
|
-- Track focus.
|
|
editBox:SetScript("OnEditFocusGained", self._createEditBox_OnFocusGained)
|
|
editBox:SetScript("OnEditFocusLost", self._createEditBox_OnFocusLost)
|
|
|
|
-- Fake a read-only EditBox by resetting contents when changed.
|
|
-- Setting this for both OnChar and OnTextChanged because OnChar is faster
|
|
-- if someone just types, but OnTextChanged is necessary to catch cutting/pasting.
|
|
editBox:SetScript("OnChar", self._createEditBox_ResetReadOnly)
|
|
editBox:SetScript("OnTextChanged", self._createEditBox_ResetReadOnly)
|
|
|
|
return editBox
|
|
end
|
|
|
|
--- Reset a "read-only" EditBox.
|
|
--- Make an EditBox read-only by setting the `bagshuiData.readOnlyText` property
|
|
--- to the text that should be immutable.
|
|
---@param editBox table EditBox from `Ui:CreateEditBox()`.
|
|
function Ui:ResetEditBox(editBox)
|
|
if not editBox.bagshuiData or (editBox.bagshuiData and not editBox.bagshuiData.readOnlyText) then
|
|
return
|
|
end
|
|
|
|
-- Make sure the text is scrolled to the right place.
|
|
if editBox.bagshuiData.scrollFrame then
|
|
editBox.bagshuiData.scrollFrame:UpdateScrollChildRect()
|
|
end
|
|
|
|
-- Reset text only if it's been edited.
|
|
if editBox:GetText() ~= editBox.bagshuiData.readOnlyText then
|
|
editBox:ClearFocus()
|
|
editBox:SetText(editBox.bagshuiData.readOnlyText)
|
|
end
|
|
end
|
|
|
|
--- Create a scrollable multiline edit box for entering text.
|
|
---@param namePrefix string Unique name prefix for the button (will be suffixed with ScrollFrame/EditBox and passed to `Ui:CreateElementName()`).
|
|
---@param parent table Parent frame.
|
|
---@param borderStyle table|string `borderStyle` parameter for `Ui:CreateScrollFrame()`.
|
|
---@param editBoxInherits string? `inherits` parameter for `Ui:CreateEditBox()`.
|
|
---@param fontObject string|table? `fontObject` parameter for `Ui:CreateEditBox()`.
|
|
---@param selectAllOnFocus boolean `selectAllOnFocus` parameter for `Ui:CreateEditBox()`.
|
|
---@return table scrollFrame
|
|
---@return table editBox
|
|
function Ui:CreateScrollableEditBox(namePrefix, parent, borderStyle, editBoxInherits, fontObject, selectAllOnFocus)
|
|
assert(namePrefix, "CreateScrollableEditBox(): namePrefix is required to create a scrollable EditBox")
|
|
|
|
-- Create the elements.
|
|
local scrollFrame = self:CreateScrollFrame(namePrefix .. "ScrollFrame", parent, borderStyle)
|
|
local editBox = self:CreateEditBox(
|
|
namePrefix .. "EditBox",
|
|
scrollFrame,
|
|
editBoxInherits,
|
|
fontObject,
|
|
true,
|
|
true,
|
|
selectAllOnFocus
|
|
)
|
|
|
|
-- Add references for easy access to related elements.
|
|
scrollFrame.bagshuiData.scrollChild = editBox -- Allow Ui:SetScrollChildWidth() to work automatically.
|
|
scrollFrame.bagshuiData.editBox = editBox
|
|
editBox.bagshuiData.scrollFrame = scrollFrame
|
|
|
|
-- Prep the EditBox with default height and width that will trigger scrolling..
|
|
editBox:SetWidth(1000)
|
|
editBox:SetHeight(1000)
|
|
|
|
-- Without SetAllPoints you get some wild behavior where the EditBox either:
|
|
-- - Can't be clicked if the CreateFrame call is made with the ScrollFrame as the parent or
|
|
-- - Has to be created with the dialog frame as the parent to be clickable, but then its
|
|
-- editing area spills out of the ScrollFrame bounds and messes with window dragability.
|
|
editBox:SetAllPoints(scrollFrame)
|
|
|
|
-- Requirements to make scrolling work.
|
|
-- Credit to WowLua source for pointing me in the right direction.
|
|
if not self._createScrollableEditBox_OnCursorChanged then
|
|
-- This is silly, but I wanted to prevent EditBoxes from being scrolled to the bottom
|
|
-- when their contents are set programmatically. To do that, OnTextSet sets the
|
|
-- textChangedProgrammatically property to 2, because there are 2 OnCursorChanged
|
|
-- events that fire afterwards. OnCursorChanged subtracts 1 every time it's called,
|
|
-- and will only trigger ScrollingEdit_OnUpdate to adjust scroll once
|
|
-- textChangedProgrammatically <= 0.
|
|
function self._createScrollableEditBox_OnCursorChanged()
|
|
self:ResetEditBox(_G.this)
|
|
if _G.this.bagshuiData.textChangedProgrammatically <= 0 then
|
|
_G.this.cursorOffset = arg2
|
|
_G.this.cursorHeight = arg4
|
|
end
|
|
_G.this.bagshuiData.textChangedProgrammatically = _G.this.bagshuiData.textChangedProgrammatically - 1
|
|
end
|
|
|
|
function self._createScrollableEditBox_OnTextSet()
|
|
if not _G.this.bagshuiData.scrollToBottomOnTextSet then
|
|
_G.this.bagshuiData.textChangedProgrammatically = 2
|
|
end
|
|
_G.this.bagshuiData.scrollFrame:UpdateScrollChildRect()
|
|
end
|
|
|
|
function self._createScrollableEditBox_OnTextChanged()
|
|
self:ResetEditBox(_G.this)
|
|
_G.this.bagshuiData.scrollFrame:UpdateScrollChildRect()
|
|
end
|
|
end
|
|
editBox.cursorOffset = 0
|
|
editBox.cursorHeight = 0
|
|
editBox.bagshuiData.textChangedProgrammatically = 0
|
|
-- Blizzard's ScrollingEdit_OnUpdate() expects to be called with no arguments and to use `this`.
|
|
-- ClassicAPI passes the frame as the first argument to script handlers (modern script args),
|
|
-- which makes ScrollingEdit_OnUpdate() treat the EditBox as the ScrollFrame and error every frame
|
|
-- ("attempt to call method 'GetVerticalScrollRange' (a nil value)"), so call it without arguments.
|
|
editBox:SetScript("OnUpdate", function()
|
|
_G.ScrollingEdit_OnUpdate()
|
|
end)
|
|
editBox:SetScript("OnCursorChanged", self._createScrollableEditBox_OnCursorChanged)
|
|
editBox:SetScript("OnTextSet", self._createScrollableEditBox_OnTextSet)
|
|
editBox:SetScript("OnTextChanged", self._createScrollableEditBox_OnTextChanged)
|
|
|
|
-- Assign the EditBox to the ScrollFrame so scrolling actually scrolls..
|
|
scrollFrame:SetScrollChild(editBox)
|
|
|
|
-- Allow a click on the empty ScrollFrame area to focus the EditBox when the
|
|
-- EditBox is shorter than the ScrollFrame.
|
|
scrollFrame:EnableMouse(true)
|
|
scrollFrame:SetScript("OnMouseDown", function()
|
|
self:CloseMenusAndClearFocuses()
|
|
editBox:SetFocus()
|
|
end)
|
|
|
|
return scrollFrame, editBox
|
|
end
|
|
|
|
--- Wrapper for `EditBox:SetText()` that also sets the `EditBox.bagshuiData.readOnlyText` property.
|
|
---@param editBox table EditBox widget.
|
|
---@param str string? Text to set.
|
|
---@param readOnly boolean? `true` to make read-only.
|
|
function Ui:SetEditBoxText(editBox, str, readOnly)
|
|
editBox:SetText(str)
|
|
if editBox.bagshuiData then
|
|
editBox.bagshuiData.readOnlyText = readOnly and str or nil
|
|
end
|
|
end
|
|
|
|
--- Wrapper for `Ui:SetEditBoxText()` that automatically sets the `EditBox.bagshuiData.readOnlyText` property.
|
|
---@param editBox table EditBox widget.
|
|
---@param str string? Text to set.
|
|
function Ui:SetEditBoxTextReadOnly(editBox, str, readOnly)
|
|
self:SetEditBoxText(editBox, str, true)
|
|
end
|
|
end)
|