This commit is contained in:
Aire-One 2024-11-03 01:59:30 +01:00
parent 67759f302d
commit fb75185889
105 changed files with 4371 additions and 1457 deletions

13
.busted Normal file
View File

@ -0,0 +1,13 @@
if os.getenv "LOCAL_LUA_DEBUGGER_VSCODE" == "1" then
require("lldebugger").start()
end
local function lua_module_paths(module_base_path)
return (module_base_path .. "/?.lua;") .. (module_base_path .. "/?/init.lua;")
end
return {
_all = {
lpath = lua_module_paths "src",
},
}

70
.cspell.json Normal file
View File

@ -0,0 +1,70 @@
{
"words": [
"Aire-One",
"autofocus",
"awesomerc",
"byidx",
"capi",
"closebutton",
"confdir",
"conffile",
"currenttags",
"dbus",
"drawin",
"dryrun",
"fatalwarnings",
"floatingbutton",
"fullscreen",
"getmaster",
"halign",
"iconwidget",
"imagebox",
"incmwfact",
"incncol",
"incnmaster",
"jumpto",
"keyboardlayout",
"keygrabber",
"keygroup",
"layoutbox",
"ldoc",
"lldebugger",
"lpath",
"luacheck",
"luacheckrc",
"luamon",
"luamonrc",
"luarocks",
"maximizedbutton",
"modkey",
"mousebinding",
"mousebindings",
"mousegrabber",
"noreset",
"numpad",
"numrow",
"ontop",
"ontopbutton",
"pkill",
"rcfile",
"rockspec",
"stickybutton",
"Stylua",
"systray",
"taglist",
"tasklist",
"textbox",
"textclock",
"titlebar",
"titlebars",
"titlewidget",
"unminimize",
"valign",
"viewnext",
"viewprev",
"viewtoggle",
"wibar",
"wibox",
"xephyr"
]
}

View File

@ -5,8 +5,17 @@ root = true
[*] [*]
indent_style = space indent_style = space
indent_size = 4 indent_size = 3
end_of_line = lf end_of_line = lf
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.json]
indent_size = 2
[*.sh]
indent_size = 4
[Makefile]
indent_style = tab

View File

@ -1,10 +1,28 @@
-- Only allow symbols available in all Lua versions std = "lua54"
std = "min"
-- Get rid of "unused argument self"-warnings files[".luamonrc"].std = {
self = false globals = {
"ext",
"lang",
},
}
files["awesomerc-dev-1.rockspec"].ignore = {
"631", -- Line is too long.
}
include_files = {
".busted",
".luacheckrc",
".luamonrc",
"*.rockspec",
"src/**/*.lua",
}
exclude_files = {
"src/awesome-wm-nice", -- Vendored code with its own .luacheckrc
}
-- Global objects defined by the C code
read_globals = { read_globals = {
"awesome", "awesome",
"button", "button",
@ -21,13 +39,9 @@ read_globals = {
"math.atan2", "math.atan2",
} }
-- screen may not be read-only, because newer luacheck versions complain about globals = {
-- screen[1].tags[1].selected = true. "screen",
-- The same happens with the following code: "mouse",
-- local tags = mouse.screen.tags "root",
-- tags[7].index = 4 "client",
-- client may not be read-only due to client.focus. }
globals = { "screen", "mouse", "root", "client" }
-- Enable cache (uses .luacheckcache relative to this rc file).
cache = true

2
.luamonrc Normal file
View File

@ -0,0 +1,2 @@
ext = { "lua" }
lang = "./scripts/run.sh runAwesome"

6
.stylua.toml Normal file
View File

@ -0,0 +1,6 @@
indent_type = "Spaces"
indent_width = 3
call_parentheses = "None"
[sort_requires]
enabled = true

View File

@ -1,9 +1,10 @@
{ {
"recommendations": [ "recommendations": [
"editorconfig.editorconfig", "streetsidesoftware.code-spell-checker",
"tomblind.local-lua-debugger-vscode", "editorconfig.editorconfig",
"sumneko.lua", "tomblind.local-lua-debugger-vscode",
"johnnymorganz.stylua", "johnnymorganz.stylua",
"dwenegar.vscode-luacheck" "dwenegar.vscode-luacheck",
] "sumneko.lua"
]
} }

32
.vscode/launch.json vendored
View File

@ -1,19 +1,17 @@
{ {
"version": "0.2.0", // Use IntelliSense to learn about possible attributes.
"configurations": [ // Hover to view descriptions of existing attributes.
{ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"type": "lua-local", "version": "0.2.0",
"request": "launch", "configurations": [
"name": "Debug with Xephyr", {
"program": { "type": "lua-local",
"command": "${workspaceFolder}/start-xephyr.sh" "request": "launch",
}, "name": "Debug with Xephyr",
"args": [ "program": {
"/usr/bin/Xephyr", "command": "${workspaceFolder}/scripts/run.sh"
"/usr/bin/awesome", },
"${workspaceFolder}/init.lua" "args": ["debug"]
], }
"postDebugTask": "Terminate All Tasks" ]
}
]
} }

25
.vscode/settings.json vendored
View File

@ -1,9 +1,20 @@
{ {
"editor.formatOnSave": true, "Lua.runtime.path": [
"editor.formatOnPaste": true, "/usr/share/awesome/lib/?.lua",
"[markdown]": { "/usr/share/awesome/lib/?/init.lua",
"editor.wordWrap": "on", "${workspaceFolder}/src/?.lua",
"editor.renderWhitespace": "all", "${workspaceFolder}/src/init.lua",
"editor.acceptSuggestionOnEnter": "off" "${workspaceFolder}/src/?/init.lua"
} ],
"[lua]": {
"editor.defaultFormatter": "JohnnyMorganz.stylua"
},
"stylua.targetReleaseVersion": "latest",
"files.associations": {
".busted": "lua",
".luacheckrc": "lua",
".luamonrc": "lua",
"*.rockspec": "lua"
},
"Lua.runtime.version": "Lua 5.4"
} }

19
.vscode/tasks.json vendored
View File

@ -1,19 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Terminate All Tasks",
"command": "${workspaceFolder}/start-xephyr.sh stop",
"type": "shell",
"problemMatcher": []
}
],
"inputs": [
{
"id": "terminate",
"type": "command",
"command": "workbench.action.tasks.terminate",
"args": "terminateAll"
}
]
}

37
Makefile Normal file
View File

@ -0,0 +1,37 @@
dev:
scripts/run.sh start
test:
luarocks test
# Install the rock in the local tree (aka user home directory)
deploy:
luarocks --local build --force
mkdir -p ~/.config/awesome
cp -rv config/awesome ~/.config
try-current:
scripts/run.sh try-current
luacheck:
luacheck .
stylua:
stylua --check .
# ldoc-dryrun:
# $(eval TMP := $(shell mktemp -d))
# ldoc --fatalwarnings --dir $(TMP) .
# rm -rf $(TMP)
cspell:
cspell lint .
lint-rockspec:
luarocks lint awesomerc-dev-1.rockspec
# For now:
# - we don't run ldoc-dryrun because we don't have documentation
# - we don't run cspell because there are too many problems I don't want to fix now since the code will change
# lint: luacheck stylua ldoc-dryrun cspell lint-rockspec
lint: luacheck stylua lint-rockspec

77
awesomerc-dev-1.rockspec Normal file
View File

@ -0,0 +1,77 @@
rockspec_format = "3.0"
package = "awesomerc"
version = "dev-1"
source = {
url = "git://gitea.aireone.xyz/Aire-One/awesomerc.git",
}
description = {
homepage = "https://gitea.aireone.xyz/Aire-One/awesomerc",
license = "GPL 2.0", -- TODO: git add the LICENSE file
-- Vendored awesome-wm-nice is licensed by mut-ex under the MIT license.
}
dependencies = {
"awesome-slot",
"awesome-battery_widget",
}
build = {
type = "builtin",
modules = {
["awesomerc.configuration.applications"] = "src/awesomerc/configuration/applications.lua",
["awesomerc.configuration.bindings.client_keybindings"] = "src/awesomerc/configuration/bindings/client_keybindings.lua",
["awesomerc.configuration.bindings.client_mousebindings"] = "src/awesomerc/configuration/bindings/client_mousebindings.lua",
["awesomerc.configuration.bindings.global_keybindings"] = "src/awesomerc/configuration/bindings/global_keybindings.lua",
["awesomerc.configuration.bindings.global_mousebindings"] = "src/awesomerc/configuration/bindings/global_mousebindings.lua",
["awesomerc.configuration.bindings.init"] = "src/awesomerc/configuration/bindings/init.lua",
["awesomerc.configuration.bindings.utils"] = "src/awesomerc/configuration/bindings/utils.lua",
["awesomerc.configuration.init"] = "src/awesomerc/configuration/init.lua",
["awesomerc.configuration.menu.init"] = "src/awesomerc/configuration/menu/init.lua",
["awesomerc.configuration.menu.myawesomemenu"] = "src/awesomerc/configuration/menu/myawesomemenu.lua",
["awesomerc.configuration.menu.mymainmenu"] = "src/awesomerc/configuration/menu/mymainmenu.lua",
["awesomerc.configuration.prompt_commands"] = "src/awesomerc/configuration/prompt_commands.lua",
["awesomerc.configuration.rules.client.firefox"] = "src/awesomerc/configuration/rules/client/firefox.lua",
["awesomerc.configuration.rules.client.floating"] = "src/awesomerc/configuration/rules/client/floating.lua",
["awesomerc.configuration.rules.client.global"] = "src/awesomerc/configuration/rules/client/global.lua",
["awesomerc.configuration.rules.client.init"] = "src/awesomerc/configuration/rules/client/init.lua",
["awesomerc.configuration.rules.client.titlebars"] = "src/awesomerc/configuration/rules/client/titlebars.lua",
["awesomerc.configuration.rules.init"] = "src/awesomerc/configuration/rules/init.lua",
["awesomerc.configuration.rules.notification.global"] = "src/awesomerc/configuration/rules/notification/global.lua",
["awesomerc.configuration.rules.notification.init"] = "src/awesomerc/configuration/rules/notification/init.lua",
["awesomerc.configuration.tag_layouts"] = "src/awesomerc/configuration/tag_layouts.lua",
["awesomerc.slots.init"] = "src/awesomerc/slots/init.lua",
["awesomerc.theme.init"] = "src/awesomerc/theme/init.lua",
["awesomerc.ui.desktop_decoration.bar.init"] = "src/awesomerc/ui/desktop_decoration/bar/init.lua",
["awesomerc.ui.desktop_decoration.bar.widgets.battery"] = "src/awesomerc/ui/desktop_decoration/bar/widgets/battery.lua",
["awesomerc.ui.desktop_decoration.bar.widgets.init"] = "src/awesomerc/ui/desktop_decoration/bar/widgets/init.lua",
["awesomerc.ui.desktop_decoration.bar.widgets.prompt"] = "src/awesomerc/ui/desktop_decoration/bar/widgets/prompt.lua",
["awesomerc.ui.desktop_decoration.init"] = "src/awesomerc/ui/desktop_decoration/init.lua",
["awesomerc.ui.hotkeys_popup.init"] = "src/awesomerc/ui/hotkeys_popup/init.lua",
["awesomerc.ui.menu.mymainmenu"] = "src/awesomerc/ui/menu/mymainmenu.lua",
["awesomerc.ui.titlebar.init"] = "src/awesomerc/ui/titlebar/init.lua",
["awesomerc.init"] = "src/awesomerc/init.lua",
["awesome-legacy.autofocus.init"] = "src/awesome-legacy/autofocus/init.lua",
["awesome-legacy.manage_error.init"] = "src/awesome-legacy/manage_error/init.lua",
["awesome-legacy.sloppy_focus.init"] = "src/awesome-legacy/sloppy_focus/init.lua",
["awesome-legacy.init"] = "src/awesome-legacy/init.lua",
["MyTagListWidget.TagItem_widget"] = "src/MyTagListWidget/TagItem_widget.lua",
["MyTagListWidget.init"] = "src/MyTagListWidget/init.lua",
["MyTagListWidget.mockup"] = "src/MyTagListWidget/mockup.lua",
["MyTagListWidget.tests.widget-testrc"] = "src/MyTagListWidget/tests/widget-testrc.lua",
["awesome-wm-nice.colors"] = "src/awesome-wm-nice/colors.lua",
["awesome-wm-nice.config"] = "src/awesome-wm-nice/config.lua",
["awesome-wm-nice.init"] = "src/awesome-wm-nice/init.lua",
["awesome-wm-nice.shade"] = "src/awesome-wm-nice/shade.lua",
["awesome-wm-nice.shapes"] = "src/awesome-wm-nice/shapes.lua",
["awesome-wm-nice.table"] = "src/awesome-wm-nice/table.lua",
["awesome-wm-nice.utils"] = "src/awesome-wm-nice/utils.lua",
["awesome-wm-nice.widgets"] = "src/awesome-wm-nice/widgets.lua",
},
}
test = {
type = "busted",
}

View File

Before

Width:  |  Height:  |  Size: 441 B

After

Width:  |  Height:  |  Size: 441 B

View File

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 337 B

View File

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

9
config/awesome/rc.lua Normal file
View File

@ -0,0 +1,9 @@
-- awesome_mode: api-level=4:screen=on
if os.getenv "LOCAL_LUA_DEBUGGER_VSCODE" == "1" then
require("lldebugger").start()
end
require "luarocks.loader"
require "awesomerc"

View File

@ -1,113 +0,0 @@
local akey = require "awful.key"
local aclient = require "awful.client"
local utils = require "rc.configuration.bindings.utils"
local client_keybindings = {
akey {
modifiers = { utils.mods.modkey },
key = "f",
description = "toggle fullscreen",
group = utils.groups.client,
on_press = function(client)
client.fullscreen = not client.fullscreen
client:raise()
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "c",
description = "close",
group = utils.groups.client,
on_press = function(client)
client:kill()
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.control },
key = "space",
description = "toggle floating",
group = utils.groups.client,
on_press = aclient.floating.toggle,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.control },
key = "Return",
description = "move to master",
group = utils.groups.client,
on_press = function(client)
client:swap(aclient.getmaster())
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "o",
description = "move to screen",
group = utils.groups.client,
on_press = function(client)
client:move_to_screen()
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "t",
description = "toggle keep on top",
group = utils.groups.client,
on_press = function(client)
client.ontop = not client.ontop
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "n",
description = "minimize",
group = utils.groups.client,
on_press = function(client)
-- The client currently has the input focus, so it cannot be
-- minimized, since minimized clients can't have the focus.
client.minimized = true
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "m",
description = "(un)maximize",
group = utils.groups.client,
on_press = function(client)
client.maximized = not client.maximized
client:raise()
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.control },
key = "m",
description = "(un)maximize vertically",
group = utils.groups.client,
on_press = function(client)
client.maximized_vertical = not client.maximized_vertical
client:raise()
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "m",
description = "(un)maximize horizontally",
group = utils.groups.client,
on_press = function(client)
client.maximized_horizontal = not client.maximized_horizontal
client:raise()
end,
},
}
return client_keybindings

View File

@ -1,39 +0,0 @@
local abutton = require "awful.button"
local utils = require "rc.configuration.bindings.utils"
local mousebindings = {
abutton {
modifiers = {},
button = abutton.names.LEFT,
on_press = function(client)
client:activate {
context = "mouse_click",
}
end,
},
abutton {
modifiers = { utils.mods.modkey },
button = abutton.names.LEFT,
on_press = function(client)
client:activate {
context = "mouse_click",
action = "mouse_move",
}
end,
},
abutton {
modifiers = { utils.mods.modkey },
button = abutton.names.RIGHT,
on_press = function(client)
client:activate {
context = "mouse_click",
action = "mouse_resize",
}
end,
},
}
return mousebindings

View File

@ -1,223 +0,0 @@
local aclient = require "awful.client"
local akey = require "awful.key"
local aprompt = require "awful.prompt"
local ascreen = require "awful.screen"
local aspawn = require "awful.spawn"
local atag = require "awful.tag"
local autil = require "awful.util"
local menubar = require "menubar"
local applications = require "rc.configuration.applications"
local desktop_bar = require "rc.ui.desktop_decoration.bar"
local hotkeys_popup = require "rc.ui.hotkeys_popup"
local mymainmenu = require "rc.ui.menu.mymainmenu"
local utils = require "rc.configuration.bindings.utils"
local capi = {
awesome = _G.awesome,
client = _G.client,
}
local global_keybindings = {
-- Awesome
akey {
modifiers = { utils.mods.modkey },
key = "s",
description = "show help",
group = utils.groups.awesome,
on_press = function()
hotkeys_popup.show_help()
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "w",
description = "show main menu",
group = utils.groups.awesome,
on_press = function()
mymainmenu():show()
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.control },
key = "r",
description = "reload awesome",
group = utils.groups.awesome,
on_press = capi.awesome.restart,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "q",
description = "quit awesome",
group = utils.groups.awesome,
on_press = capi.awesome.quit,
},
akey {
modifiers = { utils.mods.modkey },
key = "x",
description = "lua execute prompt",
group = utils.groups.awesome,
on_press = function()
aprompt.run {
prompt = "Run Lua code: ",
textbox = desktop_bar(ascreen.focused()).promptbox.widget,
exe_callback = autil.eval,
history_path = autil.get_cache_dir() .. "/history_eval",
}
end,
},
-- Launcher
akey {
modifiers = { utils.mods.modkey },
key = "Return",
description = "open a terminal",
group = utils.groups.launcher,
on_press = function()
aspawn(applications.open_terminal())
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "r",
description = "run prompt",
group = utils.groups.launcher,
on_press = function()
desktop_bar(ascreen.focused()).promptbox:run()
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "p",
description = "show the menubar",
group = utils.groups.launcher,
on_press = function()
menubar.show()
end,
},
-- Client focus
akey {
modifiers = { utils.mods.modkey },
key = "j",
group = "client",
description = "Focus next client by index",
on_press = function()
aclient.focus.byidx(1)
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "k",
group = "client",
description = "Focus previous by index",
on_press = function()
aclient.focus.byidx(-1)
end,
},
-- Layout manipulation
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "j",
group = "client",
description = "Swap with next client",
on_press = function()
aclient.swap.byidx(1)
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "k",
group = "client",
description = "Swap with previous client",
on_press = function()
aclient.swap.byidx(-1)
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "Right",
description = "Increase master width factor",
group = "client",
on_press = function()
atag.incmwfact(0.01)
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "Left",
description = "Decrease master width factor",
group = "client",
on_press = function()
atag.incmwfact(-0.01)
end,
},
-- Tags manipulation
akey {
modifiers = { utils.mods.modkey },
keygroup = akey.keygroup.NUMROW,
description = "only view tag",
group = "tag",
on_press = function(index)
local screen = ascreen.focused()
local tag = screen.tags[index]
if tag then
tag:view_only()
end
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.control },
keygroup = akey.keygroup.NUMROW,
description = "toggle tag",
group = "tag",
on_press = function(index)
local screen = ascreen.focused()
local tag = screen.tags[index]
if tag then
atag.viewtoggle(tag)
end
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
keygroup = akey.keygroup.NUMROW,
description = "move focused client to tag",
group = "tag",
on_press = function(index)
local screen = ascreen.focused()
local client = capi.client.focus
local tag = screen.tags[index]
if client and tag then
client:move_to_tag(tag)
end
end,
},
}
return global_keybindings

View File

@ -1,28 +0,0 @@
local abutton = require "awful.button"
local atag = require "awful.tag"
local mymainmenu = require "rc.ui.menu.mymainmenu"
local global_mousebindings = {
abutton {
modifiers = {},
button = abutton.names.RIGHT,
on_press = function()
mymainmenu():toggle()
end,
},
abutton {
modifiers = {},
button = abutton.names.SCROLL_UP,
on_press = tag.viewprev,
},
abutton {
modifiers = {},
button = abutton.names.SCROLL_DOWN,
on_press = atag.viewnext,
},
}
return global_mousebindings

