drag event
This commit is contained in:
306
examples/14_drag_slider.lua
Normal file
306
examples/14_drag_slider.lua
Normal file
@@ -0,0 +1,306 @@
|
||||
--[[
|
||||
FlexLove Example 14: Drag Event - Slider Implementation
|
||||
|
||||
This example demonstrates how to use the new drag event to create
|
||||
interactive sliders without any first-party slider component.
|
||||
|
||||
Features demonstrated:
|
||||
- Using drag events for continuous mouse tracking
|
||||
- Converting mouse coordinates to element-relative positions
|
||||
- Updating UI elements based on drag position
|
||||
- Creating reusable slider components
|
||||
|
||||
Run with: love /path/to/libs/examples/14_drag_slider.lua
|
||||
]]
|
||||
|
||||
local Lv = love
|
||||
|
||||
local FlexLove = require("../FlexLove")
|
||||
local Gui = FlexLove.Gui
|
||||
local Color = FlexLove.Color
|
||||
local enums = FlexLove.enums
|
||||
|
||||
-- Slider state
|
||||
local volume = 0.5 -- 0.0 to 1.0
|
||||
local brightness = 0.75 -- 0.0 to 1.0
|
||||
local temperature = 20 -- 0 to 40 (degrees)
|
||||
|
||||
-- UI elements
|
||||
local volumeValueText
|
||||
local brightnessValueText
|
||||
local temperatureValueText
|
||||
local volumeHandle
|
||||
local brightnessHandle
|
||||
local temperatureHandle
|
||||
|
||||
--- Helper function to create a slider
|
||||
---@param x string|number
|
||||
---@param y string|number
|
||||
---@param width string|number
|
||||
---@param label string
|
||||
---@param min number
|
||||
---@param max number
|
||||
---@param initialValue number
|
||||
---@param onValueChange function
|
||||
---@return table -- Returns { bg, handle, valueText }
|
||||
local function createSlider(x, y, width, label, min, max, initialValue, onValueChange)
|
||||
-- Container for the slider
|
||||
local container = Gui.new({
|
||||
x = x,
|
||||
y = y,
|
||||
width = width,
|
||||
height = "12vh",
|
||||
positioning = enums.Positioning.FLEX,
|
||||
flexDirection = enums.FlexDirection.VERTICAL,
|
||||
gap = 5,
|
||||
})
|
||||
|
||||
-- Label
|
||||
Gui.new({
|
||||
parent = container,
|
||||
height = "3vh",
|
||||
text = label,
|
||||
textSize = "2vh",
|
||||
textColor = Color.new(0.9, 0.9, 0.9, 1),
|
||||
})
|
||||
|
||||
-- Slider track background
|
||||
local sliderBg = Gui.new({
|
||||
parent = container,
|
||||
height = "4vh",
|
||||
backgroundColor = Color.new(0.2, 0.2, 0.25, 1),
|
||||
cornerRadius = 5,
|
||||
positioning = enums.Positioning.RELATIVE,
|
||||
})
|
||||
|
||||
-- Slider handle
|
||||
local normalized = (initialValue - min) / (max - min)
|
||||
local handle = Gui.new({
|
||||
parent = sliderBg,
|
||||
x = (normalized * 95) .. "%",
|
||||
y = "50%",
|
||||
width = "5%",
|
||||
height = "80%",
|
||||
backgroundColor = Color.new(0.4, 0.6, 0.9, 1),
|
||||
cornerRadius = 3,
|
||||
positioning = enums.Positioning.ABSOLUTE,
|
||||
-- Center the handle vertically
|
||||
top = "10%",
|
||||
})
|
||||
|
||||
-- Value display
|
||||
local valueText = Gui.new({
|
||||
parent = container,
|
||||
height = "3vh",
|
||||
text = string.format("%.2f", initialValue),
|
||||
textSize = "2vh",
|
||||
textColor = Color.new(0.7, 0.8, 1, 1),
|
||||
textAlign = enums.TextAlign.CENTER,
|
||||
})
|
||||
|
||||
-- Make the background track interactive
|
||||
sliderBg.callback = function(element, event)
|
||||
if event.type == "press" or event.type == "drag" then
|
||||
-- Get element bounds
|
||||
local bg_x = element.x
|
||||
local bg_width = element.width
|
||||
|
||||
-- Calculate relative position (0 to 1)
|
||||
local mouse_x = event.x
|
||||
local relative_x = mouse_x - bg_x
|
||||
local new_normalized = math.max(0, math.min(1, relative_x / bg_width))
|
||||
|
||||
-- Calculate actual value
|
||||
local new_value = min + (new_normalized * (max - min))
|
||||
|
||||
-- Update handle position (use percentage for responsiveness)
|
||||
handle.x = (new_normalized * 95) .. "%"
|
||||
|
||||
-- Update value text
|
||||
if max - min > 10 then
|
||||
-- For larger ranges (like temperature), show integers
|
||||
valueText.text = string.format("%d", new_value)
|
||||
else
|
||||
-- For smaller ranges (like 0-1), show decimals
|
||||
valueText.text = string.format("%.2f", new_value)
|
||||
end
|
||||
|
||||
-- Call the value change callback
|
||||
if onValueChange then
|
||||
onValueChange(new_value)
|
||||
end
|
||||
|
||||
-- Re-layout to apply position changes
|
||||
element:recalculateUnits(Lv.graphics.getWidth(), Lv.graphics.getHeight())
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
container = container,
|
||||
bg = sliderBg,
|
||||
handle = handle,
|
||||
valueText = valueText,
|
||||
}
|
||||
end
|
||||
|
||||
function Lv.load()
|
||||
Gui.init({
|
||||
baseScale = { width = 1920, height = 1080 },
|
||||
})
|
||||
|
||||
-- Title
|
||||
Gui.new({
|
||||
x = "2vw",
|
||||
y = "2vh",
|
||||
width = "96vw",
|
||||
height = "6vh",
|
||||
text = "FlexLove Example 14: Drag Event - Slider Implementation",
|
||||
textSize = "4vh",
|
||||
textColor = Color.new(1, 1, 1, 1),
|
||||
textAlign = enums.TextAlign.CENTER,
|
||||
})
|
||||
|
||||
-- Subtitle
|
||||
Gui.new({
|
||||
x = "2vw",
|
||||
y = "9vh",
|
||||
width = "96vw",
|
||||
height = "3vh",
|
||||
text = "Drag the sliders to change values - built using only the drag event primitive!",
|
||||
textSize = "2vh",
|
||||
textColor = Color.new(0.7, 0.7, 0.7, 1),
|
||||
textAlign = enums.TextAlign.CENTER,
|
||||
})
|
||||
|
||||
-- Volume Slider (0.0 - 1.0)
|
||||
local volumeSlider = createSlider("10vw", "18vh", "80vw", "Volume (0.0 - 1.0)", 0.0, 1.0, volume, function(value)
|
||||
volume = value
|
||||
end)
|
||||
volumeValueText = volumeSlider.valueText
|
||||
volumeHandle = volumeSlider.handle
|
||||
|
||||
-- Brightness Slider (0.0 - 1.0)
|
||||
local brightnessSlider = createSlider("10vw", "35vh", "80vw", "Brightness (0.0 - 1.0)", 0.0, 1.0, brightness, function(value)
|
||||
brightness = value
|
||||
end)
|
||||
brightnessValueText = brightnessSlider.valueText
|
||||
brightnessHandle = brightnessSlider.handle
|
||||
|
||||
-- Temperature Slider (0 - 40°C)
|
||||
local temperatureSlider = createSlider("10vw", "52vh", "80vw", "Temperature (0 - 40°C)", 0, 40, temperature, function(value)
|
||||
temperature = value
|
||||
end)
|
||||
temperatureValueText = temperatureSlider.valueText
|
||||
temperatureHandle = temperatureSlider.handle
|
||||
|
||||
-- Visual feedback section
|
||||
Gui.new({
|
||||
x = "10vw",
|
||||
y = "70vh",
|
||||
width = "80vw",
|
||||
height = "3vh",
|
||||
text = "Visual Feedback:",
|
||||
textSize = "2.5vh",
|
||||
textColor = Color.new(1, 1, 1, 1),
|
||||
})
|
||||
|
||||
-- Volume visualization
|
||||
Gui.new({
|
||||
x = "10vw",
|
||||
y = "75vh",
|
||||
width = "25vw",
|
||||
height = "20vh",
|
||||
backgroundColor = Color.new(0.15, 0.15, 0.2, 1),
|
||||
cornerRadius = 10,
|
||||
border = { top = true, right = true, bottom = true, left = true },
|
||||
borderColor = Color.new(0.3, 0.3, 0.4, 1),
|
||||
})
|
||||
|
||||
-- Brightness visualization
|
||||
Gui.new({
|
||||
x = "37.5vw",
|
||||
y = "75vh",
|
||||
width = "25vw",
|
||||
height = "20vh",
|
||||
backgroundColor = Color.new(0.15, 0.15, 0.2, 1),
|
||||
cornerRadius = 10,
|
||||
border = { top = true, right = true, bottom = true, left = true },
|
||||
borderColor = Color.new(0.3, 0.3, 0.4, 1),
|
||||
})
|
||||
|
||||
-- Temperature visualization
|
||||
Gui.new({
|
||||
x = "65vw",
|
||||
y = "75vh",
|
||||
width = "25vw",
|
||||
height = "20vh",
|
||||
backgroundColor = Color.new(0.15, 0.15, 0.2, 1),
|
||||
cornerRadius = 10,
|
||||
border = { top = true, right = true, bottom = true, left = true },
|
||||
borderColor = Color.new(0.3, 0.3, 0.4, 1),
|
||||
})
|
||||
end
|
||||
|
||||
function Lv.update(dt)
|
||||
Gui.update(dt)
|
||||
end
|
||||
|
||||
function Lv.draw()
|
||||
Lv.graphics.clear(0.05, 0.05, 0.08, 1)
|
||||
Gui.draw()
|
||||
|
||||
-- Draw volume visualization (speaker icon with bars)
|
||||
local volumeX = Lv.graphics.getWidth() * 0.10 + 20
|
||||
local volumeY = Lv.graphics.getHeight() * 0.75 + 30
|
||||
Lv.graphics.setColor(0.4, 0.6, 0.9, 1)
|
||||
Lv.graphics.print("Volume:", volumeX, volumeY, 0, 2, 2)
|
||||
|
||||
-- Volume bars
|
||||
local barCount = 10
|
||||
for i = 1, barCount do
|
||||
if i / barCount <= volume then
|
||||
Lv.graphics.setColor(0.4, 0.8, 0.4, 1)
|
||||
else
|
||||
Lv.graphics.setColor(0.2, 0.2, 0.25, 1)
|
||||
end
|
||||
local barX = volumeX + 20 + (i - 1) * 30
|
||||
local barHeight = 20 + i * 5
|
||||
Lv.graphics.rectangle("fill", barX, volumeY + 60 - barHeight, 20, barHeight, 3)
|
||||
end
|
||||
|
||||
-- Draw brightness visualization (sun icon)
|
||||
local brightnessX = Lv.graphics.getWidth() * 0.375 + 20
|
||||
local brightnessY = Lv.graphics.getHeight() * 0.75 + 30
|
||||
Lv.graphics.setColor(0.4, 0.6, 0.9, 1)
|
||||
Lv.graphics.print("Brightness:", brightnessX, brightnessY, 0, 2, 2)
|
||||
|
||||
-- Sun circle
|
||||
Lv.graphics.setColor(1, 0.9, 0.3, brightness)
|
||||
Lv.graphics.circle("fill", brightnessX + 150, brightnessY + 80, 30 * brightness + 10)
|
||||
|
||||
-- Draw temperature visualization (thermometer)
|
||||
local tempX = Lv.graphics.getWidth() * 0.65 + 20
|
||||
local tempY = Lv.graphics.getHeight() * 0.75 + 30
|
||||
Lv.graphics.setColor(0.4, 0.6, 0.9, 1)
|
||||
Lv.graphics.print("Temperature:", tempX, tempY, 0, 2, 2)
|
||||
|
||||
-- Thermometer
|
||||
local tempNormalized = temperature / 40
|
||||
local tempColor = {
|
||||
1 - tempNormalized * 0.5, -- Red increases with temp
|
||||
0.3,
|
||||
1 - tempNormalized, -- Blue decreases with temp
|
||||
}
|
||||
Lv.graphics.setColor(tempColor[1], tempColor[2], tempColor[3], 1)
|
||||
Lv.graphics.rectangle("fill", tempX + 100, tempY + 50, 40, 100 * tempNormalized, 5)
|
||||
Lv.graphics.setColor(0.3, 0.3, 0.4, 1)
|
||||
Lv.graphics.rectangle("line", tempX + 100, tempY + 50, 40, 100, 5)
|
||||
|
||||
-- Temperature text
|
||||
Lv.graphics.setColor(1, 1, 1, 1)
|
||||
Lv.graphics.print(string.format("%.0f°C", temperature), tempX + 160, tempY + 90, 0, 2, 2)
|
||||
end
|
||||
|
||||
function Lv.resize(w, h)
|
||||
Gui.resize(w, h)
|
||||
end
|
||||
Reference in New Issue
Block a user