imgui.Poll = (function()
local pollStates = {}
---@param title string Название голосования (должно быть уникальным)
---@param options table Таблица с опциями голосования (каждая опция - это таблица с ключами: title (название), percent (процент прогресса, по умолчанию 0))
---@param settings table Настройки голосования (например, {type = "multiple" или "single", animationDuration = <длительность анимации в секундах>, width = <ширина пункта>})
return function(title, options, settings)
local pollState = pollStates[title]
if not pollState then
pollState = {
options = {},
settings = settings,
animStartTime = 0,
animating = false,
voteCompleted = false,
}
for _, option in ipairs(options) do
table.insert(pollState.options, {
title = option.title,
percent = option.percent or 0,
targetPercent = option.percent or 0,
circleFill = 0,
selected = false,
initialPercent = 0,
initialCircleFill = 0,
})
end
pollStates[title] = pollState
end
local function interpolate(t)
return 1 - (1 - t) ^ 3
end
local function updateAnimation()
if pollState.animating then
local elapsed = os.clock() - pollState.animStartTime
local duration = pollState.settings.animationDuration or 1
if elapsed >= duration then
pollState.animating = false
for _, option in ipairs(pollState.options) do
option.percent = option.targetPercent
option.circleFill = option.selected and 100 or 0
end
else
local t = elapsed / duration
t = interpolate(t)
for _, option in ipairs(pollState.options) do
option.percent = option.initialPercent + (option.targetPercent - option.initialPercent) * t
option.circleFill = option.initialCircleFill + ((option.selected and 100 or 0) - option.initialCircleFill) * t
option.percent = math.max(0, math.min(100, option.percent))
option.circleFill = math.max(0, math.min(100, option.circleFill))
end
end
end
end
updateAnimation()
local fixedWidth = settings.width or 400
local boxHeight, padding = 60, 15
local windowPos = imgui.GetWindowPos()
local contentRegionMin = imgui.GetWindowContentRegionMin()
imgui.SetCursorScreenPos(imgui.ImVec2(windowPos.x + contentRegionMin.x, imgui.GetCursorScreenPos().y))
imgui.Text(title)
local p = imgui.GetCursorScreenPos()
local dl = imgui.GetWindowDrawList()
local circleRadius = 12
local barHeight, barPadding = 10, 20
local circleSegments = 64
for i, option in ipairs(pollState.options) do
local boxMin = imgui.ImVec2(p.x, p.y + (i - 1) * (boxHeight + padding))
local boxMax = imgui.ImVec2(boxMin.x + fixedWidth, boxMin.y + boxHeight)
imgui.SetCursorScreenPos(boxMin)
imgui.InvisibleButton("option" .. i .. "_" .. title, imgui.ImVec2(fixedWidth, boxHeight))
local isHovered, isClicked = imgui.IsItemHovered(), imgui.IsItemClicked()
if isClicked and not pollState.voteCompleted then
if pollState.settings.type == "single" then
for _, opt in ipairs(pollState.options) do opt.selected = false end
option.selected = true
elseif pollState.settings.type == "multiple" then
option.selected = not option.selected
end
local totalVotes = 0
for _, opt in ipairs(pollState.options) do
if opt.selected then totalVotes = totalVotes + 1 end
end
for _, opt in ipairs(pollState.options) do
if totalVotes > 0 then
opt.targetPercent = opt.selected and math.floor(100 / totalVotes) or 0
else
opt.targetPercent = 0
end
end
for _, opt in ipairs(pollState.options) do
opt.initialPercent = opt.percent
opt.initialCircleFill = opt.circleFill
end
pollState.animStartTime = os.clock()
pollState.animating = true
end
local borderColor = imgui.GetColorU32(
option.selected and imgui.Col.ButtonActive or (isHovered and imgui.Col.ButtonHovered or imgui.Col.Border)
)
dl:AddRect(boxMin, boxMax, borderColor, 10, 7, 2)
local circlePos = imgui.ImVec2(boxMin.x + circleRadius + 10, boxMin.y + boxHeight / 2)
dl:AddCircle(circlePos, circleRadius, imgui.GetColorU32(imgui.Col.Border), circleSegments, 2)
local circleFillColor = imgui.GetColorU32(imgui.Col.ButtonHovered)
local fillRadius = circleRadius * option.circleFill / 100
dl:AddCircleFilled(circlePos, fillRadius, circleFillColor, circleSegments)
local barMin = imgui.ImVec2(boxMin.x + circleRadius * 2 + 20, boxMax.y - barHeight - 15)
local barMax = imgui.ImVec2(boxMin.x + fixedWidth - barPadding, boxMax.y - 15)
local radius = barHeight / 2
dl:AddRectFilled(barMin, barMax, imgui.GetColorU32(imgui.Col.FrameBg), radius, ImDrawCornerFlags_All)
if option.percent > 0 then
local activeBarMax = imgui.ImVec2(barMin.x + (barMax.x - barMin.x) * option.percent / 100, barMax.y)
local cornerFlags = ImDrawCornerFlags_Left
if option.percent == 100 then
cornerFlags = ImDrawCornerFlags_All
end
dl:AddRectFilled(barMin, activeBarMax, imgui.GetColorU32(imgui.Col.ButtonHovered), radius, cornerFlags)
end
local textPos = imgui.ImVec2(barMin.x, boxMin.y + 10)
local percentText = string.format("%d%%", option.percent)
local percentTextSize = imgui.CalcTextSize(percentText)
local percentTextPos = imgui.ImVec2(boxMin.x + fixedWidth - percentTextSize.x - barPadding, boxMin.y + 10)
dl:AddText(textPos, imgui.GetColorU32(imgui.Col.Text), option.title)
dl:AddText(percentTextPos, imgui.GetColorU32(imgui.Col.Text), percentText)
end
end
end)()