* feat: add Audio Switcher plugin * fix: hide null audio descriptions * docs: add focused panel screenshot * docs: focus thumbnail on plugin panel
127 lines
3.5 KiB
Luau
127 lines
3.5 KiB
Luau
--!nonstrict
|
|
|
|
local PANEL_ID = "blackbartblues/audio-switcher:audio-switcher"
|
|
local SNAPSHOT_KEY = "audio_switcher_snapshot"
|
|
local COMMAND_KEY = "audio_switcher_command"
|
|
local RESULT_KEY = "audio_switcher_result"
|
|
|
|
local snapshot = noctalia.state.get(SNAPSHOT_KEY) or {
|
|
available = false,
|
|
loading = true,
|
|
outputs = {},
|
|
inputs = {},
|
|
outputVolume = 0,
|
|
inputVolume = 0,
|
|
outputMuted = false,
|
|
}
|
|
local requestId = 0
|
|
local outputVolumeDraft = nil
|
|
|
|
local function activeDevice(devices, fallback)
|
|
for _, device in ipairs(type(devices) == "table" and devices or {}) do
|
|
if device.active == true then return device end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function percent(value)
|
|
return tostring(math.floor((tonumber(value) or 0) + 0.5)) .. "%"
|
|
end
|
|
|
|
local function render()
|
|
local outputDevice = activeDevice(snapshot.outputs)
|
|
local inputDevice = activeDevice(snapshot.inputs)
|
|
local outputName = outputDevice and tostring(outputDevice.name or "") or noctalia.tr("panel.no_output")
|
|
local inputName = inputDevice and tostring(inputDevice.name or "") or noctalia.tr("panel.no_input")
|
|
local outputVolume = percent(outputVolumeDraft or snapshot.outputVolume)
|
|
local inputVolume = percent(snapshot.inputVolume)
|
|
|
|
local glyph = snapshot.outputMuted == true and "volume-off" or "volume"
|
|
if outputDevice ~= nil and snapshot.outputMuted ~= true then
|
|
glyph = tostring(outputDevice.icon or "volume")
|
|
end
|
|
|
|
barWidget.setGlyph(glyph)
|
|
if snapshot.busy == true then
|
|
barWidget.setGlyphColor("secondary")
|
|
elseif snapshot.available == true then
|
|
barWidget.setGlyphColor("primary")
|
|
else
|
|
barWidget.setGlyphColor("error")
|
|
end
|
|
|
|
local showPercentage = noctalia.getConfig("show_percentage") ~= false
|
|
barWidget.setText(showPercentage and not barWidget.isVertical() and outputVolume or "")
|
|
barWidget.setTooltip(noctalia.tr("widget.tooltip", {
|
|
output = outputName,
|
|
output_volume = outputVolume,
|
|
input = inputName,
|
|
input_volume = inputVolume,
|
|
}))
|
|
end
|
|
|
|
local function dispatch(action)
|
|
requestId += 1
|
|
noctalia.state.set(COMMAND_KEY, {
|
|
action = action,
|
|
requestId = "widget-" .. tostring(os.time()) .. "-" .. tostring(requestId),
|
|
})
|
|
end
|
|
|
|
noctalia.state.watch(SNAPSHOT_KEY, function(value)
|
|
if type(value) == "table" then
|
|
if outputVolumeDraft ~= nil and tonumber(value.outputVolume) == outputVolumeDraft then
|
|
outputVolumeDraft = nil
|
|
end
|
|
snapshot = value
|
|
render()
|
|
end
|
|
end)
|
|
|
|
noctalia.state.watch(RESULT_KEY, function(result)
|
|
if type(result) ~= "table" or not tostring(result.requestId or ""):match("^widget%-scroll%-") then return end
|
|
if result.ok ~= true then
|
|
outputVolumeDraft = nil
|
|
render()
|
|
end
|
|
end)
|
|
|
|
function update()
|
|
render()
|
|
end
|
|
|
|
function onConfigChanged()
|
|
render()
|
|
end
|
|
|
|
function onClick()
|
|
noctalia.togglePanel(PANEL_ID)
|
|
end
|
|
|
|
function onRightClick()
|
|
dispatch("cycle_output")
|
|
end
|
|
|
|
function onMiddleClick()
|
|
dispatch("cycle_input")
|
|
end
|
|
|
|
function onScroll(axis, steps)
|
|
if axis ~= "vertical" then return end
|
|
local detents = tonumber(steps) or 0
|
|
if detents == 0 then return end
|
|
local step = math.max(1, math.min(25, math.floor(tonumber(noctalia.getConfig("scroll_step")) or 5)))
|
|
local current = tonumber(outputVolumeDraft or snapshot.outputVolume) or 0
|
|
outputVolumeDraft = math.max(0, math.min(100, math.floor(current - detents * step + 0.5)))
|
|
render()
|
|
requestId += 1
|
|
noctalia.state.set(COMMAND_KEY, {
|
|
action = "set_output_volume",
|
|
value = outputVolumeDraft,
|
|
requestId = "widget-scroll-" .. tostring(os.time()) .. "-" .. tostring(requestId),
|
|
})
|
|
end
|
|
|
|
noctalia.setUpdateInterval(60000)
|
|
render()
|