--!optimize 2 --!strict local module = {} local TextService = game:GetService("TextService") local X_ALIGNMENT_MAP = { [Enum.TextXAlignment.Left] = Enum.HorizontalAlignment.Left, [Enum.TextXAlignment.Center] = Enum.HorizontalAlignment.Center, [Enum.TextXAlignment.Right] = Enum.HorizontalAlignment.Right, } local Y_ALIGNMENT_MAP = { [Enum.TextYAlignment.Top] = Enum.VerticalAlignment.Top, [Enum.TextYAlignment.Center] = Enum.VerticalAlignment.Center, [Enum.TextYAlignment.Bottom] = Enum.VerticalAlignment.Bottom, } -- Creates a Frame from a TextLabel for easy manipulation of letters. -- Intended to be used with InsertLetter(listFrame: Frame, letter: string, index: number): Frame? function module.CreateFrameFromLabel(labelTemplate: TextLabel): Frame local originalText = labelTemplate.Text local originalSize = labelTemplate.Size local originalPosition = labelTemplate.Position local originalAnchorPoint = labelTemplate.AnchorPoint local originalRotation = labelTemplate.Rotation local originalName = labelTemplate.Name local originalBackgroundColor3 = labelTemplate.BackgroundColor3 local originalBackgroundTransparency = labelTemplate.BackgroundTransparency local originalXAlignment = labelTemplate.TextXAlignment local originalYAlignment = labelTemplate.TextYAlignment local listFrame = Instance.new("Frame") listFrame.Name = originalName listFrame.Size = originalSize listFrame.Position = originalPosition listFrame.AnchorPoint = originalAnchorPoint listFrame.Rotation = originalRotation listFrame.BorderSizePixel = 0 listFrame.BackgroundTransparency = 1 listFrame.ClipsDescendants = true listFrame:SetAttribute("OriginalBackgroundColor3", originalBackgroundColor3) listFrame:SetAttribute("OriginalBackgroundTransparency", originalBackgroundTransparency) listFrame:SetAttribute("OriginalText", originalText) local layout = Instance.new("UIListLayout") layout.Name = "LetterLayout" layout.FillDirection = Enum.FillDirection.Horizontal layout.Wraps = true layout.HorizontalAlignment = X_ALIGNMENT_MAP[originalXAlignment] or Enum.HorizontalAlignment.Left layout.VerticalAlignment = Y_ALIGNMENT_MAP[originalYAlignment] or Enum.VerticalAlignment.Top layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Parent = listFrame return listFrame end -- Transforms a TextLabel into a Frame for easy manipulation of letters. -- Letter index is stored in LayoutOrder for each Frame. -- Stores necessary style information as attributes on the returned Frame. function module.TransformText(labelToTransform: TextLabel): Frame local originalText = labelToTransform.Text local originalParent = labelToTransform.Parent local originalFontFace = labelToTransform.FontFace local originalTextSize = labelToTransform.TextSize local originalTextColor = labelToTransform.TextColor3 local originalTextTransparency = labelToTransform.TextTransparency local originalTextStrokeColor = labelToTransform.TextStrokeColor3 local originalTextStrokeTransparency = labelToTransform.TextStrokeTransparency local originalXAlignment = labelToTransform.TextXAlignment local originalBackgroundColor3 = labelToTransform.BackgroundColor3 local originalBackgroundTransparency = labelToTransform.BackgroundTransparency local listFrame = module.CreateFrameFromLabel(labelToTransform) listFrame.BackgroundColor3 = originalBackgroundColor3 listFrame.BackgroundTransparency = originalBackgroundTransparency listFrame:SetAttribute("LetterFontFace", originalFontFace) listFrame:SetAttribute("LetterTextSize", originalTextSize) listFrame:SetAttribute("LetterTextColor3", originalTextColor) listFrame:SetAttribute("LetterTextTransparency", originalTextTransparency) listFrame:SetAttribute("LetterTextStrokeColor3", originalTextStrokeColor) listFrame:SetAttribute("LetterTextStrokeTransparency", originalTextStrokeTransparency) listFrame:SetAttribute("LetterTextXAlignment", originalXAlignment) local spaceParams = Instance.new("GetTextBoundsParams") spaceParams.Font = originalFontFace spaceParams.Size = originalTextSize spaceParams.Text = " " local success, spaceSize: Vector2 = pcall(function() return TextService:GetTextBoundsAsync(spaceParams) end) if success then listFrame:SetAttribute("SpaceSizeX", spaceSize.X) listFrame:SetAttribute("SpaceSizeY", spaceSize.Y) else listFrame:SetAttribute("SpaceSizeX", 10) listFrame:SetAttribute("SpaceSizeY", 10) end local charParams = Instance.new("GetTextBoundsParams") charParams.Font = originalFontFace charParams.Size = originalTextSize local textLength = utf8.len(originalText) :: number local childrenToParent: { Frame } = table.create(textLength) :: { Frame } local childIndex = 0 for char in string.gmatch(originalText, ".") do local charSize: Vector2 local isWhitespace = string.match(char, "%s") if isWhitespace then charSize = spaceSize else charParams.Text = char charSize = TextService:GetTextBoundsAsync(charParams) end local textContainer = Instance.new("Frame") textContainer.Name = char textContainer.BackgroundTransparency = 1 textContainer.BorderSizePixel = 0 textContainer.Size = UDim2.fromOffset(charSize.X, charSize.Y) textContainer.LayoutOrder = childIndex if not isWhitespace then local charLabel = Instance.new("TextLabel") charLabel.Name = "CharLabel" charLabel.FontFace = originalFontFace charLabel.TextSize = originalTextSize charLabel.TextColor3 = originalTextColor charLabel.TextTransparency = originalTextTransparency charLabel.TextStrokeColor3 = originalTextStrokeColor charLabel.TextStrokeTransparency = originalTextStrokeTransparency charLabel.BackgroundTransparency = 1 charLabel.Size = UDim2.fromScale(1, 1) charLabel.Text = char charLabel.TextXAlignment = originalXAlignment charLabel.TextYAlignment = Enum.TextYAlignment.Center charLabel.Parent = textContainer end childIndex += 1 childrenToParent[childIndex] = textContainer end for i = 1, childIndex do childrenToParent[i].Parent = listFrame end table.clear(childrenToParent) listFrame.Parent = originalParent labelToTransform:Destroy() return listFrame end -- Inserts a new letter (Frame) into the listFrame at the specified index. -- Adjusts LayoutOrder of subsequent letters. -- Returns the newly created letter Frame or nil if attributes are missing. function module.InsertLetter(listFrame: Frame, letter: string, index: number): Frame? local fontFace = listFrame:GetAttribute("LetterFontFace") :: Font? local textSize = listFrame:GetAttribute("LetterTextSize") :: number? local textColor = listFrame:GetAttribute("LetterTextColor3") :: Color3? local textTransparency = listFrame:GetAttribute("LetterTextTransparency") :: number? local textStrokeColor = listFrame:GetAttribute("LetterTextStrokeColor3") :: Color3? local textStrokeTransparency = listFrame:GetAttribute("LetterTextStrokeTransparency") :: number? local textXAlignment = listFrame:GetAttribute("LetterTextXAlignment") :: Enum.TextXAlignment? local spaceSizeX = listFrame:GetAttribute("SpaceSizeX") :: number? local spaceSizeY = listFrame:GetAttribute("SpaceSizeY") :: number? if not ( fontFace and textSize and textColor and textTransparency and textStrokeColor and textStrokeTransparency and textXAlignment and spaceSizeX and spaceSizeY ) then warn("Cannot InsertLetter: listFrame is missing required style attributes.") return nil end local spaceSize = Vector2.new(spaceSizeX, spaceSizeY) local currentLetters: { Frame } = {} for _, child in ipairs(listFrame:GetChildren()) do if child:IsA("Frame") then table.insert(currentLetters, child) end end index = math.clamp(index, 0, #currentLetters) for _, childFrame in ipairs(currentLetters) do if childFrame.LayoutOrder >= index then childFrame.LayoutOrder += 1 end end local charSize: Vector2 local isWhitespace = string.match(letter, "%s") if isWhitespace then charSize = spaceSize else local charParams = Instance.new("GetTextBoundsParams") charParams.Font = fontFace charParams.Size = textSize charParams.Text = letter charSize = TextService:GetTextBoundsAsync(charParams) end local textContainer = Instance.new("Frame") textContainer.Name = letter textContainer.BackgroundTransparency = 1 textContainer.BorderSizePixel = 0 textContainer.Size = UDim2.fromOffset(charSize.X, charSize.Y) textContainer.LayoutOrder = index if not isWhitespace then local charLabel = Instance.new("TextLabel") charLabel.Name = "CharLabel" charLabel.FontFace = fontFace charLabel.TextSize = textSize charLabel.TextColor3 = textColor charLabel.TextTransparency = textTransparency charLabel.TextStrokeColor3 = textStrokeColor charLabel.TextStrokeTransparency = textStrokeTransparency charLabel.BackgroundTransparency = 1 charLabel.Size = UDim2.fromScale(1, 1) charLabel.Text = letter charLabel.TextXAlignment = textXAlignment charLabel.TextYAlignment = Enum.TextYAlignment.Center charLabel.Parent = textContainer end textContainer.Parent = listFrame return textContainer end return module