local M = { dsp = {} } local GAPS = { left = 6, top = 6, right = 6, bottom = 6 } -- TARGET_POS: -- | 0 | 1 | 2 | -- | 3 | 4 | 5 | -- | 6 | 7 | 8 | local TARGET_POS = 2 local WINDOW_SIZE = { 960, 480 } local WS_HIDE = 'special:dropdown' local TAG_NEW = '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