Detecting Text Selection

Hello devs, I was wondering if there was a way to find/know what a player is selecting in a TextBox.

For example:
image
and it would return ‘experience does’ or something like that?

I’m trying to make a system of highlighting text and pressing a button to make it bold.

thank you!

From my understanding roblox does not support text selection outside of textboxes. With a textbox input you can use properties CursorPosition and SelectionStart. Read more on the api reference Textbox

Ah ok. Would there be a better way of making a format text system?
I was thinking to make the TextBox have RichText on and get people to just type HTML formats (eg. <b>Hi</b> would look like Hi but I didn’t think that would be the best UX.

Sadly roblox has not put a lot of effort into text editing. You still could use selection properties and rich text, inserting <b> and </b> wouldn’t be too difficult. Text boxes also clear on focus so make sure to store the text elsewhere

with some testing you also cannot really get the selection start as clicking elsewhere loses focus and the selection. So if you wanted to use a gui button you cannot because of the clicking focus loss, and if you want to use keyboard command too bad cause the textbox consumes those inputs too.

--!strict
local context = game:GetService("ContextActionService")
local textbox = script.Parent.TextBox
local boldbutton = script.Parent.TextButton

local function bold_selection()
	if textbox.SelectionStart == -1 then
		warn("No text selected")
		return
	end
	
	local cstart = math.min(textbox.CursorPosition, textbox.SelectionStart)
	local cend = math.max(textbox.CursorPosition, textbox.SelectionStart)
	local newtext = string.format("%s<b>%s</b>%s", textbox.Text:sub(0, cstart), textbox.Text:sub(cstart, cend), textbox.Text:sub(cend))
	textbox.Text = newtext
end

context:BindAction("bold", bold_selection, false, Enum.KeyCode.B)
boldbutton.Activated:Connect(bold_selection)

Here’s the script I used to test, the context action never fires and the bold button activation always warns “No text selected”, but typing in <b> manually shows rich text.

1 Like

Can’t you just turn off ClearTextOnFocus property?

1 Like

That’s the part that deletes the textbox contents when you focus into it I don’t believe it keeps your selection outside of it.

Sorry for the late reply.

It doesn’t seem to work.

Output:
image


I’ve been playing around with RichText and this is what I’ve done so far - pretty basic.

This is basically the idea I want, except I want a button that does it for you over your selection, as you know. Still don’t know how to find an alternative since the selection won’t work.

Edit - just found this:

I now know it is possible. I found a game called Lua Learning by @boatbomber which utilises this feature.

Below is a video where I was showing the features. At the end, I show some of the downsides of how it breaks.

Note: OBS didn’t record my mouse cursor, but I clicked on the buttons above the TextBox to format it.

Video

Thanks for helping so far (:

3 Likes

I just made a somewhat working implementation of this:

local textBox = script.Parent
local UIS = game:GetService("UserInputService")
local mouseHeld = false

local lastSelection

UIS.InputBegan:Connect(function(inputObject)
	if inputObject.UserInputType == Enum.UserInputType.MouseButton1 then
		mouseHeld = true
	end
end)

UIS.InputEnded:Connect(function(inputObject)
	if inputObject.UserInputType == Enum.UserInputType.MouseButton1 then
		mouseHeld = false
	end
end)

function onCursorPosChanged()
	if mouseHeld then
		repeat task.wait() until not mouseHeld
		lastSelection =  string.sub(textBox.Text, textBox.SelectionStart, textBox.CursorPosition)
		print(lastSelection)
	end
end

textBox:GetPropertyChangedSignal("CursorPosition"):Connect(onCursorPosChanged)

There is probably a much much better way to go about it, it basically just waits until the mouse is let go and stores the selection in the lastSelection variable

Thanks for replying.

I tested it out an it returns the print of what you sellected - :smiley:

This isn’t my strong point so I’m not sure but do you know how to wrap ‘<b>’ and ‘</b>’ around what you selected? Thanks.

1 Like

Yeah, you can just append it to the string using the … operator, make sure rich text is on though!

local bolded = "<b>" .. lastSelection .. "</b>"

I tried that and it didn’t work. It just makes the text turn into <b></b>.

Before:
image

Selecting:
image

After:
image

Okay, I made a test textbox to mimic the same functionality using the script I wrote before as a basis, and it worked for me in studio:

-- Changes to the previous script

-- new definitions:
local selectionStart
local selectionEnd

function onCursorPosChanged()
	if mouseHeld then
		repeat task.wait() until not mouseHeld
		lastSelection =  string.sub(textBox.Text, textBox.SelectionStart, textBox.CursorPosition-1)
		selectionStart = textBox.SelectionStart
		selectionEnd = textBox.CursorPosition
	end
end



textBox:GetPropertyChangedSignal("CursorPosition"):Connect(onCursorPosChanged)

-- Run this when the button is pressed
if lastSelection then
	local bolded = '<b>' .. lastSelection .. '</b>'
	textBox:ReleaseFocus()
	textBox.Text = string.sub(textBox.Text, 1, selectionStart-1) .. bolded .. string.sub(textBox.Text, selectionEnd)
end

I hope this works for you, I included the ReleaseFocus() since I noticed that rich text only gets bolded when a textbox is unfocused.

If you want to modify things about it later I recommend reading up on the string functions roblox provides: string , since there is probably lots to improve upon here, I’m pretty new to roblox development myself.

I’m not sure if I’m missing something because it doesn’t seem to work for me.

My script currently looks like this (`script` is child of `TextBox`):
local textBox = script.Parent
local UIS = game:GetService("UserInputService")
local mouseHeld = false

local selectionStart
local selectionEnd

local lastSelection

UIS.InputBegan:Connect(function(inputObject)
	if inputObject.UserInputType == Enum.UserInputType.MouseButton1 then
		mouseHeld = true
	end
end)

UIS.InputEnded:Connect(function(inputObject)
	if inputObject.UserInputType == Enum.UserInputType.MouseButton1 then
		mouseHeld = false
	end
end)

function onCursorPosChanged()
	if mouseHeld then
		repeat task.wait() until not mouseHeld
		lastSelection =  string.sub(textBox.Text, textBox.SelectionStart, textBox.CursorPosition-1)
		selectionStart = textBox.SelectionStart
		selectionEnd = textBox.CursorPosition
	end
end

textBox:GetPropertyChangedSignal("CursorPosition"):Connect(onCursorPosChanged)

script.Parent.Parent.Options.Bold.MouseButton1Click:Connect(function()
	if lastSelection then
		local bolded = '<b>' .. lastSelection .. '</b>'
		textBox:ReleaseFocus()
		textBox.Text = string.sub(textBox.Text, 1, selectionStart-1) .. bolded .. string.sub(textBox.Text, selectionEnd)
	end
end)

When running, if I select the word ‘Hello’, it will change to ‘Hello<b></b>Hello’ which is close to what I am looking for, but instead it should be ‘<b>Hello</b>’.

Edit: I tried printing lastSelection and returns nothing, like literally “”.

I’ve tried a normal script and a local script, and the local script works better since the normal script doesn’t run.

1 Like

a script on the server cannot know the text that the user has typed, so it uses a local script, about this, did you get what you are looking for? If not yet, I’ll get to work.