I’m developing an INTERNAL Online Help for my game, ie, a manual inside Frames (because Roblox blocks access to EXTERNAL hyperlinks).
So I have a frame with a TextLabel like this:
As in HTML Links Hyperlinks, I would like that when the player hovers over the link it changes color and when he clicks on the link it opens another frame.
But I came across this limitation of Roblox, which doesn’t allow to the creation of hyperlinks.
I also noticed that the seamless concatenation between objects of type TextLabel and TextButton is a very annoying thing to do manually.
So I decided to come up with a solution for this, where, providing a single TextLabel like this…
… and creating something like this…
To get to this result, you must provide an original TextLabel containing your normal text, but wrapping the part of the text you want to hyperlink with the same HTML syntax.
Example:
<a href="link1">Click here</a> or also <a href="link2">click on this link</a> to open some links.
So here’s the code you can put on your LocalScript:
-- from: rogeriodec_games
local function Hyperlinks(TextLabel, LinkColor, HoverColor)
local function ExtractTextAndLinks(String)
local Sequence = {}
repeat
local PosOpenA = string.find(String, '<a', 1)
if PosOpenA then
if PosOpenA > 1 then
local Text = string.sub(String, 1, PosOpenA-1)
table.insert(Sequence, {Type = 'Text', Text = Text})
end
local PosCloseA = string.find(String, '</a>', PosOpenA+2)
if not PosCloseA then
warn('No closing </a>')
return
end
local Tag = string.sub(String, PosOpenA, PosCloseA+3)
local PosHref = string.find(Tag, 'href', 4)
if not PosHref then
warn('No href')
return
end
local Key = Tag:split('"')
if #Key ~= 3 then
warn('Incomplete quotes on href')
return
end
local Href = Key[2]
local PosCloseA2 = string.find(Key[3], '</a>', 1)
local Text = string.sub(Key[3], 2, PosCloseA2-1)
table.insert(Sequence, {Type = 'Link', Href = Href, Text = Text})
String = string.sub(String, PosCloseA+4)
end
until not PosOpenA
if String then
table.insert(Sequence, {Type = 'Text', Text = String})
end
return Sequence
end
local function Trim(str)
return string.gsub(str, "^%s*(.-)%s*$", "%1")
end
local Sequence = ExtractTextAndLinks(TextLabel.Text)
if #Sequence == 1 and Sequence[1].Type == 'Text' then return end
local Pos = Vector2.new(0, 0)
for Id, Object in ipairs(Sequence) do
local Type = Object.Type == 'Link' and 'TextButton' or 'TextLabel'
local Text = Instance.new(Type)
Text.Name = string.format("%02i", Id) .. '_' .. Type
Text.Parent = TextLabel.Parent
Text.AutomaticSize = TextLabel.AutomaticSize
Text.BackgroundColor3 = TextLabel.BackgroundColor3
Text.BorderColor3 = TextLabel.BorderColor3
Text.BorderMode = TextLabel.BorderMode
Text.BorderSizePixel = TextLabel.BorderSizePixel
Text.LayoutOrder = TextLabel.LayoutOrder
Text.SizeConstraint = TextLabel.SizeConstraint
Text.Visible = TextLabel.Visible
Text.ZIndex = TextLabel.ZIndex
Text.AutoLocalize = TextLabel.AutoLocalize
Text.BackgroundTransparency = TextLabel.BackgroundTransparency
Text.FontFace = TextLabel.FontFace
Text.RichText = true
Text.Text = Object.Text
Text.TextColor3 = TextLabel.TextColor3
Text.TextSize = TextLabel.TextSize
Text.TextStrokeColor3 = TextLabel.TextStrokeColor3
Text.TextStrokeTransparency = TextLabel.TextStrokeTransparency
Text.TextTransparency = TextLabel.TextTransparency
Text.TextTruncate = TextLabel.TextTruncate
Text.TextXAlignment = TextLabel.TextXAlignment
Text.TextYAlignment = TextLabel.TextYAlignment
Text.Position = UDim2.new(0, Pos.X, 0, Pos.Y)
local Size
local Text2 = ''
repeat
Size = Text.TextBounds
Text.Size = UDim2.new(0, Size.X, 0, Size.Y)
if Pos.X + Size.X > TextLabel.Parent.AbsoluteSize.X then -- whether text must be wrapped
if Object.Type == 'Link' then
Pos = Vector2.new(0, Pos.Y + Size.Y)
--Size = Vector2.new(0, 0)
Text.Position = UDim2.new(0, 0, 0, Pos.Y)
else
repeat
local Char = string.sub(Text.Text, -1)
Text.Text = string.sub(Text.Text, 1, string.len(Text.Text) - 1)
Text2 = Char .. Text2
until not Char or Char == ' '
end
elseif Text2 ~= '' then
Text2 = Trim(Text2)
table.insert(Sequence, Id + 1, {Type = 'Text', Text = Text2})
Pos = Vector2.new(0, Pos.Y + Size.Y)
Size = Vector2.new(0, 0)
Text2 = ''
end
until Text2 == ''
Pos += Vector2.new(Size.X, 0)
if Type == 'TextButton' then
Text.Text = '<u>' .. Text.Text .. '</u>'
if LinkColor then Text.TextColor3 = LinkColor end
local TextColor3 = Text.TextColor3
Text.MouseEnter:Connect(function()
if HoverColor then Text.TextColor3 = HoverColor end
end)
Text.MouseLeave:Connect(function()
Text.TextColor3 = TextColor3
end)
Text.MouseButton1Down:Connect(function()
print(Object.Href)
end)
end
end
TextLabel:Destroy() -- remove the original TextLabel (with HTML Tags)
end
-- START ---
local TextLabel = game.Players.LocalPlayer.PlayerGui.ScreenGui.Frame2.TextLabel -- your original TextLabel (with the HTML tags)
Hyperlinks(TextLabel, Color3.fromRGB(255, 0, 0), Color3.fromRGB(127, 127, 127)) -- create many TextLabels and TextButtons as children of the same original TextLabel's Parent
Edit v1.2
Did some improvements to wrap the resulting text when its size is greater than its Parent’s size: