You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

141 lines
4.1 KiB

local M = { dsp = {} }
local GAPS <const> = { left = 6, top = 6, right = 6, bottom = 6 }
-- TARGET_POS:
-- | 0 | 1 | 2 |
-- | 3 | 4 | 5 |
-- | 6 | 7 | 8 |
local TARGET_POS <const> = 2
local WINDOW_SIZE <const> = { 960, 480 }
local WS_HIDE <const> = 'special:dropdown'
local TAG_NEW <const> = 'dropdownNew'
local windows = {}
local window_pos_cache = {}
local new_id = nil
hl.on('window.open', function (window)
for _, v in ipairs(window.tags) do
if v == TAG_NEW .. '*' and new_id then
windows[new_id] = window
hl.dispatch(hl.dsp.window.tag { tag = '-' .. TAG_NEW .. '*', window = window })
new_id = nil
return
end
end
end)
---@param cmd string
---@return string
local function generate_id(cmd)
local id = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
local id_idx = 1
local id_len = #id
for i = 1, #cmd do
local byteVal = string.byte(cmd, i)
id[id_idx] = id[id_idx] ~ byteVal
id_idx = id_idx % id_len + 1
end
-- human-readable id
--local CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
--local CHARS_LEN = #CHARS
--for i = 1, id_len do
-- local pos = id[i] % CHARS_LEN + 1
-- id[i] = CHARS:sub(pos, pos)
--end
for i = 1, id_len do
id[i] = string.char(id[i])
end
return table.concat(id)
end
---@param absolute boolean
---@return table
local function get_pos(absolute)
local m = hl.get_active_monitor()
if not m then return { 0, 0 } end
local pos = window_pos_cache[m.name]
if not pos then
local r = m.reserved
local dis_pos = absolute and { m.x + r.left, m.y + r.top } or { r.left, r.top }
local dis_width = (m.transform % 2 == 0 and m.width or m.height) // m.scale - r.left - r.right
local dis_height = (m.transform % 2 == 0 and m.height or m.width) // m.scale - r.top - r.bottom
local targetX = TARGET_POS % 3
local targetY = TARGET_POS // 3
pos = {
targetX == 0 and dis_pos[1] + GAPS.left or (
targetX == 1 and dis_pos[1] + (dis_width - WINDOW_SIZE[1]) // 2 or
dis_pos[1] + dis_width - GAPS.right - WINDOW_SIZE[1]
),
targetY == 0 and dis_pos[2] + GAPS.top or (
targetY == 1 and dis_pos[2] + (dis_height - WINDOW_SIZE[2]) // 2 or
dis_pos[2] + dis_height - GAPS.bottom - WINDOW_SIZE[2]
),
}
window_pos_cache[m.name] = pos
end
return pos
end
---@param window HL.Window
local function hide_window(window)
hl.dispatch(hl.dsp.window.pin { action = 'off', window = window })
--hl.dispatch(hl.dsp.window.move { x = POSITION_X, y = HIDDEN_X, relative = false, window = window })
hl.dispatch(hl.dsp.window.move { workspace = WS_HIDE, follow = false, window = window })
end
---@param window HL.Window
local function hide_others_and_show(window)
for _, w in pairs(windows) do
if w == window then
local pos = get_pos(true)
hl.dispatch(hl.dsp.window.move { workspace = 'm+0', follow = true, window = w })
hl.dispatch(hl.dsp.window.move { x = pos[1], y = pos[2], relative = false, window = w })
hl.dispatch(hl.dsp.window.pin { action = 'on', window = w })
else
hide_window(w)
end
end
end
---@param cmd string
function M.dsp.toggle(cmd)
assert(cmd)
return function ()
if new_id then return end
local id = generate_id(cmd)
local window = windows[id]
if window then
if hl.get_window(window) then
if window.pinned and window.monitor == hl.get_active_monitor() then
hide_window(window)
else
hide_others_and_show(window)
end
return
else
windows[id] = nil
end
end
hide_others_and_show(nil)
new_id = id
hl.exec_cmd(cmd, {
float = true,
size = WINDOW_SIZE,
move = get_pos(false),
pin = true,
tag = TAG_NEW,
animation = 'slide',
})
end
end
return M