--[[ Filename: Utility.lua Written by: jeditkacheff Version 1.0 Description: Utility module for CoreScripts --]] ------------------ CONSTANTS -------------------- local SELECTED_COLOR = Color3.fromRGB(0,162,255) local NON_SELECTED_COLOR = Color3.fromRGB(78,84,96) local ARROW_COLOR = Color3.fromRGB(204, 204, 204) local ARROW_COLOR_HOVER = Color3.fromRGB(255, 255, 255) local ARROW_COLOR_TOUCH = ARROW_COLOR_HOVER local ARROW_COLOR_INACTIVE = Color3.fromRGB(150, 150, 150) local SELECTED_LEFT_IMAGE = "rbxasset://textures/ui/Settings/Slider/SelectedBarLeft.png" local NON_SELECTED_LEFT_IMAGE = "rbxasset://textures/ui/Settings/Slider/BarLeft.png" local SELECTED_RIGHT_IMAGE = "rbxasset://textures/ui/Settings/Slider/SelectedBarRight.png" local NON_SELECTED_RIGHT_IMAGE= "rbxasset://textures/ui/Settings/Slider/BarRight.png" local CONTROLLER_SCROLL_DELTA = 0.2 local CONTROLLER_THUMBSTICK_DEADZONE = 0.8 local DROPDOWN_BG_TRANSPARENCY = 0.2 ------------- SERVICES ---------------- local HttpService = game:GetService("HttpService") local UserInputService = game:GetService("UserInputService") local GuiService = game:GetService("GuiService") local RunService = game:GetService("RunService") local CoreGui = game:GetService("CoreGui") local RobloxGui = CoreGui:FindFirstChild("RobloxGui") local ContextActionService = game:GetService("ContextActionService") local VRService = game:GetService("VRService") --------------- FLAGS ---------------- local fixSettingsMenuDropdownsSuccess, fixSettingsMenuDropdownsValue = pcall(function() return settings():GetFFlag("FixSettingsMenuDropdowns") end) local fixSettingsMenuDropdowns = fixSettingsMenuDropdownsSuccess and fixSettingsMenuDropdownsValue local success, result = pcall(function() return settings():GetFFlag('UseNotificationsLocalization') end) local FFlagUseNotificationsLocalization = success and result local FFlagFixInactiveSelectorArrowsSuccess, FFlagFixInactiveSelectorArrowsResult = pcall(function() return settings():GetFFlag("FixInactiveSelectorArrows") end) local FFlagFixInactiveSelectorArrows = FFlagFixInactiveSelectorArrowsSuccess and FFlagFixInactiveSelectorArrowsResult ------------------ VARIABLES -------------------- local tenFootInterfaceEnabled = require(RobloxGui.Modules:WaitForChild("TenFootInterface")):IsEnabled() ----------- UTILITIES -------------- local Util = {} do function Util.Create(instanceType) return function(data) local obj = Instance.new(instanceType) local parent = nil for k, v in pairs(data) do if type(k) == 'number' then v.Parent = obj elseif k == 'Parent' then parent = v else obj[k] = v end end if parent then obj.Parent = parent end return obj end end end local onResizedCallbacks = {} setmetatable(onResizedCallbacks, { __mode = 'k' }) -- used by several guis to show no selection adorn local noSelectionObject = Util.Create'ImageLabel' { Image = "", BackgroundTransparency = 1 }; -- MATH -- function clamp(low, high, input) return math.max(low, math.min(high, input)) end function ClampVector2(low, high, input) return Vector2.new(clamp(low.x, high.x, input.x), clamp(low.y, high.y, input.y)) end ---- TWEENZ ---- local function Linear(t, b, c, d) if t >= d then return b + c end return c*t/d + b end local function EaseOutQuad(t, b, c, d) if t >= d then return b + c end t = t/d return b - c*t*(t - 2) end local function EaseInOutQuad(t, b, c, d) if t >= d then return b + c end t = t/d if t < 1/2 then return 2*c*t*t + b end return b + c*(2*(2 - t)*t - 1) end function PropertyTweener(instance, prop, start, final, duration, easingFunc, cbFunc) local this = {} this.StartTime = tick() this.EndTime = this.StartTime + duration this.Cancelled = false local finished = false local percentComplete = 0 local function finalize() if instance then instance[prop] = easingFunc(1, start, final - start, 1) end finished = true percentComplete = 1 if cbFunc then cbFunc() end end -- Initial set instance[prop] = easingFunc(0, start, final - start, duration) coroutine.wrap(function() local now = tick() while now < this.EndTime and instance do if this.Cancelled then return end instance[prop] = easingFunc(now - this.StartTime, start, final - start, duration) percentComplete = clamp(0, 1, (now - this.StartTime) / duration) RunService.RenderStepped:wait() now = tick() end if this.Cancelled == false and instance then finalize() end end)() function this:GetFinal() return final end function this:GetPercentComplete() return percentComplete end function this:IsFinished() return finished end function this:Finish() if not finished then self:Cancel() finalize() end end function this:Cancel() this.Cancelled = true end return this end ----------- CLASS DECLARATION -------------- local function CreateSignal() local sig = {} local mSignaler = Instance.new('BindableEvent') local mArgData = nil local mArgDataCount = nil function sig:fire(...) mArgData = {...} mArgDataCount = select('#', ...) mSignaler:Fire() end function sig:connect(f) if not f then error("connect(nil)", 2) end return mSignaler.Event:Connect(function() f(unpack(mArgData, 1, mArgDataCount)) end) end function sig:wait() mSignaler.Event:wait() if not mArgData then error("Missing arg data, likely due to :TweenSize/Position corrupting threadrefs.") end return unpack(mArgData, 1, mArgDataCount) end return sig end local function getViewportSize() while not workspace.CurrentCamera do workspace.Changed:wait() end -- ViewportSize is initally set to 1, 1 in Camera.cpp constructor. -- Also check against 0, 0 incase this is changed in the future. while workspace.CurrentCamera.ViewportSize == Vector2.new(0,0) or workspace.CurrentCamera.ViewportSize == Vector2.new(1,1) do workspace.CurrentCamera.Changed:wait() end return workspace.CurrentCamera.ViewportSize end local function isSmallTouchScreen() local viewportSize = getViewportSize() return UserInputService.TouchEnabled and (viewportSize.Y < 500 or viewportSize.X < 700) end local function isPortrait() local viewport = getViewportSize() return viewport.Y > viewport.X end local function isTenFootInterface() return tenFootInterfaceEnabled end local function usesSelectedObject() --VR does not use selected objects (in the same way as gamepad) if VRService.VREnabled then return false end --Touch does not use selected objects unless there's also a gamepad if UserInputService.TouchEnabled and not UserInputService.GamepadEnabled then return false end --PC with gamepad, console... does use selected objects return true end local function isPosOverGui(pos, gui, debug) -- does not account for rotation local ax, ay = gui.AbsolutePosition.x, gui.AbsolutePosition.y local sx, sy = gui.AbsoluteSize.x, gui.AbsoluteSize.y local bx, by = ax+sx, ay+sy return pos.x > ax and pos.x < bx and pos.y > ay and pos.y < by end local function isPosOverGuiWithClipping(pos, gui) -- isPosOverGui, accounts for clipping and visibility, does not account for rotation if not isPosOverGui(pos, gui) then return false end local clipping = false local check = gui while true do if check == nil or (not check:IsA'GuiObject' and not check:IsA'LayerCollector') then clipping = true if check and check:IsA'CoreGui' then clipping = false end break end if check:IsA'GuiObject' and not check.Visible then clipping = true break end if check:IsA'LayerCollector' or check.ClipsDescendants then if not isPosOverGui(pos, check) then clipping = true break end end check = check.Parent end return not clipping end local function areGuisIntersecting(a, b) -- does not account for rotation local aax, aay = a.AbsolutePosition.x, a.AbsolutePosition.y local asx, asy = a.AbsoluteSize.x, a.AbsoluteSize.y local abx, aby = aax+asx, aay+asy local bax, bay = b.AbsolutePosition.x, b.AbsolutePosition.y local bsx, bsy = b.AbsoluteSize.x, b.AbsoluteSize.y local bbx, bby = bax+bsx, bay+bsy local intersectingX = aax < bbx and abx > bax local intersectingY = aay < bby and aby > bay local intersecting = intersectingX and intersectingY return intersecting end local function isGuiVisible(gui, debug) -- true if any part of the gui is visible on the screen, considers clipping, does not account for rotation local clipping = false local check = gui while true do if check == nil or not check:IsA'GuiObject' and not check:IsA'LayerCollector' then clipping = true if check and check:IsA'CoreGui' then clipping = false end break end if check:IsA'GuiObject' and not check.Visible then clipping = true break end if check:IsA'LayerCollector' or check.ClipsDescendants then if not areGuisIntersecting(check, gui) then clipping = true break end end check = check.Parent end return not clipping end local function addHoverState(button, instance, onNormalButtonState, onHoverButtonState) local function onNormalButtonStateCallback() if FFlagFixInactiveSelectorArrows then if button.Active then onNormalButtonState(instance) end else onNormalButtonState(instance) end end local function onHoverButtonStateCallback() if FFlagFixInactiveSelectorArrows then if button.Active then onHoverButtonState(instance) end else onHoverButtonState(instance) end end button.MouseEnter:Connect(onHoverButtonStateCallback) button.SelectionGained:Connect(onHoverButtonStateCallback) button.MouseLeave:Connect(onNormalButtonStateCallback) button.SelectionLost:Connect(onNormalButtonStateCallback) onNormalButtonState(instance) end local function addOnResizedCallback(key, callback) onResizedCallbacks[key] = callback callback(getViewportSize(), isPortrait()) end local gamepadSet = { [Enum.UserInputType.Gamepad1] = true; [Enum.UserInputType.Gamepad2] = true; [Enum.UserInputType.Gamepad3] = true; [Enum.UserInputType.Gamepad4] = true; [Enum.UserInputType.Gamepad5] = true; [Enum.UserInputType.Gamepad6] = true; [Enum.UserInputType.Gamepad7] = true; [Enum.UserInputType.Gamepad8] = true; } local function MakeDefaultButton(name, size, clickFunc, pageRef, hubRef) local SelectionOverrideObject = Util.Create'ImageLabel' { Image = "", BackgroundTransparency = 1, }; local button = Util.Create'ImageButton' { Name = name .. "Button", Image = "rbxasset://textures/ui/Settings/MenuBarAssets/MenuButton.png", ScaleType = Enum.ScaleType.Slice, SliceCenter = Rect.new(8,6,46,44), AutoButtonColor = false, BackgroundTransparency = 1, Size = size, ZIndex = 2, SelectionImageObject = SelectionOverrideObject }; local enabled = Util.Create'BoolValue' { Name = 'Enabled', Parent = button, Value = true } if clickFunc then button.MouseButton1Click:Connect(function() clickFunc(gamepadSet[UserInputService:GetLastInputType()] or false) end) end local function isPointerInput(inputObject) return inputObject.UserInputType == Enum.UserInputType.MouseMovement or inputObject.UserInputType == Enum.UserInputType.Touch end local rowRef = nil local function setRowRef(ref) rowRef = ref end local function selectButton() local hub = hubRef if hub == nil then if pageRef then hub = pageRef.HubRef end end if (hub and hub.Active) or hub == nil then button.Image = "rbxasset://textures/ui/Settings/MenuBarAssets/MenuButtonSelected.png" local scrollTo = button if rowRef then scrollTo = rowRef end if hub then hub:ScrollToFrame(scrollTo) end end end local function deselectButton() button.Image = "rbxasset://textures/ui/Settings/MenuBarAssets/MenuButton.png" end button.InputBegan:Connect(function(inputObject) if button.Selectable and isPointerInput(inputObject) then selectButton() end end) button.InputEnded:Connect(function(inputObject) if button.Selectable and GuiService.SelectedCoreObject ~= button and isPointerInput(inputObject) then deselectButton() end end) button.SelectionGained:Connect(function() selectButton() end) button.SelectionLost:Connect(function() deselectButton() end) local guiServiceCon = GuiService.Changed:Connect(function(prop) if prop ~= "SelectedCoreObject" then return end if not usesSelectedObject() then return end if GuiService.SelectedCoreObject == nil or GuiService.SelectedCoreObject ~= button then deselectButton() return end if button.Selectable then selectButton() end end) return button, setRowRef end local function MakeButton(name, text, size, clickFunc, pageRef, hubRef) local button, setRowRef = MakeDefaultButton(name, size, clickFunc, pageRef, hubRef) local textLabel = Util.Create'TextLabel' { Name = name .. "TextLabel", BackgroundTransparency = 1, BorderSizePixel = 0, Size = UDim2.new(1, 0, 1, -8), Position = UDim2.new(0,0,0,0), TextColor3 = Color3.fromRGB(255,255,255), TextYAlignment = Enum.TextYAlignment.Center, Font = Enum.Font.SourceSansBold, TextSize = 24, Text = text, TextScaled = true, TextWrapped = true, ZIndex = 2, Parent = button }; local constraint = Instance.new("UITextSizeConstraint",textLabel) if isSmallTouchScreen() then textLabel.TextSize = 18 elseif isTenFootInterface() then textLabel.TextSize = 36 end constraint.MaxTextSize = textLabel.TextSize return button, textLabel, setRowRef end local function MakeImageButton(name, image, size, imageSize, clickFunc, pageRef, hubRef) local button, setRowRef = MakeDefaultButton(name, size, clickFunc, pageRef, hubRef) local imageLabel = Util.Create'ImageLabel' { Name = name .. "ImageLabel", BackgroundTransparency = 1, BorderSizePixel = 0, Size = imageSize, Position = UDim2.new(0.5, 0, 0.5, 0), AnchorPoint = Vector2.new(0.5, 0.5), Image = image, ZIndex = 2, Parent = button }; return button, imageLabel, setRowRef end local function AddButtonRow(pageToAddTo, name, text, size, clickFunc, hubRef) local button, textLabel, setRowRef = MakeButton(name, text, size, clickFunc, pageToAddTo, hubRef) local row = Util.Create'Frame' { Name = name .. "Row", BackgroundTransparency = 1, Size = UDim2.new(1, 0, size.Y.Scale, size.Y.Offset), Parent = pageToAddTo.Page } button.Parent = row button.AnchorPoint = Vector2.new(1, 0) button.Position = UDim2.new(1, -20, 0, 0) return row, button, textLabel, setRowRef end local function CreateDropDown(dropDownStringTable, startPosition, settingsHub) -------------------- CONSTANTS ------------------------ local DEFAULT_DROPDOWN_TEXT = "Choose One" local SCROLLING_FRAME_PIXEL_OFFSET = 25 local SELECTION_TEXT_COLOR_NORMAL = Color3.fromRGB(178,178,178) local SELECTION_TEXT_COLOR_NORMAL_VR = Color3.fromRGB(229,229,229) local SELECTION_TEXT_COLOR_HIGHLIGHTED = Color3.fromRGB(255,255,255) -------------------- VARIABLES ------------------------ local lastSelectedCoreObject = nil -------------------- SETUP ------------------------ local this = {} this.CurrentIndex = nil local indexChangedEvent = Instance.new("BindableEvent") indexChangedEvent.Name = "IndexChanged" if type(dropDownStringTable) ~= "table" then error("CreateDropDown dropDownStringTable (first arg) is not a table", 2) return this end local indexChangedEvent = Instance.new("BindableEvent") indexChangedEvent.Name = "IndexChanged" local interactable = true local guid = HttpService:GenerateGUID(false) local dropDownButtonEnabled local lastStringTable = dropDownStringTable this.CurrentIndex = 0 ----------------- GUI SETUP ------------------------ local DropDownFullscreenFrame = Util.Create'ImageButton' { Name = "DropDownFullscreenFrame", BackgroundTransparency = DROPDOWN_BG_TRANSPARENCY, BorderSizePixel = 0, Size = UDim2.new(1, 0, 1, 0), BackgroundColor3 = Color3.fromRGB(0,0,0), ZIndex = 10, Active = true, Visible = false, Selectable = false, AutoButtonColor = false, Parent = CoreGui.RobloxGui }; local function onVREnabled(prop) if prop ~= "VREnabled" then return end if VRService.VREnabled then local Panel3D = require(CoreGui.RobloxGui.Modules.VR.Panel3D) DropDownFullscreenFrame.Parent = Panel3D.Get("SettingsMenu"):GetGUI() DropDownFullscreenFrame.BackgroundTransparency = 1 else DropDownFullscreenFrame.Parent = CoreGui.RobloxGui DropDownFullscreenFrame.BackgroundTransparency = DROPDOWN_BG_TRANSPARENCY end --Force the gui to update, but only if onVREnabled is fired later on if this.UpdateDropDownList then this:UpdateDropDownList(lastStringTable) end end VRService.Changed:Connect(onVREnabled) onVREnabled("VREnabled") local DropDownSelectionFrame = Util.Create'ImageLabel' { Name = "DropDownSelectionFrame", Image = "rbxasset://textures/ui/Settings/MenuBarAssets/MenuButton.png", ScaleType = Enum.ScaleType.Slice, SliceCenter = Rect.new(8,6,46,44), BackgroundTransparency = 1, Size = UDim2.new(0.6, 0, 0.9, 0), Position = UDim2.new(0.5, 0, 0.5, 0), AnchorPoint = Vector2.new(0.5, 0.5), ZIndex = 10, Parent = DropDownFullscreenFrame }; local DropDownScrollingFrame = Util.Create'ScrollingFrame' { Name = "DropDownScrollingFrame", BackgroundTransparency = 1, BorderSizePixel = 0, Size = UDim2.new(1, -20, 1, -SCROLLING_FRAME_PIXEL_OFFSET), Position = UDim2.new(0, 10, 0, 10), ZIndex = 10, Parent = DropDownSelectionFrame }; local guiServiceChangeCon = nil local active = false local hideDropDownSelection = function(name, inputState) if name ~= nil and inputState ~= Enum.UserInputState.Begin then return end this.DropDownFrame.Selectable = interactable if fixSettingsMenuDropdowns then --Make sure to set the hub to Active again so selecting the --dropdown button will highlight it settingsHub:SetActive(true) end if DropDownFullscreenFrame.Visible and usesSelectedObject() then GuiService.SelectedCoreObject = lastSelectedCoreObject end DropDownFullscreenFrame.Visible = false if guiServiceChangeCon then guiServiceChangeCon:Disconnect() end ContextActionService:UnbindCoreAction(guid .. "Action") ContextActionService:UnbindCoreAction(guid .. "FreezeAction") if not fixSettingsMenuDropdowns then --Setting this late will cause the dropdown button to not be highlighted correctly --so we're going to move this upward settingsHub:SetActive(true) end dropDownButtonEnabled.Value = interactable active = false if VRService.VREnabled then local Panel3D = require(CoreGui.RobloxGui.Modules.VR.Panel3D) Panel3D.Get("SettingsMenu"):SetSubpanelDepth(DropDownFullscreenFrame, 0) end end local noOpFunc = function() end local DropDownFrameClicked = function() if not interactable then return end this.DropDownFrame.Selectable = false active = true DropDownFullscreenFrame.Visible = true if VRService.VREnabled then local Panel3D = require(CoreGui.RobloxGui.Modules.VR.Panel3D) Panel3D.Get("SettingsMenu"):SetSubpanelDepth(DropDownFullscreenFrame, 0.5) end if not this.CurrentIndex then this.CurrentIndex = 1 end if this.CurrentIndex <= 0 then this.CurrentIndex = 1 end lastSelectedCoreObject = this.DropDownFrame GuiService.SelectedCoreObject = this.Selections[this.CurrentIndex] guiServiceChangeCon = GuiService:GetPropertyChangedSignal("SelectedCoreObject"):Connect(function() for i = 1, #this.Selections do if GuiService.SelectedCoreObject == this.Selections[i] then this.Selections[i].TextColor3 = SELECTION_TEXT_COLOR_HIGHLIGHTED else this.Selections[i].TextColor3 = VRService.VREnabled and SELECTION_TEXT_COLOR_NORMAL_VR or SELECTION_TEXT_COLOR_NORMAL end end end) ContextActionService:BindCoreAction(guid .. "FreezeAction", noOpFunc, false, Enum.UserInputType.Keyboard, Enum.UserInputType.Gamepad1) ContextActionService:BindCoreAction(guid .. "Action", hideDropDownSelection, false, Enum.KeyCode.ButtonB, Enum.KeyCode.Escape) settingsHub:SetActive(false) dropDownButtonEnabled.Value = false end local dropDownFrameSize = UDim2.new(0.6, 0, 0, 50) this.DropDownFrame = MakeButton("DropDownFrame", DEFAULT_DROPDOWN_TEXT, dropDownFrameSize, DropDownFrameClicked, nil, settingsHub) this.DropDownFrame.Position = UDim2.new(1, 0, 0.5, 0) this.DropDownFrame.AnchorPoint = Vector2.new(1, 0.5) dropDownButtonEnabled = this.DropDownFrame.Enabled local selectedTextLabel = this.DropDownFrame.DropDownFrameTextLabel selectedTextLabel.Position = UDim2.new(0, 15, 0, 0) selectedTextLabel.Size = UDim2.new(1, -50, 1, -8) selectedTextLabel.ClipsDescendants = true selectedTextLabel.TextXAlignment = Enum.TextXAlignment.Left local dropDownImage = Util.Create'ImageLabel' { Name = "DropDownImage", Image = "rbxasset://textures/ui/Settings/DropDown/DropDown.png", BackgroundTransparency = 1, AnchorPoint = Vector2.new(1, 0.5), Size = UDim2.new(0,15,0,10), Position = UDim2.new(1,-12,0.5,0), ZIndex = 2, Parent = this.DropDownFrame }; this.DropDownImage = dropDownImage ---------------------- FUNCTIONS ----------------------------------- local function setSelection(index) local shouldFireChanged = false for i, selectionLabel in pairs(this.Selections) do if i == index then selectedTextLabel.Text = selectionLabel.Text this.CurrentIndex = i shouldFireChanged = true end end if shouldFireChanged then indexChangedEvent:Fire(index) end end local function setSelectionByValue(value) local shouldFireChanged = false for i, selectionLabel in pairs(this.Selections) do if selectionLabel.Text == value then selectedTextLabel.Text = selectionLabel.Text this.CurrentIndex = i shouldFireChanged = true end end if shouldFireChanged then indexChangedEvent:Fire(this.CurrentIndex) end return shouldFireChanged end local enterIsDown = false local function processInput(input) if input.UserInputState == Enum.UserInputState.Begin then if input.KeyCode == Enum.KeyCode.Return then if GuiService.SelectedCoreObject == this.DropDownFrame or this.SelectionInfo and this.SelectionInfo[GuiService.SelectedCoreObject] then enterIsDown = true end end elseif input.UserInputState == Enum.UserInputState.End then if input.KeyCode == Enum.KeyCode.Return and enterIsDown then enterIsDown = false if GuiService.SelectedCoreObject == this.DropDownFrame then DropDownFrameClicked() elseif this.SelectionInfo and this.SelectionInfo[GuiService.SelectedCoreObject] then local info = this.SelectionInfo[GuiService.SelectedCoreObject] info.Clicked() end end end end local function setIsFaded(isFaded) if isFaded then this.DropDownFrame.DropDownFrameTextLabel.TextTransparency = 0.5 this.DropDownFrame.ImageTransparency = 0.5 this.DropDownImage.ImageTransparency = 0.5 else this.DropDownFrame.DropDownFrameTextLabel.TextTransparency = 0 this.DropDownFrame.ImageTransparency = 0 this.DropDownImage.ImageTransparency = 0 end end --------------------- PUBLIC FACING FUNCTIONS ----------------------- this.IndexChanged = indexChangedEvent.Event function this:SetSelectionIndex(newIndex) setSelection(newIndex) end function this:SetSelectionByValue(value) return setSelectionByValue(value) end function this:ResetSelectionIndex() this.CurrentIndex = nil selectedTextLabel.Text = DEFAULT_DROPDOWN_TEXT hideDropDownSelection() end function this:GetSelectedIndex() return this.CurrentIndex end function this:SetZIndex(newZIndex) this.DropDownFrame.ZIndex = newZIndex dropDownImage.ZIndex = newZIndex selectedTextLabel.ZIndex = newZIndex end function this:SetInteractable(value) interactable = value this.DropDownFrame.Selectable = interactable if not interactable then hideDropDownSelection() setIsFaded(VRService.VREnabled) if not VRService.VREnabled then this:SetZIndex(1) end else setIsFaded(false) if not VRService.VREnabled then this:SetZIndex(2) end end dropDownButtonEnabled.Value = value and not active end function this:UpdateDropDownList(dropDownStringTable) lastStringTable = dropDownStringTable if this.Selections then for i = 1, #this.Selections do this.Selections[i]:Destroy() end end this.Selections = {} this.SelectionInfo = {} local vrEnabled = VRService.VREnabled local font = vrEnabled and Enum.Font.SourceSansBold or Enum.Font.SourceSans local textSize = vrEnabled and 36 or 24 local itemHeight = vrEnabled and 70 or 50 local itemSpacing = itemHeight + 1 local dropDownWidth = vrEnabled and 600 or 400 for i,v in pairs(dropDownStringTable) do local SelectionOverrideObject = Util.Create'Frame' { BackgroundTransparency = 0.7, BorderSizePixel = 0, Size = UDim2.new(1, 0, 1, 0) }; local nextSelection = Util.Create'TextButton' { Name = "Selection" .. tostring(i), BackgroundTransparency = 1, BorderSizePixel = 0, AutoButtonColor = false, Size = UDim2.new(1, -28, 0, itemHeight), Position = UDim2.new(0,14,0, (i - 1) * itemSpacing), TextColor3 = VRService.VREnabled and SELECTION_TEXT_COLOR_NORMAL_VR or SELECTION_TEXT_COLOR_NORMAL, Font = font, TextSize = textSize, Text = v, ZIndex = 10, SelectionImageObject = SelectionOverrideObject, Parent = DropDownScrollingFrame }; if i == startPosition then this.CurrentIndex = i selectedTextLabel.Text = v nextSelection.TextColor3 = SELECTION_TEXT_COLOR_HIGHLIGHTED elseif not startPosition and i == 1 then nextSelection.TextColor3 = SELECTION_TEXT_COLOR_HIGHLIGHTED end local clicked = function() selectedTextLabel.Text = nextSelection.Text hideDropDownSelection() this.CurrentIndex = i indexChangedEvent:Fire(i) end nextSelection.MouseButton1Click:Connect(clicked) nextSelection.MouseEnter:Connect(function() if usesSelectedObject() then GuiService.SelectedCoreObject = nextSelection end end) this.Selections[i] = nextSelection this.SelectionInfo[nextSelection] = {Clicked = clicked} end GuiService:RemoveSelectionGroup(guid) GuiService:AddSelectionTuple(guid, unpack(this.Selections)) DropDownScrollingFrame.CanvasSize = UDim2.new(1,-20,0,#dropDownStringTable * itemSpacing) local function updateDropDownSize() if DropDownScrollingFrame.CanvasSize.Y.Offset < (DropDownFullscreenFrame.AbsoluteSize.Y - 10) then DropDownSelectionFrame.Size = UDim2.new(0, dropDownWidth, 0,DropDownScrollingFrame.CanvasSize.Y.Offset + SCROLLING_FRAME_PIXEL_OFFSET) else DropDownSelectionFrame.Size = UDim2.new(0, dropDownWidth, 0.9, 0) end end DropDownFullscreenFrame.Changed:Connect(function(prop) if prop ~= "AbsoluteSize" then return end updateDropDownSize() end) updateDropDownSize() end ----------------------- CONNECTIONS/SETUP -------------------------------- this:UpdateDropDownList(dropDownStringTable) DropDownFullscreenFrame.MouseButton1Click:Connect(hideDropDownSelection) settingsHub.PoppedMenu:Connect(function(poppedMenu) if poppedMenu == DropDownFullscreenFrame then hideDropDownSelection() end end) if not fixSettingsMenuDropdowns then UserInputService.InputBegan:Connect(processInput) UserInputService.InputEnded:Connect(processInput) end return this end local function CreateSelector(selectionStringTable, startPosition) -------------------- VARIABLES ------------------------ local lastInputDirection = 0 local TweenTime = 0.15 -------------------- SETUP ------------------------ local this = {} this.HubRef = nil if type(selectionStringTable) ~= "table" then error("CreateSelector selectionStringTable (first arg) is not a table", 2) return this end local indexChangedEvent = Instance.new("BindableEvent") indexChangedEvent.Name = "IndexChanged" local interactable = true this.CurrentIndex = 0 ----------------- GUI SETUP ------------------------ this.SelectorFrame = Util.Create'ImageButton' { Name = "Selector", Image = "", AutoButtonColor = false, NextSelectionLeft = this.SelectorFrame, NextSelectionRight = this.SelectorFrame, BackgroundTransparency = 1, Size = UDim2.new(0.6,0,0,50), Position = UDim2.new(1, 0, 0.5, 0), AnchorPoint = Vector2.new(1, 0.5), ZIndex = 2, SelectionImageObject = noSelectionObject }; local leftButton = Util.Create'ImageButton' { Name = "LeftButton", BackgroundTransparency = 1, AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.new(0,0,0.5,0), Size = UDim2.new(0,50,0,50), Image = "", ZIndex = 3, Selectable = false, SelectionImageObject = noSelectionObject, Parent = this.SelectorFrame }; local rightButton = Util.Create'ImageButton' { Name = "RightButton", BackgroundTransparency = 1, AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1,0,0.5,0), Size = UDim2.new(0,50,0,50), Image = "", ZIndex = 3, Selectable = false, SelectionImageObject = noSelectionObject, Parent = this.SelectorFrame }; local leftButtonImage = Util.Create'ImageLabel' { Name = "LeftButton", BackgroundTransparency = 1, AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(0.5,0,0.5,0), Size = UDim2.new(0,18,0,30), Image = "rbxasset://textures/ui/Settings/Slider/Left.png", ImageColor3 = ARROW_COLOR, ZIndex = 4, Parent = leftButton }; local rightButtonImage = Util.Create'ImageLabel' { Name = "RightButton", BackgroundTransparency = 1, AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(0.5,0,0.5,0), Size = UDim2.new(0,18,0,30), Image = "rbxasset://textures/ui/Settings/Slider/Right.png", ImageColor3 = ARROW_COLOR, ZIndex = 4, Parent = rightButton }; if not UserInputService.TouchEnabled then local applyNormal, applyHover = function(instance) instance.ImageColor3 = ARROW_COLOR end, function(instance) instance.ImageColor3 = ARROW_COLOR_HOVER end addHoverState(leftButton, leftButtonImage, applyNormal, applyHover) addHoverState(rightButton, rightButtonImage, applyNormal, applyHover) end this.Selections = {} local isSelectionLabelVisible = {} local isAutoSelectButton = {} local autoSelectButton = Util.Create'ImageButton'{ Name = 'AutoSelectButton', BackgroundTransparency = 1, Image = '', Position = UDim2.new(0, leftButton.Size.X.Offset, 0, 0), Size = UDim2.new(1, leftButton.Size.X.Offset * -2, 1, 0), Parent = this.SelectorFrame, ZIndex = 2, SelectionImageObject = noSelectionObject } autoSelectButton.MouseButton1Click:Connect(function() if not interactable then return end if #this.Selections <= 1 then return end local newIndex = this.CurrentIndex + 1 if newIndex > #this.Selections then newIndex = 1 end this:SetSelectionIndex(newIndex) if usesSelectedObject() then GuiService.SelectedCoreObject = this.SelectorFrame end end) isAutoSelectButton[autoSelectButton] = true ---------------------- FUNCTIONS ----------------------------------- local function setSelection(index, direction) for i, selectionLabel in pairs(this.Selections) do local isSelected = (i == index) local leftButtonUDim = UDim2.new(0,leftButton.Size.X.Offset,0,0) local tweenPos = UDim2.new(0,leftButton.Size.X.Offset * direction * 3,0,0) if isSelectionLabelVisible[selectionLabel] then tweenPos = UDim2.new(0,leftButton.Size.X.Offset * -direction * 3,0,0) end if tweenPos.X.Offset < 0 then tweenPos = UDim2.new(0,tweenPos.X.Offset + (selectionLabel.AbsoluteSize.X/4),0,0) end if isSelected then isSelectionLabelVisible[selectionLabel] = true selectionLabel.Position = tweenPos selectionLabel.Visible = true PropertyTweener(selectionLabel, "TextTransparency", 1, 0, TweenTime * 1.1, EaseOutQuad) if selectionLabel:IsDescendantOf(game) then selectionLabel:TweenPosition(leftButtonUDim, Enum.EasingDirection.In, Enum.EasingStyle.Quad, TweenTime, true) else selectionLabel.Position = leftButtonUDim end this.CurrentIndex = i indexChangedEvent:Fire(index) elseif isSelectionLabelVisible[selectionLabel] then isSelectionLabelVisible[selectionLabel] = false PropertyTweener(selectionLabel, "TextTransparency", 0, 1, TweenTime * 1.1, EaseOutQuad) if selectionLabel:IsDescendantOf(game) then selectionLabel:TweenPosition(tweenPos, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, TweenTime * 0.9, true) else selectionLabel.Position = UDim2.new(tweenPos) end end end end local function stepFunc(inputObject, step) if not interactable then return end if inputObject ~= nil and inputObject.UserInputType ~= Enum.UserInputType.MouseButton1 and inputObject.UserInputType ~= Enum.UserInputType.Gamepad1 and inputObject.UserInputType ~= Enum.UserInputType.Gamepad2 and inputObject.UserInputType ~= Enum.UserInputType.Gamepad3 and inputObject.UserInputType ~= Enum.UserInputType.Gamepad4 and inputObject.UserInputType ~= Enum.UserInputType.Keyboard then return end if usesSelectedObject() then GuiService.SelectedCoreObject = this.SelectorFrame end local newIndex = step + this.CurrentIndex local direction = 0 if newIndex > this.CurrentIndex then direction = 1 else direction = -1 end if newIndex > #this.Selections then newIndex = 1 elseif newIndex < 1 then newIndex = #this.Selections end setSelection(newIndex, direction) end local guiServiceCon = nil local function connectToGuiService() guiServiceCon = GuiService:GetPropertyChangedSignal("SelectedCoreObject"):Connect(function() if #this.Selections <= 0 then return end if GuiService.SelectedCoreObject == this.SelectorFrame then this.Selections[this.CurrentIndex].TextTransparency = 0 else if GuiService.SelectedCoreObject ~= nil and isAutoSelectButton[GuiService.SelectedCoreObject] then if VRService.VREnabled then this.Selections[this.CurrentIndex].TextTransparency = 0 else GuiService.SelectedCoreObject = this.SelectorFrame end else this.Selections[this.CurrentIndex].TextTransparency = 0.5 end end end) end --------------------- PUBLIC FACING FUNCTIONS ----------------------- this.IndexChanged = indexChangedEvent.Event function this:SetSelectionIndex(newIndex) setSelection(newIndex, 1) end function this:GetSelectedIndex() return this.CurrentIndex end function this:SetZIndex(newZIndex) leftButton.ZIndex = newZIndex rightButton.ZIndex = newZIndex leftButtonImage.ZIndex = newZIndex rightButtonImage.ZIndex = newZIndex for i = 1, #this.Selections do this.Selections[i].ZIndex = newZIndex end end function this:SetInteractable(value) interactable = value this.SelectorFrame.Selectable = interactable if FFlagFixInactiveSelectorArrows then leftButton.Active = interactable rightButton.Active = interactable end if not interactable then for i, selectionLabel in pairs(this.Selections) do selectionLabel.TextColor3 = Color3.fromRGB(49, 49, 49) end if FFlagFixInactiveSelectorArrows then leftButtonImage.ImageColor3 = ARROW_COLOR_INACTIVE rightButtonImage.ImageColor3 = ARROW_COLOR_INACTIVE end else for i, selectionLabel in pairs(this.Selections) do selectionLabel.TextColor3 = Color3.fromRGB(255, 255, 255) end if FFlagFixInactiveSelectorArrows then leftButtonImage.ImageColor3 = ARROW_COLOR rightButtonImage.ImageColor3 = ARROW_COLOR end end end function this:UpdateOptions(selectionStringTable) for i,v in pairs(this.Selections) do v:Destroy() end isSelectionLabelVisible = {} this.Selections = {} for i,v in pairs(selectionStringTable) do local nextSelection = Util.Create'TextLabel' { Name = "Selection" .. tostring(i), BackgroundTransparency = 1, BorderSizePixel = 0, Size = UDim2.new(1,leftButton.Size.X.Offset * -2, 1, 0), Position = UDim2.new(1,0,0,0), TextColor3 = Color3.fromRGB(255, 255, 255), TextYAlignment = Enum.TextYAlignment.Center, TextTransparency = 0.5, Font = Enum.Font.SourceSans, TextSize = 24, Text = v, ZIndex = 2, Visible = false, Parent = this.SelectorFrame }; if isTenFootInterface() then nextSelection.TextSize = 36 end if i == startPosition then this.CurrentIndex = i nextSelection.Position = UDim2.new(0,leftButton.Size.X.Offset,0,0) nextSelection.Visible = true isSelectionLabelVisible[nextSelection] = true else isSelectionLabelVisible[nextSelection] = false end this.Selections[i] = nextSelection end local hasMoreThanOneSelection = #this.Selections > 1 leftButton.Visible = hasMoreThanOneSelection rightButton.Visible = hasMoreThanOneSelection end --------------------- SETUP ----------------------- local function onVREnabled(prop) if prop ~= "VREnabled" then return end local vrEnabled = VRService.VREnabled leftButton.Selectable = vrEnabled rightButton.Selectable = vrEnabled autoSelectButton.Selectable = vrEnabled end VRService.Changed:Connect(onVREnabled) onVREnabled("VREnabled") leftButton.InputBegan:Connect(function(inputObject) if inputObject.UserInputType == Enum.UserInputType.Touch then stepFunc(nil, -1) end end) leftButton.MouseButton1Click:Connect(function() if not UserInputService.TouchEnabled then stepFunc(nil, -1) end end) rightButton.InputBegan:Connect(function(inputObject) if inputObject.UserInputType == Enum.UserInputType.Touch then stepFunc(nil, 1) end end) rightButton.MouseButton1Click:Connect(function() if not UserInputService.TouchEnabled then stepFunc(nil, 1) end end) local isInTree = true this:UpdateOptions(selectionStringTable) UserInputService.InputBegan:Connect(function(inputObject) if not interactable then return end if not isInTree then return end if inputObject.UserInputType ~= Enum.UserInputType.Gamepad1 and inputObject.UserInputType ~= Enum.UserInputType.Keyboard then return end if GuiService.SelectedCoreObject ~= this.SelectorFrame then return end if inputObject.KeyCode == Enum.KeyCode.DPadLeft or inputObject.KeyCode == Enum.KeyCode.Left or inputObject.KeyCode == Enum.KeyCode.A then stepFunc(inputObject, -1) elseif inputObject.KeyCode == Enum.KeyCode.DPadRight or inputObject.KeyCode == Enum.KeyCode.Right or inputObject.KeyCode == Enum.KeyCode.D then stepFunc(inputObject, 1) end end) UserInputService.InputChanged:Connect(function(inputObject) if not interactable then return end if not isInTree then lastInputDirection = 0 return end if inputObject.UserInputType ~= Enum.UserInputType.Gamepad1 then return end local selected = GuiService.SelectedCoreObject if not selected or not selected:IsDescendantOf(this.SelectorFrame.Parent) then return end if inputObject.KeyCode ~= Enum.KeyCode.Thumbstick1 then return end if inputObject.Position.X > CONTROLLER_THUMBSTICK_DEADZONE and inputObject.Delta.X > 0 and lastInputDirection ~= 1 then lastInputDirection = 1 stepFunc(inputObject, lastInputDirection) elseif inputObject.Position.X < -CONTROLLER_THUMBSTICK_DEADZONE and inputObject.Delta.X < 0 and lastInputDirection ~= -1 then lastInputDirection = -1 stepFunc(inputObject, lastInputDirection) elseif math.abs(inputObject.Position.X) < CONTROLLER_THUMBSTICK_DEADZONE then lastInputDirection = 0 end end) this.SelectorFrame.AncestryChanged:Connect(function(child, parent) isInTree = parent if not isInTree then if guiServiceCon then guiServiceCon:Disconnect() end else connectToGuiService() end end) local function onResized(viewportSize, portrait) local textSize = 0 if portrait then textSize = 16 else textSize = isTenFootInterface() and 36 or 24 end for i, selection in pairs(this.Selections) do selection.TextSize = textSize end end addOnResizedCallback(this.SelectorFrame, onResized) connectToGuiService() return this end local function ShowAlert(alertMessage, okButtonText, settingsHub, okPressedFunc, hasBackground) local parent = CoreGui.RobloxGui if parent:FindFirstChild("AlertViewFullScreen") then return end --Declare AlertViewBacking so onVREnabled can take it as an upvalue local AlertViewBacking = nil --Handle VR toggle while alert is open --Future consideration: maybe rebuild gui when VR toggles mid-game; right now only subpaneling is handled rather than visual style local function onVREnabled(prop) if prop ~= "VREnabled" then return end local Panel3D, settingsPanel = nil, nil if VRService.VREnabled then Panel3D = require(CoreGui.RobloxGui.Modules.VR.Panel3D) settingsPanel = Panel3D.Get("SettingsMenu") parent = settingsPanel:GetGUI() else parent = CoreGui.RobloxGui end if AlertViewBacking and AlertViewBacking.Parent ~= nil then AlertViewBacking.Parent = parent if VRService.VREnabled then settingsPanel:SetSubpanelDepth(AlertViewBacking, 0.5) end end end local vrEnabledConn = VRService.Changed:Connect(onVREnabled) local NON_SELECTED_TEXT_COLOR = Color3.fromRGB(59, 166, 241) local SELECTED_TEXT_COLOR = Color3.fromRGB(255, 255, 255) AlertViewBacking = Util.Create'ImageLabel' { Name = "AlertViewBacking", Image = "rbxasset://textures/ui/Settings/MenuBarAssets/MenuButton.png", ScaleType = Enum.ScaleType.Slice, SliceCenter = Rect.new(8,6,46,44), BackgroundTransparency = 1, ImageTransparency = 1, Size = UDim2.new(0, 400, 0, 350), Position = UDim2.new(0.5, -200, 0.5, -175), ZIndex = 9, Parent = parent }; onVREnabled("VREnabled") if hasBackground or VRService.VREnabled then AlertViewBacking.ImageTransparency = 0 else AlertViewBacking.Size = UDim2.new(0.8, 0, 0, 350) AlertViewBacking.Position = UDim2.new(0.1, 0, 0.1, 0) end if CoreGui.RobloxGui.AbsoluteSize.Y <= AlertViewBacking.Size.Y.Offset then AlertViewBacking.Size = UDim2.new(AlertViewBacking.Size.X.Scale, AlertViewBacking.Size.X.Offset, AlertViewBacking.Size.Y.Scale, CoreGui.RobloxGui.AbsoluteSize.Y) AlertViewBacking.Position = UDim2.new(AlertViewBacking.Position.X.Scale, -AlertViewBacking.Size.X.Offset/2, 0.5, -AlertViewBacking.Size.Y.Offset/2) end local AlertViewText = Util.Create'TextLabel' { Name = "AlertViewText", BackgroundTransparency = 1, Size = UDim2.new(0.95, 0, 0.6, 0), Position = UDim2.new(0.025, 0, 0.05, 0), Font = Enum.Font.SourceSansBold, TextSize = 36, Text = alertMessage, TextWrapped = true, TextColor3 = Color3.fromRGB(255, 255, 255), TextXAlignment = Enum.TextXAlignment.Center, TextYAlignment = Enum.TextYAlignment.Center, ZIndex = 10, Parent = AlertViewBacking }; local SelectionOverrideObject = Util.Create'ImageLabel' { Image = "", BackgroundTransparency = 1 }; local removeId = HttpService:GenerateGUID(false) local destroyAlert = function(actionName, inputState) if VRService.VREnabled and (inputState == Enum.UserInputState.Begin or inputState == Enum.UserInputState.Cancel) then return end if not AlertViewBacking then return end if VRService.VREnabled then local Panel3D = require(CoreGui.RobloxGui.Modules.VR.Panel3D) Panel3D.Get("SettingsMenu"):SetSubpanelDepth(AlertViewBacking, 0) end AlertViewBacking:Destroy() AlertViewBacking = nil if okPressedFunc then okPressedFunc() end ContextActionService:UnbindCoreAction(removeId) GuiService.SelectedCoreObject = nil if settingsHub then settingsHub:ShowBar() end if vrEnabledConn then vrEnabledConn:Disconnect() end end local AlertViewButtonSize = UDim2.new(1, -20, 0, 60) local AlertViewButtonPosition = UDim2.new(0, 10, 0.65, 0) if not hasBackground then AlertViewButtonSize = UDim2.new(0, 200, 0, 50) AlertViewButtonPosition = UDim2.new(0.5, -100, 0.65, 0) end local AlertViewButton, AlertViewText = MakeButton("AlertViewButton", okButtonText, AlertViewButtonSize, destroyAlert) AlertViewButton.Position = AlertViewButtonPosition AlertViewButton.NextSelectionLeft = AlertViewButton AlertViewButton.NextSelectionRight = AlertViewButton AlertViewButton.NextSelectionUp = AlertViewButton AlertViewButton.NextSelectionDown = AlertViewButton AlertViewButton.ZIndex = 9 AlertViewText.ZIndex = AlertViewButton.ZIndex AlertViewButton.Parent = AlertViewBacking if usesSelectedObject() then GuiService.SelectedCoreObject = AlertViewButton end GuiService.SelectedCoreObject = AlertViewButton ContextActionService:BindCoreAction(removeId, destroyAlert, false, Enum.KeyCode.Escape, Enum.KeyCode.ButtonB, Enum.KeyCode.ButtonA) if settingsHub and not VRService.VREnabled then settingsHub:HideBar() settingsHub.Pages.CurrentPage:Hide(1, 1) end end local function CreateNewSlider(numOfSteps, startStep, minStep) -------------------- SETUP ------------------------ local this = {} local spacing = 4 local initialSpacing = 8 local steps = tonumber(numOfSteps) local currentStep = startStep local lastInputDirection = 0 local timeAtLastInput = nil local interactable = true local renderStepBindName = HttpService:GenerateGUID(false) -- this is done to prevent using these values below (trying to keep the variables consistent) numOfSteps = "" startStep = "" if steps <= 0 then error("CreateNewSlider failed because numOfSteps (first arg) is 0 or negative, please supply a positive integer", 2) return end local valueChangedEvent = Instance.new("BindableEvent") valueChangedEvent.Name = "ValueChanged" ----------------- GUI SETUP ------------------------ this.SliderFrame = Util.Create'ImageButton' { Name = "Slider", Image = "", AutoButtonColor = false, NextSelectionLeft = this.SliderFrame, NextSelectionRight = this.SliderFrame, BackgroundTransparency = 1, Size = UDim2.new(0.6, 0, 0, 50), Position = UDim2.new(1, 0, 0.5, 0), AnchorPoint = Vector2.new(1, 0.5), SelectionImageObject = noSelectionObject, ZIndex = 2 }; this.StepsContainer = Util.Create "Frame" { Name = "StepsContainer", Position = UDim2.new(0.5, 0, 0.5, 0), Size = UDim2.new(1, -100, 1, 0), AnchorPoint = Vector2.new(0.5, 0.5), BackgroundTransparency = 1, Parent = this.SliderFrame, } local leftButton = Util.Create'ImageButton' { Name = "LeftButton", BackgroundTransparency = 1, AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.new(0,0,0.5,0), Size = UDim2.new(0,50,0,50), Image = "", ZIndex = 3, Selectable = false, SelectionImageObject = noSelectionObject, Active = true, Parent = this.SliderFrame }; local rightButton = Util.Create'ImageButton' { Name = "RightButton", BackgroundTransparency = 1, AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1,0,0.5,0), Size = UDim2.new(0,50,0,50), Image = "", ZIndex = 3, Selectable = false, SelectionImageObject = noSelectionObject, Active = true, Parent = this.SliderFrame }; local leftButtonImage = Util.Create'ImageLabel' { Name = "LeftButton", BackgroundTransparency = 1, AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(0.5,0,0.5,0), Size = UDim2.new(0,30,0,30), Image = "rbxasset://textures/ui/Settings/Slider/Less.png", ZIndex = 4, Parent = leftButton, ImageColor3 = UserInputService.TouchEnabled and ARROW_COLOR_TOUCH or ARROW_COLOR }; local rightButtonImage = Util.Create'ImageLabel' { Name = "RightButton", BackgroundTransparency = 1, AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(0.5,0,0.5,0), Size = UDim2.new(0,30,0,30), Image = "rbxasset://textures/ui/Settings/Slider/More.png", ZIndex = 4, Parent = rightButton, ImageColor3 = UserInputService.TouchEnabled and ARROW_COLOR_TOUCH or ARROW_COLOR }; if not UserInputService.TouchEnabled then local onNormalButtonState, onHoverButtonState = function(instance) instance.ImageColor3 = ARROW_COLOR end, function(instance) instance.ImageColor3 = ARROW_COLOR_HOVER end addHoverState(leftButton, leftButtonImage, onNormalButtonState, onHoverButtonState) addHoverState(rightButton, rightButtonImage, onNormalButtonState, onHoverButtonState) end this.Steps = {} local stepXSize = 35 if isSmallTouchScreen() then stepXSize = 25 end local stepXScale = 1 / steps stepXSize = 0 for i = 1, steps do local nextStep = Util.Create'ImageButton' { Name = "Step" .. tostring(i), BackgroundColor3 = SELECTED_COLOR, BackgroundTransparency = 0.36, BorderSizePixel = 0, AutoButtonColor = false, Active = false, AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.new((i - 1) * stepXScale, spacing / 2, 0.5, 0), Size = UDim2.new(stepXScale,-spacing, 24 / 50, 0), Image = "", ZIndex = 3, Selectable = false, ImageTransparency = 0.36, Parent = this.StepsContainer, SelectionImageObject = noSelectionObject }; if i > currentStep then nextStep.BackgroundColor3 = NON_SELECTED_COLOR end if i == 1 or i == steps then nextStep.BackgroundTransparency = 1 nextStep.ScaleType = Enum.ScaleType.Slice nextStep.SliceCenter = Rect.new(3,3,32,21) if i <= currentStep then if i == 1 then nextStep.Image = SELECTED_LEFT_IMAGE else nextStep.Image = SELECTED_RIGHT_IMAGE end else if i == 1 then nextStep.Image = NON_SELECTED_LEFT_IMAGE else nextStep.Image = NON_SELECTED_RIGHT_IMAGE end end end this.Steps[#this.Steps + 1] = nextStep end ------------------- FUNCTIONS --------------------- local function hideSelection() for i = 1, steps do this.Steps[i].BackgroundColor3 = NON_SELECTED_COLOR if i == 1 then this.Steps[i].Image = NON_SELECTED_LEFT_IMAGE elseif i == steps then this.Steps[i].Image = NON_SELECTED_RIGHT_IMAGE end end end local function showSelection() for i = 1, steps do if i > currentStep then break end this.Steps[i].BackgroundColor3 = SELECTED_COLOR if i == 1 then this.Steps[i].Image = SELECTED_LEFT_IMAGE elseif i == steps then this.Steps[i].Image = SELECTED_RIGHT_IMAGE end end end local function modifySelection(alpha) for i = 1, steps do if i == 1 or i == steps then this.Steps[i].ImageTransparency = alpha else this.Steps[i].BackgroundTransparency = alpha end end end local function setCurrentStep(newStepPosition) if not minStep then minStep = 0 end leftButton.Visible = true rightButton.Visible = true if newStepPosition <= minStep then newStepPosition = minStep leftButton.Visible = false end if newStepPosition >= steps then newStepPosition = steps rightButton.Visible = false end if currentStep == newStepPosition then return end currentStep = newStepPosition hideSelection() showSelection() timeAtLastInput = tick() valueChangedEvent:Fire(currentStep) end local function isActivateEvent(inputObject) if not inputObject then return false end return inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch or (inputObject.UserInputType == Enum.UserInputType.Gamepad1 and inputObject.KeyCode == Enum.KeyCode.ButtonA) end local function mouseDownFunc(inputObject, newStepPos, repeatAction) if not interactable then return end if inputObject == nil then return end if not isActivateEvent(inputObject) then return end if usesSelectedObject() and not VRService.VREnabled then GuiService.SelectedCoreObject = this.SliderFrame end if not VRService.VREnabled then if repeatAction then lastInputDirection = newStepPos - currentStep else lastInputDirection = 0 local mouseInputMovedCon = nil local mouseInputEndedCon = nil mouseInputMovedCon = UserInputService.InputChanged:Connect(function(inputObject) if inputObject.UserInputType ~= Enum.UserInputType.MouseMovement then return end local mousePos = inputObject.Position.X for i = 1, steps do local stepPosition = this.Steps[i].AbsolutePosition.X local stepSize = this.Steps[i].AbsoluteSize.X if mousePos >= stepPosition and mousePos <= stepPosition + stepSize then setCurrentStep(i) break elseif i == 1 and mousePos < stepPosition then setCurrentStep(0) break elseif i == steps and mousePos >= stepPosition then setCurrentStep(i) break end end end) mouseInputEndedCon = UserInputService.InputEnded:Connect(function(inputObject) if not isActivateEvent(inputObject) then return end lastInputDirection = 0 mouseInputEndedCon:Disconnect() mouseInputMovedCon:Disconnect() end) end else lastInputDirection = 0 end setCurrentStep(newStepPos) end local function mouseUpFunc(inputObject) if not interactable then return end if not isActivateEvent(inputObject) then return end lastInputDirection = 0 end local function touchClickFunc(inputObject, newStepPos, repeatAction) mouseDownFunc(inputObject, newStepPos, repeatAction) end --------------------- PUBLIC FACING FUNCTIONS ----------------------- this.ValueChanged = valueChangedEvent.Event function this:SetValue(newValue) setCurrentStep(newValue) end function this:GetValue() return currentStep end function this:SetInteractable(value) lastInputDirection = 0 interactable = value this.SliderFrame.Selectable = value if not interactable then hideSelection() else showSelection() end end function this:SetZIndex(newZIndex) leftButton.ZIndex = newZIndex rightButton.ZIndex = newZIndex leftButtonImage.ZIndex = newZIndex rightButtonImage.ZIndex = newZIndex for i = 1, #this.Steps do this.Steps[i].ZIndex = newZIndex end end function this:SetMinStep(newMinStep) if newMinStep >= 0 and newMinStep <= steps then minStep = newMinStep end if currentStep <= minStep then currentStep = minStep leftButton.Visible = false end if currentStep >= steps then currentStep = steps rightButton.Visible = false end end --------------------- SETUP ----------------------- leftButton.InputBegan:Connect(function(inputObject) mouseDownFunc(inputObject, currentStep - 1, true) end) leftButton.InputEnded:Connect(function(inputObject) mouseUpFunc(inputObject) end) rightButton.InputBegan:Connect(function(inputObject) mouseDownFunc(inputObject, currentStep + 1, true) end) rightButton.InputEnded:Connect(function(inputObject) mouseUpFunc(inputObject) end) local function onVREnabled(prop) if prop ~= "VREnabled" then return end if VRService.VREnabled then leftButton.Selectable = interactable rightButton.Selectable = interactable this.SliderFrame.Selectable = interactable for i = 1, steps do this.Steps[i].Selectable = interactable this.Steps[i].Active = interactable end else leftButton.Selectable = false rightButton.Selectable = false this.SliderFrame.Selectable = interactable for i = 1, steps do this.Steps[i].Selectable = false this.Steps[i].Active = false end end end VRService.Changed:Connect(onVREnabled) onVREnabled("VREnabled") for i = 1, steps do this.Steps[i].InputBegan:Connect(function(inputObject) mouseDownFunc(inputObject, i) end) this.Steps[i].InputEnded:Connect(function(inputObject) mouseUpFunc(inputObject) end) end this.SliderFrame.InputBegan:Connect(function(inputObject) if VRService.VREnabled then local selected = GuiService.SelectedCoreObject if not selected or not selected:IsDescendantOf(this.SliderFrame.Parent) then return end end mouseDownFunc(inputObject, currentStep) end) this.SliderFrame.InputEnded:Connect(function(inputObject) if VRService.VREnabled then local selected = GuiService.SelectedCoreObject if not selected or not selected:IsDescendantOf(this.SliderFrame.Parent) then return end end mouseUpFunc(inputObject) end) local stepSliderFunc = function() if timeAtLastInput == nil then return end local currentTime = tick() local timeSinceLastInput = currentTime - timeAtLastInput if timeSinceLastInput >= CONTROLLER_SCROLL_DELTA then setCurrentStep(currentStep + lastInputDirection) end end local isInTree = true local navigateLeft = -1 --these are just for differentiation, the actual value isn't important as long as they coerce to boolean true (all numbers do in Lua) local navigateRight = 1 local navigationKeyCodes = { [Enum.KeyCode.Thumbstick1] = true, --thumbstick can be either direction [Enum.KeyCode.DPadLeft] = navigateLeft, [Enum.KeyCode.DPadRight] = navigateRight, [Enum.KeyCode.Left] = navigateLeft, [Enum.KeyCode.Right] = navigateRight, [Enum.KeyCode.A] = navigateLeft, [Enum.KeyCode.D] = navigateRight, [Enum.KeyCode.ButtonA] = true --buttonA can be either direction } UserInputService.InputBegan:Connect(function(inputObject) if not interactable then return end if not isInTree then return end if inputObject.UserInputType ~= Enum.UserInputType.Gamepad1 and inputObject.UserInputType ~= Enum.UserInputType.Keyboard then return end local selected = GuiService.SelectedCoreObject if not selected or not selected:IsDescendantOf(this.SliderFrame.Parent) then return end if navigationKeyCodes[inputObject.KeyCode] == navigateLeft then lastInputDirection = -1 setCurrentStep(currentStep - 1) elseif navigationKeyCodes[inputObject.KeyCode] == navigateRight then lastInputDirection = 1 setCurrentStep(currentStep + 1) end end) UserInputService.InputEnded:Connect(function(inputObject) if not interactable then return end if inputObject.UserInputType ~= Enum.UserInputType.Gamepad1 and inputObject.UserInputType ~= Enum.UserInputType.Keyboard then return end local selected = GuiService.SelectedCoreObject if not selected or not selected:IsDescendantOf(this.SliderFrame.Parent) then return end if navigationKeyCodes[inputObject.KeyCode] then --detect any keycode considered a navigation key lastInputDirection = 0 end end) UserInputService.InputChanged:Connect(function(inputObject) if not interactable then lastInputDirection = 0 return end if not isInTree then lastInputDirection = 0 return end if inputObject.UserInputType ~= Enum.UserInputType.Gamepad1 then return end local selected = GuiService.SelectedCoreObject if not selected or not selected:IsDescendantOf(this.SliderFrame.Parent) then return end if inputObject.KeyCode ~= Enum.KeyCode.Thumbstick1 then return end if inputObject.Position.X > CONTROLLER_THUMBSTICK_DEADZONE and inputObject.Delta.X > 0 and lastInputDirection ~= 1 then lastInputDirection = 1 setCurrentStep(currentStep + 1) elseif inputObject.Position.X < -CONTROLLER_THUMBSTICK_DEADZONE and inputObject.Delta.X < 0 and lastInputDirection ~= -1 then lastInputDirection = -1 setCurrentStep(currentStep - 1) elseif math.abs(inputObject.Position.X) < CONTROLLER_THUMBSTICK_DEADZONE then lastInputDirection = 0 end end) local isBound = false GuiService.Changed:Connect(function(prop) if prop ~= "SelectedCoreObject" then return end local selected = GuiService.SelectedCoreObject local isThisSelected = selected and selected:IsDescendantOf(this.SliderFrame.Parent) if isThisSelected then modifySelection(0) if not isBound then isBound = true timeAtLastInput = tick() RunService:BindToRenderStep(renderStepBindName, Enum.RenderPriority.Input.Value + 1, stepSliderFunc) end else modifySelection(0.36) if isBound then isBound = false RunService:UnbindFromRenderStep(renderStepBindName) end end end) this.SliderFrame.AncestryChanged:Connect(function(child, parent) isInTree = parent end) setCurrentStep(currentStep) return this end local ROW_HEIGHT = 50 if isTenFootInterface() then ROW_HEIGHT = 90 end local nextPosTable = {} local function AddNewRow(pageToAddTo, rowDisplayName, selectionType, rowValues, rowDefault, extraSpacing) local nextRowPositionY = 0 local isARealRow = selectionType ~= 'TextBox' -- Textboxes are constructed in this function - they don't have an associated class. if nextPosTable[pageToAddTo] then nextRowPositionY = nextPosTable[pageToAddTo] end local RowFrame = nil RowFrame = Util.Create'ImageButton' { Name = rowDisplayName .. "Frame", BackgroundTransparency = 1, BorderSizePixel = 0, Image = "rbxasset://textures/ui/VR/rectBackgroundWhite.png", ScaleType = Enum.ScaleType.Slice, SliceCenter = Rect.new(2, 2, 18, 18), ImageTransparency = 1, Active = false, AutoButtonColor = false, Size = UDim2.new(1,0,0,ROW_HEIGHT), Position = UDim2.new(0,0,0,nextRowPositionY), ZIndex = 2, Selectable = false, SelectionImageObject = noSelectionObject, Parent = pageToAddTo.Page }; RowFrame.ImageColor3 = RowFrame.BackgroundColor3 if RowFrame and extraSpacing then RowFrame.Position = UDim2.new(RowFrame.Position.X.Scale,RowFrame.Position.X.Offset, RowFrame.Position.Y.Scale,RowFrame.Position.Y.Offset + extraSpacing) end local RowLabel = nil RowLabel = Util.Create'TextLabel' { Name = rowDisplayName .. "Label", Text = rowDisplayName, Font = Enum.Font.SourceSansBold, TextSize = 16, TextColor3 = Color3.fromRGB(255,255,255), TextXAlignment = Enum.TextXAlignment.Left, BackgroundTransparency = 1, Size = UDim2.new(0,200,1,0), Position = UDim2.new(0,10,0,0), ZIndex = 2, Parent = RowFrame }; local RowLabelTextSizeConstraint = Instance.new("UITextSizeConstraint") if FFlagUseNotificationsLocalization then RowLabel.Size = UDim2.new(0.35,0,1,0) RowLabel.TextScaled = true RowLabel.TextWrapped = true RowLabelTextSizeConstraint.Parent = RowLabel RowLabelTextSizeConstraint.MaxTextSize = 16 end if not isARealRow then RowLabel.Text = '' end local function onResized(viewportSize, portrait) if portrait then RowLabel.TextSize = 16 else RowLabel.TextSize = isTenFootInterface() and 36 or 24 end RowLabelTextSizeConstraint.MaxTextSize = RowLabel.TextSize end onResized(getViewportSize(), isPortrait()) addOnResizedCallback(RowFrame, onResized) local ValueChangerSelection = nil local ValueChangerInstance = nil if selectionType == "Slider" then ValueChangerInstance = CreateNewSlider(rowValues, rowDefault) ValueChangerInstance.SliderFrame.Parent = RowFrame ValueChangerSelection = ValueChangerInstance.SliderFrame elseif selectionType == "Selector" then ValueChangerInstance = CreateSelector(rowValues, rowDefault) ValueChangerInstance.SelectorFrame.Parent = RowFrame ValueChangerSelection = ValueChangerInstance.SelectorFrame elseif selectionType == "DropDown" then ValueChangerInstance = CreateDropDown(rowValues, rowDefault, pageToAddTo.HubRef) ValueChangerInstance.DropDownFrame.Parent = RowFrame ValueChangerSelection = ValueChangerInstance.DropDownFrame elseif selectionType == "TextBox" then local isMouseOverRow = false local forceReturnSelectionOnFocusLost = false local SelectionOverrideObject = Util.Create'ImageLabel' { Image = "", BackgroundTransparency = 1, }; ValueChangerInstance = {} ValueChangerInstance.HubRef = nil local box = Util.Create'TextBox' { AnchorPoint = Vector2.new(1, 0.5), Size = UDim2.new(0.6,0,1,0), Position = UDim2.new(1,0,0.5,0), Text = rowDisplayName, TextColor3 = Color3.fromRGB(49, 49, 49), BackgroundTransparency = 0.5, BorderSizePixel = 0, TextYAlignment = Enum.TextYAlignment.Top, TextXAlignment = Enum.TextXAlignment.Left, TextWrapped = true, Font = Enum.Font.SourceSans, TextSize = 24, ZIndex = 2, SelectionImageObject = SelectionOverrideObject, ClearTextOnFocus = false, Parent = RowFrame }; ValueChangerSelection = box box.Focused:Connect(function() if usesSelectedObject() then GuiService.SelectedCoreObject = box end if box.Text == rowDisplayName then box.Text = "" end end) box.FocusLost:Connect(function(enterPressed, inputObject) forceReturnSelectionOnFocusLost = false end) if extraSpacing then box.Position = UDim2.new(box.Position.X.Scale,box.Position.X.Offset, box.Position.Y.Scale,box.Position.Y.Offset + extraSpacing) end ValueChangerSelection.SelectionGained:Connect(function() if usesSelectedObject() then box.BackgroundTransparency = 0.1 if ValueChangerInstance.HubRef then ValueChangerInstance.HubRef:ScrollToFrame(ValueChangerSelection) end end end) ValueChangerSelection.SelectionLost:Connect(function() if usesSelectedObject() then box.BackgroundTransparency = 0.5 end end) local setRowSelection = function() local fullscreenDropDown = CoreGui.RobloxGui:FindFirstChild("DropDownFullscreenFrame") if fullscreenDropDown and fullscreenDropDown.Visible then return end local valueFrame = ValueChangerSelection if valueFrame and valueFrame.Visible and valueFrame.ZIndex > 1 and usesSelectedObject() and pageToAddTo.Active then GuiService.SelectedCoreObject = valueFrame isMouseOverRow = true end end local function processInput(input) if input.UserInputState == Enum.UserInputState.Begin then if input.KeyCode == Enum.KeyCode.Return then if GuiService.SelectedCoreObject == ValueChangerSelection then forceReturnSelectionOnFocusLost = true box:CaptureFocus() end end end end box.MouseEnter:Connect(setRowSelection) UserInputService.InputBegan:Connect(processInput) elseif selectionType == "TextEntry" then local isMouseOverRow = false local forceReturnSelectionOnFocusLost = false local SelectionOverrideObject = Util.Create'ImageLabel' { Image = "", BackgroundTransparency = 1, }; ValueChangerInstance = {} ValueChangerInstance.HubRef = nil local box = Util.Create'TextBox' { AnchorPoint = Vector2.new(1, 0.5), Size = UDim2.new(0.4,-10,0,40), Position = UDim2.new(1,0,0.5,0), Text = rowDisplayName, TextColor3 = Color3.fromRGB(178, 178, 178), BackgroundTransparency = 1.0, BorderSizePixel = 0, TextYAlignment = Enum.TextYAlignment.Center, TextXAlignment = Enum.TextXAlignment.Center, TextWrapped = false, Font = Enum.Font.SourceSans, TextSize = 24, ZIndex = 2, SelectionImageObject = SelectionOverrideObject, ClearTextOnFocus = false, Parent = RowFrame }; ValueChangerSelection = box box.Focused:Connect(function() if usesSelectedObject() then GuiService.SelectedCoreObject = box end if box.Text == rowDisplayName then box.Text = "" end end) box.FocusLost:Connect(function(enterPressed, inputObject) forceReturnSelectionOnFocusLost = false end) if extraSpacing then box.Position = UDim2.new(box.Position.X.Scale,box.Position.X.Offset, box.Position.Y.Scale,box.Position.Y.Offset + extraSpacing) end ValueChangerSelection.SelectionGained:Connect(function() if usesSelectedObject() then box.BackgroundTransparency = 0.8 if ValueChangerInstance.HubRef then ValueChangerInstance.HubRef:ScrollToFrame(ValueChangerSelection) end end end) ValueChangerSelection.SelectionLost:Connect(function() if usesSelectedObject() then box.BackgroundTransparency = 1.0 end end) local setRowSelection = function() local fullscreenDropDown = CoreGui.RobloxGui:FindFirstChild("DropDownFullscreenFrame") if fullscreenDropDown and fullscreenDropDown.Visible then return end local valueFrame = ValueChangerSelection if valueFrame and valueFrame.Visible and valueFrame.ZIndex > 1 and usesSelectedObject() and pageToAddTo.Active then GuiService.SelectedCoreObject = valueFrame isMouseOverRow = true end end local function processInput(input) if input.UserInputState == Enum.UserInputState.Begin then if input.KeyCode == Enum.KeyCode.Return then if GuiService.SelectedCoreObject == ValueChangerSelection then forceReturnSelectionOnFocusLost = true box:CaptureFocus() end end end end RowFrame.MouseEnter:Connect(setRowSelection) function ValueChangerInstance:SetZIndex(newZIndex) box.ZIndex = newZIndex end function ValueChangerInstance:SetInteractable(interactable) box.Selectable = interactable if not interactable then box.TextColor3 = Color3.fromRGB(49,49,49) box.ZIndex = 1 else box.TextColor3 = Color3.fromRGB(178,178,178) box.ZIndex = 2 end end function ValueChangerInstance:SetValue(value) -- should this do more? box.Text = value end local valueChangedEvent = Instance.new("BindableEvent") valueChangedEvent.Name = "ValueChanged" box.FocusLost:Connect(function() valueChangedEvent:Fire(box.Text) end) ValueChangerInstance.ValueChanged = valueChangedEvent.Event UserInputService.InputBegan:Connect(processInput) end ValueChangerInstance.Name = rowDisplayName .. "ValueChanger" nextRowPositionY = nextRowPositionY + ROW_HEIGHT if extraSpacing then nextRowPositionY = nextRowPositionY + extraSpacing end nextPosTable[pageToAddTo] = nextRowPositionY if isARealRow then local setRowSelection = function() local fullscreenDropDown = CoreGui.RobloxGui:FindFirstChild("DropDownFullscreenFrame") if fullscreenDropDown and fullscreenDropDown.Visible then return end local valueFrame = ValueChangerInstance.SliderFrame if not valueFrame then valueFrame = ValueChangerInstance.SliderFrame end if not valueFrame then valueFrame = ValueChangerInstance.DropDownFrame end if not valueFrame then valueFrame = ValueChangerInstance.SelectorFrame end if valueFrame and valueFrame.Visible and valueFrame.ZIndex > 1 and usesSelectedObject() and pageToAddTo.Active then GuiService.SelectedCoreObject = valueFrame end end RowFrame.MouseEnter:Connect(setRowSelection) --Could this be cleaned up even more? local function onVREnabled(prop) if prop == "VREnabled" then if VRService.VREnabled then RowFrame.Selectable = true RowFrame.Active = true ValueChangerSelection.Active = true GuiService.Changed:Connect(function(prop) if prop == "SelectedCoreObject" then local selected = GuiService.SelectedCoreObject if selected and (selected == RowFrame or selected:IsDescendantOf(RowFrame)) then RowFrame.ImageTransparency = 0.5 RowFrame.BackgroundTransparency = 1 else RowFrame.ImageTransparency = 1 RowFrame.BackgroundTransparency = 1 end end end) else RowFrame.Selectable = false RowFrame.Active = false end end end VRService.Changed:Connect(onVREnabled) onVREnabled("VREnabled") ValueChangerSelection.SelectionGained:Connect(function() if usesSelectedObject() then if VRService.VREnabled then RowFrame.ImageTransparency = 0.5 RowFrame.BackgroundTransparency = 1 else RowFrame.ImageTransparency = 1 RowFrame.BackgroundTransparency = 0.5 end if ValueChangerInstance.HubRef then ValueChangerInstance.HubRef:ScrollToFrame(RowFrame) end end end) ValueChangerSelection.SelectionLost:Connect(function() if usesSelectedObject() then RowFrame.ImageTransparency = 1 RowFrame.BackgroundTransparency = 1 end end) end pageToAddTo:AddRow(RowFrame, RowLabel, ValueChangerInstance, extraSpacing, false) ValueChangerInstance.Selection = ValueChangerSelection return RowFrame, RowLabel, ValueChangerInstance end local function AddNewRowObject(pageToAddTo, rowDisplayName, rowObject, extraSpacing) local nextRowPositionY = 0 if nextPosTable[pageToAddTo] then nextRowPositionY = nextPosTable[pageToAddTo] end local RowFrame = Util.Create'ImageButton' { Name = rowDisplayName .. "Frame", BackgroundTransparency = 1, BorderSizePixel = 0, Image = "rbxasset://textures/ui/VR/rectBackgroundWhite.png", ScaleType = Enum.ScaleType.Slice, SliceCenter = Rect.new(10,10,10,10), ImageTransparency = 1, Active = false, AutoButtonColor = false, Size = UDim2.new(1,0,0,ROW_HEIGHT), Position = UDim2.new(0,0,0,nextRowPositionY), ZIndex = 2, Selectable = not fixSettingsMenuDropdowns, --this should be false after removing FFlagFixSettingsMenuDropdowns SelectionImageObject = noSelectionObject, Parent = pageToAddTo.Page }; RowFrame.ImageColor3 = RowFrame.BackgroundColor3 RowFrame.SelectionGained:Connect(function() RowFrame.BackgroundTransparency = 0.5 end) RowFrame.SelectionLost:Connect(function() RowFrame.BackgroundTransparency = 1 end) local RowLabel = Util.Create'TextLabel' { Name = rowDisplayName .. "Label", Text = rowDisplayName, Font = Enum.Font.SourceSansBold, TextSize = 16, TextColor3 = Color3.fromRGB(255,255,255), TextXAlignment = Enum.TextXAlignment.Left, BackgroundTransparency = 1, Size = UDim2.new(0,200,1,0), Position = UDim2.new(0,10,0,0), ZIndex = 2, Parent = RowFrame }; local function onResized(viewportSize, portrait) if portrait then RowLabel.TextSize = 16 else RowLabel.TextSize = isTenFootInterface() and 36 or 24 end end addOnResizedCallback(RowFrame, onResized) if extraSpacing then RowFrame.Position = UDim2.new(RowFrame.Position.X.Scale,RowFrame.Position.X.Offset, RowFrame.Position.Y.Scale,RowFrame.Position.Y.Offset + extraSpacing) end nextRowPositionY = nextRowPositionY + ROW_HEIGHT if extraSpacing then nextRowPositionY = nextRowPositionY + extraSpacing end nextPosTable[pageToAddTo] = nextRowPositionY local setRowSelection = function() if RowFrame.Visible then GuiService.SelectedCoreObject = RowFrame end end RowFrame.MouseEnter:Connect(setRowSelection) rowObject.SelectionImageObject = noSelectionObject rowObject.SelectionGained:Connect(function() if VRService.VREnabled then RowFrame.ImageTransparency = 0.5 RowFrame.BackgroundTransparency = 1 else RowFrame.ImageTransparency = 1 RowFrame.BackgroundTransparency = 0.5 end end) rowObject.SelectionLost:Connect(function() RowFrame.ImageTransparency = 1 RowFrame.BackgroundTransparency = 1 end) rowObject.Parent = RowFrame pageToAddTo:AddRow(RowFrame, RowLabel, rowObject, extraSpacing, true) return RowFrame end -------- public facing API ---------------- local moduleApiTable = {} function moduleApiTable:Create(instanceType) return function(data) local obj = Instance.new(instanceType) local parent = nil for k, v in pairs(data) do if type(k) == 'number' then v.Parent = obj elseif k == 'Parent' then parent = v else obj[k] = v end end if parent then obj.Parent = parent end return obj end end -- RayPlaneIntersection (shortened) -- http://www.siggraph.org/education/materials/HyperGraph/raytrace/rayplane_intersection.htm function moduleApiTable:RayPlaneIntersection(ray, planeNormal, pointOnPlane) planeNormal = planeNormal.unit ray = ray.Unit local Vd = planeNormal:Dot(ray.Direction) if Vd == 0 then -- parallel, no intersection return nil end local V0 = planeNormal:Dot(pointOnPlane - ray.Origin) local t = V0 / Vd if t < 0 then --plane is behind ray origin, and thus there is no intersection return nil end return ray.Origin + ray.Direction * t end function moduleApiTable:GetEaseLinear() return Linear end function moduleApiTable:GetEaseOutQuad() return EaseOutQuad end function moduleApiTable:GetEaseInOutQuad() return EaseInOutQuad end function moduleApiTable:CreateNewSlider(numOfSteps, startStep, minStep) return CreateNewSlider(numOfSteps, startStep, minStep) end function moduleApiTable:CreateNewSelector(selectionStringTable, startPosition) return CreateSelector(selectionStringTable, startPosition) end function moduleApiTable:CreateNewDropDown(dropDownStringTable, startPosition) return CreateDropDown(dropDownStringTable, startPosition, nil) end function moduleApiTable:AddNewRow(pageToAddTo, rowDisplayName, selectionType, rowValues, rowDefault, extraSpacing) return AddNewRow(pageToAddTo, rowDisplayName, selectionType, rowValues, rowDefault, extraSpacing) end function moduleApiTable:AddNewRowObject(pageToAddTo, rowDisplayName, rowObject, extraSpacing) return AddNewRowObject(pageToAddTo, rowDisplayName, rowObject, extraSpacing) end function moduleApiTable:ShowAlert(alertMessage, okButtonText, settingsHub, okPressedFunc, hasBackground) ShowAlert(alertMessage, okButtonText, settingsHub, okPressedFunc, hasBackground) end function moduleApiTable:IsSmallTouchScreen() return isSmallTouchScreen() end function moduleApiTable:IsPortrait() return isPortrait() end function moduleApiTable:MakeStyledButton(name, text, size, clickFunc, pageRef, hubRef) return MakeButton(name, text, size, clickFunc, pageRef, hubRef) end function moduleApiTable:MakeStyledImageButton(name, image, size, imageSize, clickFunc, pageRef, hubRef) return MakeImageButton(name, image, size, imageSize, clickFunc, pageRef, hubRef) end function moduleApiTable:AddButtonRow(pageToAddTo, name, text, size, clickFunc, hubRef) return AddButtonRow(pageToAddTo, name, text, size, clickFunc, hubRef) end function moduleApiTable:CreateSignal() return CreateSignal() end function moduleApiTable:UsesSelectedObject() return usesSelectedObject() end function moduleApiTable:TweenProperty(instance, prop, start, final, duration, easingFunc, cbFunc) return PropertyTweener(instance, prop, start, final, duration, easingFunc, cbFunc) end function moduleApiTable:OnResized(key, callback) return addOnResizedCallback(key, callback) end function moduleApiTable:FireOnResized() local newSize = getViewportSize() local portrait = moduleApiTable:IsPortrait() for key, callback in pairs(onResizedCallbacks) do callback(newSize, portrait) end end return moduleApiTable