View File

@ -1,13 +0,0 @@
local bindings = {}
bindings.client_keybindings =
require "rc.configuration.bindings.client_keybindings"
bindings.client_mousebindings =
require "rc.configuration.bindings.client_mousebindings"
bindings.global_keybindings =
require "rc.configuration.bindings.global_keybindings"
bindings.global_mousebindings =
require "rc.configuration.bindings.global_mousebindings"
bindings.utils = require "rc.configuration.bindings.utils"
return bindings

View File

@ -1,16 +0,0 @@
local mods = {
control = "Control",
modkey = "Mod4",
shift = "Shift",
}
local groups = {
client = "client",
awesome = "awesome",
launcher = "launcher",
}
return {
mods = mods,
groups = groups,
}

View File

@ -1,10 +0,0 @@
local rc_configuration = {}
rc_configuration.applications = require "rc.configuration.applications"
rc_configuration.bindings = require "rc.configuration.bindings"
rc_configuration.menu = require "rc.configuration.menu"
rc_configuration.rules = require "rc.configuration.rules"
rc_configuration.prompt_commands = require "rc.configuration.prompt_commands"
rc_configuration.tag_layouts = require "rc.configuration.tag_layouts"
return rc_configuration

View File

@ -1,6 +0,0 @@
local configuration_menu = {}
configuration_menu.myawesomemenu = require "rc.configuration.menu.myawesomemenu"
configuration_menu.mymainmenu = require "rc.configuration.menu.mymainmenu"
return configuration_menu

View File

@ -1,31 +0,0 @@
local applications = require "rc.configuration.applications"
local hotkeys_popup = require "rc.ui.hotkeys_popup"
local capi = {
awesome = _G.awesome,
}
local myawesomemenu = {
{
"hotkeys",
function()
hotkeys_popup.show_help()
end,
},
{ "manual", applications.open_man "awesome" },
{ "edit config", applications.open_editor(capi.awesome.conffile) },
{
"restart",
function()
capi.awesome.restart()
end,
},
{
"quit",
function()
capi.awesome.quit()
end,
},
}
return myawesomemenu

View File

@ -1,11 +0,0 @@
local beautiful = require "beautiful"
local applications = require "rc.configuration.applications"
local myawesomemenu = require "rc.configuration.menu.myawesomemenu"
local mymainmenu = {
{ "awesome", myawesomemenu, beautiful.awesome_icon },
{ "open terminal", applications.terminal },
}
return mymainmenu

View File

@ -1,50 +0,0 @@
local atag = require "awful.tag"
local layout_suit = require "awful.layout.suit"
local commands = {}
commands["o"] = {
callback = function(parameters)
local tag_name = parameters[1] or "New-Tag"
atag.add(tag_name, {
layout = layout_suit.tile,
}):view_only()
end,
}
commands["O"] = {
callback = function(parameters)
local aspawn = require "awful.spawn"
local application = parameters[1]
local tag_name = parameters[2] or application
local t = atag.add(tag_name, {
layout = layout_suit.tile,
volatile = true,
})
t:view_only()
aspawn(application, { tag = t })
end,
}
commands["q"] = {
callback = function()
local ascreen = require "awful.screen"
local tags = ascreen.focused().selected_tags
for _, tag in ipairs(tags) do
tag.volatile = true
for _, client in ipairs(tag:clients()) do
client:kill()
end
tag:delete()
end
end,
}
return commands

View File

@ -1,7 +0,0 @@
local firefox_rule = {
id = "firefox",
rule = { class = "Firefox" },
properties = { screen = 1, tag = "2" },
}
return firefox_rule

View File

@ -1,28 +0,0 @@
local floating_rule = {
id = "floating",
rule_any = {
instance = { "copyq", "pinentry" },
class = {
"Arandr",
"Blueman-manager",
"Gpick",
"Kruler",
"Sxiv",
"Tor Browser",
"Wpa_gui",
"veromix",
"xtightvncviewer",
},
name = {
"Event Tester", -- xev.
},
role = {
"AlarmWindow", -- Thunderbird's calendar.
"ConfigManager", -- Thunderbird's about:config.
"pop-up", -- e.g. Google Chrome's (detached) Developer Tools.
},
},
properties = { floating = true },
}
return floating_rule

View File

@ -1,16 +0,0 @@
local aclient = require "awful.client"
local aplacement = require "awful.placement"
local ascreen = require "awful.screen"
local global_rule = {
id = "global",
rule = {},
properties = {
focus = aclient.focus.filter,
raise = true,
screen = ascreen.preferred,
placement = aplacement.no_overlap + aplacement.no_offscreen,
},
}
return global_rule

View File

@ -1,8 +0,0 @@
local client_rules = {}
client_rules.global = require "rc.configuration.rules.client.global"
client_rules.floating = require "rc.configuration.rules.client.floating"
client_rules.titlebar = require "rc.configuration.rules.client.titlebars"
-- client_rules.firefox = require 'rc.configuration.rules.client.firefox'
return client_rules

View File

@ -1,9 +0,0 @@
local titlebar_rule = {
id = "titlebars",
rule_any = {
type = { "normal", "dialog" },
},
properties = { titlebars_enabled = true },
}
return titlebar_rule

View File

@ -1,6 +0,0 @@
local rules = {}
rules.client = require "rc.configuration.rules.client"
rules.notification = require "rc.configuration.rules.notification"
return rules

View File

@ -1,11 +0,0 @@
local ascreen = require "awful.screen"
local global_rule = {
rule = {},
properties = {
screen = ascreen.preferred,
implicit_timeout = 5,
},
}
return global_rule

View File

@ -1,5 +0,0 @@
local notification_rules = {}
notification_rules.global = require "rc.configuration.rules.notification.global"
return notification_rules

View File

@ -1,19 +0,0 @@
local alayout = require "awful.layout"
local tag_layouts = {
alayout.suit.floating,
alayout.suit.tile,
alayout.suit.tile.left,
alayout.suit.tile.bottom,
alayout.suit.tile.top,
alayout.suit.fair,
alayout.suit.fair.horizontal,
alayout.suit.spiral,
alayout.suit.spiral.dwindle,
alayout.suit.max,
alayout.suit.max.fullscreen,
alayout.suit.magnifier,
alayout.suit.corner.nw,
}
return tag_layouts

153
init.lua
View File

@ -1,153 +0,0 @@
-- awesome_mode: api-level=4:screen=on
if os.getenv "LOCAL_LUA_DEBUGGER_VSCODE" == "1" then
require("lldebugger").start()
end
-- If LuaRocks is installed, make sure that packages installed through it are
-- found (e.g. lgi). If LuaRocks is not installed, do nothing.
pcall(require, "luarocks.loader")
local gtimer = require "gears.timer"
local legacy = require "awesome-legacy"
local naughty = require "naughty"
local ruled = require "ruled"
local slot = require "awesome-slot"
-- Load global awesome components from the C API
local capi = {
client = _G.client,
screen = _G.screen,
tag = _G.tag,
}
-- Beautiful needs to be initialized as soon as possible to make theme
-- variables available to the configuration module.
legacy.beautiful {
base = "default",
theme = require "rc.theme",
}
legacy.manage_error()
legacy.autofocus()
legacy.sloppy_focus()
local configuration = require "rc.configuration"
local my_slots = require "rc.slots"
-- This needs to be run after awesome has completed C API initialization and
-- the `root` object is available.
gtimer.delayed_call(function()
legacy.global_mouse_bindings(configuration.bindings.global_mousebindings)
legacy.global_keybindings(configuration.bindings.global_keybindings)
end)
-- luacheck: ignore unused variable load_wallpaper
local load_wallpaper = slot {
id = "LOAD_WALLPAPER",
connect = true,
target = capi.screen,
signal = "request::wallpaper",
slot = my_slots.wallpaper,
}
-- luacheck: ignore unused variable default_layout
local default_layout = slot {
id = "DEFAULT_LAYOUTS",
connect = true,
target = capi.tag,
signal = "request::default_layouts",
slot = slot.slots.tag.default_layouts,
slot_params = {
layouts = configuration.tag_layouts,
},
}
-- luacheck: ignore unused variable create_tag
local create_tag = slot {
id = "CREATE_TAGS",
connect = true,
target = capi.screen,
signal = "request::desktop_decoration",
slot = my_slots.create_tags,
}
-- luacheck: ignore unused variable desktop_decoration
local desktop_decoration = slot {
id = "DESKTOP_DECORATION",
connect = true,
target = capi.screen,
signal = "request::desktop_decoration",
slot = my_slots.build_desktop_decoration,
}
-- luacheck: ignore unused variable client_mousebinding
local client_mousebinding = slot {
id = "CLIENT_MOUSE_BINDINGS",
connect = true,
target = capi.client,
signal = "request::default_mousebindings",
slot = slot.slots.client.append_mousebindings,
slot_params = {
mousebindings = configuration.bindings.client_mousebindings,
},
}
-- luacheck: ignore unused variable client_keybinding
local client_keybinding = slot {
id = "CLIENT_KEY_BINDINGS",
connect = true,
target = capi.client,
signal = "request::default_keybindings",
slot = slot.slots.client.append_keybindings,
slot_params = {
keybindings = configuration.bindings.client_keybindings,
},
}
-- luacheck: ignore unused variable ruled_client
local ruled_client = slot {
id = "RULED_CLIENT",
connect = true,
target = ruled.client,
signal = "request::rules",
slot = slot.slots.ruled.append_client_rules,
slot_params = {
rules = configuration.rules.client,
},
}
-- luacheck: ignore unused variable client_titlebar
local client_titlebar = slot {
id = "CLIENT_TITLEBAR",
connect = true,
target = capi.client,
signal = "request::titlebars",
slot = my_slots.build_client_titlebars,
}
-- luacheck: ignore unused variable ruled_notification
local ruled_notification = slot {
id = "RULED_NOTIFICATION",
connect = true,
target = ruled.notification,
signal = "request::rules",
slot = slot.slots.ruled.append_notification_rules,
slot_params = {
rules = configuration.rules.notification,
},
}
-- luacheck: ignore unused variable naughty_display
local naughty_display = slot {
id = "NAUGHTY_DISPLAY",
connect = true,
target = naughty,
signal = "request::display",
slot = my_slots.naughty_display,
}
naughty.notify {
title = "Aire-One dots",
message = "You successfully ran the Aire-One default rc!",
}

45
scripts/run.sh Executable file
View File

@ -0,0 +1,45 @@
#!/usr/bin/env sh
xephyr=/usr/bin/Xephyr
awesome=/usr/bin/awesome
# TODO: configurable
confdir=./src
rcfile=./config/awesome/rc.lua
screen=1600x900
display=:1.0
startXephyr() {
$xephyr $display -ac -br -noreset -screen $screen >/dev/null 2>&1 &
}
runAwesome() {
DISPLAY=$display $awesome \
--config $rcfile \
--search $confdir
}
case $1 in
start)
startXephyr
luamon .
pkill Xephyr
;;
runAwesome)
runAwesome
;;
debug)
startXephyr
sleep 1 # wait for Xephyr to be ready
runAwesome
;;
try-current)
startXephyr
sleep 1 # wait for Xephyr to be ready
DISPLAY=$display $awesome
;;
*)
echo "Need command"
;;
esac

View File

@ -1,123 +0,0 @@
local abutton = require "awful.button"
local atitlebar = require "awful.titlebar"
local beautiful = require "beautiful"
local lalign = require "wibox.layout.align"
local lfixed = require "wibox.layout.fixed"
local lflex = require "wibox.layout.flex"
local naughty = require "naughty"
local slots = {}
function slots.wallpaper(screen)
local awallpaper = require "awful.wallpaper"
local beautiful = require "beautiful"
local gcolor = require "gears.color"
local gsurface = require "gears.surface"
local imagebox = require "wibox.widget.imagebox"
local cairo = require("lgi").cairo
local screen_geo = screen.geometry
local source = cairo.ImageSurface(
cairo.Format.RGB32,
screen_geo.width,
screen_geo.height
)
local cr = cairo.Context(source)
-- Load base image
local image_surface = gsurface.load_uncached(beautiful.wallpaper)
local w, h = gsurface.get_size(image_surface)
cr:scale(screen_geo.width / w, screen_geo.height / h)
cr:set_source_surface(image_surface, 0, 0)
cr:paint()
-- Add color layer
local color_pattern = gcolor.create_linear_pattern {
from = { 0, 0 },
to = { screen.width, screen.height },
stops = {
{ 0, "#26323840" },
},
}
cr:set_source(color_pattern)
cr:paint()
awallpaper {
screen = screen,
widget = {
image = source,
widget = imagebox,
},
}
end
function slots.create_tags(screen)
local atag = require "awful.tag"
local gtimer = require "gears.timer"
local home_layout = require "MyTagLayout.home_layout"
local first_tag = atag.add("home", {
screen = screen,
icon = beautiful.icon_hometag,
layout = home_layout,
master_width_factor = beautiful.hometag_master_width_factor,
})
gtimer.delayed_call(function()
local spawn = require "awful.spawn"
local apps = require "configuration.applications"
first_tag:view_only()
spawn(apps.open_terminal(), { screen = screen, tag = first_tag })
end)
end
function slots.build_desktop_decoration(screen)
local desktop_bar = require "rc.ui.desktop_decoration.bar"
desktop_bar(screen)
end
function slots.build_client_titlebars(client)
-- Mouse buttons bindings for the titlebar
local buttons = {
abutton({}, 1, function()
client:activate { context = "titlebar", action = "mouse_move" }
end),
abutton({}, 3, function()
client:activate { context = "titlebar", action = "mouse_resize" }
end),
}
-- Titlebar UI
atitlebar(client).widget = {
{ -- Left
atitlebar.widget.iconwidget(client),
buttons = buttons,
layout = lfixed.horizontal,
},
{ -- Middle
{ -- Title
align = "center",
widget = atitlebar.widget.titlewidget(client),
},
buttons = buttons,
layout = lflex.horizontal,
},
{ -- Right
atitlebar.widget.floatingbutton(client),
atitlebar.widget.maximizedbutton(client),
atitlebar.widget.stickybutton(client),
atitlebar.widget.ontopbutton(client),
atitlebar.widget.closebutton(client),
layout = lfixed.horizontal(),
},
layout = lalign.horizontal,
}
end
function slots.naughty_display(notification)
naughty.layout.box { notification = notification }
end
return slots

5
spec/default_spec.lua Normal file
View File

@ -0,0 +1,5 @@
describe("default", function()
it("should work", function()
assert.are.equal(1, 1)
end)
end)

View File

