luca-awesome-dotfiles/home/.config/awesome/libs/awesome_switcher.lua
Hydroxycarbamide 2401cfcee2 Add revelation
Update and add bling task_preview, tag_preview and window_switcher
Add catppuccin-macchiato
Use my weather key
Update keybindings
Update autorun
Fix sliders
2023-03-20 09:51:48 +01:00

532 lines
14 KiB
Lua

local cairo = require("lgi").cairo
local mouse = mouse
local screen = screen
local wibox = require('wibox')
local table = table
local keygrabber = keygrabber
local math = require('math')
local awful = require('awful')
local gears = require("gears")
local timer = gears.timer
local client = client
awful.client = require('awful.client')
local naughty = require("naughty")
local string = string
local tostring = tostring
local tonumber = tonumber
local debug = debug
local pairs = pairs
local unpack = unpack or table.unpack
local surface = cairo.ImageSurface(cairo.Format.RGB24,20,20)
local cr = cairo.Context(surface)
local _M = {}
-- settings
_M.settings = {
preview_box = true,
preview_box_bg = "#18192611",
preview_box_border = "#18192611",
preview_box_fps = 30,
preview_box_delay = 100,
preview_box_title_font = {"Ubuntu","italic","normal"},
preview_box_title_font_size_factor = 0.8,
preview_box_title_color = {202,211,245,1},
client_opacity = true,
client_opacity_value_selected = 1,
client_opacity_value_in_focus = 0.5,
client_opacity_value = 0.5,
cycle_raise_client = true,
}
-- Create a wibox to contain all the client-widgets
_M.preview_wbox = wibox({ width = screen[mouse.screen].geometry.width })
_M.preview_wbox.border_width = 3
_M.preview_wbox.ontop = true
_M.preview_wbox.visible = false
_M.preview_live_timer = timer({ timeout = 1/_M.settings.preview_box_fps })
_M.preview_widgets = {}
_M.altTabTable = {}
_M.altTabIndex = 1
_M.source = string.sub(debug.getinfo(1,'S').source, 2)
_M.path = string.sub(_M.source, 1, string.find(_M.source, "/[^/]*$"))
_M.noicon = _M.path .. "noicon.png"
-- simple function for counting the size of a table
function _M.tableLength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
-- this function returns the list of clients to be shown.
function _M.getClients()
local clients = {}
-- Get focus history for current tag
local s = mouse.screen;
local idx = 0
local c = awful.client.focus.history.get(s, idx)
while c do
table.insert(clients, c)
idx = idx + 1
c = awful.client.focus.history.get(s, idx)
end
-- Minimized clients will not appear in the focus history
-- Find them by cycling through all clients, and adding them to the list
-- if not already there.
-- This will preserve the history AND enable you to focus on minimized clients
local t = s.selected_tag
local all = client.get(s)
for i = 1, #all do
local c = all[i]
local ctags = c:tags();
-- check if the client is on the current tag
local isCurrentTag = false
for j = 1, #ctags do
if t == ctags[j] then
isCurrentTag = true
break
end
end
if isCurrentTag then
-- check if client is already in the history
-- if not, add it
local addToTable = true
for k = 1, #clients do
if clients[k] == c then
addToTable = false
break
end
end
if addToTable then
table.insert(clients, c)
end
end
end
return clients
end
-- here we populate altTabTable using the list of clients taken from
-- _M.getClients(). In case we have altTabTable with some value, the list of the
-- old known clients is restored.
function _M.populateAltTabTable()
local clients = _M.getClients()
if _M.tableLength(_M.altTabTable) then
for ci = 1, #clients do
for ti = 1, #_M.altTabTable do
if _M.altTabTable[ti].client == clients[ci] then
_M.altTabTable[ti].client.opacity = _M.altTabTable[ti].opacity
_M.altTabTable[ti].client.minimized = _M.altTabTable[ti].minimized
break
end
end
end
end
_M.altTabTable = {}
for i = 1, #clients do
table.insert(_M.altTabTable, {
client = clients[i],
minimized = clients[i].minimized,
opacity = clients[i].opacity
})
end
end
-- If the length of list of clients is not equal to the length of altTabTable,
-- we need to repopulate the array and update the UI. This function does this
-- check.
function _M.clientsHaveChanged()
local clients = _M.getClients()
return _M.tableLength(clients) ~= _M.tableLength(_M.altTabTable)
end
function _M.createPreviewText(client)
if client.class then
return " " .. client.class
else
return " " .. client.name
end
end
-- Preview is created here.
function _M.clientOpacity()
if not _M.settings.client_opacity then return end
local opacity = _M.settings.client_opacity_value
if opacity > 1 then opacity = 1 end
for i,data in pairs(_M.altTabTable) do
data.client.opacity = opacity
end
if client.focus == _M.altTabTable[_M.altTabIndex].client then
-- Let's normalize the value up to 1.
local opacityFocusSelected = _M.settings.client_opacity_value_selected + _M.settings.client_opacity_value_in_focus
if opacityFocusSelected > 1 then opacityFocusSelected = 1 end
client.focus.opacity = opacityFocusSelected
else
-- Let's normalize the value up to 1.
local opacityFocus = _M.settings.client_opacity_value_in_focus
if opacityFocus > 1 then opacityFocus = 1 end
local opacitySelected = _M.settings.client_opacity_value_selected
if opacitySelected > 1 then opacitySelected = 1 end
client.focus.opacity = opacityFocus
_M.altTabTable[_M.altTabIndex].client.opacity = opacitySelected
end
end
-- This is called any _M.settings.preview_box_fps milliseconds. In case the list
-- of clients is changed, we need to redraw the whole preview box. Otherwise, a
-- simple widget::updated signal is enough
function _M.updatePreview()
if _M.clientsHaveChanged() then
_M.populateAltTabTable()
_M.preview()
end
for i = 1, #_M.preview_widgets do
_M.preview_widgets[i]:emit_signal("widget::updated")
end
end
function _M.cycle(dir)
-- Switch to next client
_M.altTabIndex = _M.altTabIndex + dir
if _M.altTabIndex > #_M.altTabTable then
_M.altTabIndex = 1 -- wrap around
elseif _M.altTabIndex < 1 then
_M.altTabIndex = #_M.altTabTable -- wrap around
end
_M.updatePreview()
_M.altTabTable[_M.altTabIndex].client.minimized = false
if not _M.settings.preview_box and not _M.settings.client_opacity then
client.focus = _M.altTabTable[_M.altTabIndex].client
end
if _M.settings.client_opacity and _M.preview_wbox.visible then
_M.clientOpacity()
end
if _M.settings.cycle_raise_client == true then
_M.altTabTable[_M.altTabIndex].client:raise()
end
end
function _M.preview()
if not _M.settings.preview_box then return end
-- Apply settings
_M.preview_wbox:set_bg(_M.settings.preview_box_bg)
_M.preview_wbox.border_color = _M.settings.preview_box_border
-- Make the wibox the right size, based on the number of clients
local n = math.max(7, #_M.altTabTable)
local W = screen[mouse.screen].geometry.width -- + 2 * _M.preview_wbox.border_width
local w = W / n -- widget width
local h = w * 0.75 -- widget height
local textboxHeight = w * 0.125
local x = screen[mouse.screen].geometry.x - _M.preview_wbox.border_width
local y = screen[mouse.screen].geometry.y + (screen[mouse.screen].geometry.height - h - textboxHeight) / 2
_M.preview_wbox:geometry({x = x, y = y, width = W, height = h + textboxHeight})
-- create a list that holds the clients to preview, from left to right
local leftRightTab = {}
local leftRightTabToAltTabIndex = {} -- save mapping from leftRightTab to altTabTable as well
local nLeft
local nRight
if #_M.altTabTable == 2 then
nLeft = 0
nRight = 2
else
nLeft = math.floor(#_M.altTabTable / 2)
nRight = math.ceil(#_M.altTabTable / 2)
end
for i = 1, nLeft do
table.insert(leftRightTab, _M.altTabTable[#_M.altTabTable - nLeft + i].client)
table.insert(leftRightTabToAltTabIndex, #_M.altTabTable - nLeft + i)
end
for i = 1, nRight do
table.insert(leftRightTab, _M.altTabTable[i].client)
table.insert(leftRightTabToAltTabIndex, i)
end
-- determine fontsize -> find maximum classname-length
local text, textWidth, textHeight, maxText
local maxTextWidth = 0
local maxTextHeight = 0
local bigFont = textboxHeight / 2
cr:set_font_size(fontSize)
for i = 1, #leftRightTab do
text = _M.createPreviewText(leftRightTab[i])
textWidth = cr:text_extents(text).width
textHeight = cr:text_extents(text).height
if textWidth > maxTextWidth or textHeight > maxTextHeight then
maxTextHeight = textHeight
maxTextWidth = textWidth
maxText = text
end
end
while true do
cr:set_font_size(bigFont)
textWidth = cr:text_extents(maxText).width
textHeight = cr:text_extents(maxText).height
if textWidth < w - textboxHeight and textHeight < textboxHeight then
break
end
bigFont = bigFont - 1
end
local smallFont = bigFont * _M.settings.preview_box_title_font_size_factor
_M.preview_widgets = {}
-- create all the widgets
for i = 1, #leftRightTab do
_M.preview_widgets[i] = wibox.widget.base.make_widget()
_M.preview_widgets[i].fit = function(preview_widget, width, height)
return w, h
end
local c = leftRightTab[i]
_M.preview_widgets[i].draw = function(preview_widget, preview_wbox, cr, width, height)
if width ~= 0 and height ~= 0 then
local a = 0.8
local overlay = 0.6
local fontSize = smallFont
if c == _M.altTabTable[_M.altTabIndex].client then
a = 0.9
overlay = 0
fontSize = bigFont
end
local sx, sy, tx, ty
-- Icons
local icon
if c.icon == nil then
icon = gears.surface(gears.surface.load(_M.noicon))
else
icon = gears.surface(c.icon)
end
local iconboxWidth = 0.9 * textboxHeight
local iconboxHeight = iconboxWidth
-- Titles
cr:select_font_face(unpack(_M.settings.preview_box_title_font))
cr:set_font_face(cr:get_font_face())
cr:set_font_size(fontSize)
text = _M.createPreviewText(c)
textWidth = cr:text_extents(text).width
textHeight = cr:text_extents(text).height
local titleboxWidth = textWidth + iconboxWidth
local titleboxHeight = textboxHeight
-- Draw icons
tx = (w - titleboxWidth) / 2
ty = h
sx = iconboxWidth / icon.width
sy = iconboxHeight / icon.height
cr:translate(tx, ty)
cr:scale(sx, sy)
cr:set_source_surface(icon, 0, 0)
cr:paint()
cr:scale(1/sx, 1/sy)
cr:translate(-tx, -ty)
-- Draw titles
tx = tx + iconboxWidth
ty = h + (textboxHeight + textHeight) / 2
cr:set_source_rgba(unpack(_M.settings.preview_box_title_color))
cr:move_to(tx, ty)
cr:show_text(text)
cr:stroke()
-- Draw previews
local cg = c:geometry()
if cg.width > cg.height then
sx = a * w / cg.width
sy = math.min(sx, a * h / cg.height)
else
sy = a * h / cg.height
sx = math.min(sy, a * h / cg.width)
end
tx = (w - sx * cg.width) / 2
ty = (h - sy * cg.height) / 2
local tmp = gears.surface(c.content)
cr:translate(tx, ty)
cr:scale(sx, sy)
cr:set_source_surface(tmp, 0, 0)
cr:paint()
tmp:finish()
-- Overlays
cr:scale(1/sx, 1/sy)
cr:translate(-tx, -ty)
cr:set_source_rgba(0,0,0,overlay)
cr:rectangle(tx, ty, sx * cg.width, sy * cg.height)
cr:fill()
end
end
-- Add mouse handler
_M.preview_widgets[i]:connect_signal("mouse::enter", function()
_M.cycle(leftRightTabToAltTabIndex[i] - _M.altTabIndex)
end)
end
-- Spacers left and right
local spacer = wibox.widget.base.make_widget()
spacer.fit = function(leftSpacer, width, height)
return (W - w * #_M.altTabTable) / 2, _M.preview_wbox.height
end
spacer.draw = function(preview_widget, preview_wbox, cr, width, height) end
--layout
preview_layout = wibox.layout.fixed.horizontal()
preview_layout:add(spacer)
for i = 1, #leftRightTab do
preview_layout:add(_M.preview_widgets[i])
end
preview_layout:add(spacer)
_M.preview_wbox:set_widget(preview_layout)
end
-- This starts the timer for updating and it shows the preview UI.
function _M.showPreview()
_M.preview_live_timer.timeout = 1 / _M.settings.preview_box_fps
_M.preview_live_timer:connect_signal("timeout", _M.updatePreview)
_M.preview_live_timer:start()
_M.preview()
_M.preview_wbox.visible = true
_M.clientOpacity()
end
function _M.switch(dir, mod_key1, release_key, mod_key2, key_switch)
_M.populateAltTabTable()
if #_M.altTabTable == 0 then
return
elseif #_M.altTabTable == 1 then
_M.altTabTable[1].client.minimized = false
_M.altTabTable[1].client:raise()
return
end
-- reset index
_M.altTabIndex = 1
-- preview delay timer
local previewDelay = _M.settings.preview_box_delay / 1000
_M.previewDelayTimer = timer({timeout = previewDelay})
_M.previewDelayTimer:connect_signal("timeout", function()
_M.previewDelayTimer:stop()
_M.showPreview()
end)
_M.previewDelayTimer:start()
-- Now that we have collected all windows, we should run a keygrabber
-- as long as the user is alt-tabbing:
keygrabber.run(
function (mod, key, event)
-- Stop alt-tabbing when the alt-key is released
if gears.table.hasitem(mod, mod_key1) then
if (key == release_key or key == "Escape") and event == "release" then
if _M.preview_wbox.visible == true then
_M.preview_wbox.visible = false
_M.preview_live_timer:stop()
else
_M.previewDelayTimer:stop()
end
if key == "Escape" then
for i = 1, #_M.altTabTable do
_M.altTabTable[i].client.opacity = _M.altTabTable[i].opacity
_M.altTabTable[i].client.minimized = _M.altTabTable[i].minimized
end
else
-- Raise clients in order to restore history
local c
for i = 1, _M.altTabIndex - 1 do
c = _M.altTabTable[_M.altTabIndex - i].client
if not _M.altTabTable[i].minimized then
c:raise()
client.focus = c
end
end
-- raise chosen client on top of all
c = _M.altTabTable[_M.altTabIndex].client
c:raise()
client.focus = c
-- restore minimized clients
for i = 1, #_M.altTabTable do
if i ~= _M.altTabIndex and _M.altTabTable[i].minimized then
_M.altTabTable[i].client.minimized = true
end
_M.altTabTable[i].client.opacity = _M.altTabTable[i].opacity
end
end
keygrabber.stop()
elseif key == key_switch and event == "press" then
if gears.table.hasitem(mod, mod_key2) then
-- Move to previous client on Shift-Tab
_M.cycle(-1)
else
-- Move to next client on each Tab-press
_M.cycle( 1)
end
end
end
end
)
-- switch to next client
_M.cycle(dir)
end -- function altTab
return {switch = _M.switch, settings = _M.settings}