Implement window switcher

This commit is contained in:
elenapan 2020-03-22 13:29:43 +02:00
parent a6b7c069a2
commit dfd34f11bc
3 changed files with 290 additions and 18 deletions

View file

@ -0,0 +1,274 @@
local awful = require("awful")
local wibox = require("wibox")
local beautiful = require("beautiful")
local helpers = require("helpers")
local keys = require("keys")
-- TODO ability to switch to minimized clients:
-- Might need to ditch the "easy" tasklist approach for something manual
local window_switcher_margin = dpi(10)
local item_size = dpi(50)
-- Set up text symbols and colors to be used instead of awful.widget.clienticon
-- Based on the client's `class` property
local class_icons = {
-- Terminals
['kitty'] = { symbol = "", color = x.color5 },
['Alacritty'] = { symbol = "", color = x.color5 },
['Termite'] = { symbol = "", color = x.color5 },
['URxvt'] = { symbol = "", color = x.color5 },
['st'] = { symbol = "", color = x.color5 },
['st-256color'] = { symbol = "", color = x.color5 },
-- Image viewers
['feh'] = { symbol = "", color = x.color1 },
['Sxiv'] = { symbol = "", color = x.color1 },
-- General
['TelegramDesktop'] = { symbol = "", color = x.color4 },
['Firefox'] = { symbol = "", color = x.color3 },
['Chromium'] = { symbol = "", color = x.color4 },
['Chromium-browser'] = { symbol = "", color = x.color4 },
['Steam'] = { symbol = "", color = x.color2 },
['Lutris'] = { symbol = "", color = x.color6 },
['editor'] = { symbol = "", color = x.color5 },
['Emacs'] = { symbol = "", color = x.color5 },
['email'] = { symbol = "", color = x.color6 },
['music'] = { symbol = "", color = x.color6 },
['mpv'] = { symbol = "", color = x.color6 },
['KeePassXC'] = { symbol = "", color = x.color1 },
['Gucharmap'] = { symbol = "", color = x.color6 },
['Pavucontrol'] = { symbol = "", color = x.color4 },
['htop'] = { symbol = "", color = x.color2 },
['Screenruler'] = { symbol = "", color = x.color3 },
['Galculator'] = { symbol = "", color = x.color2 },
-- File managers
['Thunar'] = { symbol = "", color = x.color3 },
['Nemo'] = { symbol = "", color = x.color3 },
['Gimp'] = { symbol = "", color = x.color4 },
['Inkscape'] = { symbol = "", color = x.color2 },
['Gpick'] = { symbol = "", color = x.color6 },
-- Default
['_'] = { symbol = "", color = x.color7.."99" }
}
awful.screen.connect_for_each_screen(function(s)
s.window_switcher = wibox({
width = dpi(500),
height = dpi(100), -- Will change dynamically
visible = false,
ontop = true,
screen = s,
bg = "#00000000",
fg = x.foreground
})
awful.placement.centered(s.window_switcher, { honor_workarea = true, honor_padding = true })
-- Tasklist
s.window_switcher_tasklist = awful.widget.tasklist {
screen = s,
filter = awful.widget.tasklist.filter.currenttags,
buttons = keys.tasklist_buttons,
style = {
font = beautiful.tasklist_font,
-- font = "sans 10 medium",
bg = x.color0,
},
layout = {
layout = wibox.layout.fixed.vertical
},
widget_template = {
{
-- Standard icon (from beautiful.icon_theme)
-- {
-- awful.widget.clienticon,
-- margins = 5,
-- widget = wibox.container.margin
-- },
-- Text icon
{
id = 'text_icon',
font = 'icomoon 25',
forced_width = dpi(50),
align = "center",
valign = "center",
widget = wibox.widget.textbox,
},
{
{
id = 'text_role',
align = "center",
widget = wibox.widget.textbox,
},
left = dpi(6),
right = dpi(14),
-- Add margins to top and bottom in order to force the
-- text to be on a single line, if needed. Might need
-- to adjust them according to font size.
top = dpi(14),
bottom = dpi(14),
widget = wibox.container.margin
},
layout = wibox.layout.fixed.horizontal
},
forced_height = item_size,
id = "bg_role",
widget = wibox.container.background,
create_callback = function(self, c, _, __)
local i
if class_icons[c.class] then
i = c.class
else
i = '_'
end
self:get_children_by_id('text_icon')[1].markup = helpers.colorize_text(class_icons[i].symbol, class_icons[i].color)
end,
},
}
s.window_switcher:setup {
{
s.window_switcher_tasklist,
margins = window_switcher_margin,
widget = wibox.container.margin
},
bg = x.color0,
shape = helpers.rrect(beautiful.border_radius),
widget = wibox.container.background
}
end)
-- Keygrabber configuration
local window_switcher_grabber
local function window_switcher_hide()
-- Add currently focused client to history
if client.focus then
awful.client.focus.history.add(client.focus)
client.focus:raise()
end
-- Resume recording focus history
awful.client.focus.history.enable_tracking()
-- Stop and hide window_switcher
local s = awful.screen.focused()
awful.keygrabber.stop(window_switcher_grabber)
s.window_switcher.visible = false
end
-- Helper functions for keybinds
local window_search = function()
window_switcher_hide()
awful.spawn.with_shell("rofi -show windowcd")
end
local function get_num_clients(s)
local clients = s.selected_tag:clients()
local num_clients = 0
for _, c in pairs(clients) do
if not c.skip_taskbar then
num_clients = num_clients + 1
end
end
return num_clients
end
local close = function()
if client.focus then
client.focus:kill()
local s = awful.screen.focused()
local num_clients = get_num_clients(s)
-- Hide or adjust size and position if needed
if num_clients > 1 then
s.window_switcher.height = s.window_switcher.height - item_size
awful.placement.centered(s.window_switcher, { honor_workarea = true, honor_padding = true })
else
window_switcher_hide()
end
end
end
local unminimize = function()
local c = awful.client.restore()
-- Focus restored client
if c then
client.focus = c
c:raise()
end
end
local cycle = function(direction)
awful.client.focus.byidx(direction)
if client.focus then
client.focus:raise()
end
end
-- Set up keybinds
-- Single keys only
local keybinds = {
['Escape'] = window_switcher_hide,
['Tab'] = function() cycle(1) end,
-- (Un)Minimize
['n'] = function() if client.focus then client.focus.minimized = true end end,
['N'] = unminimize,
['u'] = unminimize, -- `u` for up
-- Close
['d'] = close,
['q'] = close,
-- Move with vim keys
['j'] = function() cycle(1) end,
['k'] = function() cycle(-1) end,
-- Move with arrow keys
['Down'] = function() cycle(1) end,
['Up'] = function() cycle(-1) end,
-- Space
[' '] = window_search
}
function window_switcher_show(s)
local num_clients = get_num_clients(s)
if num_clients == 0 or (num_clients == 1 and client.focus) then
return
end
-- Adjust size and position
s.window_switcher.height = num_clients * item_size + window_switcher_margin * 2
awful.placement.centered(s.window_switcher, { honor_workarea = true, honor_padding = true })
-- Go to previously focused client (in the tag)
awful.client.focus.history.previous()
if client.focus then
client.focus:raise()
end
-- Stop recording focus history
awful.client.focus.history.disable_tracking()
-- Start the keygrabber
window_switcher_grabber = awful.keygrabber.run(function(modifiers, 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()
end
-- Do nothing
return
end
-- Run function attached to key, if it exists
if keybinds[key] then
keybinds[key]()
end
end)
-- Finally make the window switcher wibox visible
s.window_switcher.visible = true
end

View file

@ -131,19 +131,25 @@ keys.globalkeys = gears.table.join(
end,
{description = "focus right", group = "client"}),
-- Focus client by index (cycle through clients)
-- Window switcher
awful.key({ superkey }, "Tab",
function ()
awful.client.focus.byidx( 1)
window_switcher_show(awful.screen.focused())
end,
{description = "focus next by index", group = "client"}
),
awful.key({ superkey, shiftkey }, "Tab",
{description = "activate window switcher", group = "client"}),
-- Focus client by index (cycle through clients)
awful.key({ superkey }, "z",
function ()
awful.client.focus.byidx(1)
end,
{description = "focus next by index", group = "client"}),
awful.key({ superkey, shiftkey }, "z",
function ()
awful.client.focus.byidx(-1)
end,
{description = "focus previous by index", group = "client"}
),
{description = "focus next by index", group = "client"}),
-- Gaps
awful.key({ superkey, shiftkey }, "minus",
@ -217,14 +223,6 @@ keys.globalkeys = gears.table.join(
end,
{description = "jump to urgent client", group = "client"}),
awful.key({ superkey, }, "z",
function ()
awful.client.focus.history.previous()
if client.focus then
client.focus:raise()
end
end,
{description = "go back", group = "client"}),
awful.key({ superkey, }, "x",
function ()
awful.tag.history.restore()
@ -727,10 +725,8 @@ keys.clientkeys = gears.table.join(
-- Minimize
awful.key({ superkey, }, "n",
function (c)
-- The client currently has the input focus, so it cannot be
-- minimized, since minimized clients can't have the focus.
c.minimized = true
end ,
end,
{description = "minimize", group = "client"}),
-- Maximize

View file

@ -202,6 +202,8 @@ require("elemental.dashboard")
require("elemental.lock_screen")
-- App drawer
require("elemental.app_drawer")
-- Window switcher
require("elemental.window_switcher")
-- >> Daemons
-- Most widgets that display system/external info depend on evil.