@ -0,0 +1,59 @@
# My Tag List Widget
The standard Awesome wm API already contains a Tag List Widget implementation. However, this implementation doesn't match my needs, so here is my attempt to create my own Tag List Widget.
It is a personnal project.
# Mockup
Here are some references I'm writing to define what I think my widget should looks like. This section will introduice a mockup of the widget in a taskbar.
This mockup concept have its implementation example in the script `mockup.lua`.
## The taskbar
The widget is designed to be used in a taskbar. Here, I'll only briefly talk about the bar, keep in mind inspiration for this come from *literally **any*** desktop environment and OS you can try.
Basically, the bar will look like this:
```
+-----------------------------+
| X X X X |
+-----------------------------+
```
Every X symbols is an item of the Tag List Widget.
## Items
*(I call them "items" because I didn't find a better word :s)*
In a clasical tasklist, an item is an icon of a runing application on your desktop. Here, I want to use the *Tag* system to introduice a new way to think about it.
Every tag will be shown as a single icon. This icon should show to the user a numerous informations about the tag itself and its associated clients. In one look, the user should be able to identify tags, which are selected, how may extra-clients there is, if a client is flaged as `urgent`.
Here is a discomposition of an item:
```
+---------+ +---------+ +---------+ +---------+
| | | | | *| | |
| O | | O | | O | | O |
| | | ------- | | | | + + + |
+---------+ +---------+ +---------+ +---------+
Simple Selected Urgent Clients
```
*The frame is used to delimite the size of the item. It is not aimed to be drawned.*
Legend:
* `O` : the icon on the center of the item
* `-----` : the bottom line, shown when the tag is selected
* `*` : the notification dot drawn when the tag is marked "urgent"
* `+` : some dots to show how many additionals clients are on the tag
# References
Most of the code is based on the awful standard library and the module `awful.widget.taglist.lua`.
Special thanks to u/EmpressNoodle [for the inspiration](https://www.reddit.com/r/unixporn/comments/a900p7/awesome_mechanical_love/) and the good example of implementation on github (https://github.com/elenapan/dotfiles/blob/master/config/awesome/noodle/icon_taglist.lua).

View File

@ -0,0 +1,139 @@
-----------------
-- MyTagListWidget - TagItem_widget
--
-- Definition of a TagItem_widget.
-- A TagItem_widget is the widget representing a tag in the TagList widget.
--
-- @author : Aire-One (Aire-One@github.com ; Aire-One@gitlab.com)
-- @copyright (C) 2019 Aire-One
-----------------
local aplacement = require "awful.placement"
local beautiful = require "beautiful"
local empty_widget = require("wibox.widget.base").empty_widget
local gshape = require "gears.shape"
local wibox = require "wibox"
--- Set the widget's image.
-- @tparam TagItem_widget self The widget itself.
-- @tparam Image image The image to use.
local set_image = function(self, image)
self.internal_role.icon_role.image = image
end
--- Toogle the widget's selected status.
-- Toogle the selected status will whether show the selected_line_role widget
-- or the additionals_clients_role widget.
-- @tparam TagItem_widget self The widget itself.
-- @tparam bool selected Is the widget selected?
local toogle_selected = function(self, selected)
self.internal_role.selected_line_role.visible = selected
self.internal_role.additionals_clients_role.visible = not selected
end
--- Toogle the widget's notification dot.
-- @tparam TagItem_widget self The widget itself.
-- @tparam bool show Show the notification dot?
local toogle_notification_dot = function(self, show)
self.internal_role.notification_dot_role.visible = show
end
--- Set the number of additionals clients shown in the widget.
-- @tparam TagItem_widget self The widget itself.
-- @tparam Number count The number of additionals clients.
-- (Should be >= 0 and not too high)
local set_additional_client_count = function(self, count)
count = count >= 0 and count or 0
while #self.internal_role.additionals_clients_role.children < count do
self.internal_role.additionals_clients_role:add(wibox.widget {
layout = wibox.container.background,
wibox.widget.base.empty_widget(),
bg = "#ECEFF1",
shape = gshape.circle,
forced_height = 3,
forced_width = 3,
})
end
while #self.internal_role.additionals_clients_role.children > count do
self.internal_role.additionals_clients_role:remove(1)
end
end
local tagItem_widget = {}
--- Create a new tag item widget instance.
-- @tparam Tag tag The associated tag.
-- @treturn TagItem_widget The created widget.
tagItem_widget.new = function(tag)
-- TODO: remove all constants - prefere use a `params` table to initialize them.
local w = wibox.widget {
id = "container_role",
layout = wibox.container.margin,
{
id = "internal_role",
layout = wibox.layout.manual,
{
id = "selected_line_role",
layout = wibox.container.margin,
forced_height = 2,
point = aplacement.maximize_horizontally + aplacement.bottom,
right = 2,
left = 2,
{
layout = wibox.container.background,
empty_widget(),
bg = "#ECEFF1",
shape = gshape.rounded_bar,
},
},
{
id = "icon_role",
widget = wibox.widget.imagebox,
resize = true,
image = tag.icon or beautiful.awesome_icon,
forced_height = 16,
forced_width = 16,
point = aplacement.centered,
},
{
id = "notification_dot_role",
layout = wibox.container.background,
visible = false,
wibox.widget.base.empty_widget(),
bg = "#F44336",
-- shape_border_width = 1,
-- shape_border_color = '#B71C1C',
shape = gshape.circle,
forced_height = 6,
forced_width = 6,
point = aplacement.top_right,
},
{
id = "additionals_clients_role",
layout = wibox.layout.flex.horizontal,
visible = false,
forced_height = 3,
spacing = 2,
point = aplacement.bottom,
},
},
}
-- it seems that setting the margins in the hierarchy doesn't work 🤷‍♂️
-- w:set_margins(0)
w.margins = 0
-- Save the tag as an internal property
w.tag = tag
w.set_image = set_image
w.toogle_selected = toogle_selected
w.toogle_notification_dot = toogle_notification_dot
w.set_additional_client_count = set_additional_client_count
return w
end
return tagItem_widget

View File

@ -0,0 +1,209 @@
-----------------
-- MyTagListWidget
--
-- My implementationof the tag list widget for the Awesome WM.
--
-- @author : Aire-One (Aire-One@github.com ; Aire-One@gitlab.com)
-- @copyright (C) 2019 Aire-One
-----------------
local awful = require "awful"
local beautiful = require "beautiful"
local client = client -- luacheck: ignore client
local gsurface = require "gears.surface"
local gtable = require "gears.table"
local wibox = require "wibox"
local TagItem_widget = require "MyTagListWidget.TagItem_widget"
--- Get the tag widget associated with a specific tag.
-- @tparam MyTagListWidget taglist The tag list widget to look up.
-- @tparam Tag tag The tag to look for.
-- @treturn[1] widget The tag widget (nil if none).
-- @treturn[1] number The position of the widget in the list.
-- @treturn[2] nil If the tag does not have its widget.
local get_tagwidget = function(taglist, tag)
for i, child in ipairs(taglist.children) do
if gtable.hasitem(child, tag) then
return child, i
end
end
return nil
end
--- Create a new tag icon widget.
-- @tparam Tag tag The targeted tag.
-- @treturn widget The created widget.
local create_tagwidget = function(tag)
local w = TagItem_widget.new(tag)
w:buttons(gtable.join(
-- Left click - Focus Tag
awful.button({}, 1, function()
tag:view_only()
end),
-- Right click - Show options
awful.button({}, 3, function()
-- TODO : design a menu
print("OPTION FOR " .. tag.name)
end)
))
return w
end
--- Manage a client update.
-- Do everything the widget should check when client is updated.
-- @tparam MyTagListWidget self The MyTagListWidget instance to update.
-- @tparam client client The client which trigger the update.
local client_update = function(self, client) end -- luacheck: ignore
--- Manage an icon update.
-- @tparam MyTagListWidget self The MyTagListWidget instance to update.
-- @tparam Tag tag The targeted tag.
local update_icon = function(self, tag)
-- TODO : this function shouldn't retrive the widget
local w = get_tagwidget(self, tag)
if not w then
return
end
-- by default, we should use the predefined tag icon
-- then master client icon
-- then default to awesome_icon
if tag.icon then
w:set_image(tag.icon)
elseif #tag:clients() > 0 then
local attach_icon = function()
w:set_image(gsurface(tag:clients()[1].icon))
end
-- first client should be masters
-- so usually, we should prioritize firsts clients
tag:clients()[1]:connect_signal("property::icon", attach_icon)
for i = 2, #tag:clients() do
tag:clients()[i]:disconnect_signal("property::icon", attach_icon)
end
else
w:set_image(beautiful.awesome_icon)
end
end
--- Manage the additionals clients count on a tagItem_widget.
-- @tparam MyTagListWidget self The MyTagListWidget instance to update.
-- @tparam Tag tag The targeted tag.
local update_additionalsclient = function(self, tag)
-- TODO : this function shouldn't retrive the widget
local w = get_tagwidget(self, tag)
if not w then
return
end
-- TODO : better additionals clients count computation
-- (detect important clients and not count them)
w:set_additional_client_count(#tag:clients() - tag.master_count)
end
--- Update the widget.
-- @tparam MyTagListWidget self The MyTagListWidget instance to update.
local update_widget = function(self)
for i, tag in ipairs(self.screen.tags) do
-- w is the TagItem_widget associated with the tag.
local w = get_tagwidget(self, tag)
-- If none exists, create it.
if not w then
w = create_tagwidget(tag)
self:insert(i, w) -- add the widget at its position in the layout
end
w:toogle_selected(tag.selected)
w:toogle_notification_dot(awful.tag.getproperty(tag, "urgent"))
update_icon(self, tag)
if not tag.selected then
update_additionalsclient(self, tag)
end
end
-- Remove deleted tags:
for i, wtag in ipairs(self.children) do
if wtag.tag.screen ~= self.screen then
self:remove(i)
end
end
end
local my_taglist_widget = {}
--- MyTagListWidget Constructor.
-- Build a new instance of MyTagListWidget.
-- @tparam table params Parameters to build the Widget.
-- @tparam screen params.screen Screen where the widget will be.
-- @treturn MyTagListWidget The new widget instance.
my_taglist_widget.new = function(params)
-- TODO : use more beautiful theming properties with params
local self = wibox.widget {
layout = wibox.layout.flex.horizontal,
screen = params.screen,
spacing = 2,
max_widget_size = 20,
}
-- Add publics methods
self.force_update_widget = update_widget
-- Add buttons events
self:buttons(gtable.join(
-- Scroll - Cycle through tags
awful.button({}, 4, function()
awful.tag.viewprev()
end),
awful.button({}, 5, function()
awful.tag.viewnext()
end)
))
-- Attach signals to update the widget
-- TODO : specialize signal events (ie not always call UPDATE_EVERYTHING when a shorter function can do the job)
client.connect_signal("manage", function()
self:force_update_widget()
end)
client.connect_signal("unmanage", function()
self:force_update_widget()
end)
client.connect_signal("tagged", function()
self:force_update_widget()
end)
client.connect_signal("untagged", function()
self:force_update_widget()
end)
client.connect_signal("screen", function()
self:force_update_widget()
end)
awful.tag.attached_connect_signal(self.screen, "property::selected", function()
self:force_update_widget()
end)
awful.tag.attached_connect_signal(self.screen, "property::hide", function()
self:force_update_widget()
end)
awful.tag.attached_connect_signal(self.screen, "property::activated", function()
self:force_update_widget()
end)
awful.tag.attached_connect_signal(self.screen, "property::screen", function()
self:force_update_widget()
end)
awful.tag.attached_connect_signal(self.screen, "property::index", function()
self:force_update_widget()
end)
awful.tag.attached_connect_signal(self.screen, "property::urgent", function()
self:force_update_widget()
end)
-- Build the widget with an update
update_widget(self)
return self
end
return my_taglist_widget

View File

@ -0,0 +1,281 @@
-----------------
-- MyTagListWidget mockup
--
-- This file define a mockup of the tag list widget in a taskbar
-- as I wish to create my own.
--
-- This mockup will create a fake taskbar with a fake Tag List Widget to help
-- me for the conceptualisation of the design for my Tag List Widget.
--
-- <h1>The taskbar</h1>
-- Basically, the bar will look like this:
--
-- +---------------------+
-- | X X X X |
-- +---------------------+
--
-- Every X symbols is an item of the Tag List Widget.
--
-- <h1>Items</h1>
-- An item represente a Tag and is discomposable like this:
--
-- +---------+ +---------+ +---------+ +---------+
-- | | | | | *| | |
-- | O | | O | | O | | O |
-- | | | ------- | | | | + + + |
-- +---------+ +---------+ +---------+ +---------+
-- Simple Selected Urgent Clients
--
-- The frame is used to delimite the size of the item.
-- It is not aimed to be drawned.
--
-- * O : the icon on the center of the item
-- * ----- : the bottom line, shown when the tag is selected
-- * \* : a notification dot drawn when the tag is marked "urgent"
-- * \+ : some dots to show how many additionals clients are on the tag
--
-- @author : Aire-One (Aire-One@github.com ; Aire-One@gitlab.com)
-- @copyright (C) 2019 Aire-One
-----------------
-- require user rc.lua as a base config
-- user rc.lua file should be on a place loaded by awesomewm
require "rc"
local awful = require "awful"
local gears = require "gears"
local wibox = require "wibox"
-- Configs
local bar_height = 20 -- used by the wibox embedding the widget
local bar_padding = 0 -- used by the wibox embedding the widget
local item_size = 20 -- size of a tag icon placeholder
local icon_size = 16 -- size of the icon itself
local selected_line_height = 3 -- size of the bar below the icon
local notification_dot_size = 6 -- size of the notification dot (request::urgent)
local screen = awful.screen.focused()
-- the taskbar is symbolized by this wibox
screen.bar = wibox {
type = "dock",
ontop = true,
visible = true,
x = 50,
y = 200,
width = 400,
height = bar_height,
}
screen.bar:setup {
-- The bar is a container.margin
layout = wibox.container.margin,
top = bar_padding / 2,
bottom = bar_padding / 2,
right = bar_padding,
left = bar_padding,
{
-- The Tag List widget
layout = wibox.layout.flex.horizontal,
spacing = 2,
max_widget_size = item_size, -- each widget will be a rectangular box
-- Every Tag is represented by this widget
{
-- first a container.margin
layout = wibox.container.margin,
top = 0,
bottom = 0,
right = 0,
left = 0,
{
-- The internal widget design
layout = wibox.layout.manual,
{
-- simple icon
-- use a predefined icon depending on the tag name
-- use a client (master client?) icon
-- use the tag name as a string (widget.textbox) ???
widget = wibox.widget.imagebox,
resize = true,
image = "/usr/share/icons/Adwaita/16x16/places/user-home-symbolic.symbolic.png",
forced_height = icon_size,
forced_width = icon_size,
point = awful.placement.centered,
},
},
},
{
-- first a container.margin
layout = wibox.container.margin,
top = 0,
bottom = 0,
right = 0,
left = 0,
{
-- The internal widget design
layout = wibox.layout.manual,
{
-- underline (request::selected)
-- should be placed below the tag icon
layout = wibox.container.background,
wibox.widget.base.empty_widget(),
bg = "#eeeeee",
shape = gears.shape.rounded_bar,
forced_height = selected_line_height,
point = awful.placement.maximize_horizontally + awful.placement.bottom,
},
{
widget = wibox.widget.imagebox,
resize = true,
image = "/usr/share/icons/hicolor/16x16/apps/atom.png",
forced_height = icon_size,
forced_width = icon_size,
point = awful.placement.centered,
},
},
},
{
-- first a container.margin
layout = wibox.container.margin,
top = 0,
bottom = 0,
right = 0,
left = 0,
{
-- The internal widget design
layout = wibox.layout.manual,
{
widget = wibox.widget.imagebox,
resize = true,
image = "/usr/share/icons/hicolor/16x16/apps/thunderbird.png",
forced_height = icon_size,
forced_width = icon_size,
point = awful.placement.centered,
},
{
-- Notification dot (request::urgent)
-- should be placed on the top of the icon
layout = wibox.container.background,
wibox.widget.base.empty_widget(),
bg = "#ff0000",
shape_border_width = 2,
shape_border_color = "#8b0000",
shape = gears.shape.circle,
forced_height = notification_dot_size,
forced_width = notification_dot_size,
point = awful.placement.top_right,
},
},
},
{
-- first a container.margin
layout = wibox.container.margin,
top = 0,
bottom = 0,
right = 0,
left = 0,
{
-- The internal widget design
layout = wibox.layout.manual,
{
-- simple icon
-- use a predefined icon depending on the tag name
-- use a client (master client?) icon
-- use the tag name as a string (widget.textbox) ???
widget = wibox.widget.imagebox,
resize = true,
image = "/usr/share/icons/Adwaita/16x16/apps/utilities-terminal-symbolic.symbolic.png",
forced_height = icon_size,
forced_width = icon_size,
point = awful.placement.centered,
},
{
-- number of clients on the tag
layout = wibox.layout.flex.horizontal,
{
layout = wibox.container.background,
wibox.widget.base.empty_widget(),
bg = "#eeeeee",
shape = gears.shape.circle,
forced_height = 3,
forced_width = 3,
},
{
layout = wibox.container.background,
wibox.widget.base.empty_widget(),
bg = "#eeeeee",
shape = gears.shape.circle,
forced_height = 3,
forced_width = 3,
},
{
layout = wibox.container.background,
wibox.widget.base.empty_widget(),
bg = "#eeeeee",
shape = gears.shape.circle,
forced_height = 3,
forced_width = 3,
},
{
layout = wibox.container.background,
wibox.widget.base.empty_widget(),
bg = "#eeeeee",
shape = gears.shape.circle,
forced_height = 3,
forced_width = 3,
},
forced_height = selected_line_height,
spacing = 2,
point = awful.placement.bottom,
},
},
},
{
-- first a container.margin
layout = wibox.container.margin,
top = 0,
bottom = 0,
right = 0,
left = 0,
{
-- The internal widget design
layout = wibox.layout.manual,
{
-- simple icon
-- use a predefined icon depending on the tag name
-- use a client (master client?) icon
-- use the tag name as a string (widget.textbox) ???
widget = wibox.widget.imagebox,
resize = true,
image = "/usr/share/icons/Adwaita/16x16/apps/utilities-terminal-symbolic.symbolic.png",
forced_height = icon_size,
forced_width = icon_size,
point = awful.placement.centered,
},
{
-- number of clients on the tag
layout = wibox.layout.flex.horizontal,
{
layout = wibox.container.background,
wibox.widget.base.empty_widget(),
bg = "#eeeeee",
shape = gears.shape.circle,
forced_height = 3,
forced_width = 3,
},
{
layout = wibox.container.background,
wibox.widget.base.empty_widget(),
bg = "#eeeeee",
shape = gears.shape.circle,
forced_height = 3,
forced_width = 3,
},
forced_height = selected_line_height,
spacing = 2,
point = awful.placement.bottom,
},
},
},
},
}

View File

@ -0,0 +1,34 @@
-----------------
-- tests/widget-testrc.lua
--
-- Author : Aire-One (Aire-One@github.com ; Aire-One@gitlab.com)
-- Copyright (C) 2018 Aire-One
-----------------
-- require user rc.lua as a base config
-- user rc.lua file should be on a place loaded by awesomewm
require "rc"
-- Code to test the widget
local awful = require "awful"
local wibox = require "wibox"
local MyTagListWidget = require "MyTagListWidget"
-- build the widget on the current screen
local widget = MyTagListWidget.new { screen = awful.screen.focused() }
-- wibox to host the MyTagListWidget instance
awful.screen.w = wibox {
type = "dock",
ontop = true,
visible = true,
opacity = 1,
width = 200,
height = 30,
}
awful.screen.w:setup {
layout = wibox.container.background,
widget,
}

View File

@ -0,0 +1,7 @@
-----
-- autofocus module
--
-- TODO: remove this module as it's now deprecated.
-----
return require "awful.autofocus"

View File

@ -0,0 +1,66 @@
-----
-- legacy module
--
-- This modules aims to hold all the legacy features from rc.lua
-----
local awesome_legacy = {}
function awesome_legacy.autofocus()
return require "awesome-legacy.autofocus"
end
function awesome_legacy.manage_error()
return require "awesome-legacy.manage_error"
end
function awesome_legacy.sloppy_focus()
return require "awesome-legacy.sloppy_focus"
end
function awesome_legacy.global_mouse_bindings(mousebindings)
local amouse = require "awful.mouse"
amouse.append_global_mousebindings(mousebindings)
end
function awesome_legacy.global_keybindings(keybindings)
local akeyboard = require "awful.keyboard"
akeyboard.append_global_keybindings(keybindings)
end
function awesome_legacy.beautiful(args)
local os = os
local dofile = dofile
local beautiful = require "beautiful"
local gfs = require "gears.filesystem"
local gstring = require "gears.string"
local gtable = require "gears.table"
local protected_call = require "gears.protected_call"
local default_theme = {}
if args.base then
if type(args.base) == "string" then
-- Replace the '~' by the fullpath to the home directory.
local theme_path = args.base:gsub("^~/", os.getenv "HOME" .. "/")
-- Complete the path for standard themes
if not gstring.startswith(theme_path, "/") then
theme_path = gfs.get_themes_dir() .. args.base .. "/theme.lua"
end
-- Execute the file to get the theme table
default_theme = protected_call(dofile, theme_path)
elseif type(args.base) == "table" then
default_theme = args.base
end
end
local theme = gtable.crush(default_theme, args.theme)
beautiful.init(theme)
end
return awesome_legacy

View File

@ -0,0 +1,15 @@
-----
-- Error module
-----
local naughty = require "naughty"
-- Check if awesome encountered an error during startup and fell back to
-- another config (This code will only ever execute for the fallback config)
naughty.connect_signal("request::display_error", function(message, startup)
naughty.notification {
urgency = "critical",
title = "Oops, an error happened" .. (startup and " during startup!" or "!"),
message = message,
}
end)

View File

@ -0,0 +1,15 @@
-----
-- Sloppy focus module
-----
local capi = {
client = _G.client,
}
-- Enable sloppy focus, so that focus follows mouse.
capi.client.connect_signal("mouse::enter", function(client)
client:activate {
context = "mouse_enter",
raise = false,
}
end)

View File

@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

1
src/awesome-wm-nice/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
color_rules

View File

@ -0,0 +1,33 @@
-- Only allow symbols available in all Lua versions
std = "min"
-- Get rid of "unused argument self"-warnings
self = false
-- Global objects defined by the C code
read_globals = {
"awesome",
"button",
"dbus",
"drawable",
"drawin",
"key",
"keygrabber",
"mousegrabber",
"selection",
"tag",
"window",
"table.unpack",
"math.atan2",
}
-- screen may not be read-only, because newer luacheck versions complain about
-- screen[1].tags[1].selected = true.
-- The same happens with the following code:
-- local tags = mouse.screen.tags
-- tags[7].index = 4
-- client may not be read-only due to client.focus.
globals = { "screen", "mouse", "root", "client" }
-- Enable cache (uses .luacheckcache relative to this rc file).
cache = true

View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2020 mut-ex
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,257 @@
# :thumbsup:nice
<p align="center">
If you would like to show your appreciation for this project,<br>please consider a donation :)<br><br>
<a href="https://www.paypal.com/donate/?business=Y4Y75KP2JBNJW&currency_code=USD">
<img src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" alt="PayPal donation link"/></a>
<p>
**N.B. This branch is for Awesome v4.3 git. [You can find the branch for Awesome v4.3 stable here](https://github.com/mut-ex/awesome-wm-nice/tree/awesome-4v3-stable)**
nice is an easy to use, highly configurable extension for **[Awesome WM](https://awesomewm.org/)** that adds beautiful window decorations (and extra functionality!) to clients. It...
* ...adds a **subtle 3D look**, and soft, **rounded anti-aliased, corners** to windows
* ...picks the window **decoration color based on the client content for a seamless look** , and adjusts the window title text color accordingly
* ...**auto-generates titlebar buttons** (and their states) for you based on the colors your pick *or* you can let it pick the colors for you!
* ...allows you to customize which titlebar buttons to include, their order, and their layout
* ...adds the **ability to maximize/unmaximize** floating windows by **double clicking the titlebar**, and of course, **moving them by clicking and holding**
* ...adds the ability to **"roll up"** and **"roll down"** the client window like a **window shade**! Scroll up over the titlebar to **instantly hide the window contents but keep the title bar** right where it is. And then either scroll down or click the titlebar to make the window contents visible again!
![Preview](https://raw.githubusercontent.com/mut-ex/awesome-wm-nice/master/preview.png)
## Getting Started
### Prerequisites
* You need **[Awesome WM](https://awesomewm.org/)** with a working basic configuration. **This branch is for Awesome v4.3 git. [You can find the branch for Awesome v4.3 stable here](https://github.com/mut-ex/awesome-wm-nice/tree/awesome-4v3-stable)**
* You also need **[picom](https://github.com/yshui/picom)**. Make sure you have `shadow-ignore-shaped = false` in your configuration otherwise picom will not draw shadows. My recommended shadow settings are given below:
```
shadow = true;
shadow-radius = 40;
shadow-opacity = .55;
shadow-offset-x = -40;
shadow-offset-y = -20;
shadow-exclude = [
"_NET_WM_WINDOW_TYPE:a = '_NET_WM_WINDOW_TYPE_NOTIFICATION'",
"_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'",
"_GTK_FRAME_EXTENTS@:c"
];
shadow-ignore-shaped = false
```
* For **GTK** applications add the following line to **~/.config/gtk-3.0/settings.ini** under the **[Settings]** section to hide client-side window control buttons:
```
gtk-decoration-layout=menu:
```
* Within you Awesome configuration, make sure that you do not already have code in place that request default titlebars for clients. Something like this:
```lua
client.connect_signal("request::titlebars", function(c) ... end) -- Remove this
```
* Additionally, nice only adds window decorations to clients that have the `titlebars_enabled` property set to true. So configure your client rules accordingly.
### Installation
The easiest and quickest way to get started is by cloning this repository to your awesome configuration directory
```shell
$ cd ~/.config/awesome
$ git clone https://github.com/mut-ex/awesome-wm-nice.git nice
```
## Usage
To use nice, you first need to load the module. To do that, put the following line right after `beautiful.init(...)`
```lua
local nice = require("nice")
nice()
```
If you are fine using the default configuration, you are all done!
nice will automatically detect and change the window decoration color to match the client. However...
* To pick the window decoration color yourself, right-click the titlebar and select **'Manually Pick Color'**
* To update the window decoration colors, right-click on the titlebar and select **'Redo Window Decorations'**
* Scroll-up with your mouse over the titlebar to "roll up" the window shade. Scroll-down over the titlebar, or left-click to "roll down" the window shade
* nice saves its color rules in the **color_rules** file within the module directory. If you wish you can manually edit it, or delete the file if you want to start again.
## Configuration
You can override the defaults by passing your own configuration. For example
```lua
local nice = require("nice")
nice {
titlebar_color = "#00ff00",
-- You only need to pass the parameter you are changing
context_menu_theme = {
width = 300,
},
-- Swap the designated buttons for resizing, and opening the context menu
mb_resize = nice.MB_MIDDLE,
mb_contextmenu = nice.MB_RIGHT,
}
```
Below you will find further details explaining the configuration parameters for nice.
| Parameter | Type | Description | Default |
| --------------------- | :--: | ----------- | ------------------- |
| `titlebar_height` | integer | The height of the titlebar | `38` |
| `titlebar_radius` | integer | The radius of the top left and top right corners of the titlebar. Should be `>= 3` and `<= titlebar_height` | `9` |
| `titlebar_color` | string | The default color of the titlebar and window decorations. Should be a hex color string | `"#1e1e24"` |
| `titlebar_padding_left` | integer | The padding on the left side of the titlebar | `0` |
| `titlebar_padding_right` | integer | The padding on the right side of the titlebar | `0` |
| `titlebar_font` | string | The font and font size for text within the titlebar. See the default value for an example of the format | `"Sans 11"` |
| `win_shade_enabled` | boolean | Whether the window shade feature should be enabled | `true` |
| `no_titlebar_maximized` | boolean | Whether the titlebar should be hidden for maximized windows | `false` |
| `mb_move` | integer or named constant | Mouse button to move a window. | `nice.MB_LEFT` |
| `mb_contextmenu` | integer or named constant | Mouse button to open the nice context menu | `nice.MB_MIDDLE` |
| `mb_resize` | integer or named constant | Mouse button to resize a window | `nice.MB_RIGHT` |
| `mb_win_shade_rollup` | integer or named constant | Mouse button to roll up/hide window contents | `nice.MB_SCROLL_UP` |
| `mb_win_shade_rolldown` | integer or named constant | Mouse button to roll down/show window contents | `nice.MB_SCROLL_DOWN` |
| `button_size` | integer | The size (diameter) of the titlebar buttons | 16 |
| `button_margin_horizontal` | integer | The horizontal margin around each titlebar button. `button_margin_left` and `button_margin_right`can override this parameter. | 5 |
| `button_margin_vertical` | integer | The vertical margin above and below each titlebar button. `button_margin_top` and `button_margin_bottom` can override this parameter. | nil |
| `button_margin_top` | integer | The margin above each titlebar button | 2 |
| `button_margin_bottom` | integer | The margin below each titlebar button | nil |
| `button_margin_left` | integer | The margin to the left of each titlebar button | 0 |
| `button_margin_right` | integer | The margin to the right of each titlebar button | 0 |
| `tooltips_enabled` | boolean | If tooltip hints should be shown when the mouse cursor is hovered over a titlebar button | nil |
| `close_color` | string | The base color for the close button | "#ee4266" |
| `minimize_color` | string | The base color for the minimize button | "#ffb400" |
| `maximize_color` | string | The base color for the maximize button | "#4cbb17" |
| `floating_color` | string | The base color for the floating mode toggle button | "#f6a2ed" |
| `ontop_color` | string | The base color for the on top mode toggle button | "#f6a2ed" |
| `sticky_color` | string | The base color for the sticky mode toggle button | "#f6a2ed" |
In addition to the above mentioned parameters, there some more parameters that require a little more explanation:
### titlebar_items
`titlebar_items` — Specifies the titlebar items to include
* It should be a table with the following keys:
* `left` — Specifies the item(s) to place on the left side of the titlebar
* `middle` — Specifies the item(s) to place in the middle of the titlebar
* `right` — Specifies the items(s) to place on the right side of the titlebar
* Multiple items should be passed as an array of identifiers. For a single item simply passing the identifier is sufficient
* Valid titlebar item identifiers are:
* `"close"`
* `"minimize"`
* `"maximize"`
* `"floating"`
* `"ontop"`
* `"sticky"`
* `"title"`
* Default value for `titlebar_items` is:
```lua
titlebar_items = {
left = {"close", "minimize", "maximize"},
middle = "title",
right = {"sticky", "ontop", "floating"},
}
```
### context_menu_theme
`context_menu_theme` — Specifies theming parameters for the context (default right-click) menu
* It should be a table with the following keys:
* `bg_focus` — Background color of focused menu item
* `bg_normal` — Background color of not-focused menu items
* `border_color` — Color of the border around the entire menu
* `border_width` — Width of the border around the entire menu
* `fg_focus` — Foreground color of focused menu item
* `fg_normal` — Foreground color of not-focused menu items
* `font` — Font used for menu text
* `height` — Height of each menu list item
* `width` — Width of the menu
* Default value for `context_menu_theme` is:
```lua
context_menu_theme = {
bg_focus = "#aed9e0",
bg_normal = "#5e6472",
border_color = "#00000000",
border_width = 0,
fg_focus = "#242424",
fg_normal = "#fefefa",
font = "Sans 11",
height = 27.5,
width = 250,
}
```
### tooltip_messages
`tooltip_messages` — Specifies the hints that are shown when the mouse cursor is hovered over a titlebar button
* It should be a table with the following keys:
* `close` — Text shown when hovering over the close button
* `minimize` — Text shown when hovering over the minimize button
* `maximize_active` — Text shown when hovering over the maximize button when the window is maximized
* `maximize_inactive` — Text shown when hovering over the maximize button when the window is not maximized
* `floating_active` — Text shown when hovering over the floating button when the window is floating
* `floating_inactive` — Text shown when hovering over the floating button when the window is tiled
* `ontop_active` — Text shown when hovering over the ontop button when the window is set to be above other windows
* `ontop_inactive` — Text shown when hovering over the ontop button when the window is not set to be above other windows
* `sticky_active` — Text shown when hovering over the sticky button when the window is set to be available on all tags
* `sticky_inactive` — Text shown when hovering over the sticky button when the window is not to be available on all tags
The default value for `tooltip_messages` is:
```lua
tooltip_messages = {
close = "close",
minimize = "minimize",
maximize_active = "unmaximize",
maximize_inactive = "maximize",
floating_active = "enable tiling mode",
floating_inactive = "enable floating mode",
ontop_active = "don't keep above other windows",
ontop_inactive = "keep above other windows",
sticky_active = "disable sticky mode",
sticky_inactive = "enable sticky mode",
}
```
## Using
nice will automatically detect and change the window decoration color to match the client. However...
* If nice doesn't pick the right color or you want to specify it yourself, right-click the titlebar and select 'Manually Pick Color'
* If the client theme changes (for example if you change your terminal emulator colors), to update the window decoration colors, right-click on the titlebar and select 'Redo Window Decorations'
* Scroll-up with your mouse over the titlebar to "roll-up" the window shade. Scroll-down over the titlebar, or left-click to "roll-down" the window shade
* nice saves its color rules in the color_rules file within the module directory. If you wish you can manually edit it, or delete the file if you want to start again.
## Issues
If you face any bugs or issues (or have a feature request), please feel free to open an issue on here
## License
[![License](http://img.shields.io/:license-mit-blue.svg)](http://doge.mit-license.org)

View File

@ -0,0 +1,164 @@
-- => Colors
-- Provides utility functions for handling colors
-- ============================================================
local math = math
local floor = math.floor
local max = math.max
local min = math.min
local random = math.random
local gcolor = require "gears.color"
local parse_color = gcolor.parse_color
-- Returns a value that is clipped to interval edges if it falls outside the interval
local function clip(num, min_num, max_num)
return max(min(num, max_num), min_num)
end
-- Converts the given hex color to normalized rgba
local function hex2rgb(color)
-- color = color:gsub("#", "")
-- local strlen = color:len()
-- if strlen == 6 then
-- return tonumber("0x" .. color:sub(1, 2)) / 255,
-- tonumber("0x" .. color:sub(3, 4)) / 255,
-- tonumber("0x" .. color:sub(5, 6)) / 255, 1
-- end
-- if strlen == 8 then
-- return tonumber("0x" .. color:sub(1, 2)) / 255,
-- tonumber("0x" .. color:sub(3, 4)) / 255,
-- tonumber("0x" .. color:sub(5, 6)) / 255,
-- tonumber("0x" .. color:sub(7, 8)) / 255
-- end
return parse_color(color)
end
-- Converts the given hex color to hsv
local function hex2hsv(color)
local r, g, b = hex2rgb(color)
local C_max = max(r, g, b)
local C_min = min(r, g, b)
local delta = C_max - C_min
local H, S, V
if delta == 0 then
H = 0
elseif C_max == r then
H = 60 * (((g - b) / delta) % 6)
elseif C_max == g then
H = 60 * (((b - r) / delta) + 2)
elseif C_max == b then
H = 60 * (((r - g) / delta) + 4)
end
if C_max == 0 then
S = 0
else
S = delta / C_max
end
V = C_max
return H, S * 100, V * 100
end
-- Converts the given hsv color to hex
local function hsv2hex(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 - math.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
-- Calculates the relative luminance of the given color
local function relative_luminance(color)
local r, g, b = hex2rgb(color)
local function from_sRGB(u)
return u <= 0.0031308 and 25 * u / 323 or ((200 * u + 11) / 211) ^ (12 / 5)
end
return 0.2126 * from_sRGB(r) + 0.7152 * from_sRGB(g) + 0.0722 * from_sRGB(b)
end
-- Calculates the contrast ratio between the two given colors
local function contrast_ratio(fg, bg)
return (relative_luminance(fg) + 0.05) / (relative_luminance(bg) + 0.05)
end
-- Returns true if the contrast between the two given colors is suitable
local function is_contrast_acceptable(fg, bg)
return contrast_ratio(fg, bg) >= 7 and true
end
-- Returns a bright-ish, saturated-ish, color of random hue
local function rand_hex(lb_angle, ub_angle)
return hsv2hex(random(lb_angle or 0, ub_angle or 360), 70, 90)
end
-- Rotates the hue of the given hex color by the specified angle (in degrees)
local function rotate_hue(color, angle)
local H, S, V = hex2hsv(color)
angle = clip(angle or 0, 0, 360)
H = (H + angle) % 360
return hsv2hex(H, S, V)
end
-- Lightens a given hex color by the specified amount
local function lighten(color, amount)
local r, g, b
r, g, b = hex2rgb(color)
r = 255 * r
g = 255 * g
b = 255 * b
r = r + floor(2.55 * amount)
g = g + floor(2.55 * amount)
b = b + floor(2.55 * amount)
r = r > 255 and 255 or r
g = g > 255 and 255 or g
b = b > 255 and 255 or b
return ("#%02x%02x%02x"):format(r, g, b)
end
-- Darkens a given hex color by the specified amount
local function darken(color, amount)
local r, g, b
r, g, b = hex2rgb(color)
r = 255 * r
g = 255 * g
b = 255 * b
r = max(0, r - floor(r * (amount / 100)))
g = max(0, g - floor(g * (amount / 100)))
b = max(0, b - floor(b * (amount / 100)))
return ("#%02x%02x%02x"):format(r, g, b)
end
return {
clip = clip,
hex2rgb = hex2rgb,
hex2hsv = hex2hsv,
hsv2hex = hsv2hex,
relative_luminance = relative_luminance,
contrast_ratio = contrast_ratio,
is_contrast_acceptable = is_contrast_acceptable,
rand_hex = rand_hex,
rotate_hue = rotate_hue,
lighten = lighten,
darken = darken,
}

View File

@ -0,0 +1,22 @@
project = "awesome-wm-nice"
title = "An Awesome WM module that add MacOS-like window decorations"
all = false
dir = "doc"
format = "markdown"
pretty = "lua"
prettify_files = true
backtick_references = true
merge = true
use_markdown_titles = true
wrap = true
sort_modules = true
not_luadoc = true
file = {
"init.lua",
}
-- Define some new ldoc tags from the AwesomeWM doc
new_type("constructorfct", "Constructor", false, "Parameters")
new_type("staticfct", "Static functions", false, "Parameters")

View File

@ -0,0 +1,95 @@
local abutton = require "awful.button"
local gtable = require "gears.table"
local config = { mt = {}, _private = {} }
-- Titlebar
config._private.titlebar_height = 38
config._private.titlebar_radius = 9
config._private.titlebar_color = "#1E1E24"
config._private.titlebar_margin_left = 0
config._private.titlebar_margin_right = 0
config._private.titlebar_font = "Sans 11"
config._private.titlebar_items = {
left = { "close", "minimize", "maximize" },
middle = "title",
right = { "sticky", "ontop", "floating" },
}
config._private.context_menu_theme = {
bg_focus = "#aed9e0",
bg_normal = "#5e6472",
border_color = "#00000000",
border_width = 0,
fg_focus = "#242424",
fg_normal = "#fefefa",
font = "Sans 11",
height = 27.5,
width = 250,
}
config._private.win_shade_enabled = true
config._private.no_titlebar_maximized = false
config._private.mb_move = abutton.names.LEFT
config._private.mb_contextmenu = abutton.names.MIDDLE
config._private.mb_resize = abutton.names.RIGHT
config._private.mb_win_shade_rollup = abutton.names.SCROLL_UP
config._private.mb_win_shade_rolldown = abutton.names.SCROLL_DOWN
-- Titlebar Items
config._private.button_size = 16
config._private.button_margin_horizontal = 5
-- _private.button_margin_vertical
config._private.button_margin_top = 2
-- _private.button_margin_bottom = 0
-- _private.button_margin_left = 0
-- _private.button_margin_right = 0
config._private.tooltips_enabled = true
config._private.tooltip_messages = {
close = "close",
minimize = "minimize",
maximize_active = "unmaximize",
maximize_inactive = "maximize",
floating_active = "enable tiling mode",
floating_inactive = "enable floating mode",
ontop_active = "don't keep above other windows",
ontop_inactive = "keep above other windows",
sticky_active = "disable sticky mode",
sticky_inactive = "enable sticky mode",
}
config._private.close_color = "#ee4266"
config._private.minimize_color = "#ffb400"
config._private.maximize_color = "#4CBB17"
config._private.floating_color = "#f6a2ed"
config._private.ontop_color = "#f6a2ed"
config._private.sticky_color = "#f6a2ed"
function config.init(args)
-- properties that are table
local table_args = {
titlebar_items = true,
context_menu_theme = true,
tooltip_messages = true,
}
-- Apply changes to our _private properties
if args then
for prop, value in pairs(args) do
if table_args[prop] == true then
gtable.crush(config._private[prop], value)
elseif prop == "titlebar_radius" then
config._private[prop] = math.max(3, value)
else
config._private[prop] = value
end
end
end
end
function config.mt:__index(k)
return config._private[k]
end
function config.mt:__newindex(k, v)
config._private[k] = v
end
return setmetatable(config, config.mt)

View File

@ -0,0 +1,472 @@
--[[
Author: mu-tex
License: MIT
Repository: https://github.com/mut-ex/awesome-wm-nice
]]
local awful = require "awful"
local abutton = awful.button
local wibox = require "wibox"
-- Widgets
local imagebox = wibox.widget.imagebox
-- Layouts
local wlayout = wibox.layout
local wlayout_align_horizontal = wlayout.align.horizontal
local wlayout_flex_horizontal = wlayout.flex.horizontal
-- Containers
local wcontainer = wibox.container
local wcontainer_background = wcontainer.background
local wcontainer_margin = wcontainer.margin
-- Gears
local gtimer = require "gears.timer"
local gtimer_weak_start_new = gtimer.weak_start_new
local gtable = require "gears.table"
-- ------------------------------------------------------------
-- => Math + standard Lua methods
-- ============================================================
local math = math
local abs = math.abs
-- ------------------------------------------------------------
-- => LGI
-- ============================================================
local lgi = require "lgi"
local gdk = lgi.require("Gdk", "3.0")
-- ------------------------------------------------------------
-- => nice
-- ============================================================
-- Config
local config = require "awesome-wm-nice.config"
-- Colors
local colors = require "awesome-wm-nice.colors"
local color_darken = colors.darken
local color_lighten = colors.lighten
local relative_luminance = colors.relative_luminance
-- Client Shade
local shade = require "awesome-wm-nice.shade"
-- Shapes
local shapes = require "awesome-wm-nice.shapes"
local create_corner_top_left = shapes.create_corner_top_left
local create_edge_left = shapes.create_edge_left
local create_edge_top_middle = shapes.create_edge_top_middle
local gradient = shapes.duotone_gradient_vertical
-- Utils
local utils = require "awesome-wm-nice.utils"
-- Widgets builder
local widgets = require "awesome-wm-nice.widgets"
-- ------------------------------------------------------------
gdk.init {}
-- => Local settings
-- ============================================================
local bottom_edge_height = 3
local double_click_jitter_tolerance = 4
local double_click_time_window_ms = 250
local stroke_inner_bottom_lighten_mul = 0.4
local stroke_inner_sides_lighten_mul = 0.4
local stroke_outer_top_darken_mul = 0.7
local titlebar_gradient_c1_lighten = 1
local titlebar_gradient_c2_offset = 0.5
-- ------------------------------------------------------------
local nice = {}
-- => Defaults
-- ============================================================
-- ------------------------------------------------------------
-- => Saving and loading of color rules
-- ============================================================
local t = require "awesome-wm-nice.table"
-- Load the color rules or create an empty table if there aren't any
local gfilesys = require "gears.filesystem"
local config_dir = gfilesys.get_configuration_dir()
local color_rules_filename = "color_rules"
local color_rules_filepath = config_dir .. "/nice/" .. color_rules_filename
config.color_rules = t.load(color_rules_filepath) or {}
-- Saves the contents of config.color_rules table to file
local function save_color_rules()
t.save(config.color_rules, color_rules_filepath)
end
-- Adds a color rule entry to the color_rules table for the given client and saves to file
local function set_color_rule(c, color)
config.color_rules[c.instance] = color
save_color_rules()
end
-- Fetches the color rule for the given client instance
local function get_color_rule(c)
return config.color_rules[c.instance]
end
-- ------------------------------------------------------------
function nice.get_titlebar_mouse_bindings(c)
local shade_enabled = config.win_shade_enabled
-- Add functionality for double click to (un)maximize, and single click and hold to move
local clicks = 0
local tolerance = double_click_jitter_tolerance
local buttons = {
abutton({}, config.mb_move, function()
local cx, cy = _G.mouse.coords().x, _G.mouse.coords().y
local delta = double_click_time_window_ms / 1000
clicks = clicks + 1
if clicks == 2 then
local nx, ny = _G.mouse.coords().x, _G.mouse.coords().y
-- The second click is only counted as a double click if it is
-- within the neighborhood of the first click's position, and
-- occurs within the set time window
if abs(cx - nx) <= tolerance and abs(cy - ny) <= tolerance then
if shade_enabled then
shade.shade_roll_down(c)
end
c.maximized = not c.maximized
end
else
if shade_enabled and c._nice_window_shade_up then
-- shade.shade_roll_down(c)
awful.mouse.wibox.move(c._nice_window_shade)
else
c:activate { context = "titlebar", action = "mouse_move" }
end
end
-- Start a timer to clear the click count
gtimer_weak_start_new(delta, function()
clicks = 0
end)
end),
abutton({}, config.mb_contextmenu, function()
local menu_items = {}
local function add_item(text, callback)
menu_items[#menu_items + 1] = { text, callback }
end
-- TODO: Add client control options as menu entries for options that haven't had their buttons added
add_item("Redo Window Decorations", function()
c._nice_base_color = utils.get_dominant_color(c)
set_color_rule(c, c._nice_base_color)
nice.add_window_decoration(c)
end)
add_item("Manually Pick Color", function()
_G.mousegrabber.run(function(m)
if m.buttons[1] then
c._nice_base_color = utils.get_pixel_at(m.x, m.y)
set_color_rule(c, c._nice_base_color)
nice.add_window_decoration(c)
return false
end
return true
end, "crosshair")
end)
add_item("Nevermind...", function() end)
if c._nice_right_click_menu then
c._nice_right_click_menu:hide()
end
c._nice_right_click_menu = awful.menu {
items = menu_items,
theme = config.context_menu_theme,
}
c._nice_right_click_menu:show()
end),
abutton({}, config.mb_resize, function()
c:activate { context = "mouse_click", action = "mouse_resize" }
end),
}
if config.win_shade_enabled then
buttons[#buttons + 1] = abutton({}, config.mb_win_shade_rollup, function()
shade.shade_roll_up(c)
end)
buttons[#buttons + 1] = abutton({}, config.mb_win_shade_rolldown, function()
shade.shade_roll_down(c)
end)
end
return buttons
end
-- ------------------------------------------------------------
-- Puts all the pieces together and decorates the given client
function nice.add_window_decoration(c)
local client_color = c._nice_base_color
local client_geometry = c:geometry()
-- Closures to avoid repitition
local lighten = function(amount)
return color_lighten(client_color, amount)
end
local darken = function(amount)
return color_darken(client_color, amount)
end
-- > Color computations
local luminance = relative_luminance(client_color)
local lighten_amount = utils.rel_lighten(luminance)
local darken_amount = utils.rel_darken(luminance)
-- Inner strokes
local stroke_color_inner_top = lighten(lighten_amount)
local stroke_color_inner_sides = lighten(lighten_amount * stroke_inner_sides_lighten_mul)
local stroke_color_inner_bottom = lighten(lighten_amount * stroke_inner_bottom_lighten_mul)
-- Outer strokes
local stroke_color_outer_top = darken(darken_amount * stroke_outer_top_darken_mul)
local stroke_color_outer_sides = darken(darken_amount)
local stroke_color_outer_bottom = darken(darken_amount)
local titlebar_height = config.titlebar_height
local background_fill_top =
gradient(lighten(titlebar_gradient_c1_lighten), client_color, titlebar_height, 0, titlebar_gradient_c2_offset)
-- The top left corner of the titlebar
local corner_top_left_img = create_corner_top_left {
background_source = background_fill_top,
color = client_color,
height = titlebar_height,
radius = config.titlebar_radius,
stroke_offset_inner = 1.5,
stroke_width_inner = 1,
stroke_offset_outer = 0.5,
stroke_width_outer = 1,
stroke_source_inner = gradient(stroke_color_inner_top, stroke_color_inner_sides, titlebar_height),
stroke_source_outer = gradient(stroke_color_outer_top, stroke_color_outer_sides, titlebar_height),
}
-- The top right corner of the titlebar
local corner_top_right_img = shapes.flip(corner_top_left_img, "horizontal")
-- The middle part of the titlebar
local top_edge = create_edge_top_middle {
background_source = background_fill_top,
color = client_color,
height = titlebar_height,
stroke_color_inner = stroke_color_inner_top,
stroke_color_outer = stroke_color_outer_top,
stroke_offset_inner = 1.25,
stroke_offset_outer = 0.5,
stroke_width_inner = 2,
stroke_width_outer = 1,
width = client_geometry.width,
}
-- Create the titlebar
local titlebar = awful.titlebar(c, { size = titlebar_height, bg = "transparent" })
-- Arrange the graphics
titlebar.widget = {
imagebox(corner_top_left_img, false),
{
{
{
utils.create_titlebar_items(c, config.titlebar_items.left),
widget = wcontainer_margin,
left = config.titlebar_margin_left,
},
{
utils.create_titlebar_items(c, config.titlebar_items.middle),
buttons = nice.get_titlebar_mouse_bindings(c),
layout = wlayout_flex_horizontal,
},
{
utils.create_titlebar_items(c, config.titlebar_items.right),
widget = wcontainer_margin,
right = config.titlebar_margin_right,
},
layout = wlayout_align_horizontal,
},
widget = wcontainer_background,
bgimage = top_edge,
},
imagebox(corner_top_right_img, false),
layout = wlayout_align_horizontal,
}
local resize_button = {
abutton({}, 1, function()
c:activate { context = "mouse_click", action = "mouse_resize" }
end),
}
-- The left side border
local left_border_img = create_edge_left {
client_color = client_color,
height = client_geometry.height,
stroke_offset_outer = 0.5,
stroke_width_outer = 1,
stroke_color_outer = stroke_color_outer_sides,
stroke_offset_inner = 1.5,
stroke_width_inner = 1.5,
inner_stroke_color = stroke_color_inner_sides,
}
-- The right side border
local right_border_img = shapes.flip(left_border_img, "horizontal")
local left_side_border = awful.titlebar(c, {
position = "left",
size = 2,
bg = client_color,
widget = wcontainer_background,
})
left_side_border:setup {
buttons = resize_button,
widget = wcontainer_background,
bgimage = left_border_img,
}
local right_side_border = awful.titlebar(c, {
position = "right",
size = 2,
bg = client_color,
widget = wcontainer_background,
})
right_side_border:setup {
widget = wcontainer_background,
bgimage = right_border_img,
buttons = resize_button,
}
local corner_bottom_left_img = shapes.flip(
create_corner_top_left {
color = client_color,
radius = bottom_edge_height,
height = bottom_edge_height,
background_source = background_fill_top,
stroke_offset_inner = 1.5,
stroke_offset_outer = 0.5,
stroke_source_outer = gradient(
stroke_color_outer_bottom,
stroke_color_outer_sides,
bottom_edge_height,
0,
0.25
),
stroke_source_inner = gradient(stroke_color_inner_bottom, stroke_color_inner_sides, bottom_edge_height),
stroke_width_inner = 1.5,
stroke_width_outer = 2,
},
"vertical"
)
local corner_bottom_right_img = shapes.flip(corner_bottom_left_img, "horizontal")
local bottom_edge = shapes.flip(
create_edge_top_middle {
color = client_color,
height = bottom_edge_height,
background_source = background_fill_top,
stroke_color_inner = stroke_color_inner_bottom,
stroke_color_outer = stroke_color_outer_bottom,
stroke_offset_inner = 1.25,
stroke_offset_outer = 0.5,
stroke_width_inner = 1,
stroke_width_outer = 1,
width = client_geometry.width,
},
"vertical"
)
local bottom = awful.titlebar(c, {
size = bottom_edge_height,
bg = "transparent",
position = "bottom",
})
bottom.widget = wibox.widget {
imagebox(corner_bottom_left_img, false),
-- {widget = wcontainer_background, bgimage = bottom_edge},
imagebox(bottom_edge, false),
imagebox(corner_bottom_right_img, false),
layout = wlayout_align_horizontal,
buttons = resize_button,
}
if config.win_shade_enabled then
shade.add_window_shade(c, titlebar.widget, bottom.widget)
end
if config.no_titlebar_maximized then
c:connect_signal("property::maximized", function()
if c.maximized then
local curr_screen_workarea = client.focus.screen.workarea
awful.titlebar.hide(c)
c.shape = nil
c:geometry {
x = curr_screen_workarea.x,
y = curr_screen_workarea.y,
width = curr_screen_workarea.width,
height = curr_screen_workarea.height,
}
else
awful.titlebar.show(c)
-- Shape the client
c.shape = shapes.rounded_rect {
tl = config.titlebar_radius,
tr = config.titlebar_radius,
bl = 4,
br = 4,
}
end
end)
end
-- Clean up
collectgarbage "collect"
end
function nice.apply_client_shape(c)
c.shape = shapes.rounded_rect {
tl = config.titlebar_radius,
tr = config.titlebar_radius,
bl = 4,
br = 4,
}
end
function nice.initialize(args)
config.init(args)
utils.validate_mb_bindings(config)
_G.client.connect_signal("request::titlebars", function(c)
-- Callback
c._cb_add_window_decorations = function()
gtimer_weak_start_new(0.25, function()
c._nice_base_color = utils.get_dominant_color(c)
set_color_rule(c, c._nice_base_color)
nice.add_window_decoration(c)
-- table.save(config, config_dir .. "/nice/private")
c:disconnect_signal("request::activate", c._cb_add_window_decorations)
end)
end -- _cb_add_window_decorations
-- Check if a color rule already exists...
local base_color = get_color_rule(c)
if base_color then
-- If so, use that color rule
c._nice_base_color = base_color
nice.add_window_decoration(c)
else
-- Otherwise use the default titlebar temporarily
c._nice_base_color = config.titlebar_color
nice.add_window_decoration(c)
-- Connect a signal to determine the client color and then re-decorate it
c:connect_signal("request::activate", c._cb_add_window_decorations)
end
-- Shape the client
nice.apply_client_shape(c)
end)
-- Force the window decoration to be re-created when the client size change.
_G.client.connect_signal("property::size", function(c)
nice.add_window_decoration(c)
end)
end
return gtable.join(nice, {
colors = colors,
config = config,
shade = shade,
shapes = shapes,
table = t,
utils = utils,
widgets = widgets,
})

View File

@ -0,0 +1,79 @@
local config = require "awesome-wm-nice.config"
local shapes = require "awesome-wm-nice.shapes"
local wibox = require "wibox"
local wlayout_manual = require "wibox.layout.manual"
local shade = {}
-- Legacy global variables
local bottom_edge_height = 3
-- Adds a window shade to the given client
function shade.add_window_shade(c, src_top, src_bottom)
local geo = c:geometry()
local w = wibox()
w.width = geo.width
w.background = "transparent"
w.x = geo.x
w.y = geo.y
w.height = config.titlebar_height + bottom_edge_height
w.ontop = true
w.visible = false
w.shape = shapes.rounded_rect {
tl = config.titlebar_radius,
tr = config.titlebar_radius,
bl = 4,
br = 4,
}
-- Need to use a manual layout because layout fixed seems to introduce a thin gap
src_top.point = { x = 0, y = 0 }
src_top.forced_width = geo.width
src_bottom.point = { x = 0, y = config.titlebar_height }
w.widget = { src_top, src_bottom, layout = wlayout_manual }
-- Clean up resources when a client is killed
c:connect_signal("request::unmanage", function()
if c._nice_window_shade then
c._nice_window_shade.visible = false
c._nice_window_shade = nil
end
-- Clean up
collectgarbage "collect"
end)
c._nice_window_shade_up = false
c._nice_window_shade = w
end
-- Shows the window contents
function shade.shade_roll_down(c)
if not c._nice_window_shade_up then
return
end
c:geometry { x = c._nice_window_shade.x, y = c._nice_window_shade.y }
c:activate()
c._nice_window_shade.visible = false
c._nice_window_shade_up = false
end
-- Hides the window contents
function shade.shade_roll_up(c)
if c._nice_window_shade_up then
return
end
local w = c._nice_window_shade
local geo = c:geometry()
w.x = geo.x
w.y = geo.y
w.width = geo.width
c.minimized = true
w.visible = true
w.ontop = true
c._nice_window_shade_up = true
end
-- Toggles the window shade state
function shade.shade_toggle(c)
c.minimized = not c.minimized
c._nice_window_shade.visible = c.minimized
end
return shade

View File

@ -0,0 +1,247 @@
-- => Shapes
-- Provides utility functions for handling cairo shapes and geometry
-- ============================================================
--
local colors = require "awesome-wm-nice.colors"
local lgi = require "lgi"
local hex2rgb = colors.hex2rgb
local cairo = lgi.cairo
local math = math
local rad = math.rad
-- Returns a shape function for a rounded rectangle with independently configurable corner radii
local function rounded_rect(args)
local r1 = args.tl or 0
local r2 = args.bl or 0
local r3 = args.br or 0
local r4 = args.tr or 0
return function(cr, width, height)
cr:new_sub_path()
cr:arc(width - r1, r1, r1, rad(-90), rad(0))
cr:arc(width - r2, height - r2, r2, rad(0), rad(90))
cr:arc(r3, height - r3, r3, rad(90), rad(180))
cr:arc(r4, r4, r4, rad(180), rad(270))
cr:close_path()
end
end
-- Returns a circle of the specified size filled with the specified color
local function circle_filled(color, size)
color = color or "#fefefa"
local surface = cairo.ImageSurface.create("ARGB32", size, size)
local cr = cairo.Context.create(surface)
cr:arc(size / 2, size / 2, size / 2, rad(0), rad(360))
cr:set_source_rgba(hex2rgb(color))
cr.antialias = cairo.Antialias.BEST
cr:fill()
-- cr:arc(
-- size / 2, size / 2, size / 2 - 0.5, rad(135), rad(270))
-- cr:set_source_rgba(hex2rgb(darken(color, 25)))
-- cr.line_width = 1
-- cr:stroke()
return surface
end
-- Returns a vertical gradient pattern going from cololr_1 -> color_2
local function duotone_gradient_vertical(color_1, color_2, height, offset_1, offset_2)
local fill_pattern = cairo.Pattern.create_linear(0, 0, 0, height)
local r, g, b, a
r, g, b, a = hex2rgb(color_1)
fill_pattern:add_color_stop_rgba(offset_1 or 0, r, g, b, a)
r, g, b, a = hex2rgb(color_2)
fill_pattern:add_color_stop_rgba(offset_2 or 1, r, g, b, a)
return fill_pattern
end
-- Returns a horizontal gradient pattern going from cololr_1 -> color_2
local function duotone_gradient_horizontal(color, width)
local fill_pattern = cairo.Pattern.create_linear(0, 0, width, 0)
local r, g, b, a
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(0, r, g, b, a)
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(0.5, r, g, b, a)
r, g, b, a = hex2rgb "#00000000"
fill_pattern:add_color_stop_rgba(0.6, r, g, b, a)
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(0.7, r, g, b, a)
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(1, r, g, b, a)
return fill_pattern
end
-- Flips the given surface around the specified axis
local function flip(surface, axis)
local width = surface:get_width()
local height = surface:get_height()
local flipped = cairo.ImageSurface.create("ARGB32", width, height)
local cr = cairo.Context.create(flipped)
local source_pattern = cairo.Pattern.create_for_surface(surface)
if axis == "horizontal" then
source_pattern.matrix = cairo.Matrix { xx = -1, yy = 1, x0 = width }
elseif axis == "vertical" then
source_pattern.matrix = cairo.Matrix { xx = 1, yy = -1, y0 = height }
elseif axis == "both" then
source_pattern.matrix = cairo.Matrix {
xx = -1,
yy = -1,
x0 = width,
y0 = height,
}
end
cr.source = source_pattern
cr:rectangle(0, 0, width, height)
cr:paint()
return flipped
end
-- Draws the left corner of the titlebar
local function create_corner_top_left(args)
local radius = args.radius
local height = args.height
local surface = cairo.ImageSurface.create("ARGB32", radius, height)
local cr = cairo.Context.create(surface)
-- Create the corner shape and fill it with a gradient
local radius_offset = 1 -- To soften the corner
cr:move_to(0, height)
cr:line_to(0, radius - radius_offset)
cr:arc(radius + radius_offset, radius + radius_offset, radius, rad(180), rad(270))
cr:line_to(radius, height)
cr:close_path()
cr.source = args.background_source
cr.antialias = cairo.Antialias.BEST
cr:fill()
-- Next add the subtle 3D look
local function add_stroke(nargs)
local arc_radius = nargs.radius
local offset_x = nargs.offset_x
local offset_y = nargs.offset_y
cr:new_sub_path()
cr:move_to(offset_x, height)
cr:line_to(offset_x, arc_radius + offset_y)
cr:arc(arc_radius + offset_x, arc_radius + offset_y, arc_radius, rad(180), rad(270))
cr.source = nargs.source
cr.line_width = nargs.width
cr.antialias = cairo.Antialias.BEST
cr:stroke()
end
-- Outer dark stroke
add_stroke {
offset_x = args.stroke_offset_outer,
offset_y = args.stroke_offset_outer,
radius = radius + 0.5,
source = args.stroke_source_outer,
width = args.stroke_width_outer,
}
-- Inner light stroke
add_stroke {
offset_x = args.stroke_offset_inner,
offset_y = args.stroke_offset_inner,
radius = radius,
width = args.stroke_width_inner,
source = args.stroke_source_inner,
}
return surface
end
-- Draws the middle of the titlebar
local function create_edge_top_middle(args)
local height = args.height
local width = args.width
local surface = cairo.ImageSurface.create("ARGB32", width, height)
local cr = cairo.Context.create(surface)
-- Create the background shape and fill it with a gradient
cr:rectangle(0, 0, width, height)
cr.source = args.background_source
cr:fill()
-- Then add the light and dark strokes for that 3D look
local function add_stroke(stroke_width, stroke_offset, stroke_color)
cr:new_sub_path()
cr:move_to(0, stroke_offset)
cr:line_to(width, stroke_offset)
cr.line_width = stroke_width
cr:set_source_rgb(hex2rgb(stroke_color))
cr:stroke()
end
-- Inner light stroke
add_stroke(
args.stroke_width_inner, -- 2
args.stroke_offset_inner, -- 1.25
args.stroke_color_inner -- color_lighten(client_color, utils.rel_lighten(relative_luminance(client_color)))
)
-- Outer dark stroke
add_stroke(
args.stroke_width_outer, -- 1
args.stroke_offset_outer, -- 0.5
args.stroke_color_outer -- color_darken(client_color, utils.rel_darken(relative_luminance(client_color)) * 0.7)
)
return surface
end
local function create_edge_left(args)
local height = args.height
local width = 2
-- height = height or 1080
local surface = cairo.ImageSurface.create("ARGB32", width, height)
local cr = cairo.Context.create(surface)
cr:rectangle(0, 0, 2, args.height)
cr:set_source_rgb(hex2rgb(args.client_color))
cr:fill()
-- Inner light stroke
cr:new_sub_path()
cr:move_to(args.stroke_offset_inner, 0) -- 1/5
cr:line_to(args.stroke_offset_inner, height)
cr.line_width = args.stroke_width_inner -- 1.5
cr:set_source_rgb(hex2rgb(args.inner_stroke_color))
cr:stroke()
-- Outer dark stroke
cr:new_sub_path()
cr:move_to(args.stroke_offset_outer, 0)
cr:line_to(args.stroke_offset_outer, height)
cr.line_width = args.stroke_width_outer -- 1
cr:set_source_rgb(hex2rgb(args.stroke_color_outer))
cr:stroke()
return surface
end
local function set_font(cr, font)
cr:set_font_size(font.size)
cr:select_font_face(font.font or "Inter", font.italic and 1 or 0, font.bold and 1 or 0)
end
local function text_label(args)
local surface = cairo.ImageSurface.create("ARGB32", 1, 1)
local cr = cairo.Context.create(surface)
set_font(cr, args.font)
local text = args.text
local kern = args.font.kerning or 0
local ext = cr:text_extents(text)
surface = cairo.ImageSurface.create("ARGB32", ext.width + string.len(text) * kern, ext.height)
cr = cairo.Context.create(surface)
set_font(cr, args.font)
cr:move_to(0, ext.height)
cr:set_source_rgb(hex2rgb(args.color))
-- cr:show_text(text)
text:gsub(".", function(c)
-- do something with c
cr:show_text(c)
cr:rel_move_to(kern, 0)
end)
return surface
end
return {
rounded_rect = rounded_rect,
circle_filled = circle_filled,
duotone_gradient_vertical = duotone_gradient_vertical,
flip = flip,
create_corner_top_left = create_corner_top_left,
create_edge_top_middle = create_edge_top_middle,
create_edge_left = create_edge_left,
text_label = text_label,
}

View File

@ -0,0 +1,108 @@
--[[
Courtesy of: http://lua-users.org/wiki/SaveTableToFile
]]
local function exportstring(s)
return string.format("%q", s)
end
-- The Save Function
local function save(tbl, filename)
local charS, charE = " ", "\n"
local file, err = io.open(filename, "wb")
if err then
return err
end
-- Initialize variables for save procedure
local tables, lookup = { tbl }, { [tbl] = 1 }
file:write("return {" .. charE)
for idx, t in ipairs(tables) do
file:write("-- Table: {" .. idx .. "}" .. charE)
file:write("{" .. charE)
local thandled = {}
for i, v in ipairs(t) do
thandled[i] = true
local stype = type(v)
-- only handle value
if stype == "table" then
if not lookup[v] then
table.insert(tables, v)
lookup[v] = #tables
end
file:write(charS .. "{" .. lookup[v] .. "}," .. charE)
elseif stype == "string" then
file:write(charS .. exportstring(v) .. "," .. charE)
elseif stype == "number" then
file:write(charS .. tostring(v) .. "," .. charE)
end
end
for i, v in pairs(t) do
-- escape handled values
if not thandled[i] then
local str = ""
local stype = type(i)
-- handle index
if stype == "table" then
if not lookup[i] then
table.insert(tables, i)
lookup[i] = #tables
end
str = charS .. "[{" .. lookup[i] .. "}]="
elseif stype == "string" then
str = charS .. "[" .. exportstring(i) .. "]="
elseif stype == "number" then
str = charS .. "[" .. tostring(i) .. "]="
end
if str ~= "" then
stype = type(v)
-- handle value
if stype == "table" then
if not lookup[v] then
table.insert(tables, v)
lookup[v] = #tables
end
file:write(str .. "{" .. lookup[v] .. "}," .. charE)
elseif stype == "string" then
file:write(str .. exportstring(v) .. "," .. charE)
elseif stype == "number" then
file:write(str .. tostring(v) .. "," .. charE)
end
end
end
end
file:write("}," .. charE)
end
file:write "}"
file:close()
end
-- The Load Function
local function load(sfile)
local ftables, err = loadfile(sfile)
if err then
return nil, err
end
local tables = ftables()
for idx = 1, #tables do
local tolinki = {}
for i, v in pairs(tables[idx]) do
if type(v) == "table" then
tables[idx][i] = tables[v[1]]
end
if type(i) == "table" and tables[i[1]] then
table.insert(tolinki, { i, tables[i[1]] })
end
end
-- link indices
for _, v in ipairs(tolinki) do
tables[idx][v[2]], tables[idx][v[1]] = tables[idx][v[1]], nil
end
end
return tables[1]
end
return { save = save, load = load }

View File

@ -0,0 +1,149 @@
local gsurface = require "gears.surface"
local lgi = require "lgi"
local wibox = require "wibox"
local widgets = require "awesome-wm-nice.widgets"
local wlayout_fixed = require "wibox.layout.fixed"
local gdk = lgi.require("Gdk", "3.0")
local utils = {}
function utils.rel_lighten(lum)
return lum * 90 + 10
end
function utils.rel_darken(lum)
return -(lum * 70) + 100
end
-- Returns the hex color for the pixel at the given coordinates on the screen
function utils.get_pixel_at(x, y)
local pixbuf = gdk.pixbuf_get_from_window(gdk.get_default_root_window(), x, y, 1, 1)
local bytes = pixbuf:get_pixels()
return "#" .. bytes:gsub(".", function(c)
return ("%02x"):format(c:byte())
end)
end
-- Determines the dominant color of the client's top region
function utils.get_dominant_color(client)
local color
-- gsurface(client.content):write_to_png(
-- "/home/mutex/nice/" .. client.class .. "_" .. client.instance .. ".png")
local pb
local bytes
local tally = {}
local content = gsurface(client.content)
local cgeo = client:geometry()
local x_offset = 2
local y_offset = 2
local x_lim = math.floor(cgeo.width / 2)
for x_pos = 0, x_lim, 2 do
for y_pos = 0, 8, 1 do
pb = gdk.pixbuf_get_from_surface(content, x_offset + x_pos, y_offset + y_pos, 1, 1)
bytes = pb:get_pixels()
color = "#" .. bytes:gsub(".", function(c)
return ("%02x"):format(c:byte())
end)
if not tally[color] then
tally[color] = 1
else
tally[color] = tally[color] + 1
end
end
end
local mode
local mode_c = 0
for kolor, kount in pairs(tally) do
if kount > mode_c then
mode_c = kount
mode = kolor
end
end
color = mode
return color
end
-- Returns a titlebar item
function utils.get_titlebar_item(c, name)
if name == "close" then
return widgets.create_titlebar_button(c, name, function()
c:kill()
end)
elseif name == "maximize" then
return widgets.create_titlebar_button(c, name, function()
c.maximized = not c.maximized
end, "maximized")
elseif name == "minimize" then
return widgets.create_titlebar_button(c, name, function()
c.minimized = true
end)
elseif name == "ontop" then
return widgets.create_titlebar_button(c, name, function()
c.ontop = not c.ontop
end, "ontop")
elseif name == "floating" then
return widgets.create_titlebar_button(c, name, function()
c.floating = not c.floating
if c.floating then
c.maximized = false
end
end, "floating")
elseif name == "sticky" then
return widgets.create_titlebar_button(c, name, function()
c.sticky = not c.sticky
return c.sticky
end, "sticky")
elseif name == "title" then
return widgets.create_titlebar_title(c)
end
end
-- Creates titlebar items for a given group of item names
-- group can be a string (=item name) or a table (= array of "item-name"s)
function utils.create_titlebar_items(c, group)
if not group then
return nil
end
if type(group) == "string" then
return utils.get_titlebar_item(c, group)
end
local titlebar_group_items = wibox.widget {
layout = wlayout_fixed.horizontal,
}
local item
for _, name in ipairs(group) do
item = utils.get_titlebar_item(c, name)
if item then
titlebar_group_items:add(item)
end
end
return titlebar_group_items
end
function utils.validate_mb_bindings(private)
local action_mbs = {
"mb_move",
"mb_contextmenu",
"mb_resize",
"mb_win_shade_rollup",
"mb_win_shade_rolldown",
}
local mb_specified = { false, false, false, false, false }
local mb
local mb_conflict_test
for _, action_mb in ipairs(action_mbs) do
mb = private[action_mb]
if mb then
assert(mb >= 1 and mb <= 5, "Invalid mouse button specified!")
mb_conflict_test = mb_specified[mb]
if not mb_conflict_test then
mb_specified[mb] = action_mb
else
error(("%s and %s can not be bound to the same mouse button"):format(action_mb, mb_conflict_test))
end
end
end
end
return utils

View File

@ -0,0 +1,201 @@
local abutton = require "awful.button"
local atooltip = require "awful.tooltip"
local colors = require "awesome-wm-nice.colors"
local config = require "awesome-wm-nice.config"
local get_font_height = require("beautiful").get_font_height
local imagebox = require "wibox.widget.imagebox"
local shapes = require "awesome-wm-nice.shapes"
local textbox = require "wibox.widget.textbox"
local wcontainer_constraint = require "wibox.container.constraint"
local wcontainer_margin = require "wibox.container.margin"
local wcontainer_place = require "wibox.container.place"
local wibox = require "wibox"
local widgets = {}
local cache = {}
-- Legacy global variables
local title_color_dark = "#242424"
local title_color_light = "#fefefa"
local title_unfocused_opacity = 0.7
-- Returns a color that is analogous to the last color returned
-- To make sure that the "randomly" generated colors look cohesive, only the
-- first color is truly random, the rest are generated by offseting the hue by
-- +33 degrees
local next_color = colors.rand_hex()
local function get_next_color()
local prev_color = next_color
next_color = colors.rotate_hue(prev_color, 33)
return prev_color
end
-- Returns (or generates) a button image based on the given params
function widgets.create_button_image(name, is_focused, event, is_on)
local focus_state = is_focused and "focused" or "unfocused"
local key_img
-- If it is a toggle button, then the key has an extra param
if is_on ~= nil then
local toggle_state = is_on and "on" or "off"
key_img = ("%s_%s_%s_%s"):format(name, toggle_state, focus_state, event)
else
key_img = ("%s_%s_%s"):format(name, focus_state, event)
end
-- If an image already exists, then we are done
if cache[key_img] then
return cache[key_img]
end
-- The color key just has _color at the end
local key_color = key_img .. "_color"
-- If the user hasn't provided a color, then we have to generate one
if not cache[key_color] then
local key_base_color = name .. "_color"
-- Maybe the user has at least provided a base color? If not we just pick a pesudo-random color
local base_color = config[key_base_color] or get_next_color()
cache[key_base_color] = base_color
local button_color = base_color
local H = colors.hex2hsv(base_color)
-- Unfocused buttons are desaturated and darkened (except when they are being hovered over)
if not is_focused and event ~= "hover" then
button_color = colors.hsv2hex(H, 0, 50)
end
-- Then the color is lightened if the button is being hovered over, or
-- darkened if it is being pressed, otherwise it is left as is
button_color = (event == "hover") and colors.lighten(button_color, 25)
or (event == "press") and colors.darken(button_color, 25)
or button_color
-- Save the generate color because why not lol
cache[key_color] = button_color
end
local button_size = config.button_size
-- If it is a toggle button, we create an outline instead of a filled shape if it is in off state
-- config[key_img] = (is_on ~= nil and is_on == false) and
-- shapes.circle_outline(
-- config[key_color], button_size,
-- config.button_border_width) or
-- shapes.circle_filled(
-- config[key_color], button_size)
cache[key_img] = shapes.circle_filled(cache[key_color], button_size)
return cache[key_img]
end
-- Creates a titlebar button widget
function widgets.create_titlebar_button(c, name, button_callback, property)
local button_img = imagebox(nil, false)
if config.tooltips_enabled then
local tooltip = atooltip {
timer_function = function()
local prop = name .. (property and (c[property] and "_active" or "_inactive") or "")
return config.tooltip_messages[prop]
end,
delay_show = 0.5,
margins_leftright = 12,
margins_topbottom = 6,
timeout = 0.25,
align = "bottom_right",
}
tooltip:add_to_object(button_img)
end
local is_on, is_focused
local event = "normal"
local function update()
is_focused = c.active
-- If the button is for a property that can be toggled
if property then
is_on = c[property]
button_img.image = widgets.create_button_image(name, is_focused, event, is_on)
else
button_img.image = widgets.create_button_image(name, is_focused, event)
end
end
-- Update the button when the client gains/loses focus
c:connect_signal("unfocus", update)
c:connect_signal("focus", update)
-- If the button is for a property that can be toggled, update it accordingly
if property then
c:connect_signal("property::" .. property, update)
end
-- Update the button on mouse hover/leave
button_img:connect_signal("mouse::enter", function()
event = "hover"
update()
end)
button_img:connect_signal("mouse::leave", function()
event = "normal"
update()
end)
-- The button is updated on both click and release, but the call back is executed on release
button_img.buttons = abutton({}, abutton.names.LEFT, function()
event = "press"
update()
end, function()
if button_callback then
event = "normal"
button_callback()
else
event = "hover"
end
update()
end)
button_img.id = "button_image"
update()
return wibox.widget {
widget = wcontainer_place,
{
widget = wcontainer_margin,
top = config.button_margin_top or config.button_margin_vertical or config.button_margin,
bottom = config.button_margin_bottom or config.button_margin_vertical or config.button_margin,
left = config.button_margin_left or config.button_margin_horizontal or config.button_margin,
right = config.button_margin_right or config.button_margin_horizontal or config.button_margin,
{
button_img,
widget = wcontainer_constraint,
height = config.button_size,
width = config.button_size,
strategy = "exact",
},
},
}
end
-- Returns a titlebar widget for the given client
function widgets.create_titlebar_title(c)
local client_color = c._nice_base_color
local title_widget = wibox.widget {
align = "center",
ellipsize = "middle",
opacity = c.active and 1 or title_unfocused_opacity,
valign = "center",
widget = textbox,
}
local function update()
local text_color = colors.is_contrast_acceptable(title_color_light, client_color) and title_color_light
or title_color_dark
title_widget.markup = ("<span foreground='%s' font='%s'>%s</span>"):format(
text_color,
config.titlebar_font,
c.name
)
end
c:connect_signal("property::name", update)
c:connect_signal("unfocus", function()
title_widget.opacity = title_unfocused_opacity
end)
c:connect_signal("focus", function()
title_widget.opacity = 1
end)
update()
local titlebar_font_height = get_font_height(config.titlebar_font)
local leftover_space = config.titlebar_height - titlebar_font_height
local margin_vertical = leftover_space > 1 and leftover_space / 2 or 0
return {
title_widget,
widget = wcontainer_margin,
top = margin_vertical,
bottom = margin_vertical,
}
end
return widgets

View File

@ -7,15 +7,15 @@ applications.browser = "firefox-nightly"
applications.web = "qutebrowser" applications.web = "qutebrowser"
applications.open_terminal = function() applications.open_terminal = function()
return applications.terminal .. " -e tmux" return applications.terminal .. " -e tmux"
end end
applications.open_editor = function(file) applications.open_editor = function(file)
return applications.terminal .. " -e " .. applications.editor .. " " .. file return applications.terminal .. " -e " .. applications.editor .. " " .. file
end end
applications.open_man = function(file) applications.open_man = function(file)
return applications.terminal .. " -e man " .. file return applications.terminal .. " -e man " .. file
end end
return applications return applications

View File

@ -0,0 +1,113 @@
local aclient = require "awful.client"
local akey = require "awful.key"
local utils = require "awesomerc.configuration.bindings.utils"
local client_keybindings = {
akey {
modifiers = { utils.mods.modkey },
key = "f",
description = "toggle fullscreen",
group = utils.groups.client,
on_press = function(client)
client.fullscreen = not client.fullscreen
client:raise()
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "c",
description = "close",
group = utils.groups.client,
on_press = function(client)
client:kill()
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.control },
key = "space",
description = "toggle floating",
group = utils.groups.client,
on_press = aclient.floating.toggle,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.control },
key = "Return",
description = "move to master",
group = utils.groups.client,
on_press = function(client)
client:swap(aclient.getmaster())
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "o",
description = "move to screen",
group = utils.groups.client,
on_press = function(client)
client:move_to_screen()
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "t",
description = "toggle keep on top",
group = utils.groups.client,
on_press = function(client)
client.ontop = not client.ontop
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "n",
description = "minimize",
group = utils.groups.client,
on_press = function(client)
-- The client currently has the input focus, so it cannot be
-- minimized, since minimized clients can't have the focus.
client.minimized = true
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "m",
description = "(un)maximize",
group = utils.groups.client,
on_press = function(client)
client.maximized = not client.maximized
client:raise()
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.control },
key = "m",
description = "(un)maximize vertically",
group = utils.groups.client,
on_press = function(client)
client.maximized_vertical = not client.maximized_vertical
client:raise()
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "m",
description = "(un)maximize horizontally",
group = utils.groups.client,
on_press = function(client)
client.maximized_horizontal = not client.maximized_horizontal
client:raise()
end,
},
}
return client_keybindings

View File

@ -0,0 +1,39 @@
local abutton = require "awful.button"
local utils = require "awesomerc.configuration.bindings.utils"
local mousebindings = {
abutton {
modifiers = {},
button = abutton.names.LEFT,
on_press = function(client)
client:activate {
context = "mouse_click",
}
end,
},
abutton {
modifiers = { utils.mods.modkey },
button = abutton.names.LEFT,
on_press = function(client)
client:activate {
context = "mouse_click",
action = "mouse_move",
}
end,
},
abutton {
modifiers = { utils.mods.modkey },
button = abutton.names.RIGHT,
on_press = function(client)
client:activate {
context = "mouse_click",
action = "mouse_resize",
}
end,
},
}
return mousebindings

View File

@ -0,0 +1,223 @@
local aclient = require "awful.client"
local akey = require "awful.key"
local aprompt = require "awful.prompt"
local ascreen = require "awful.screen"
local aspawn = require "awful.spawn"
local atag = require "awful.tag"
local autil = require "awful.util"
local menubar = require "menubar"
local applications = require "awesomerc.configuration.applications"
local desktop_bar = require "awesomerc.ui.desktop_decoration.bar"
local hotkeys_popup = require "awesomerc.ui.hotkeys_popup"
local mymainmenu = require "awesomerc.ui.menu.mymainmenu"
local utils = require "awesomerc.configuration.bindings.utils"
local capi = {
awesome = _G.awesome,
client = _G.client,
}
local global_keybindings = {
-- Awesome
akey {
modifiers = { utils.mods.modkey },
key = "s",
description = "show help",
group = utils.groups.awesome,
on_press = function()
hotkeys_popup.show_help()
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "w",
description = "show main menu",
group = utils.groups.awesome,
on_press = function()
mymainmenu():show()
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.control },
key = "r",
description = "reload awesome",
group = utils.groups.awesome,
on_press = capi.awesome.restart,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "q",
description = "quit awesome",
group = utils.groups.awesome,
on_press = capi.awesome.quit,
},
akey {
modifiers = { utils.mods.modkey },
key = "x",
description = "lua execute prompt",
group = utils.groups.awesome,
on_press = function()
aprompt.run {
prompt = "Run Lua code: ",
textbox = desktop_bar(ascreen.focused()).promptbox.widget,
exe_callback = autil.eval,
history_path = autil.get_cache_dir() .. "/history_eval",
}
end,
},
-- Launcher
akey {
modifiers = { utils.mods.modkey },
key = "Return",
description = "open a terminal",
group = utils.groups.launcher,
on_press = function()
aspawn(applications.open_terminal())
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "r",
description = "run prompt",
group = utils.groups.launcher,
on_press = function()
desktop_bar(ascreen.focused()).promptbox:run()
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "p",
description = "show the menubar",
group = utils.groups.launcher,
on_press = function()
menubar.show()
end,
},
-- Client focus
akey {
modifiers = { utils.mods.modkey },
key = "j",
group = "client",
description = "Focus next client by index",
on_press = function()
aclient.focus.byidx(1)
end,
},
akey {
modifiers = { utils.mods.modkey },
key = "k",
group = "client",
description = "Focus previous by index",
on_press = function()
aclient.focus.byidx(-1)
end,
},
-- Layout manipulation
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "j",
group = "client",
description = "Swap with next client",
on_press = function()
aclient.swap.byidx(1)
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "k",
group = "client",
description = "Swap with previous client",
on_press = function()
aclient.swap.byidx(-1)
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "Right",
description = "Increase master width factor",
group = "client",
on_press = function()
atag.incmwfact(0.01)
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
key = "Left",
description = "Decrease master width factor",
group = "client",
on_press = function()
atag.incmwfact(-0.01)
end,
},
-- Tags manipulation
akey {
modifiers = { utils.mods.modkey },
keygroup = akey.keygroup.NUMROW,
description = "only view tag",
group = "tag",
on_press = function(index)
local screen = ascreen.focused()
local tag = screen.tags[index]
if tag then
tag:view_only()
end
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.control },
keygroup = akey.keygroup.NUMROW,
description = "toggle tag",
group = "tag",
on_press = function(index)
local screen = ascreen.focused()
local tag = screen.tags[index]
if tag then
atag.viewtoggle(tag)
end
end,
},
akey {
modifiers = { utils.mods.modkey, utils.mods.shift },
keygroup = akey.keygroup.NUMROW,
description = "move focused client to tag",
group = "tag",
on_press = function(index)
local screen = ascreen.focused()
local client = capi.client.focus
local tag = screen.tags[index]
if client and tag then
client:move_to_tag(tag)
end
end,
},
}
return global_keybindings

View File

@ -0,0 +1,28 @@
local abutton = require "awful.button"
local atag = require "awful.tag"
local mymainmenu = require "awesomerc.ui.menu.mymainmenu"
local global_mousebindings = {
abutton {
modifiers = {},
button = abutton.names.RIGHT,
on_press = function()
mymainmenu():toggle()
end,
},
abutton {
modifiers = {},
button = abutton.names.SCROLL_UP,
on_press = tag.viewprev,
},
abutton {
modifiers = {},
button = abutton.names.SCROLL_DOWN,
on_press = atag.viewnext,
},
}
return global_mousebindings

View File

@ -0,0 +1,9 @@
local bindings = {}
bindings.client_keybindings = require "awesomerc.configuration.bindings.client_keybindings"
bindings.client_mousebindings = require "awesomerc.configuration.bindings.client_mousebindings"
bindings.global_keybindings = require "awesomerc.configuration.bindings.global_keybindings"
bindings.global_mousebindings = require "awesomerc.configuration.bindings.global_mousebindings"
bindings.utils = require "awesomerc.configuration.bindings.utils"
return bindings

View File

@ -0,0 +1,16 @@
local mods = {
control = "Control",
modkey = "Mod4",
shift = "Shift",
}
local groups = {
client = "client",
awesome = "awesome",
launcher = "launcher",
}
return {
mods = mods,
groups = groups,
}

View File

@ -0,0 +1,10 @@
local rc_configuration = {}
rc_configuration.applications = require "awesomerc.configuration.applications"
rc_configuration.bindings = require "awesomerc.configuration.bindings"
rc_configuration.menu = require "awesomerc.configuration.menu"
rc_configuration.rules = require "awesomerc.configuration.rules"
rc_configuration.prompt_commands = require "awesomerc.configuration.prompt_commands"
rc_configuration.tag_layouts = require "awesomerc.configuration.tag_layouts"
return rc_configuration

View File

@ -0,0 +1,6 @@
local configuration_menu = {}
configuration_menu.myawesomemenu = require "awesomerc.configuration.menu.myawesomemenu"
configuration_menu.mymainmenu = require "awesomerc.configuration.menu.mymainmenu"
return configuration_menu

View File

@ -0,0 +1,31 @@
local applications = require "awesomerc.configuration.applications"
local hotkeys_popup = require "awesomerc.ui.hotkeys_popup"
local capi = {
awesome = _G.awesome,
}
local myawesomemenu = {
{
"hotkeys",
function()
hotkeys_popup.show_help()
end,
},
{ "manual", applications.open_man "awesome" },
{ "edit config", applications.open_editor(capi.awesome.conffile) },
{
"restart",
function()
capi.awesome.restart()
end,
},
{
"quit",
function()
capi.awesome.quit()
end,
},
}
return myawesomemenu

View File

@ -0,0 +1,11 @@
local beautiful = require "beautiful"
local applications = require "awesomerc.configuration.applications"
local myawesomemenu = require "awesomerc.configuration.menu.myawesomemenu"
local mymainmenu = {
{ "awesome", myawesomemenu, beautiful.awesome_icon },
{ "open terminal", applications.terminal },
}
return mymainmenu

View File

@ -0,0 +1,52 @@
local atag = require "awful.tag"
local layout_suit = require "awful.layout.suit"
local commands = {}
commands["o"] = {
callback = function(parameters)
local tag_name = parameters[1] or "New-Tag"
atag
.add(tag_name, {
layout = layout_suit.tile,
})
:view_only()
end,
}
commands["O"] = {
callback = function(parameters)
local aspawn = require "awful.spawn"
local application = parameters[1]
local tag_name = parameters[2] or application
local t = atag.add(tag_name, {
layout = layout_suit.tile,
volatile = true,
})
t:view_only()
aspawn(application, { tag = t })
end,
}
commands["q"] = {
callback = function()
local ascreen = require "awful.screen"
local tags = ascreen.focused().selected_tags
for _, tag in ipairs(tags) do
tag.volatile = true
for _, client in ipairs(tag:clients()) do
client:kill()
end
tag:delete()
end
end,
}
return commands

View File

@ -0,0 +1,7 @@
local firefox_rule = {
id = "firefox",
rule = { class = "Firefox" },
properties = { screen = 1, tag = "2" },
}
return firefox_rule

View File

@ -0,0 +1,28 @@
local floating_rule = {
id = "floating",
rule_any = {
instance = { "copyq", "pinentry" },
class = {
"Arandr",
"Blueman-manager",
"Gpick",
"Kruler",
"Sxiv",
"Tor Browser",
"Wpa_gui",
"veromix",
"xtightvncviewer",
},
name = {
"Event Tester", -- xev.
},
role = {
"AlarmWindow", -- Thunderbird's calendar.
"ConfigManager", -- Thunderbird's about:config.
"pop-up", -- e.g. Google Chrome's (detached) Developer Tools.
},
},
properties = { floating = true },
}
return floating_rule

View File

@ -0,0 +1,16 @@
local aclient = require "awful.client"
local aplacement = require "awful.placement"
local ascreen = require "awful.screen"
local global_rule = {
id = "global",
rule = {},
properties = {
focus = aclient.focus.filter,
raise = true,
screen = ascreen.preferred,
placement = aplacement.no_overlap + aplacement.no_offscreen,
},
}
return global_rule

View File

@ -0,0 +1,8 @@
local client_rules = {}
client_rules.global = require "awesomerc.configuration.rules.client.global"
client_rules.floating = require "awesomerc.configuration.rules.client.floating"
client_rules.titlebar = require "awesomerc.configuration.rules.client.titlebars"
-- client_rules.firefox = require 'awesomerc.configuration.rules.client.firefox'
return client_rules

View File

@ -0,0 +1,9 @@
local titlebar_rule = {
id = "titlebars",
rule_any = {
type = { "normal", "dialog" },
},
properties = { titlebars_enabled = true },
}
return titlebar_rule

View File

@ -0,0 +1,6 @@
local rules = {}
rules.client = require "awesomerc.configuration.rules.client"
rules.notification = require "awesomerc.configuration.rules.notification"
return rules

View File

@ -0,0 +1,11 @@
local ascreen = require "awful.screen"
local global_rule = {
rule = {},
properties = {
screen = ascreen.preferred,
implicit_timeout = 5,
},
}
return global_rule

View File

@ -0,0 +1,5 @@
local notification_rules = {}
notification_rules.global = require "awesomerc.configuration.rules.notification.global"
return notification_rules

View File

@ -0,0 +1,19 @@
local alayout = require "awful.layout"
local tag_layouts = {
alayout.suit.floating,
alayout.suit.tile,
alayout.suit.tile.left,
alayout.suit.tile.bottom,
alayout.suit.tile.top,
alayout.suit.fair,
alayout.suit.fair.horizontal,
alayout.suit.spiral,
alayout.suit.spiral.dwindle,
alayout.suit.max,
alayout.suit.max.fullscreen,
alayout.suit.magnifier,
alayout.suit.corner.nw,
}
return tag_layouts

162
src/awesomerc/init.lua Normal file
View File

@ -0,0 +1,162 @@
local gtimer = require "gears.timer" -- cspell:ignore gtimer
local legacy = require "awesome-legacy"
local naughty = require "naughty"
local ruled = require "ruled"
local slot = require "awesome-slot"
-- Load global awesome components from the C API
local capi = {
client = _G.client,
screen = _G.screen,
tag = _G.tag,
}
-- Beautiful needs to be initialized as soon as possible to make theme
-- variables available to the configuration module.
legacy.beautiful {
base = "default",
theme = require "awesomerc.theme",
}
legacy.manage_error()
legacy.autofocus()
legacy.sloppy_focus()
local configuration = require "awesomerc.configuration"
local my_slots = require "awesomerc.slots"
-- This needs to be run after awesome has completed C API initialization and
-- the `root` object is available.
gtimer.delayed_call(function()
legacy.global_mouse_bindings(configuration.bindings.global_mousebindings)
legacy.global_keybindings(configuration.bindings.global_keybindings)
end)
-- luacheck: ignore unused variable load_wallpaper
local load_wallpaper = slot {
id = "LOAD_WALLPAPER",
connect = true,
target = capi.screen,
signal = "request::wallpaper",
slot = my_slots.wallpaper,
}
-- luacheck: ignore unused variable default_layout
local default_layout = slot {
id = "DEFAULT_LAYOUTS",
connect = true,
target = capi.tag,
signal = "request::default_layouts",
slot = slot.slots.tag.default_layouts,
slot_params = {
layouts = configuration.tag_layouts,
},
}
-- luacheck: ignore unused variable create_tag
local create_tag = slot {
id = "CREATE_TAGS",
connect = true,
target = capi.screen,
signal = "request::desktop_decoration",
slot = my_slots.create_tags,
}
-- luacheck: ignore unused variable desktop_decoration
local desktop_decoration = slot {
id = "DESKTOP_DECORATION",
connect = true,
target = capi.screen,
signal = "request::desktop_decoration",
slot = my_slots.build_desktop_decoration,
}
-- luacheck: ignore unused variable client_mousebinding
local client_mousebinding = slot {
id = "CLIENT_MOUSE_BINDINGS",
connect = true,
target = capi.client,
signal = "request::default_mousebindings",
slot = slot.slots.client.append_mousebindings,
slot_params = {
mousebindings = configuration.bindings.client_mousebindings,
},
}
-- luacheck: ignore unused variable client_keybinding
local client_keybinding = slot {
id = "CLIENT_KEY_BINDINGS",
connect = true,
target = capi.client,
signal = "request::default_keybindings",
slot = slot.slots.client.append_keybindings,
slot_params = {
keybindings = configuration.bindings.client_keybindings,
},
}
-- luacheck: ignore unused variable ruled_client
local ruled_client = slot {
id = "RULED_CLIENT",
connect = true,
target = ruled.client,
signal = "request::rules",
slot = slot.slots.ruled.append_client_rules,
slot_params = {
rules = configuration.rules.client,
},
}
-- luacheck: ignore unused variable client_titlebar
local client_titlebar = slot {
id = "CLIENT_TITLEBAR",
connect = true,
target = capi.client,
signal = "request::titlebars",
slot = my_slots.build_titlebars,
}
gtimer.delayed_call(function()
local nice_config = require "awesome-wm-nice.config"
local nice_utils = require "awesome-wm-nice.utils"
nice_config.init()
nice_utils.validate_mb_bindings(nice_config)
end)
-- luacheck: ignore unused variable client_shape
local client_shape = slot {
id = "CLIENT_SHAPE",
connect = true,
target = capi.client,
signal = "request::manage",
slot = my_slots.client_shape,
}
-- luacheck: ignore unused variable ruled_notification
local ruled_notification = slot {
id = "RULED_NOTIFICATION",
connect = true,
target = ruled.notification,
signal = "request::rules",
slot = slot.slots.ruled.append_notification_rules,
slot_params = {
rules = configuration.rules.notification,
},
}
-- luacheck: ignore unused variable naughty_display
local naughty_display = slot {
id = "NAUGHTY_DISPLAY",
connect = true,
target = naughty,
signal = "request::display",
slot = my_slots.naughty_display,
}
gtimer.delayed_call(function()
naughty.notify {
title = "Aire-One dots",
message = "Welcome to the Aire-One rc!",
}
end)

View File

@ -0,0 +1,86 @@
local alayout = require "awful.layout"
local atag = require "awful.tag"
local awallpaper = require "awful.wallpaper"
local beautiful = require "beautiful"
local desktop_bar = require "awesomerc.ui.desktop_decoration.bar"
local gcolor = require "gears.color"
local gsurface = require "gears.surface"
local gtimer = require "gears.timer"
local imagebox = require "wibox.widget.imagebox"
local lgi = require "lgi"
local naughty = require "naughty"
local nice_shapes = require "awesome-wm-nice.shapes"
local titlebar = require "awesomerc.ui.titlebar"
local cairo = lgi.cairo
local slots = {}
function slots.wallpaper(screen)
local screen_geo = screen.geometry
local source = cairo.ImageSurface(cairo.Format.RGB32, screen_geo.width, screen_geo.height)
local cr = cairo.Context(source)
-- Load base image
local image_surface = gsurface.load_uncached(beautiful.wallpaper)
local w, h = gsurface.get_size(image_surface)
cr:scale(screen_geo.width / w, screen_geo.height / h)
cr:set_source_surface(image_surface, 0, 0)
cr:paint()
-- Add color layer
local color_pattern = gcolor.create_linear_pattern {
from = { 0, 0 },
to = { screen.width, screen.height },
stops = {
{ 0, "#26323840" },
},
}
cr:set_source(color_pattern)
cr:paint()
awallpaper {
screen = screen,
widget = {
image = source,
widget = imagebox,
},
}
end
function slots.create_tags(screen)
local first_tag = atag.add("home", {
screen = screen,
layout = alayout.suit.tile,
icon = beautiful.icon_hometag,
gap = beautiful.gaps_hometag,
})
gtimer.delayed_call(function()
first_tag:view_only()
-- spawn(apps.open_terminal(), { screen = screen, tag = first_tag })
end)
end
function slots.build_desktop_decoration(screen)
desktop_bar(screen)
end
function slots.build_titlebars(client)
titlebar(client)
end
function slots.client_shape(client)
client.shape = nice_shapes.rounded_rect {
tl = beautiful.client_corner_radius_top,
tr = beautiful.client_corner_radius_top,
bl = beautiful.client_corner_radius_bottom,
br = beautiful.client_corner_radius_bottom,
}
end
function slots.naughty_display(notification)
naughty.layout.box { notification = notification }
end
return slots

View File

@ -1,17 +1,18 @@
local gfs = require "gears.filesystem" local gfs = require "gears.filesystem"
local config_dir = gfs.get_configuration_dir() local config_dir = gfs.get_configuration_dir()
local theme_dir = config_dir .. "theme/" local assets_dir = config_dir .. "assets/"
local assets_dir = theme_dir .. "assets/"
local icons_dir = assets_dir .. "icons/" local icons_dir = assets_dir .. "icons/"
local theme = {} local theme = {}
--- Basic --- Basic
theme.font = "Noto Mono 9" theme.font = "Noto Mono 9"
theme.border_width = 0
--- Tags --- Clients
theme.hometag_master_width_factor = 0.65 theme.client_corner_radius_top = 9
theme.client_corner_radius_bottom = 4
--- Icons --- Icons
theme.icon_hometag = icons_dir .. "home-circle.svg" theme.icon_hometag = icons_dir .. "home-circle.svg"
@ -32,7 +33,7 @@ theme.wibar_bg = "#0000"
--- Spacing and margins --- Spacing and margins
-- Naming convention: -- Naming convention:
-- <rc.ui.desktop_decoration element>_<widget element>_<property> -- <awesomerc.ui.desktop_decoration element>_<widget element>_<property>
theme.spacing = 8 theme.spacing = 8
theme.margin = 4 theme.margin = 4
theme.padding = 4 theme.padding = 4
@ -51,11 +52,13 @@ theme.bar_box_padding_x = theme.padding * 2
theme.bar_box_padding_y = theme.padding theme.bar_box_padding_y = theme.padding
theme.bar_box_spacing = theme.spacing theme.bar_box_spacing = theme.spacing
theme.gaps_hometag = theme.padding
--- Widgets specific --- Widgets specific
theme.bg_systray = "#455A64" theme.bg_systray = "#455A64"
theme.systray_icon_spacing = 3 theme.systray_icon_spacing = 3
--- Wallpaper --- Wallpaper
theme.wallpaper = assets_dir .. "wallpapers/pizza.jpg" theme.wallpaper = assets_dir .. "wallpapers/poke.jpg"
return theme return theme

View File

@ -0,0 +1,241 @@
local awful = require "awful"
local awibar = require "awful.wibar"
local battery_widget = require "awesome-battery_widget"
local beautiful = require "beautiful"
local container_background = require "wibox.container.background"
local container_margin = require "wibox.container.margin"
local container_place = require "wibox.container.place"
local gshape = require "gears.shape"
local layout_align = require "wibox.layout.align"
local layout_fixed = require "wibox.layout.fixed"
local systray = require "wibox.widget.systray"
local textclock = require "wibox.widget.textclock"
local widget = require "wibox.widget"
local mycommands = require "awesomerc.configuration.prompt_commands"
local mymainmenu = require "awesomerc.ui.menu.mymainmenu"
local mytaglist = require "MyTagListWidget"
local bar_widgets = require "awesomerc.ui.desktop_decoration.bar.widgets"
local mybattery = bar_widgets.battery
local myprompt = bar_widgets.prompt
local capi = {
screen = _G.screen,
}
local abs = math.abs
local dpi = beautiful.xresources.apply_dpi
local function get_screen_id(screen)
local s = capi.screen[screen or 1]
return s.index
end
--- Build a widget box in the bar.
-- A widget box is a combinaision of "shaped" `wibox.container/background` and
-- `wibox.container.margin` to create this nice round-colored-buttons I like.
-- @tparam args table
-- @tparam args.screen The screen where the widget will be drawn.
-- @tparam args.widget The widget to draw.
-- @tparam[opt] args.bg The box's background.
-- @tpram[opt] args.fg The box's foreground (used mainly for textbox text color).
-- @treturn wibox.container.background The builded widget.
local function build_widget(args)
local screen_id = get_screen_id(args.screen)
-- Callback for the shape function.
-- If the widget is almost a square: draw a circle. Otherwise, draw a
-- rounded_bar.
local shape_callback = function(cr, width, height)
local shape = gshape.circle
-- 10 is an arbitrary value I found after some tests :shrug:
if abs(width - height) > 10 then
shape = gshape.rounded_bar
end
return shape(cr, width, height)
end
local box_widget = widget {
{
widget = container_margin,
draw_empty = false,
top = dpi(beautiful.bar_box_padding_y, screen_id),
bottom = dpi(beautiful.bar_box_padding_y, screen_id),
right = dpi(beautiful.bar_box_padding_x, screen_id),
left = dpi(beautiful.bar_box_padding_x, screen_id),
args.widget,
},
bg = args.bg,
fg = args.fg,
shape = shape_callback,
widget = container_background,
}
return box_widget
end
local bar = { _private = { instances = {} }, mt = {} }
bar.widgets = bar_widgets
--- Get the bar instance for a given screen.
-- If no instance was found, we build a new one.
-- @tparam screen screen|integer The bar's screen.
-- @treturn wibox.wibar
function bar:instance(screen)
local screen_id = get_screen_id(screen)
local instance = self._private.instances[screen_id]
if not instance then
instance = bar.new(screen)
self._private.instances[screen_id] = instance
end
return instance
end
function bar.new(screen)
local my_bar = {}
my_bar.launcher = build_widget {
screen = screen,
bg = "#2196F3",
widget = awful.widget.launcher {
image = beautiful.icon_apps,
menu = mymainmenu(),
},
}
my_bar.textclock = build_widget {
screen = screen,
bg = "#FF5722",
fg = "#ECEFF1",
widget = textclock "%l:%M %p",
}
my_bar.promptbox = myprompt {
commands = mycommands,
}
-- This widget needs to be reworded and integrated inside the project.
my_bar.taglist = mytaglist.new {
screen = screen,
}
my_bar.battery = build_widget {
screen = screen,
bg = "#673AB7",
widget = mybattery {
screen = screen,
device_path = battery_widget.get_BAT0_device_path(),
color = "#ECEFF1",
},
}
my_bar.systray = build_widget {
screen = screen,
bg = "#455A64",
widget = systray(),
}
my_bar.wibar = awibar {
screen = screen,
position = "bottom",
height = dpi(beautiful.bar_height, screen),
width = beautiful.bar_width, -- Width is a percentage of the screen size
}
my_bar.wibar:setup {
-- Bar margins
{
-- Physical bar
{
-- Bar paddings
{
-- Bar content
{
-- Left side of the bar, align on the left
{
-- box margins
{
-- Left widget boxes
my_bar.launcher,
my_bar.promptbox,
spacing = dpi(beautiful.bar_box_spacing, screen),
layout = layout_fixed.horizontal,
},
top = dpi(beautiful.bar_box_margin_y, screen),
bottom = dpi(beautiful.bar_box_margin_y, screen),
right = dpi(beautiful.bar_box_margin_x, screen),
left = dpi(beautiful.bar_box_margin_x, screen),
widget = container_margin,
},
halign = "left",
widget = container_place,
},
expand = "outside",
layout = layout_align.horizontal,
-- Middle widget is the custom taglist
-- it doesn't need box/margins/...
{
my_bar.taglist,
widget = container_place,
},
{
-- Right side of the bar, align on the right
{
-- box margins
{
-- Right widget boxes
my_bar.systray,
my_bar.battery,
my_bar.textclock,
spacing = dpi(beautiful.bar_box_spacing, screen),
layout = layout_fixed.horizontal,
},
top = dpi(beautiful.bar_box_margin_y, screen),
bottom = dpi(beautiful.bar_box_margin_y, screen),
right = dpi(beautiful.bar_box_margin_x, screen),
left = dpi(beautiful.bar_box_margin_x, screen),
widget = container_margin,
},
halign = "right",
widget = container_place,
},
},
top = dpi(beautiful.bar_padding_y, screen),
bottom = dpi(beautiful.bar_padding_y, screen),
right = dpi(beautiful.bar_padding_x, screen),
left = dpi(beautiful.bar_padding_x, screen),
widget = container_margin,
},
bg = beautiful.bar_bg,
shape = function(cr, width, height)
gshape.rounded_rect(cr, width, height, 10)
end,
widget = container_background,
},
top = dpi(beautiful.bar_margin_y, screen),
bottom = dpi(beautiful.bar_margin_y, screen),
right = dpi(beautiful.bar_margin_x, screen),
left = dpi(beautiful.bar_margin_x, screen),
widget = container_margin,
}
return my_bar
end
function bar.mt:__call(...)
return self:instance(...)
end
return setmetatable(bar, bar.mt)

View File

@ -17,7 +17,7 @@ local beautiful = require "beautiful"
local gcolor = require "gears.color" local gcolor = require "gears.color"
local imagebox = require "wibox.widget.imagebox" local imagebox = require "wibox.widget.imagebox"
local battery_widget = require "battery-widget" local battery_widget = require "awesome-battery_widget"
local my_battery = {} local my_battery = {}
local mt = {} local mt = {}
@ -27,25 +27,25 @@ local mt = {}
-- @tparam gears.color|string color The color of the drawing. -- @tparam gears.color|string color The color of the drawing.
-- @treturn cairo.ImageSurface The generated surface for the drawing. -- @treturn cairo.ImageSurface The generated surface for the drawing.
function my_battery.draw_battery(percentage, color) function my_battery.draw_battery(percentage, color)
local svg_handle = rsvg.Handle.new_from_file(beautiful.icon_battery_outline) local svg_handle = rsvg.Handle.new_from_file(beautiful.icon_battery_outline)
if not svg_handle then if not svg_handle then
return return
end end
local surface = cairo.ImageSurface.create(cairo.Format.ARGB32, 24, 24) local surface = cairo.ImageSurface.create(cairo.Format.ARGB32, 24, 24)
local cr = cairo.Context(surface) local cr = cairo.Context(surface)
svg_handle:render_cairo(cr) svg_handle:render_cairo(cr)
local max_height = 14 local max_height = 14
local x = 8 local x = 8
local width = 8 local width = 8
local height = percentage / 100 * max_height local height = percentage / 100 * max_height
local y = 6 + max_height - height local y = 6 + max_height - height
cr:set_source(gcolor(color)) cr:set_source(gcolor(color))
cr:rectangle(x, y, width, height) cr:rectangle(x, y, width, height)
cr:fill() cr:fill()
return surface return surface
end end
--- Update handler for the battery widget. This is the function called --- Update handler for the battery widget. This is the function called
@ -53,16 +53,16 @@ end
-- @tparam my_battery widget The battery widget to update. -- @tparam my_battery widget The battery widget to update.
-- @tparam UPowerGLib.Device device The UPower device to monitor. -- @tparam UPowerGLib.Device device The UPower device to monitor.
function my_battery.update(widget, device) function my_battery.update(widget, device)
widget.image = my_battery.draw_battery(device.percentage, widget.color) widget.image = my_battery.draw_battery(device.percentage, widget.color)
end end
--- Give the widget template for the battery widget. --- Give the widget template for the battery widget.
-- (It's a basic `wibox.widget.imagebox` for my battery implementation) -- (It's a basic `wibox.widget.imagebox` for my battery implementation)
function my_battery.widget_template() function my_battery.widget_template()
local widget = imagebox() local widget = imagebox()
widget.resize = true widget.resize = true
return widget return widget
end end
--- Constructor of my battery widget! --- Constructor of my battery widget!
@ -72,31 +72,32 @@ end
-- @tparam gears.color|string args.color Color to use to draw the battery. -- @tparam gears.color|string args.color Color to use to draw the battery.
-- @treturn my_battery The instantiated widget. -- @treturn my_battery The instantiated widget.
function my_battery.new(args) function my_battery.new(args)
local widget = battery_widget { local widget = battery_widget {
screen = args.screen, screen = args.screen,
device_path = args.device_path, -- device_path = args.device_path,
widget_template = my_battery.widget_template(), use_display_device = true,
instant_update = true, widget_template = my_battery.widget_template(),
} instant_update = true,
}
widget.color = args.color widget.color = args.color
widget:connect_signal("upower::update", function(w, device) widget:connect_signal("upower::update", function(w, device)
my_battery.update(w, device) my_battery.update(w, device)
end) end)
widget.tooltip = atooltip { widget.tooltip = atooltip {
objects = { widget }, objects = { widget },
timer_function = function() timer_function = function()
return string.format("%3d", widget.device.percentage) .. "%" return string.format("%3d", widget.device.percentage) .. "%"
end, end,
} }
return widget return widget
end end
function mt:__call(...) function mt:__call(...) -- luacheck: ignore unused self
return my_battery.new(...) return my_battery.new(...)
end end
return setmetatable(my_battery, mt) return setmetatable(my_battery, mt)

View File

@ -0,0 +1,6 @@
local bar_widgets = {}
bar_widgets.battery = require "awesomerc.ui.desktop_decoration.bar.widgets.battery"
bar_widgets.prompt = require "awesomerc.ui.desktop_decoration.bar.widgets.prompt"
return bar_widgets

View File

@ -0,0 +1,72 @@
local acompletion = require "awful.completion"
local aspawn = require "awful.spawn"
local gstring = require "gears.string"
local gtable = require "gears.table"
local prompt = require "awful.widget.prompt"
local string = string
local function completion_cb(self, command_before_comp, cur_pos_before_comp, ncomp)
return acompletion.generic(command_before_comp, cur_pos_before_comp, ncomp, self.completion_keywords)
end
local function exe_cb(self, input)
-- Exit if the input is empty
if not input or #input == 0 then
return
end
-- Trim
input = string.gsub(input, "^%s*(.-)%s*$", "%1")
-- If the input is not a VI command, spawn it
if input:sub(1, 1) ~= ":" then
aspawn(input)
return
end
-- Parse the custom command
local command, parameters = input:gmatch ":([%w-]+)%s*(.*)"()
command = command:sub(1, 1)
parameters = gstring.split(parameters, "%s")
-- Quit if the command doesn't exist
if not self.commands[command] then
print('":' .. command .. '" not reconized as a command')
return
end
self.commands[command].callback(parameters)
end
local my_prompt = { mt = {} }
function my_prompt.new(args)
local prompt_widget = nil
prompt_widget = prompt {
prompt = "<b></b> ", -- needs the 3 spaces
completion_callback = function(...)
return completion_cb(prompt_widget, ...)
end,
exe_callback = function(...)
return exe_cb(prompt_widget, ...)
end,
}
prompt_widget.commands = args.commands
prompt_widget.completion_keywords = gtable.join(
-- TODO: find applications list
gtable.find_keys(args.commands, function()
return true
end, false),
args.completion_keywords or {}
)
return prompt_widget
end
function my_prompt.mt:__call(...) -- luacheck: ignore unused arg self
return my_prompt.new(...)
end
return setmetatable(my_prompt, my_prompt.mt)

View File

@ -0,0 +1,5 @@
local desktop_decoration = {}
desktop_decoration.bar = require "awesomerc.ui.desktop_decoration.bar"
return desktop_decoration

View File

@ -0,0 +1,12 @@
local hotkeys_popup = require "awful.hotkeys_popup"
-- luacheck: ignore unset variable keys
local keys = {
vim = require "awful.hotkeys_popup.keys.vim",
firefox = require "awful.hotkeys_popup.keys.firefox",
tmux = require "awful.hotkeys_popup.keys.tmux",
qutebrowser = require "awful.hotkeys_popup.keys.qutebrowser",
termite = require "awful.hotkeys_popup.keys.termite",
}
return hotkeys_popup

View File

@ -0,0 +1,32 @@
local amenu = require "awful.menu"
local configuration = {
menu = require "awesomerc.configuration.menu",
}
local mymainmenu = { _private = {}, mt = {} }
function mymainmenu:instance()
local instance = self._private.instance
if not instance then
instance = mymainmenu.new()
self._private.instance = instance
end
return instance
end
function mymainmenu.new()
local menu = amenu {
items = configuration.menu.mymainmenu,
}
return menu
end
function mymainmenu.mt:__call()
return self:instance()
end
return setmetatable(mymainmenu, mymainmenu.mt)

View File

@ -0,0 +1,24 @@
local gtimer = require "gears.timer"
local nice = require "awesome-wm-nice"
local titlebar = { mt = {} }
function titlebar.new(client)
-- We need first to manually set the base color because of how nice is designed
client._nice_base_color = nice.config.titlebar_color
-- We need to delay the call to add_window_decoration because the client has
-- to be fully initialized first (e.g. the client's geometry has to be set)
-- for the decoration to be added correctly.
-- Note: resizing the client will not update the decoration size, so it feels
-- a bit hacky. I need to fix this.
gtimer.delayed_call(function()
nice.add_window_decoration(client)
end)
end
function titlebar.mt:__call(client) -- luacheck: ignore unused arg self
return titlebar.new(client)
end
return setmetatable(titlebar, titlebar.mt)

View File

@ -1,33 +0,0 @@
#!/usr/bin/env sh
killXephyr() {
kill "$(pgrep Xephyr)" >/dev/null 2>&1
}
trap killXephyr EXIT
if [ "$1" = "stop" ]; then
killXephyr
exit 0
fi;
xephyr=$1
awesome=$2
rc_file=$3
# Check for the first free display
for i in $(seq 1 10); do
if [ ! -f "/tmp/.X${i}-lock" ]; then
D=$i;
break;
fi;
done
# Start Xephyr
$xephyr :"$D" -name xephyr_"$D" -ac -br -noreset -screen 1600x900 >/dev/null 2>&1 &
sleep 1
# Start Awesome
DISPLAY=:$D.0 $awesome \
--config "$rc_file"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 MiB

View File

@ -1,247 +0,0 @@
local awibar = require "awful.wibar"
local awful = require "awful"
local battery_widget = require "battery-widget"
local beautiful = require "beautiful"
local container_background = require "wibox.container.background"
local container_margin = require "wibox.container.margin"
local container_place = require "wibox.container.place"
local gshape = require "gears.shape"
local layout_align = require "wibox.layout.align"
local layout_fixed = require "wibox.layout.fixed"
local systray = require "wibox.widget.systray"
local textclock = require "wibox.widget.textclock"
local widget = require "wibox.widget"
local mycommands = require "rc.configuration.prompt_commands"
local mymainmenu = require "rc.ui.menu.mymainmenu"
local mytaglist = require "MyTagListWidget"
local bar_widgets = require "rc.ui.desktop_decoration.bar.widgets"
local mybattery = bar_widgets.battery
local myprompt = bar_widgets.prompt
local capi = {
screen = _G.screen,
}
local abs = math.abs
local dpi = beautiful.xresources.apply_dpi
local function get_screen_id(screen)
local s = capi.screen[screen or 1]
return s.index
end
--- Build a widget box in the bar.
-- A widget box is a combinaision of "shaped" `wibox.container/background` and
-- `wibox.container.margin` to create this nice round-colored-buttons I like.
-- @tparam args table
-- @tparam args.screen The screen where the widget will be drawn.
-- @tparam args.widget The widget to draw.
-- @tparam[opt] args.bg The box's background.
-- @tpram[opt] args.fg The box's foreground (used mainly for textbox text color).
-- @treturn wibox.container.background The builded widget.
local function build_widget(args)
local screen_id = get_screen_id(args.screen)
-- Callback for the shape function.
-- If the widget is almost a square: draw a circle. Otherwise, draw a
-- rounded_bar.
local shape_callback = function(cr, width, height)
local shape = gshape.circle
-- 10 is an arbitrary value I found after some tests :shrug:
if abs(width - height) > 10 then
shape = gshape.rounded_bar
end
return shape(cr, width, height)
end
local box_widget = widget {
{
widget = container_margin,
draw_empty = false,
top = dpi(beautiful.bar_box_padding_y, screen_id),
bottom = dpi(beautiful.bar_box_padding_y, screen_id),
right = dpi(beautiful.bar_box_padding_x, screen_id),
left = dpi(beautiful.bar_box_padding_x, screen_id),
args.widget,
},
bg = args.bg,
fg = args.fg,
shape = shape_callback,
widget = container_background,
}
return box_widget
end
local bar = { _private = { instances = {} }, mt = {} }
bar.widgets = bar_widgets
--- Get the bar instance for a given screen.
-- If no instance was found, we build a new one.
-- @tparam screen screen|integer The bar's screen.
-- @treturn wibox.wibar
function bar:instance(screen)
local screen_id = get_screen_id(screen)
local instance = self._private.instances[screen_id]
if not instance then
instance = bar.new(screen)
self._private.instances[screen_id] = instance
end
return instance
end
function bar.new(screen)
local my_bar = {}
my_bar.launcher = build_widget {
screen = screen,
bg = "#2196F3",
widget = awful.widget.launcher {
image = beautiful.icon_apps,
menu = mymainmenu(),
},
}
my_bar.textclock = build_widget {
screen = screen,
bg = "#FF5722",
fg = "#ECEFF1",
widget = textclock "%l:%M %p",
}
my_bar.promptbox = myprompt {
commands = mycommands,
}
-- This widget needs to be reworded and integrated inside the project.
my_bar.taglist = mytaglist.new {
screen = screen,
}
my_bar.battery = build_widget {
screen = screen,
bg = "#673AB7",
widget = mybattery {
screen = screen,
device_path = battery_widget.get_BAT0_device_path(),
color = "#ECEFF1",
},
}
my_bar.systray = build_widget {
screen = screen,
bg = "#455A64",
widget = systray(),
}
my_bar.wibar = awibar {
screen = screen,
position = "bottom",
height = dpi(beautiful.bar_height, screen),
width = beautiful.bar_width, -- Width is a percentage of the screen size
}
my_bar.wibar:setup {
-- Bar margins
{
-- Physical bar
{
-- Bar paddings
{
-- Bar content
{
-- Left side of the bar, align on the left
{
-- box margins
{
-- Left widget boxes
my_bar.launcher,
my_bar.promptbox,
spacing = dpi(
beautiful.bar_box_spacing,
screen
),
layout = layout_fixed.horizontal,
},
top = dpi(beautiful.bar_box_margin_y, screen),
bottom = dpi(beautiful.bar_box_margin_y, screen),
right = dpi(beautiful.bar_box_margin_x, screen),
left = dpi(beautiful.bar_box_margin_x, screen),
widget = container_margin,
},
halign = "left",
widget = container_place,
},
expand = "outside",
layout = layout_align.horizontal,
-- Middle widget is the custom taglist
-- it doesn't need box/margins/...
{
my_bar.taglist,
widget = container_place,
},
{
-- Right side of the bar, align on the right
{
-- box margins
{
-- Right widget boxes
my_bar.systray,
my_bar.battery,
my_bar.textclock,
spacing = dpi(
beautiful.bar_box_spacing,
screen
),
layout = layout_fixed.horizontal,
},
top = dpi(beautiful.bar_box_margin_y, screen),
bottom = dpi(beautiful.bar_box_margin_y, screen),
right = dpi(beautiful.bar_box_margin_x, screen),
left = dpi(beautiful.bar_box_margin_x, screen),
widget = container_margin,
},
halign = "right",
widget = container_place,
},
},
top = dpi(beautiful.bar_padding_y, screen),
bottom = dpi(beautiful.bar_padding_y, screen),
right = dpi(beautiful.bar_padding_x, screen),
left = dpi(beautiful.bar_padding_x, screen),
widget = container_margin,
},
bg = beautiful.bar_bg,
shape = function(cr, width, height)
gshape.rounded_rect(cr, width, height, 10)
end,
widget = container_background,
},
top = dpi(beautiful.bar_margin_y, screen),
bottom = dpi(beautiful.bar_margin_y, screen),
right = dpi(beautiful.bar_margin_x, screen),
left = dpi(beautiful.bar_margin_x, screen),
widget = container_margin,
}
return my_bar
end
function bar.mt:__call(...)
return self:instance(...)
end
return setmetatable(bar, bar.mt)

Some files were not shown because too many files have changed in this diff Show More