General purpose modules for Roblox development. [Read-only Codeberg mirror]
1--!optimize 2
2--!strict
3local module = {}
4
5local TextService = game:GetService("TextService")
6
7local X_ALIGNMENT_MAP = {
8 [Enum.TextXAlignment.Left] = Enum.HorizontalAlignment.Left,
9 [Enum.TextXAlignment.Center] = Enum.HorizontalAlignment.Center,
10 [Enum.TextXAlignment.Right] = Enum.HorizontalAlignment.Right,
11}
12local Y_ALIGNMENT_MAP = {
13 [Enum.TextYAlignment.Top] = Enum.VerticalAlignment.Top,
14 [Enum.TextYAlignment.Center] = Enum.VerticalAlignment.Center,
15 [Enum.TextYAlignment.Bottom] = Enum.VerticalAlignment.Bottom,
16}
17
18-- Creates a <code>Frame</code> from a <code>TextLabel</code> for easy manipulation of letters.
19-- Intended to be used with <code>InsertLetter(listFrame: Frame, letter: string, index: number): Frame?</code>
20function module.CreateFrameFromLabel(labelTemplate: TextLabel): Frame
21 local originalText = labelTemplate.Text
22 local originalSize = labelTemplate.Size
23 local originalPosition = labelTemplate.Position
24 local originalAnchorPoint = labelTemplate.AnchorPoint
25 local originalRotation = labelTemplate.Rotation
26 local originalName = labelTemplate.Name
27 local originalBackgroundColor3 = labelTemplate.BackgroundColor3
28 local originalBackgroundTransparency = labelTemplate.BackgroundTransparency
29 local originalXAlignment = labelTemplate.TextXAlignment
30 local originalYAlignment = labelTemplate.TextYAlignment
31
32 local listFrame = Instance.new("Frame")
33 listFrame.Name = originalName
34 listFrame.Size = originalSize
35 listFrame.Position = originalPosition
36 listFrame.AnchorPoint = originalAnchorPoint
37 listFrame.Rotation = originalRotation
38 listFrame.BorderSizePixel = 0
39 listFrame.BackgroundTransparency = 1
40 listFrame.ClipsDescendants = true
41 listFrame:SetAttribute("OriginalBackgroundColor3", originalBackgroundColor3)
42 listFrame:SetAttribute("OriginalBackgroundTransparency", originalBackgroundTransparency)
43 listFrame:SetAttribute("OriginalText", originalText)
44
45 local layout = Instance.new("UIListLayout")
46 layout.Name = "LetterLayout"
47 layout.FillDirection = Enum.FillDirection.Horizontal
48 layout.Wraps = true
49 layout.HorizontalAlignment = X_ALIGNMENT_MAP[originalXAlignment] or Enum.HorizontalAlignment.Left
50 layout.VerticalAlignment = Y_ALIGNMENT_MAP[originalYAlignment] or Enum.VerticalAlignment.Top
51 layout.SortOrder = Enum.SortOrder.LayoutOrder
52 layout.Parent = listFrame
53
54 return listFrame
55end
56
57-- Transforms a <code>TextLabel</code> into a <code>Frame</code> for easy manipulation of letters.
58-- Letter index is stored in <code>LayoutOrder</code> for each <code>Frame</code>.
59-- Stores necessary style information as attributes on the returned <code>Frame</code>.
60function module.TransformText(labelToTransform: TextLabel): Frame
61 local originalText = labelToTransform.Text
62 local originalParent = labelToTransform.Parent
63 local originalFontFace = labelToTransform.FontFace
64 local originalTextSize = labelToTransform.TextSize
65 local originalTextColor = labelToTransform.TextColor3
66 local originalTextTransparency = labelToTransform.TextTransparency
67 local originalTextStrokeColor = labelToTransform.TextStrokeColor3
68 local originalTextStrokeTransparency = labelToTransform.TextStrokeTransparency
69 local originalXAlignment = labelToTransform.TextXAlignment
70 local originalBackgroundColor3 = labelToTransform.BackgroundColor3
71 local originalBackgroundTransparency = labelToTransform.BackgroundTransparency
72
73 local listFrame = module.CreateFrameFromLabel(labelToTransform)
74 listFrame.BackgroundColor3 = originalBackgroundColor3
75 listFrame.BackgroundTransparency = originalBackgroundTransparency
76
77 listFrame:SetAttribute("LetterFontFace", originalFontFace)
78 listFrame:SetAttribute("LetterTextSize", originalTextSize)
79 listFrame:SetAttribute("LetterTextColor3", originalTextColor)
80 listFrame:SetAttribute("LetterTextTransparency", originalTextTransparency)
81 listFrame:SetAttribute("LetterTextStrokeColor3", originalTextStrokeColor)
82 listFrame:SetAttribute("LetterTextStrokeTransparency", originalTextStrokeTransparency)
83 listFrame:SetAttribute("LetterTextXAlignment", originalXAlignment)
84
85 local spaceParams = Instance.new("GetTextBoundsParams")
86 spaceParams.Font = originalFontFace
87 spaceParams.Size = originalTextSize
88 spaceParams.Text = " "
89 local success, spaceSize: Vector2 = pcall(function()
90 return TextService:GetTextBoundsAsync(spaceParams)
91 end)
92 if success then
93 listFrame:SetAttribute("SpaceSizeX", spaceSize.X)
94 listFrame:SetAttribute("SpaceSizeY", spaceSize.Y)
95 else
96 listFrame:SetAttribute("SpaceSizeX", 10)
97 listFrame:SetAttribute("SpaceSizeY", 10)
98 end
99
100 local charParams = Instance.new("GetTextBoundsParams")
101 charParams.Font = originalFontFace
102 charParams.Size = originalTextSize
103
104 local textLength = utf8.len(originalText) :: number
105 local childrenToParent: { Frame } = table.create(textLength) :: { Frame }
106 local childIndex = 0
107 for char in string.gmatch(originalText, ".") do
108 local charSize: Vector2
109 local isWhitespace = string.match(char, "%s")
110
111 if isWhitespace then
112 charSize = spaceSize
113 else
114 charParams.Text = char
115 charSize = TextService:GetTextBoundsAsync(charParams)
116 end
117
118 local textContainer = Instance.new("Frame")
119 textContainer.Name = char
120 textContainer.BackgroundTransparency = 1
121 textContainer.BorderSizePixel = 0
122 textContainer.Size = UDim2.fromOffset(charSize.X, charSize.Y)
123 textContainer.LayoutOrder = childIndex
124
125 if not isWhitespace then
126 local charLabel = Instance.new("TextLabel")
127 charLabel.Name = "CharLabel"
128 charLabel.FontFace = originalFontFace
129 charLabel.TextSize = originalTextSize
130 charLabel.TextColor3 = originalTextColor
131 charLabel.TextTransparency = originalTextTransparency
132 charLabel.TextStrokeColor3 = originalTextStrokeColor
133 charLabel.TextStrokeTransparency = originalTextStrokeTransparency
134 charLabel.BackgroundTransparency = 1
135 charLabel.Size = UDim2.fromScale(1, 1)
136 charLabel.Text = char
137 charLabel.TextXAlignment = originalXAlignment
138 charLabel.TextYAlignment = Enum.TextYAlignment.Center
139 charLabel.Parent = textContainer
140 end
141
142 childIndex += 1
143 childrenToParent[childIndex] = textContainer
144 end
145
146 for i = 1, childIndex do
147 childrenToParent[i].Parent = listFrame
148 end
149
150 table.clear(childrenToParent)
151
152 listFrame.Parent = originalParent
153 labelToTransform:Destroy()
154
155 return listFrame
156end
157
158-- Inserts a new letter (<code>Frame</code>) into the <code>listFrame</code> at the specified index.
159-- Adjusts <code>LayoutOrder</code> of subsequent letters.
160-- Returns the newly created letter <code>Frame</code> or <code>nil</code> if attributes are missing.
161function module.InsertLetter(listFrame: Frame, letter: string, index: number): Frame?
162 local fontFace = listFrame:GetAttribute("LetterFontFace") :: Font?
163 local textSize = listFrame:GetAttribute("LetterTextSize") :: number?
164 local textColor = listFrame:GetAttribute("LetterTextColor3") :: Color3?
165 local textTransparency = listFrame:GetAttribute("LetterTextTransparency") :: number?
166 local textStrokeColor = listFrame:GetAttribute("LetterTextStrokeColor3") :: Color3?
167 local textStrokeTransparency = listFrame:GetAttribute("LetterTextStrokeTransparency") :: number?
168 local textXAlignment = listFrame:GetAttribute("LetterTextXAlignment") :: Enum.TextXAlignment?
169 local spaceSizeX = listFrame:GetAttribute("SpaceSizeX") :: number?
170 local spaceSizeY = listFrame:GetAttribute("SpaceSizeY") :: number?
171
172 if
173 not (
174 fontFace
175 and textSize
176 and textColor
177 and textTransparency
178 and textStrokeColor
179 and textStrokeTransparency
180 and textXAlignment
181 and spaceSizeX
182 and spaceSizeY
183 )
184 then
185 warn("Cannot InsertLetter: listFrame is missing required style attributes.")
186 return nil
187 end
188
189 local spaceSize = Vector2.new(spaceSizeX, spaceSizeY)
190
191 local currentLetters: { Frame } = {}
192 for _, child in ipairs(listFrame:GetChildren()) do
193 if child:IsA("Frame") then
194 table.insert(currentLetters, child)
195 end
196 end
197
198 index = math.clamp(index, 0, #currentLetters)
199
200 for _, childFrame in ipairs(currentLetters) do
201 if childFrame.LayoutOrder >= index then
202 childFrame.LayoutOrder += 1
203 end
204 end
205
206 local charSize: Vector2
207 local isWhitespace = string.match(letter, "%s")
208
209 if isWhitespace then
210 charSize = spaceSize
211 else
212 local charParams = Instance.new("GetTextBoundsParams")
213 charParams.Font = fontFace
214 charParams.Size = textSize
215 charParams.Text = letter
216 charSize = TextService:GetTextBoundsAsync(charParams)
217 end
218
219 local textContainer = Instance.new("Frame")
220 textContainer.Name = letter
221 textContainer.BackgroundTransparency = 1
222 textContainer.BorderSizePixel = 0
223 textContainer.Size = UDim2.fromOffset(charSize.X, charSize.Y)
224 textContainer.LayoutOrder = index
225
226 if not isWhitespace then
227 local charLabel = Instance.new("TextLabel")
228 charLabel.Name = "CharLabel"
229 charLabel.FontFace = fontFace
230 charLabel.TextSize = textSize
231 charLabel.TextColor3 = textColor
232 charLabel.TextTransparency = textTransparency
233 charLabel.TextStrokeColor3 = textStrokeColor
234 charLabel.TextStrokeTransparency = textStrokeTransparency
235 charLabel.BackgroundTransparency = 1
236 charLabel.Size = UDim2.fromScale(1, 1)
237 charLabel.Text = letter
238 charLabel.TextXAlignment = textXAlignment
239 charLabel.TextYAlignment = Enum.TextYAlignment.Center
240 charLabel.Parent = textContainer
241 end
242
243 textContainer.Parent = listFrame
244
245 return textContainer
246end
247
248return module