diff --git a/src/libs/gui.luau b/src/libs/gui.luau index 1d7eede..ded5113 100644 --- a/src/libs/gui.luau +++ b/src/libs/gui.luau @@ -395,7 +395,7 @@ function createInputArea(parent: Instance) ZIndex = 4, FontFace = Font.fromEnum(Enum.Font.Code), TextSize = 15, - PlaceholderText = "Search...", + PlaceholderText = "", Text = "", PlaceholderColor3 = guiModule.colorPalette[guiModule.selectedPalette].Subtext0, TextColor3 = guiModule.colorPalette[guiModule.selectedPalette].Subtext0, @@ -404,6 +404,23 @@ function createInputArea(parent: Instance) ClearTextOnFocus = false, }) + -- Create the placeholder label (sits behind the TextBox) + createInstance("TextLabel", { + Name = "TextLabel", + Parent = input, + BackgroundTransparency = 1, + + Position = UDim2.new(0, 10, 0, 0), + Size = UDim2.new(0.942, 16, 1, 0), + + ZIndex = 3, + FontFace = Font.fromEnum(Enum.Font.Code), + TextSize = 15, + Text = "Search...", + TextColor3 = guiModule.colorPalette[guiModule.selectedPalette].Subtext0, + TextXAlignment = Enum.TextXAlignment.Left, + }) + -- Register the input frame in the table guiModule.guiElements.Gui.Input = input @@ -605,7 +622,7 @@ function guiModule:rebuildUI() removeAllCommands() -- add all commands back w/ selection. - for i, cmd in filtered do + for i, cmd in ipairs(filtered) do local isSelected = (i == guiModule.selectedCommand) createCommandPreview(guiModule.guiElements.Gui.CommandsScrolling, cmd.title, cmd.callback, isSelected) end @@ -658,18 +675,74 @@ function guiModule:activateSelected() -- Fire our entry! local entry = guiModule.guiElements.Gui.Commands[idx] if entry then + -- The Enter that got us here is spent, so it can't leak into whatever + -- the callback opens (an input prompt, a pushed page, ...) + guiModule._inputSubmitGuardUntil = os.clock() + 0.14 + entry.callback() end end +--// Hides the placeholder label whenever the user has typed something +function updatePlaceholder() + local input = guiModule.guiElements.Gui.Input + if not input then + return + end + + local textBox = input:FindFirstChild("TextBox") + local placeholder = input:FindFirstChild("TextLabel") + + if not textBox or not placeholder then + return + end + + -- The box is single line, so a newline is always Roblox leaking the Enter + -- that opened it: strip it, which re-runs this with the clean text. + local text = (textBox.Text:gsub("[\r\n]", "")) + if text ~= textBox.Text then + textBox.Text = text + return + end + + placeholder.Visible = text == "" +end + +--// Focuses the input box, but only once the Enter that opened it is gone +function focusInputBox(textBox: TextBox, token: number) + task.delay(math.max(0, guiModule._inputSubmitGuardUntil - os.clock()), function() + -- Is this still the active request? + if guiModule._inputModeToken ~= token or not textBox.Parent then + return + end + + textBox.Text = "" + textBox:CaptureFocus() + + -- Hard clear in case Roblox injected a stray character + task.defer(function() + if guiModule._inputModeToken ~= token or not textBox.Parent then + return + end + + textBox.Text = "" + updatePlaceholder() + end) + end) +end + --// Setup the search feature on the palette function setupSearch() -- Input setup! local inputBox: TextBox = guiModule.guiElements.Gui.Input:WaitForChild("TextBox") inputBox:GetPropertyChangedSignal("Text"):Connect(function() + updatePlaceholder() guiModule:rebuildUI() end) + -- Sync once for the initial (empty) state + updatePlaceholder() + -- Capture focus inputBox:CaptureFocus() @@ -693,7 +766,7 @@ function guiModule:addCommands(commands: { { title: string, callback: () -> () } guiModule.registeredCommands = {} -- Insert commands into registered - for _, cmd in commands do + for _, cmd in ipairs(commands) do -- Is test & developer mode enabled? if cmd.isTest and not _G.developerMode then @@ -713,12 +786,18 @@ function guiModule:pushCommands(commands) guiModule._inputSubmitGuardUntil = os.clock() + 0.14 local textBox = guiModule.guiElements.Gui.Input and guiModule.guiElements.Gui.Input:FindFirstChild("TextBox") + local placeholder = guiModule.guiElements.Gui.Input and guiModule.guiElements.Gui.Input:FindFirstChild("TextLabel") if textBox then textBox:ReleaseFocus() textBox.Text = "" end + -- Remember this level's placeholder so popping back restores it + if placeholder then + guiModule.registeredCommands.placeholder = placeholder.Text + end + -- Insert registered commands into the stack table.insert(guiModule.navigationStack, guiModule.registeredCommands) @@ -726,7 +805,7 @@ function guiModule:pushCommands(commands) guiModule.registeredCommands = {} -- loop through commands - for _, cmd in commands do + for _, cmd in ipairs(commands) do table.insert(guiModule.registeredCommands, cmd) end @@ -766,11 +845,16 @@ function guiModule:popCommands() guiModule._inputModeToken += 1 local textBox = guiModule.guiElements.Gui.Input:FindFirstChild("TextBox") + local placeholder = guiModule.guiElements.Gui.Input:FindFirstChild("TextLabel") + if placeholder then + placeholder.Text = guiModule._inputModePrevPlaceholder or "Search..." + end if textBox then - textBox.PlaceholderText = guiModule._inputModePrevPlaceholder or "Search..." textBox.Text = "" end + updatePlaceholder() + guiModule.inputMode = false guiModule:rebuildUI() return @@ -782,9 +866,9 @@ function guiModule:popCommands() -- does previous exist? if prev then -- restore placeholder - local textBox = guiModule.guiElements.Gui.Input:FindFirstChild("TextBox") - if textBox and prev.placeholder then - textBox.PlaceholderText = prev.placeholder + local placeholder = guiModule.guiElements.Gui.Input:FindFirstChild("TextLabel") + if placeholder and prev.placeholder then + placeholder.Text = prev.placeholder end guiModule.registeredCommands = prev @@ -795,6 +879,8 @@ function guiModule:popCommands() if guiModule.guiElements.Gui.Input:FindFirstChild("TextBox") then guiModule.guiElements.Gui.Input:FindFirstChild("TextBox").Text = "" end + + updatePlaceholder() else guiModule:closePalette() end @@ -811,6 +897,7 @@ just be warned! --// Cancel input mode system function guiModule:cancelInputMode(returnedValue : string, callback:(string) -> (any), token : number) local textBox : TextBox = guiModule.guiElements.Gui.Input:WaitForChild("TextBox") + local placeholder: TextLabel = guiModule.guiElements.Gui.Input:WaitForChild("TextLabel") local thisToken = token or guiModule._inputModeToken -- Guard against return leaking @@ -823,8 +910,9 @@ function guiModule:cancelInputMode(returnedValue : string, callback:(string) -> end guiModule.inputMode = false - textBox.PlaceholderText = guiModule._inputModePrevPlaceholder or "Search..." + placeholder.Text = guiModule._inputModePrevPlaceholder or "Search..." textBox.Text = "" + updatePlaceholder() guiModule:rebuildUI() end) @@ -835,13 +923,14 @@ end function guiModule:requestUserInput(textPrompt : string, callback: (string) -> (any)) local textBox : TextBox = guiModule.guiElements.Gui.Input:WaitForChild("TextBox") + local placeholder: TextLabel = guiModule.guiElements.Gui.Input:WaitForChild("TextLabel") -- Generation/token for req guiModule._inputModeToken += 1 local myToken = guiModule._inputModeToken -- Get old before replacement - local oldPlaceholderText = textBox.PlaceholderText + local oldPlaceholderText = placeholder.Text -- Value the user inputs (input string) local returnedValue = "" @@ -855,31 +944,16 @@ function guiModule:requestUserInput(textPrompt : string, callback: (string) -> ( end -- TextBox! - textBox.PlaceholderText = textPrompt + placeholder.Text = textPrompt textBox.Text = "" + updatePlaceholder() -- More setup removeAllCommands() resizePalette(0) - -- Defer focus so the previous Enter press doesn't bleed into this box - task.defer(function() - -- Is this still the active request? - if guiModule._inputModeToken ~= myToken then - return - end - - textBox:CaptureFocus() - - -- Hard reset again after focus in case Roblox injects junk - task.defer(function() - if guiModule._inputModeToken ~= myToken then - return - end - - textBox.Text = "" - end) - end) + -- Delay focus so the Enter press that opened this doesn't bleed into the box + focusInputBox(textBox, myToken) local conn conn = textBox.FocusLost:Connect(function(enterPressed) @@ -888,6 +962,13 @@ function guiModule:requestUserInput(textPrompt : string, callback: (string) -> ( return end + -- The Enter that opened this request drops focus on the box we just + -- took over; that isn't a submission, so keep the prompt up & refocus. + if os.clock() < guiModule._inputSubmitGuardUntil then + focusInputBox(textBox, myToken) + return + end + if conn then conn:Disconnect() conn = nil @@ -916,9 +997,10 @@ function guiModule:requestUserInput(textPrompt : string, callback: (string) -> ( end guiModule.inputMode = false - textBox.PlaceholderText = guiModule._inputModePrevPlaceholder or "Search..." + placeholder.Text = guiModule._inputModePrevPlaceholder or "Search..." textBox.Text = "" guiModule._inputModePrevPlaceholder = nil + updatePlaceholder() guiModule:rebuildUI() end) end) @@ -942,8 +1024,11 @@ function guiModule:rebuildTheme() if gui.Input then gui.Input.BackgroundColor3 = guiModule.colorPalette[guiModule.selectedPalette].Surface0 local textBox : TextBox = gui.Input:FindFirstChild("TextBox") + local placeholder: TextLabel = gui.Input:FindFirstChild("TextLabel") + if placeholder then + placeholder.TextColor3 = guiModule.colorPalette[guiModule.selectedPalette].Subtext0 + end if textBox then - textBox.PlaceholderColor3 = guiModule.colorPalette[guiModule.selectedPalette].Subtext0 textBox.TextColor3 = guiModule.colorPalette[guiModule.selectedPalette].Subtext0 end end diff --git a/src/libs/search.luau b/src/libs/search.luau index d6e798d..00e8b4d 100644 --- a/src/libs/search.luau +++ b/src/libs/search.luau @@ -14,7 +14,7 @@ function searchModule:search(query : string, commandList : {{title : string, cal local sorted = {} -- fuzzy search through all the commands based on query - for _, cmd in commandList do + for _, cmd in ipairs(commandList) do local matched, score = utilityModule:fuzzySearch(query, cmd.title) if matched then table.insert(results, { cmd = cmd, score = score })