Merge pull request #5 from JavaCafe01/master

New Tabbar Theme
This commit is contained in:
Nooo37 2020-11-24 08:11:57 +01:00 committed by GitHub
commit 1e680125df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 296 additions and 151 deletions

View File

@ -83,6 +83,7 @@ Put those variables in your ``theme.lua`` if you want to edit appearance and som
For the **mstab layout**:
```lua
mstab_bar_ontop -- allow the bar to be ontop of clients (default -> false)
mstab_tabbar_position -- set to "bottom" for tabbar at button
mstab_bar_height -- height of the tabbar
mstab_border_radius -- corners radius of the tabbar
@ -118,7 +119,7 @@ tabbed_spawn_into_tab -- set to true if you want new windows to spawn into y
## 😲 Preview
### Mstab (dynamic tabbing layout)
![](https://media.discordapp.net/attachments/716379882363551804/769870675250249808/shot_1025032923.png)
![](https://imgur.com/HZRgApE.png)
screenshot by [javacafe](https://github.com/JavaCafe01)
@ -146,7 +147,7 @@ gif by me :)
- [ ] Scratchpad module
- [ ] Some more documentation on the tabbed module
- [x] Add a cool alternative tabbar style
- [ ] Add another cool tabbar style (we need more styles)
- [x] Add another cool tabbar style (we need more styles)
- [ ] Make the mstab layout compatible with vertical tabbars (left and right)
- [ ] Add option to mstab layout to not shrink windows down when they are in the tabbed pane and unfocused (for example for people using transparent terminals)

View File

@ -8,91 +8,108 @@ local mylayout = {}
mylayout.name = "mstab"
local tabbar_ontop = beautiful.mstab_bar_ontop or false
local tabbar_padding = beautiful.mstab_bar_padding or "default"
local tabbar_height = beautiful.mstab_bar_height or beautiful.tabbar_size or 40
local border_radius = beautiful.mstab_border_radius or beautiful.border_radius or 0
local tabbar_font = beautiful.mstab_font or beautiful.tabbar_font or beautiful.font or "Monospace 8"
local bg_focus = beautiful.mstab_bg_focus or beautiful.tabbar_bg_focus or beautiful.bg_focus or "#ff0000"
local bg_normal = beautiful.mstab_bg_normal or beautiful.tabbar_bg_normal or beautiful.bg_normal or "#000000"
local fg_focus = beautiful.mstab_fg_focus or beautiful.tabbar_fg_focus or beautiful.fg_focus or "#000000"
local fg_normal = beautiful.mstab_fg_normal or beautiful.tabbar_fg_normal or beautiful.fg_normal or "#ffffff"
local border_radius =
beautiful.mstab_border_radius or beautiful.border_radius or 0
local tabbar_font = beautiful.mstab_font or beautiful.tabbar_font or
beautiful.font or "Monospace 8"
local bg_focus = beautiful.mstab_bg_focus or beautiful.tabbar_bg_focus or
beautiful.bg_focus or "#ff0000"
local bg_normal = beautiful.mstab_bg_normal or beautiful.tabbar_bg_normal or
beautiful.bg_normal or "#000000"
local fg_focus = beautiful.mstab_fg_focus or beautiful.tabbar_fg_focus or
beautiful.fg_focus or "#000000"
local fg_normal = beautiful.mstab_fg_normal or beautiful.tabbar_fg_normal or
beautiful.fg_normal or "#ffffff"
local tabbar_position = beautiful.mstab_tabbar_position or beautiful.tabbar_position or "top"
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 bar_style = beautiful.mstab_tabbar_style or beautiful.tabbar_style or
"default"
local bar = require(tostring(...):match(".*bling") .. ".widget.tabbar." ..
bar_style)
-- 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
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)
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)
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
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)
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_exists then
s.tabbar = wibox {
shape = function(cr, width, height) gears.shape.rounded_rect(cr, width, height, border_radius) end,
bg = bg_normal,
visible = true
}
s.tabbar = wibox {
ontop = tabbar_ontop,
shape = function(cr, width, height)
gears.shape.rounded_rect(cr, width, height, border_radius)
end,
bg = bg_normal,
visible = true
}
s.tabbar_exists = 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)
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("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)
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)
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.width = slave_area_width - 2 * t.gap
s.tabbar.height = tabbar_height
if tabbar_position == "bottom" then
if tabbar_position == "bottom" then
s.tabbar.y = area.y + area.height - tabbar_height - t.gap
end
end
-- update clientlist
s.tabbar:setup {
layout = wibox.layout.flex.horizontal,
clientlist,
}
s.tabbar:setup{layout = wibox.layout.flex.horizontal, clientlist}
end
@ -108,9 +125,7 @@ function mylayout.arrange(p)
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
if tabbar_padding == "default" then tabbar_padding = 2 * t.gap end
-- Special case: No masters -> full screen slave width
if nmaster == 0 then
@ -121,24 +136,22 @@ function mylayout.arrange(p)
-- 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_exists then
s.tabbar.visible = false
end
if s.tabbar_exists then s.tabbar.visible = false end
-- otherwise just do tile right
awful.layout.suit.tile.right.arrange(p)
return
end
return
end
-- Iterate through masters
for idx=1,nmaster do
local c = p.clients[idx]
local g = {
for idx = 1, nmaster do
local c = p.clients[idx]
local g = {
x = area.x,
y = area.y+(idx-1)*(area.height/nmaster),
y = area.y + (idx - 1) * (area.height / nmaster),
width = master_area_width,
height = area.height/nmaster,
}
p.geometries[c] = g
height = area.height / nmaster
}
p.geometries[c] = g
end
-- TODO: The way that the slave clients are arranged is currently very hacky and unclean
@ -146,41 +159,42 @@ function mylayout.arrange(p)
-- Currently clients are just shrunken down and placed "under" the "focused slave client"
-- Ideal would be hide the same way as that small scratchpad script:
-- https://github.com/notnew/awesome-scratch/blob/master/scratch.lua
-- 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=1, y=1, width=1, height=1}
local g = {
x = area.x + master_area_width + slave_area_width/4,
y = area.y + tabbar_height + area.height/4,
width = slave_area_width/2,
height = area.height/4 - tabbar_height
}
if idx == t.top_idx then
g.width = slave_area_width
g.height = area.height - tabbar_height - tabbar_padding
g.x = area.x + master_area_width
g.y = area.y
if tabbar_position == "top" then
g.y = g.y + tabbar_height + tabbar_padding
else
g.y = g.y
end
end
p.geometries[c] = g
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 = 1, y = 1, width = 1, height = 1}
local g = {
x = area.x + master_area_width + slave_area_width / 4,
y = area.y + tabbar_height + area.height / 4,
width = slave_area_width / 2,
height = area.height / 4 - tabbar_height
}
if idx == t.top_idx then
g.width = slave_area_width
g.height = area.height - tabbar_height - tabbar_padding
g.x = area.x + master_area_width
g.y = area.y
if tabbar_position == "top" then
g.y = g.y + tabbar_height + tabbar_padding
else
g.y = g.y
end
end
p.geometries[c] = g
end
update_tabbar(slave_clients, t, t.top_idx, area, master_area_width, slave_area_width)
update_tabbar(slave_clients, t, t.top_idx, area, master_area_width,
slave_area_width)
end
local icon_raw = gears.filesystem.get_configuration_dir() .. tostring(...):match("^.*bling"):gsub("%.", "/") .. "/icons/layouts/mstab.png"
local icon_raw = gears.filesystem.get_configuration_dir() ..
tostring(...):match("^.*bling"):gsub("%.", "/") ..
"/icons/layouts/mstab.png"
local function get_icon()
if icon_raw ~= nil then
@ -190,8 +204,4 @@ local function get_icon()
end
end
return {
layout = mylayout,
icon_raw = icon_raw,
get_icon = get_icon,
}
return {layout = mylayout, icon_raw = icon_raw, get_icon = get_icon}

View File

@ -1,4 +1,5 @@
local awful = require("awful")
local gears = require("gears")
local helpers = {}
@ -6,7 +7,7 @@ local helpers = {}
helpers.turn_off = function(c)
local current_tag = awful.tag.selected(c.screen)
local ctags = {}
for k,tag in pairs(c:tags()) do
for k, tag in pairs(c:tags()) do
if tag ~= current_tag then table.insert(ctags, tag) end
end
c:tags(ctags)
@ -16,7 +17,7 @@ end
helpers.turn_on = function(c)
local current_tag = awful.tag.selected(c.screen)
ctags = {current_tag}
for k,tag in pairs(c:tags()) do
for k, tag in pairs(c:tags()) do
if tag ~= current_tag then table.insert(ctags, tag) end
end
c:tags(ctags)
@ -24,4 +25,21 @@ helpers.turn_on = function(c)
client.focus = c
end
-- Create rounded rectangle shape (in one line)
helpers.rrect = function(radius)
return function(cr, width, height)
gears.shape.rounded_rect(cr, width, height, radius)
end
end
-- Create partially rounded rect
helpers.prrect = function(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 helpers

View File

@ -8,9 +8,7 @@ In the function themselves, the same object is refered to as "tabobj" which is w
you will often see something like: "local tabobj = some_client.bling_tabbed" at the beginning
of a function.
--]]
local awful = require("awful")
--]] local awful = require("awful")
local wibox = require("wibox")
local gears = require("gears")
local beautiful = require("beautiful")
@ -18,16 +16,12 @@ local beautiful = require("beautiful")
local helpers = require(tostring(...):match(".*bling.module") .. ".helpers")
local bar_style = beautiful.tabbar_style or "default"
local bar = require(tostring(...):match(".*bling") .. ".widget.tabbar." .. bar_style)
local bar = require(tostring(...):match(".*bling") .. ".widget.tabbar." ..
bar_style)
local function copy_size(c, parent_client)
if not c or not parent_client then
return
end
if not c.valid or not parent_client.valid then
return
end
if not c or not parent_client then return end
if not c.valid or not parent_client.valid then return end
c.floating = parent_client.floating
c.x = parent_client.x
c.y = parent_client.y
@ -40,17 +34,15 @@ tabbed = {}
-- 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.bling_tabbed then return end
if 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
if new_idx == 0 then new_idx = #tabobj.clients end
tabbed.switch_to(tabobj, new_idx)
end
end
-- removes a given client from its tab object
tabbed.remove = function(c)
tabbed.remove = function(c)
if not c.bling_tabbed then return end
local tabobj = c.bling_tabbed
table.remove(tabobj.clients, tabobj.focused_idx)
@ -63,13 +55,13 @@ end
tabbed.pop = function()
if not client.focus.bling_tabbed then return end
tabbed.remove(client.focus)
end
end
-- adds a client to a given tabobj
tabbed.add = function(c, tabobj)
if c.bling_tabbed then return end
copy_size(c, tabobj.clients[tabobj.focused_idx])
tabobj.clients[#tabobj.clients+1] = c
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
@ -89,19 +81,17 @@ tabbed.pick = function()
local output = handle:read("*a")
handle:close()
-- search the client that was picked
for _,c in ipairs(client.get()) do
if tonumber(c.pid) == tonumber(output) then
tabbed.add(c, tabobj)
end
end
for _, c in ipairs(client.get()) do
if tonumber(c.pid) == tonumber(output) then tabbed.add(c, tabobj) end
end
end
-- update everything about one tab object
tabbed.update = function(tabobj)
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
for idx, c in ipairs(tabobj.clients) do
if c.valid then
c.bling_tabbed = tabobj
copy_size(c, currently_focused_c)
-- the following handles killing a client while the client is tabbed
@ -111,18 +101,18 @@ tabbed.update = function(tabobj)
c:connect_signal("unmanage", function(c)
if not c.bling_tabbed then return end
local old_tabobj = c.bling_tabbed
local tabobj = {clients={}, focused_idx=1}
for _, c_temp in ipairs(old_tabobj.clients) do
local tabobj = {clients = {}, focused_idx = 1}
for _, c_temp in ipairs(old_tabobj.clients) do
if c_temp.window ~= c.window then
tabobj.clients[#tabobj.clients+1] = c_temp
tabobj.clients[#tabobj.clients + 1] = c_temp
end
end
end
tabbed.update(tabobj)
tabbed.switch_to(tabobj, 1)
end)
end
end
end
tabbed.update_tabbar(tabobj)
end
@ -130,45 +120,42 @@ end
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
for idx, c in ipairs(tabobj.clients) do
if idx ~= new_idx then
helpers.turn_off(c)
else
else
helpers.turn_on(c)
c:raise()
if old_focused_c and old_focused_c.valid then
if old_focused_c and old_focused_c.valid then
c:swap(old_focused_c)
end
copy_size(c, old_focused_c)
end
end
end
tabbed.update(tabobj)
end
tabbed.update_tabbar = function(tabobj)
local flexlist = bar.layout()
-- 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))
wid_temp = bar.create(c, (idx==tabobj.focused_idx), buttons)
for idx, c in ipairs(tabobj.clients) do
local buttons = gears.table.join(
awful.button({}, 1, function()
tabbed.switch_to(tabobj, idx)
end))
wid_temp = bar.create(c, (idx == tabobj.focused_idx), buttons)
flexlist:add(wid_temp)
end
end
-- add tabbar to each tabbed client (clients will be hided anyway)
for _,c in ipairs(tabobj.clients) do
for _, c in ipairs(tabobj.clients) do
local titlebar = awful.titlebar(c, {
bg_normal = bar.bg_normal,
bg_focus = bar.bg_focus,
bg = bar.bg_normal,
size = bar.size,
position = bar.position
})
titlebar:setup {
layout = wibox.layout.flex.horizontal,
flexlist,
}
end
end
titlebar:setup{layout = wibox.layout.flex.horizontal, flexlist}
end
end
tabbed.init = function(c)
local tabobj = {}
@ -177,11 +164,11 @@ tabbed.init = function(c)
tabbed.update(tabobj)
end
if beautiful.tabbed_spawn_in_tab then
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
if previous_client and previous_client.bling_tabbed then
tabbed.add(c, previous_client.bling_tabbed)
end
end)

129
widget/tabbar/modern.lua Normal file
View File

@ -0,0 +1,129 @@
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") .. ".module.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 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_orientation 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 {
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.visible = true
return tb
end
local function create(c, focused_bool, buttons)
-- local flexlist = wibox.layout.flex.horizontal()
local title_temp = c.name or c.class or "-"
local bg_temp = bg_normal
local fg_temp = fg_normal
if focused_bool then
bg_temp = bg_focus
fg_temp = 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 = text_temp
local close = create_title_button(c, close_color, bg_normal)
close:connect_signal("button::press", function() c:kill() 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,
{
{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 wid_temp = wibox.widget({
buttons = buttons,
{
tab_content,
bg = bg_temp,
shape = helpers.prrect(border_radius, true, true, false, false),
widget = wibox.container.background
},
top = dpi(8),
left = dpi(5),
right = dpi(5),
bottom = dpi(0),
widget = wibox.container.margin
})
return wid_temp
end
return {
layout = wibox.layout.flex.horizontal,
create = create,
position = "top",
size = size,
bg_normal = bg_normal,
bg_focus = bg_focus
}