Detect `Shift` inside a TextBox

Hello. To get to the point, I am working on a Studio Chat plugin.

Basically, whenever I press Shift + Return / Enter, I want a TextBox to create a new line.
I will not be using the Multiline property for TextBoxes since you do not need to hold Shift to create a new line.

Here are the codes I tried:

#1: UserInputService:IsKeyDown()

-- RunContext Plugin
local TextBox: TextBox = widget.TextBox
local UserInputService = game:GetService("UserInputService")

TextBox.FocusLost:Connect(function(enterPressed)
    if not enterPressed then return end

    if UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or UserInputService:IsKeyDown(Enum.KeyCode.RightShift) then
        TextBox:CaptureFocus()
        print("New line")
        -- rest of the code....
    end
end

It seems that neither of them were being detected.

#2: holdsShift variable via InputBegan & InputEnded

-- RunContext Plugin
local TextBox: TextBox = widget.TextBox
local UserInputService = game:GetService("UserInputService")

local holdsShift = false

UserInputService.InputBegan:Connect(function(input)
    if input.KeyCode == Enum.KeyCode.LeftShift or input.KeyCode == Enum.KeyCode.RightShift then
        print("InputBegan Shift")
        holdsShift = true
    end
end

UserInputService.InputEnded:Connect(function(input)
    if input.KeyCode == Enum.KeyCode.LeftShift or input.KeyCode == Enum.KeyCode.RightShift then
        print("InputEnded Shift")
        holdsShift = false
    end
end

TextBox.FocusLost:Connect(function(enterPressed)
    if not enterPressed then return end

    if holdsShift then
        TextBox:CaptureFocus()
        print("New line")
        -- rest of the code....
    end
end

This did detect Shift, however, it seems that as soon as you focus on the TextBox it sets the variable to false.

Is there anything I can do to fix this? Are there any workarounds or a plugin function for detecting keybinds? Please let me know.

2 Likes

i got the same question as you like 2 weeks ago! anyways here’s what I got:

local TextBox = widget.TextBox
local UserInputService = game:GetService("UserInputService")

local handledEnter = false

local function getCursorIndex()
    local text = TextBox.Text or ""
    local index = TextBox.SelectionStart

    if not index or index < 0 then
        index = TextBox.CursorPosition or 0
    end
    if index < 0 then index = 0 end
    local len = #text
    if index > len then index = len end

    return index
end

local function insertAtCursor(insertStr)
    local text = TextBox.Text or ""
    local index = getCursorIndex()
    local left = text:sub(1, index)
    local right = text:sub(index + 1)
    local newText = left .. insertStr .. right
    TextBox.Text = newText

    local success = pcall(function()
        TextBox.CursorPosition = index + #insertStr
    end)

    TextBox:CaptureFocus()
    return success
end

UserInputService.InputBegan:Connect(function(input, gpi)
    if gpi then return end
    if input.UserInputType == Enum.UserInputType.Keyboard then
        local key = input.KeyCode
        if (key == Enum.KeyCode.Return or key == Enum.KeyCode.KeypadEnter) and TextBox:IsFocused() then
            if UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or UserInputService:IsKeyDown(Enum.KeyCode.RightShift) then
                handledEnter = true
                insertAtCursor("\n")
            end
        end
    end
end)

TextBox.FocusLost:Connect(function(enterPressed)
    if handledEnter then
        handledEnter = false
        return
    end
    if not enterPressed then
        return
    end
end)

TextBox.Focused:Connect(function() end)
2 Likes

Thank you. I will try the code and let you know if it works.

Sorry for the late reply.

Using your code, it turns out nothing past the key variable part works. Here is everything I’ve changed in your code:

UserInputService.InputBegan:Connect(function(input, gpi)
	if not gpi then return end -- Reversed gpi so it detects when you're pressing keys even if you are focused
	if input.UserInputType == Enum.UserInputType.Keyboard then
		local key = input.KeyCode
		print(key)


		-- Nothing happens past this point

		if (key == Enum.KeyCode.Return or key == Enum.KeyCode.KeypadEnter) and TextBox:IsFocused() then
			print("Is focused")
			if UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or UserInputService:IsKeyDown(Enum.KeyCode.RightShift) then
				handledEnter = true
				print("New line")
				insertAtCursor("\n")
			end
		end
	end
end)

TextBox.FocusLost:Connect(function(enterPressed)
	if handledEnter then
		handledEnter = false
		print("Handled enter") -- Doesn't print
		return
	end

	print("No new line")

	if not enterPressed then
		return
	end
end)

image
Am I doing something wrong?

1 Like

Well my script actually works on my end so maybe its the way you used it? anyways your script have mistakes, sorry!

that basically stopped everything from running. youre basically saying “if roblox did NOT process the input, stop running.” so yea never write this line in any code ever :upside_down_face:

also i changed my code a bit check it out!

local TextBox = widget.TextBox
local UserInputService = game:GetService("UserInputService")

local handledEnter = false

local function getCursorIndex()
    local text = TextBox.Text
    local index = TextBox.CursorPosition
    if index == -1 then
        index = #text + 1
    end
    index = math.clamp(index, 1, #text + 1)
    return index
end

local function insertAtCursor(insertStr)
    local text = TextBox.Text
    local index = getCursorIndex()
    local left = text:sub(1, index - 1)
    local right = text:sub(index)
    local newText = left .. insertStr .. right
    TextBox.Text = newText

    task.defer(function() --no throttle
        TextBox.CursorPosition = index + #insertStr
    end)

    return true
end

UserInputService.InputBegan:Connect(function(input, gpi)
    if gpi then return end
    if input.UserInputType == Enum.UserInputType.Keyboard then
        local key = input.KeyCode
        if (key == Enum.KeyCode.Return or key == Enum.KeyCode.KeypadEnter) and TextBox:IsFocused() then
            if UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or UserInputService:IsKeyDown(Enum.KeyCode.RightShift) then
                handledEnter = true
                insertAtCursor("\n")
            end
        end
    end
end)

TextBox.FocusLost:Connect(function(enterPressed)
    if handledEnter then
        handledEnter = false
        return
    end
    if not enterPressed then
        return
    end
end)

TextBox.Focused:Connect(function() end)
1 Like

I really don’t know what I could be doing wrong here.
As I was saying, I tested this in a newly created place and I pasted your exact code:

local TextBox = script.Parent
local UserInputService = game:GetService("UserInputService")

local handledEnter = false

local function getCursorIndex()
	local text = TextBox.Text
	local index = TextBox.CursorPosition
	if index == -1 then
		index = #text + 1
	end
	index = math.clamp(index, 1, #text + 1)
	return index
end

local function insertAtCursor(insertStr)
	local text = TextBox.Text
	local index = getCursorIndex()
	local left = text:sub(1, index - 1)
	local right = text:sub(index)
	local newText = left .. insertStr .. right
	TextBox.Text = newText

	task.defer(function() --no throttle
		TextBox.CursorPosition = index + #insertStr
	end)

	return true
end

UserInputService.InputBegan:Connect(function(input, gpi)
	if gpi then return end
	if input.UserInputType == Enum.UserInputType.Keyboard then
		local key = input.KeyCode
		if (key == Enum.KeyCode.Return or key == Enum.KeyCode.KeypadEnter) and TextBox:IsFocused() then
			if UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or UserInputService:IsKeyDown(Enum.KeyCode.RightShift) then
				handledEnter = true
				insertAtCursor("\n")
			end
		end
	end
end)

TextBox.FocusLost:Connect(function(enterPressed)
	if handledEnter then
		handledEnter = false
		return
	end
	if not enterPressed then
		return
	end
end)

TextBox.Focused:Connect(function() end)

Yet when I press Shift + Enter, it just seems to lose focus.
Since it is a new place, the Script’s run context is Client.

wait do you have multiline on or no

I don’t have it

characterssssss

uhm

TextBox.Multiline = true

Instead of checking Shift during FocusLost, detect Enter in InputBegan while the textbox is focused.

This lets you check Shift and Enter at the same time, before the textbox loses focus.

InputBegan runs before the textbox processes Enter and you’d use IsKeDown() to correctly report Shift.

Something like:

if gameProcessed then return end
if not TextBox:IsFocused() then return end

if input.KeyCode == Enum.KeyCode.Return then
	if UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) 
	or UserInputService:IsKeyDown(Enum.KeyCode.RightShift) then
			
		TextBox.Text ..= "\n"
			
		print("New line")
	end
end
1 Like

Sorry for the late reply once again.

Do you need multiline for this too?

I already implemented @qushenya’s code and turns out if you enable multiline it works. There is one issue where pressing Enter (without holding Shift) adds a new line too, but you can just remove that using TextBox.Text:sub(1, -2).

I am not quite sure what to mark as the solution here.

Thank you for the time though.

@MadnessIsHear111222

Multiline is required for newline characters, otherwise the TextBox will automatically ignore it.

1 Like

Got it. Thank you. I’ll try it when I have time. :slight_smile:

psst

local TextBox = widget.TextBox
local UserInputService = game:GetService("UserInputService")

local handledEnter = false

TextBox.MultiLine = true
TextBox.ClearTextOnFocus = false

local function getCursorIndex()
    local text = TextBox.Text
    local index = TextBox.CursorPosition
    if index == -1 then
        index = #text + 1
    end
    index = math.clamp(index, 1, #text + 1)
    return index
end

local function insertAtCursor(insertStr)
    local text = TextBox.Text
    local index = getCursorIndex()
    local left = text:sub(1, index - 1)
    local right = text:sub(index)
    local newText = left .. insertStr .. right
    TextBox.Text = newText

    task.defer(function()
        TextBox.CursorPosition = index + #insertStr
    end)

    return true
end

UserInputService.InputBegan:Connect(function(input, gpi)
    if gpi then return end
    if input.UserInputType == Enum.UserInputType.Keyboard then
        local key = input.KeyCode
        if (key == Enum.KeyCode.Return or key == Enum.KeyCode.KeypadEnter) and TextBox:IsFocused() then
            if UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or UserInputService:IsKeyDown(Enum.KeyCode.RightShift) then
                handledEnter = true
                insertAtCursor("\n")
            else
                TextBox:ReleaseFocus(true)
            end
        end
    end
end)

TextBox.FocusLost:Connect(function(enterPressed)
    if handledEnter then
        handledEnter = false
        TextBox:CaptureFocus()
        return
    end
    if not enterPressed then
        return
    end
end)

TextBox.Focused:Connect(function() end)

yes it works

only difference is that you cannot “lose focus” by pressing enter, which is kind of the whole point here

you also needed to press Enter/Return twice to lose focus, idk why that is

field.MultiLine = true
field.ClearTextOnFocus = false

local internal = false
field:GetPropertyChangedSignal("Text"):Connect(function()
	if internal then
		internal = false
		return
	end

	local text = field.Text
	if text:sub(-2) == "\\n" then
		internal = true
		field.Text = text:sub(1, -3) .. "\n"
		field.CursorPosition = #field.Text + 1
		return
	end
	if text:sub(-1) == "\n" then
		field.Text = text:sub(1, -2)
		send()
	end
end)
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.