New Setup 📦

This commit is contained in:
Luca 2023-02-05 05:02:49 +01:00
parent d16174b447
commit 415dbd08a1
10194 changed files with 1368647 additions and 4 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,656 @@
---------------------------------------------------------------------------
--- Modified Prompt module.
-- @author Julien Danjou <julien@danjou.info>
-- @copyright 2008 Julien Danjou
---------------------------------------------------------------------------
local akey = require("awful.key")
local keygrabber = require("awful.keygrabber")
local gobject = require("gears.object")
local gdebug = require('gears.debug')
local gtable = require("gears.table")
local gcolor = require("gears.color")
local gstring = require("gears.string")
local gfs = require("gears.filesystem")
local wibox = require("wibox")
local beautiful = require("beautiful")
local io = io
local table = table
local math = math
local ipairs = ipairs
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
local capi = { selection = selection }
local prompt = { mt = {} }
--- Private data
local data = {}
data.history = {}
local function itera(inc,a, i)
i = i + inc
local v = a[i]
if v then return i,v end
end
local function history_check_load(id, max)
if id and id ~= "" and not data.history[id] then
data.history[id] = { max = 50, table = {} }
if max then
data.history[id].max = max
end
local f = io.open(id, "r")
if not f then return end
-- Read history file
for line in f:lines() do
if gtable.hasitem(data.history[id].table, line) == nil then
table.insert(data.history[id].table, line)
if #data.history[id].table >= data.history[id].max then
break
end
end
end
f:close()
end
end
local function is_word_char(c)
if string.find(c, "[{[(,.:;_-+=@/ ]") then
return false
else
return true
end
end
local function cword_start(s, pos)
local i = pos
if i > 1 then
i = i - 1
end
while i >= 1 and not is_word_char(s:sub(i, i)) do
i = i - 1
end
while i >= 1 and is_word_char(s:sub(i, i)) do
i = i - 1
end
if i <= #s then
i = i + 1
end
return i
end
local function cword_end(s, pos)
local i = pos
while i <= #s and not is_word_char(s:sub(i, i)) do
i = i + 1
end
while i <= #s and is_word_char(s:sub(i, i)) do
i = i + 1
end
return i
end
local function history_save(id)
if data.history[id] then
gfs.make_parent_directories(id)
local f = io.open(id, "w")
if not f then
gdebug.print_warning("Failed to write the history to "..id)
return
end
for i = 1, math.min(#data.history[id].table, data.history[id].max) do
f:write(data.history[id].table[i] .. "\n")
end
f:close()
end
end
local function history_items(id)
if data.history[id] then
return #data.history[id].table
else
return -1
end
end
local function history_add(id, command)
if data.history[id] and command ~= "" then
local index = gtable.hasitem(data.history[id].table, command)
if index == nil then
table.insert(data.history[id].table, command)
-- Do not exceed our max_cmd
if #data.history[id].table > data.history[id].max then
table.remove(data.history[id].table, 1)
end
history_save(id)
else
-- Bump this command to the end of history
table.remove(data.history[id].table, index)
table.insert(data.history[id].table, command)
history_save(id)
end
end
end
local function have_multibyte_char_at(text, position)
return text:sub(position, position):wlen() == -1
end
local function prompt_text_with_cursor(args)
local char, spacer, text_start, text_end, ret
local text = args.text or ""
local _prompt = args.prompt or ""
local underline = args.cursor_ul or "none"
if args.select_all then
if #text == 0 then char = " " else char = gstring.xml_escape(text) end
spacer = " "
text_start = ""
text_end = ""
elseif #text < args.cursor_pos then
char = " "
spacer = ""
text_start = gstring.xml_escape(text)
text_end = ""
else
local offset = 0
if have_multibyte_char_at(text, args.cursor_pos) then
offset = 1
end
char = gstring.xml_escape(text:sub(args.cursor_pos, args.cursor_pos + offset))
spacer = " "
text_start = gstring.xml_escape(text:sub(1, args.cursor_pos - 1))
text_end = gstring.xml_escape(text:sub(args.cursor_pos + 1 + offset))
end
local cursor_color = gcolor.ensure_pango_color(args.cursor_color)
local text_color = gcolor.ensure_pango_color(args.text_color)
if args.highlighter then
text_start, text_end = args.highlighter(text_start, text_end)
end
ret = _prompt .. text_start .. "<span background=\"" .. cursor_color ..
"\" foreground=\"" .. text_color .. "\" underline=\"" .. underline ..
"\">" .. char .. "</span>" .. text_end .. spacer
return ret
end
local function update(self)
self.textbox:set_font(self.font)
self.textbox:set_markup(prompt_text_with_cursor{
text = self.command, text_color = self.fg_cursor, cursor_color = self.bg_cursor,
cursor_pos = self._private_cur_pos, cursor_ul = self.ul_cursor, select_all = self.select_all,
prompt = self.prompt, highlighter = self.highlighter })
end
local function exec(self, cb, command_to_history)
self.textbox:set_markup("")
history_add(self.history_path, command_to_history)
keygrabber.stop(self._private.grabber)
if cb then cb(self.command) end
if self.done_callback then
self.done_callback()
end
end
function prompt:start()
-- The cursor position
if self.reset_on_stop == true or self._private_cur_pos == nil then
self._private_cur_pos = (self.select_all and 1) or self.text:wlen() + 1
end
if self.reset_on_stop == true then self.text = "" self.command = "" end
self.textbox:set_font(self.font)
self.textbox:set_markup(prompt_text_with_cursor{
text = self.reset_on_stop and self.text or self.command, text_color = self.fg_cursor, cursor_color = self.bg_cursor,
cursor_pos = self._private_cur_pos, cursor_ul = self.ul_cursor, select_all = self.select_all,
prompt = self.prompt, highlighter = self.highlighter})
self._private.search_term = nil
history_check_load(self.history_path, self.history_max)
local history_index = history_items(self.history_path) + 1
-- The completion element to use on completion request.
local ncomp = 1
local command_before_comp
local cur_pos_before_comp
self._private.grabber = keygrabber.run(function(modifiers, key, event)
-- Convert index array to hash table
local mod = {}
for _, v in ipairs(modifiers) do mod[v] = true end
if event ~= "press" then
if self.keyreleased_callback then
self.keyreleased_callback(mod, key, self.command)
end
return
end
-- Call the user specified callback. If it returns true as
-- the first result then return from the function. Treat the
-- second and third results as a new command and new prompt
-- to be set (if provided)
if self.keypressed_callback then
local user_catched, new_command, new_prompt =
self.keypressed_callback(mod, key, self.command)
if new_command or new_prompt then
if new_command then
self.command = new_command
end
if new_prompt then
self.prompt = new_prompt
end
update(self)
end
if user_catched then
if self.changed_callback then
self.changed_callback(self.command)
end
return
end
end
local filtered_modifiers = {}
-- User defined cases
if self.hooks[key] then
-- Remove caps and num lock
for _, m in ipairs(modifiers) do
if not gtable.hasitem(akey.ignore_modifiers, m) then
table.insert(filtered_modifiers, m)
end
end
for _,v in ipairs(self.hooks[key]) do
if #filtered_modifiers == #v[1] then
local match = true
for _,v2 in ipairs(v[1]) do
match = match and mod[v2]
end
if match then
local cb
local ret, quit = v[3](self.command)
local original_command = self.command
-- Support both a "simple" and a "complex" way to
-- control if the prompt should quit.
quit = quit == nil and (ret ~= true) or (quit~=false)
-- Allow the callback to change the command
self.command = (ret ~= true) and ret or self.command
-- Quit by default, but allow it to be disabled
if ret and type(ret) ~= "boolean" then
cb = self.exe_callback
if not quit then
self._private_cur_pos = ret:wlen() + 1
update(self)
end
elseif quit then
-- No callback.
cb = function() end
end
-- Execute the callback
if cb then
exec(self, cb, original_command)
end
return
end
end
end
end
-- Get out cases
if (mod.Control and (key == "c" or key == "g"))
or (not mod.Control and key == "Escape") then
self:stop()
return false
elseif (mod.Control and (key == "j" or key == "m"))
-- or (not mod.Control and key == "Return")
-- or (not mod.Control and key == "KP_Enter")
then
exec(self, self.exe_callback, self.command)
-- We already unregistered ourselves so we don't want to return
-- true, otherwise we may unregister someone else.
return
end
-- Control cases
if mod.Control then
self.select_all = nil
if key == "v" then
local selection = capi.selection()
if selection then
-- Remove \n
local n = selection:find("\n")
if n then
selection = selection:sub(1, n - 1)
end
self.command = self.command:sub(1, self._private_cur_pos - 1) .. selection .. self.command:sub(self._private_cur_pos)
self._private_cur_pos = self._private_cur_pos + #selection
end
elseif key == "a" then
self._private_cur_pos = 1
elseif key == "b" then
if self._private_cur_pos > 1 then
self._private_cur_pos = self._private_cur_pos - 1
if have_multibyte_char_at(self.command, self._private_cur_pos) then
self._private_cur_pos = self._private_cur_pos - 1
end
end
elseif key == "d" then
if self._private_cur_pos <= #self.command then
self.command = self.command:sub(1, self._private_cur_pos - 1) .. self.command:sub(self._private_cur_pos + 1)
end
elseif key == "p" then
if history_index > 1 then
history_index = history_index - 1
self.command = data.history[self.history_path].table[history_index]
self._private_cur_pos = #self.command + 2
end
elseif key == "n" then
if history_index < history_items(self.history_path) then
history_index = history_index + 1
self.command = data.history[self.history_path].table[history_index]
self._private_cur_pos = #self.command + 2
elseif history_index == history_items(self.history_path) then
history_index = history_index + 1
self.command = ""
self._private_cur_pos = 1
end
elseif key == "e" then
self._private_cur_pos = #self.command + 1
elseif key == "r" then
self._private.search_term = self._private.search_term or self.command:sub(1, self._private_cur_pos - 1)
for i,v in (function(a,i) return itera(-1,a,i) end), data.history[self.history_path].table, history_index do
if v:find(self._private.search_term,1,true) ~= nil then
self.command=v
history_index=i
self._private_cur_pos=#self.command+1
break
end
end
elseif key == "s" then
self._private.search_term = self._private.search_term or self.command:sub(1, self._private_cur_pos - 1)
for i,v in (function(a,i) return itera(1,a,i) end), data.history[self.history_path].table, history_index do
if v:find(self._private.search_term,1,true) ~= nil then
self.command=v
history_index=i
self._private_cur_pos=#self.command+1
break
end
end
elseif key == "f" then
if self._private_cur_pos <= #self.command then
if have_multibyte_char_at(self.command, self._private_cur_pos) then
self._private_cur_pos = self._private_cur_pos + 2
else
self._private_cur_pos = self._private_cur_pos + 1
end
end
elseif key == "h" then
if self._private_cur_pos > 1 then
local offset = 0
if have_multibyte_char_at(self.command, self._private_cur_pos - 1) then
offset = 1
end
self.command = self.command:sub(1, self._private_cur_pos - 2 - offset) .. self.command:sub(self._private_cur_pos)
self._private_cur_pos = self._private_cur_pos - 1 - offset
end
elseif key == "k" then
self.command = self.command:sub(1, self._private_cur_pos - 1)
elseif key == "u" then
self.command = self.command:sub(self._private_cur_pos, #self.command)
self._private_cur_pos = 1
elseif key == "Prior" then
self._private.search_term = self.command:sub(1, self._private_cur_pos - 1) or ""
for i,v in (function(a,i) return itera(-1,a,i) end), data.history[self.history_path].table, history_index do
if v:find(self._private.search_term,1,true) == 1 then
self.command=v
history_index=i
break
end
end
elseif key == "Next" then
self._private.search_term = self.command:sub(1, self._private_cur_pos - 1) or ""
for i,v in (function(a,i) return itera(1,a,i) end), data.history[self.history_path].table, history_index do
if v:find(self._private.search_term,1,true) == 1 then
self.command=v
history_index=i
break
end
end
elseif key == "w" or key == "BackSpace" then
local wstart = 1
local wend = 1
local cword_start_pos = 1
local cword_end_pos = 1
while wend < self._private_cur_pos do
wend = self.command:find("[{[(,.:;_-+=@/ ]", wstart)
if not wend then wend = #self.command + 1 end
if self._private_cur_pos >= wstart and self._private_cur_pos <= wend + 1 then
cword_start_pos = wstart
cword_end_pos = self._private_cur_pos - 1
break
end
wstart = wend + 1
end
self.command = self.command:sub(1, cword_start_pos - 1) .. self.command:sub(cword_end_pos + 1)
self._private_cur_pos = cword_start_pos
elseif key == "Delete" then
-- delete from history only if:
-- we are not dealing with a new command
-- the user has not edited an existing entry
if self.command == data.history[self.history_path].table[history_index] then
table.remove(data.history[self.history_path].table, history_index)
if history_index <= history_items(self.history_path) then
self.command = data.history[self.history_path].table[history_index]
self._private_cur_pos = #self.command + 2
elseif history_index > 1 then
history_index = history_index - 1
self.command = data.history[self.history_path].table[history_index]
self._private_cur_pos = #self.command + 2
else
self.command = ""
self._private_cur_pos = 1
end
end
end
elseif mod.Mod1 or mod.Mod3 then
if key == "b" then
self._private_cur_pos = cword_start(self.command, self._private_cur_pos)
elseif key == "f" then
self._private_cur_pos = cword_end(self.command, self._private_cur_pos)
elseif key == "d" then
self.command = self.command:sub(1, self._private_cur_pos - 1) .. self.command:sub(cword_end(self.command, self._private_cur_pos))
elseif key == "BackSpace" then
local wstart = cword_start(self.command, self._private_cur_pos)
self.command = self.command:sub(1, wstart - 1) .. self.command:sub(self._private_cur_pos)
self._private_cur_pos = wstart
end
else
if self.completion_callback then
if key == "Tab" or key == "ISO_Left_Tab" then
if key == "ISO_Left_Tab" or mod.Shift then
if ncomp == 1 then return end
if ncomp == 2 then
self.command = command_before_comp
self.textbox:set_font(self.font)
self.textbox:set_markup(prompt_text_with_cursor{
text = command_before_comp, text_color = self.fg_cursor, cursor_color = self.bg_cursor,
cursor_pos = self._private_cur_pos, cursor_ul = self.ul_cursor, select_all = self.select_all,
prompt = self.prompt })
self._private_cur_pos = cur_pos_before_comp
ncomp = 1
return
end
ncomp = ncomp - 2
elseif ncomp == 1 then
command_before_comp = self.command
cur_pos_before_comp = self._private_cur_pos
end
local matches
self.command, self._private_cur_pos, matches = self.completion_callback(command_before_comp, cur_pos_before_comp, ncomp)
ncomp = ncomp + 1
key = ""
-- execute if only one match found and autoexec flag set
if matches and #matches == 1 and args.autoexec then
exec(self, self.exe_callback)
return
end
elseif key ~= "Shift_L" and key ~= "Shift_R" then
ncomp = 1
end
end
-- Typin cases
if mod.Shift and key == "Insert" then
local selection = capi.selection()
if selection then
-- Remove \n
local n = selection:find("\n")
if n then
selection = selection:sub(1, n - 1)
end
self.command = self.command:sub(1, self._private_cur_pos - 1) .. selection .. self.command:sub(self._private_cur_pos)
self._private_cur_pos = self._private_cur_pos + #selection
end
elseif key == "Home" then
self._private_cur_pos = 1
elseif key == "End" then
self._private_cur_pos = #self.command + 1
elseif key == "BackSpace" then
if self._private_cur_pos > 1 then
local offset = 0
if have_multibyte_char_at(self.command, self._private_cur_pos - 1) then
offset = 1
end
self.command = self.command:sub(1, self._private_cur_pos - 2 - offset) .. self.command:sub(self._private_cur_pos)
self._private_cur_pos = self._private_cur_pos - 1 - offset
end
elseif key == "Delete" then
self.command = self.command:sub(1, self._private_cur_pos - 1) .. self.command:sub(self._private_cur_pos + 1)
elseif key == "Left" then
self._private_cur_pos = self._private_cur_pos - 1
elseif key == "Right" then
self._private_cur_pos = self._private_cur_pos + 1
elseif key == "Prior" then
if history_index > 1 then
history_index = history_index - 1
self.command = data.history[self.history_path].table[history_index]
self._private_cur_pos = #self.command + 2
end
elseif key == "Next" then
if history_index < history_items(self.history_path) then
history_index = history_index + 1
self.command = data.history[self.history_path].table[history_index]
self._private_cur_pos = #self.command + 2
elseif history_index == history_items(self.history_path) then
history_index = history_index + 1
self.command = ""
self._private_cur_pos = 1
end
else
-- wlen() is UTF-8 aware but #key is not,
-- so check that we have one UTF-8 char but advance the cursor of # position
if key:wlen() == 1 then
if self.select_all then self.command = "" end
self.command = self.command:sub(1, self._private_cur_pos - 1) .. key .. self.command:sub(self._private_cur_pos)
self._private_cur_pos = self._private_cur_pos + #key
end
end
if self._private_cur_pos < 1 then
self._private_cur_pos = 1
elseif self._private_cur_pos > #self.command + 1 then
self._private_cur_pos = #self.command + 1
end
self.select_all = nil
end
update(self)
if self.changed_callback then
self.changed_callback(self.command)
end
end)
end
function prompt:stop()
keygrabber.stop(self._private.grabber)
history_save(self.history_path)
if self.done_callback then self.done_callback() end
return false
end
local function new(args)
args = args or {}
args.command = args.text or ""
args.prompt = args.prompt or ""
args.text = args.text or ""
args.font = args.font or beautiful.prompt_font or beautiful.font
args.bg_cursor = args.bg_cursor or beautiful.prompt_bg_cursor or beautiful.bg_focus or "white"
args.fg_cursor = args.fg_cursor or beautiful.prompt_fg_cursor or beautiful.fg_focus or "black"
args.ul_cursor = args.ul_cursor or nil
args.reset_on_stop = args.reset_on_stop == nil and true or args.reset_on_stop
args.select_all = args.select_all or nil
args.highlighter = args.highlighter or nil
args.hooks = args.hooks or {}
args.keypressed_callback = args.keypressed_callback or nil
args.changed_callback = args.changed_callback or nil
args.done_callback = args.done_callback or nil
args.history_max = args.history_max or nil
args.history_path = args.history_path or nil
args.completion_callback = args.completion_callback or nil
args.exe_callback = args.exe_callback or nil
args.textbox = args.textbox or wibox.widget.textbox()
-- Build the hook map
local hooks = {}
for _,v in ipairs(args.hooks) do
if #v == 3 then
local _,key,callback = unpack(v)
if type(callback) == "function" then
hooks[key] = hooks[key] or {}
hooks[key][#hooks[key]+1] = v
else
gdebug.print_warning("The hook's 3rd parameter has to be a function.")
end
else
gdebug.print_warning("The hook has to have 3 parameters.")
end
end
args.hooks = hooks
local ret = gobject({})
ret._private = {}
gtable.crush(ret, prompt)
gtable.crush(ret, args)
return ret
end
function prompt.mt:__call(...)
return new(...)
end
return setmetatable(prompt, prompt.mt)

View file

@ -0,0 +1,7 @@
return {
tag_preview = require(... .. ".tag_preview"),
task_preview = require(... .. ".task_preview"),
window_switcher = require(... .. ".window_switcher"),
tabbed_misc = require(... .. ".tabbed_misc"),
app_launcher = require(... .. ".app_launcher"),
}

View file

@ -0,0 +1,57 @@
local awful = require("awful")
local gears = require("gears")
local wibox = require("wibox")
local beautiful = require("beautiful")
local bg_normal = beautiful.tabbar_bg_normal or beautiful.bg_normal or "#ffffff"
local fg_normal = beautiful.tabbar_fg_normal or beautiful.fg_normal or "#000000"
local bg_focus = beautiful.tabbar_bg_focus or beautiful.bg_focus or "#000000"
local fg_focus = beautiful.tabbar_fg_focus or beautiful.fg_focus or "#ffffff"
local bg_focus_inactive = beautiful.tabbar_bg_focus_inactive or bg_focus
local fg_focus_inactive = beautiful.tabbar_fg_focus_inactive or fg_focus
local bg_normal_inactive = beautiful.tabbar_bg_normal_inactive or bg_normal
local fg_normal_inactive = beautiful.tabbar_fg_normal_inactive or fg_normal
local font = beautiful.tabbar_font or beautiful.font or "Hack 15"
local size = beautiful.tabbar_size or 40
local position = beautiful.tabbar_position or "bottom"
local function create(c, focused_bool, buttons, inactive_bool)
local bg_temp = inactive_bool and bg_normal_inactive or bg_normal
local fg_temp = inactive_bool and fg_normal_inactive or fg_normal
if focused_bool then
bg_temp = inactive_bool and bg_focus_inactive or bg_focus
fg_temp = inactive_bool and fg_focus_inactive or fg_focus
end
local wid_temp = wibox.widget({
{
{
awful.widget.clienticon(c),
left = 10,
right = 10,
bottom = 10,
top = 10,
widget = wibox.container.margin(),
},
widget = wibox.container.place(),
},
buttons = buttons,
bg = bg_temp,
widget = wibox.container.background(),
})
return wid_temp
end
local layout = wibox.layout.fixed.horizontal
if position == "left" or position == "right" then
layout = wibox.layout.fixed.vertical
end
return {
layout = layout,
create = create,
position = position,
size = size,
bg_normal = bg_normal,
bg_focus = bg_normal,
}

View file

@ -0,0 +1,60 @@
local gears = require("gears")
local wibox = require("wibox")
local beautiful = require("beautiful")
local bg_normal = beautiful.tabbar_bg_normal or beautiful.bg_normal or "#ffffff"
local fg_normal = beautiful.tabbar_fg_normal or beautiful.fg_normal or "#000000"
local bg_focus = beautiful.tabbar_bg_focus or beautiful.bg_focus or "#000000"
local fg_focus = beautiful.tabbar_fg_focus or beautiful.fg_focus or "#ffffff"
local bg_focus_inactive = beautiful.tabbar_bg_focus_inactive or bg_focus
local fg_focus_inactive = beautiful.tabbar_fg_focus_inactive or fg_focus
local bg_normal_inactive = beautiful.tabbar_bg_normal_inactive or bg_normal
local fg_normal_inactive = beautiful.tabbar_fg_normal_inactive or fg_normal
local font = beautiful.tabbar_font or beautiful.font or "Hack 15"
local size = beautiful.tabbar_size or 20
local position = beautiful.tabbar_position or "top"
local function create(c, focused_bool, buttons, inactive_bool)
local flexlist = wibox.layout.flex.horizontal()
local title_temp = c.name or c.class or "-"
local bg_temp = inactive_bool and bg_normal_inactive or bg_normal
local fg_temp = inactive_bool and fg_normal_inactive or fg_normal
if focused_bool then
bg_temp = inactive_bool and bg_focus_inactive or bg_focus
fg_temp = inactive_bool and fg_focus_inactive or fg_focus
end
local text_temp = wibox.widget.textbox()
text_temp.align = "center"
text_temp.valign = "center"
text_temp.font = font
text_temp.markup = "<span foreground='"
.. fg_temp
.. "'>"
.. title_temp
.. "</span>"
c:connect_signal("property::name", function(_)
local title_temp = c.name or c.class or "-"
text_temp.markup = "<span foreground='"
.. fg_temp
.. "'>"
.. title_temp
.. "</span>"
end)
local wid_temp = wibox.widget({
text_temp,
buttons = buttons,
bg = bg_temp,
widget = wibox.container.background(),
})
return wid_temp
end
return {
layout = wibox.layout.flex.horizontal,
create = create,
position = position,
size = size,
bg_normal = bg_normal,
bg_focus = bg_focus,
}

View file

@ -0,0 +1,271 @@
local awful = require("awful")
local gears = require("gears")
local wibox = require("wibox")
local beautiful = require("beautiful")
local xresources = require("beautiful.xresources")
local dpi = xresources.apply_dpi
local helpers = require(tostring(...):match(".*bling") .. ".helpers")
local bg_normal = beautiful.tabbar_bg_normal or beautiful.bg_normal or "#ffffff"
local fg_normal = beautiful.tabbar_fg_normal or beautiful.fg_normal or "#000000"
local bg_focus = beautiful.tabbar_bg_focus or beautiful.bg_focus or "#000000"
local fg_focus = beautiful.tabbar_fg_focus or beautiful.fg_focus or "#ffffff"
local bg_focus_inactive = beautiful.tabbar_bg_focus_inactive or bg_focus
local fg_focus_inactive = beautiful.tabbar_fg_focus_inactive or fg_focus
local bg_normal_inactive = beautiful.tabbar_bg_normal_inactive or bg_normal
local fg_normal_inactive = beautiful.tabbar_fg_normal_inactive or fg_normal
local font = beautiful.tabbar_font or beautiful.font or "Hack 15"
local size = beautiful.tabbar_size or dpi(40)
local border_radius = beautiful.mstab_border_radius
or beautiful.border_radius
or 6
local position = beautiful.tabbar_position or "top"
local close_color = beautiful.tabbar_color_close
or beautiful.xcolor1
or "#f9929b"
local min_color = beautiful.tabbar_color_min or beautiful.xcolor3 or "#fbdf90"
local float_color = beautiful.tabbar_color_float
or beautiful.xcolor5
or "#ccaced"
-- Helper to create buttons
local function create_title_button(c, color_focus, color_unfocus)
local tb_color = wibox.widget({
wibox.widget.textbox(),
forced_width = dpi(8),
forced_height = dpi(8),
bg = color_focus,
shape = gears.shape.circle,
widget = wibox.container.background,
})
local tb = wibox.widget({
tb_color,
width = dpi(25),
height = dpi(25),
strategy = "min",
layout = wibox.layout.constraint,
})
local function update()
if client.focus == c then
tb_color.bg = color_focus
else
tb_color.bg = color_unfocus
end
end
update()
c:connect_signal("focus", update)
c:connect_signal("unfocus", update)
tb:connect_signal("mouse::enter", function()
tb_color.bg = color_focus .. "70"
end)
tb:connect_signal("mouse::leave", function()
tb_color.bg = color_focus
end)
tb.visible = true
return tb
end
local function create(c, focused_bool, buttons, inactive_bool)
-- local flexlist = wibox.layout.flex.horizontal()
local title_temp = c.name or c.class or "-"
local bg_temp = inactive_bool and bg_normal_inactive or bg_normal
local fg_temp = inactive_bool and fg_normal_inactive or fg_normal
if focused_bool then
bg_temp = inactive_bool and bg_focus_inactive or bg_focus
fg_temp = inactive_bool and fg_focus_inactive or fg_focus
end
local text_temp = wibox.widget.textbox()
text_temp.align = "center"
text_temp.valign = "center"
text_temp.font = font
text_temp.markup = "<span foreground='"
.. fg_temp
.. "'>"
.. title_temp
.. "</span>"
c:connect_signal("property::name", function(_)
local title_temp = c.name or c.class or "-"
text_temp.markup = "<span foreground='"
.. fg_temp
.. "'>"
.. title_temp
.. "</span>"
end)
local tab_content = wibox.widget({
{
awful.widget.clienticon(c),
top = dpi(6),
left = dpi(15),
bottom = dpi(6),
widget = wibox.container.margin,
},
text_temp,
nill,
expand = "none",
layout = wibox.layout.align.horizontal,
})
local close = create_title_button(c, close_color, bg_normal)
close:connect_signal("button::press", function()
c:kill()
end)
local floating = create_title_button(c, float_color, bg_normal)
floating:connect_signal("button::press", function()
c.floating = not c.floating
end)
local min = create_title_button(c, min_color, bg_normal)
min:connect_signal("button::press", function()
c.minimized = true
end)
if focused_bool then
tab_content = wibox.widget({
{
awful.widget.clienticon(c),
top = dpi(10),
left = dpi(15),
bottom = dpi(10),
widget = wibox.container.margin,
},
text_temp,
{
{ min, floating, close, layout = wibox.layout.fixed.horizontal },
top = dpi(10),
right = dpi(10),
bottom = dpi(10),
widget = wibox.container.margin,
},
expand = "none",
layout = wibox.layout.align.horizontal,
})
end
local main_content = nil
local left_shape = nil
local right_shape = nil
if position == "top" then
main_content = wibox.widget({
{
tab_content,
bg = bg_temp,
shape = helpers.shape.prrect(
border_radius,
true,
true,
false,
false
),
widget = wibox.container.background,
},
top = dpi(8),
widget = wibox.container.margin,
})
left_shape = helpers.shape.prrect(
border_radius,
false,
false,
true,
false
)
right_shape = helpers.shape.prrect(
border_radius,
false,
false,
false,
true
)
else
main_content = wibox.widget({
{
tab_content,
bg = bg_temp,
shape = helpers.shape.prrect(
border_radius,
false,
false,
true,
true
),
widget = wibox.container.background,
},
bottom = dpi(8),
widget = wibox.container.margin,
})
left_shape = helpers.shape.prrect(
border_radius,
false,
true,
false,
false
)
right_shape = helpers.shape.prrect(
border_radius,
true,
false,
false,
false
)
end
local wid_temp = wibox.widget({
buttons = buttons,
{
{
{
wibox.widget.textbox(),
bg = bg_normal,
shape = left_shape,
widget = wibox.container.background,
},
bg = bg_temp,
shape = gears.rectangle,
widget = wibox.container.background,
},
width = border_radius + (border_radius / 2),
height = size,
strategy = "exact",
layout = wibox.layout.constraint,
},
main_content,
{
{
{
wibox.widget.textbox(),
bg = bg_normal,
shape = right_shape,
widget = wibox.container.background,
},
bg = bg_temp,
shape = gears.rectangle,
widget = wibox.container.background,
},
width = border_radius + (border_radius / 2),
height = size,
strategy = "exact",
layout = wibox.layout.constraint,
},
layout = wibox.layout.align.horizontal,
})
return wid_temp
end
return {
layout = wibox.layout.flex.horizontal,
create = create,
position = position,
size = size,
bg_normal = bg_normal,
bg_focus = bg_focus,
}

View file

@ -0,0 +1,81 @@
local awful = require("awful")
local gears = require("gears")
local wibox = require("wibox")
local gcolor = require("gears.color")
local beautiful = require("beautiful")
local bg_normal = beautiful.tabbar_bg_normal or beautiful.bg_normal or "#ffffff"
local fg_normal = beautiful.tabbar_fg_normal or beautiful.fg_normal or "#000000"
local bg_focus = beautiful.tabbar_bg_focus or beautiful.bg_focus or "#000000"
local fg_focus = beautiful.tabbar_fg_focus or beautiful.fg_focus or "#ffffff"
local bg_focus_inactive = beautiful.tabbar_bg_focus_inactive or bg_focus
local fg_focus_inactive = beautiful.tabbar_fg_focus_inactive or fg_focus
local bg_normal_inactive = beautiful.tabbar_bg_normal_inactive or bg_normal
local fg_normal_inactive = beautiful.tabbar_fg_normal_inactive or fg_normal
local font = beautiful.tabbar_font or beautiful.font or "Hack 15"
local size = beautiful.tabbar_size or 20
local position = beautiful.tabbar_position or "top"
local function create(c, focused_bool, buttons, inactive_bool)
local bg_temp = inactive_bool and bg_normal_inactive or bg_normal
local fg_temp = inactive_bool and fg_normal_inactive or fg_normal
if focused_bool then
bg_temp = inactive_bool and bg_focus_inactive or bg_focus
fg_temp = inactive_bool and fg_focus_inactive or fg_focus
end
local wid_temp = wibox.widget({
{
{ -- Left
wibox.widget.base.make_widget(
awful.titlebar.widget.iconwidget(c)
),
buttons = buttons,
layout = wibox.layout.fixed.horizontal,
},
{ -- Title
wibox.widget.base.make_widget(
awful.titlebar.widget.titlewidget(c)
),
buttons = buttons,
widget = wibox.container.place,
},
{ -- Right
focused_bool and wibox.widget.base.make_widget(
awful.titlebar.widget.floatingbutton(c)
) or nil,
focused_bool and wibox.widget.base.make_widget(
awful.titlebar.widget.stickybutton(c)
) or nil,
focused_bool and wibox.widget.base.make_widget(
awful.titlebar.widget.ontopbutton(c)
) or nil,
focused_bool and wibox.widget.base.make_widget(
awful.titlebar.widget.maximizedbutton(c)
) or nil,
focused_bool and wibox.widget.base.make_widget(
awful.titlebar.widget.minimizebutton(c)
) or nil,
focused_bool and wibox.widget.base.make_widget(
awful.titlebar.widget.closebutton(c)
) or nil,
layout = wibox.layout.fixed.horizontal,
},
layout = wibox.layout.align.horizontal,
},
bg = bg_temp,
fg = fg_temp,
widget = wibox.container.background,
})
return wid_temp
end
return {
layout = wibox.layout.flex.horizontal,
create = create,
position = position,
size = size,
bg_normal = bg_normal,
bg_focus = bg_focus,
}

View file

@ -0,0 +1,51 @@
local wibox = require("wibox")
local awful = require("awful")
local gears = require("gears")
local beautiful = require("beautiful")
local dpi = require("beautiful.xresources").apply_dpi
local function tabobj_support(self, c, index, clients)
-- Self is the background widget in this context
if not c.bling_tabbed and #c.bling_tabbed.clients > 1 then
return
end
local group = c.bling_tabbed
-- TODO: Allow customization here
local layout_v = wibox.widget {
vertical_spacing = dpi(2),
horizontal_spacing = dpi(2),
layout = wibox.layout.grid.horizontal,
forced_num_rows = 2,
forced_num_cols = 2,
homogeneous = true
}
local wrapper = wibox.widget({
layout_v,
id = "click_role",
widget = wibox.container.margin,
margins = dpi(5),
})
-- To get the ball rolling.
for idx, c in ipairs(group.clients) do
if not (c and c.icon) then goto skip end
-- Add to the last layout
layout_v:add(wibox.widget {
{
widget = awful.widget.clienticon,
client = c
},
widget = wibox.container.constraint,
width = dpi(24),
height = dpi(24)
})
::skip::
end
self.widget = wrapper
end
return tabobj_support

View file

@ -0,0 +1,9 @@
return {
titlebar_indicator = require(
tostring(...):match(".*bling")
.. ".widget.tabbed_misc.titlebar_indicator"
),
custom_tasklist = require(
tostring(...):match(".*bling") .. ".widget.tabbed_misc.custom_tasklist"
),
}

View file

@ -0,0 +1,133 @@
local wibox = require("wibox")
local awful = require("awful")
local gears = require("gears")
local beautiful = require("beautiful")
local dpi = require("beautiful.xresources").apply_dpi
local tabbed_module = require(
tostring(...):match(".*bling") .. ".module.tabbed"
)
-- Just check if a table contains a value.
local function tbl_contains(tbl, item)
for _, v in ipairs(tbl) do
if v == item then
return true
end
end
return false
end
-- Needs to be run, every time a new titlbear is created
return function(c, opts)
-- Args & Fallback -- Widget templates are in their original loactions
opts = gears.table.crush({
layout_spacing = dpi(4),
icon_size = dpi(20),
icon_margin = dpi(4),
bg_color_focus = "#ff0000",
bg_color = "#00000000",
fg_color = "#fafafa",
fg_color_focus = "#e0e0e0",
icon_shape = function(cr, w, h)
gears.shape.rounded_rect(cr, w, h, 0)
end,
layout = wibox.layout.fixed.horizontal,
}, gears.table.join(
opts,
beautiful.bling_tabbed_misc_titlebar_indicator
))
-- Container to store icons
local tabbed_icons = wibox.widget({
layout = opts.layout,
spacing = opts.layout_spacing,
})
awesome.connect_signal("bling::tabbed::client_removed", function(_, removed_c)
-- Remove from list
for idx, icon in ipairs(tabbed_icons.children) do
if icon._client == removed_c then
tabbed_icons:remove(idx)
end
end
-- Empty list
if removed_c == c then
tabbed_icons:reset()
end
end)
local function recreate(group)
if tbl_contains(group.clients, c) then
tabbed_icons:reset()
local focused = group.clients[group.focused_idx]
-- Autohide?
if #group.clients == 1 then
return
end
for idx, client in ipairs(group.clients) do
local widget = wibox.widget(
opts.widget_template or {
{
{
{
id = "icon_role",
forced_width = opts.icon_size,
forced_height = opts.icon_size,
widget = awful.widget.clienticon,
},
margins = opts.icon_margin,
widget = wibox.container.margin,
},
shape = opts.icon_shape,
id = "bg_role",
widget = wibox.container.background,
},
halign = "center",
valign = "center",
widget = wibox.container.place,
})
widget._client = client
-- No creation call back since this would be called on creation & every time the widget updated.
if opts.widget_template and opts.widget_template.update_callback then
opts.widget_template.update_callback(widget, client, group)
end
-- Add icons & etc
for _, w in ipairs(widget:get_children_by_id("icon_role")) do
-- TODO: Allow fallback icon?
w.image = client.icon
w.client = client
end
for _, w in ipairs(widget:get_children_by_id("bg_role")) do
w:add_button(awful.button({}, 1, function()
tabbed_module.switch_to(group, idx)
end))
if client == focused then
w.bg = opts.bg_color_focus
w.fg = opts.fg_color_focus
else
w.bg = opts.bg_color
w.fg = opts.fg_color
end
end
for _, w in ipairs(widget:get_children_by_id("text_role")) do
w.text = client.name
end
tabbed_icons:add(widget)
end
end
end
awesome.connect_signal("bling::tabbed::client_added", recreate)
awesome.connect_signal("bling::tabbed::changed_focus", recreate)
return tabbed_icons
end

View file

@ -0,0 +1,246 @@
--
-- Provides:
-- bling::tag_preview::update -- first line is the signal
-- t (tag) -- indented lines are function parameters
-- bling::tag_preview::visibility
-- s (screen)
-- v (boolean)
--
local awful = require("awful")
local wibox = require("wibox")
local helpers = require(tostring(...):match(".*bling") .. ".helpers")
local gears = require("gears")
local beautiful = require("beautiful")
local dpi = beautiful.xresources.apply_dpi
local cairo = require("lgi").cairo
local function draw_widget(
t,
tag_preview_image,
scale,
screen_radius,
client_radius,
client_opacity,
client_bg,
client_border_color,
client_border_width,
widget_bg,
widget_border_color,
widget_border_width,
geo,
margin,
background_image
)
local client_list = wibox.layout.manual()
client_list.forced_height = geo.height
client_list.forced_width = geo.width
local tag_screen = t.screen
for i, c in ipairs(t:clients()) do
if not c.hidden and not c.minimized then
local img_box = wibox.widget ({
resize = true,
forced_height = 100 * scale,
forced_width = 100 * scale,
widget = wibox.widget.imagebox,
})
-- If fails to set image, fallback to a awesome icon
if not pcall(function() img_box.image = gears.surface.load(c.icon) end) then
img_box.image = beautiful.theme_assets.awesome_icon (24, "#222222", "#fafafa")
end
if tag_preview_image then
if c.prev_content or t.selected then
local content
if t.selected then
content = gears.surface(c.content)
else
content = gears.surface(c.prev_content)
end
local cr = cairo.Context(content)
local x, y, w, h = cr:clip_extents()
local img = cairo.ImageSurface.create(
cairo.Format.ARGB32,
w - x,
h - y
)
cr = cairo.Context(img)
cr:set_source_surface(content, 0, 0)
cr.operator = cairo.Operator.SOURCE
cr:paint()
img_box = wibox.widget({
image = gears.surface.load(img),
resize = true,
opacity = client_opacity,
forced_height = math.floor(c.height * scale),
forced_width = math.floor(c.width * scale),
widget = wibox.widget.imagebox,
})
end
end
local client_box = wibox.widget({
{
nil,
{
nil,
img_box,
nil,
expand = "outside",
layout = wibox.layout.align.horizontal,
},
nil,
expand = "outside",
widget = wibox.layout.align.vertical,
},
forced_height = math.floor(c.height * scale),
forced_width = math.floor(c.width * scale),
bg = client_bg,
shape_border_color = client_border_color,
shape_border_width = client_border_width,
shape = helpers.shape.rrect(client_radius),
widget = wibox.container.background,
})
client_box.point = {
x = math.floor((c.x - geo.x) * scale),
y = math.floor((c.y - geo.y) * scale),
}
client_list:add(client_box)
end
end
return wibox.widget {
{
background_image,
{
{
{
{
client_list,
forced_height = geo.height,
forced_width = geo.width,
widget = wibox.container.place,
},
layout = wibox.layout.align.horizontal,
},
layout = wibox.layout.align.vertical,
},
margins = margin,
widget = wibox.container.margin,
},
layout = wibox.layout.stack
},
bg = widget_bg,
shape_border_width = widget_border_width,
shape_border_color = widget_border_color,
shape = helpers.shape.rrect(screen_radius),
widget = wibox.container.background,
}
end
local enable = function(opts)
local opts = opts or {}
local tag_preview_image = opts.show_client_content or false
local widget_x = opts.x or dpi(20)
local widget_y = opts.y or dpi(20)
local scale = opts.scale or 0.2
local work_area = opts.honor_workarea or false
local padding = opts.honor_padding or false
local placement_fn = opts.placement_fn or nil
local background_image = opts.background_widget or nil
local margin = beautiful.tag_preview_widget_margin or dpi(0)
local screen_radius = beautiful.tag_preview_widget_border_radius or dpi(0)
local client_radius = beautiful.tag_preview_client_border_radius or dpi(0)
local client_opacity = beautiful.tag_preview_client_opacity or 0.5
local client_bg = beautiful.tag_preview_client_bg or "#000000"
local client_border_color = beautiful.tag_preview_client_border_color
or "#ffffff"
local client_border_width = beautiful.tag_preview_client_border_width
or dpi(3)
local widget_bg = beautiful.tag_preview_widget_bg or "#000000"
local widget_border_color = beautiful.tag_preview_widget_border_color
or "#ffffff"
local widget_border_width = beautiful.tag_preview_widget_border_width
or dpi(3)
local tag_preview_box = awful.popup({
type = "dropdown_menu",
visible = false,
ontop = true,
placement = placement_fn,
widget = wibox.container.background,
input_passthrough = true,
bg = "#00000000",
})
tag.connect_signal("property::selected", function(t)
-- Awesome switches up tags on startup really fast it seems, probably depends on what rules you have set
-- which can cause the c.content to not show the correct image
gears.timer
{
timeout = 0.1,
call_now = false,
autostart = true,
single_shot = true,
callback = function()
if t.selected == true then
for _, c in ipairs(t:clients()) do
c.prev_content = gears.surface.duplicate_surface(c.content)
end
end
end
}
end)
awesome.connect_signal("bling::tag_preview::update", function(t)
local geo = t.screen:get_bounding_geometry({
honor_padding = padding,
honor_workarea = work_area,
})
tag_preview_box.maximum_width = scale * geo.width + margin * 2
tag_preview_box.maximum_height = scale * geo.height + margin * 2
tag_preview_box.widget = draw_widget(
t,
tag_preview_image,
scale,
screen_radius,
client_radius,
client_opacity,
client_bg,
client_border_color,
client_border_width,
widget_bg,
widget_border_color,
widget_border_width,
geo,
margin,
background_image
)
end)
awesome.connect_signal("bling::tag_preview::visibility", function(s, v)
if not placement_fn then
tag_preview_box.x = s.geometry.x + widget_x
tag_preview_box.y = s.geometry.y + widget_y
end
if v == false then
tag_preview_box.widget = nil
collectgarbage("collect")
end
tag_preview_box.visible = v
end)
end
return {enable = enable, draw_widget = draw_widget}

View file

@ -0,0 +1,199 @@
--
-- Provides:
-- bling::task_preview::visibility
-- s (screen)
-- v (boolean)
-- c (client)
--
local awful = require("awful")
local wibox = require("wibox")
local helpers = require(tostring(...):match(".*bling") .. ".helpers")
local gears = require("gears")
local beautiful = require("beautiful")
local dpi = beautiful.xresources.apply_dpi
local cairo = require("lgi").cairo
-- TODO: rename structure to something better?
local function draw_widget(
c,
widget_template,
screen_radius,
widget_bg,
widget_border_color,
widget_border_width,
margin,
widget_width,
widget_height
)
if not pcall(function()
return type(c.content)
end) then
return
end
local content = nil
if c.active then
content = gears.surface(c.content)
elseif c.prev_content then
content = gears.surface(c.prev_content)
end
local img = nil
if content ~= nil then
local cr = cairo.Context(content)
local x, y, w, h = cr:clip_extents()
img = cairo.ImageSurface.create(cairo.Format.ARGB32, w - x, h - y)
cr = cairo.Context(img)
cr:set_source_surface(content, 0, 0)
cr.operator = cairo.Operator.SOURCE
cr:paint()
end
local widget = wibox.widget({
(widget_template or {
{
{
{
{
id = "icon_role",
resize = true,
forced_height = dpi(20),
forced_width = dpi(20),
widget = wibox.widget.imagebox,
},
{
{
id = "name_role",
align = "center",
widget = wibox.widget.textbox,
},
left = dpi(4),
right = dpi(4),
widget = wibox.container.margin,
},
layout = wibox.layout.align.horizontal,
},
{
{
{
id = "image_role",
resize = true,
clip_shape = helpers.shape.rrect(screen_radius),
widget = wibox.widget.imagebox,
},
valign = "center",
halign = "center",
widget = wibox.container.place,
},
top = margin * 0.25,
widget = wibox.container.margin,
},
fill_space = true,
layout = wibox.layout.fixed.vertical,
},
margins = margin,
widget = wibox.container.margin,
},
bg = widget_bg,
shape_border_width = widget_border_width,
shape_border_color = widget_border_color,
shape = helpers.shape.rrect(screen_radius),
widget = wibox.container.background,
}),
width = widget_width,
height = widget_height,
widget = wibox.container.constraint,
})
-- TODO: have something like a create callback here?
for _, w in ipairs(widget:get_children_by_id("image_role")) do
w.image = img -- TODO: copy it with gears.surface.xxx or something
end
for _, w in ipairs(widget:get_children_by_id("name_role")) do
w.text = c.name
end
for _, w in ipairs(widget:get_children_by_id("icon_role")) do
w.image = c.icon -- TODO: detect clienticon
end
return widget
end
local enable = function(opts)
local opts = opts or {}
local widget_x = opts.x or dpi(20)
local widget_y = opts.y or dpi(20)
local widget_height = opts.height or dpi(200)
local widget_width = opts.width or dpi(200)
local placement_fn = opts.placement_fn or nil
local margin = beautiful.task_preview_widget_margin or dpi(0)
local screen_radius = beautiful.task_preview_widget_border_radius or dpi(0)
local widget_bg = beautiful.task_preview_widget_bg or "#000000"
local widget_border_color = beautiful.task_preview_widget_border_color
or "#ffffff"
local widget_border_width = beautiful.task_preview_widget_border_width
or dpi(3)
local task_preview_box = awful.popup({
type = "dropdown_menu",
visible = false,
ontop = true,
placement = placement_fn,
widget = wibox.container.background, -- A dummy widget to make awful.popup not scream
input_passthrough = true,
bg = "#00000000",
})
tag.connect_signal("property::selected", function(t)
-- Awesome switches up tags on startup really fast it seems, probably depends on what rules you have set
-- which can cause the c.content to not show the correct image
gears.timer
{
timeout = 0.1,
call_now = false,
autostart = true,
single_shot = true,
callback = function()
if t.selected == true then
for _, c in ipairs(t:clients()) do
c.prev_content = gears.surface.duplicate_surface(c.content)
end
end
end
}
end)
awesome.connect_signal("bling::task_preview::visibility", function(s, v, c)
if v then
-- Update task preview contents
task_preview_box.widget = draw_widget(
c,
opts.structure,
screen_radius,
widget_bg,
widget_border_color,
widget_border_width,
margin,
widget_width,
widget_height
)
else
task_preview_box.widget = nil
collectgarbage("collect")
end
if not placement_fn then
task_preview_box.x = s.geometry.x + widget_x
task_preview_box.y = s.geometry.y + widget_y
end
task_preview_box.visible = v
end)
end
return { enable = enable, draw_widget = draw_widget }

View file

@ -0,0 +1,461 @@
local cairo = require("lgi").cairo
local awful = require("awful")
local gears = require("gears")
local wibox = require("wibox")
local beautiful = require("beautiful")
local helpers = require(tostring(...):match(".*bling") .. ".helpers")
local dpi = beautiful.xresources.apply_dpi
local window_switcher_first_client -- The client that was focused when the window_switcher was activated
local window_switcher_minimized_clients = {} -- The clients that were minimized when the window switcher was activated
local window_switcher_grabber
local get_num_clients = function()
local minimized_clients_in_tag = 0
local matcher = function(c)
return awful.rules.match(
c,
{
minimized = true,
skip_taskbar = false,
hidden = false,
first_tag = awful.screen.focused().selected_tag,
}
)
end
for c in awful.client.iterate(matcher) do
minimized_clients_in_tag = minimized_clients_in_tag + 1
end
return minimized_clients_in_tag + #awful.screen.focused().clients
end
local window_switcher_hide = function(window_switcher_box)
-- Add currently focused client to history
if client.focus then
local window_switcher_last_client = client.focus
awful.client.focus.history.add(window_switcher_last_client)
-- Raise client that was focused originally
-- Then raise last focused client
if
window_switcher_first_client and window_switcher_first_client.valid
then
window_switcher_first_client:raise()
window_switcher_last_client:raise()
end
end
-- Minimize originally minimized clients
local s = awful.screen.focused()
for _, c in pairs(window_switcher_minimized_clients) do
if c and c.valid and not (client.focus and client.focus == c) then
c.minimized = true
end
end
-- Reset helper table
window_switcher_minimized_clients = {}
-- Resume recording focus history
awful.client.focus.history.enable_tracking()
-- Stop and hide window_switcher
awful.keygrabber.stop(window_switcher_grabber)
window_switcher_box.visible = false
window_switcher_box.widget = nil
collectgarbage("collect")
end
local function draw_widget(
type,
background,
border_width,
border_radius,
border_color,
clients_spacing,
client_icon_horizontal_spacing,
client_width,
client_height,
client_margins,
thumbnail_margins,
thumbnail_scale,
name_margins,
name_valign,
name_forced_width,
name_font,
name_normal_color,
name_focus_color,
icon_valign,
icon_width,
mouse_keys,
filterClients
)
filterClients = filterClients or awful.widget.tasklist.filter.currenttags
local tasklist_widget = type == "thumbnail"
and awful.widget.tasklist({
screen = awful.screen.focused(),
filter = filterClients,
buttons = mouse_keys,
style = {
font = name_font,
fg_normal = name_normal_color,
fg_focus = name_focus_color,
},
layout = {
layout = wibox.layout.flex.horizontal,
spacing = clients_spacing,
},
widget_template = {
widget = wibox.container.background,
id = "bg_role",
forced_width = client_width,
forced_height = client_height,
create_callback = function(self, c, _, __)
local content = gears.surface(c.content)
local cr = cairo.Context(content)
local x, y, w, h = cr:clip_extents()
local img = cairo.ImageSurface.create(
cairo.Format.ARGB32,
w - x,
h - y
)
cr = cairo.Context(img)
cr:set_source_surface(content, 0, 0)
cr.operator = cairo.Operator.SOURCE
cr:paint()
self:get_children_by_id("thumbnail")[1].image =
gears.surface.load(
img
)
end,
{
{
{
horizontal_fit_policy = thumbnail_scale == true
and "fit"
or "auto",
vertical_fit_policy = thumbnail_scale == true
and "fit"
or "auto",
id = "thumbnail",
widget = wibox.widget.imagebox,
},
margins = thumbnail_margins,
widget = wibox.container.margin,
},
{
{
{
id = "icon_role",
widget = wibox.widget.imagebox,
},
forced_width = icon_width,
valign = icon_valign,
widget = wibox.container.place,
},
{
{
forced_width = name_forced_width,
valign = name_valign,
id = "text_role",
widget = wibox.widget.textbox,
},
margins = name_margins,
widget = wibox.container.margin,
},
spacing = client_icon_horizontal_spacing,
layout = wibox.layout.fixed.horizontal,
},
layout = wibox.layout.flex.vertical,
},
},
})
or awful.widget.tasklist({
screen = awful.screen.focused(),
filter = filterClients,
buttons = mouse_keys,
style = {
font = name_font,
fg_normal = name_normal_color,
fg_focus = name_focus_color,
},
layout = {
layout = wibox.layout.fixed.vertical,
spacing = clients_spacing,
},
widget_template = {
widget = wibox.container.background,
id = "bg_role",
forced_width = client_width,
forced_height = client_height,
{
{
{
id = "icon_role",
widget = wibox.widget.imagebox,
},
forced_width = icon_width,
valign = icon_valign,
widget = wibox.container.place,
},
{
{
forced_width = name_forced_width,
valign = name_valign,
id = "text_role",
widget = wibox.widget.textbox,
},
margins = name_margins,
widget = wibox.container.margin,
},
spacing = client_icon_horizontal_spacing,
layout = wibox.layout.fixed.horizontal,
},
},
})
return wibox.widget({
{
tasklist_widget,
margins = client_margins,
widget = wibox.container.margin,
},
shape_border_width = border_width,
shape_border_color = border_color,
bg = background,
shape = helpers.shape.rrect(border_radius),
widget = wibox.container.background,
})
end
local enable = function(opts)
local opts = opts or {}
local type = opts.type or "thumbnail"
local background = beautiful.window_switcher_widget_bg or "#000000"
local border_width = beautiful.window_switcher_widget_border_width or dpi(3)
local border_radius = beautiful.window_switcher_widget_border_radius
or dpi(0)
local border_color = beautiful.window_switcher_widget_border_color
or "#ffffff"
local clients_spacing = beautiful.window_switcher_clients_spacing or dpi(20)
local client_icon_horizontal_spacing = beautiful.window_switcher_client_icon_horizontal_spacing
or dpi(5)
local client_width = beautiful.window_switcher_client_width
or dpi(type == "thumbnail" and 150 or 500)
local client_height = beautiful.window_switcher_client_height
or dpi(type == "thumbnail" and 250 or 50)
local client_margins = beautiful.window_switcher_client_margins or dpi(10)
local thumbnail_margins = beautiful.window_switcher_thumbnail_margins
or dpi(5)
local thumbnail_scale = beautiful.thumbnail_scale or false
local name_margins = beautiful.window_switcher_name_margins or dpi(10)
local name_valign = beautiful.window_switcher_name_valign or "center"
local name_forced_width = beautiful.window_switcher_name_forced_width
or dpi(type == "thumbnail" and 200 or 550)
local name_font = beautiful.window_switcher_name_font or beautiful.font
local name_normal_color = beautiful.window_switcher_name_normal_color
or "#FFFFFF"
local name_focus_color = beautiful.window_switcher_name_focus_color
or "#FF0000"
local icon_valign = beautiful.window_switcher_icon_valign or "center"
local icon_width = beautiful.window_switcher_icon_width or dpi(40)
local hide_window_switcher_key = opts.hide_window_switcher_key or "Escape"
local select_client_key = opts.select_client_key or 1
local minimize_key = opts.minimize_key or "n"
local unminimize_key = opts.unminimize_key or "N"
local kill_client_key = opts.kill_client_key or "q"
local cycle_key = opts.cycle_key or "Tab"
local previous_key = opts.previous_key or "Left"
local next_key = opts.next_key or "Right"
local vim_previous_key = opts.vim_previous_key or "h"
local vim_next_key = opts.vim_next_key or "l"
local scroll_previous_key = opts.scroll_previous_key or 4
local scroll_next_key = opts.scroll_next_key or 5
local cycleClientsByIdx = opts.cycleClientsByIdx or awful.client.focus.byidx
local filterClients = opts.filterClients or awful.widget.tasklist.filter.currenttags
local window_switcher_box = awful.popup({
bg = "#00000000",
visible = false,
ontop = true,
placement = awful.placement.centered,
screen = awful.screen.focused(),
widget = wibox.container.background, -- A dummy widget to make awful.popup not scream
widget = {
{
draw_widget(),
margins = client_margins,
widget = wibox.container.margin,
},
shape_border_width = border_width,
shape_border_color = border_color,
bg = background,
shape = helpers.shape.rrect(border_radius),
widget = wibox.container.background,
},
})
local mouse_keys = gears.table.join(
awful.button({
modifiers = { "Any" },
button = select_client_key,
on_press = function(c)
client.focus = c
end,
}),
awful.button({
modifiers = { "Any" },
button = scroll_previous_key,
on_press = function()
cycleClientsByIdx(-1)
end,
}),
awful.button({
modifiers = { "Any" },
button = scroll_next_key,
on_press = function()
cycleClientsByIdx(1)
end,
})
)
local keyboard_keys = {
[hide_window_switcher_key] = function()
window_switcher_hide(window_switcher_box)
end,
[minimize_key] = function()
if client.focus then
client.focus.minimized = true
end
end,
[unminimize_key] = function()
if awful.client.restore() then
client.focus = awful.client.restore()
end
end,
[kill_client_key] = function()
if client.focus then
client.focus:kill()
end
end,
[cycle_key] = function()
cycleClientsByIdx(1)
end,
[previous_key] = function()
cycleClientsByIdx(1)
end,
[next_key] = function()
cycleClientsByIdx(-1)
end,
[vim_previous_key] = function()
cycleClientsByIdx(1)
end,
[vim_next_key] = function()
cycleClientsByIdx(-1)
end,
}
window_switcher_box:connect_signal("property::width", function()
if window_switcher_box.visible and get_num_clients() == 0 then
window_switcher_hide(window_switcher_box)
end
end)
window_switcher_box:connect_signal("property::height", function()
if window_switcher_box.visible and get_num_clients() == 0 then
window_switcher_hide(window_switcher_box)
end
end)
awesome.connect_signal("bling::window_switcher::turn_on", function()
local number_of_clients = get_num_clients()
if number_of_clients == 0 then
return
end
-- Store client that is focused in a variable
window_switcher_first_client = client.focus
-- Stop recording focus history
awful.client.focus.history.disable_tracking()
-- Go to previously focused client (in the tag)
awful.client.focus.history.previous()
-- Track minimized clients
-- Unminimize them
-- Lower them so that they are always below other
-- originally unminimized windows
local clients = awful.screen.focused().selected_tag:clients()
for _, c in pairs(clients) do
if c.minimized then
table.insert(window_switcher_minimized_clients, c)
c.minimized = false
c:lower()
end
end
-- Start the keygrabber
window_switcher_grabber = awful.keygrabber.run(function(_, key, event)
if event == "release" then
-- Hide if the modifier was released
-- We try to match Super or Alt or Control since we do not know which keybind is
-- used to activate the window switcher (the keybind is set by the user in keys.lua)
if
key:match("Super")
or key:match("Alt")
or key:match("Control")
then
window_switcher_hide(window_switcher_box)
end
-- Do nothing
return
end
-- Run function attached to key, if it exists
if keyboard_keys[key] then
keyboard_keys[key]()
end
end)
window_switcher_box.widget = draw_widget(
type,
background,
border_width,
border_radius,
border_color,
clients_spacing,
client_icon_horizontal_spacing,
client_width,
client_height,
client_margins,
thumbnail_margins,
thumbnail_scale,
name_margins,
name_valign,
name_forced_width,
name_font,
name_normal_color,
name_focus_color,
icon_valign,
icon_width,
mouse_keys,
filterClients
)
window_switcher_box.screen = awful.screen.focused()
window_switcher_box.visible = true
end)
end
return { enable = enable }