Advertisement
alex290

TextInput.lua

Feb 24th, 2025
183
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.04 KB | None | 0 0
  1. local TextService = game:GetService("TextService")
  2.  
  3. local root = script.Parent.Parent.Parent
  4.  
  5. local gui = root.Gui
  6. local colors = require(gui.Colors)
  7.  
  8. local core = require(gui.Core)
  9. local event = core.Event
  10. local instanceEvent = core.InstanceEvent
  11. local forceMousePosition = core.ForceMousePosition
  12. local forceInputChange = core.ForceInputChange
  13.  
  14. local functions = root.Functions
  15. local setupTooltip = require(functions.Tooltip).Setup
  16. local sanitizeRichText = require(functions.SanitizeRichText)
  17. local color3Difference = require(functions.Color3Difference)
  18.  
  19. local function getTextWidth(text)
  20.     return TextService:GetTextSize(text, 14, Enum.Font.SourceSans, Vector2.new(math.huge, math.huge)).X
  21. end
  22.  
  23. type Module = (name: string, parent: ScrollingFrame, default: string?, tooltip: string?) -> Frame
  24. return function(name, parent, default, tooltip)
  25.     if typeof(parent) ~= "Instance" or parent.ClassName ~= "ScrollingFrame" then error("Invalid parent.") end
  26.    
  27.     local container = Instance.new("Frame")
  28.     container.Size = UDim2.new(1, -2, 0, 20)
  29.     container.BackgroundTransparency = 1
  30.     container.BorderSizePixel = 0
  31.     container.Name = name
  32.     container.Parent = parent
  33.    
  34.     local left = Instance.new("Frame")
  35.     left.Position = UDim2.new(0, 1, 0, 0)
  36.     left.Size = UDim2.new(0.5, -1, 1, 0)
  37.     left.BackgroundColor3 = colors.Background
  38.     left.Name = "Left"
  39.     left.Parent = container
  40.     do
  41.         local stroke = Instance.new("UIStroke")
  42.         stroke.Color = colors.Border.Secondary
  43.         stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
  44.         stroke.LineJoinMode = Enum.LineJoinMode.Miter
  45.         stroke.Parent = left
  46.     end
  47.     if type(tooltip) == "string" then
  48.         setupTooltip(left, tooltip)
  49.     end
  50.    
  51.     local right = Instance.new("Frame")
  52.     right.Position = UDim2.new(0.5, 1, 0, 0)
  53.     right.Size = UDim2.fromScale(0.5, 1)
  54.     right.BackgroundColor3 = colors.Background
  55.     right.ClipsDescendants = true
  56.     right.Name = "Right"
  57.     right.Parent = container
  58.     do
  59.         local stroke = Instance.new("UIStroke")
  60.         stroke.Color = colors.Border.Secondary
  61.         stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
  62.         stroke.LineJoinMode = Enum.LineJoinMode.Miter
  63.         stroke.Parent = right
  64.     end
  65.     local rightBottomBorderCover = Instance.new("Frame")
  66.     rightBottomBorderCover.Position = UDim2.new(0.5, 1, 1, 0)
  67.     rightBottomBorderCover.Size = UDim2.new(0.5, 0, 0, 1)
  68.     rightBottomBorderCover.BackgroundColor3 = colors.TextField
  69.     rightBottomBorderCover.BorderSizePixel = 0
  70.     rightBottomBorderCover.ZIndex = 2147483647 -- int-32 limit.
  71.     rightBottomBorderCover.Visible = false
  72.     rightBottomBorderCover.Name = "RightBottomBorderCover"
  73.     rightBottomBorderCover.Parent = container
  74.    
  75.     container:SetAttribute("Selected", false)
  76.     instanceEvent.MouseEnter(container, function()
  77.         if not container:GetAttribute("Selected") then
  78.             left.BackgroundColor3 = colors.Highlighted
  79.             right.BackgroundColor3 = colors.Highlighted
  80.         end
  81.     end)
  82.     instanceEvent.MouseLeave(container, function()
  83.         if not container:GetAttribute("Selected") then
  84.             left.BackgroundColor3 = colors.Background
  85.             right.BackgroundColor3 = colors.Background
  86.         end
  87.     end)
  88.    
  89.     local label = Instance.new("TextLabel")
  90.     label.Position = UDim2.fromOffset(24, 2)
  91.     label.Size = UDim2.new(1, -28, 1, 0)
  92.     label.BackgroundTransparency = 1
  93.     label.Font = Enum.Font.SourceSans
  94.     label.TextSize = 14
  95.     label.TextColor3 = colors.Text.Primary
  96.     label.TextXAlignment = Enum.TextXAlignment.Left
  97.     label.TextYAlignment = Enum.TextYAlignment.Top
  98.     label.TextTruncate = Enum.TextTruncate.SplitWord
  99.     label.Text = name
  100.     label.Name = "Label"
  101.     label.Parent = left
  102.    
  103.     local textBox = Instance.new("TextBox")
  104.     textBox.Size = UDim2.fromScale(1, 1)
  105.     textBox.BackgroundTransparency = 1
  106.     textBox.TextTransparency = 1
  107.     textBox.Font = Enum.Font.SourceSans
  108.     textBox.TextSize = 14
  109.     textBox.TextXAlignment = Enum.TextXAlignment.Left
  110.     textBox.TextYAlignment = Enum.TextYAlignment.Top
  111.     textBox.ClearTextOnFocus = false
  112.     textBox.Active = false
  113.     textBox.Parent = right
  114.     do
  115.         local padding = Instance.new("UIPadding")
  116.         padding.PaddingTop = UDim.new(0, 2)
  117.         padding.PaddingLeft = UDim.new(0, 2)
  118.         padding.PaddingRight = UDim.new(0, 2)
  119.         padding.Parent = textBox
  120.     end
  121.     local textLabel = Instance.new("TextLabel")
  122.     textLabel.Position = UDim2.fromOffset(4, 2)
  123.     textLabel.Size = UDim2.new(1, -8, 1, -12)
  124.     textLabel.BackgroundTransparency = 1
  125.     textLabel.BorderSizePixel = 0
  126.     textLabel.Font = Enum.Font.SourceSans
  127.     textLabel.TextSize = 14
  128.     textLabel.TextColor3 = colors.Text.Primary
  129.     textLabel.TextXAlignment = Enum.TextXAlignment.Left
  130.     textLabel.TextYAlignment = Enum.TextYAlignment.Top
  131.     textLabel.TextTruncate = Enum.TextTruncate.SplitWord
  132.     textLabel.Parent = right
  133.    
  134.     if type(default) == "string" then
  135.         container:SetAttribute("Text", default)
  136.         textBox.Text = default
  137.         textLabel.Text = sanitizeRichText(default)
  138.     else
  139.         container:SetAttribute("Text", "")
  140.         textBox.Text = ""
  141.         textLabel.Text = ""
  142.     end
  143.    
  144.     local cursor = Instance.new("Frame")
  145.     cursor.Position = UDim2.fromOffset(0, 1)
  146.     cursor.Size = UDim2.fromOffset(1, 15)
  147.     cursor.BackgroundColor3 = colors.Cursor -- rbg(255, 255, 255) with "difference" blending mode for identical to Studio - not supported in Roblox unfortunately.
  148.     cursor.BorderSizePixel = 0
  149.     cursor.ZIndex = 2
  150.     cursor.Visible = false
  151.     cursor.Parent = textBox
  152.    
  153.     local selectionBackground = Instance.new("Frame")
  154.     selectionBackground.Position = UDim2.fromOffset(0, 1)
  155.     selectionBackground.Size = UDim2.fromOffset(0, 15)
  156.     selectionBackground.BackgroundColor3 = colors.Primary
  157.     selectionBackground.BorderSizePixel = 0
  158.     selectionBackground.Visible = false
  159.     selectionBackground.Parent = textBox
  160.    
  161.     local cursorThread = nil
  162.     local hoverConnection = nil
  163.     local inputConnection = nil
  164.    
  165.     local lastAbsoluteCursorPosition = 0
  166.     local lastCursorPosition = -1
  167.     local lastSelectionStart = -1
  168.     local lastText = textBox.Text
  169.    
  170.     local justDefocused = false
  171.    
  172.     local differentSelectionColor = color3Difference(colors.Text.Edit, colors.Text.Highlighted) > 0.000004 -- We detect 0.01 difference; a difference that is visually unpresent, yet the code can detect.
  173.     event.ThemeChanged:Connect(function()
  174.         if color3Difference(colors.Text.Edit, colors.Text.Highlighted) < 0.000004 then
  175.             textLabel.Text = lastText
  176.             textLabel.RichText = false
  177.             differentSelectionColor = false
  178.         elseif not differentSelectionColor then
  179.             textLabel.Text = sanitizeRichText(lastText)
  180.             textLabel.RichText = true
  181.             differentSelectionColor = true
  182.         end
  183.     end)
  184.    
  185.     local function cursorLoop()
  186.         while true do
  187.             task.wait(0.533)
  188.             cursor.Visible = false
  189.             task.wait(0.533)
  190.             cursor.Visible = true
  191.         end
  192.     end
  193.    
  194.     local function updateSelection()
  195.         -- lastText will be current text.
  196.         -- lastSelectionStart will be current selection start.
  197.         -- lastCursorPosition will be current cursor position.
  198.         -- lastAbsoluteCursorPosition will be current absolute cursor position.
  199.        
  200.         if lastSelectionStart == -1 then
  201.             selectionBackground.Visible = false
  202.            
  203.             textLabel.Text = lastText
  204.             textLabel.RichText = false
  205.         else
  206.             if lastSelectionStart > lastCursorPosition then
  207.                 local selectedText = lastText:sub(lastCursorPosition, lastSelectionStart - 1)
  208.                 selectionBackground.Size = UDim2.fromOffset(getTextWidth(selectedText), 15)
  209.                 selectionBackground.Position = UDim2.fromOffset(textLabel.Position.X.Offset - 2 + lastAbsoluteCursorPosition, 1)
  210.                
  211.                 if differentSelectionColor then
  212.                     textLabel.Text = sanitizeRichText(lastText:sub(1, lastCursorPosition - 1))..'<font color="#'..colors.Text.Highlighted:ToHex()..'">'..sanitizeRichText(selectedText).."</font>"..sanitizeRichText(lastText:sub(lastSelectionStart, -1))
  213.                     textLabel.RichText = true
  214.                 end
  215.             else
  216.                 local selectedText = lastText:sub(lastSelectionStart, lastCursorPosition - 1)
  217.                 local leftOfSelectionText = lastText:sub(1, lastSelectionStart - 1)
  218.                 selectionBackground.Size = UDim2.fromOffset(getTextWidth(selectedText), 15)
  219.                 selectionBackground.Position = UDim2.fromOffset(textLabel.Position.X.Offset - 2 + getTextWidth(leftOfSelectionText), 1)
  220.                
  221.                 if differentSelectionColor then
  222.                     textLabel.Text = sanitizeRichText(leftOfSelectionText)..'<font color="#'..colors.Text.Highlighted:ToHex()..'">'..sanitizeRichText(selectedText).."</font>"..sanitizeRichText(lastText:sub(lastCursorPosition, -1))
  223.                     textLabel.RichText = true
  224.                 end
  225.             end
  226.            
  227.             selectionBackground.Visible = true
  228.         end
  229.     end
  230.     local function updatePosition()
  231.         -- lastText will be current text.
  232.        
  233.         coroutine.close(cursorThread)
  234.         cursor.Visible = true
  235.         cursorThread = coroutine.create(cursorLoop)
  236.         coroutine.resume(cursorThread)
  237.        
  238.         local absoluteCursorPosition = getTextWidth(lastText:sub(1, textBox.CursorPosition - 1))
  239.         local move = absoluteCursorPosition - lastAbsoluteCursorPosition
  240.         if move > 0 then
  241.             lastAbsoluteCursorPosition = absoluteCursorPosition
  242.            
  243.             local newCursorPosition = cursor.Position.X.Offset + move
  244.             local difference = newCursorPosition - (textBox.AbsoluteSize.X - 4)
  245.             if difference > 0 then
  246.                 textLabel.Position = UDim2.fromOffset(textLabel.Position.X.Offset - difference, 2)
  247.                 textLabel.Size = UDim2.new(1, textLabel.Size.X.Offset + difference, 1, -12)
  248.                 cursor.Position = UDim2.fromOffset(textBox.AbsoluteSize.X - 4, 1)
  249.             else
  250.                 cursor.Position = UDim2.fromOffset(newCursorPosition, 1)
  251.             end
  252.         elseif move < 0 then
  253.             lastAbsoluteCursorPosition = absoluteCursorPosition
  254.            
  255.             local newCursorPosition = cursor.Position.X.Offset + move
  256.             if newCursorPosition < 0 then
  257.                 textLabel.Position = UDim2.fromOffset(textLabel.Position.X.Offset - newCursorPosition, 2)
  258.                 textLabel.Size = UDim2.new(1, textLabel.Size.X.Offset + newCursorPosition, 1, -12)
  259.                
  260.                 cursor.Position = UDim2.fromOffset(0, 1)
  261.             else
  262.                 cursor.Position = UDim2.fromOffset(newCursorPosition, 1)
  263.             end
  264.         end
  265.        
  266.         if textLabel.Position.X.Offset < 2 then
  267.             local difference = textLabel.Position.X.Offset + getTextWidth(lastText) - (textBox.AbsoluteSize.X - 2)
  268.             if difference < 0 then
  269.                 local position = textLabel.Position.X.Offset - difference
  270.                 if position > 2 then
  271.                     textLabel.Position = UDim2.fromOffset(2, 2)
  272.                     textLabel.Size = UDim2.new(1, textLabel.Size.X.Offset + difference + position - 2, 1, -12)
  273.                     cursor.Position = UDim2.fromOffset(cursor.Position.X.Offset - difference - position + 2, 1)
  274.                 else
  275.                     textLabel.Position = UDim2.fromOffset(position, 2)
  276.                     textLabel.Size = UDim2.new(1, textLabel.Size.X.Offset + difference, 1, -12)
  277.                     cursor.Position = UDim2.fromOffset(cursor.Position.X.Offset - difference, 1)
  278.                 end
  279.             end
  280.         end
  281.     end
  282.     textBox.Changed:Connect(function()
  283.         if cursorThread == nil then return end
  284.        
  285.         local newCursorPosition = false
  286.        
  287.         if textBox.Text ~= lastText then
  288.             lastText = textBox.Text -- lastText is now current text.
  289.             if differentSelectionColor then
  290.                 textLabel.Text = sanitizeRichText(lastText)
  291.             else
  292.                 textLabel.Text = lastText
  293.             end
  294.            
  295.             updatePosition()
  296.            
  297.             container:SetAttribute("Text", lastText)
  298.         elseif textBox.CursorPosition ~= lastCursorPosition then
  299.             newCursorPosition = true
  300.             lastCursorPosition = textBox.CursorPosition
  301.            
  302.             updatePosition()
  303.             updateSelection()
  304.         end
  305.        
  306.         if newCursorPosition then
  307.             updateSelection()
  308.         elseif lastSelectionStart ~= textBox.SelectionStart then
  309.             lastSelectionStart = textBox.SelectionStart -- lastSelectionStart is now current selection start.
  310.            
  311.             updateSelection()
  312.         end
  313.     end)
  314.     instanceEvent.MouseButton1Down(container, function()
  315.         local focused = false
  316.        
  317.         if cursorThread == nil and not justDefocused then
  318.             focused = true
  319.            
  320.             textBox:CaptureFocus()
  321.             textBox.ZIndex = 2147483647 -- int-32 limit.
  322.             textBox.SelectionStart = 1
  323.            
  324.             hoverConnection = textBox.MouseEnter:Connect(function()
  325.                 forceMousePosition(Vector2.new(textBox.AbsolutePosition.X, textBox.AbsolutePosition.Y))
  326.             end)
  327.             inputConnection = textBox.InputChanged:Connect(forceInputChange)
  328.            
  329.             textLabel.Position = UDim2.fromOffset(2, 2)
  330.             textLabel.Size = UDim2.new(1, -4, 1, -12)
  331.             textLabel.TextColor3 = colors.Text.Edit
  332.             right.BackgroundColor3 = colors.TextField
  333.             textLabel.TextTruncate = Enum.TextTruncate.None
  334.             cursor.Visible = true
  335.             rightBottomBorderCover.Visible = true
  336.            
  337.             cursorThread = coroutine.create(cursorLoop)
  338.             coroutine.resume(cursorThread)
  339.         end
  340.        
  341.         if not container:GetAttribute("Selected") then
  342.             container:SetAttribute("Selected", true)
  343.             left.BackgroundColor3 = colors.Primary
  344.             if cursorThread == nil then
  345.                 right.BackgroundColor3 = colors.Primary
  346.             end
  347.             label.TextColor3 = colors.Text.Highlighted
  348.             if not differentSelectionColor or not focused then
  349.                 textLabel.TextColor3 = colors.Text.Highlighted
  350.             else
  351.                 textLabel.TextColor3 = colors.Text.Edit
  352.             end
  353.            
  354.             for _, instance in ipairs(parent:GetChildren()) do
  355.                 if instance.ClassName ~= "Frame" then continue end
  356.                 if instance == container then continue end
  357.                
  358.                 instance:SetAttribute("Selected", false)
  359.             end
  360.         end
  361.     end)
  362.     textBox.FocusLost:Connect(function()
  363.         justDefocused = true
  364.        
  365.         coroutine.close(cursorThread)
  366.         cursorThread = nil
  367.         hoverConnection:Disconnect()
  368.         hoverConnection = nil
  369.         inputConnection:Disconnect()
  370.         inputConnection = nil
  371.        
  372.         textBox.ZIndex = 1
  373.        
  374.         lastCursorPosition = -1
  375.         lastAbsoluteCursorPosition = 0
  376.        
  377.         cursor.Position = UDim2.fromOffset(0, 1)
  378.         textLabel.Position = UDim2.fromOffset(4, 2)
  379.         textLabel.Size = UDim2.new(1, -8, 1, -12)
  380.         if container:GetAttribute("Selected") then
  381.             textLabel.TextColor3 = colors.Text.Highlighted
  382.         else
  383.             textLabel.TextColor3 = colors.Text.Primary
  384.         end
  385.         right.BackgroundColor3 = colors.Primary
  386.         textLabel.TextTruncate = Enum.TextTruncate.SplitWord
  387.         cursor.Visible = false
  388.         rightBottomBorderCover.Visible = false
  389.        
  390.         task.wait()
  391.         justDefocused = false
  392.     end)
  393.    
  394.     container:GetAttributeChangedSignal("Selected"):Connect(function()
  395.         if not container:GetAttribute("Selected") then
  396.             left.BackgroundColor3 = colors.Background
  397.             right.BackgroundColor3 = colors.Background
  398.             label.TextColor3 = colors.Text.Primary
  399.             textLabel.TextColor3 = colors.Text.Primary
  400.         end
  401.     end)
  402.    
  403.     return container
  404. end :: Module
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement