diff --git a/.busted b/.busted new file mode 100644 index 0000000..e8e9cfb --- /dev/null +++ b/.busted @@ -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", + }, +} diff --git a/.cspell.json b/.cspell.json new file mode 100644 index 0000000..c505535 --- /dev/null +++ b/.cspell.json @@ -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" + ] +} diff --git a/.editorconfig b/.editorconfig index c1e2c64..21b11e1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,8 +5,17 @@ root = true [*] indent_style = space -indent_size = 4 +indent_size = 3 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true + +[*.json] +indent_size = 2 + +[*.sh] +indent_size = 4 + +[Makefile] +indent_style = tab diff --git a/.luacheckrc b/.luacheckrc index 8094e43..7e6bfc4 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -1,10 +1,28 @@ --- Only allow symbols available in all Lua versions -std = "min" +std = "lua54" --- Get rid of "unused argument self"-warnings -self = false +files[".luamonrc"].std = { + 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 = { "awesome", "button", @@ -21,13 +39,9 @@ read_globals = { "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 +globals = { + "screen", + "mouse", + "root", + "client", +} diff --git a/.luamonrc b/.luamonrc new file mode 100644 index 0000000..6275058 --- /dev/null +++ b/.luamonrc @@ -0,0 +1,2 @@ +ext = { "lua" } +lang = "./scripts/run.sh runAwesome" diff --git a/.stylua.toml b/.stylua.toml new file mode 100644 index 0000000..8440490 --- /dev/null +++ b/.stylua.toml @@ -0,0 +1,6 @@ +indent_type = "Spaces" +indent_width = 3 +call_parentheses = "None" + +[sort_requires] +enabled = true diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 28a1bfe..6a07a7e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,9 +1,10 @@ { - "recommendations": [ - "editorconfig.editorconfig", - "tomblind.local-lua-debugger-vscode", - "sumneko.lua", - "johnnymorganz.stylua", - "dwenegar.vscode-luacheck" - ] + "recommendations": [ + "streetsidesoftware.code-spell-checker", + "editorconfig.editorconfig", + "tomblind.local-lua-debugger-vscode", + "johnnymorganz.stylua", + "dwenegar.vscode-luacheck", + "sumneko.lua" + ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index f8145a4..0d21972 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,19 +1,17 @@ { - "version": "0.2.0", - "configurations": [ - { - "type": "lua-local", - "request": "launch", - "name": "Debug with Xephyr", - "program": { - "command": "${workspaceFolder}/start-xephyr.sh" - }, - "args": [ - "/usr/bin/Xephyr", - "/usr/bin/awesome", - "${workspaceFolder}/init.lua" - ], - "postDebugTask": "Terminate All Tasks" - } - ] + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lua-local", + "request": "launch", + "name": "Debug with Xephyr", + "program": { + "command": "${workspaceFolder}/scripts/run.sh" + }, + "args": ["debug"] + } + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index ea79314..6b444e2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,20 @@ { - "editor.formatOnSave": true, - "editor.formatOnPaste": true, - "[markdown]": { - "editor.wordWrap": "on", - "editor.renderWhitespace": "all", - "editor.acceptSuggestionOnEnter": "off" - } + "Lua.runtime.path": [ + "/usr/share/awesome/lib/?.lua", + "/usr/share/awesome/lib/?/init.lua", + "${workspaceFolder}/src/?.lua", + "${workspaceFolder}/src/init.lua", + "${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" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index dd690d8..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -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" - } - ] -} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3e52060 --- /dev/null +++ b/Makefile @@ -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 diff --git a/awesomerc-dev-1.rockspec b/awesomerc-dev-1.rockspec new file mode 100644 index 0000000..7d6a7d7 --- /dev/null +++ b/awesomerc-dev-1.rockspec @@ -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", +} diff --git a/theme/assets/icons/apps.svg b/config/awesome/assets/icons/apps.svg similarity index 100% rename from theme/assets/icons/apps.svg rename to config/awesome/assets/icons/apps.svg diff --git a/theme/assets/icons/battery-outline.svg b/config/awesome/assets/icons/battery-outline.svg similarity index 100% rename from theme/assets/icons/battery-outline.svg rename to config/awesome/assets/icons/battery-outline.svg diff --git a/theme/assets/icons/home-circle.svg b/config/awesome/assets/icons/home-circle.svg similarity index 100% rename from theme/assets/icons/home-circle.svg rename to config/awesome/assets/icons/home-circle.svg diff --git a/config/awesome/assets/wallpapers/poke.jpg b/config/awesome/assets/wallpapers/poke.jpg new file mode 100644 index 0000000..3ed8003 Binary files /dev/null and b/config/awesome/assets/wallpapers/poke.jpg differ diff --git a/config/awesome/rc.lua b/config/awesome/rc.lua new file mode 100644 index 0000000..2d0ea0b --- /dev/null +++ b/config/awesome/rc.lua @@ -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" diff --git a/configuration/bindings/client_keybindings.lua b/configuration/bindings/client_keybindings.lua deleted file mode 100644 index 1b25a43..0000000 --- a/configuration/bindings/client_keybindings.lua +++ /dev/null @@ -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 diff --git a/configuration/bindings/client_mousebindings.lua b/configuration/bindings/client_mousebindings.lua deleted file mode 100644 index 8817c86..0000000 --- a/configuration/bindings/client_mousebindings.lua +++ /dev/null @@ -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 diff --git a/configuration/bindings/global_keybindings.lua b/configuration/bindings/global_keybindings.lua deleted file mode 100644 index f66e589..0000000 --- a/configuration/bindings/global_keybindings.lua +++ /dev/null @@ -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 diff --git a/configuration/bindings/global_mousebindings.lua b/configuration/bindings/global_mousebindings.lua deleted file mode 100644 index eac992f..0000000 --- a/configuration/bindings/global_mousebindings.lua +++ /dev/null @@ -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 diff --git a/configuration/bindings/init.lua b/configuration/bindings/init.lua deleted file mode 100644 index 9ce68b2..0000000 --- a/configuration/bindings/init.lua +++ /dev/null @@ -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 diff --git a/configuration/bindings/utils.lua b/configuration/bindings/utils.lua deleted file mode 100644 index 584e52a..0000000 --- a/configuration/bindings/utils.lua +++ /dev/null @@ -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, -} diff --git a/configuration/init.lua b/configuration/init.lua deleted file mode 100644 index 6c84ac8..0000000 --- a/configuration/init.lua +++ /dev/null @@ -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 diff --git a/configuration/menu/init.lua b/configuration/menu/init.lua deleted file mode 100644 index ee9131a..0000000 --- a/configuration/menu/init.lua +++ /dev/null @@ -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 diff --git a/configuration/menu/myawesomemenu.lua b/configuration/menu/myawesomemenu.lua deleted file mode 100644 index 0bc8b37..0000000 --- a/configuration/menu/myawesomemenu.lua +++ /dev/null @@ -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 diff --git a/configuration/menu/mymainmenu.lua b/configuration/menu/mymainmenu.lua deleted file mode 100644 index 5062750..0000000 --- a/configuration/menu/mymainmenu.lua +++ /dev/null @@ -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 diff --git a/configuration/prompt_commands.lua b/configuration/prompt_commands.lua deleted file mode 100644 index 0bd7a15..0000000 --- a/configuration/prompt_commands.lua +++ /dev/null @@ -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 diff --git a/configuration/rules/client/firefox.lua b/configuration/rules/client/firefox.lua deleted file mode 100644 index 35df7a3..0000000 --- a/configuration/rules/client/firefox.lua +++ /dev/null @@ -1,7 +0,0 @@ -local firefox_rule = { - id = "firefox", - rule = { class = "Firefox" }, - properties = { screen = 1, tag = "2" }, -} - -return firefox_rule diff --git a/configuration/rules/client/floating.lua b/configuration/rules/client/floating.lua deleted file mode 100644 index 239820e..0000000 --- a/configuration/rules/client/floating.lua +++ /dev/null @@ -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 diff --git a/configuration/rules/client/global.lua b/configuration/rules/client/global.lua deleted file mode 100644 index 5d812c4..0000000 --- a/configuration/rules/client/global.lua +++ /dev/null @@ -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 diff --git a/configuration/rules/client/init.lua b/configuration/rules/client/init.lua deleted file mode 100644 index f0bb4d7..0000000 --- a/configuration/rules/client/init.lua +++ /dev/null @@ -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 diff --git a/configuration/rules/client/titlebars.lua b/configuration/rules/client/titlebars.lua deleted file mode 100644 index 99e4d1b..0000000 --- a/configuration/rules/client/titlebars.lua +++ /dev/null @@ -1,9 +0,0 @@ -local titlebar_rule = { - id = "titlebars", - rule_any = { - type = { "normal", "dialog" }, - }, - properties = { titlebars_enabled = true }, -} - -return titlebar_rule diff --git a/configuration/rules/init.lua b/configuration/rules/init.lua deleted file mode 100644 index 02d2a8f..0000000 --- a/configuration/rules/init.lua +++ /dev/null @@ -1,6 +0,0 @@ -local rules = {} - -rules.client = require "rc.configuration.rules.client" -rules.notification = require "rc.configuration.rules.notification" - -return rules diff --git a/configuration/rules/notification/global.lua b/configuration/rules/notification/global.lua deleted file mode 100644 index 21f2c6f..0000000 --- a/configuration/rules/notification/global.lua +++ /dev/null @@ -1,11 +0,0 @@ -local ascreen = require "awful.screen" - -local global_rule = { - rule = {}, - properties = { - screen = ascreen.preferred, - implicit_timeout = 5, - }, -} - -return global_rule diff --git a/configuration/rules/notification/init.lua b/configuration/rules/notification/init.lua deleted file mode 100644 index 36f35c1..0000000 --- a/configuration/rules/notification/init.lua +++ /dev/null @@ -1,5 +0,0 @@ -local notification_rules = {} - -notification_rules.global = require "rc.configuration.rules.notification.global" - -return notification_rules diff --git a/configuration/tag_layouts.lua b/configuration/tag_layouts.lua deleted file mode 100644 index 5fbbea2..0000000 --- a/configuration/tag_layouts.lua +++ /dev/null @@ -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 diff --git a/init.lua b/init.lua deleted file mode 100644 index 00fba18..0000000 --- a/init.lua +++ /dev/null @@ -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!", -} diff --git a/scripts/run.sh b/scripts/run.sh new file mode 100755 index 0000000..acf2c20 --- /dev/null +++ b/scripts/run.sh @@ -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 diff --git a/slots/init.lua b/slots/init.lua deleted file mode 100644 index 59056bf..0000000 --- a/slots/init.lua +++ /dev/null @@ -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 diff --git a/spec/default_spec.lua b/spec/default_spec.lua new file mode 100644 index 0000000..dcf5eb0 --- /dev/null +++ b/spec/default_spec.lua @@ -0,0 +1,5 @@ +describe("default", function() + it("should work", function() + assert.are.equal(1, 1) + end) +end) diff --git a/src/MyTagListWidget/README.md b/src/MyTagListWidget/README.md new file mode 100644 index 0000000..0117394 --- /dev/null +++ b/src/MyTagListWidget/README.md @@ -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). diff --git a/src/MyTagListWidget/TagItem_widget.lua b/src/MyTagListWidget/TagItem_widget.lua new file mode 100644 index 0000000..06f5da3 --- /dev/null +++ b/src/MyTagListWidget/TagItem_widget.lua @@ -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 diff --git a/src/MyTagListWidget/init.lua b/src/MyTagListWidget/init.lua new file mode 100644 index 0000000..80f27ab --- /dev/null +++ b/src/MyTagListWidget/init.lua @@ -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 diff --git a/src/MyTagListWidget/mockup.lua b/src/MyTagListWidget/mockup.lua new file mode 100644 index 0000000..689913e --- /dev/null +++ b/src/MyTagListWidget/mockup.lua @@ -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. +-- +--
+If you would like to show your appreciation for this project,
please consider a donation :)
+
+
+
+
+**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)
+
+
+
diff --git a/src/awesome-wm-nice/colors.lua b/src/awesome-wm-nice/colors.lua
new file mode 100644
index 0000000..f29096b
--- /dev/null
+++ b/src/awesome-wm-nice/colors.lua
@@ -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,
+}
diff --git a/src/awesome-wm-nice/config.ld b/src/awesome-wm-nice/config.ld
new file mode 100644
index 0000000..2484d5d
--- /dev/null
+++ b/src/awesome-wm-nice/config.ld
@@ -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")
diff --git a/src/awesome-wm-nice/config.lua b/src/awesome-wm-nice/config.lua
new file mode 100644
index 0000000..38290ca
--- /dev/null
+++ b/src/awesome-wm-nice/config.lua
@@ -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)
diff --git a/src/awesome-wm-nice/init.lua b/src/awesome-wm-nice/init.lua
new file mode 100644
index 0000000..33fd1ac
--- /dev/null
+++ b/src/awesome-wm-nice/init.lua
@@ -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,
+})
diff --git a/src/awesome-wm-nice/shade.lua b/src/awesome-wm-nice/shade.lua
new file mode 100644
index 0000000..4c37de7
--- /dev/null
+++ b/src/awesome-wm-nice/shade.lua
@@ -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
diff --git a/src/awesome-wm-nice/shapes.lua b/src/awesome-wm-nice/shapes.lua
new file mode 100644
index 0000000..294f5b9
--- /dev/null
+++ b/src/awesome-wm-nice/shapes.lua
@@ -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,
+}
diff --git a/stylua.toml b/src/awesome-wm-nice/stylua.toml
similarity index 100%
rename from stylua.toml
rename to src/awesome-wm-nice/stylua.toml
diff --git a/src/awesome-wm-nice/table.lua b/src/awesome-wm-nice/table.lua
new file mode 100644
index 0000000..7305b7f
--- /dev/null
+++ b/src/awesome-wm-nice/table.lua
@@ -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 }
diff --git a/src/awesome-wm-nice/utils.lua b/src/awesome-wm-nice/utils.lua
new file mode 100644
index 0000000..d4b781d
--- /dev/null
+++ b/src/awesome-wm-nice/utils.lua
@@ -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
diff --git a/src/awesome-wm-nice/widgets.lua b/src/awesome-wm-nice/widgets.lua
new file mode 100644
index 0000000..9ce3450
--- /dev/null
+++ b/src/awesome-wm-nice/widgets.lua
@@ -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 = ("%s"):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
diff --git a/configuration/applications.lua b/src/awesomerc/configuration/applications.lua
similarity index 64%
rename from configuration/applications.lua
rename to src/awesomerc/configuration/applications.lua
index bf4cc29..d10204f 100644
--- a/configuration/applications.lua
+++ b/src/awesomerc/configuration/applications.lua
@@ -7,15 +7,15 @@ applications.browser = "firefox-nightly"
applications.web = "qutebrowser"
applications.open_terminal = function()
- return applications.terminal .. " -e tmux"
+ return applications.terminal .. " -e tmux"
end
applications.open_editor = function(file)
- return applications.terminal .. " -e " .. applications.editor .. " " .. file
+ return applications.terminal .. " -e " .. applications.editor .. " " .. file
end
applications.open_man = function(file)
- return applications.terminal .. " -e man " .. file
+ return applications.terminal .. " -e man " .. file
end
return applications
diff --git a/src/awesomerc/configuration/bindings/client_keybindings.lua b/src/awesomerc/configuration/bindings/client_keybindings.lua
new file mode 100644
index 0000000..02ed748
--- /dev/null
+++ b/src/awesomerc/configuration/bindings/client_keybindings.lua
@@ -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
diff --git a/src/awesomerc/configuration/bindings/client_mousebindings.lua b/src/awesomerc/configuration/bindings/client_mousebindings.lua
new file mode 100644
index 0000000..ee5776e
--- /dev/null
+++ b/src/awesomerc/configuration/bindings/client_mousebindings.lua
@@ -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
diff --git a/src/awesomerc/configuration/bindings/global_keybindings.lua b/src/awesomerc/configuration/bindings/global_keybindings.lua
new file mode 100644
index 0000000..216bd25
--- /dev/null
+++ b/src/awesomerc/configuration/bindings/global_keybindings.lua
@@ -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
diff --git a/src/awesomerc/configuration/bindings/global_mousebindings.lua b/src/awesomerc/configuration/bindings/global_mousebindings.lua
new file mode 100644
index 0000000..de11e88
--- /dev/null
+++ b/src/awesomerc/configuration/bindings/global_mousebindings.lua
@@ -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
diff --git a/src/awesomerc/configuration/bindings/init.lua b/src/awesomerc/configuration/bindings/init.lua
new file mode 100644
index 0000000..0ccff30
--- /dev/null
+++ b/src/awesomerc/configuration/bindings/init.lua
@@ -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
diff --git a/src/awesomerc/configuration/bindings/utils.lua b/src/awesomerc/configuration/bindings/utils.lua
new file mode 100644
index 0000000..c2d3f76
--- /dev/null
+++ b/src/awesomerc/configuration/bindings/utils.lua
@@ -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,
+}
diff --git a/src/awesomerc/configuration/init.lua b/src/awesomerc/configuration/init.lua
new file mode 100644
index 0000000..16178e4
--- /dev/null
+++ b/src/awesomerc/configuration/init.lua
@@ -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
diff --git a/src/awesomerc/configuration/menu/init.lua b/src/awesomerc/configuration/menu/init.lua
new file mode 100644
index 0000000..baa692a
--- /dev/null
+++ b/src/awesomerc/configuration/menu/init.lua
@@ -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
diff --git a/src/awesomerc/configuration/menu/myawesomemenu.lua b/src/awesomerc/configuration/menu/myawesomemenu.lua
new file mode 100644
index 0000000..099576b
--- /dev/null
+++ b/src/awesomerc/configuration/menu/myawesomemenu.lua
@@ -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
diff --git a/src/awesomerc/configuration/menu/mymainmenu.lua b/src/awesomerc/configuration/menu/mymainmenu.lua
new file mode 100644
index 0000000..95a5010
--- /dev/null
+++ b/src/awesomerc/configuration/menu/mymainmenu.lua
@@ -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
diff --git a/src/awesomerc/configuration/prompt_commands.lua b/src/awesomerc/configuration/prompt_commands.lua
new file mode 100644
index 0000000..bbd45b3
--- /dev/null
+++ b/src/awesomerc/configuration/prompt_commands.lua
@@ -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
diff --git a/src/awesomerc/configuration/rules/client/firefox.lua b/src/awesomerc/configuration/rules/client/firefox.lua
new file mode 100644
index 0000000..0954049
--- /dev/null
+++ b/src/awesomerc/configuration/rules/client/firefox.lua
@@ -0,0 +1,7 @@
+local firefox_rule = {
+ id = "firefox",
+ rule = { class = "Firefox" },
+ properties = { screen = 1, tag = "2" },
+}
+
+return firefox_rule
diff --git a/src/awesomerc/configuration/rules/client/floating.lua b/src/awesomerc/configuration/rules/client/floating.lua
new file mode 100644
index 0000000..f307fa3
--- /dev/null
+++ b/src/awesomerc/configuration/rules/client/floating.lua
@@ -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
diff --git a/src/awesomerc/configuration/rules/client/global.lua b/src/awesomerc/configuration/rules/client/global.lua
new file mode 100644
index 0000000..f8dee74
--- /dev/null
+++ b/src/awesomerc/configuration/rules/client/global.lua
@@ -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
diff --git a/src/awesomerc/configuration/rules/client/init.lua b/src/awesomerc/configuration/rules/client/init.lua
new file mode 100644
index 0000000..eb81f5f
--- /dev/null
+++ b/src/awesomerc/configuration/rules/client/init.lua
@@ -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
diff --git a/src/awesomerc/configuration/rules/client/titlebars.lua b/src/awesomerc/configuration/rules/client/titlebars.lua
new file mode 100644
index 0000000..631d885
--- /dev/null
+++ b/src/awesomerc/configuration/rules/client/titlebars.lua
@@ -0,0 +1,9 @@
+local titlebar_rule = {
+ id = "titlebars",
+ rule_any = {
+ type = { "normal", "dialog" },
+ },
+ properties = { titlebars_enabled = true },
+}
+
+return titlebar_rule
diff --git a/src/awesomerc/configuration/rules/init.lua b/src/awesomerc/configuration/rules/init.lua
new file mode 100644
index 0000000..962edb0
--- /dev/null
+++ b/src/awesomerc/configuration/rules/init.lua
@@ -0,0 +1,6 @@
+local rules = {}
+
+rules.client = require "awesomerc.configuration.rules.client"
+rules.notification = require "awesomerc.configuration.rules.notification"
+
+return rules
diff --git a/src/awesomerc/configuration/rules/notification/global.lua b/src/awesomerc/configuration/rules/notification/global.lua
new file mode 100644
index 0000000..1e3149f
--- /dev/null
+++ b/src/awesomerc/configuration/rules/notification/global.lua
@@ -0,0 +1,11 @@
+local ascreen = require "awful.screen"
+
+local global_rule = {
+ rule = {},
+ properties = {
+ screen = ascreen.preferred,
+ implicit_timeout = 5,
+ },
+}
+
+return global_rule
diff --git a/src/awesomerc/configuration/rules/notification/init.lua b/src/awesomerc/configuration/rules/notification/init.lua
new file mode 100644
index 0000000..b31518c
--- /dev/null
+++ b/src/awesomerc/configuration/rules/notification/init.lua
@@ -0,0 +1,5 @@
+local notification_rules = {}
+
+notification_rules.global = require "awesomerc.configuration.rules.notification.global"
+
+return notification_rules
diff --git a/src/awesomerc/configuration/tag_layouts.lua b/src/awesomerc/configuration/tag_layouts.lua
new file mode 100644
index 0000000..46776be
--- /dev/null
+++ b/src/awesomerc/configuration/tag_layouts.lua
@@ -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
diff --git a/src/awesomerc/init.lua b/src/awesomerc/init.lua
new file mode 100644
index 0000000..8ea0644
--- /dev/null
+++ b/src/awesomerc/init.lua
@@ -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)
diff --git a/src/awesomerc/slots/init.lua b/src/awesomerc/slots/init.lua
new file mode 100644
index 0000000..4d95cbf
--- /dev/null
+++ b/src/awesomerc/slots/init.lua
@@ -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
diff --git a/theme/init.lua b/src/awesomerc/theme/init.lua
similarity index 82%
rename from theme/init.lua
rename to src/awesomerc/theme/init.lua
index 0b5235e..9bf5b82 100644
--- a/theme/init.lua
+++ b/src/awesomerc/theme/init.lua
@@ -1,17 +1,18 @@
local gfs = require "gears.filesystem"
local config_dir = gfs.get_configuration_dir()
-local theme_dir = config_dir .. "theme/"
-local assets_dir = theme_dir .. "assets/"
+local assets_dir = config_dir .. "assets/"
local icons_dir = assets_dir .. "icons/"
local theme = {}
--- Basic
theme.font = "Noto Mono 9"
+theme.border_width = 0
---- Tags
-theme.hometag_master_width_factor = 0.65
+--- Clients
+theme.client_corner_radius_top = 9
+theme.client_corner_radius_bottom = 4
--- Icons
theme.icon_hometag = icons_dir .. "home-circle.svg"
@@ -32,7 +33,7 @@ theme.wibar_bg = "#0000"
--- Spacing and margins
-- Naming convention:
---