2020-11-07 18:16:16 +01:00
|
|
|
--[[
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
2020-11-29 10:37:22 +01:00
|
|
|
--]]
|
|
|
|
|
|
|
|
local awful = require("awful")
|
2020-11-07 18:16:16 +01:00
|
|
|
local wibox = require("wibox")
|
|
|
|
local gears = require("gears")
|
|
|
|
local beautiful = require("beautiful")
|
2021-01-23 23:04:22 +01:00
|
|
|
local helpers = require(tostring(...):match(".*bling") .. ".helpers")
|
2020-11-07 18:16:16 +01:00
|
|
|
|
2020-11-22 10:18:39 +01:00
|
|
|
local bar_style = beautiful.tabbar_style or "default"
|
2021-08-27 20:01:22 +02:00
|
|
|
local bar = require(
|
|
|
|
tostring(...):match(".*bling") .. ".widget.tabbar." .. bar_style
|
|
|
|
)
|
2020-11-07 18:16:16 +01:00
|
|
|
|
|
|
|
tabbed = {}
|
|
|
|
|
2021-10-03 16:19:17 +02:00
|
|
|
-- 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
|
|
|
|
|
2021-08-27 20:01:22 +02:00
|
|
|
-- used to change focused tab relative to the currently focused one
|
2020-11-07 18:16:16 +01:00
|
|
|
tabbed.iter = function(idx)
|
2021-08-27 20:01:22 +02:00
|
|
|
if not idx then
|
|
|
|
idx = 1
|
|
|
|
end
|
|
|
|
if not client.focus or not client.focus.bling_tabbed then
|
|
|
|
return
|
|
|
|
end
|
2020-11-07 18:16:16 +01:00
|
|
|
local tabobj = client.focus.bling_tabbed
|
|
|
|
local new_idx = (tabobj.focused_idx + idx) % #tabobj.clients
|
2021-08-27 20:01:22 +02:00
|
|
|
if new_idx == 0 then
|
|
|
|
new_idx = #tabobj.clients
|
|
|
|
end
|
2020-11-07 18:16:16 +01:00
|
|
|
tabbed.switch_to(tabobj, new_idx)
|
2020-11-24 01:43:05 +01:00
|
|
|
end
|
2020-11-07 18:16:16 +01:00
|
|
|
|
2020-11-09 15:59:36 +01:00
|
|
|
-- removes a given client from its tab object
|
2020-11-24 01:43:05 +01:00
|
|
|
tabbed.remove = function(c)
|
2021-08-27 20:01:22 +02:00
|
|
|
if not c or not c.bling_tabbed then
|
|
|
|
return
|
|
|
|
end
|
2020-11-09 15:59:36 +01:00
|
|
|
local tabobj = c.bling_tabbed
|
|
|
|
table.remove(tabobj.clients, tabobj.focused_idx)
|
2021-07-27 16:36:20 +02:00
|
|
|
if not beautiful.tabbar_disable then
|
|
|
|
awful.titlebar.hide(c, bar.position)
|
|
|
|
end
|
2020-11-09 15:59:36 +01:00
|
|
|
c.bling_tabbed = nil
|
2021-10-03 16:19:17 +02:00
|
|
|
c:disconnect_signal("focus", update_tabbar_from)
|
|
|
|
c:disconnect_signal("unfocus", update_tabbar_from)
|
2021-08-18 20:39:33 +02:00
|
|
|
awesome.emit_signal("bling::tabbed::client_removed", tabobj, c)
|
2020-11-07 18:16:16 +01:00
|
|
|
tabbed.switch_to(tabobj, 1)
|
2020-11-09 15:59:36 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
-- removes the currently focused client from the tab object
|
|
|
|
tabbed.pop = function()
|
2021-08-27 20:01:22 +02:00
|
|
|
if not client.focus or not client.focus.bling_tabbed then
|
|
|
|
return
|
|
|
|
end
|
2020-11-09 15:59:36 +01:00
|
|
|
tabbed.remove(client.focus)
|
2020-11-24 01:43:05 +01:00
|
|
|
end
|
2020-11-07 18:16:16 +01:00
|
|
|
|
2020-11-09 15:59:36 +01:00
|
|
|
-- adds a client to a given tabobj
|
|
|
|
tabbed.add = function(c, tabobj)
|
2021-06-07 22:37:18 +02:00
|
|
|
if c.bling_tabbed then
|
|
|
|
tabbed.remove(c)
|
|
|
|
end
|
2021-10-03 16:19:17 +02:00
|
|
|
c:connect_signal("focus", update_tabbar_from)
|
|
|
|
c:connect_signal("unfocus", update_tabbar_from)
|
2021-03-15 13:50:25 +01:00
|
|
|
helpers.client.sync(c, tabobj.clients[tabobj.focused_idx])
|
2020-11-24 01:43:05 +01:00
|
|
|
tabobj.clients[#tabobj.clients + 1] = c
|
2020-11-09 15:59:36 +01:00
|
|
|
tabobj.focused_idx = #tabobj.clients
|
|
|
|
-- calls update even though switch_to calls update again
|
2021-08-27 20:01:22 +02:00
|
|
|
-- but the new client needs to have the tabobj property
|
2020-11-09 15:59:36 +01:00
|
|
|
-- before a clean switch can happen
|
|
|
|
tabbed.update(tabobj)
|
2021-08-18 20:39:33 +02:00
|
|
|
awesome.emit_signal("bling::tabbed::client_added", tabobj, c)
|
2020-11-09 15:59:36 +01:00
|
|
|
tabbed.switch_to(tabobj, #tabobj.clients)
|
|
|
|
end
|
|
|
|
|
2021-05-09 23:10:11 +02:00
|
|
|
-- use xwininfo to select one client and make it tab in the currently focused tab
|
2020-11-09 15:59:36 +01:00
|
|
|
tabbed.pick = function()
|
2021-08-27 20:01:22 +02:00
|
|
|
if not client.focus then
|
|
|
|
return
|
|
|
|
end
|
2021-05-09 23:10:11 +02:00
|
|
|
-- this function uses xwininfo to grab a client window id which is then
|
|
|
|
-- compared to all other clients window ids
|
2020-11-26 00:43:31 +01:00
|
|
|
|
2021-08-27 20:01:22 +02:00
|
|
|
local xwininfo_cmd =
|
|
|
|
[[ xwininfo | grep 'xwininfo: Window id:' | cut -d " " -f 4 ]]
|
2021-05-09 23:10:11 +02:00
|
|
|
awful.spawn.easy_async_with_shell(xwininfo_cmd, function(output)
|
2020-11-26 00:43:31 +01:00
|
|
|
for _, c in ipairs(client.get()) do
|
2021-05-09 23:10:11 +02:00
|
|
|
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
|
2020-11-26 00:43:31 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
2020-11-07 18:16:16 +01:00
|
|
|
end
|
|
|
|
|
2021-07-27 19:54:29 +02:00
|
|
|
-- select a client by direction and make it tab in the currently focused tab
|
2021-08-27 20:01:22 +02:00
|
|
|
tabbed.pick_by_direction = function(direction)
|
2021-07-27 19:54:29 +02:00
|
|
|
local sel = client.focus
|
2021-08-27 20:01:22 +02:00
|
|
|
if not sel then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if not sel.bling_tabbed then
|
|
|
|
tabbed.init(sel)
|
|
|
|
end
|
2021-07-27 19:54:29 +02:00
|
|
|
local c = helpers.client.get_by_direction(direction)
|
2021-08-27 20:01:22 +02:00
|
|
|
if not c then
|
|
|
|
return
|
|
|
|
end
|
2021-07-27 19:54:29 +02:00
|
|
|
tabbed.add(c, sel.bling_tabbed)
|
|
|
|
end
|
|
|
|
|
2021-08-27 20:01:22 +02:00
|
|
|
-- use dmenu to select a client and make it tab in the currently focused tab
|
2020-12-20 20:38:13 +01:00
|
|
|
tabbed.pick_with_dmenu = function(dmenu_command)
|
2021-08-27 20:01:22 +02:00
|
|
|
if not client.focus then
|
|
|
|
return
|
|
|
|
end
|
2020-12-20 20:38:13 +01:00
|
|
|
|
2021-08-27 20:01:22 +02:00
|
|
|
if not dmenu_command then
|
|
|
|
dmenu_command = "rofi -dmenu -i"
|
|
|
|
end
|
2020-12-20 20:38:13 +01:00
|
|
|
|
|
|
|
-- 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
|
2021-06-07 22:37:18 +02:00
|
|
|
if c.window ~= client.focus.window then
|
2020-12-20 20:38:13 +01:00
|
|
|
list_clients[#list_clients + 1] = c
|
|
|
|
if #list_clients ~= 1 then
|
|
|
|
list_clients_string = list_clients_string .. "\\n"
|
|
|
|
end
|
2021-08-27 20:01:22 +02:00
|
|
|
list_clients_string = list_clients_string
|
|
|
|
.. tostring(c.window)
|
|
|
|
.. " "
|
|
|
|
.. c.name
|
2020-12-20 20:38:13 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-08-27 20:01:22 +02:00
|
|
|
if #list_clients == 0 then
|
|
|
|
return
|
|
|
|
end
|
2020-12-20 20:38:13 +01:00
|
|
|
-- calls the actual dmenu
|
2021-08-27 20:01:22 +02:00
|
|
|
local xprop_cmd = [[ echo -e "]]
|
|
|
|
.. list_clients_string
|
|
|
|
.. [[" | ]]
|
|
|
|
.. dmenu_command
|
|
|
|
.. [[ | awk '{ print $1 }' ]]
|
2020-12-20 20:38:13 +01:00
|
|
|
awful.spawn.easy_async_with_shell(xprop_cmd, function(output)
|
|
|
|
for _, c in ipairs(list_clients) do
|
|
|
|
if tonumber(c.window) == tonumber(output) then
|
2021-08-27 20:01:22 +02:00
|
|
|
if not client.focus.bling_tabbed then
|
|
|
|
tabbed.init(client.focus)
|
|
|
|
end
|
2021-06-07 22:37:18 +02:00
|
|
|
local tabobj = client.focus.bling_tabbed
|
2020-12-20 20:38:13 +01:00
|
|
|
tabbed.add(c, tabobj)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2020-11-09 15:59:36 +01:00
|
|
|
-- update everything about one tab object
|
2020-11-24 01:43:05 +01:00
|
|
|
tabbed.update = function(tabobj)
|
2020-11-07 18:16:16 +01:00
|
|
|
local currently_focused_c = tabobj.clients[tabobj.focused_idx]
|
2020-11-09 15:59:36 +01:00
|
|
|
-- update tabobj of each client and other things
|
2020-11-24 01:43:05 +01:00
|
|
|
for idx, c in ipairs(tabobj.clients) do
|
|
|
|
if c.valid then
|
2020-11-09 15:59:36 +01:00
|
|
|
c.bling_tabbed = tabobj
|
2021-03-15 13:50:25 +01:00
|
|
|
helpers.client.sync(c, currently_focused_c)
|
2020-11-09 15:59:36 +01:00
|
|
|
-- the following handles killing a client while the client is tabbed
|
2021-08-27 20:01:22 +02:00
|
|
|
c:connect_signal("unmanage", function(c)
|
|
|
|
tabbed.remove(c)
|
|
|
|
end)
|
2020-11-09 15:59:36 +01:00
|
|
|
end
|
2020-11-24 01:43:05 +01:00
|
|
|
end
|
|
|
|
|
2021-08-18 20:39:33 +02:00
|
|
|
-- Maybe remove if I'm the only one using it?
|
2021-07-27 16:36:20 +02:00
|
|
|
awesome.emit_signal("bling::tabbed::update", tabobj)
|
2021-08-27 20:01:22 +02:00
|
|
|
if not beautiful.tabbar_disable then
|
2021-07-27 16:36:20 +02:00
|
|
|
tabbed.update_tabbar(tabobj)
|
|
|
|
end
|
2020-11-07 18:16:16 +01:00
|
|
|
end
|
|
|
|
|
2021-07-27 16:36:20 +02:00
|
|
|
-- change focused tab by absolute index
|
2020-11-07 18:16:16 +01:00
|
|
|
tabbed.switch_to = function(tabobj, new_idx)
|
|
|
|
local old_focused_c = tabobj.clients[tabobj.focused_idx]
|
|
|
|
tabobj.focused_idx = new_idx
|
2020-11-24 01:43:05 +01:00
|
|
|
for idx, c in ipairs(tabobj.clients) do
|
|
|
|
if idx ~= new_idx then
|
2021-01-23 23:04:22 +01:00
|
|
|
helpers.client.turn_off(c)
|
2020-11-24 01:43:05 +01:00
|
|
|
else
|
2021-01-23 23:04:22 +01:00
|
|
|
helpers.client.turn_on(c)
|
2020-11-07 18:16:16 +01:00
|
|
|
c:raise()
|
2020-11-24 01:43:05 +01:00
|
|
|
if old_focused_c and old_focused_c.valid then
|
2020-11-09 15:59:36 +01:00
|
|
|
c:swap(old_focused_c)
|
|
|
|
end
|
2021-03-15 13:50:25 +01:00
|
|
|
helpers.client.sync(c, old_focused_c)
|
2020-11-09 15:59:36 +01:00
|
|
|
end
|
2020-11-24 01:43:05 +01:00
|
|
|
end
|
2021-08-18 20:39:33 +02:00
|
|
|
awesome.emit_signal("bling::tabbed::changed_focus", tabobj)
|
2020-11-07 18:16:16 +01:00
|
|
|
tabbed.update(tabobj)
|
|
|
|
end
|
|
|
|
|
|
|
|
tabbed.update_tabbar = function(tabobj)
|
2020-11-09 15:59:36 +01:00
|
|
|
local flexlist = bar.layout()
|
2021-10-03 16:19:17 +02:00
|
|
|
local tabobj_focused_client = tabobj.clients[tabobj.focused_idx]
|
|
|
|
local tabobj_is_focused = (client.focus == tabobj_focused_client)
|
2020-11-07 18:16:16 +01:00
|
|
|
-- itearte over all tabbed clients to create the widget tabbed list
|
2020-11-24 01:43:05 +01:00
|
|
|
for idx, c in ipairs(tabobj.clients) do
|
2021-08-27 20:01:22 +02:00
|
|
|
local buttons = gears.table.join(awful.button({}, 1, function()
|
|
|
|
tabbed.switch_to(tabobj, idx)
|
|
|
|
end))
|
2021-10-03 16:19:17 +02:00
|
|
|
local wid_temp = bar.create(c, (idx == tabobj.focused_idx), buttons,
|
|
|
|
not tabobj_is_focused)
|
2020-11-07 18:16:16 +01:00
|
|
|
flexlist:add(wid_temp)
|
2020-11-24 01:43:05 +01:00
|
|
|
end
|
2020-11-07 18:16:16 +01:00
|
|
|
-- add tabbar to each tabbed client (clients will be hided anyway)
|
2020-11-24 01:43:05 +01:00
|
|
|
for _, c in ipairs(tabobj.clients) do
|
2020-11-07 18:16:16 +01:00
|
|
|
local titlebar = awful.titlebar(c, {
|
2020-11-24 01:43:05 +01:00
|
|
|
bg = bar.bg_normal,
|
2020-11-15 10:05:56 +01:00
|
|
|
size = bar.size,
|
2021-08-27 20:01:22 +02:00
|
|
|
position = bar.position,
|
2020-11-07 18:16:16 +01:00
|
|
|
})
|
2021-08-27 20:01:22 +02:00
|
|
|
titlebar:setup({ layout = wibox.layout.flex.horizontal, flexlist })
|
2020-11-24 01:43:05 +01:00
|
|
|
end
|
|
|
|
end
|
2020-11-07 18:16:16 +01:00
|
|
|
|
|
|
|
tabbed.init = function(c)
|
2020-11-09 15:59:36 +01:00
|
|
|
local tabobj = {}
|
2021-08-27 20:01:22 +02:00
|
|
|
tabobj.clients = { c }
|
2021-10-03 16:19:17 +02:00
|
|
|
c:connect_signal("focus", update_tabbar_from)
|
|
|
|
c:connect_signal("unfocus", update_tabbar_from)
|
2020-11-09 15:59:36 +01:00
|
|
|
tabobj.focused_idx = 1
|
|
|
|
tabbed.update(tabobj)
|
2020-11-07 18:16:16 +01:00
|
|
|
end
|
|
|
|
|
2020-11-24 01:43:05 +01:00
|
|
|
if beautiful.tabbed_spawn_in_tab then
|
2020-11-15 10:05:56 +01:00
|
|
|
client.connect_signal("manage", function(c)
|
|
|
|
local s = awful.screen.focused()
|
|
|
|
local previous_client = awful.client.focus.history.get(s, 1)
|
2020-11-24 01:43:05 +01:00
|
|
|
if previous_client and previous_client.bling_tabbed then
|
2020-11-15 10:05:56 +01:00
|
|
|
tabbed.add(c, previous_client.bling_tabbed)
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2020-11-07 18:16:16 +01:00
|
|
|
return tabbed
|