mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Refactor draggable module and cleanup functions
Refactor draggable functionality to improve clarity and maintainability.
This commit is contained in:
parent
5362f7ecee
commit
7d89cb5a1b
1 changed files with 68 additions and 175 deletions
|
|
@ -1,198 +1,91 @@
|
||||||
|
-- src/draggable.luau
|
||||||
local UserInputService = game and game:GetService("UserInputService") or nil
|
local UserInputService = game and game:GetService("UserInputService") or nil
|
||||||
|
|
||||||
local function clamp(x, a, b)
|
-- Factory: inject vide.action and vide.cleanup to avoid cyclic requires
|
||||||
if x < a then return a end
|
return function(action, cleanup)
|
||||||
if x > b then return b end
|
return function(opts)
|
||||||
return x
|
opts = opts or {}
|
||||||
end
|
local axis = opts.axis or "both" -- "x" | "y" | "both"
|
||||||
|
|
||||||
local function lerp(a, b, t)
|
return action(function(gui)
|
||||||
return a + (b - a) * t
|
-- allow require in non-Roblox test environments
|
||||||
end
|
|
||||||
|
|
||||||
local function invLerp(a, b, v)
|
|
||||||
if a == b then return 0 end
|
|
||||||
return (v - a) / (b - a)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function quantize(v, step)
|
|
||||||
if not step or step <= 0 then return v end
|
|
||||||
return math.floor((v / step) + 0.5) * step
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Factory: inject Vide functions to avoid cyclic requires
|
|
||||||
return function(create, source, action, cleanup)
|
|
||||||
return function(props)
|
|
||||||
props = props or {}
|
|
||||||
|
|
||||||
local minV = props.min or 0
|
|
||||||
local maxV = props.max or 1
|
|
||||||
local step = props.step
|
|
||||||
local value = props.value
|
|
||||||
assert(value, "vide.slider: props.value (source<number>) is required")
|
|
||||||
|
|
||||||
local theme = props.theme or {}
|
|
||||||
local format = props.format or function(v) return tostring(v) end
|
|
||||||
local onChanged = props.onChanged
|
|
||||||
|
|
||||||
local trackRef = source(nil)
|
|
||||||
|
|
||||||
local function setFromX(screenX)
|
|
||||||
local track = trackRef()
|
|
||||||
if not track then return end
|
|
||||||
|
|
||||||
local absPosX = track.AbsolutePosition.X
|
|
||||||
local absSizeX = track.AbsoluteSize.X
|
|
||||||
if absSizeX <= 0 then return end
|
|
||||||
|
|
||||||
local t = clamp((screenX - absPosX) / absSizeX, 0, 1)
|
|
||||||
local v = lerp(minV, maxV, t)
|
|
||||||
v = quantize(v, step)
|
|
||||||
|
|
||||||
value(v)
|
|
||||||
if onChanged then onChanged(v) end
|
|
||||||
end
|
|
||||||
|
|
||||||
return create "Frame" {
|
|
||||||
Name = props.name or "Slider",
|
|
||||||
Size = props.size or UDim2.fromOffset(516, 90),
|
|
||||||
Position = props.position,
|
|
||||||
AnchorPoint = props.anchorPoint,
|
|
||||||
LayoutOrder = props.layoutOrder,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
|
|
||||||
create "TextLabel" {
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
Size = UDim2.fromOffset(516, 24),
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Left,
|
|
||||||
FontFace = props.fontFace,
|
|
||||||
TextSize = props.labelTextSize or 16,
|
|
||||||
TextColor3 = props.labelTextColor or Color3.fromRGB(235, 238, 245),
|
|
||||||
Text = props.label or "Slider",
|
|
||||||
},
|
|
||||||
|
|
||||||
create "TextLabel" {
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
Size = UDim2.fromOffset(516, 24),
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Right,
|
|
||||||
FontFace = props.fontFace,
|
|
||||||
TextSize = props.valueTextSize or 16,
|
|
||||||
TextColor3 = props.valueTextColor or Color3.fromRGB(200, 206, 220),
|
|
||||||
Text = function()
|
|
||||||
return format(value())
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
|
|
||||||
create "Frame" {
|
|
||||||
Name = "Track",
|
|
||||||
Position = UDim2.fromOffset(0, 38),
|
|
||||||
Size = UDim2.fromOffset(516, 16),
|
|
||||||
BackgroundColor3 = theme.trackColor or Color3.fromRGB(20, 22, 28),
|
|
||||||
BackgroundTransparency = theme.trackTransparency or 0.15,
|
|
||||||
|
|
||||||
action(function(track)
|
|
||||||
trackRef(track)
|
|
||||||
|
|
||||||
local trackBeganConn = track.InputBegan:Connect(function(input)
|
|
||||||
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
|
|
||||||
setFromX(input.Position.X)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
cleanup(function()
|
|
||||||
if trackRef() == track then trackRef(nil) end
|
|
||||||
trackBeganConn:Disconnect()
|
|
||||||
end)
|
|
||||||
end),
|
|
||||||
|
|
||||||
create "UICorner" { CornerRadius = UDim.new(0, theme.cornerRadius or 8) },
|
|
||||||
create "UIStroke" {
|
|
||||||
Thickness = 1,
|
|
||||||
Color = theme.strokeColor or Color3.fromRGB(255, 255, 255),
|
|
||||||
Transparency = theme.strokeTransparency or 0.8,
|
|
||||||
},
|
|
||||||
|
|
||||||
create "Frame" {
|
|
||||||
Name = "Fill",
|
|
||||||
Size = function()
|
|
||||||
local t = clamp(invLerp(minV, maxV, value()), 0, 1)
|
|
||||||
return UDim2.fromScale(t, 1)
|
|
||||||
end,
|
|
||||||
BackgroundColor3 = theme.fillColor or Color3.fromRGB(80, 140, 255),
|
|
||||||
BackgroundTransparency = theme.fillTransparency or 0.15,
|
|
||||||
create "UICorner" { CornerRadius = UDim.new(0, theme.cornerRadius or 8) },
|
|
||||||
},
|
|
||||||
|
|
||||||
create "TextButton" {
|
|
||||||
Name = "Knob",
|
|
||||||
AutoButtonColor = false,
|
|
||||||
Text = "",
|
|
||||||
Size = UDim2.fromOffset(24, 24),
|
|
||||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
|
||||||
Position = function()
|
|
||||||
local t = clamp(invLerp(minV, maxV, value()), 0, 1)
|
|
||||||
return UDim2.fromScale(t, 0.5)
|
|
||||||
end,
|
|
||||||
BackgroundColor3 = theme.knobColor or Color3.fromRGB(235, 238, 245),
|
|
||||||
BackgroundTransparency = theme.knobTransparency or 0,
|
|
||||||
|
|
||||||
create "UICorner" { CornerRadius = UDim.new(0, theme.knobRadius or 999) },
|
|
||||||
create "UIStroke" { Thickness = 1, Color = Color3.fromRGB(0, 0, 0), Transparency = 0.75 },
|
|
||||||
|
|
||||||
action(function(btn)
|
|
||||||
-- Non-Roblox / unit-test environment: allow requiring this module without crashing.
|
|
||||||
-- Slider still renders; dragging is disabled when UserInputService is unavailable.
|
|
||||||
if not UserInputService then
|
if not UserInputService then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if typeof(btn) ~= "Instance" or not btn:IsA("GuiButton") then
|
if typeof(gui) ~= "Instance" or not gui:IsA("GuiObject") then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local dragInput
|
|
||||||
local dragging = false
|
local dragging = false
|
||||||
local beganConn
|
local dragInput
|
||||||
local changedConn
|
local dragStart
|
||||||
local uisChangedConn
|
local startPos
|
||||||
|
|
||||||
local function update(input)
|
local function update(input)
|
||||||
setFromX(input.Position.X)
|
local delta = input.Position - dragStart
|
||||||
|
|
||||||
|
local xOff = startPos.X.Offset
|
||||||
|
local yOff = startPos.Y.Offset
|
||||||
|
|
||||||
|
if axis == "x" or axis == "both" then
|
||||||
|
xOff = startPos.X.Offset + delta.X
|
||||||
|
end
|
||||||
|
if axis == "y" or axis == "both" then
|
||||||
|
yOff = startPos.Y.Offset + delta.Y
|
||||||
end
|
end
|
||||||
|
|
||||||
beganConn = btn.InputBegan:Connect(function(input)
|
gui.Position = UDim2.new(startPos.X.Scale, xOff, startPos.Y.Scale, yOff)
|
||||||
|
|
||||||
|
if opts.onDrag then
|
||||||
|
opts.onDrag(gui.Position)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local beganConn = gui.InputBegan:Connect(function(input)
|
||||||
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
|
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
|
||||||
dragging = true
|
dragging = true
|
||||||
update(input)
|
dragStart = input.Position
|
||||||
|
startPos = gui.Position
|
||||||
|
|
||||||
|
if opts.onDragStart then
|
||||||
|
opts.onDragStart(startPos)
|
||||||
|
end
|
||||||
|
|
||||||
|
local changedConn
|
||||||
changedConn = input.Changed:Connect(function()
|
changedConn = input.Changed:Connect(function()
|
||||||
if input.UserInputState == Enum.UserInputState.End then
|
if input.UserInputState == Enum.UserInputState.End then
|
||||||
dragging = false
|
dragging = false
|
||||||
|
if changedConn then changedConn:Disconnect() end
|
||||||
|
if opts.onDragEnd then
|
||||||
|
opts.onDragEnd(gui.Position)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
cleanup(function()
|
||||||
|
if changedConn then changedConn:Disconnect() end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
btn.InputChanged:Connect(function(input)
|
local changedConn = gui.InputChanged:Connect(function(input)
|
||||||
if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
|
if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
|
||||||
dragInput = input
|
dragInput = input
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
uisChangedConn = UserInputService.InputChanged:Connect(function(input)
|
local uisConn = UserInputService.InputChanged:Connect(function(input)
|
||||||
if input == dragInput and dragging then
|
if input == dragInput and dragging then
|
||||||
update(input)
|
update(input)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
cleanup(function()
|
cleanup(function()
|
||||||
if beganConn then beganConn:Disconnect() end
|
beganConn:Disconnect()
|
||||||
if changedConn then changedConn:Disconnect() end
|
changedConn:Disconnect()
|
||||||
if uisChangedConn then uisChangedConn:Disconnect() end
|
uisConn:Disconnect()
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
end),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue