Add awesome extras
This commit is contained in:
24
.config/awesome/extras/bling/.editorconfig
Executable file
24
.config/awesome/extras/bling/.editorconfig
Executable file
@@ -0,0 +1,24 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.lua]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
max_line_length = 80
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.{html,css}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
127
.config/awesome/extras/bling/helpers/client.lua
Executable file
127
.config/awesome/extras/bling/helpers/client.lua
Executable file
@@ -0,0 +1,127 @@
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
|
||||
local _client = {}
|
||||
|
||||
--- Turn off passed client
|
||||
-- Remove current tag from window's tags
|
||||
--
|
||||
-- @param c A client
|
||||
function _client.turn_off(c, current_tag)
|
||||
if current_tag == nil then
|
||||
current_tag = c.screen.selected_tag
|
||||
end
|
||||
local ctags = {}
|
||||
for k, tag in pairs(c:tags()) do
|
||||
if tag ~= current_tag then
|
||||
table.insert(ctags, tag)
|
||||
end
|
||||
end
|
||||
c:tags(ctags)
|
||||
c.sticky = false
|
||||
end
|
||||
|
||||
--- Turn on passed client (add current tag to window's tags)
|
||||
--
|
||||
-- @param c A client
|
||||
function _client.turn_on(c)
|
||||
local current_tag = c.screen.selected_tag
|
||||
ctags = { current_tag }
|
||||
for k, tag in pairs(c:tags()) do
|
||||
if tag ~= current_tag then
|
||||
table.insert(ctags, tag)
|
||||
end
|
||||
end
|
||||
c:tags(ctags)
|
||||
c:raise()
|
||||
client.focus = c
|
||||
end
|
||||
|
||||
--- Sync two clients
|
||||
--
|
||||
-- @param to_c The client to which to write all properties
|
||||
-- @param from_c The client from which to read all properties
|
||||
function _client.sync(to_c, from_c)
|
||||
if not from_c or not to_c then
|
||||
return
|
||||
end
|
||||
if not from_c.valid or not to_c.valid then
|
||||
return
|
||||
end
|
||||
if from_c.modal then
|
||||
return
|
||||
end
|
||||
to_c.floating = from_c.floating
|
||||
to_c.maximized = from_c.maximized
|
||||
to_c.above = from_c.above
|
||||
to_c.below = from_c.below
|
||||
to_c:geometry(from_c:geometry())
|
||||
-- TODO: Should also copy over the position in a tiling layout
|
||||
end
|
||||
|
||||
--- Checks whether the passed client is a childprocess of a given process ID
|
||||
--
|
||||
-- @param c A client
|
||||
-- @param pid The process ID
|
||||
-- @return True if the passed client is a childprocess of the given PID otherwise false
|
||||
function _client.is_child_of(c, pid)
|
||||
-- io.popen is normally discouraged. Should probably be changed
|
||||
if not c or not c.valid then
|
||||
return false
|
||||
end
|
||||
if tostring(c.pid) == tostring(pid) then
|
||||
return true
|
||||
end
|
||||
local pid_cmd = [[pstree -T -p -a -s ]]
|
||||
.. tostring(c.pid)
|
||||
.. [[ | sed '2q;d' | grep -o '[0-9]*$' | tr -d '\n']]
|
||||
local handle = io.popen(pid_cmd)
|
||||
local parent_pid = handle:read("*a")
|
||||
handle:close()
|
||||
return tostring(parent_pid) == tostring(pid)
|
||||
or tostring(parent_pid) == tostring(c.pid)
|
||||
end
|
||||
|
||||
--- Finds all clients that satisfy the passed rule
|
||||
--
|
||||
-- @param rule The rule to be searched for
|
||||
-- @retrun A list of clients that match the given rule
|
||||
function _client.find(rule)
|
||||
local function matcher(c)
|
||||
return awful.rules.match(c, rule)
|
||||
end
|
||||
local clients = client.get()
|
||||
local findex = gears.table.hasitem(clients, client.focus) or 1
|
||||
local start = gears.math.cycle(#clients, findex + 1)
|
||||
|
||||
local matches = {}
|
||||
for c in awful.client.iterate(matcher, start) do
|
||||
matches[#matches + 1] = c
|
||||
end
|
||||
|
||||
return matches
|
||||
end
|
||||
|
||||
--- Gets the next client by direction from the focused one
|
||||
--
|
||||
-- @param direction it the direction as a string ("up", "down", "left" or "right")
|
||||
-- @retrun the client in the given direction starting at the currently focused one, nil otherwise
|
||||
function _client.get_by_direction(direction)
|
||||
local sel = client.focus
|
||||
if not sel then
|
||||
return nil
|
||||
end
|
||||
local cltbl = sel.screen:get_clients()
|
||||
local geomtbl = {}
|
||||
for i, cl in ipairs(cltbl) do
|
||||
geomtbl[i] = cl:geometry()
|
||||
end
|
||||
local target = gears.geometry.rectangle.get_in_direction(
|
||||
direction,
|
||||
geomtbl,
|
||||
sel:geometry()
|
||||
)
|
||||
return cltbl[target]
|
||||
end
|
||||
|
||||
return _client
|
158
.config/awesome/extras/bling/helpers/color.lua
Executable file
158
.config/awesome/extras/bling/helpers/color.lua
Executable file
@@ -0,0 +1,158 @@
|
||||
local tonumber = tonumber
|
||||
local string = string
|
||||
local math = math
|
||||
local floor = math.floor
|
||||
local max = math.max
|
||||
local min = math.min
|
||||
local abs = math.abs
|
||||
local format = string.format
|
||||
|
||||
local _color = {}
|
||||
|
||||
--- Try to guess if a color is dark or light.
|
||||
--
|
||||
-- @string color The color with hexadecimal HTML format `"#RRGGBB"`.
|
||||
-- @treturn bool `true` if the color is dark, `false` if it is light.
|
||||
function _color.is_dark(color)
|
||||
-- Try to determine if the color is dark or light
|
||||
local numeric_value = 0
|
||||
for s in color:gmatch("[a-fA-F0-9][a-fA-F0-9]") do
|
||||
numeric_value = numeric_value + tonumber("0x" .. s)
|
||||
end
|
||||
return (numeric_value < 383)
|
||||
end
|
||||
|
||||
function _color.is_opaque(color)
|
||||
if type(color) == "string" then
|
||||
color = _color.hex_to_rgba(color)
|
||||
end
|
||||
|
||||
return color.a < 0.01
|
||||
end
|
||||
|
||||
--- Lighten a color.
|
||||
--
|
||||
-- @string color The color to lighten with hexadecimal HTML format `"#RRGGBB"`.
|
||||
-- @int[opt=26] amount How much light from 0 to 255. Default is around 10%.
|
||||
-- @treturn string The lighter color
|
||||
function _color.lighten(color, amount)
|
||||
amount = amount or 26
|
||||
local c = {
|
||||
r = tonumber("0x" .. color:sub(2, 3)),
|
||||
g = tonumber("0x" .. color:sub(4, 5)),
|
||||
b = tonumber("0x" .. color:sub(6, 7)),
|
||||
}
|
||||
|
||||
c.r = c.r + amount
|
||||
c.r = c.r < 0 and 0 or c.r
|
||||
c.r = c.r > 255 and 255 or c.r
|
||||
c.g = c.g + amount
|
||||
c.g = c.g < 0 and 0 or c.g
|
||||
c.g = c.g > 255 and 255 or c.g
|
||||
c.b = c.b + amount
|
||||
c.b = c.b < 0 and 0 or c.b
|
||||
c.b = c.b > 255 and 255 or c.b
|
||||
|
||||
return string.format("#%02x%02x%02x", c.r, c.g, c.b)
|
||||
end
|
||||
|
||||
--- Darken a color.
|
||||
--
|
||||
-- @string color The color to darken with hexadecimal HTML format `"#RRGGBB"`.
|
||||
-- @int[opt=26] amount How much dark from 0 to 255. Default is around 10%.
|
||||
-- @treturn string The darker color
|
||||
function _color.darken(color, amount)
|
||||
amount = amount or 26
|
||||
return _color.lighten(color, -amount)
|
||||
end
|
||||
|
||||
-- Returns a value that is clipped to interval edges if it falls outside the interval
|
||||
function _color.clip(num, min_num, max_num)
|
||||
return max(min(num, max_num), min_num)
|
||||
end
|
||||
|
||||
-- Converts the given hex color to rgba
|
||||
function _color.hex_to_rgba(color)
|
||||
color = color:gsub("#", "")
|
||||
return { r = tonumber("0x" .. color:sub(1, 2)),
|
||||
g = tonumber("0x" .. color:sub(3, 4)),
|
||||
b = tonumber("0x" .. color:sub(5, 6)),
|
||||
a = #color == 8 and tonumber("0x" .. color:sub(7, 8)) or 255 }
|
||||
end
|
||||
|
||||
-- Converts the given rgba color to hex
|
||||
function _color.rgba_to_hex(color)
|
||||
local r = _color.clip(color.r or color[1], 0, 255)
|
||||
local g = _color.clip(color.g or color[2], 0, 255)
|
||||
local b = _color.clip(color.b or color[3], 0, 255)
|
||||
local a = _color.clip(color.a or color[4] or 255, 0, 255)
|
||||
return "#" .. format("%02x%02x%02x%02x",
|
||||
floor(r),
|
||||
floor(g),
|
||||
floor(b),
|
||||
floor(a))
|
||||
end
|
||||
|
||||
-- Converts the given hex color to hsv
|
||||
function _color.hex_to_hsv(color)
|
||||
local color = _color.hex2rgb(color)
|
||||
local C_max = max(color.r, color.g, color.b)
|
||||
local C_min = min(color.r, color.g, color.b)
|
||||
local delta = C_max - C_min
|
||||
local H, S, V
|
||||
if delta == 0 then
|
||||
H = 0
|
||||
elseif C_max == color.r then
|
||||
H = 60 * (((color.g - color.b) / delta) % 6)
|
||||
elseif C_max == color.g then
|
||||
H = 60 * (((color.b - color.r) / delta) + 2)
|
||||
elseif C_max == color.b then
|
||||
H = 60 * (((color.r - color.g) / delta) + 4)
|
||||
end
|
||||
if C_max == 0 then
|
||||
S = 0
|
||||
else
|
||||
S = delta / C_max
|
||||
end
|
||||
V = C_max
|
||||
|
||||
return { h = H,
|
||||
s = S * 100,
|
||||
v = V * 100 }
|
||||
end
|
||||
|
||||
-- Converts the given hsv color to hex
|
||||
function _color.hsv_to_hex(H, S, V)
|
||||
S = S / 100
|
||||
V = V / 100
|
||||
if H > 360 then H = 360 end
|
||||
if H < 0 then H = 0 end
|
||||
local C = V * S
|
||||
local X = C * (1 - abs(((H / 60) % 2) - 1))
|
||||
local m = V - C
|
||||
local r_, g_, b_ = 0, 0, 0
|
||||
if H >= 0 and H < 60 then
|
||||
r_, g_, b_ = C, X, 0
|
||||
elseif H >= 60 and H < 120 then
|
||||
r_, g_, b_ = X, C, 0
|
||||
elseif H >= 120 and H < 180 then
|
||||
r_, g_, b_ = 0, C, X
|
||||
elseif H >= 180 and H < 240 then
|
||||
r_, g_, b_ = 0, X, C
|
||||
elseif H >= 240 and H < 300 then
|
||||
r_, g_, b_ = X, 0, C
|
||||
elseif H >= 300 and H < 360 then
|
||||
r_, g_, b_ = C, 0, X
|
||||
end
|
||||
local r, g, b = (r_ + m) * 255, (g_ + m) * 255, (b_ + m) * 255
|
||||
return ("#%02x%02x%02x"):format(floor(r), floor(g), floor(b))
|
||||
end
|
||||
|
||||
function _color.multiply(color, amount)
|
||||
return { _color.clip(color.r * amount, 0, 255),
|
||||
_color.clip(color.g * amount, 0, 255),
|
||||
_color.clip(color.b * amount, 0, 255),
|
||||
255 }
|
||||
end
|
||||
|
||||
return _color
|
53
.config/awesome/extras/bling/helpers/filesystem.lua
Executable file
53
.config/awesome/extras/bling/helpers/filesystem.lua
Executable file
@@ -0,0 +1,53 @@
|
||||
local Gio = require("lgi").Gio
|
||||
|
||||
local _filesystem = {}
|
||||
|
||||
--- Get a list of files from a given directory.
|
||||
-- @string path The directory to search.
|
||||
-- @tparam[opt] table exts Specific extensions to limit the search to. eg:`{ "jpg", "png" }`
|
||||
-- If ommited, all files are considered.
|
||||
-- @bool[opt=false] recursive List files from subdirectories
|
||||
-- @staticfct bling.helpers.filesystem.get_random_file_from_dir
|
||||
function _filesystem.list_directory_files(path, exts, recursive)
|
||||
recursive = recursive or false
|
||||
local files, valid_exts = {}, {}
|
||||
|
||||
-- Transforms { "jpg", ... } into { [jpg] = #, ... }
|
||||
if exts then
|
||||
for i, j in ipairs(exts) do
|
||||
valid_exts[j:lower()] = i
|
||||
end
|
||||
end
|
||||
|
||||
-- Build a table of files from the path with the required extensions
|
||||
local file_list = Gio.File.new_for_path(path):enumerate_children(
|
||||
"standard::*",
|
||||
0
|
||||
)
|
||||
if file_list then
|
||||
for file in function()
|
||||
return file_list:next_file()
|
||||
end do
|
||||
local file_type = file:get_file_type()
|
||||
if file_type == "REGULAR" then
|
||||
local file_name = file:get_display_name()
|
||||
if
|
||||
not exts
|
||||
or valid_exts[file_name:lower():match(".+%.(.*)$") or ""]
|
||||
then
|
||||
table.insert(files, file_name)
|
||||
end
|
||||
elseif recursive and file_type == "DIRECTORY" then
|
||||
local file_name = file:get_display_name()
|
||||
files = gears.table.join(
|
||||
files,
|
||||
list_directory_files(file_name, exts, recursive)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return files
|
||||
end
|
||||
|
||||
return _filesystem
|
134
.config/awesome/extras/bling/helpers/icon_theme.lua
Executable file
134
.config/awesome/extras/bling/helpers/icon_theme.lua
Executable file
@@ -0,0 +1,134 @@
|
||||
local Gio = require("lgi").Gio
|
||||
local Gtk = require("lgi").Gtk
|
||||
local gobject = require("gears.object")
|
||||
local gtable = require("gears.table")
|
||||
local helpers = require("helpers")
|
||||
local setmetatable = setmetatable
|
||||
local ipairs = ipairs
|
||||
|
||||
local icon_theme = { mt = {} }
|
||||
|
||||
function icon_theme:get_client_icon_path(client)
|
||||
local function find_icon(class)
|
||||
if self._private.client_icon_cache[class] ~= nil then
|
||||
return self._private.client_icon_cache[class]
|
||||
end
|
||||
|
||||
for _, app in ipairs(Gio.AppInfo.get_all()) do
|
||||
local id = Gio.AppInfo.get_id(app)
|
||||
if id:match(helpers.misc.case_insensitive_pattern(class)) then
|
||||
self._private.client_icon_cache[class] = self:get_gicon_path(Gio.AppInfo.get_icon(app))
|
||||
return self._private.client_icon_cache[class]
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local class = client.class
|
||||
if class == "jetbrains-studio" then
|
||||
class = "android-studio"
|
||||
end
|
||||
|
||||
local icon = self:get_icon_path("gnome-window-manager")
|
||||
|
||||
if class ~= nil then
|
||||
class = class:gsub("[%-]", "%%%0")
|
||||
icon = find_icon(class) or icon
|
||||
|
||||
class = client.class
|
||||
class = class:gsub("[%-]", "")
|
||||
icon = find_icon(class) or icon
|
||||
|
||||
class = client.class
|
||||
class = class:gsub("[%-]", ".")
|
||||
icon = find_icon(class) or icon
|
||||
|
||||
class = client.class
|
||||
class = class:match("(.-)-") or class
|
||||
class = class:match("(.-)%.") or class
|
||||
class = class:match("(.-)%s+") or class
|
||||
class = class:gsub("[%-]", "%%%0")
|
||||
icon = find_icon(class) or icon
|
||||
end
|
||||
|
||||
return icon
|
||||
end
|
||||
|
||||
function icon_theme:choose_icon(icons_names)
|
||||
local icon_info = Gtk.IconTheme.choose_icon(self.gtk_theme, icons_names, self.icon_size, 0);
|
||||
if icon_info then
|
||||
local icon_path = Gtk.IconInfo.get_filename(icon_info)
|
||||
if icon_path then
|
||||
return icon_path
|
||||
end
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
|
||||
function icon_theme:get_gicon_path(gicon)
|
||||
if gicon == nil then
|
||||
return ""
|
||||
end
|
||||
|
||||
if self._private.icon_cache[gicon] ~= nil then
|
||||
return self._private.icon_cache[gicon]
|
||||
end
|
||||
|
||||
local icon_info = Gtk.IconTheme.lookup_by_gicon(self.gtk_theme, gicon, self.icon_size, 0);
|
||||
if icon_info then
|
||||
local icon_path = Gtk.IconInfo.get_filename(icon_info)
|
||||
if icon_path then
|
||||
self._private.icon_cache[gicon] = icon_path
|
||||
return icon_path
|
||||
end
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
function icon_theme:get_icon_path(icon_name)
|
||||
if self._private.icon_cache[icon_name] ~= nil then
|
||||
return self._private.icon_cache[icon_name]
|
||||
end
|
||||
|
||||
local icon_info = Gtk.IconTheme.lookup_icon(self.gtk_theme, icon_name, self.icon_size, 0);
|
||||
if icon_info then
|
||||
local icon_path = Gtk.IconInfo.get_filename(icon_info)
|
||||
if icon_path then
|
||||
self._private.icon_cache[icon_name] = icon_path
|
||||
return icon_path
|
||||
end
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
local function new(theme_name, icon_size)
|
||||
local ret = gobject{}
|
||||
gtable.crush(ret, icon_theme, true)
|
||||
|
||||
ret._private = {}
|
||||
ret._private.client_icon_cache = {}
|
||||
ret._private.icon_cache = {}
|
||||
|
||||
ret.name = theme_name or nil
|
||||
ret.icon_size = icon_size or 48
|
||||
|
||||
if theme_name then
|
||||
ret.gtk_theme = Gtk.IconTheme.new()
|
||||
Gtk.IconTheme.set_custom_theme(ret.gtk_theme, theme_name);
|
||||
else
|
||||
ret.gtk_theme = Gtk.IconTheme.get_default()
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
function icon_theme.mt:__call(...)
|
||||
return new(...)
|
||||
end
|
||||
|
||||
return setmetatable(icon_theme, icon_theme.mt)
|
7
.config/awesome/extras/bling/helpers/init.lua
Executable file
7
.config/awesome/extras/bling/helpers/init.lua
Executable file
@@ -0,0 +1,7 @@
|
||||
return {
|
||||
client = require(... .. ".client"),
|
||||
color = require(... .. ".color"),
|
||||
filesystem = require(... .. ".filesystem"),
|
||||
shape = require(... .. ".shape"),
|
||||
time = require(... .. ".time"),
|
||||
}
|
30
.config/awesome/extras/bling/helpers/shape.lua
Executable file
30
.config/awesome/extras/bling/helpers/shape.lua
Executable file
@@ -0,0 +1,30 @@
|
||||
local gears = require("gears")
|
||||
|
||||
shape = {}
|
||||
|
||||
-- Create rounded rectangle shape (in one line)
|
||||
|
||||
function shape.rrect(radius)
|
||||
return function(cr, width, height)
|
||||
gears.shape.rounded_rect(cr, width, height, radius)
|
||||
end
|
||||
end
|
||||
|
||||
-- Create partially rounded rect
|
||||
|
||||
function shape.prrect(radius, tl, tr, br, bl)
|
||||
return function(cr, width, height)
|
||||
gears.shape.partially_rounded_rect(
|
||||
cr,
|
||||
width,
|
||||
height,
|
||||
tl,
|
||||
tr,
|
||||
br,
|
||||
bl,
|
||||
radius
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
return shape
|
24
.config/awesome/extras/bling/helpers/time.lua
Executable file
24
.config/awesome/extras/bling/helpers/time.lua
Executable file
@@ -0,0 +1,24 @@
|
||||
local time = {}
|
||||
|
||||
--- Parse a time string to seconds (from midnight)
|
||||
--
|
||||
-- @string time The time (`HH:MM:SS`)
|
||||
-- @treturn int The number of seconds since 00:00:00
|
||||
function time.hhmmss_to_seconds(time)
|
||||
hour_sec = tonumber(string.sub(time, 1, 2)) * 3600
|
||||
min_sec = tonumber(string.sub(time, 4, 5)) * 60
|
||||
get_sec = tonumber(string.sub(time, 7, 8))
|
||||
return (hour_sec + min_sec + get_sec)
|
||||
end
|
||||
|
||||
--- Get time difference in seconds.
|
||||
--
|
||||
-- @tparam string base The time to compare from (`HH:MM:SS`).
|
||||
-- @tparam string base The time to compare to (`HH:MM:SS`).
|
||||
-- @treturn int Number of seconds between the two times.
|
||||
function time.time_diff(base, compare)
|
||||
local diff = time.hhmmss_to_seconds(base) - time.hhmmss_to_seconds(compare)
|
||||
return diff
|
||||
end
|
||||
|
||||
return time
|
BIN
.config/awesome/extras/bling/icons/layouts/centered.png
(Stored with Git LFS)
Executable file
BIN
.config/awesome/extras/bling/icons/layouts/centered.png
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
.config/awesome/extras/bling/icons/layouts/deck.png
(Stored with Git LFS)
Executable file
BIN
.config/awesome/extras/bling/icons/layouts/deck.png
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
.config/awesome/extras/bling/icons/layouts/equalarea.png
(Stored with Git LFS)
Executable file
BIN
.config/awesome/extras/bling/icons/layouts/equalarea.png
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
.config/awesome/extras/bling/icons/layouts/horizontal.png
(Stored with Git LFS)
Executable file
BIN
.config/awesome/extras/bling/icons/layouts/horizontal.png
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
.config/awesome/extras/bling/icons/layouts/mstab.png
(Stored with Git LFS)
Executable file
BIN
.config/awesome/extras/bling/icons/layouts/mstab.png
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
.config/awesome/extras/bling/icons/layouts/vertical.png
(Stored with Git LFS)
Executable file
BIN
.config/awesome/extras/bling/icons/layouts/vertical.png
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
.config/awesome/extras/bling/images/bling_banner-2x.png
(Stored with Git LFS)
Executable file
BIN
.config/awesome/extras/bling/images/bling_banner-2x.png
(Stored with Git LFS)
Executable file
Binary file not shown.
BIN
.config/awesome/extras/bling/images/bling_banner.png
(Stored with Git LFS)
Executable file
BIN
.config/awesome/extras/bling/images/bling_banner.png
(Stored with Git LFS)
Executable file
Binary file not shown.
7
.config/awesome/extras/bling/init.lua
Executable file
7
.config/awesome/extras/bling/init.lua
Executable file
@@ -0,0 +1,7 @@
|
||||
return {
|
||||
layout = require(... .. ".layout"),
|
||||
--module = require(... .. ".module"),
|
||||
--helpers = require(... .. ".helpers"),
|
||||
--signal = require(... .. ".signal"),
|
||||
--widget = require(... .. ".widget"),
|
||||
}
|
84
.config/awesome/extras/bling/layout/centered.lua
Executable file
84
.config/awesome/extras/bling/layout/centered.lua
Executable file
@@ -0,0 +1,84 @@
|
||||
local awful = require("awful")
|
||||
local math = math
|
||||
|
||||
local mylayout = {}
|
||||
|
||||
mylayout.name = "centered"
|
||||
|
||||
function mylayout.arrange(p)
|
||||
local area = p.workarea
|
||||
local t = p.tag or screen[p.screen].selected_tag
|
||||
local mwfact = t.master_width_factor
|
||||
local nmaster = math.min(t.master_count, #p.clients)
|
||||
local nslaves = #p.clients - nmaster
|
||||
|
||||
local master_area_width = area.width * mwfact
|
||||
local slave_area_width = area.width - master_area_width
|
||||
local master_area_x = area.x + 0.5 * slave_area_width
|
||||
|
||||
local number_of_left_sided_slaves = math.floor(nslaves / 2)
|
||||
local number_of_right_sided_slaves = nslaves - number_of_left_sided_slaves
|
||||
local left_iterator = 0
|
||||
local right_iterator = 0
|
||||
|
||||
-- Special case: no maters -> rrelapse into awesomes fair layout
|
||||
if t.master_count == 0 then
|
||||
awful.layout.suit.fair.arrange(p)
|
||||
return
|
||||
end
|
||||
|
||||
-- Special case: one slave -> relapse into awesomes masterstack tile layout
|
||||
if nslaves == 1 then
|
||||
awful.layout.suit.tile.right.arrange(p)
|
||||
return
|
||||
end
|
||||
|
||||
-- Special case: no slaves -> fullscreen master area
|
||||
if nslaves < 1 then
|
||||
master_area_width = area.width
|
||||
master_area_x = area.x
|
||||
end
|
||||
|
||||
-- iterate through masters
|
||||
for idx = 1, nmaster do
|
||||
local c = p.clients[idx]
|
||||
local g
|
||||
g = {
|
||||
x = master_area_x,
|
||||
y = area.y + (nmaster - idx) * (area.height / nmaster),
|
||||
width = master_area_width,
|
||||
height = area.height / nmaster,
|
||||
}
|
||||
p.geometries[c] = g
|
||||
end
|
||||
|
||||
-- iterate through slaves
|
||||
for idx = 1, nslaves do -- idx=nmaster+1,#p.clients do
|
||||
local c = p.clients[idx + nmaster]
|
||||
local g
|
||||
if idx % 2 == 0 then
|
||||
g = {
|
||||
x = area.x,
|
||||
y = area.y
|
||||
+ left_iterator
|
||||
* (area.height / number_of_left_sided_slaves),
|
||||
width = slave_area_width / 2,
|
||||
height = area.height / number_of_left_sided_slaves,
|
||||
}
|
||||
left_iterator = left_iterator + 1
|
||||
else
|
||||
g = {
|
||||
x = area.x + master_area_width + slave_area_width / 2,
|
||||
y = area.y
|
||||
+ right_iterator
|
||||
* (area.height / number_of_right_sided_slaves),
|
||||
width = slave_area_width / 2,
|
||||
height = area.height / number_of_right_sided_slaves,
|
||||
}
|
||||
right_iterator = right_iterator + 1
|
||||
end
|
||||
p.geometries[c] = g
|
||||
end
|
||||
end
|
||||
|
||||
return mylayout
|
37
.config/awesome/extras/bling/layout/deck.lua
Executable file
37
.config/awesome/extras/bling/layout/deck.lua
Executable file
@@ -0,0 +1,37 @@
|
||||
local mylayout = {}
|
||||
|
||||
mylayout.name = "deck"
|
||||
|
||||
function mylayout.arrange(p)
|
||||
local area = p.workarea
|
||||
local t = p.tag or screen[p.screen].selected_tag
|
||||
local client_count = #p.clients
|
||||
|
||||
if client_count == 1 then
|
||||
local c = p.clients[1]
|
||||
local g = {
|
||||
x = area.x,
|
||||
y = area.y,
|
||||
width = area.width,
|
||||
height = area.height,
|
||||
}
|
||||
p.geometries[c] = g
|
||||
return
|
||||
end
|
||||
|
||||
local xoffset = area.width * 0.1 / (client_count - 1)
|
||||
local yoffset = area.height * 0.1 / (client_count - 1)
|
||||
|
||||
for idx = 1, client_count do
|
||||
local c = p.clients[idx]
|
||||
local g = {
|
||||
x = area.x + (idx - 1) * xoffset,
|
||||
y = area.y + (idx - 1) * yoffset,
|
||||
width = area.width - (xoffset * (client_count - 1)),
|
||||
height = area.height - (yoffset * (client_count - 1)),
|
||||
}
|
||||
p.geometries[c] = g
|
||||
end
|
||||
end
|
||||
|
||||
return mylayout
|
77
.config/awesome/extras/bling/layout/equalarea.lua
Executable file
77
.config/awesome/extras/bling/layout/equalarea.lua
Executable file
@@ -0,0 +1,77 @@
|
||||
local math = math
|
||||
local screen = screen
|
||||
local mylayout = {}
|
||||
mylayout.name = "equalarea"
|
||||
|
||||
local function divide(p, g, low, high, cls, mwfact, mcount)
|
||||
if low == high then
|
||||
p.geometries[cls[low]] = g
|
||||
else
|
||||
local masters = math.max(0, math.min(mcount, high) - low + 1)
|
||||
local numblock = high - low + 1
|
||||
local slaves = numblock - masters
|
||||
local smalldiv
|
||||
if numblock > 5 and (numblock % 5) == 0 then
|
||||
smalldiv = math.floor(numblock / 5)
|
||||
else
|
||||
if (numblock % 3) == 0 then
|
||||
smalldiv = math.floor(numblock / 3)
|
||||
else
|
||||
smalldiv = math.floor(numblock / 2)
|
||||
end
|
||||
end
|
||||
local bigdiv = numblock - smalldiv
|
||||
local smallmasters = math.min(masters, smalldiv)
|
||||
local bigmasters = masters - smallmasters
|
||||
local smallg = {}
|
||||
local bigg = {}
|
||||
smallg.x = g.x
|
||||
smallg.y = g.y
|
||||
if g.width > (g.height * 1.3) then
|
||||
smallg.height = g.height
|
||||
bigg.height = g.height
|
||||
bigg.width = math.floor(
|
||||
g.width
|
||||
* (bigmasters * (mwfact - 1) + bigdiv)
|
||||
/ (slaves + mwfact * masters)
|
||||
)
|
||||
smallg.width = g.width - bigg.width
|
||||
bigg.y = g.y
|
||||
bigg.x = g.x + smallg.width
|
||||
else
|
||||
smallg.width = g.width
|
||||
bigg.width = g.width
|
||||
bigg.height = math.floor(
|
||||
g.height
|
||||
* (bigmasters * (mwfact - 1) + bigdiv)
|
||||
/ (slaves + mwfact * masters)
|
||||
)
|
||||
smallg.height = g.height - bigg.height
|
||||
bigg.x = g.x
|
||||
bigg.y = g.y + smallg.height
|
||||
end
|
||||
divide(p, smallg, low, high - bigdiv, cls, mwfact, mcount)
|
||||
divide(p, bigg, low + smalldiv, high, cls, mwfact, mcount)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
function mylayout.arrange(p)
|
||||
local t = p.tag or screen[p.screen].selected_tag
|
||||
local wa = p.workarea
|
||||
local cls = p.clients
|
||||
|
||||
if #cls == 0 then
|
||||
return
|
||||
end
|
||||
local mwfact = t.master_width_factor * 2
|
||||
local mcount = t.master_count
|
||||
local g = {}
|
||||
g.height = wa.height
|
||||
g.width = wa.width
|
||||
g.x = wa.x
|
||||
g.y = wa.y
|
||||
divide(p, g, 1, #cls, cls, mwfact, mcount)
|
||||
end
|
||||
|
||||
return mylayout
|
56
.config/awesome/extras/bling/layout/horizontal.lua
Executable file
56
.config/awesome/extras/bling/layout/horizontal.lua
Executable file
@@ -0,0 +1,56 @@
|
||||
local math = math
|
||||
|
||||
local mylayout = {}
|
||||
|
||||
mylayout.name = "horizontal"
|
||||
|
||||
function mylayout.arrange(p)
|
||||
local area = p.workarea
|
||||
local t = p.tag or screen[p.screen].selected_tag
|
||||
local mwfact = t.master_width_factor
|
||||
local nmaster = math.min(t.master_count, #p.clients)
|
||||
local nslaves = #p.clients - nmaster
|
||||
|
||||
local master_area_height = area.height * mwfact
|
||||
local slave_area_height = area.height - master_area_height
|
||||
|
||||
-- Special case: no slaves
|
||||
if nslaves == 0 then
|
||||
master_area_height = area.height
|
||||
slave_area_height = 0
|
||||
end
|
||||
|
||||
-- Special case: no masters
|
||||
if nmaster == 0 then
|
||||
master_area_height = 0
|
||||
slave_area_height = area.height
|
||||
end
|
||||
|
||||
-- itearte through masters
|
||||
for idx = 1, nmaster do
|
||||
local c = p.clients[idx]
|
||||
local g = {
|
||||
x = area.x + (idx - 1) * (area.width / nmaster),
|
||||
y = area.y,
|
||||
width = area.width / nmaster,
|
||||
height = master_area_height,
|
||||
}
|
||||
p.geometries[c] = g
|
||||
end
|
||||
|
||||
-- iterate through slaves
|
||||
for idx = 1, nslaves do
|
||||
local c = p.clients[idx + nmaster]
|
||||
local g = {
|
||||
x = area.x,
|
||||
y = area.y
|
||||
+ master_area_height
|
||||
+ (idx - 1) * (slave_area_height / nslaves),
|
||||
width = area.width,
|
||||
height = slave_area_height / nslaves,
|
||||
}
|
||||
p.geometries[c] = g
|
||||
end
|
||||
end
|
||||
|
||||
return mylayout
|
44
.config/awesome/extras/bling/layout/init.lua
Executable file
44
.config/awesome/extras/bling/layout/init.lua
Executable file
@@ -0,0 +1,44 @@
|
||||
local beautiful = require("beautiful")
|
||||
local gears = require("gears")
|
||||
|
||||
local M = {}
|
||||
local relative_lua_path = tostring(...)
|
||||
|
||||
local function get_layout_icon_path(name)
|
||||
local relative_icon_path = relative_lua_path
|
||||
:match("^.*bling"):gsub("%.", "/")
|
||||
.. "/icons/layouts/" .. name .. ".png"
|
||||
|
||||
for p in package.path:gmatch('([^;]+)') do
|
||||
p = p:gsub("?.*", "")
|
||||
local absolute_icon_path = p .. relative_icon_path
|
||||
if gears.filesystem.file_readable(absolute_icon_path) then
|
||||
return absolute_icon_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function get_icon(icon_raw)
|
||||
if icon_raw ~= nil then
|
||||
return gears.color.recolor_image(icon_raw, "#e6e6e6")
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local layouts = {
|
||||
"mstab",
|
||||
"vertical",
|
||||
"horizontal",
|
||||
"centered",
|
||||
"equalarea",
|
||||
"deck"
|
||||
}
|
||||
|
||||
for _, layout_name in ipairs(layouts) do
|
||||
local icon_raw = get_layout_icon_path(layout_name)
|
||||
beautiful["layout_" .. layout_name] = get_icon(icon_raw)
|
||||
M[layout_name] = require(... .. "." .. layout_name)
|
||||
end
|
||||
|
||||
return M
|
249
.config/awesome/extras/bling/layout/mstab.lua
Executable file
249
.config/awesome/extras/bling/layout/mstab.lua
Executable file
@@ -0,0 +1,249 @@
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local wibox = require("wibox")
|
||||
local beautiful = require("beautiful")
|
||||
|
||||
local mylayout = {}
|
||||
|
||||
mylayout.name = "mstab"
|
||||
|
||||
local tabbar_disable = beautiful.mstab_bar_disable or false
|
||||
local tabbar_ontop = beautiful.mstab_bar_ontop or false
|
||||
local tabbar_padding = beautiful.mstab_bar_padding or "default"
|
||||
local border_radius = beautiful.mstab_border_radius
|
||||
or beautiful.border_radius
|
||||
or 0
|
||||
local tabbar_position = beautiful.mstab_tabbar_position
|
||||
or beautiful.tabbar_position
|
||||
or "top"
|
||||
|
||||
local bar_style = beautiful.mstab_tabbar_style
|
||||
or beautiful.tabbar_style
|
||||
or "default"
|
||||
local bar = require(
|
||||
tostring(...):match(".*bling") .. ".widget.tabbar." .. bar_style
|
||||
)
|
||||
local tabbar_size = bar.size
|
||||
or beautiful.mstab_bar_height
|
||||
or beautiful.tabbar_size
|
||||
or 40
|
||||
local dont_resize_slaves = beautiful.mstab_dont_resize_slaves or false
|
||||
|
||||
-- The top_idx is the idx of the slave clients (excluding all master clients)
|
||||
-- that should be on top of all other slave clients ("the focused slave")
|
||||
-- by creating a variable outside of the arrange function, this layout can "remember" that client
|
||||
-- by creating it as a new property of every tag, this layout can be active on different tags and
|
||||
-- still have different "focused slave clients"
|
||||
for idx, tag in ipairs(root.tags()) do
|
||||
tag.top_idx = 1
|
||||
end
|
||||
|
||||
-- Haven't found a signal that is emitted when a new tag is added. That should work though
|
||||
-- since you can't use a layout on a tag that you haven't selected previously
|
||||
tag.connect_signal("property::selected", function(t)
|
||||
if not t.top_idx then
|
||||
t.top_idx = 1
|
||||
end
|
||||
end)
|
||||
|
||||
function update_tabbar(
|
||||
clients,
|
||||
t,
|
||||
top_idx,
|
||||
area,
|
||||
master_area_width,
|
||||
slave_area_width
|
||||
)
|
||||
local s = t.screen
|
||||
|
||||
-- create the list of clients for the tabbar
|
||||
local clientlist = bar.layout()
|
||||
for idx, c in ipairs(clients) do
|
||||
-- focus with right click, kill with mid click, minimize with left click
|
||||
local buttons = gears.table.join(
|
||||
awful.button({}, 1, function()
|
||||
c:raise()
|
||||
client.focus = c
|
||||
end),
|
||||
awful.button({}, 2, function()
|
||||
c:kill()
|
||||
end),
|
||||
awful.button({}, 3, function()
|
||||
c.minimized = true
|
||||
end)
|
||||
)
|
||||
local client_box = bar.create(c, (idx == top_idx), buttons)
|
||||
clientlist:add(client_box)
|
||||
end
|
||||
|
||||
-- if no tabbar exists, create one
|
||||
if not s.tabbar then
|
||||
s.tabbar = wibox({
|
||||
ontop = tabbar_ontop,
|
||||
shape = function(cr, width, height)
|
||||
gears.shape.rounded_rect(cr, width, height, border_radius)
|
||||
end,
|
||||
bg = bar.bg_normal,
|
||||
visible = true,
|
||||
})
|
||||
|
||||
-- Change visibility of the tab bar when layout, selected tag or number of clients (visible, master, slave) changes
|
||||
local function adjust_visiblity(t)
|
||||
s.tabbar.visible = (#t:clients() - t.master_count > 1)
|
||||
and (t.layout.name == mylayout.name)
|
||||
end
|
||||
|
||||
tag.connect_signal("property::selected", function(t)
|
||||
adjust_visiblity(t)
|
||||
end)
|
||||
tag.connect_signal("property::layout", function(t, layout)
|
||||
adjust_visiblity(t)
|
||||
end)
|
||||
tag.connect_signal("tagged", function(t, c)
|
||||
adjust_visiblity(t)
|
||||
end)
|
||||
tag.connect_signal("untagged", function(t, c)
|
||||
adjust_visiblity(t)
|
||||
end)
|
||||
tag.connect_signal("property::master_count", function(t)
|
||||
adjust_visiblity(t)
|
||||
end)
|
||||
client.connect_signal("property::minimized", function(c)
|
||||
local t = c.first_tag
|
||||
adjust_visiblity(t)
|
||||
end)
|
||||
end
|
||||
|
||||
-- update the tabbar size and position (to support gap size change on the fly)
|
||||
if tabbar_position == "top" then
|
||||
s.tabbar.x = area.x + master_area_width + t.gap
|
||||
s.tabbar.y = area.y + t.gap
|
||||
s.tabbar.width = slave_area_width - 2 * t.gap
|
||||
s.tabbar.height = tabbar_size
|
||||
elseif tabbar_position == "bottom" then
|
||||
s.tabbar.x = area.x + master_area_width + t.gap
|
||||
s.tabbar.y = area.y + area.height - tabbar_size - t.gap
|
||||
s.tabbar.width = slave_area_width - 2 * t.gap
|
||||
s.tabbar.height = tabbar_size
|
||||
elseif tabbar_position == "left" then
|
||||
s.tabbar.x = area.x + master_area_width + t.gap
|
||||
s.tabbar.y = area.y + t.gap
|
||||
s.tabbar.width = tabbar_size
|
||||
s.tabbar.height = area.height - 2 * t.gap
|
||||
elseif tabbar_position == "right" then
|
||||
s.tabbar.x = area.x
|
||||
+ master_area_width
|
||||
+ slave_area_width
|
||||
- tabbar_size
|
||||
- t.gap
|
||||
s.tabbar.y = area.y + t.gap
|
||||
s.tabbar.width = tabbar_size
|
||||
s.tabbar.height = area.height - 2 * t.gap
|
||||
end
|
||||
|
||||
-- update clientlist
|
||||
s.tabbar:setup({ layout = wibox.layout.flex.horizontal, clientlist })
|
||||
end
|
||||
|
||||
function mylayout.arrange(p)
|
||||
local area = p.workarea
|
||||
local t = p.tag or screen[p.screen].selected_tag
|
||||
local s = t.screen
|
||||
local mwfact = t.master_width_factor
|
||||
local nmaster = math.min(t.master_count, #p.clients)
|
||||
local nslaves = #p.clients - nmaster
|
||||
|
||||
local master_area_width = area.width * mwfact
|
||||
local slave_area_width = area.width - master_area_width
|
||||
|
||||
-- "default" means that it uses standard useless gap size
|
||||
if tabbar_padding == "default" then
|
||||
tabbar_padding = 2 * t.gap
|
||||
end
|
||||
|
||||
-- Special case: No masters -> full screen slave width
|
||||
if nmaster == 0 then
|
||||
master_area_width = 1
|
||||
slave_area_width = area.width
|
||||
end
|
||||
|
||||
-- Special case: One or zero slaves -> no tabbar (essentially tile right)
|
||||
if nslaves <= 1 then
|
||||
-- since update_tabbar isnt called that way we have to hide it manually
|
||||
if s.tabbar then
|
||||
s.tabbar.visible = false
|
||||
end
|
||||
-- otherwise just do tile right
|
||||
awful.layout.suit.tile.right.arrange(p)
|
||||
return
|
||||
end
|
||||
|
||||
-- Iterate through masters
|
||||
for idx = 1, nmaster do
|
||||
local c = p.clients[idx]
|
||||
local g = {
|
||||
x = area.x,
|
||||
y = area.y + (idx - 1) * (area.height / nmaster),
|
||||
width = master_area_width,
|
||||
height = area.height / nmaster,
|
||||
}
|
||||
p.geometries[c] = g
|
||||
end
|
||||
|
||||
local tabbar_size_change = 0
|
||||
local tabbar_width_change = 0
|
||||
local tabbar_y_change = 0
|
||||
local tabbar_x_change = 0
|
||||
if not tabbar_disable then
|
||||
if tabbar_position == "top" then
|
||||
tabbar_size_change = tabbar_size + tabbar_padding
|
||||
tabbar_y_change = tabbar_size + tabbar_padding
|
||||
elseif tabbar_position == "bottom" then
|
||||
tabbar_size_change = tabbar_size + tabbar_padding
|
||||
elseif tabbar_position == "left" then
|
||||
tabbar_width_change = tabbar_size + tabbar_padding
|
||||
tabbar_x_change = tabbar_size + tabbar_padding
|
||||
elseif tabbar_position == "right" then
|
||||
tabbar_width_change = tabbar_size + tabbar_padding
|
||||
end
|
||||
end
|
||||
|
||||
-- Iterate through slaves
|
||||
-- (also creates a list of all slave clients for update_tabbar)
|
||||
local slave_clients = {}
|
||||
for idx = 1, nslaves do
|
||||
local c = p.clients[idx + nmaster]
|
||||
slave_clients[#slave_clients + 1] = c
|
||||
if c == client.focus then
|
||||
t.top_idx = #slave_clients
|
||||
end
|
||||
local g = {
|
||||
x = area.x + master_area_width + tabbar_x_change,
|
||||
y = area.y + tabbar_y_change,
|
||||
width = slave_area_width - tabbar_width_change,
|
||||
height = area.height - tabbar_size_change,
|
||||
}
|
||||
if not dont_resize_slaves and idx ~= t.top_idx then
|
||||
g = {
|
||||
x = area.x + master_area_width + slave_area_width / 4,
|
||||
y = area.y + tabbar_size + area.height / 4,
|
||||
width = slave_area_width / 2,
|
||||
height = area.height / 4 - tabbar_size,
|
||||
}
|
||||
end
|
||||
p.geometries[c] = g
|
||||
end
|
||||
|
||||
if not tabbar_disable then
|
||||
update_tabbar(
|
||||
slave_clients,
|
||||
t,
|
||||
t.top_idx,
|
||||
area,
|
||||
master_area_width,
|
||||
slave_area_width
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
return mylayout
|
56
.config/awesome/extras/bling/layout/vertical.lua
Executable file
56
.config/awesome/extras/bling/layout/vertical.lua
Executable file
@@ -0,0 +1,56 @@
|
||||
local math = math
|
||||
|
||||
local mylayout = {}
|
||||
|
||||
mylayout.name = "vertical"
|
||||
|
||||
function mylayout.arrange(p)
|
||||
local area = p.workarea
|
||||
local t = p.tag or screen[p.screen].selected_tag
|
||||
local mwfact = t.master_width_factor
|
||||
local nmaster = math.min(t.master_count, #p.clients)
|
||||
local nslaves = #p.clients - nmaster
|
||||
|
||||
local master_area_width = area.width * mwfact
|
||||
local slave_area_width = area.width - master_area_width
|
||||
|
||||
-- Special case: no slaves
|
||||
if nslaves == 0 then
|
||||
master_area_width = area.width
|
||||
slave_area_width = 0
|
||||
end
|
||||
|
||||
-- Special case: no masters
|
||||
if nmaster == 0 then
|
||||
master_area_width = 0
|
||||
slave_area_width = area.width
|
||||
end
|
||||
|
||||
-- iterate through masters
|
||||
for idx = 1, nmaster do
|
||||
local c = p.clients[idx]
|
||||
local g = {
|
||||
x = area.x,
|
||||
y = area.y + (idx - 1) * (area.height / nmaster),
|
||||
width = master_area_width,
|
||||
height = area.height / nmaster,
|
||||
}
|
||||
p.geometries[c] = g
|
||||
end
|
||||
|
||||
-- itearte through slaves
|
||||
for idx = 1, nslaves do
|
||||
local c = p.clients[idx + nmaster]
|
||||
local g = {
|
||||
x = area.x
|
||||
+ master_area_width
|
||||
+ (idx - 1) * (slave_area_width / nslaves),
|
||||
y = area.y,
|
||||
width = slave_area_width / nslaves,
|
||||
height = area.height,
|
||||
}
|
||||
p.geometries[c] = g
|
||||
end
|
||||
end
|
||||
|
||||
return mylayout
|
44
.config/awesome/extras/bling/module/flash_focus.lua
Executable file
44
.config/awesome/extras/bling/module/flash_focus.lua
Executable file
@@ -0,0 +1,44 @@
|
||||
local gears = require("gears")
|
||||
local beautiful = require("beautiful")
|
||||
|
||||
local op = beautiful.flash_focus_start_opacity or 0.6
|
||||
local stp = beautiful.flash_focus_step or 0.01
|
||||
|
||||
local flashfocus = function(c)
|
||||
if c then
|
||||
c.opacity = op
|
||||
local q = op
|
||||
local g = gears.timer({
|
||||
timeout = stp,
|
||||
call_now = false,
|
||||
autostart = true,
|
||||
})
|
||||
|
||||
g:connect_signal("timeout", function()
|
||||
if not c.valid then
|
||||
return
|
||||
end
|
||||
if q >= 1 then
|
||||
c.opacity = 1
|
||||
g:stop()
|
||||
else
|
||||
c.opacity = q
|
||||
q = q + stp
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Bring the focused client to the top
|
||||
if c then
|
||||
c:raise()
|
||||
end
|
||||
end
|
||||
|
||||
local enable = function()
|
||||
client.connect_signal("focus", flashfocus)
|
||||
end
|
||||
local disable = function()
|
||||
client.disconnect_signal("focus", flashfocus)
|
||||
end
|
||||
|
||||
return { enable = enable, disable = disable, flashfocus = flashfocus }
|
8
.config/awesome/extras/bling/module/init.lua
Executable file
8
.config/awesome/extras/bling/module/init.lua
Executable file
@@ -0,0 +1,8 @@
|
||||
return {
|
||||
window_swallowing = require(... .. ".window_swallowing"),
|
||||
tiled_wallpaper = require(... .. ".tiled_wallpaper"),
|
||||
wallpaper = require(... .. ".wallpaper"),
|
||||
flash_focus = require(... .. ".flash_focus"),
|
||||
tabbed = require(... .. ".tabbed"),
|
||||
scratchpad = require(... .. ".scratchpad"),
|
||||
}
|
374
.config/awesome/extras/bling/module/scratchpad.lua
Executable file
374
.config/awesome/extras/bling/module/scratchpad.lua
Executable file
@@ -0,0 +1,374 @@
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local naughty = require("naughty")
|
||||
local helpers = require(tostring(...):match(".*bling") .. ".helpers")
|
||||
local capi = { awesome = awesome, client = client }
|
||||
local ruled = capi.awesome.version ~= "v4.3" and require("ruled") or nil
|
||||
local pairs = pairs
|
||||
|
||||
local Scratchpad = { mt = {} }
|
||||
|
||||
--- Called when the turn off animation has ended
|
||||
local function on_animate_turn_off_end(self, tag)
|
||||
-- When toggling off a scratchpad that's present on multiple tags
|
||||
-- depsite still being unminizmied on the other tags it will become invisible
|
||||
-- as it's position could be outside the screen from the animation
|
||||
self.client:geometry({
|
||||
x = self.geometry.x + self.client.screen.geometry.x,
|
||||
y = self.geometry.y + self.client.screen.geometry.y,
|
||||
width = self.geometry.width,
|
||||
height = self.geometry.height,
|
||||
})
|
||||
|
||||
helpers.client.turn_off(self.client, tag)
|
||||
|
||||
self.turning_off = false
|
||||
|
||||
self:emit_signal("turn_off", self.client)
|
||||
end
|
||||
|
||||
--- The turn off animation
|
||||
local function animate_turn_off(self, anim, axis)
|
||||
self.screen_on_toggled_scratchpad = self.client.screen
|
||||
self.tag_on_toggled_scratchpad = self.screen_on_toggled_scratchpad.selected_tag
|
||||
|
||||
if self.client.floating == false then
|
||||
-- Save the client geometry before floating it
|
||||
local non_floating_x = self.client.x
|
||||
local non_floating_y = self.client.y
|
||||
local non_floating_width = self.client.width
|
||||
local non_floating_height = self.client.height
|
||||
|
||||
-- Can't animate non floating clients
|
||||
self.client.floating = true
|
||||
|
||||
-- Set the client geometry back to what it was before floating it
|
||||
self.client:geometry({
|
||||
x = non_floating_x,
|
||||
y = non_floating_y,
|
||||
width = non_floating_width,
|
||||
height = non_floating_height,
|
||||
})
|
||||
end
|
||||
|
||||
if axis == "x" then
|
||||
anim.pos = self.client.x
|
||||
else
|
||||
anim.pos = self.client.y
|
||||
end
|
||||
|
||||
anim:set(anim:initial())
|
||||
end
|
||||
|
||||
-- Handles changing tag mid animation
|
||||
local function abort_if_tag_was_switched(self)
|
||||
-- Check for the following scenerio:
|
||||
-- Toggle on scratchpad at tag 1
|
||||
-- Toggle on scratchpad at tag 2
|
||||
-- Toggle off scratchpad at tag 1
|
||||
-- Switch to tag 2
|
||||
-- Outcome: The client will remain on tag 1 and will instead be removed from tag 2
|
||||
if (self.turning_off) and (self.screen_on_toggled_scratchpad and
|
||||
self.screen_on_toggled_scratchpad.selected_tag) ~= self.tag_on_toggled_scratchpad
|
||||
then
|
||||
if self.rubato.x then
|
||||
self.rubato.x:abort()
|
||||
end
|
||||
if self.rubato.y then
|
||||
self.rubato.y:abort()
|
||||
end
|
||||
on_animate_turn_off_end(self, self.tag_on_toggled_scratchpad)
|
||||
self.screen_on_toggled_scratchpad.selected_tag = nil
|
||||
self.tag_on_toggled_scratchpad = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- The turn on animation
|
||||
local function animate_turn_on(self, anim, axis)
|
||||
-- Check for the following scenerio:
|
||||
-- Toggle on scratchpad at tag 1
|
||||
-- Toggle on scratchpad at tag 2
|
||||
-- The animation will instantly end
|
||||
-- as the timer pos is already at the on position
|
||||
-- from toggling on the scratchpad at tag 1
|
||||
if axis == "x" and anim.pos == self.geometry.x then
|
||||
anim.pos = anim:initial()
|
||||
else
|
||||
if anim.pos == self.geometry.y then
|
||||
anim.pos = anim:initial()
|
||||
end
|
||||
end
|
||||
|
||||
if axis == "x" then
|
||||
anim:set(self.geometry.x)
|
||||
else
|
||||
anim:set(self.geometry.y)
|
||||
end
|
||||
end
|
||||
|
||||
--- Creates a new scratchpad object based on the argument
|
||||
--
|
||||
-- @param args A table of possible arguments
|
||||
-- @return The new scratchpad object
|
||||
function Scratchpad:new(args)
|
||||
args = args or {}
|
||||
if args.awestore then
|
||||
naughty.notify({
|
||||
title = "Bling Error",
|
||||
text = "Awestore is no longer supported! Please take a look at the scratchpad documentation and use rubato for animations instead.",
|
||||
})
|
||||
end
|
||||
|
||||
args.rubato = args.rubato or {}
|
||||
|
||||
local ret = gears.object{}
|
||||
gears.table.crush(ret, Scratchpad)
|
||||
gears.table.crush(ret, args)
|
||||
|
||||
if ret.rubato.x then
|
||||
ret.rubato.x:subscribe(function(pos)
|
||||
if ret.client and ret.client.valid then
|
||||
ret.client.x = pos
|
||||
end
|
||||
abort_if_tag_was_switched(ret)
|
||||
end)
|
||||
|
||||
ret.rubato.x.ended:subscribe(function()
|
||||
if ((ret.rubato.y and ret.rubato.y.state == false) or (ret.rubato.y == nil)) and ret.turning_off == true then
|
||||
on_animate_turn_off_end(ret)
|
||||
end
|
||||
end)
|
||||
end
|
||||
if ret.rubato.y then
|
||||
ret.rubato.y:subscribe(function(pos)
|
||||
if ret.client and ret.client.valid then
|
||||
ret.client.y = pos
|
||||
end
|
||||
abort_if_tag_was_switched(ret)
|
||||
end)
|
||||
|
||||
ret.rubato.y.ended:subscribe(function()
|
||||
if ((ret.rubato.x and ret.rubato.x.state == false) or (ret.rubato.x == nil)) and ret.turning_off == true then
|
||||
on_animate_turn_off_end(ret)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Find all clients that satisfy the the rule
|
||||
--
|
||||
-- @return A list of all clients that satisfy the rule
|
||||
function Scratchpad:find()
|
||||
return helpers.client.find(self.rule)
|
||||
end
|
||||
|
||||
--- Applies the objects scratchpad properties to a given client
|
||||
--
|
||||
-- @param c A client to which to apply the properties
|
||||
function Scratchpad:apply(c)
|
||||
if not c or not c.valid then
|
||||
return
|
||||
end
|
||||
c.floating = self.floating
|
||||
c.sticky = self.sticky
|
||||
c.fullscreen = false
|
||||
c.maximized = false
|
||||
c:geometry({
|
||||
x = self.geometry.x + awful.screen.focused().geometry.x,
|
||||
y = self.geometry.y + awful.screen.focused().geometry.y,
|
||||
width = self.geometry.width,
|
||||
height = self.geometry.height,
|
||||
})
|
||||
|
||||
if self.autoclose then
|
||||
c:connect_signal("unfocus", function(c1)
|
||||
c1.sticky = false -- client won't turn off if sticky
|
||||
helpers.client.turn_off(c1)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
--- Turns the scratchpad on
|
||||
function Scratchpad:turn_on()
|
||||
self.client = self:find()[1]
|
||||
|
||||
local anim_x = self.rubato.x
|
||||
local anim_y = self.rubato.y
|
||||
|
||||
local in_anim = false
|
||||
if (anim_x and anim_x.state == true) or (anim_y and anim_y.state == true) then
|
||||
in_anim = true
|
||||
end
|
||||
|
||||
if self.client and not in_anim and self.client.first_tag and self.client.first_tag.selected then
|
||||
self.client:raise()
|
||||
capi.client.focus = self.client
|
||||
return
|
||||
end
|
||||
if self.client and not in_anim then
|
||||
-- if a client was found, turn it on
|
||||
if self.reapply then
|
||||
self:apply(self.client)
|
||||
end
|
||||
-- c.sticky was set to false in turn_off so it has to be reapplied anyway
|
||||
self.client.sticky = self.sticky
|
||||
|
||||
if anim_x then
|
||||
animate_turn_on(self, anim_x, "x")
|
||||
end
|
||||
if anim_y then
|
||||
animate_turn_on(self, anim_y, "y")
|
||||
end
|
||||
|
||||
helpers.client.turn_on(self.client)
|
||||
self:emit_signal("turn_on", self.client)
|
||||
|
||||
return
|
||||
end
|
||||
if not self.client then
|
||||
-- if no client was found, spawn one, find the corresponding window,
|
||||
-- apply the properties only once (until the next closing)
|
||||
local pid = awful.spawn.with_shell(self.command)
|
||||
if capi.awesome.version ~= "v4.3" then
|
||||
ruled.client.append_rule({
|
||||
id = "scratchpad",
|
||||
rule = self.rule,
|
||||
properties = {
|
||||
-- If a scratchpad is opened it should spawn at the current tag
|
||||
-- the same way it will behave if the client was already open
|
||||
tag = awful.screen.focused().selected_tag,
|
||||
switch_to_tags = false,
|
||||
-- Hide the client until the gemoetry rules are applied
|
||||
hidden = true,
|
||||
minimized = true,
|
||||
},
|
||||
callback = function(c)
|
||||
-- For a reason I can't quite get the gemotery rules will fail to apply unless we use this timer
|
||||
gears.timer({
|
||||
timeout = 0.15,
|
||||
autostart = true,
|
||||
single_shot = true,
|
||||
callback = function()
|
||||
self.client = c
|
||||
|
||||
self:apply(c)
|
||||
c.hidden = false
|
||||
c.minimized = false
|
||||
-- Some clients fail to gain focus
|
||||
c:activate({})
|
||||
|
||||
if anim_x then
|
||||
animate_turn_on(self, anim_x, "x")
|
||||
end
|
||||
if anim_y then
|
||||
animate_turn_on(self, anim_y, "y")
|
||||
end
|
||||
|
||||
self:emit_signal("inital_apply", c)
|
||||
|
||||
-- Discord spawns 2 windows, so keep the rule until the 2nd window shows
|
||||
if c.name ~= "Discord Updater" then
|
||||
ruled.client.remove_rule("scratchpad")
|
||||
end
|
||||
-- In a case Discord is killed before the second window spawns
|
||||
c:connect_signal("request::unmanage", function()
|
||||
ruled.client.remove_rule("scratchpad")
|
||||
end)
|
||||
end,
|
||||
})
|
||||
end,
|
||||
})
|
||||
else
|
||||
local function inital_apply(c1)
|
||||
if helpers.client.is_child_of(c1, pid) then
|
||||
self.client = c1
|
||||
|
||||
self:apply(c1)
|
||||
if anim_x then
|
||||
animate_turn_on(self, anim_x, "x")
|
||||
end
|
||||
if anim_y then
|
||||
animate_turn_on(self, anim_y, "y")
|
||||
end
|
||||
self:emit_signal("inital_apply", c1)
|
||||
self.client.disconnect_signal("manage", inital_apply)
|
||||
end
|
||||
end
|
||||
self.client.connect_signal("manage", inital_apply)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Turns the scratchpad off
|
||||
function Scratchpad:turn_off()
|
||||
self.client = self:find()[1]
|
||||
|
||||
-- Get the tweens
|
||||
local anim_x = self.rubato.x
|
||||
local anim_y = self.rubato.y
|
||||
|
||||
local in_anim = false
|
||||
if (anim_x and anim_x.state == true) or (anim_y and anim_y.state == true) then
|
||||
in_anim = true
|
||||
end
|
||||
|
||||
if self.client and not in_anim then
|
||||
if anim_x then
|
||||
self.turning_off = true
|
||||
animate_turn_off(self, anim_x, "x")
|
||||
end
|
||||
if anim_y then
|
||||
self.turning_off = true
|
||||
animate_turn_off(self, anim_y, "y")
|
||||
end
|
||||
|
||||
if not anim_x and not anim_y then
|
||||
helpers.client.turn_off(self.client)
|
||||
self:emit_signal("turn_off", self.client)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Turns the scratchpad off if it is focused otherwise it raises the scratchpad
|
||||
function Scratchpad:toggle()
|
||||
local is_turn_off = false
|
||||
local c = self:find()[1]
|
||||
if self.dont_focus_before_close then
|
||||
if c then
|
||||
if c.sticky and #c:tags() > 0 then
|
||||
is_turn_off = true
|
||||
else
|
||||
local current_tag = c.screen.selected_tag
|
||||
for k, tag in pairs(c:tags()) do
|
||||
if tag == current_tag then
|
||||
is_turn_off = true
|
||||
break
|
||||
else
|
||||
is_turn_off = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
is_turn_off = capi.client.focus
|
||||
and awful.rules.match(capi.client.focus, self.rule)
|
||||
end
|
||||
|
||||
if is_turn_off then
|
||||
self:turn_off()
|
||||
else
|
||||
self:turn_on()
|
||||
end
|
||||
end
|
||||
|
||||
--- Make the module callable without putting a `:new` at the end of it
|
||||
--
|
||||
-- @param args A table of possible arguments
|
||||
-- @return The new scratchpad object
|
||||
function Scratchpad.mt:__call(...)
|
||||
return Scratchpad:new(...)
|
||||
end
|
||||
|
||||
return setmetatable(Scratchpad, Scratchpad.mt)
|
274
.config/awesome/extras/bling/module/tabbed.lua
Executable file
274
.config/awesome/extras/bling/module/tabbed.lua
Executable file
@@ -0,0 +1,274 @@
|
||||
--[[
|
||||
|
||||
This module currently works by adding a new property to each client that is tabbed.
|
||||
That new property is called bling_tabbed.
|
||||
So each client in a tabbed state has the property "bling_tabbed" which is a table.
|
||||
Each client that is not tabbed doesn't have that property.
|
||||
In the function themselves, the same object is refered to as "tabobj" which is why
|
||||
you will often see something like: "local tabobj = some_client.bling_tabbed" at the beginning
|
||||
of a function.
|
||||
|
||||
--]]
|
||||
|
||||
local awful = require("awful")
|
||||
local wibox = require("wibox")
|
||||
local gears = require("gears")
|
||||
local beautiful = require("beautiful")
|
||||
local helpers = require(tostring(...):match(".*bling") .. ".helpers")
|
||||
|
||||
local bar_style = beautiful.tabbar_style or "default"
|
||||
local bar = require(
|
||||
tostring(...):match(".*bling") .. ".widget.tabbar." .. bar_style
|
||||
)
|
||||
|
||||
tabbed = {}
|
||||
|
||||
-- helper function to connect to the (un)focus signals
|
||||
local function update_tabbar_from(c)
|
||||
if not c or not c.bling_tabbed then
|
||||
return
|
||||
end
|
||||
tabbed.update_tabbar(c.bling_tabbed)
|
||||
end
|
||||
|
||||
-- used to change focused tab relative to the currently focused one
|
||||
tabbed.iter = function(idx)
|
||||
if not idx then
|
||||
idx = 1
|
||||
end
|
||||
if not client.focus or not client.focus.bling_tabbed then
|
||||
return
|
||||
end
|
||||
local tabobj = client.focus.bling_tabbed
|
||||
local new_idx = (tabobj.focused_idx + idx) % #tabobj.clients
|
||||
if new_idx == 0 then
|
||||
new_idx = #tabobj.clients
|
||||
end
|
||||
tabbed.switch_to(tabobj, new_idx)
|
||||
end
|
||||
|
||||
-- removes a given client from its tab object
|
||||
tabbed.remove = function(c)
|
||||
if not c or not c.bling_tabbed then
|
||||
return
|
||||
end
|
||||
local tabobj = c.bling_tabbed
|
||||
table.remove(tabobj.clients, tabobj.focused_idx)
|
||||
if not beautiful.tabbar_disable then
|
||||
awful.titlebar.hide(c, bar.position)
|
||||
end
|
||||
c.bling_tabbed = nil
|
||||
c:disconnect_signal("focus", update_tabbar_from)
|
||||
c:disconnect_signal("unfocus", update_tabbar_from)
|
||||
awesome.emit_signal("bling::tabbed::client_removed", tabobj, c)
|
||||
tabbed.switch_to(tabobj, 1)
|
||||
end
|
||||
|
||||
-- removes the currently focused client from the tab object
|
||||
tabbed.pop = function()
|
||||
if not client.focus or not client.focus.bling_tabbed then
|
||||
return
|
||||
end
|
||||
tabbed.remove(client.focus)
|
||||
end
|
||||
|
||||
-- adds a client to a given tabobj
|
||||
tabbed.add = function(c, tabobj)
|
||||
if c.bling_tabbed then
|
||||
tabbed.remove(c)
|
||||
end
|
||||
c:connect_signal("focus", update_tabbar_from)
|
||||
c:connect_signal("unfocus", update_tabbar_from)
|
||||
helpers.client.sync(c, tabobj.clients[tabobj.focused_idx])
|
||||
tabobj.clients[#tabobj.clients + 1] = c
|
||||
tabobj.focused_idx = #tabobj.clients
|
||||
-- calls update even though switch_to calls update again
|
||||
-- but the new client needs to have the tabobj property
|
||||
-- before a clean switch can happen
|
||||
tabbed.update(tabobj)
|
||||
awesome.emit_signal("bling::tabbed::client_added", tabobj, c)
|
||||
tabbed.switch_to(tabobj, #tabobj.clients)
|
||||
end
|
||||
|
||||
-- use xwininfo to select one client and make it tab in the currently focused tab
|
||||
tabbed.pick = function()
|
||||
if not client.focus then
|
||||
return
|
||||
end
|
||||
-- this function uses xwininfo to grab a client window id which is then
|
||||
-- compared to all other clients window ids
|
||||
|
||||
local xwininfo_cmd =
|
||||
[[ xwininfo | grep 'xwininfo: Window id:' | cut -d " " -f 4 ]]
|
||||
awful.spawn.easy_async_with_shell(xwininfo_cmd, function(output)
|
||||
for _, c in ipairs(client.get()) do
|
||||
if tonumber(c.window) == tonumber(output) then
|
||||
if not client.focus.bling_tabbed and not c.bling_tabbed then
|
||||
tabbed.init(client.focus)
|
||||
tabbed.add(c, client.focus.bling_tabbed)
|
||||
end
|
||||
if not client.focus.bling_tabbed and c.bling_tabbed then
|
||||
tabbed.add(client.focus, c.bling_tabbed)
|
||||
end
|
||||
if client.focus.bling_tabbed and not c.bling_tabbed then
|
||||
tabbed.add(c, client.focus.bling_tabbed)
|
||||
end
|
||||
-- TODO: Should also merge tabs when focus and picked
|
||||
-- both are tab groups
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- select a client by direction and make it tab in the currently focused tab
|
||||
tabbed.pick_by_direction = function(direction)
|
||||
local sel = client.focus
|
||||
if not sel then
|
||||
return
|
||||
end
|
||||
if not sel.bling_tabbed then
|
||||
tabbed.init(sel)
|
||||
end
|
||||
local c = helpers.client.get_by_direction(direction)
|
||||
if not c then
|
||||
return
|
||||
end
|
||||
tabbed.add(c, sel.bling_tabbed)
|
||||
end
|
||||
|
||||
-- use dmenu to select a client and make it tab in the currently focused tab
|
||||
tabbed.pick_with_dmenu = function(dmenu_command)
|
||||
if not client.focus then
|
||||
return
|
||||
end
|
||||
|
||||
if not dmenu_command then
|
||||
dmenu_command = "rofi -dmenu -i"
|
||||
end
|
||||
|
||||
-- get all clients from the current tag
|
||||
-- ignores the case where multiple tags are selected
|
||||
local t = awful.screen.focused().selected_tag
|
||||
local list_clients = {}
|
||||
local list_clients_string = ""
|
||||
for idx, c in ipairs(t:clients()) do
|
||||
if c.window ~= client.focus.window then
|
||||
list_clients[#list_clients + 1] = c
|
||||
if #list_clients ~= 1 then
|
||||
list_clients_string = list_clients_string .. "\\n"
|
||||
end
|
||||
list_clients_string = list_clients_string
|
||||
.. tostring(c.window)
|
||||
.. " "
|
||||
.. c.name
|
||||
end
|
||||
end
|
||||
|
||||
if #list_clients == 0 then
|
||||
return
|
||||
end
|
||||
-- calls the actual dmenu
|
||||
local xprop_cmd = [[ echo -e "]]
|
||||
.. list_clients_string
|
||||
.. [[" | ]]
|
||||
.. dmenu_command
|
||||
.. [[ | awk '{ print $1 }' ]]
|
||||
awful.spawn.easy_async_with_shell(xprop_cmd, function(output)
|
||||
for _, c in ipairs(list_clients) do
|
||||
if tonumber(c.window) == tonumber(output) then
|
||||
if not client.focus.bling_tabbed then
|
||||
tabbed.init(client.focus)
|
||||
end
|
||||
local tabobj = client.focus.bling_tabbed
|
||||
tabbed.add(c, tabobj)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- update everything about one tab object
|
||||
tabbed.update = function(tabobj)
|
||||
local currently_focused_c = tabobj.clients[tabobj.focused_idx]
|
||||
-- update tabobj of each client and other things
|
||||
for idx, c in ipairs(tabobj.clients) do
|
||||
if c.valid then
|
||||
c.bling_tabbed = tabobj
|
||||
helpers.client.sync(c, currently_focused_c)
|
||||
-- the following handles killing a client while the client is tabbed
|
||||
c:connect_signal("unmanage", function(c)
|
||||
tabbed.remove(c)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- Maybe remove if I'm the only one using it?
|
||||
awesome.emit_signal("bling::tabbed::update", tabobj)
|
||||
if not beautiful.tabbar_disable then
|
||||
tabbed.update_tabbar(tabobj)
|
||||
end
|
||||
end
|
||||
|
||||
-- change focused tab by absolute index
|
||||
tabbed.switch_to = function(tabobj, new_idx)
|
||||
local old_focused_c = tabobj.clients[tabobj.focused_idx]
|
||||
tabobj.focused_idx = new_idx
|
||||
for idx, c in ipairs(tabobj.clients) do
|
||||
if idx ~= new_idx then
|
||||
helpers.client.turn_off(c)
|
||||
else
|
||||
helpers.client.turn_on(c)
|
||||
c:raise()
|
||||
if old_focused_c and old_focused_c.valid then
|
||||
c:swap(old_focused_c)
|
||||
end
|
||||
helpers.client.sync(c, old_focused_c)
|
||||
end
|
||||
end
|
||||
awesome.emit_signal("bling::tabbed::changed_focus", tabobj)
|
||||
tabbed.update(tabobj)
|
||||
end
|
||||
|
||||
tabbed.update_tabbar = function(tabobj)
|
||||
local flexlist = bar.layout()
|
||||
local tabobj_focused_client = tabobj.clients[tabobj.focused_idx]
|
||||
local tabobj_is_focused = (client.focus == tabobj_focused_client)
|
||||
-- itearte over all tabbed clients to create the widget tabbed list
|
||||
for idx, c in ipairs(tabobj.clients) do
|
||||
local buttons = gears.table.join(awful.button({}, 1, function()
|
||||
tabbed.switch_to(tabobj, idx)
|
||||
end))
|
||||
local wid_temp = bar.create(c, (idx == tabobj.focused_idx), buttons,
|
||||
not tabobj_is_focused)
|
||||
flexlist:add(wid_temp)
|
||||
end
|
||||
-- add tabbar to each tabbed client (clients will be hided anyway)
|
||||
for _, c in ipairs(tabobj.clients) do
|
||||
local titlebar = awful.titlebar(c, {
|
||||
bg = bar.bg_normal,
|
||||
size = bar.size,
|
||||
position = bar.position,
|
||||
})
|
||||
titlebar:setup({ layout = wibox.layout.flex.horizontal, flexlist })
|
||||
end
|
||||
end
|
||||
|
||||
tabbed.init = function(c)
|
||||
local tabobj = {}
|
||||
tabobj.clients = { c }
|
||||
c:connect_signal("focus", update_tabbar_from)
|
||||
c:connect_signal("unfocus", update_tabbar_from)
|
||||
tabobj.focused_idx = 1
|
||||
tabbed.update(tabobj)
|
||||
end
|
||||
|
||||
if beautiful.tabbed_spawn_in_tab then
|
||||
client.connect_signal("manage", function(c)
|
||||
local s = awful.screen.focused()
|
||||
local previous_client = awful.client.focus.history.get(s, 1)
|
||||
if previous_client and previous_client.bling_tabbed then
|
||||
tabbed.add(c, previous_client.bling_tabbed)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return tabbed
|
56
.config/awesome/extras/bling/module/tiled_wallpaper.lua
Executable file
56
.config/awesome/extras/bling/module/tiled_wallpaper.lua
Executable file
@@ -0,0 +1,56 @@
|
||||
--[[
|
||||
This module makes use of cairo surfaces
|
||||
For documentation take a look at the C docs:
|
||||
https://www.cairographics.org/
|
||||
They can be applied to lua by changing the naming conventions
|
||||
and adjusting for the missing namespaces (and classes)
|
||||
for example:
|
||||
cairo_rectangle(cr, 1, 1, 1, 1) in C would be written as
|
||||
cr:rectangle(1, 1, 1, 1) in lua
|
||||
and
|
||||
cairo_fill(cr) in C would be written as
|
||||
cr:fill() in lua
|
||||
--]]
|
||||
|
||||
local cairo = require("lgi").cairo
|
||||
local gears = require("gears")
|
||||
|
||||
function create_tiled_wallpaper(str, s, args_table)
|
||||
-- user input
|
||||
args_table = args_table or {}
|
||||
local fg = args_table.fg or "#ff0000"
|
||||
local bg = args_table.bg or "#00ffff"
|
||||
local offset_x = args_table.offset_x
|
||||
local offset_y = args_table.offset_y
|
||||
local font = args_table.font or "Hack"
|
||||
local font_size = tonumber(args_table.font_size) or 16
|
||||
local zickzack_bool = args_table.zickzack or false
|
||||
local padding = args_table.padding or 100
|
||||
|
||||
-- create cairo image wallpaper
|
||||
local img = cairo.ImageSurface(cairo.Format.RGB24, padding, padding)
|
||||
cr = cairo.Context(img)
|
||||
|
||||
cr:set_source(gears.color(bg))
|
||||
cr:paint()
|
||||
|
||||
cr:set_source(gears.color(fg))
|
||||
|
||||
cr:set_font_size(font_size)
|
||||
cr:select_font_face(font)
|
||||
|
||||
if zickzack_bool then
|
||||
cr:set_source(gears.color(fg))
|
||||
cr:move_to(padding / 2 + font_size, padding / 2 + font_size)
|
||||
cr:show_text(str)
|
||||
end
|
||||
|
||||
cr:set_source(gears.color(fg))
|
||||
cr:move_to(font_size, font_size)
|
||||
cr:show_text(str)
|
||||
|
||||
-- tile cairo image
|
||||
gears.wallpaper.tiled(img, s, { x = offset_x, y = offset_y })
|
||||
end
|
||||
|
||||
return create_tiled_wallpaper
|
339
.config/awesome/extras/bling/module/wallpaper.lua
Executable file
339
.config/awesome/extras/bling/module/wallpaper.lua
Executable file
@@ -0,0 +1,339 @@
|
||||
---------------------------------------------------------------------------
|
||||
-- High-level declarative function for setting your wallpaper.
|
||||
--
|
||||
--
|
||||
-- An easy way to setup a complex wallpaper with slideshow, random, schedule, extensibility.
|
||||
--
|
||||
-- @usage
|
||||
-- local wallpaper = require("wallpaper")
|
||||
-- -- A silly example
|
||||
-- wallpaper.setup { -- I want a wallpaper
|
||||
-- change_timer = 500, -- changing every 5 minutes
|
||||
-- set_function = wallpaper.setters.random, -- in a random way
|
||||
-- wallpaper = {"#abcdef",
|
||||
-- "~/Pictures",
|
||||
-- wallpaper.setters.awesome}, -- from this list (a color, a directory with pictures and the Awesome wallpaper)
|
||||
-- recursive = false, -- do not read subfolders of "~/Pictures"
|
||||
-- position = "centered", -- center it on the screen (for pictures)
|
||||
-- scale = 2, -- 2 time bigger (for pictures)
|
||||
-- }
|
||||
--
|
||||
-- @author Grumph
|
||||
-- @copyright 2021 Grumph
|
||||
--
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local awful = require("awful")
|
||||
local beautiful = require("beautiful")
|
||||
local gears = require("gears")
|
||||
local helpers = require(tostring(...):match(".*bling") .. ".helpers")
|
||||
|
||||
local setters = {}
|
||||
|
||||
--- Apply a wallpaper.
|
||||
--
|
||||
-- This function is a helper that will apply a wallpaper_object,
|
||||
-- either using gears.wallpaper.set or gears.wallpaper.* higher level functions when applicable.
|
||||
-- @param wallpaper_object A wallpaper object, either
|
||||
-- a `pattern` (see `gears.wallpaper.set`)
|
||||
-- a `surf` (see `gears.wallpaper.centered`)
|
||||
-- a function that actually sets the wallpaper.
|
||||
-- @tparam table args The argument table containing any of the arguments below.
|
||||
-- @int[opt=nil] args.screen The screen to use (as used in `gears.wallpaper` functions)
|
||||
-- @string[opt=nil or "centered"] args.position The `gears.wallpaper` position function to use.
|
||||
-- Must be set when wallpaper is a file.
|
||||
-- It can be `"centered"`, `"fit"`, `"tiled"` or `"maximized"`.
|
||||
-- @string[opt=beautiful.bg_normal or "black"] args.background See `gears.wallpaper`.
|
||||
-- @bool[opt=false] args.ignore_aspect See `gears.wallpaper`.
|
||||
-- @tparam[opt={x=0,y=0}] table args.offset See `gears.wallpaper`.
|
||||
-- @int[opt=1] args.scale See `gears.wallpaper`.
|
||||
function apply(wallpaper_object, args)
|
||||
args.background = args.background or beautiful.bg_normal or "black"
|
||||
args.ignore_aspect = args.ignore_aspect or false -- false = keep aspect ratio
|
||||
args.offset = args.offset or { x = 0, y = 0 }
|
||||
args.scale = args.scale or 1
|
||||
local positions = {
|
||||
["centered"] = function()
|
||||
gears.wallpaper.centered(
|
||||
wallpaper_object,
|
||||
args.screen,
|
||||
args.background,
|
||||
args.scale
|
||||
)
|
||||
end,
|
||||
["tiled"] = function()
|
||||
gears.wallpaper.tiled(wallpaper_object, args.screen, args.offset)
|
||||
end,
|
||||
["maximized"] = function()
|
||||
gears.wallpaper.maximized(
|
||||
wallpaper_object,
|
||||
args.screen,
|
||||
args.ignore_aspect,
|
||||
args.offset
|
||||
)
|
||||
end,
|
||||
["fit"] = function()
|
||||
gears.wallpaper.fit(wallpaper_object, args.screen, args.background)
|
||||
end,
|
||||
}
|
||||
if
|
||||
type(wallpaper_object) == "string"
|
||||
and gears.filesystem.file_readable(wallpaper_object)
|
||||
then
|
||||
-- path of an image file, we use a position function
|
||||
local p = args.position or "centered"
|
||||
positions[p]()
|
||||
elseif type(wallpaper_object) == "function" then
|
||||
-- function
|
||||
wallpaper_object(args)
|
||||
elseif
|
||||
(not gears.color.ensure_pango_color(wallpaper_object, nil))
|
||||
and args.position
|
||||
then
|
||||
-- if the user sets a position function, wallpaper_object should be a cairo surface
|
||||
positions[args.position]()
|
||||
else
|
||||
gears.wallpaper.set(wallpaper_object)
|
||||
end
|
||||
end
|
||||
|
||||
--- Converts `args.wallpaper` to a list of `wallpaper_objects` readable by `apply` function).
|
||||
--
|
||||
-- @tparam table args The argument table containing the argument below.
|
||||
-- @param[opt=`beautiful.wallpaper_path` or `"black"`] args.wallpaper A wallpaper object.
|
||||
-- It can be a color or a cairo pattern (what `gears.wallpaper.set` understands),
|
||||
-- a cairo suface (set with gears.wallpaper.set if `args.position` is nil, or with
|
||||
-- `gears.wallpaper` position functions, see `args.position`),
|
||||
-- a function similar to args.set_function that will effectively set a wallpaper (usually
|
||||
-- with `gears.wallpaper` functions),
|
||||
-- a path to a file,
|
||||
-- path to a directory containing images,
|
||||
-- or a list with any of the previous choices.
|
||||
-- @tparam[opt=`{"jpg", "jpeg", "png", "bmp"}`] table args.image_formats A list of
|
||||
-- file extensions to filter when `args.wallpaper` is a directory.
|
||||
-- @bool[opt=true] args.recursive Either to recurse or not when `args.wallpaper` is a directory.
|
||||
-- @treturn table A list of `wallpaper_objects` (what `apply` can read).
|
||||
-- @see apply
|
||||
function prepare_list(args)
|
||||
args.image_formats = args.image_formats or { "jpg", "jpeg", "png", "bmp" }
|
||||
args.recursive = args.recursive or true
|
||||
|
||||
local wallpapers = (args.wallpaper or beautiful.wallpaper_path or "black")
|
||||
local res = {}
|
||||
if type(wallpapers) ~= "table" then
|
||||
wallpapers = { wallpapers }
|
||||
end
|
||||
for _, w in ipairs(wallpapers) do
|
||||
-- w is either:
|
||||
-- - a directory path (string)
|
||||
-- - an image path or a color (string)
|
||||
-- - a cairo surface or a cairo pattern
|
||||
-- - a function for setting the wallpaper
|
||||
if type(w) == "string" and gears.filesystem.dir_readable(w) then
|
||||
local file_list = helpers.filesystem.list_directory_files(
|
||||
w,
|
||||
args.image_formats,
|
||||
args.recursive
|
||||
)
|
||||
for _, f in ipairs(file_list) do
|
||||
res[#res + 1] = w .. "/" .. f
|
||||
end
|
||||
else
|
||||
res[#res + 1] = w
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local simple_index = 0
|
||||
--- Set the next wallpaper in a list.
|
||||
--
|
||||
-- @tparam table args See `prepare_list` and `apply` arguments
|
||||
-- @see apply
|
||||
-- @see prepare_list
|
||||
function setters.simple(args)
|
||||
local wallpapers = prepare_list(args)
|
||||
simple_index = (simple_index % #wallpapers) + 1
|
||||
apply(wallpapers[simple_index], args)
|
||||
end
|
||||
|
||||
--- Set a random wallpaper from a list.
|
||||
--
|
||||
-- @tparam table args See `prepare_list` and `apply` arguments
|
||||
-- @see apply
|
||||
-- @see prepare_list
|
||||
function setters.random(args)
|
||||
local wallpapers = prepare_list(args)
|
||||
apply(wallpapers[math.random(#wallpapers)], args)
|
||||
end
|
||||
|
||||
local simple_schedule_object = nil
|
||||
--- A schedule setter.
|
||||
--
|
||||
-- This simple schedule setter was freely inspired by [dynamic-wallpaper](https://github.com/manilarome/awesome-glorious-widgets/blob/master/dynamic-wallpaper/init.lua).
|
||||
-- @tparam table args The argument table containing any of the arguments below.
|
||||
-- @tparam table args.wallpaper The schedule table, with the form
|
||||
-- {
|
||||
-- ["HH:MM:SS"] = wallpaper,
|
||||
-- ["HH:MM:SS"] = wallpaper2,
|
||||
-- }
|
||||
-- The wallpapers definition can be anything the `schedule_set_function` can read
|
||||
-- (what you would place in `args.wallpaper` for this function),
|
||||
-- @tparam[opt=`setters.simple`] function args.wallpaper_set_function The set_function used by default
|
||||
function setters.simple_schedule(args)
|
||||
local function update_wallpaper()
|
||||
local fake_args = gears.table.join(args, {
|
||||
wallpaper = args.wallpaper[simple_schedule_object.closest_lower_time],
|
||||
})
|
||||
simple_schedule_object.schedule_set_function(fake_args)
|
||||
end
|
||||
if not simple_schedule_object then
|
||||
simple_schedule_object = {}
|
||||
-- initialize the schedule object, so we don't do it for every call
|
||||
simple_schedule_object.schedule_set_function = args.schedule_set_function
|
||||
or setters.simple
|
||||
-- we get the sorted time keys
|
||||
simple_schedule_object.times = {}
|
||||
for k in pairs(args.wallpaper) do
|
||||
table.insert(simple_schedule_object.times, k)
|
||||
end
|
||||
table.sort(simple_schedule_object.times)
|
||||
-- now we get the closest time which is below current time (the current applicable period)
|
||||
local function update_timer()
|
||||
local current_time = os.date("%H:%M:%S")
|
||||
local next_time = simple_schedule_object.times[1]
|
||||
simple_schedule_object.closest_lower_time =
|
||||
simple_schedule_object.times[#simple_schedule_object.times]
|
||||
for _, k in ipairs(simple_schedule_object.times) do
|
||||
if k > current_time then
|
||||
next_time = k
|
||||
break
|
||||
end
|
||||
simple_schedule_object.closest_lower_time = k
|
||||
end
|
||||
simple_schedule_object.timer.timeout = helpers.time.time_diff(
|
||||
next_time,
|
||||
current_time
|
||||
)
|
||||
if simple_schedule_object.timer.timeout < 0 then
|
||||
-- the next_time is the day after, so we add 24 hours to the timer
|
||||
simple_schedule_object.timer.timeout = simple_schedule_object.timer.timeout
|
||||
+ 86400
|
||||
end
|
||||
simple_schedule_object.timer:again()
|
||||
update_wallpaper()
|
||||
end
|
||||
simple_schedule_object.timer = gears.timer({
|
||||
callback = update_timer,
|
||||
})
|
||||
update_timer()
|
||||
else
|
||||
-- if called again (usually when the change_timer is set), we just change the wallpaper depending on current parameters
|
||||
update_wallpaper()
|
||||
end
|
||||
end
|
||||
|
||||
--- Set the AWESOME wallpaper.
|
||||
--
|
||||
-- @tparam table args The argument table containing the argument below.
|
||||
-- @param[opt=`beautiful.bg_normal`] args.colors.bg The bg color.
|
||||
-- If the default is used, the color is darkened if `beautiful.bg_normal` is light
|
||||
-- or lightned if `beautiful.bg_normal` is dark.
|
||||
-- @param[opt=`beautiful.fg_normal`] args.colors.fg The fg color.
|
||||
-- @param[opt=`beautiful.fg_focus`] args.colors.alt_fg The alt_fg color.
|
||||
--
|
||||
-- see beautiful.theme_assets.wallpaper
|
||||
function setters.awesome_wallpaper(args)
|
||||
local colors = {
|
||||
bg = beautiful.bg_normal,
|
||||
fg = beautiful.fg_normal,
|
||||
alt_fg = beautiful.bg_focus,
|
||||
}
|
||||
colors.bg = helpers.color.is_dark(beautiful.bg_normal)
|
||||
and helpers.color.lighten(colors.bg)
|
||||
or helpers.color.darken(colors.bg)
|
||||
if type(args.colors) == "table" then
|
||||
colors.bg = args.colors.bg or colors.bg
|
||||
colors.fg = args.colors.fg or colors.fg
|
||||
colors.alt_fg = args.colors.alt_fg or colors.alt_fg
|
||||
end
|
||||
-- Generate wallpaper:
|
||||
if not args.screen then
|
||||
for s in screen do
|
||||
gears.wallpaper.set(
|
||||
beautiful.theme_assets.wallpaper(
|
||||
colors.bg,
|
||||
colors.fg,
|
||||
colors.alt_fg,
|
||||
s
|
||||
)
|
||||
)
|
||||
end
|
||||
else
|
||||
gears.wallpaper.set(
|
||||
beautiful.theme_assets.wallpaper(
|
||||
colors.bg,
|
||||
colors.fg,
|
||||
colors.alt_fg,
|
||||
args.screen
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
--- Setup a wallpaper.
|
||||
--
|
||||
-- @tparam table args Parameters for the wallpaper. It may also contain all parameters your `args.set_function` needs
|
||||
-- @int[opt=nil] args.screen The screen to use (as used in `gears.wallpaper` functions)
|
||||
-- @int[opt=nil] args.change_timer Time in seconds for wallpaper changes
|
||||
-- @tparam[opt=`setters.awesome` or `setters.simple`] function args.set_function A function to set the wallpaper
|
||||
-- It takes args as parameter (the same args as the setup function).
|
||||
-- This function is called at `"request::wallpaper"` `screen` signals and at `args.change_timer` timeouts.
|
||||
-- There is no obligation, but for consistency, the function should use `args.wallpaper` as a feeder.
|
||||
-- If `args.wallpaper` is defined, the default function is `setters.simple`, else it will be `setters.awesome`.
|
||||
--
|
||||
-- @usage
|
||||
-- local wallpaper = require("wallpaper")
|
||||
-- wallpaper.setup {
|
||||
-- change_timer = 631, -- Prime number is better
|
||||
-- set_function = wallpaper.setters.random,
|
||||
-- -- parameters for the random setter
|
||||
-- wallpaper = '/data/pictures/wallpapers',
|
||||
-- position = "maximized",
|
||||
-- }
|
||||
--
|
||||
-- @see apply
|
||||
-- @see prepare_list
|
||||
-- @see setters.simple
|
||||
function setup(args)
|
||||
local config = args or {}
|
||||
config.set_function = config.set_function
|
||||
or (config.wallpaper and setters.simple or setters.awesome_wallpaper)
|
||||
local function set_wallpaper(s)
|
||||
config.screen = s or config.screen
|
||||
config.set_function(config)
|
||||
end
|
||||
|
||||
if config.change_timer and config.change_timer > 0 then
|
||||
gears.timer({
|
||||
timeout = config.change_timer,
|
||||
call_now = false,
|
||||
autostart = true,
|
||||
callback = function()
|
||||
set_wallpaper()
|
||||
end,
|
||||
})
|
||||
end
|
||||
if awesome.version == "v4.3" then
|
||||
awful.screen.connect_for_each_screen(set_wallpaper)
|
||||
else
|
||||
screen.connect_signal("request::wallpaper", set_wallpaper)
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
setup = setup,
|
||||
setters = setters,
|
||||
apply = apply,
|
||||
prepare_list = prepare_list,
|
||||
}
|
88
.config/awesome/extras/bling/module/window_swallowing.lua
Executable file
88
.config/awesome/extras/bling/module/window_swallowing.lua
Executable file
@@ -0,0 +1,88 @@
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local beautiful = require("beautiful")
|
||||
|
||||
local helpers = require(tostring(...):match(".*bling") .. ".helpers")
|
||||
|
||||
-- It might actually swallow too much, that's why there is a filter option by classname
|
||||
-- without the don't-swallow-list it would also swallow for example
|
||||
-- file pickers or new firefox windows spawned by an already existing one
|
||||
|
||||
local window_swallowing_activated = false
|
||||
|
||||
-- you might want to add or remove applications here
|
||||
local dont_swallow_classname_list = beautiful.dont_swallow_classname_list
|
||||
or { "firefox", "Gimp", "Google-chrome" }
|
||||
local activate_dont_swallow_filter = beautiful.dont_swallow_filter_activated
|
||||
or true
|
||||
|
||||
-- checks if client classname matches with any entry of the dont-swallow-list
|
||||
local function check_if_swallow(c)
|
||||
if not activate_dont_swallow_filter then
|
||||
return true
|
||||
end
|
||||
for _, classname in ipairs(dont_swallow_classname_list) do
|
||||
if classname == c.class then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- the function that will be connected to / disconnected from the spawn client signal
|
||||
local function manage_clientspawn(c)
|
||||
-- get the last focused window to check if it is a parent window
|
||||
local parent_client = awful.client.focus.history.get(c.screen, 1)
|
||||
if not parent_client then
|
||||
return
|
||||
end
|
||||
|
||||
-- io.popen is normally discouraged. Should probably be changed
|
||||
local handle = io.popen(
|
||||
[[pstree -T -p -a -s ]]
|
||||
.. tostring(c.pid)
|
||||
.. [[ | sed '2q;d' | grep -o '[0-9]*$' | tr -d '\n']]
|
||||
)
|
||||
local parent_pid = handle:read("*a")
|
||||
handle:close()
|
||||
|
||||
if
|
||||
(tostring(parent_pid) == tostring(parent_client.pid))
|
||||
and check_if_swallow(c)
|
||||
then
|
||||
c:connect_signal("unmanage", function()
|
||||
helpers.client.turn_on(parent_client)
|
||||
helpers.client.sync(parent_client, c)
|
||||
end)
|
||||
|
||||
helpers.client.sync(c, parent_client)
|
||||
helpers.client.turn_off(parent_client)
|
||||
end
|
||||
end
|
||||
|
||||
-- without the following functions that module would be autoloaded by require("bling")
|
||||
-- a toggle window swallowing hotkey is also possible that way
|
||||
|
||||
local function start()
|
||||
client.connect_signal("manage", manage_clientspawn)
|
||||
window_swallowing_activated = true
|
||||
end
|
||||
|
||||
local function stop()
|
||||
client.disconnect_signal("manage", manage_clientspawn)
|
||||
window_swallowing_activated = false
|
||||
end
|
||||
|
||||
local function toggle()
|
||||
if window_swallowing_activated then
|
||||
stop()
|
||||
else
|
||||
start()
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
start = start,
|
||||
stop = stop,
|
||||
toggle = toggle,
|
||||
}
|
1
.config/awesome/extras/bling/signal/init.lua
Executable file
1
.config/awesome/extras/bling/signal/init.lua
Executable file
@@ -0,0 +1 @@
|
||||
return { playerctl = require(... .. ".playerctl") }
|
19
.config/awesome/extras/bling/signal/playerctl/init.lua
Executable file
19
.config/awesome/extras/bling/signal/playerctl/init.lua
Executable file
@@ -0,0 +1,19 @@
|
||||
local beautiful = require("beautiful")
|
||||
|
||||
-- Use CLI backend as default as it is supported on most if not all systems
|
||||
local backend_config = beautiful.playerctl_backend or "playerctl_cli"
|
||||
local backends = {
|
||||
playerctl_cli = require(... .. ".playerctl_cli"),
|
||||
playerctl_lib = require(... .. ".playerctl_lib"),
|
||||
}
|
||||
|
||||
local function enable_wrapper(args)
|
||||
backend_config = (args and args.backend) or backend_config
|
||||
backends[backend_config].enable(args)
|
||||
end
|
||||
|
||||
local function disable_wrapper()
|
||||
backends[backend_config].disable()
|
||||
end
|
||||
|
||||
return { enable = enable_wrapper, disable = disable_wrapper }
|
151
.config/awesome/extras/bling/signal/playerctl/playerctl_cli.lua
Executable file
151
.config/awesome/extras/bling/signal/playerctl/playerctl_cli.lua
Executable file
@@ -0,0 +1,151 @@
|
||||
--
|
||||
-- Provides:
|
||||
-- bling::playerctl::status
|
||||
-- playing (boolean)
|
||||
-- bling::playerctl::title_artist_album
|
||||
-- title (string)
|
||||
-- artist (string)
|
||||
-- album_path (string)
|
||||
-- bling::playerctl::position
|
||||
-- interval_sec (number)
|
||||
-- length_sec (number)
|
||||
-- bling::playerctl::no_players
|
||||
--
|
||||
local awful = require("awful")
|
||||
local beautiful = require("beautiful")
|
||||
|
||||
local interval = beautiful.playerctl_position_update_interval or 1
|
||||
|
||||
local function emit_player_status()
|
||||
local status_cmd = "playerctl status -F"
|
||||
|
||||
-- Follow status
|
||||
awful.spawn.easy_async({
|
||||
"pkill",
|
||||
"--full",
|
||||
"--uid",
|
||||
os.getenv("USER"),
|
||||
"^playerctl status",
|
||||
}, function()
|
||||
awful.spawn.with_line_callback(status_cmd, {
|
||||
stdout = function(line)
|
||||
local playing = false
|
||||
if line:find("Playing") then
|
||||
playing = true
|
||||
else
|
||||
playing = false
|
||||
end
|
||||
awesome.emit_signal("bling::playerctl::status", playing)
|
||||
end,
|
||||
})
|
||||
collectgarbage("collect")
|
||||
end)
|
||||
end
|
||||
|
||||
local function emit_player_info()
|
||||
local art_script = [[
|
||||
sh -c '
|
||||
|
||||
tmp_dir="$XDG_CACHE_HOME/awesome/"
|
||||
|
||||
if [ -z ${XDG_CACHE_HOME} ]; then
|
||||
tmp_dir="$HOME/.cache/awesome/"
|
||||
fi
|
||||
|
||||
tmp_cover_path=${tmp_dir}"cover.png"
|
||||
|
||||
if [ ! -d $tmp_dir ]; then
|
||||
mkdir -p $tmp_dir
|
||||
fi
|
||||
|
||||
link="$(playerctl metadata mpris:artUrl)"
|
||||
|
||||
curl -s "$link" --output $tmp_cover_path
|
||||
|
||||
echo "$tmp_cover_path"
|
||||
']]
|
||||
|
||||
-- Command that lists artist and title in a format to find and follow
|
||||
local song_follow_cmd =
|
||||
"playerctl metadata --format 'artist_{{artist}}title_{{title}}' -F"
|
||||
|
||||
-- Progress Cmds
|
||||
local prog_cmd = "playerctl position"
|
||||
local length_cmd = "playerctl metadata mpris:length"
|
||||
|
||||
awful.widget.watch(prog_cmd, interval, function(_, interval)
|
||||
awful.spawn.easy_async_with_shell(length_cmd, function(length)
|
||||
local length_sec = tonumber(length) -- in microseconds
|
||||
local interval_sec = tonumber(interval) -- in seconds
|
||||
if length_sec and interval_sec then
|
||||
if interval_sec >= 0 and length_sec > 0 then
|
||||
awesome.emit_signal(
|
||||
"bling::playerctl::position",
|
||||
interval_sec,
|
||||
length_sec / 1000000
|
||||
)
|
||||
end
|
||||
end
|
||||
end)
|
||||
collectgarbage("collect")
|
||||
end)
|
||||
|
||||
-- Follow title
|
||||
awful.spawn.easy_async({
|
||||
"pkill",
|
||||
"--full",
|
||||
"--uid",
|
||||
os.getenv("USER"),
|
||||
"^playerctl metadata",
|
||||
}, function()
|
||||
awful.spawn.with_line_callback(song_follow_cmd, {
|
||||
stdout = function(line)
|
||||
local album_path = ""
|
||||
awful.spawn.easy_async_with_shell(art_script, function(out)
|
||||
-- Get album path
|
||||
album_path = out:gsub("%\n", "")
|
||||
-- Get title and artist
|
||||
local artist = line:match("artist_(.*)title_")
|
||||
local title = line:match("title_(.*)")
|
||||
-- If the title is nil or empty then the players stopped
|
||||
if title and title ~= "" then
|
||||
awesome.emit_signal(
|
||||
"bling::playerctl::title_artist_album",
|
||||
title,
|
||||
artist,
|
||||
album_path
|
||||
)
|
||||
else
|
||||
awesome.emit_signal("bling::playerctl::no_players")
|
||||
end
|
||||
end)
|
||||
collectgarbage("collect")
|
||||
end,
|
||||
})
|
||||
collectgarbage("collect")
|
||||
end)
|
||||
end
|
||||
|
||||
-- Emit info
|
||||
-- emit_player_status()
|
||||
-- emit_player_info()
|
||||
|
||||
local enable = function(args)
|
||||
interval = (args and args.interval) or interval
|
||||
emit_player_status()
|
||||
emit_player_info()
|
||||
end
|
||||
|
||||
local disable = function()
|
||||
awful.spawn.with_shell(
|
||||
"pkill --full --uid " .. os.getenv("USER") .. " '^playerctl status -F'"
|
||||
)
|
||||
|
||||
awful.spawn.with_shell(
|
||||
"pkill --full --uid "
|
||||
.. os.getenv("USER")
|
||||
.. " '^playerctl metadata --format'"
|
||||
)
|
||||
end
|
||||
|
||||
return { enable = enable, disable = disable }
|
350
.config/awesome/extras/bling/signal/playerctl/playerctl_lib.lua
Executable file
350
.config/awesome/extras/bling/signal/playerctl/playerctl_lib.lua
Executable file
@@ -0,0 +1,350 @@
|
||||
-- Playerctl signals
|
||||
--
|
||||
-- Provides:
|
||||
-- bling::playerctl::status
|
||||
-- playing (boolean)
|
||||
-- player_name (string)
|
||||
-- bling::playerctl::title_artist_album
|
||||
-- title (string)
|
||||
-- artist (string)
|
||||
-- album_path (string)
|
||||
-- player_name (string)
|
||||
-- bling::playerctl::position
|
||||
-- interval_sec (number)
|
||||
-- length_sec (number)
|
||||
-- player_name (string)
|
||||
-- bling::playerctl::no_players
|
||||
-- (No parameters)
|
||||
|
||||
local gears = require("gears")
|
||||
local awful = require("awful")
|
||||
local beautiful = require("beautiful")
|
||||
local Playerctl = nil
|
||||
|
||||
local manager = nil
|
||||
local metadata_timer = nil
|
||||
local position_timer = nil
|
||||
|
||||
local ignore = {}
|
||||
local priority = {}
|
||||
local update_on_activity = true
|
||||
local interval = 1
|
||||
|
||||
-- Track position callback
|
||||
local last_position = -1
|
||||
local last_length = -1
|
||||
local function position_cb()
|
||||
local player = manager.players[1]
|
||||
if player then
|
||||
local position = player:get_position() / 1000000
|
||||
local length = (player.metadata.value["mpris:length"] or 0) / 1000000
|
||||
if position ~= last_position or length ~= last_length then
|
||||
awesome.emit_signal(
|
||||
"bling::playerctl::position",
|
||||
position,
|
||||
length,
|
||||
player.player_name
|
||||
)
|
||||
last_position = position
|
||||
last_length = length
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function get_album_art(url)
|
||||
return awful.util.shell
|
||||
.. [[ -c '
|
||||
|
||||
tmp_dir="$XDG_CACHE_HOME/awesome/"
|
||||
|
||||
if [ -z "$XDG_CACHE_HOME" ]; then
|
||||
tmp_dir="$HOME/.cache/awesome/"
|
||||
fi
|
||||
|
||||
tmp_cover_path="${tmp_dir}cover.png"
|
||||
|
||||
if [ ! -d "$tmp_dir" ]; then
|
||||
mkdir -p $tmp_dir
|
||||
fi
|
||||
|
||||
curl -s ']]
|
||||
.. url
|
||||
.. [[' --output $tmp_cover_path
|
||||
|
||||
echo "$tmp_cover_path"
|
||||
']]
|
||||
end
|
||||
|
||||
-- Metadata callback for title, artist, and album art
|
||||
local last_player = nil
|
||||
local last_title = ""
|
||||
local last_artist = ""
|
||||
local last_artUrl = ""
|
||||
local function metadata_cb(player, metadata)
|
||||
if update_on_activity then
|
||||
manager:move_player_to_top(player)
|
||||
end
|
||||
|
||||
local data = metadata.value
|
||||
|
||||
local title = data["xesam:title"] or ""
|
||||
local artist = data["xesam:artist"][1] or ""
|
||||
for i = 2, #data["xesam:artist"] do
|
||||
artist = artist .. ", " .. data["xesam:artist"][i]
|
||||
end
|
||||
local artUrl = data["mpris:artUrl"] or ""
|
||||
-- Spotify client doesn't report its art URL's correctly...
|
||||
if player.player_name == "spotify" then
|
||||
artUrl = artUrl:gsub("open.spotify.com", "i.scdn.co")
|
||||
end
|
||||
|
||||
if player == manager.players[1] then
|
||||
-- Callback can be called even though values we care about haven't
|
||||
-- changed, so check to see if they have
|
||||
if
|
||||
player ~= last_player
|
||||
or title ~= last_title
|
||||
or artist ~= last_artist
|
||||
or artUrl ~= last_artUrl
|
||||
then
|
||||
if title == "" and artist == "" and artUrl == "" then
|
||||
return
|
||||
end
|
||||
|
||||
if metadata_timer ~= nil then
|
||||
if metadata_timer.started then
|
||||
metadata_timer:stop()
|
||||
end
|
||||
end
|
||||
|
||||
metadata_timer = gears.timer({
|
||||
timeout = 0.3,
|
||||
autostart = true,
|
||||
single_shot = true,
|
||||
callback = function()
|
||||
if artUrl ~= "" then
|
||||
awful.spawn.with_line_callback(get_album_art(artUrl), {
|
||||
stdout = function(line)
|
||||
awesome.emit_signal(
|
||||
"bling::playerctl::title_artist_album",
|
||||
title,
|
||||
artist,
|
||||
line,
|
||||
player.player_name
|
||||
)
|
||||
end,
|
||||
})
|
||||
else
|
||||
awesome.emit_signal(
|
||||
"bling::playerctl::title_artist_album",
|
||||
title,
|
||||
artist,
|
||||
"",
|
||||
player.player_name
|
||||
)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Re-sync with position timer when track changes
|
||||
position_timer:again()
|
||||
last_player = player
|
||||
last_title = title
|
||||
last_artist = artist
|
||||
last_artUrl = artUrl
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Playback status callback
|
||||
-- Reported as PLAYING, PAUSED, or STOPPED
|
||||
local function playback_status_cb(player, status)
|
||||
if update_on_activity then
|
||||
manager:move_player_to_top(player)
|
||||
end
|
||||
|
||||
if player == manager.players[1] then
|
||||
if status == "PLAYING" then
|
||||
awesome.emit_signal(
|
||||
"bling::playerctl::status",
|
||||
true,
|
||||
player.player_name
|
||||
)
|
||||
else
|
||||
awesome.emit_signal(
|
||||
"bling::playerctl::status",
|
||||
false,
|
||||
player.player_name
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Determine if player should be managed
|
||||
local function name_is_selected(name)
|
||||
if ignore[name.name] then
|
||||
return false
|
||||
end
|
||||
|
||||
if #priority > 0 then
|
||||
for _, arg in pairs(priority) do
|
||||
if arg == name.name or arg == "%any" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Create new player and connect it to callbacks
|
||||
local function init_player(name)
|
||||
if name_is_selected(name) then
|
||||
local player = Playerctl.Player.new_from_name(name)
|
||||
manager:manage_player(player)
|
||||
player.on_playback_status = playback_status_cb
|
||||
player.on_metadata = metadata_cb
|
||||
|
||||
-- Start position timer if its not already running
|
||||
if not position_timer.started then
|
||||
position_timer:again()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Determine if a player name comes before or after another according to the
|
||||
-- priority order
|
||||
local function player_compare_name(name_a, name_b)
|
||||
local any_index = math.huge
|
||||
local a_match_index = nil
|
||||
local b_match_index = nil
|
||||
|
||||
if name_a == name_b then
|
||||
return 0
|
||||
end
|
||||
|
||||
for index, name in ipairs(priority) do
|
||||
if name == "%any" then
|
||||
any_index = (any_index == math.huge) and index or any_index
|
||||
elseif name == name_a then
|
||||
a_match_index = a_match_index or index
|
||||
elseif name == name_b then
|
||||
b_match_index = b_match_index or index
|
||||
end
|
||||
end
|
||||
|
||||
if not a_match_index and not b_match_index then
|
||||
return 0
|
||||
elseif not a_match_index then
|
||||
return (b_match_index < any_index) and 1 or -1
|
||||
elseif not b_match_index then
|
||||
return (a_match_index < any_index) and -1 or 1
|
||||
elseif a_match_index == b_match_index then
|
||||
return 0
|
||||
else
|
||||
return (a_match_index < b_match_index) and -1 or 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Sorting function used by manager if a priority order is specified
|
||||
local function player_compare(a, b)
|
||||
local player_a = Playerctl.Player(a)
|
||||
local player_b = Playerctl.Player(b)
|
||||
return player_compare_name(player_a.player_name, player_b.player_name)
|
||||
end
|
||||
|
||||
local function start_manager()
|
||||
manager = Playerctl.PlayerManager()
|
||||
if #priority > 0 then
|
||||
manager:set_sort_func(player_compare)
|
||||
end
|
||||
|
||||
-- Timer to update track position at specified interval
|
||||
position_timer = gears.timer({
|
||||
timeout = interval,
|
||||
callback = position_cb,
|
||||
})
|
||||
|
||||
-- Manage existing players on startup
|
||||
for _, name in ipairs(manager.player_names) do
|
||||
init_player(name)
|
||||
end
|
||||
|
||||
-- Callback to manage new players
|
||||
function manager:on_name_appeared(name)
|
||||
init_player(name)
|
||||
end
|
||||
|
||||
-- Callback to check if all players have exited
|
||||
function manager:on_name_vanished(name)
|
||||
if #manager.players == 0 then
|
||||
metadata_timer:stop()
|
||||
position_timer:stop()
|
||||
awesome.emit_signal("bling::playerctl::no_players")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Parse arguments
|
||||
local function parse_args(args)
|
||||
if args then
|
||||
update_on_activity = args.update_on_activity or update_on_activity
|
||||
interval = args.interval or interval
|
||||
|
||||
if type(args.ignore) == "string" then
|
||||
ignore[args.ignore] = true
|
||||
elseif type(args.ignore) == "table" then
|
||||
for _, name in pairs(args.ignore) do
|
||||
ignore[name] = true
|
||||
end
|
||||
end
|
||||
|
||||
if type(args.player) == "string" then
|
||||
priority[1] = args.player
|
||||
elseif type(args.player) == "table" then
|
||||
priority = args.player
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function playerctl_enable(args)
|
||||
args = args or {}
|
||||
-- Grab settings from beautiful variables if not set explicitly
|
||||
args.ignore = args.ignore or beautiful.playerctl_ignore
|
||||
args.player = args.player or beautiful.playerctl_player
|
||||
args.update_on_activity = args.update_on_activity
|
||||
or beautiful.playerctl_update_on_activity
|
||||
args.interval = args.interval
|
||||
or beautiful.playerctl_position_update_interval
|
||||
parse_args(args)
|
||||
|
||||
-- Grab playerctl library
|
||||
Playerctl = require("lgi").Playerctl
|
||||
|
||||
-- Ensure main event loop has started before starting player manager
|
||||
gears.timer.delayed_call(start_manager)
|
||||
end
|
||||
|
||||
local function playerctl_disable()
|
||||
-- Remove manager and timer
|
||||
manager = nil
|
||||
metadata_timer:stop()
|
||||
metadata_timer = nil
|
||||
position_timer:stop()
|
||||
position_timer = nil
|
||||
-- Restore default settings
|
||||
ignore = {}
|
||||
priority = {}
|
||||
update_on_activity = true
|
||||
interval = 1
|
||||
-- Reset default values
|
||||
last_position = -1
|
||||
last_length = -1
|
||||
last_player = nil
|
||||
last_title = ""
|
||||
last_artist = ""
|
||||
last_artUrl = ""
|
||||
end
|
||||
|
||||
return { enable = playerctl_enable, disable = playerctl_disable }
|
1053
.config/awesome/extras/bling/widget/app_launcher/init.lua
Executable file
1053
.config/awesome/extras/bling/widget/app_launcher/init.lua
Executable file
File diff suppressed because it is too large
Load Diff
656
.config/awesome/extras/bling/widget/app_launcher/prompt.lua
Executable file
656
.config/awesome/extras/bling/widget/app_launcher/prompt.lua
Executable 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)
|
7
.config/awesome/extras/bling/widget/init.lua
Executable file
7
.config/awesome/extras/bling/widget/init.lua
Executable 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"),
|
||||
}
|
57
.config/awesome/extras/bling/widget/tabbar/boxes.lua
Executable file
57
.config/awesome/extras/bling/widget/tabbar/boxes.lua
Executable 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,
|
||||
}
|
60
.config/awesome/extras/bling/widget/tabbar/default.lua
Executable file
60
.config/awesome/extras/bling/widget/tabbar/default.lua
Executable 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,
|
||||
}
|
271
.config/awesome/extras/bling/widget/tabbar/modern.lua
Executable file
271
.config/awesome/extras/bling/widget/tabbar/modern.lua
Executable 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,
|
||||
}
|
81
.config/awesome/extras/bling/widget/tabbar/pure.lua
Executable file
81
.config/awesome/extras/bling/widget/tabbar/pure.lua
Executable 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,
|
||||
}
|
51
.config/awesome/extras/bling/widget/tabbed_misc/custom_tasklist.lua
Executable file
51
.config/awesome/extras/bling/widget/tabbed_misc/custom_tasklist.lua
Executable 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
|
9
.config/awesome/extras/bling/widget/tabbed_misc/init.lua
Executable file
9
.config/awesome/extras/bling/widget/tabbed_misc/init.lua
Executable 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"
|
||||
),
|
||||
}
|
133
.config/awesome/extras/bling/widget/tabbed_misc/titlebar_indicator.lua
Executable file
133
.config/awesome/extras/bling/widget/tabbed_misc/titlebar_indicator.lua
Executable 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
|
246
.config/awesome/extras/bling/widget/tag_preview.lua
Executable file
246
.config/awesome/extras/bling/widget/tag_preview.lua
Executable 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}
|
199
.config/awesome/extras/bling/widget/task_preview.lua
Executable file
199
.config/awesome/extras/bling/widget/task_preview.lua
Executable 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 }
|
454
.config/awesome/extras/bling/widget/window_switcher.lua
Executable file
454
.config/awesome/extras/bling/widget/window_switcher.lua
Executable file
@@ -0,0 +1,454 @@
|
||||
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
|
||||
)
|
||||
local tasklist_widget = type == "thumbnail"
|
||||
and awful.widget.tasklist({
|
||||
screen = awful.screen.focused(),
|
||||
filter = awful.widget.tasklist.filter.currenttags,
|
||||
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 = awful.widget.tasklist.filter.currenttags,
|
||||
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 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()
|
||||
awful.client.focus.byidx(-1)
|
||||
end,
|
||||
}),
|
||||
|
||||
awful.button({
|
||||
modifiers = { "Any" },
|
||||
button = scroll_next_key,
|
||||
on_press = function()
|
||||
awful.client.focus.byidx(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()
|
||||
awful.client.focus.byidx(1)
|
||||
end,
|
||||
|
||||
[previous_key] = function()
|
||||
awful.client.focus.byidx(1)
|
||||
end,
|
||||
[next_key] = function()
|
||||
awful.client.focus.byidx(-1)
|
||||
end,
|
||||
|
||||
[vim_previous_key] = function()
|
||||
awful.client.focus.byidx(1)
|
||||
end,
|
||||
[vim_next_key] = function()
|
||||
awful.client.focus.byidx(-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
|
||||
)
|
||||
window_switcher_box.visible = true
|
||||
end)
|
||||
end
|
||||
|
||||
return { enable = enable }
|
1
.config/awesome/extras/nice
Submodule
1
.config/awesome/extras/nice
Submodule
Submodule .config/awesome/extras/nice added at 810aa72bbe
Reference in New Issue
Block a user