diff --git a/awesome/.config/awesome/edges-config.lua b/awesome/.config/awesome/edges-config.lua new file mode 100644 index 0000000..373b632 --- /dev/null +++ b/awesome/.config/awesome/edges-config.lua @@ -0,0 +1,76 @@ +----------------------------------------------------------------------------------------------------------------------- +-- Active screen edges config -- +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +local awful = require("awful") +local redflat = require("redflat") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local edges = {} + +local switcher = redflat.float.appswitcher +local currenttags = redflat.widget.tasklist.filter.currenttags +local allscreen = redflat.widget.tasklist.filter.allscreen + +-- Active screen edges +----------------------------------------------------------------------------------------------------------------------- +function edges:init(args) + + args = args or {} + local ew = args.width or 1 -- edge width + local workarea = args.workarea or screen[mouse.screen].workarea + + -- edge geometry + local egeometry = { + top = { width = workarea.width - 2 * ew, height = ew , x = ew, y = 0 }, + right = { width = ew, height = workarea.height, x = workarea.width - ew, y = 0 }, + left = { width = ew, height = workarea.height, x = 0, y = 0 } + } + + -- Top + -------------------------------------------------------------------------------- + local top = redflat.util.desktop.edge("horizontal") + top.wibox:geometry(egeometry["top"]) + + top.layout:buttons(awful.util.table.join( + awful.button({}, 1, + function() + if client.focus then client.focus.maximized = not client.focus.maximized end + end + ) + )) + + -- Right + -------------------------------------------------------------------------------- + local right = redflat.util.desktop.edge("vertical") + right.wibox:geometry(egeometry["right"]) + + right.layout:buttons(awful.util.table.join( + awful.button({}, 5, function() awful.tag.viewnext(mouse.screen) end), + awful.button({}, 4, function() awful.tag.viewprev(mouse.screen) end) + )) + + -- Left + -------------------------------------------------------------------------------- + local left = redflat.util.desktop.edge("vertical", { ew, workarea.height - ew }) + left.wibox:geometry(egeometry["left"]) + + left.area[1]:buttons(awful.util.table.join( + awful.button({}, 4, function() switcher:show({ filter = allscreen }) end), + awful.button({}, 5, function() switcher:show({ filter = allscreen, reverse = true }) end) + )) + + left.area[2]:buttons(awful.util.table.join( + awful.button({}, 9, function() if client.focus then client.focus.minimized = true end end), + awful.button({}, 4, function() switcher:show({ filter = currenttags }) end), + awful.button({}, 5, function() switcher:show({ filter = currenttags, reverse = true }) end) + )) + + left.wibox:connect_signal("mouse::leave", function() redflat.float.appswitcher:hide() end) +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return edges diff --git a/awesome/.config/awesome/env-config.lua b/awesome/.config/awesome/env-config.lua new file mode 100644 index 0000000..acc792e --- /dev/null +++ b/awesome/.config/awesome/env-config.lua @@ -0,0 +1,100 @@ +----------------------------------------------------------------------------------------------------------------------- +-- Environment config -- +----------------------------------------------------------------------------------------------------------------------- + +local awful = require("awful") +local gears = require("gears") +local beautiful = require("beautiful") +local wibox = require("wibox") +local naughty = require("naughty") + +local redflat = require("redflat") + +local unpack = unpack or table.unpack + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local env = {} + +-- Build hotkeys depended on config parameters +----------------------------------------------------------------------------------------------------------------------- +function env:init(args) + + -- init vars + args = args or {} + + -- environment vars + self.terminal = args.terminal or "termite" + self.editor = os.getenv("EDITOR") or "vim" + self.editor_cmd = self.terminal .. " -e " .. self.editor + + self.mod = args.mod or "Mod4" + self.fm = args.fm or "nemo" + self.mail = args.mail or "thunderbird" + self.player = args.player or self.terminal .. "-e ncmpcpp" + self.updates = args.updates or "bash -c 'pacman -Qu | grep -v ignored | wc -l'" + self.home = os.getenv("HOME") + self.themedir = awful.util.get_configuration_dir() .. "themes/default" + + -- boolean defaults is pain + self.sloppy_focus = args.sloppy_focus or false + self.color_border_focus = args.color_border_focus or false + self.set_slave = args.set_slave == nil and true or false + self.set_center = args.set_center or false + self.desktop_autohide = args.desktop_autohide or false + + -- theme setup + beautiful.init(env.themedir .. "/theme.lua") + beautiful.useless_gap = 5 + -- naughty config + naughty.config.padding = beautiful.useless_gap and 2 * beautiful.useless_gap or 0 + + if beautiful.naughty then + naughty.config.presets.normal = redflat.util.table.merge(beautiful.naughty.base, beautiful.naughty.normal) + naughty.config.presets.critical = redflat.util.table.merge(beautiful.naughty.base, beautiful.naughty.critical) + naughty.config.presets.low = redflat.util.table.merge(beautiful.naughty.base, beautiful.naughty.low) + end +end + + +-- Common functions +----------------------------------------------------------------------------------------------------------------------- + +-- Wallpaper setup +-------------------------------------------------------------------------------- +env.wallpaper = function(s) + if beautiful.wallpaper then + if not env.desktop_autohide and awful.util.file_readable(beautiful.wallpaper) then + gears.wallpaper.maximized(beautiful.wallpaper, s, true) + else + gears.wallpaper.set(beautiful.color and beautiful.color.bg) + end + end +end + +-- Tag tooltip text generation +-------------------------------------------------------------------------------- +env.tagtip = function(t) + local layname = awful.layout.getname(awful.tag.getproperty(t, "layout")) + if redflat.util.table.check(beautiful, "widget.layoutbox.name_alias") then + layname = beautiful.widget.layoutbox.name_alias[layname] or layname + end + return string.format("%s (%d apps) [%s]", t.name, #(t:clients()), layname) +end + +-- Panel widgets wrapper +-------------------------------------------------------------------------------- +env.wrapper = function(widget, name, buttons) + local margin = redflat.util.table.check(beautiful, "widget.wrapper") + and beautiful.widget.wrapper[name] or { 0, 0, 0, 0 } + if buttons then + widget:buttons(buttons) + end + + return wibox.container.margin(widget, unpack(margin)) +end + + +-- End +----------------------------------------------------------------------------------------------------------------------- +return env diff --git a/awesome/.config/awesome/ercheck-config.lua b/awesome/.config/awesome/ercheck-config.lua new file mode 100644 index 0000000..e7e9c54 --- /dev/null +++ b/awesome/.config/awesome/ercheck-config.lua @@ -0,0 +1,27 @@ +local naughty = require("naughty") + +if awesome.startup_errors then + naughty.notify({ + preset = naughty.config.presets.critical, + title = "Oops, there were errors during startup!", + text = awesome.startup_error + }) +end + +do + local in_error = false + awesome.connect_signal( + "debug::error", + function (err) + if in_error then return end + in_error = true + + naughty.notify({ + preset = naughty.config.presets.critical, + title = "Oops, an error happened!", + text = err + }) + in_error = false + end + ) +end diff --git a/awesome/.config/awesome/keys-config.lua b/awesome/.config/awesome/keys-config.lua new file mode 100644 index 0000000..42a76dc --- /dev/null +++ b/awesome/.config/awesome/keys-config.lua @@ -0,0 +1,871 @@ +----------------------------------------------------------------------------------------------------------------------- +-- Hotkeys and mouse buttons config -- +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +local table = table +local awful = require("awful") +local redflat = require("redflat") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local hotkeys = { mouse = {}, raw = {}, keys = {}, fake = {} } + +-- key aliases +local apprunner = redflat.float.apprunner +local appswitcher = redflat.float.appswitcher +local current = redflat.widget.tasklist.filter.currenttags +local allscr = redflat.widget.tasklist.filter.allscreen +local laybox = redflat.widget.layoutbox +local redtip = redflat.float.hotkeys +local laycom = redflat.layout.common +local grid = redflat.layout.grid +local map = redflat.layout.map +local redtitle = redflat.titlebar +local qlaunch = redflat.float.qlaunch + +-- Key support functions +----------------------------------------------------------------------------------------------------------------------- + +-- change window focus by history +local function focus_to_previous() + awful.client.focus.history.previous() + if client.focus then client.focus:raise() end +end + +-- change window focus by direction +local focus_switch_byd = function(dir) + return function() + awful.client.focus.bydirection(dir) + if client.focus then client.focus:raise() end + end +end + +-- minimize and restore windows +local function minimize_all() + for _, c in ipairs(client.get()) do + if current(c, mouse.screen) then c.minimized = true end + end +end + +local function minimize_all_except_focused() + for _, c in ipairs(client.get()) do + if current(c, mouse.screen) and c ~= client.focus then c.minimized = true end + end +end + +local function restore_all() + for _, c in ipairs(client.get()) do + if current(c, mouse.screen) and c.minimized then c.minimized = false end + end +end + +local function restore_client() + local c = awful.client.restore() + if c then client.focus = c; c:raise() end +end + +-- close window +local function kill_all() + for _, c in ipairs(client.get()) do + if current(c, mouse.screen) and not c.sticky then c:kill() end + end +end + +-- new clients placement +local function toggle_placement(env) + env.set_slave = not env.set_slave + redflat.float.notify:show({ text = (env.set_slave and "Slave" or "Master") .. " placement" }) +end + +-- numeric keys function builders +local function tag_numkey(i, mod, action) + return awful.key( + mod, "#" .. i + 9, + function () + local screen = awful.screen.focused() + local tag = screen.tags[i] + if tag then action(tag) end + end + ) +end + +local function client_numkey(i, mod, action) + return awful.key( + mod, "#" .. i + 9, + function () + if client.focus then + local tag = client.focus.screen.tags[i] + if tag then action(tag) end + end + end + ) +end + +-- brightness functions +local brightness = function(args) + redflat.float.brightness:change_with_xbacklight(args) -- use xbacklight +end + +-- right bottom corner position +local rb_corner = function() + return { x = screen[mouse.screen].workarea.x + screen[mouse.screen].workarea.width, + y = screen[mouse.screen].workarea.y + screen[mouse.screen].workarea.height } +end + +-- Build hotkeys depended on config parameters +----------------------------------------------------------------------------------------------------------------------- +function hotkeys:init(args) + + -- Init vars + args = args or {} + local env = args.env + local volume = args.volume + local mainmenu = args.menu + local appkeys = args.appkeys or {} + + self.mouse.root = (awful.util.table.join( + awful.button({ }, 3, function () mainmenu:toggle() end), + awful.button({ }, 4, awful.tag.viewnext), + awful.button({ }, 5, awful.tag.viewprev) + )) + + -- volume functions + local volume_raise = function() volume:change_volume({ show_notify = true }) end + local volume_lower = function() volume:change_volume({ show_notify = true, down = true }) end + local volume_mute = function() volume:mute() end + + -- Init widgets + redflat.float.qlaunch:init() + + -- Application hotkeys helper + -------------------------------------------------------------------------------- + local apphelper = function(keys) + if not client.focus then return end + + local app = client.focus.class:lower() + for name, sheet in pairs(keys) do + if name == app then + redtip:set_pack( + client.focus.class, sheet.pack, sheet.style.column, sheet.style.geometry, + function() redtip:remove_pack() end + ) + redtip:show() + return + end + end + + redflat.float.notify:show({ text = "No tips for " .. client.focus.class }) + end + + -- Keys for widgets + -------------------------------------------------------------------------------- + + -- Apprunner widget + ------------------------------------------------------------ + local apprunner_keys_move = { + { + { env.mod }, "k", function() apprunner:down() end, + { description = "Select next item", group = "Navigation" } + }, + { + { env.mod }, "i", function() apprunner:up() end, + { description = "Select previous item", group = "Navigation" } + }, + } + + -- apprunner:set_keys(awful.util.table.join(apprunner.keys.move, apprunner_keys_move), "move") + apprunner:set_keys(apprunner_keys_move, "move") + + -- Menu widget + ------------------------------------------------------------ + local menu_keys_move = { + { + { env.mod }, "k", redflat.menu.action.down, + { description = "Select next item", group = "Navigation" } + }, + { + { env.mod }, "i", redflat.menu.action.up, + { description = "Select previous item", group = "Navigation" } + }, + { + { env.mod }, "j", redflat.menu.action.back, + { description = "Go back", group = "Navigation" } + }, + { + { env.mod }, "l", redflat.menu.action.enter, + { description = "Open submenu", group = "Navigation" } + }, + } + + -- redflat.menu:set_keys(awful.util.table.join(redflat.menu.keys.move, menu_keys_move), "move") + redflat.menu:set_keys(menu_keys_move, "move") + + -- Appswitcher widget + ------------------------------------------------------------ + local appswitcher_keys = { + { + { env.mod }, "a", function() appswitcher:switch() end, + { description = "Select next app", group = "Navigation" } + }, + { + { env.mod, "Shift" }, "a", function() appswitcher:switch() end, + {} -- hidden key + }, + { + { env.mod }, "q", function() appswitcher:switch({ reverse = true }) end, + { description = "Select previous app", group = "Navigation" } + }, + { + { env.mod, "Shift" }, "q", function() appswitcher:switch({ reverse = true }) end, + {} -- hidden key + }, + { + {}, "Super_L", function() appswitcher:hide() end, + { description = "Activate and exit", group = "Action" } + }, + { + { env.mod }, "Super_L", function() appswitcher:hide() end, + {} -- hidden key + }, + { + { env.mod, "Shift" }, "Super_L", function() appswitcher:hide() end, + {} -- hidden key + }, + { + {}, "Return", function() appswitcher:hide() end, + { description = "Activate and exit", group = "Action" } + }, + { + {}, "Escape", function() appswitcher:hide(true) end, + { description = "Exit", group = "Action" } + }, + { + { env.mod }, "Escape", function() appswitcher:hide(true) end, + {} -- hidden key + }, + { + { env.mod }, "F1", function() redtip:show() end, + { description = "Show hotkeys helper", group = "Action" } + }, + } + + appswitcher:set_keys(appswitcher_keys) + + -- Emacs like key sequences + -------------------------------------------------------------------------------- + + -- initial key + local keyseq = { { env.mod }, "c", {}, {} } + + -- group + keyseq[3] = { + { {}, "k", {}, {} }, -- application kill group + { {}, "c", {}, {} }, -- client managment group + { {}, "r", {}, {} }, -- client managment group + { {}, "n", {}, {} }, -- client managment group + { {}, "g", {}, {} }, -- run or rise group + { {}, "f", {}, {} }, -- launch application group + } + + -- quick launch key sequence actions + for i = 1, 9 do + local ik = tostring(i) + table.insert(keyseq[3][5][3], { + {}, ik, function() qlaunch:run_or_raise(ik) end, + { description = "Run or rise application №" .. ik, group = "Run or Rise", keyset = { ik } } + }) + table.insert(keyseq[3][6][3], { + {}, ik, function() qlaunch:run_or_raise(ik, true) end, + { description = "Launch application №".. ik, group = "Quick Launch", keyset = { ik } } + }) + end + + -- application kill sequence actions + keyseq[3][1][3] = { + { + {}, "f", function() if client.focus then client.focus:kill() end end, + { description = "Kill focused client", group = "Kill application", keyset = { "f" } } + }, + { + {}, "a", kill_all, + { description = "Kill all clients with current tag", group = "Kill application", keyset = { "a" } } + }, + } + + -- client managment sequence actions + keyseq[3][2][3] = { + { + {}, "p", function () toggle_placement(env) end, + { description = "Switch master/slave window placement", group = "Clients managment", keyset = { "p" } } + }, + } + + keyseq[3][3][3] = { + { + {}, "f", restore_client, + { description = "Restore minimized client", group = "Clients managment", keyset = { "f" } } + }, + { + {}, "a", restore_all, + { description = "Restore all clients with current tag", group = "Clients managment", keyset = { "a" } } + }, + } + + keyseq[3][4][3] = { + { + {}, "f", function() if client.focus then client.focus.minimized = true end end, + { description = "Minimized focused client", group = "Clients managment", keyset = { "f" } } + }, + { + {}, "a", minimize_all, + { description = "Minimized all clients with current tag", group = "Clients managment", keyset = { "a" } } + }, + { + {}, "e", minimize_all_except_focused, + { description = "Minimized all clients except focused", group = "Clients managment", keyset = { "e" } } + }, + } + + + -- Layouts + -------------------------------------------------------------------------------- + + -- shared layout keys + local layout_tile = { + { + { env.mod }, "l", function () awful.tag.incmwfact( 0.05) end, + { description = "Increase master width factor", group = "Layout" } + }, + { + { env.mod }, "j", function () awful.tag.incmwfact(-0.05) end, + { description = "Decrease master width factor", group = "Layout" } + }, + { + { env.mod }, "i", function () awful.client.incwfact( 0.05) end, + { description = "Increase window factor of a client", group = "Layout" } + }, + { + { env.mod }, "k", function () awful.client.incwfact(-0.05) end, + { description = "Decrease window factor of a client", group = "Layout" } + }, + { + { env.mod, }, "+", function () awful.tag.incnmaster( 1, nil, true) end, + { description = "Increase the number of master clients", group = "Layout" } + }, + { + { env.mod }, "-", function () awful.tag.incnmaster(-1, nil, true) end, + { description = "Decrease the number of master clients", group = "Layout" } + }, + { + { env.mod, "Control" }, "+", function () awful.tag.incncol( 1, nil, true) end, + { description = "Increase the number of columns", group = "Layout" } + }, + { + { env.mod, "Control" }, "-", function () awful.tag.incncol(-1, nil, true) end, + { description = "Decrease the number of columns", group = "Layout" } + }, + } + + laycom:set_keys(layout_tile, "tile") + + -- grid layout keys + local layout_grid_move = { + { + { env.mod }, "KP_Up", function() grid.move_to("up") end, + { description = "Move window up", group = "Movement" } + }, + { + { env.mod }, "KP_Down", function() grid.move_to("down") end, + { description = "Move window down", group = "Movement" } + }, + { + { env.mod }, "KP_Left", function() grid.move_to("left") end, + { description = "Move window left", group = "Movement" } + }, + { + { env.mod }, "KP_right", function() grid.move_to("right") end, + { description = "Move window right", group = "Movement" } + }, + { + { env.mod, "Control" }, "KP_Up", function() grid.move_to("up", true) end, + { description = "Move window up by bound", group = "Movement" } + }, + { + { env.mod, "Control" }, "KP_Down", function() grid.move_to("down", true) end, + { description = "Move window down by bound", group = "Movement" } + }, + { + { env.mod, "Control" }, "KP_Left", function() grid.move_to("left", true) end, + { description = "Move window left by bound", group = "Movement" } + }, + { + { env.mod, "Control" }, "KP_Right", function() grid.move_to("right", true) end, + { description = "Move window right by bound", group = "Movement" } + }, + } + + local layout_grid_resize = { + { + { env.mod }, "i", function() grid.resize_to("up") end, + { description = "Inrease window size to the up", group = "Resize" } + }, + { + { env.mod }, "k", function() grid.resize_to("down") end, + { description = "Inrease window size to the down", group = "Resize" } + }, + { + { env.mod }, "j", function() grid.resize_to("left") end, + { description = "Inrease window size to the left", group = "Resize" } + }, + { + { env.mod }, "l", function() grid.resize_to("right") end, + { description = "Inrease window size to the right", group = "Resize" } + }, + { + { env.mod, "Shift" }, "i", function() grid.resize_to("up", nil, true) end, + { description = "Decrease window size from the up", group = "Resize" } + }, + { + { env.mod, "Shift" }, "k", function() grid.resize_to("down", nil, true) end, + { description = "Decrease window size from the down", group = "Resize" } + }, + { + { env.mod, "Shift" }, "j", function() grid.resize_to("left", nil, true) end, + { description = "Decrease window size from the left", group = "Resize" } + }, + { + { env.mod, "Shift" }, "l", function() grid.resize_to("right", nil, true) end, + { description = "Decrease window size from the right", group = "Resize" } + }, + { + { env.mod, "Control" }, "i", function() grid.resize_to("up", true) end, + { description = "Increase window size to the up by bound", group = "Resize" } + }, + { + { env.mod, "Control" }, "k", function() grid.resize_to("down", true) end, + { description = "Increase window size to the down by bound", group = "Resize" } + }, + { + { env.mod, "Control" }, "j", function() grid.resize_to("left", true) end, + { description = "Increase window size to the left by bound", group = "Resize" } + }, + { + { env.mod, "Control" }, "l", function() grid.resize_to("right", true) end, + { description = "Increase window size to the right by bound", group = "Resize" } + }, + { + { env.mod, "Control", "Shift" }, "i", function() grid.resize_to("up", true, true) end, + { description = "Decrease window size from the up by bound ", group = "Resize" } + }, + { + { env.mod, "Control", "Shift" }, "k", function() grid.resize_to("down", true, true) end, + { description = "Decrease window size from the down by bound ", group = "Resize" } + }, + { + { env.mod, "Control", "Shift" }, "j", function() grid.resize_to("left", true, true) end, + { description = "Decrease window size from the left by bound ", group = "Resize" } + }, + { + { env.mod, "Control", "Shift" }, "l", function() grid.resize_to("right", true, true) end, + { description = "Decrease window size from the right by bound ", group = "Resize" } + }, + } + + redflat.layout.grid:set_keys(layout_grid_move, "move") + redflat.layout.grid:set_keys(layout_grid_resize, "resize") + + -- user map layout keys + local layout_map_layout = { + { + { env.mod }, "s", function() map.swap_group() end, + { description = "Change placement direction for group", group = "Layout" } + }, + { + { env.mod }, "v", function() map.new_group(true) end, + { description = "Create new vertical group", group = "Layout" } + }, + { + { env.mod }, "h", function() map.new_group(false) end, + { description = "Create new horizontal group", group = "Layout" } + }, + { + { env.mod, "Control" }, "v", function() map.insert_group(true) end, + { description = "Insert new vertical group before active", group = "Layout" } + }, + { + { env.mod, "Control" }, "h", function() map.insert_group(false) end, + { description = "Insert new horizontal group before active", group = "Layout" } + }, + { + { env.mod }, "d", function() map.delete_group() end, + { description = "Destroy group", group = "Layout" } + }, + { + { env.mod, "Control" }, "d", function() map.clean_groups() end, + { description = "Destroy all empty groups", group = "Layout" } + }, + { + { env.mod }, "f", function() map.set_active() end, + { description = "Set active group", group = "Layout" } + }, + { + { env.mod }, "g", function() map.move_to_active() end, + { description = "Move focused client to active group", group = "Layout" } + }, + { + { env.mod, "Control" }, "f", function() map.hilight_active() end, + { description = "Hilight active group", group = "Layout" } + }, + { + { env.mod }, "a", function() map.switch_active(1) end, + { description = "Activate next group", group = "Layout" } + }, + { + { env.mod }, "q", function() map.switch_active(-1) end, + { description = "Activate previous group", group = "Layout" } + }, + { + { env.mod }, "]", function() map.move_group(1) end, + { description = "Move active group to the top", group = "Layout" } + }, + { + { env.mod }, "[", function() map.move_group(-1) end, + { description = "Move active group to the bottom", group = "Layout" } + }, + { + { env.mod }, "r", function() map.reset_tree() end, + { description = "Reset layout structure", group = "Layout" } + }, + } + + local layout_map_resize = { + { + { env.mod }, "j", function() map.incfactor(nil, 0.1, false) end, + { description = "Increase window horizontal size factor", group = "Resize" } + }, + { + { env.mod }, "l", function() map.incfactor(nil, -0.1, false) end, + { description = "Decrease window horizontal size factor", group = "Resize" } + }, + { + { env.mod }, "i", function() map.incfactor(nil, 0.1, true) end, + { description = "Increase window vertical size factor", group = "Resize" } + }, + { + { env.mod }, "k", function() map.incfactor(nil, -0.1, true) end, + { description = "Decrease window vertical size factor", group = "Resize" } + }, + { + { env.mod, "Control" }, "j", function() map.incfactor(nil, 0.1, false, true) end, + { description = "Increase group horizontal size factor", group = "Resize" } + }, + { + { env.mod, "Control" }, "l", function() map.incfactor(nil, -0.1, false, true) end, + { description = "Decrease group horizontal size factor", group = "Resize" } + }, + { + { env.mod, "Control" }, "i", function() map.incfactor(nil, 0.1, true, true) end, + { description = "Increase group vertical size factor", group = "Resize" } + }, + { + { env.mod, "Control" }, "k", function() map.incfactor(nil, -0.1, true, true) end, + { description = "Decrease group vertical size factor", group = "Resize" } + }, + } + + redflat.layout.map:set_keys(layout_map_layout, "layout") + redflat.layout.map:set_keys(layout_map_resize, "resize") + + + -- Global keys + -------------------------------------------------------------------------------- + self.raw.root = { + { + { env.mod }, "F1", function() redtip:show() end, + { description = "[Hold] Show awesome hotkeys helper", group = "Main" } + }, + { + { env.mod, "Control" }, "F1", function() apphelper(appkeys) end, + { description = "[Hold] Show hotkeys helper for application", group = "Main" } + }, + { + { env.mod }, "c", function() redflat.float.keychain:activate(keyseq, "User") end, + { description = "[Hold] User key sequence", group = "Main" } + }, + + { + { env.mod }, "F2", function () redflat.service.navigator:run() end, + { description = "[Hold] Tiling window control mode", group = "Window control" } + }, + { + { env.mod }, "h", function() redflat.float.control:show() end, + { description = "[Hold] Floating window control mode", group = "Window control" } + }, + + { + { env.mod }, "Return", function() awful.spawn(env.terminal) end, + { description = "Open a terminal", group = "Actions" } + }, + { + { env.mod, "Mod1" }, "space", function() awful.spawn("clipflap --show") end, + { description = "Clipboard manager", group = "Actions" } + }, + { + { env.mod, "Control" }, "r", awesome.restart, + { description = "Reload WM", group = "Actions" } + }, + + { + { env.mod }, "l", focus_switch_byd("right"), + { description = "Go to right client", group = "Client focus" } + }, + { + { env.mod }, "j", focus_switch_byd("left"), + { description = "Go to left client", group = "Client focus" } + }, + { + { env.mod }, "i", focus_switch_byd("up"), + { description = "Go to upper client", group = "Client focus" } + }, + { + { env.mod }, "k", focus_switch_byd("down"), + { description = "Go to lower client", group = "Client focus" } + }, + { + { env.mod }, "u", awful.client.urgent.jumpto, + { description = "Go to urgent client", group = "Client focus" } + }, + { + { env.mod }, "Tab", focus_to_previous, + { description = "Go to previos client", group = "Client focus" } + }, + + { + { env.mod }, "w", function() mainmenu:show() end, + { description = "Show main menu", group = "Widgets" } + }, + { + { env.mod }, "r", function() apprunner:show() end, + { description = "Application launcher", group = "Widgets" } + }, + { + { env.mod }, "p", function() redflat.float.prompt:run() end, + { description = "Show the prompt box", group = "Widgets" } + }, + { + { env.mod }, "x", function() redflat.float.top:show("cpu") end, + { description = "Show the top process list", group = "Widgets" } + }, + { + { env.mod, "Control" }, "m", function() redflat.widget.mail:update(true) end, + { description = "Check new mail", group = "Widgets" } + }, + { + { env.mod, "Control" }, "i", function() redflat.widget.minitray:toggle() end, + { description = "Show minitray", group = "Widgets" } + }, + { + { env.mod, "Control" }, "u", function() redflat.widget.updates:update(true) end, + { description = "Check available updates", group = "Widgets" } + }, + { + { env.mod }, "g", function() qlaunch:show() end, + { description = "Application quick launcher", group = "Widgets" } + }, + + { + { env.mod }, "y", function() laybox:toggle_menu(mouse.screen.selected_tag) end, + { description = "Show layout menu", group = "Layouts" } + }, + { + { env.mod}, "Up", function() awful.layout.inc(1) end, + { description = "Select next layout", group = "Layouts" } + }, + { + { env.mod }, "Down", function() awful.layout.inc(-1) end, + { description = "Select previous layout", group = "Layouts" } + }, + + { + {}, "XF86MonBrightnessUp", function() brightness({ step = 2 }) end, + { description = "Increase brightness", group = "Brightness control" } + }, + { + {}, "XF86MonBrightnessDown", function() brightness({ step = 2, down = true }) end, + { description = "Reduce brightness", group = "Brightness control" } + }, + + { + {}, "XF86AudioRaiseVolume", volume_raise, + { description = "Increase volume", group = "Volume control" } + }, + { + {}, "XF86AudioLowerVolume", volume_lower, + { description = "Reduce volume", group = "Volume control" } + }, + { + {}, "XF86AudioMute", volume_mute, + { description = "Mute audio", group = "Volume control" } + }, + + { + { env.mod }, "a", nil, function() appswitcher:show({ filter = current }) end, + { description = "Switch to next with current tag", group = "Application switcher" } + }, + { + { env.mod }, "q", nil, function() appswitcher:show({ filter = current, reverse = true }) end, + { description = "Switch to previous with current tag", group = "Application switcher" } + }, + { + { env.mod, "Shift" }, "a", nil, function() appswitcher:show({ filter = allscr }) end, + { description = "Switch to next through all tags", group = "Application switcher" } + }, + { + { env.mod, "Shift" }, "q", nil, function() appswitcher:show({ filter = allscr, reverse = true }) end, + { description = "Switch to previous through all tags", group = "Application switcher" } + }, + + { + { env.mod }, "Escape", awful.tag.history.restore, + { description = "Go previos tag", group = "Tag navigation" } + }, + { + { env.mod }, "Right", awful.tag.viewnext, + { description = "View next tag", group = "Tag navigation" } + }, + { + { env.mod }, "Left", awful.tag.viewprev, + { description = "View previous tag", group = "Tag navigation" } + }, + + { + { env.mod }, "t", function() redtitle.toggle(client.focus) end, + { description = "Show/hide titlebar for focused client", group = "Titlebar" } + }, + --{ + -- { env.mod, "Control" }, "t", function() redtitle.switch(client.focus) end, + -- { description = "Switch titlebar view for focused client", group = "Titlebar" } + --}, + { + { env.mod, "Shift" }, "t", function() redtitle.toggle_all() end, + { description = "Show/hide titlebar for all clients", group = "Titlebar" } + }, + { + { env.mod, "Control", "Shift" }, "t", function() redtitle.global_switch() end, + { description = "Switch titlebar view for all clients", group = "Titlebar" } + }, + + { + { env.mod }, "e", function() redflat.float.player:show(rb_corner()) end, + { description = "Show/hide widget", group = "Audio player" } + }, + { + {}, "XF86AudioPlay", function() redflat.float.player:action("PlayPause") end, + { description = "Play/Pause track", group = "Audio player" } + }, + { + {}, "XF86AudioNext", function() redflat.float.player:action("Next") end, + { description = "Next track", group = "Audio player" } + }, + { + {}, "XF86AudioPrev", function() redflat.float.player:action("Previous") end, + { description = "Previous track", group = "Audio player" } + }, + + { + { env.mod, "Control" }, "s", function() for s in screen do env.wallpaper(s) end end, + {} -- hidden key + } + } + + -- Client keys + -------------------------------------------------------------------------------- + self.raw.client = { + { + { env.mod }, "f", function(c) c.fullscreen = not c.fullscreen; c:raise() end, + { description = "Toggle fullscreen", group = "Client keys" } + }, + { + { env.mod }, "F4", function(c) c:kill() end, + { description = "Close", group = "Client keys" } + }, + { + { env.mod, "Control" }, "f", awful.client.floating.toggle, + { description = "Toggle floating", group = "Client keys" } + }, + { + { env.mod, "Control" }, "o", function(c) c.ontop = not c.ontop end, + { description = "Toggle keep on top", group = "Client keys" } + }, + { + { env.mod }, "n", function(c) c.minimized = true end, + { description = "Minimize", group = "Client keys" } + }, + { + { env.mod }, "m", function(c) c.maximized = not c.maximized; c:raise() end, + { description = "Maximize", group = "Client keys" } + } + } + + self.keys.root = redflat.util.key.build(self.raw.root) + self.keys.client = redflat.util.key.build(self.raw.client) + + -- Numkeys + -------------------------------------------------------------------------------- + + -- add real keys without description here + for i = 1, 9 do + self.keys.root = awful.util.table.join( + self.keys.root, + tag_numkey(i, { env.mod }, function(t) t:view_only() end), + tag_numkey(i, { env.mod, "Control" }, function(t) awful.tag.viewtoggle(t) end), + client_numkey(i, { env.mod, "Shift" }, function(t) client.focus:move_to_tag(t) end), + client_numkey(i, { env.mod, "Control", "Shift" }, function(t) client.focus:toggle_tag(t) end) + ) + end + + -- make fake keys with description special for key helper widget + local numkeys = { "1", "2", "3", "4", "5", "6", "7", "8", "9" } + + self.fake.numkeys = { + { + { env.mod }, "1..9", nil, + { description = "Switch to tag", group = "Numeric keys", keyset = numkeys } + }, + { + { env.mod, "Control" }, "1..9", nil, + { description = "Toggle tag", group = "Numeric keys", keyset = numkeys } + }, + { + { env.mod, "Shift" }, "1..9", nil, + { description = "Move focused client to tag", group = "Numeric keys", keyset = numkeys } + }, + { + { env.mod, "Control", "Shift" }, "1..9", nil, + { description = "Toggle focused client on tag", group = "Numeric keys", keyset = numkeys } + }, + } + + -- Hotkeys helper setup + -------------------------------------------------------------------------------- + redflat.float.hotkeys:set_pack("Main", awful.util.table.join(self.raw.root, self.raw.client, self.fake.numkeys), 2) + + -- Mouse buttons + -------------------------------------------------------------------------------- + self.mouse.client = awful.util.table.join( + awful.button({}, 1, function (c) client.focus = c; c:raise() end), + awful.button({}, 2, awful.mouse.client.move), + awful.button({ env.mod }, 3, awful.mouse.client.resize), + awful.button({}, 8, function(c) c:kill() end) + ) + + -- Set root hotkeys + -------------------------------------------------------------------------------- + root.keys(self.keys.root) + root.buttons(self.mouse.root) +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return hotkeys diff --git a/awesome/.config/awesome/layout-config.lua b/awesome/.config/awesome/layout-config.lua new file mode 100644 index 0000000..acb05f8 --- /dev/null +++ b/awesome/.config/awesome/layout-config.lua @@ -0,0 +1,86 @@ +----------------------------------------------------------------------------------------------------------------------- +-- Layouts config -- +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +local awful = require("awful") +local redflat = require("redflat") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local layouts = {} + + +-- Build table +----------------------------------------------------------------------------------------------------------------------- +function layouts:init() + + -- layouts list + local layset = { + awful.layout.suit.floating, + redflat.layout.grid, + awful.layout.suit.tile, + awful.layout.suit.tile.left, + awful.layout.suit.tile.bottom, + awful.layout.suit.tile.top, + awful.layout.suit.fair, + redflat.layout.map, + awful.layout.suit.max, + awful.layout.suit.max.fullscreen, + } + + awful.layout.layouts = layset +end + +-- some advanced layout settings +redflat.layout.map.notification = false + + +-- connect alternative moving handler to allow using custom handler per layout +-- by now custom handler provided for 'redflat.layout.grid' only +-- feel free to remove if you don't use this one +client.disconnect_signal("request::geometry", awful.layout.move_handler) +client.connect_signal("request::geometry", redflat.layout.common.mouse.move) + + +-- connect additional signal for 'redflat.layout.map' +-- this one removing client in smart way and correct tiling scheme +-- feel free to remove if you want to restore plain queue behavior +client.connect_signal("unmanage", redflat.layout.map.clean_client) + +client.connect_signal("property::minimized", function(c) + if c.minimized and redflat.layout.map.check_client(c) then redflat.layout.map.clean_client(c) end +end) +client.connect_signal("property::floating", function(c) + if c.floating and redflat.layout.map.check_client(c) then redflat.layout.map.clean_client(c) end +end) + +client.connect_signal("untagged", function(c, t) + if redflat.layout.map.data[t] then redflat.layout.map.clean_client(c) end +end) + + +-- user map layout preset +-- preset can be defined for individual tags, but this should be done after tag initialization + +-- redflat.layout.map.base_construct = function(wa) +-- local tree = { set = {}, active = 1, autoaim = true } + +-- tree.set[1] = redflat.layout.map.construct_itempack({}, wa, false) +-- tree.set[2] = redflat.layout.map.base_set_new_pack({}, wa, true, tree.set[1]) +-- tree.set[3] = redflat.layout.map.base_set_new_pack({}, wa, true, tree.set[1]) +-- tree.set[4] = redflat.layout.map.base_set_new_pack({}, wa, true, tree.set[1]) + +-- function tree:aim() +-- for i = 2, 4 do if #self.set[i].items == 0 then return i end end +-- local active = #self.set[4].items >= #self.set[2].items and 2 or 4 +-- return active +-- end + +-- return tree +-- end + + +-- End +----------------------------------------------------------------------------------------------------------------------- +return layouts diff --git a/awesome/.config/awesome/menu.lua b/awesome/.config/awesome/menu.lua new file mode 100644 index 0000000..b313e42 --- /dev/null +++ b/awesome/.config/awesome/menu.lua @@ -0,0 +1,96 @@ +local beautiful = require("beautiful") +local redflat = require("redflat") +local awful = require("awful") +local naughty = require("naughty") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local menu = {} + +-- Build function +-------------------------------------------------------------------------------- +function menu:init(args) + + -- vars + args = args or {} + local env = args.env or {} -- fix this? + local separator = args.separator or { widget = redflat.gauge.separator.horizontal() } + local theme = args.theme or { auto_hotkey = true } + local icon_style = args.icon_style or { custom_only = true, scalable_only = true } + + -- theme vars + local default_icon = redflat.util.base.placeholder() + local icon = redflat.util.table.check(beautiful, "icon.awesome") and beautiful.icon.awesome or default_icon + local color = redflat.util.table.check(beautiful, "color.icon") and beautiful.color.icon or nil + + -- icon finder + local function micon(name) + return redflat.service.dfparser.lookup_icon(name, icon_style) + end + + -- extra commands + local ranger_comm = env.terminal .. " -e ranger" + + -- Application submenu + ------------------------------------------------------------ + local appmenu = redflat.service.dfparser.menu({ icons = icon_style, wm_name = "awesome" }) + + -- Awesome submenu + ------------------------------------------------------------ + local awesomemenu = { + { "Restart", awesome.restart, micon("gnome-session-reboot") }, + separator, + { "Awesome config", env.fm .. " ~/.config/awesome", micon("folder-bookmarks") }, + } + + -- Places submenu + ------------------------------------------------------------ + local placesmenu = { + { "Documents", env.fm .. " Documents", micon("folder-documents") }, + { "Downloads", env.fm .. " Downloads", micon("folder-download") }, + { "Music", env.fm .. " Music", micon("folder-music") }, + { "Pictures", env.fm .. " Pictures", micon("folder-pictures") }, + { "Videos", env.fm .. " Videos", micon("folder-videos") }, + separator, + { "Media", env.fm .. " /mnt/media", micon("folder-bookmarks") }, + { "Storage", env.fm .. " /mnt/storage", micon("folder-bookmarks") }, + } + + -- Exit submenu + ------------------------------------------------------------ + local exitmenu = { + { "Reboot", "reboot", micon("gnome-session-reboot") }, + { "Shutdown", "shutdown now", micon("system-shutdown") }, + separator, + { "Switch user", "dm-tool switch-to-greeter", micon("gnome-session-switch") }, + { "Suspend", "systemctl suspend" , micon("gnome-session-suspend") }, + { "Log out", awesome.quit, micon("exit") }, + } + + -- Main menu + ------------------------------------------------------------ + self.mainmenu = redflat.menu({ theme = theme, + items = { + { "Awesome", awesomemenu, micon("awesome") }, + { "Applications", appmenu, micon("distributor-logo") }, + { "Places", placesmenu, micon("folder_home"), key = "c" }, + separator, + { "Terminal", env.terminal, micon("terminal") }, + { "Nemo", env.fm, micon("folder"), key = "n" }, + { "Ranger", ranger_comm, micon("folder"), key = "r" }, + { "Editor", "emacs", micon("emacs") }, + separator, + { "Exit", exitmenu, micon("exit") }, + } + }) + + -- Menu panel widget + ------------------------------------------------------------ + + self.widget = redflat.gauge.svgbox(icon, nil, color) + self.buttons = awful.util.table.join( + awful.button({ }, 1, function () self.mainmenu:toggle() end) + ) +end + +return menu \ No newline at end of file diff --git a/awesome/.config/awesome/redflat/.gitignore b/awesome/.config/awesome/redflat/.gitignore new file mode 100644 index 0000000..e504a36 --- /dev/null +++ b/awesome/.config/awesome/redflat/.gitignore @@ -0,0 +1,2 @@ +# IDE settings +.idea \ No newline at end of file diff --git a/awesome/.config/awesome/redflat/.luacheckrc b/awesome/.config/awesome/redflat/.luacheckrc new file mode 100644 index 0000000..88eaa72 --- /dev/null +++ b/awesome/.config/awesome/redflat/.luacheckrc @@ -0,0 +1,39 @@ +-- Only allow symbols available in all Lua versions +std = "min" + +-- Get rid of "unused argument self" - warnings +self = false + +-- Do not check files outside of config +exclude_files = { + ".luacheckrc", -- this file itself +} + +-- Global objects defined by the C code +read_globals = { + -- awesome API + "awesome", + "button", + "dbus", + "drawable", + "drawin", + "key", + "keygrabber", + "mousegrabber", + "root", + "selection", + "tag", + "window", + + -- lua unpack + "table.unpack", + "unpack" +} + +-- Not read-only globals. +globals = { + -- awesome API + "screen", + "mouse", + "client", +} \ No newline at end of file diff --git a/awesome/.config/awesome/redflat/desktop/calendar.lua b/awesome/.config/awesome/redflat/desktop/calendar.lua new file mode 100644 index 0000000..639efc8 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/calendar.lua @@ -0,0 +1,201 @@ +----------------------------------------------------------------------------------------------------------------------- +-- Red Flat calendar desktop widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Multi monitoring widget +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local os = os +local string = string +local setmetatable = setmetatable + +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") +local timer = require("gears.timer") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local calendar = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + show_pointer = true, + label = { gap = 12, font = { font = "Sans", size = 18, face = 1, slant = 0 }, sep = "-" }, + mark = { height = 20, width = 40, dx = 10, line = 4 }, + color = { main = "#b1222b", wibox = "#161616", gray = "#404040", bg = "#161616" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.calendar") or {}) +end + +local days_in_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function is_leap_year(year) + return year % 4 == 0 and (year % 100 ~= 0 or year % 400 == 0) +end + +-- Drawing function +----------------------------------------------------------------------------------------------------------------------- +local function daymarks(style) + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + + widg._data = { + gap = 1, + label_x = 0, + pointer = { show = false, index = 1, label = "01-01" }, + days = 31, + marks = 31, + today = 1, + label = "01-01", + weekend = { 6, 0 } + } + + -- User functions + ------------------------------------------------------------ + function widg:update_data() + local date = os.date('*t') + local first_week_day = os.date('%w', os.time({ year = date.year, month = date.month, day = 1 })) + + self._data.today = date.day + self._data.days = date.month == 2 and is_leap_year(date.year) and 29 or days_in_month[date.month] + self._data.weekend = { (7 - first_week_day) % 7, (8 - first_week_day) % 7 } + self._data.label = string.format("%.2d%s%.2d", date.day, style.label.sep, date.month) + + self:emit_signal("widget::redraw_needed") + end + + function widg:update_pointer(show, index) + self._data.pointer.show = show + if index then self._data.pointer.index = index end + + local date = os.date('*t') + self._data.pointer.label = string.format("%.2d%s%.2d", self._data.pointer.index, style.label.sep, date.month) + + self:emit_signal("widget::redraw_needed") + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + return width, height + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + + -- main draw + self._data.gap = (height - self._data.days * style.mark.height) / (self._data.days - 1) + self._data.label_x = width - style.mark.width - style.mark.dx - style.label.gap + cr:set_line_width(style.mark.line) + + for i = 1, self._data.days do + -- calendar marks + local id = i % 7 + local is_weekend = id == self._data.weekend[1] or id == self._data.weekend[2] + + cr:set_source(color(is_weekend and style.color.main or style.color.gray)) + cr:move_to(width, (style.mark.height + self._data.gap) * (i - 1)) + cr:rel_line_to(0, style.mark.height) + cr:rel_line_to(-style.mark.width, 0) + cr:rel_line_to(-style.mark.dx, -style.mark.height / 2) + cr:rel_line_to(style.mark.dx, -style.mark.height / 2) + cr:close_path() + cr:fill() + + -- data label + if i == self._data.today or (self._data.pointer.show and i == self._data.pointer.index) then + cr:set_source(color(i == self._data.today and style.color.main or style.color.gray)) + local coord_y = ((style.mark.height + self._data.gap) * (i - 1)) + style.mark.height / 2 + redutil.cairo.set_font(cr, style.label.font) + + local ext = cr:text_extents(self._data.label) + cr:move_to( + self._data.label_x - (ext.width + 2 * ext.x_bearing), coord_y - (ext.height/2 + ext.y_bearing) + ) + cr:show_text(i == self._data.today and self._data.label or self._data.pointer.label) + end + end + end + + -------------------------------------------------------------------------------- + return widg +end + + +-- Create a new widget +----------------------------------------------------------------------------------------------------------------------- +function calendar.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + local dwidget = {} + args = args or {} + style = redutil.table.merge(default_style(), style or {}) + local timeout = args.timeout or 300 + + dwidget.style = style + + -- Create calendar widget + -------------------------------------------------------------------------------- + dwidget.calendar = daymarks(style) + dwidget.area = wibox.container.margin(dwidget.calendar) + + -- Set update timer + -------------------------------------------------------------------------------- + local t = timer({ timeout = timeout }) + t:connect_signal("timeout", function () dwidget.calendar:update_data() end) + t:start() + t:emit_signal("timeout") + + -- Drawing date label under mouse + -------------------------------------------------------------------------------- + function dwidget:activate_wibox(wbox) + if style.show_pointer then + wbox:connect_signal("mouse::move", function(_, x, y) + local show_poiter = false + local index + + if x > self.calendar._data.label_x then + for i = 1, self.calendar._data.days do + local cy = y - (i - 1) * (self.calendar._data.gap + style.mark.height) + if cy > 0 and cy < style.mark.height then + show_poiter = true + index = i + break + end + end + end + + if self.calendar._data.pointer.show ~= show_poiter then + self.calendar:update_pointer(show_poiter, index) + end + end) + + wbox:connect_signal("mouse::leave", function() + if self.calendar._data.pointer.show then self.calendar:update_pointer(false) end + end) + end + end + + -------------------------------------------------------------------------------- + return dwidget +end + +-- Config metatable to call module as function +----------------------------------------------------------------------------------------------------------------------- +function calendar.mt:__call(...) + return calendar.new(...) +end + +return setmetatable(calendar, calendar.mt) diff --git a/awesome/.config/awesome/redflat/desktop/common/bar/init.lua b/awesome/.config/awesome/redflat/desktop/common/bar/init.lua new file mode 100644 index 0000000..e90fe55 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/common/bar/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.desktop.common.bar" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/desktop/common/bar/plain.lua b/awesome/.config/awesome/redflat/desktop/common/bar/plain.lua new file mode 100644 index 0000000..03d0091 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/common/bar/plain.lua @@ -0,0 +1,114 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat desktop progressbar widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Dashed horizontal progress bar +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math +local wibox = require("wibox") +local color = require("gears.color") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local progressbar = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + maxm = 1, + width = nil, + height = nil, + chunk = { gap = 5, width = 5 }, + autoscale = false, + color = { main = "#b1222b", gray = "#404040" } + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.common.bar.plain") or {}) +end + +-- Cairo drawing functions +----------------------------------------------------------------------------------------------------------------------- + +local function draw_progressbar(cr, width, height, gap, first_point, last_point, fill_color) + cr:set_source(color(fill_color)) + for i = first_point, last_point do + cr:rectangle((i - 1) * (width + gap), 0, width, height) + end + cr:fill() +end + +-- Create a new progressbar widget +-- @param style.chunk Table containing dash parameters +-- @param style.color.main Main color +-- @param style.width Widget width (optional) +-- @param style.height Widget height (optional) +-- @param style.autoscale Scaling received values, true by default +-- @param style.maxm Scaling value if autoscale = false +----------------------------------------------------------------------------------------------------------------------- +function progressbar.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + local maxm = style.maxm + + --style aliases + local stg, stw = style.chunk.gap, style.chunk.width + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + widg._data = { value = 0, chunks = 1, gap = 1, cnum = 0 } + + function widg:set_value(x) + if style.autoscale then + if x > maxm then maxm = x end + end + + local cx = x / maxm + if cx > 1 then cx = 1 end + + self._data.value = cx + local num = math.ceil(self._data.chunks * self._data.value) + + if num ~= self._data.cnum then + self:emit_signal("widget::redraw_needed") + end + end + + function widg:fit(_, width, height) + local w = style.width and math.min(style.width, width) or width + local h = style.height and math.min(style.height, height) or height + return w, h + end + + -- Draw function + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + -- progressbar + self._data.chunks = math.floor((width + stg) / (stw + stg)) + self._data.gap = stg + (width - (self._data.chunks - 1) * (stw + stg) - stw) / (self._data.chunks - 1) + self._data.cnum = math.ceil(self._data.chunks * self._data.value) + + draw_progressbar(cr, stw, height, self._data.gap, 1, self._data.cnum, style.color.main) + draw_progressbar(cr, stw, height, self._data.gap, self._data.cnum + 1, self._data.chunks, style.color.gray) + end + -------------------------------------------------------------------------------- + + return widg +end + +-- Config metatable to call progressbar module as function +----------------------------------------------------------------------------------------------------------------------- +function progressbar.mt:__call(...) + return progressbar.new(...) +end + +return setmetatable(progressbar, progressbar.mt) diff --git a/awesome/.config/awesome/redflat/desktop/common/bar/shaped.lua b/awesome/.config/awesome/redflat/desktop/common/bar/shaped.lua new file mode 100644 index 0000000..672b5a0 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/common/bar/shaped.lua @@ -0,0 +1,144 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat corners widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Vertical progress indicator with custom shape +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math +local wibox = require("wibox") +local color = require("gears.color") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") +local tooltip = require("redflat.float.tooltip") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local progressbar = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + chunk = { num = 10, line = 5, height = 10 }, + maxm = 1, + width = nil, + height = nil, + autoscale = false, + show = { tooltip = false }, + tooltip = {}, + shape = "corner", + color = { main = "#b1222b", gray = "#404040" } + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.common.bar.shaped") or {}) +end + +-- Cairo drawing functions +----------------------------------------------------------------------------------------------------------------------- + +local function draw_corner(cr, width, height, gap, first_point, last_point, fill_color, style) + cr:set_source(color(fill_color)) + for i = first_point, last_point do + cr:move_to(0, height - (i - 1) * (style.chunk.line + gap)) + cr:rel_line_to(width / 2, - style.chunk.height) + cr:rel_line_to(width / 2, style.chunk.height) + cr:rel_line_to(- style.chunk.line, 0) + cr:rel_line_to(- width / 2 + style.chunk.line, - style.chunk.height + style.chunk.line) + cr:rel_line_to(- width / 2 + style.chunk.line, style.chunk.height - style.chunk.line) + cr:close_path() + end + cr:fill() +end + +local function draw_line(cr, width, height, dy, first_point, last_point, fill_color, style) + cr:set_source(color(fill_color)) + for i = first_point, last_point do + cr:rectangle(0, height - (i - 1) * dy, width, - style.chunk.line) + end + cr:fill() +end + +-- Create a new progressbar widget +-- @param style.chunk Table containing number and sizes for progress bar chunks +-- @param style.color Main color +-- @param style.width Widget width (optional) +-- @param style.height Widget height (optional) +-- @param style.autoscale Scaling received values, true by default +-- @param style.maxm Scaling value if autoscale = false +----------------------------------------------------------------------------------------------------------------------- +function progressbar.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + local maxm = style.maxm + + --style aliases + local stn = style.chunk.num + + -- Create custom widget + -------------------------------------------------------------------------------- + local shapewidg = wibox.widget.base.make_widget() + shapewidg._data = { value = 0, cnum = 0 } + + -- tooltip + if style.show.tooltip then + shapewidg._tp = tooltip({ objects = { shapewidg } }, style.tooltip) + end + + -- setup + function shapewidg:set_value(x) + if style.autoscale then + if x > maxm then maxm = x end + end + + local cx = x / maxm + if cx > 1 then cx = 1 end + + self._data.value = cx + local num = math.ceil(stn * self._data.value) + + if num ~= self._data.cnum then + self:emit_signal("widget::redraw_needed") + end + end + + function shapewidg:set_tip(txt) + if self._tp then self._tp:set_text(txt) end + end + + function shapewidg:fit(_, width, height) + return style.width or width, style.height or height + end + + -- Draw function + ------------------------------------------------------------ + function shapewidg:draw(_, cr, width, height) + self._data.cnum = math.ceil(stn * self._data.value) + + if style.shape == "plain" then + local line_gap = style.chunk.line + (height - style.chunk.line * stn)/(stn - 1) + draw_line(cr, width, height, line_gap, 1, self._data.cnum, style.color.main, style) + draw_line(cr, width, height, line_gap, self._data.cnum + 1, stn, style.color.gray, style) + elseif style.shape == "corner" then + local corner_gap = (height - (stn - 1) * style.chunk.line - style.chunk.height) / (stn - 1) + draw_corner(cr, width, height, corner_gap, 1, self._data.cnum, style.color.main, style) + draw_corner(cr, width, height, corner_gap, self._data.cnum + 1, stn, style.color.gray, style) + end + end + + -------------------------------------------------------------------------------- + return shapewidg +end + +-- Config metatable to call progressbar module as function +----------------------------------------------------------------------------------------------------------------------- +function progressbar.mt:__call(...) + return progressbar.new(...) +end + +return setmetatable(progressbar, progressbar.mt) diff --git a/awesome/.config/awesome/redflat/desktop/common/chart.lua b/awesome/.config/awesome/redflat/desktop/common/chart.lua new file mode 100644 index 0000000..79bb1d8 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/common/chart.lua @@ -0,0 +1,121 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat chart widget -- +----------------------------------------------------------------------------------------------------------------------- +-- History graph for desktop widgets +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local table = table +local ipairs = ipairs +local math = math +local wibox = require("wibox") +local color = require("gears.color") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local chart = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + maxm = 1, + color = "#404040", + width = nil, + height = nil, + zero_height = 4, + bar = { gap = 5, width = 5 }, + autoscale = true + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.common.chart") or {}) +end + +-- Create a new chart widget +-- @param style Table containing chart geometry and style +-- See block of local vars below for more details +----------------------------------------------------------------------------------------------------------------------- +function chart.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + local count = 0 + local barnum + local current_maxm = style.maxm + + -- updating values + local data = { + values = {} + } + + -- Create custom widget + -------------------------------------------------------------------------------- + local chartwidg = wibox.widget.base.make_widget() + + -- User functions + ------------------------------------------------------------ + function chartwidg:set_value(x) + if barnum then + count = count % barnum + 1 + data.values[count] = x + self:emit_signal("widget::redraw_needed") + end + end + + -- Fit function + ------------------------------------------------------------ + function chartwidg:fit(_, width, height) + return style.width or width, style.height or height + end + + -- Draw function + ------------------------------------------------------------ + function chartwidg:draw(_, cr, width, height) + + --scale + if style.autoscale then + current_maxm = style.maxm + + for _, v in ipairs(data.values) do + if v > current_maxm then current_maxm = v end + end + end + + -- chart + barnum = math.floor((width + style.bar.gap) / (style.bar.width + style.bar.gap)) + while #data.values < barnum do + table.insert(data.values, 0) + end + local real_gap = style.bar.gap + (width - (barnum - 1) * (style.bar.width + style.bar.gap) + - style.bar.width) / (barnum - 1) + + cr:set_source(color(style.color)) + for i = 0, barnum - 1 do + local n = (count + i) % barnum + 1 + local k = data.values[n] / current_maxm + if k > 1 then k = 1 end + local bar_height = - k * (height - style.zero_height) + cr:rectangle(i * (style.bar.width + real_gap), height, style.bar.width, - style.zero_height) + cr:rectangle(i * (style.bar.width + real_gap), height - style.zero_height, style.bar.width, bar_height) + end + + cr:fill() + end + + -------------------------------------------------------------------------------- + return chartwidg +end + +-- Config metatable to call chart module as function +----------------------------------------------------------------------------------------------------------------------- +function chart.mt:__call(...) + return chart.new(...) +end + +return setmetatable(chart, chart.mt) diff --git a/awesome/.config/awesome/redflat/desktop/common/init.lua b/awesome/.config/awesome/redflat/desktop/common/init.lua new file mode 100644 index 0000000..c982503 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/common/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.desktop.common" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/desktop/common/pack/init.lua b/awesome/.config/awesome/redflat/desktop/common/pack/init.lua new file mode 100644 index 0000000..26e3e72 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/common/pack/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.desktop.common.pack" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/desktop/common/pack/lines.lua b/awesome/.config/awesome/redflat/desktop/common/pack/lines.lua new file mode 100644 index 0000000..792ae81 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/common/pack/lines.lua @@ -0,0 +1,137 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat barpack widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Group of indicators with progressbar, label and text in every line +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local wibox = require("wibox") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") +local dcommon = require("redflat.desktop.common") +local tooltip = require("redflat.float.tooltip") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local barpack = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + label = {}, + text = {}, + show = { text = true, label = true, tooltip = false }, + progressbar = {}, + line = { height = 20 }, + gap = { text = 20, label = 20 }, + tooltip = {}, + color = {} + } + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.common.pack.lines") or {}) +end + + +-- Create a new barpack widget +----------------------------------------------------------------------------------------------------------------------- +function barpack.new(num, style) + + local pack = {} + style = redutil.table.merge(default_style(), style or {}) + local progressbar_style = redutil.table.merge(style.progressbar, { color = style.color }) + local label_style = redutil.table.merge(style.label, { color = style.color.gray }) + local text_style = redutil.table.merge(style.text, { color = style.color.gray }) + + -- Construct group of lines + -------------------------------------------------------------------------------- + pack.layout = wibox.layout.align.vertical() + local flex_vertical = wibox.layout.flex.vertical() + local lines = {} + + for i = 1, num do + lines[i] = {} + + -- bar + local line_align = wibox.layout.align.horizontal() + line_align:set_forced_height(style.line.height) + lines[i].bar = dcommon.bar.plain(progressbar_style) + line_align:set_middle(lines[i].bar) + + -- label + lines[i]._label_txt = "" + lines[i].label = dcommon.textbox("", label_style) + lines[i].label:set_width(0) + lines[i].label_margin = wibox.container.margin(lines[i].label) + line_align:set_left(lines[i].label_margin) + + -- value text + lines[i].text = dcommon.textbox("", text_style) + lines[i].text:set_width(0) + lines[i].text_margin = wibox.container.margin(lines[i].text) + line_align:set_right(lines[i].text_margin) + + -- tooltip + if style.show.tooltip then + lines[i].tooltip = tooltip({ objects = { line_align } }, style.tooltip) + end + + if i == 1 then + pack.layout:set_top(line_align) + else + local line_space = wibox.layout.align.vertical() + line_space:set_bottom(line_align) + flex_vertical:add(line_space) + end + end + pack.layout:set_middle(flex_vertical) + + -- Setup functions + -------------------------------------------------------------------------------- + + function pack:set_values(value, index) + lines[index].bar:set_value(value) + end + + function pack:set_text(value, index) + if style.show.text then + lines[index].text:set_text(value) + lines[index].text:set_width(value and text_style.width or 0) + lines[index].text_margin:set_left(value and style.gap.text or 0) + end + + if lines[index].tooltip then + lines[index].tooltip:set_text(string.format("%s %s", lines[index]._label_txt, value)) + end + end + + function pack:set_text_color(value, index) + lines[index].text:set_color(value) + end + + function pack:set_label_color(value, index) + lines[index].label:set_color(value) + end + + function pack:set_label(value, index) + lines[index]._label_txt = value + if style.show.label then + lines[index].label:set_text(value) + lines[index].label:set_width(value and label_style.width or 0) + lines[index].label_margin:set_right(value and style.gap.label or 0) + end + end + + -------------------------------------------------------------------------------- + return pack +end + +-- Config metatable to call barpack module as function +----------------------------------------------------------------------------------------------------------------------- +function barpack.mt:__call(...) + return barpack.new(...) +end + +return setmetatable(barpack, barpack.mt) diff --git a/awesome/.config/awesome/redflat/desktop/common/pack/upright.lua b/awesome/.config/awesome/redflat/desktop/common/pack/upright.lua new file mode 100644 index 0000000..784b801 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/common/pack/upright.lua @@ -0,0 +1,72 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat ber pack widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Group of upright indicators placed in horizontal layout +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable + +local wibox = require("wibox") +--local beautiful = require("beautiful") + +local progressbar = require("redflat.desktop.common.bar.shaped") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local barpack = { mt = {} } + +-- Create a new barpack widget +-- @param num Number of indicators +-- @param style Style variables for redflat shaped progressbar widget +----------------------------------------------------------------------------------------------------------------------- +function barpack.new(num, style) + + local pack = {} + style = style or {} + + -- construct group of bar indicators + pack.layout = wibox.layout.align.horizontal() + local flex_horizontal = wibox.layout.flex.horizontal() + local crn = {} + + for i = 1, num do + crn[i] = progressbar(style) + if i == 1 then + pack.layout:set_left(crn[i]) + else + local bar_space = wibox.layout.align.horizontal() + bar_space:set_right(crn[i]) + flex_horizontal:add(bar_space) + end + end + pack.layout:set_middle(flex_horizontal) + + -- setup functions + function pack:set_values(values, n, tip) + if n then + if crn[n] then + crn[n]:set_value(values) + if tip then crn[n]:set_tip(tip) end + end + else + for i, v in ipairs(values) do + if crn[i] then + crn[i]:set_value(v) + if tip then crn[n]:set_tip(tip) end + end + end + end + end + + return pack +end + +-- Config metatable to call barpack module as function +----------------------------------------------------------------------------------------------------------------------- +function barpack.mt:__call(...) + return barpack.new(...) +end + +return setmetatable(barpack, barpack.mt) diff --git a/awesome/.config/awesome/redflat/desktop/common/textbox.lua b/awesome/.config/awesome/redflat/desktop/common/textbox.lua new file mode 100644 index 0000000..866dc64 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/common/textbox.lua @@ -0,0 +1,158 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat textbox widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Custom textbox for desktop widgets +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local wibox = require("wibox") +local color = require("gears.color") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") + + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local textbox = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = nil, + height = nil, + draw = "by_left", + separator = '%s', + color = "#404040", + font = { font = "Sans", size = 20, face = 0, slant = 0 } + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.common.textbox") or {}) +end + +-- Text alignment functions +----------------------------------------------------------------------------------------------------------------------- +local align = {} + +function align.by_left(cr, _, _, text) + local ext = cr:text_extents(text) + --cr:move_to(0, height) + cr:move_to(0, ext.height) + cr:show_text(text) +end + +function align.by_right(cr, width, _, text) + local ext = cr:text_extents(text) + --cr:move_to(width - (ext.width + ext.x_bearing), height) + cr:move_to(width - (ext.width + ext.x_bearing), ext.height) + cr:show_text(text) +end + +function align.by_edges(cr, width, height, text, style) + local left_text, right_text = string.match(text, "(.+)" .. style.separator .. "(.+)") + if left_text and right_text then + align.by_left(cr, width, height, left_text) + align.by_right(cr, width, height, right_text) + else + align.by_right(cr, width, height, text) + end +end + +function align.by_width(cr, width, _, text) + local ext = cr:text_extents(text) + local text_gap = (width - ext.width - ext.x_bearing)/(#text - 1) + local gap = 0 + + for i = 1, #text do + local c = string.sub(text, i, i) + --cr:move_to(gap, height) + cr:move_to(gap, ext.height) + cr:show_text(c) + + local c_ext = cr:text_extents(c) + gap = gap + text_gap + c_ext.width + c_ext.x_bearing + -- !!! WORKAROUND for space character width only for font size = 24 font = "Play bold" !!! + --if c == " " then + -- gap = gap + 6 + --end + end +end + +-- Create a new textbox widget +-- @param txt Text to display +-- @param style.text Table containing font parameters +-- @param style.color Font color +-- @param style.draw Text align method +-- @param style.width Widget width (optional) +-- @param style.height Widget height (optional) +----------------------------------------------------------------------------------------------------------------------- +function textbox.new(txt, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) +-- local textdraw = align[style.draw] or align.by_left + + -- Create custom widget + -------------------------------------------------------------------------------- + local textwidg = wibox.widget.base.make_widget() + textwidg._data = { + text = txt or "", + width = style.width, + color = style.color + } + + -- User functions + ------------------------------------------------------------ + function textwidg:set_text(text) + if self._data.text ~= text then + self._data.text = text + self:emit_signal("widget::redraw_needed") + end + end + + function textwidg:set_color(value) + if self._data.color ~= value then + self._data.color = value + self:emit_signal("widget::redraw_needed") + end + end + + function textwidg:set_width(width) + if self._data.width ~= width then + self._data.width = width + self:emit_signal("widget::redraw_needed") + end + end + + -- Fit + ------------------------------------------------------------ + function textwidg:fit(_, width, height) + local w = self._data.width and math.min(self._data.width, width) or width + local h = style.height and math.min(style.height, height) or height + return w, h + end + + -- Draw + ------------------------------------------------------------ + function textwidg:draw(_, cr, width, height) + cr:set_source(color(self._data.color)) + redutil.cairo.set_font(cr, style.font) + + align[style.draw](cr, width, height, self._data.text, style) + end + + -------------------------------------------------------------------------------- + return textwidg +end + +-- Config metatable to call textbox module as function +----------------------------------------------------------------------------------------------------------------------- +function textbox.mt:__call(...) + return textbox.new(...) +end + +return setmetatable(textbox, textbox.mt) diff --git a/awesome/.config/awesome/redflat/desktop/init.lua b/awesome/.config/awesome/redflat/desktop/init.lua new file mode 100644 index 0000000..82f61a3 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.desktop" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/desktop/multiline.lua b/awesome/.config/awesome/redflat/desktop/multiline.lua new file mode 100644 index 0000000..3a0627f --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/multiline.lua @@ -0,0 +1,136 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat dashpack desktop widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Multi monitoring widget +-- Several lines with progressbar, label and text +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local string = string +local unpack = unpack or table.unpack + +local wibox = require("wibox") +local beautiful = require("beautiful") +local timer = require("gears.timer") + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") +local lines = require("redflat.desktop.common.pack.lines") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local dashpack = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + icon = { image = nil, margin = { 0, 0, 0, 0 } }, + lines = {}, + margin = { 0, 0, 0, 0 }, + digits = 3, + dislabel = "OFF", + unit = { { "B", -1 }, { "KB", 1024 }, { "MB", 1024^2 }, { "GB", 1024^3 } }, + color = { main = "#b1222b", wibox = "#161616", gray = "#404040" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.multiline") or {}) +end + +local default_args = { timeout = 60, sensors = {} } + +-- Create a new widget +----------------------------------------------------------------------------------------------------------------------- +function dashpack.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + local dwidget = {} + args = redutil.table.merge(default_args, args or {}) + style = redutil.table.merge(default_style(), style or {}) + local alert_data = { counter = 0, state = false } + + dwidget.style = style + + -- initialize progressbar lines + local lines_style = redutil.table.merge(style.lines, { color = style.color }) + local pack = lines(#args.sensors, lines_style) + + -- add icon if needed + if style.icon.image then + dwidget.icon = svgbox(style.icon.image) + dwidget.icon:set_color(style.color.gray) + + dwidget.area = wibox.layout.align.horizontal() + dwidget.area:set_middle(wibox.container.margin(pack.layout, unpack(style.margin))) + dwidget.area:set_left(wibox.container.margin(dwidget.icon, unpack(style.icon.margin))) + else + dwidget.area = wibox.container.margin(pack.layout, unpack(style.margin)) + end + + for i, sensor in ipairs(args.sensors) do + if sensor.name then pack:set_label(string.upper(sensor.name), i) end + end + + -- Update info function + -------------------------------------------------------------------------------- + local function set_raw_state(state, maxm, crit, i) + local alert = crit and state[1] > crit + local text_color = alert and style.color.main or style.color.gray + + pack:set_values(state[1] / maxm, i) + pack:set_label_color(text_color, i) + + if style.lines.show.text or style.lines.show.tooltip then + local txt = state.off and style.dislabel + or redutil.text.dformat(state[2] or state[1], style.unit, style.digits) + pack:set_text(txt, i) + pack:set_text_color(text_color, i) + end + + if style.icon.image then + alert_data.counter = alert_data.counter + 1 + alert_data.state = alert_data.state or alert + if alert_data.counter == #args.sensors then + dwidget.icon:set_color(alert_data.state and style.color.main or style.color.gray) + end + end + end + + local function line_hadnler(maxm, crit, i) + return function(state) set_raw_state(state, maxm, crit, i) end + end + + local function update() + alert_data = { counter = 0, state = false } + --if style.icon.image then dwidget.icon:set_color(style.color.gray) end + for i, sens in ipairs(args.sensors) do + local maxm, crit = sens.maxm, sens.crit + if sens.meter_function then + local state = sens.meter_function(sens.args) + set_raw_state(state, maxm, crit, i) + else + sens.async_function(line_hadnler(maxm, crit, i)) + end + end + end + + -- Set update timer + -------------------------------------------------------------------------------- + local t = timer({ timeout = args.timeout }) + t:connect_signal("timeout", update) + t:start() + t:emit_signal("timeout") + + -------------------------------------------------------------------------------- + return dwidget +end + +-- Config metatable to call module as function +----------------------------------------------------------------------------------------------------------------------- +function dashpack.mt:__call(...) + return dashpack.new(...) +end + +return setmetatable(dashpack, dashpack.mt) diff --git a/awesome/.config/awesome/redflat/desktop/multimeter.lua b/awesome/.config/awesome/redflat/desktop/multimeter.lua new file mode 100644 index 0000000..80de75f --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/multimeter.lua @@ -0,0 +1,167 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat multi monitoring deskotp widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Multi monitoring widget +-- Pack of vertical indicators and two lines with labeled progressbar +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +--local awful = require("awful") +local wibox = require("wibox") +local beautiful = require("beautiful") +local timer = require("gears.timer") +local unpack = unpack or table.unpack + +local dcommon = require("redflat.desktop.common") +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local multim = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + lines = {}, + upbar = { width = 40 }, + digits = 3, + height = { upright = 100, lines = 60 }, + icon = { image = nil, margin = { 0, 20, 0, 0 }, full = false }, + labels = {}, + unit = { { "MB", - 1 }, { "GB", 1024 } }, + color = { main = "#b1222b", wibox = "#161616", gray = "#404040" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.multimeter") or {}) +end + +local default_args = { + topbars = { num = 1, maxm = 1}, + lines = { maxm = 1 }, + meter = {}, + timeout = 60, +} + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function set_info(value, args, upright, lines, icon, last, style) + local upright_alert = value.alert + + -- set progressbar values and color + for i, line in ipairs(args.lines) do + lines:set_values(value.lines[i][1] / line.maxm, i) + lines:set_text(redutil.text.dformat(value.lines[i][2], line.unit or style.unit, style.digits), i) + + if line.crit then + local cc = value.lines[i][1] > line.crit and style.color.main or style.color.gray + lines:set_text_color(cc, i) + if style.labels[i] then lines:set_label_color(cc, i) end + end + end + + -- set upright value + for i = 1, args.topbars.num do + local v = value.bars[i] and value.bars[i].value or 0 + local tip = value.bars[i] and value.bars[i].text or nil + upright:set_values(v / args.topbars.maxm, i, tip) + if args.topbars.crit then upright_alert = upright_alert or v > args.topbars.crit end + end + + -- colorize icon if needed + if style.icon.image and upright_alert ~= last.alert then + icon:set_color(upright_alert and style.color.main or style.color.gray) + last.alert = upright_alert + end +end + + +-- Create a new widget +----------------------------------------------------------------------------------------------------------------------- +function multim.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + local dwidget = {} + local icon + local last = { alert = false } + + args = redutil.table.merge(default_args, args or {}) + --local geometry = redutil.table.merge(default_geometry, geometry or {}) + style = redutil.table.merge(default_style(), style or {}) + + local lines_style = redutil.table.merge(style.lines, { progressbar = { color = style.color } }) + local upbar_style = redutil.table.merge(style.upbar, { color = style.color }) + + dwidget.style = style + + -- Construct layouts + -------------------------------------------------------------------------------- + local lines = dcommon.pack.lines(#args.lines, lines_style) + local upright = dcommon.pack.upright(args.topbars.num, upbar_style) + lines.layout:set_forced_height(style.height.lines) + + if style.icon.image then + icon = svgbox(style.icon.image) + icon:set_color(style.color.gray) + end + + dwidget.area = wibox.widget({ + { + icon and not style.icon.full and wibox.container.margin(icon, unpack(style.icon.margin)), + upright.layout, + nil, + forced_height = style.height.upright, + layout = wibox.layout.align.horizontal + }, + nil, + lines.layout, + layout = wibox.layout.align.vertical + }) + + if icon and style.icon.full then + dwidget.area = wibox.widget({ + wibox.container.margin(icon, unpack(style.icon.margin)), + dwidget.area, + nil, + layout = wibox.layout.align.horizontal + }) + end + + for i, label in ipairs(style.labels) do + lines:set_label(label, i) + end + + -- Update info function + -------------------------------------------------------------------------------- + local function raw_set(state) + set_info(state, args, upright, lines, icon, last, style) + end + + local function update_plain() + local state = args.meter.func(args.meter.args) + set_info(state, args, upright, lines, icon, last, style) + end + + local update = args.meter.async and function() args.meter.async(raw_set, args.meter.args) end or update_plain + + -- Set update timer + -------------------------------------------------------------------------------- + local t = timer({ timeout = args.timeout }) + t:connect_signal("timeout", update) + t:start() + t:emit_signal("timeout") + + -------------------------------------------------------------------------------- + return dwidget +end + +-- Config metatable to call module as function +----------------------------------------------------------------------------------------------------------------------- +function multim.mt:__call(...) + return multim.new(...) +end + +return setmetatable(multim, multim.mt) diff --git a/awesome/.config/awesome/redflat/desktop/singleline.lua b/awesome/.config/awesome/redflat/desktop/singleline.lua new file mode 100644 index 0000000..7af6a34 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/singleline.lua @@ -0,0 +1,138 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat simple line desktop widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Multi monitoring widget +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local string = string + +local wibox = require("wibox") +local beautiful = require("beautiful") +local timer = require("gears.timer") + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") +local textbox = require("redflat.desktop.common.textbox") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local sline = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + lbox = { draw = "by_left", width = 50 }, + rbox = { draw = "by_right", width = 50 }, + digits = 3, + icon = nil, + iwidth = 120, + unit = { { "B", -1 }, { "KB", 1024 }, { "MB", 1024^2 }, { "GB", 1024^3 } }, + color = { main = "#b1222b", wibox = "#161616", gray = "#404040" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.singleline") or {}) +end + +local default_args = { timeout = 60, sensors = {} } + +-- Create a new widget +----------------------------------------------------------------------------------------------------------------------- +function sline.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + local dwidget = {} + args = redutil.table.merge(default_args, args or {}) + style = redutil.table.merge(default_style(), style or {}) + + dwidget.style = style + + -- Initialize layouts + -------------------------------------------------------------------------------- + dwidget.item = {} + dwidget.icon = {} + dwidget.area = wibox.layout.align.horizontal() + + local mid = wibox.layout.flex.horizontal() + + -- construct line + for i, _ in ipairs(args.sensors) do + dwidget.item[i] = textbox("", style.rbox) + + if style.icon then dwidget.icon[i] = svgbox(style.icon) end + + local boxlayout = wibox.widget({ + textbox(string.upper(args.sensors[i].name or "mon"), style.lbox), + style.icon and { + nil, dwidget.icon[i], nil, + expand = "outside", + layout = wibox.layout.align.horizontal + }, + dwidget.item[i], + forced_width = style.iwidth, + layout = wibox.layout.align.horizontal + }) + + if i == 1 then + dwidget.area:set_left(boxlayout) + else + local space = wibox.layout.align.horizontal() + space:set_right(boxlayout) + mid:add(space) + end + end + + dwidget.area:set_middle(mid) + + -- Update info function + -------------------------------------------------------------------------------- + local function set_raw_state(state, crit, i) + local text_color = crit and state[1] > crit and style.color.main or style.color.gray + local txt = redutil.text.dformat(state[2] or state[1], style.unit, style.digits) + + dwidget.item[i]:set_text(txt) + dwidget.item[i]:set_color(text_color) + + if dwidget.icon[i] then + local icon_color = state.off and style.color.gray or style.color.main + dwidget.icon[i]:set_color(icon_color) + end + end + + local function item_hadnler(crit, i) + return function(state) set_raw_state(state, crit, i) end + end + + local function update() + for i, sens in ipairs(args.sensors) do + local crit = sens.crit + if sens.meter_function then + local state = sens.meter_function(sens.args) + set_raw_state(state, crit, i) + else + sens.async_function(item_hadnler(crit, i)) + end + end + end + + -- Set update timer + -------------------------------------------------------------------------------- + local t = timer({ timeout = args.timeout }) + t:connect_signal("timeout", update) + t:start() + t:emit_signal("timeout") + + -------------------------------------------------------------------------------- + return dwidget +end + +-- Config metatable to call module as function +----------------------------------------------------------------------------------------------------------------------- +function sline.mt:__call(...) + return sline.new(...) +end + +return setmetatable(sline, sline.mt) diff --git a/awesome/.config/awesome/redflat/desktop/speedmeter/compact.lua b/awesome/.config/awesome/redflat/desktop/speedmeter/compact.lua new file mode 100644 index 0000000..c31f2d0 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/speedmeter/compact.lua @@ -0,0 +1,173 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat speed meter deskotp widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Network or disk i/o speed indicators +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable + +local wibox = require("wibox") +local beautiful = require("beautiful") +local timer = require("gears.timer") +local unpack = unpack or table.unpack + +local system = require("redflat.system") +local redutil = require("redflat.util") +local dcommon = require("redflat.desktop.common") +local svgbox = require("redflat.gauge.svgbox") + + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local speedmeter = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + icon = { margin = { 0, 0, 0, 0 }, }, + label = { width = 100 }, + margins = { label = { 0, 0, 0, 0 }, chart = { 0, 0, 2, 2 } }, + progressbar = { chunk = { width = 10, gap = 5 }, height = 4 }, + chart = {}, + height = { chart = 50 }, + digits = 2, + unit = { { "B", -1 }, { "KB", 1024 }, { "MB", 1024^2 }, { "GB", 1024^3 } }, + color = { main = "#b1222b", wibox = "#161616", gray = "#404040" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.speedmeter.compact") or {}) +end + +local default_args = { + autoscale = true, + label = "NETWORK", + timeout = 5, + interface = "eth0", + meter_function = system.net_speed +} + +local default_maxspeed = { up = 10 * 1024, down = 10 * 1024 } + + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Construct chart with support elements +-------------------------------------------------------------------------------- +local function value_chart(style, image, maxspeed) + local chart = dcommon.chart(redutil.table.merge(style.chart, { maxm = maxspeed })) + local progressbar = dcommon.bar.plain(redutil.table.merge(style.progressbar, { maxm = maxspeed })) + + local text = dcommon.textbox("", style.label) + local icon = image and svgbox(image) or nil + + local area = wibox.widget({ + progressbar, + { + { + nil, + wibox.container.margin(text, unpack(style.margins.label)), + nil, + expand = "outside", + layout = wibox.layout.align.vertical + }, + wibox.container.margin(chart, unpack(style.margins.chart)), + nil, + layout = wibox.layout.align.horizontal + }, + progressbar, + forced_height = style.height.chart, + layout = wibox.layout.align.vertical + }) + + if image then + icon:set_color(style.color.gray) + area = wibox.widget({ + wibox.container.margin(icon, unpack(style.icon.margin)), + area, + nil, + forced_height = style.height.chart, + layout = wibox.layout.align.horizontal + }) + end + + return { chart = chart, progressbar = progressbar, text = text, icon = icon, area = area } +end + + +-- Create a new speed meter widget +----------------------------------------------------------------------------------------------------------------------- +function speedmeter.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + local dwidget = {} + local storage = {} + local last_state = { false, false } + + args = redutil.table.merge(default_args, args or {}) + style = redutil.table.merge(default_style(), style or {}) + local maxspeed = redutil.table.merge(default_maxspeed, args.maxspeed or {}) + + style.chart = redutil.table.merge(style.chart, { autoscale = args.autoscale, color = style.color.gray }) + style.progressbar = redutil.table.merge(style.progressbar, { autoscale = args.autoscale, color = style.color }) + style.label = redutil.table.merge(style.label, { draw = "by_edges", color = style.color.gray }) + + dwidget.style = style + + -- Construct indicators + -------------------------------------------------------------------------------- + local widg = { {}, {} } + local placement = { "up", "down" } + + for i = 1, 2 do + widg[i] = value_chart(style, style.icon[placement[i]], maxspeed[placement[i]]) + end + + dwidget.area = wibox.widget({ + widg[1].area, nil, widg[2].area, + layout = wibox.layout.align.vertical + }) + + -- Update info + -------------------------------------------------------------------------------- + local function update() + local state = args.meter_function(args.interface, storage) + + for i = 1, 2 do + widg[i].chart:set_value(state[i]) + widg[i].progressbar:set_value(state[i]) + widg[i].text:set_text(redutil.text.dformat(state[i], style.unit, style.digits)) + + if widg[i].icon and args.crit then + local st = state[i] > args.crit[placement[i]] + if st ~= last_state[i] then + local newc = st and style.color.main or style.color.gray + widg[i].icon:set_color(newc) + widg[i].text:set_color(newc) + last_state[i] = st + end + end + end + end + + -- Set update timer + -------------------------------------------------------------------------------- + local t = timer({ timeout = args.timeout }) + t:connect_signal("timeout", update) + t:start() + t:emit_signal("timeout") + + -------------------------------------------------------------------------------- + return dwidget +end + +-- Config metatable to call module as function +----------------------------------------------------------------------------------------------------------------------- +function speedmeter.mt:__call(...) + return speedmeter.new(...) +end + +return setmetatable(speedmeter, speedmeter.mt) diff --git a/awesome/.config/awesome/redflat/desktop/speedmeter/init.lua b/awesome/.config/awesome/redflat/desktop/speedmeter/init.lua new file mode 100644 index 0000000..6081eb2 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/speedmeter/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.desktop.speedmeter" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/desktop/speedmeter/normal.lua b/awesome/.config/awesome/redflat/desktop/speedmeter/normal.lua new file mode 100644 index 0000000..3c5b178 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/speedmeter/normal.lua @@ -0,0 +1,199 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat speed meter deskotp widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Network or disk i/o speed indicators +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable + +local wibox = require("wibox") +local beautiful = require("beautiful") +local timer = require("gears.timer") + +local system = require("redflat.system") +local redutil = require("redflat.util") +local dcommon = require("redflat.desktop.common") +local svgbox = require("redflat.gauge.svgbox") + + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local speedmeter = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + images = {}, + label = { height = 20, separator = "^" }, + progressbar = { chunk = { width = 10, gap = 5 }, height = 4 }, + chart = {}, + barvalue_height = 32, + fullchart_height = 78, + digits = 2, + image_gap = 20, + unit = { { "B", -1 }, { "KB", 1024 }, { "MB", 1024^2 }, { "GB", 1024^3 } }, + color = { main = "#b1222b", wibox = "#161616", gray = "#404040" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.speedmeter.normal") or {}) +end + +local default_args = { + autoscale = true, + label = "NETWORK", + timeout = 5, + interface = "eth0", + meter_function = system.net_speed +} + +local default_maxspeed = { up = 10 * 1024, down = 10 * 1024 } + + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function set_fullchart_info(objects, label, state, style) + for i, o in ipairs(objects) do + o.barvalue:set_value(state[i]) + o.barvalue:set_text( + label .. style.label.separator .. redutil.text.dformat(state[i], style.unit, style.digits, " ") + ) + o.chart:set_value(state[i]) + end +end + +local function colorize_icon(objects, last_state, values, crit, style) + for i, o in ipairs(objects) do + local st = values[i] > crit[i] + if st ~= last_state[i] then + o:set_color(st and style.color.main or style.color.gray) + last_state[i] = st + end + end +end + +-- Construct complex indicator with progress bar and label on top of it +-------------------------------------------------------------------------------- +local function barvalue(progressbar_style, label_style) + local widg = {} + + -- construct layout with indicators + local progressbar = dcommon.bar.plain(progressbar_style) + local label = dcommon.textbox(nil, label_style) + + widg.layout = wibox.widget({ + label, nil, progressbar, + layout = wibox.layout.align.vertical, + }) + + -- setup functions + function widg:set_text(text) label:set_text(text) end + function widg:set_value(x) progressbar:set_value(x) end + + return widg +end + +-- Construct complex indicator with history chart, progress bar and label +-------------------------------------------------------------------------------- +local function fullchart(label_style, progressbar_style, chart_style, barvalue_height, maxm) + + local widg = {} + chart_style = redutil.table.merge(chart_style, { maxm = maxm }) + progressbar_style = redutil.table.merge(progressbar_style, { maxm = maxm }) + + -- construct layout with indicators + widg.barvalue = barvalue(progressbar_style, label_style) + widg.chart = dcommon.chart(chart_style) + widg.barvalue.layout:set_forced_height(barvalue_height) + + widg.layout = wibox.widget({ + widg.barvalue.layout, nil, widg.chart, + layout = wibox.layout.align.vertical, + }) + + return widg +end + +-- Construct speed info elements (fullchart and icon in one layout) +-------------------------------------------------------------------------------- +local function speed_line(image, maxm, el_style, style) + local fc = fullchart(el_style.label, el_style.progressbar, el_style.chart, style.barvalue_height, maxm) + local align = wibox.layout.align.horizontal() + local icon + + align:set_right(fc.layout) + align:set_forced_height(style.fullchart_height) + + if image then + icon = svgbox(image) + icon:set_color(style.color.gray) + align:set_left(wibox.container.margin(icon, 0, style.image_gap)) + end + + return fc, align, icon +end + + +-- Create a new speed meter widget +----------------------------------------------------------------------------------------------------------------------- +function speedmeter.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + local dwidget = {} + local storage = {} + local last = {} + + args = redutil.table.merge(default_args, args or {}) + style = redutil.table.merge(default_style(), style or {}) + local maxspeed = redutil.table.merge(default_maxspeed, args.maxspeed or {}) + + local elements_style = { + label = redutil.table.merge(style.label, { draw = "by_edges", color = style.color.gray }), + progressbar = redutil.table.merge(style.progressbar, { autoscale = args.autoscale, color = style.color }), + chart = redutil.table.merge(style.chart, { autoscale = args.autoscale, color = style.color.gray }) + } + + dwidget.style = style + + -- Construct indicators + -------------------------------------------------------------------------------- + local up_widget, up_layout, up_icon = speed_line(style.images[1], maxspeed.up, elements_style, style) + local down_widget, down_layout, down_icon = speed_line(style.images[2], maxspeed.down, elements_style, style) + + dwidget.area = wibox.widget({ + up_layout, nil, down_layout, + layout = wibox.layout.align.vertical + }) + + -- Update info + -------------------------------------------------------------------------------- + local function update() + local state = args.meter_function(args.interface, storage) + + set_fullchart_info({ up_widget, down_widget }, args.label, state, style) + + if style.images and args.crit then + colorize_icon({ up_icon, down_icon }, last, state, { args.crit.up, args.crit.down }, style) + end + end + + -- Set update timer + -------------------------------------------------------------------------------- + local t = timer({ timeout = args.timeout }) + t:connect_signal("timeout", update) + t:start() + t:emit_signal("timeout") + + -------------------------------------------------------------------------------- + return dwidget +end + +-- Config metatable to call module as function +----------------------------------------------------------------------------------------------------------------------- +function speedmeter.mt:__call(...) + return speedmeter.new(...) +end + +return setmetatable(speedmeter, speedmeter.mt) diff --git a/awesome/.config/awesome/redflat/desktop/textset.lua b/awesome/.config/awesome/redflat/desktop/textset.lua new file mode 100644 index 0000000..41587b7 --- /dev/null +++ b/awesome/.config/awesome/redflat/desktop/textset.lua @@ -0,0 +1,98 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat desktop text widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Text widget with text update function +----------------------------------------------------------------------------------------------------------------------- + +local setmetatable = setmetatable + +local textbox = require("wibox.widget.textbox") +local beautiful = require("beautiful") +local timer = require("gears.timer") + +local lgi = require("lgi") +local Pango = lgi.Pango + +local redutil = require("redflat.util") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local textset = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + font = "Sans 12", + spacing = 0, + color = { gray = "#525252" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "desktop.textset") or {}) +end + +-- Create a textset widget. It draws the time it is in a textbox. +-- @param format The time format. Default is " %a %b %d, %H:%M ". +-- @param timeout How often update the time. Default is 60. +-- @return A textbox widget +----------------------------------------------------------------------------------------------------------------------- +function textset.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + args = args or {} + --local funcarg = args.arg or {} + --local timeout = args.timeout or { 60 } + --local actions = args.actions or {} + --local async = args.async or {} + style = redutil.table.merge(default_style(), style or {}) + + -- Create widget + -------------------------------------------------------------------------------- + local widg = textbox() + widg:set_font(style.font) + widg:set_valign("top") + + -- Some pivate properties of awesome textbox v4.0 + widg._private.layout:set_justify(true) + widg._private.layout:set_spacing(Pango.units_from_double(style.spacing)) + + -- data setup + local data = {} + local timers = {} + for i = 1, #args do data[i] = "" end + + -- update info function + local function update() + local state = {} + for _, txt in ipairs(data) do state[#state + 1] = txt end + widg:set_markup(string.format('%s', style.color.gray, table.concat(state))) + end + + -- Set update timers + -------------------------------------------------------------------------------- + for i, block in ipairs(args) do + timers[i] = timer({ timeout = block.timeout or args[1].timeout }) + if block.async then + timers[i]:connect_signal("timeout", function() + block.async(function(state) data[i] = block.action(state); update() end) + end) + else + timers[i]:connect_signal("timeout", function() + data[i] = block.action(); update() + end) + end + timers[i]:start() + timers[i]:emit_signal("timeout") + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call textset module as function +----------------------------------------------------------------------------------------------------------------------- +function textset.mt:__call(...) + return textset.new(...) +end + +return setmetatable(textset, textset.mt) diff --git a/awesome/.config/awesome/redflat/float/apprunner.lua b/awesome/.config/awesome/redflat/float/apprunner.lua new file mode 100644 index 0000000..40b4918 --- /dev/null +++ b/awesome/.config/awesome/redflat/float/apprunner.lua @@ -0,0 +1,367 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat application launcher widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Widget with application list and quick search +-- Application list generated by redflat.dfparser +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ awful.menubar v3.5.2 +------ (c) 2009, 2011-2012 Antonio Terceiro, Alexander Yakushev +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local unpack = unpack or table.unpack + +local awful = require("awful") +local beautiful = require("beautiful") +local wibox = require("wibox") + +local svgbox = require("redflat.gauge.svgbox") +local dfparser = require("redflat.service.dfparser") +local redutil = require("redflat.util") +local decoration = require("redflat.float.decoration") +local redtip = require("redflat.float.hotkeys") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local apprunner = { applist = {}, command = "", keys = {} } + +local programs = {} +local lastquery + +-- key bindings +apprunner.keys.move = { + { + {}, "Down", function() apprunner:down() end, + { description = "Select next item", group = "Navigation" } + }, + { + {}, "Up", function() apprunner:up() end, + { description = "Select previous item", group = "Navigation" } + }, +} + +apprunner.keys.action = { + { + { "Mod4" }, "F1", function() redtip:show() end, + { description = "Show hotkeys helper", group = "Action" } + }, + -- fake keys used for hotkeys helper + { + {}, "Enter", nil, + { description = "Activate item", group = "Action" } + }, + { + {}, "Escape", nil, + { description = "Close widget", group = "Action" } + }, +} + +apprunner.keys.all = awful.util.table.join(apprunner.keys.move, apprunner.keys.action) + + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + itemnum = 5, + geometry = { width = 620, height = 520 }, + border_margin = { 10, 10, 10, 10 }, + title_height = 48, + prompt_height = 35, + title_icon = nil, + icon_margin = { 8, 12, 0, 0 }, + parser = {}, + list_text_vgap = 4, + list_icon_margin = { 6, 12, 6, 6 }, + name_font = "Sans 12", + comment_font = "Sans 12", + border_width = 2, + keytip = { geometry = { width = 400 } }, + dimage = redutil.base.placeholder(), + color = { border = "#575757", text = "#aaaaaa", highlight = "#eeeeee", main = "#b1222b", + bg = "#161616", bg_second = "#181818", wibox = "#202020", icon = "a0a0a0" }, + shape = nil + } + return redutil.table.merge(style, redutil.table.check(beautiful, "float.apprunner") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Fuction to build list item +-------------------------------------------------------------------------------- +local function construct_item(style) + local item = { + icon = wibox.widget.imagebox(), + name = wibox.widget.textbox(), + comment = wibox.widget.textbox(), + bg = style.color.bg, + cmd = "" + } + + item.name:set_font(style.name_font) + item.comment:set_font(style.comment_font) + + -- Construct item layouts + ------------------------------------------------------------ + local text_vertical = wibox.layout.align.vertical() + local text_horizontal = wibox.layout.align.horizontal() + text_horizontal:set_left(text_vertical) + text_vertical:set_top(wibox.container.margin(item.name, 0, 0, style.list_text_vgap)) + text_vertical:set_middle(item.comment) + + local item_horizontal = wibox.layout.align.horizontal() + item_horizontal:set_left(wibox.container.margin(item.icon, unpack(style.list_icon_margin))) + item_horizontal:set_middle(text_horizontal) + + item.layout = wibox.container.background(item_horizontal, item.bg) + + -- Item functions + ------------------------------------------------------------ + function item:set(args) + args = args or {} + + local name_text = awful.util.escape(args.Name) or "" + item.name:set_markup(name_text) + + local comment_text = args.Comment and awful.util.escape(args.Comment) + or args.Name and "No description" + or "" + item.comment:set_markup(comment_text) + + item.icon:set_image(args.icon_path or style.dimage) + item.icon:set_visible((args.Name)) + + item.cmd = args.cmdline + end + + function item:set_bg(color) + item.bg = color + item.layout:set_bg(color) + end + + function item:set_select() + item.layout:set_bg(style.color.main) + item.layout:set_fg(style.color.highlight) + end + + function item:set_unselect() + item.layout:set_bg(item.bg) + item.layout:set_fg(style.color.text) + end + + function item:run() + awful.spawn(item.cmd) + end + + ------------------------------------------------------------ + return item +end + +-- Fuction to build application list +-------------------------------------------------------------------------------- +local function construct_list(num, progs, style) + local list = { selected = 1, position = 1 } + + -- Construct application list + ------------------------------------------------------------ + local list_layout = wibox.layout.flex.vertical() + list.layout = wibox.container.background(list_layout, style.color.bg) + + list.items = {} + for i = 1, num do + list.items[i] = construct_item(style) + list.items[i]:set_bg((i % 2) == 1 and style.color.bg or style.color.bg_second) + list_layout:add(list.items[i].layout) + end + + -- Application list functions + ------------------------------------------------------------ + function list:set_select(index) + list.items[list.selected]:set_unselect() + list.selected = index + list.items[list.selected]:set_select() + end + + function list:update(t) + for i = list.position, (list.position - 1 + num) do list.items[i - list.position + 1]:set(t[i]) end + list:set_select(list.selected) + end + + -- First run actions + ------------------------------------------------------------ + list:update(progs) + list:set_select(1) + + ------------------------------------------------------------ + return list +end + +-- Sort function +-------------------------------------------------------------------------------- +local function sort_by_query(t, query) + local l = string.len(query) + local function s(a, b) + return string.lower(string.sub(a.Name, 1, l)) == query and string.lower(string.sub(b.Name, 1, l)) ~= query + end + table.sort(t, s) +end + +-- Function to filter application list by quick search input +-------------------------------------------------------------------------------- +local function list_filtrate(query) + if lastquery ~= query then + programs.current = {} + + for _, p in ipairs(programs.all) do + if string.match(string.lower(p.Name), query) then + table.insert(programs.current, p) + end + end + + sort_by_query(programs.current, query) + + apprunner.applist.position = 1 + apprunner.applist:update(programs.current) + apprunner.applist:set_select(1) + lastquery = query + end +end + +-- Functions to navigate through application list +----------------------------------------------------------------------------------------------------------------------- +function apprunner:down() + if self.applist.selected < math.min(self.itemnum, #programs.current) then + self.applist:set_select(self.applist.selected + 1) + elseif self.applist.selected + self.applist.position - 1 < #programs.current then + self.applist.position = self.applist.position + 1 + self.applist:update(programs.current) + end +end + +function apprunner:up() + if self.applist.selected > 1 then + self.applist:set_select(self.applist.selected - 1) + elseif self.applist.position > 1 then + self.applist.position = self.applist.position - 1 + self.applist:update(programs.current) + end +end + +-- Keypress handler +----------------------------------------------------------------------------------------------------------------------- +local function keypressed_callback(mod, key) + for _, k in ipairs(apprunner.keys.all) do + if redutil.key.match_prompt(k, mod, key) and k[3] then k[3](); return true end + end + return false +end + +-- Initialize apprunner widget +----------------------------------------------------------------------------------------------------------------------- +function apprunner:init() + + -- Initialize vars + -------------------------------------------------------------------------------- + local style = default_style() + self.itemnum = style.itemnum + self.keytip = style.keytip + + -- get full application list + programs.all = dfparser.program_list(style.parser) + programs.current = awful.util.table.clone(programs.all) + + -- Create quick search widget + -------------------------------------------------------------------------------- + self.textbox = wibox.widget.textbox() + self.textbox:set_ellipsize("start") + self.decorated_widget = decoration.textfield(self.textbox, style.field) + + -- Build application list + -------------------------------------------------------------------------------- + self.applist = construct_list(apprunner.itemnum, programs.current, style) + + -- Construct widget layouts + -------------------------------------------------------------------------------- + local prompt_width = style.geometry.width - 2 * style.border_margin[1] + - style.title_height - style.icon_margin[1] - style.icon_margin[2] + local prompt_layout = wibox.container.constraint(self.decorated_widget, "exact", prompt_width, style.prompt_height) + + local prompt_vertical = wibox.layout.align.vertical() + prompt_vertical:set_expand("outside") + prompt_vertical:set_middle(prompt_layout) + + local prompt_area_horizontal = wibox.layout.align.horizontal() + local title_image = svgbox(style.title_icon) + title_image:set_color(style.color.icon) + prompt_area_horizontal:set_left(wibox.container.margin(title_image, unpack(style.icon_margin))) + prompt_area_horizontal:set_right(prompt_vertical) + + local prompt_area_layout = wibox.container.constraint(prompt_area_horizontal, "exact", nil, style.title_height) + + local area_vertical = wibox.layout.align.vertical() + area_vertical:set_top(prompt_area_layout) + area_vertical:set_middle(wibox.container.margin(self.applist.layout, 0, 0, style.border_margin[3])) + local area_layout = wibox.container.margin(area_vertical, unpack(style.border_margin)) + + -- Create floating wibox for apprunner widget + -------------------------------------------------------------------------------- + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + + self.wibox:set_widget(area_layout) + self.wibox:geometry(style.geometry) +end + +-- Show apprunner widget +-- Wibox appears on call and hides after "enter" or "esc" pressed +----------------------------------------------------------------------------------------------------------------------- +function apprunner:show() + if not self.wibox then + self:init() + else + list_filtrate("") + self.applist:set_select(1) + end + + redutil.placement.centered(self.wibox, nil, mouse.screen.workarea) + self.wibox.visible = true + redtip:set_pack("Apprunner", self.keys.all, self.keytip.column, self.keytip.geometry) + + return awful.prompt.run({ + prompt = "", + textbox = self.textbox, + exe_callback = function () self.applist.items[self.applist.selected]:run() end, + done_callback = function () self:hide() end, + keypressed_callback = keypressed_callback, + changed_callback = list_filtrate, + }) +end + +function apprunner:hide() + self.wibox.visible = false + redtip:remove_pack() +end + +-- Set user hotkeys +----------------------------------------------------------------------------------------------------------------------- +function apprunner:set_keys(keys, layout) + layout = layout or "all" + if keys then + self.keys[layout] = keys + if layout ~= "all" then self.keys.all = awful.util.table.join(self.keys.move, self.keys.action) end + end + + -- self.tip = awful.util.table.join(self.keys.all, self._fake_keys) +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return apprunner diff --git a/awesome/.config/awesome/redflat/float/appswitcher.lua b/awesome/.config/awesome/redflat/float/appswitcher.lua new file mode 100644 index 0000000..f15783b --- /dev/null +++ b/awesome/.config/awesome/redflat/float/appswitcher.lua @@ -0,0 +1,459 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat appswitcher widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Advanced application switcher +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ Familiar Alt Tab by Joren Heit +------ https://github.com/jorenheit/awesome_alttab +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local type = type +local math = math +local unpack = unpack or table.unpack +local table = table + +local awful = require("awful") +local beautiful = require("beautiful") +local wibox = require("wibox") +local timer = require("gears.timer") +local gears = require("gears") + +local pixbuf +local function load_pixbuf() + local _ = require("lgi").Gdk + pixbuf = require("lgi").GdkPixbuf +end +local is_pixbuf_loaded = pcall(load_pixbuf) + +local dfparser = require("redflat.service.dfparser") +local redutil = require("redflat.util") +local redtip = require("redflat.float.hotkeys") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local appswitcher = { clients_list = {}, index = 1, hotkeys = {}, svgsize = 256, keys = {} } + +local cache = {} +local svgcache = {} +local _empty_surface = redutil.base.placeholder({ txt = " " }) + +-- key bindings +appswitcher.keys.move = { + { + {}, "Right", function() appswitcher:switch() end, + { description = "Select next app", group = "Navigation" } + }, + { + {}, "Left", function() appswitcher:switch({ reverse = true }) end, + { description = "Select previous app", group = "Navigation" } + }, +} + +appswitcher.keys.action = { + { + {}, "Return", function() appswitcher:hide() end, + { description = "Activate and exit", group = "Action" } + }, + { + { "Mod4" }, "F1", function() redtip:show() end, + { description = "Show hotkeys helper", group = "Action" } + }, +} + +appswitcher.keys.all = awful.util.table.join(appswitcher.keys.move, appswitcher.keys.action) + +appswitcher._fake_keys = { + { + {}, "N", nil, + { description = "Select app by key", group = "Navigation" } + }, +} + + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + wibox_height = 200, + label_height = 20, + title_height = 20, + icon_size = 48, + preview_gap = 20, + preview_format = 16/10, + preview_margin = { 20, 20, 20, 20 }, + border_margin = { 6, 6, 6, 6 }, + border_width = 2, + parser = {}, + update_timeout = 1, + min_icon_number = 4, + keytip = { geometry = { width = 400 }, exit = false }, + title_font = "Sans 12", + hotkeys = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" }, + font = { font = "Sans", size = 16, face = 0, slant = 0 }, + color = { border = "#575757", text = "#aaaaaa", main = "#b1222b", preview_bg = "#b1222b80", + wibox = "#202020", icon = "#a0a0a0", bg = "#161616", gray = "#575757" }, + shape = nil + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "float.appswitcher") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Create icon visual for client +-------------------------------------------------------------------------------- +local function get_icon_visual(icon_db, c, size) + local surface, buf + + if icon_db[string.lower(c.class)] then + local icon = icon_db[string.lower(c.class)] + + if type(icon) == "string" and string.match(icon, "%.svg") and is_pixbuf_loaded then + if svgcache[icon] then + buf = svgcache[icon] + else + buf = pixbuf.Pixbuf.new_from_file_at_scale(icon, size, size, true) + svgcache[icon] = buf + end + else + surface = gears.surface(icon) + end + else + surface = c.icon and gears.surface(c.icon) or _empty_surface + end + + return surface, buf +end + +-- Find all clients to be shown +-------------------------------------------------------------------------------- +local function clients_find(filter) + local clients = {} + for _, c in ipairs(client.get()) do + if not (c.skip_taskbar or c.hidden or c.type == "splash" or c.type == "dock" or c.type == "desktop") + and filter(c, mouse.screen) then + table.insert(clients, c) + end + end + return clients +end + +-- Loop iterating through table +-------------------------------------------------------------------------------- +local function iterate(tabl, i, diff) + local nxt = i + diff + + if nxt > #tabl then nxt = 1 + elseif nxt < 1 then nxt = #tabl end + + return nxt +end + +-- Select new focused window +-------------------------------------------------------------------------------- +local function focus_and_raise(c) + if c.minimized then c.minimized = false end + + if not c:isvisible() then + awful.tag.viewmore(c:tags(), c.screen) + end + + client.focus = c + c:raise() +end + +-- Initialize appswitcher widget +----------------------------------------------------------------------------------------------------------------------- +function appswitcher:init() + + -- Initialize style vars + -------------------------------------------------------------------------------- + local style = default_style() + local icon_db = dfparser.icon_list(style.parser) + local iscf = 1 -- icon size correction factor + + self.keytip = style.keytip + self._fake_keys[1][4].keyset = style.hotkeys + self:set_keys() + + -- Create floating wibox for appswitcher widget + -------------------------------------------------------------------------------- + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + + -- Keygrabber + -------------------------------------------------------------------------------- + local function focus_by_key(key) + self:switch({ index = awful.util.table.hasitem(style.hotkeys, key) }) + end + + self.keygrabber = function(mod, key, event) + if event == "press" then return false end + + for _, k in ipairs(self.keys.all) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return false end + end + + if awful.util.table.hasitem(style.hotkeys, key) then focus_by_key(key) end + end + + -- Function to form title string for given client (name and tags) + -------------------------------------------------------------------------------- + function self.title_generator(c) + local client_tags = {} + + for _, t in ipairs(c:tags()) do + client_tags[#client_tags + 1] = string.upper(t.name) + end + + local tag_text = string.format('[%s]', style.color.gray, table.concat(client_tags, " ")) + + return string.format("%s %s", awful.util.escape(c.name) or "Untitled", tag_text) + end + + -- Function to correct wibox size for given namber of icons + -------------------------------------------------------------------------------- + function self.size_correction(inum) + local w, h + inum = math.max(inum, style.min_icon_number) + local expen_h = (inum - 1) * style.preview_gap + style.preview_margin[1] + style.preview_margin[2] + + style.border_margin[1] + style.border_margin[2] + local expen_v = style.label_height + style.preview_margin[3] + style.preview_margin[4] + style.title_height + + style.border_margin[3] + style.border_margin[4] + + -- calculate width + local widget_height = style.wibox_height - expen_v + style.label_height + local max_width = screen[mouse.screen].geometry.width - 2 * self.wibox.border_width + local wanted_width = inum * ((widget_height - style.label_height) * style.preview_format) + expen_h + + -- check if need size correction + if wanted_width <= max_width then + -- correct width + w = wanted_width + h = style.wibox_height + iscf = 1 + else + -- correct height + local wanted_widget_width = (max_width - expen_h) / inum + local corrected_height = wanted_widget_width / style.preview_format + expen_v + + w = max_width + h = corrected_height + iscf = (corrected_height - expen_v) / (style.wibox_height - expen_v) + end + + -- set wibox size + self.wibox:geometry({ width = w, height = h }) + end + + -- Create custom widget to draw previews + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) return width, height end + + -- Draw + ------------------------------------------------------------ + function widg.draw(_, _, cr, _, height) + + -- calculate preview pattern size + local psize = { + width = (height - style.label_height) * style.preview_format, + height = (height - style.label_height) + } + + -- Shift pack of preview icons to center of widget if needed + if #self.clients_list < style.min_icon_number then + local tr = (style.min_icon_number - #self.clients_list) * (style.preview_gap + psize.width) / 2 + cr:translate(tr, 0) + end + + -- draw all previews + for i = 1, #self.clients_list do + + -- support vars + local sc, tr, surface, pixbuf_ + local c = self.clients_list[i] + + -- create surface and calculate scale and translate factors + if c:isvisible() then + surface = gears.surface(c.content) + local cg = c:geometry() + + if cg.width/cg.height > style.preview_format then + sc = psize.width / cg.width + tr = {0, (psize.height - sc * cg.height) / 2} + else + sc = psize.height / cg.height + tr = {(psize.width - sc * cg.width) / 2, 0} + end + else + -- surface = gears.surface(icon_db[string.lower(c.class)] or c.icon) + surface, pixbuf_ = get_icon_visual(icon_db, c, self.svgsize) + + -- sc = style.icon_size / surface.width * iscf + sc = style.icon_size / (surface and surface.width or pixbuf_.width) * iscf + tr = {(psize.width - style.icon_size * iscf) / 2, (psize.height - style.icon_size * iscf) / 2} + end + + -- translate cairo for every icon + if i > 1 then cr:translate(style.preview_gap + psize.width, 0) end + + -- draw background for preview + cr:set_source(gears.color(i == self.index and style.color.main or style.color.preview_bg)) + cr:rectangle(0, 0, psize.width, psize.height) + cr:fill() + + -- draw current preview or application icon + cr:save() + cr:translate(unpack(tr)) + cr:scale(sc, sc) + + if pixbuf_ then + cr:set_source_pixbuf(pixbuf_, 0, 0) + else + cr:set_source_surface(surface, 0, 0) + end + cr:paint() + cr:restore() + + -- draw label + local txt = style.hotkeys[i] or "?" + cr:set_source(gears.color(i == self.index and style.color.main or style.color.text)) + redutil.cairo.set_font(cr, style.font) + redutil.cairo.textcentre.horizontal(cr, { psize.width/2, psize.height + style.label_height }, txt) + end + + collectgarbage() -- prevents memory leak after complex draw function + end + + -- Set widget and create title for wibox + -------------------------------------------------------------------------------- + self.widget = widg + + self.titlebox = wibox.widget.textbox("TEXT") + self.titlebox:set_align("center") + self.titlebox:set_font(style.title_font) + + local title_layout = wibox.container.constraint(self.titlebox, "exact", nil, style.title_height) + local vertical_layout = wibox.layout.fixed.vertical() + local widget_bg = wibox.container.background( + wibox.container.margin(self.widget, unpack(style.preview_margin)), + style.color.bg + ) + vertical_layout:add(title_layout) + vertical_layout:add(widget_bg) + + self.wibox:set_widget(wibox.container.margin(vertical_layout, unpack(style.border_margin))) + + -- Set preview icons update timer + -------------------------------------------------------------------------------- + self.update_timer = timer({ timeout = style.update_timeout }) + self.update_timer:connect_signal("timeout", function() self.widget:emit_signal("widget::redraw_needed") end) + + -- Restart switcher if any client was closed + -------------------------------------------------------------------------------- + client.connect_signal("unmanage", + function(c) + if self.wibox.visible and awful.util.table.hasitem(self.clients_list, c) then + self:hide(true) + self:show(cache.args) + end + end + ) +end + +-- Show appswitcher widget +----------------------------------------------------------------------------------------------------------------------- +function appswitcher:show(args) + + args = args or {} + local filter = args.filter + local noaction = args.noaction + + if not self.wibox then self:init() end + if self.wibox.visible then + self:switch(args) + return + end + + local clients = clients_find(filter) + if #clients == 0 then return end + + self.clients_list = clients + cache.args = args + self.size_correction(#clients) + redutil.placement.centered(self.wibox, nil, mouse.screen.workarea) + self.update_timer:start() + awful.keygrabber.run(self.keygrabber) + + self.index = awful.util.table.hasitem(self.clients_list, client.focus) or 1 + self.titlebox:set_markup(self.title_generator(self.clients_list[self.index])) + if not noaction then self:switch(args) end + self.widget:emit_signal("widget::redraw_needed") + + self.wibox.visible = true + + redtip:set_pack( + "Appswitcher", self.tip, self.keytip.column, self.keytip.geometry, + self.keytip.exit and function() appswitcher:hide(true) end + ) +end + +-- Hide appswitcher widget +----------------------------------------------------------------------------------------------------------------------- +function appswitcher:hide(is_empty_call) + + if not self.wibox then self:init() end + if not self.wibox.visible then return end + self.wibox.visible = false + redtip:remove_pack() + self.update_timer:stop() + awful.keygrabber.stop(self.keygrabber) + + if not is_empty_call then focus_and_raise(self.clients_list[self.index]) end +end + +-- Toggle appswitcher widget +----------------------------------------------------------------------------------------------------------------------- +function appswitcher:switch(args) + args = args or {} + + if args.index then + if self.clients_list[args.index] then self.index = args.index end + else + local reverse = args.reverse or false + local diff = reverse and -1 or 1 + self.index = iterate(self.clients_list, self.index, diff) + end + + self.titlebox:set_markup(self.title_generator(self.clients_list[self.index])) + self.widget:emit_signal("widget::redraw_needed") +end + +-- Set user hotkeys +----------------------------------------------------------------------------------------------------------------------- +function appswitcher:set_keys(keys, layout) + layout = layout or "all" + if keys then + self.keys[layout] = keys + if layout ~= "all" then self.keys.all = awful.util.table.join(self.keys.move, self.keys.action) end + end + + self.tip = awful.util.table.join(self.keys.all, self._fake_keys) +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return appswitcher diff --git a/awesome/.config/awesome/redflat/float/bartip.lua b/awesome/.config/awesome/redflat/float/bartip.lua new file mode 100644 index 0000000..6931a15 --- /dev/null +++ b/awesome/.config/awesome/redflat/float/bartip.lua @@ -0,0 +1,221 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat titlebar helper widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Titlebar info widget +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local unpack = unpack or table.unpack + +local beautiful = require("beautiful") +local awful = require("awful") +local wibox = require("wibox") + +local redutil = require("redflat.util") +local redtitle = require("redflat.titlebar") +local redtip = require("redflat.float.hotkeys") +local svgbox = require("redflat.gauge.svgbox") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local bartip = {} + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + geometry = { width = 400, height = 60 }, + border_width = 2, + font = "Sans 14", + set_position = nil, + names = {}, + keytip = { geometry = { width = 600 } }, + shape = nil, + margin = { icon = { title = { 10, 10, 2, 2 }, state = { 10, 10, 2, 2 } } }, + icon = { + title = redutil.base.placeholder({ txt = "[]" }), + active = redutil.base.placeholder({ txt = "+" }), + absent = redutil.base.placeholder({ txt = "!" }), + disabled = redutil.base.placeholder({ txt = "X" }), + hidden = redutil.base.placeholder({ txt = "↓" }), + unknown = redutil.base.placeholder({ txt = "?" }), + }, + color = { border = "#575757", text = "#aaaaaa", main = "#b1222b", wibox = "#202020", + gray = "#575757", icon = "#a0a0a0" }, + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "float.bartip") or {}) +end + +-- key bindings +bartip.keys = {} +bartip.keys.bar = { + { + { "Mod4" }, "b", function() redtitle.toggle(client.focus); bartip:update() end, + { description = "Show/hide titlebar for focused client", group = "Titlebar control" } + }, + { + { "Mod4" }, "a", function() redtitle.toggle_all(); bartip:update() end, + { description = "Show/hide titlebar for all clients", group = "Titlebar control" } + }, + --{ + -- { "Mod4" }, "v", function() redtitle.switch(client.focus); bartip:update() end, + -- { description = "Switch titlebar view for focused client", group = "Titlebar control" } + --}, + { + { "Mod4" }, "n", function() redtitle.global_switch(); bartip:update() end, + { description = "Switch titlebar view for all clients", group = "Titlebar control" } + }, +} +bartip.keys.action = { + { + { "Mod4" }, "Super_L", function() bartip:hide() end, + { description = "Close top list widget", group = "Action" } + }, + { + { "Mod4" }, "F1", function() redtip:show() end, + { description = "Show hotkeys helper", group = "Action" } + }, +} + +bartip.keys.all = awful.util.table.join(bartip.keys.bar, bartip.keys.action) + + +-- Initialize widget +----------------------------------------------------------------------------------------------------------------------- +function bartip:init() + + -- Initialize vars + -------------------------------------------------------------------------------- + local style = default_style() + self.style = style + + -- Create floating wibox for top widget + -------------------------------------------------------------------------------- + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + + self.wibox:geometry(style.geometry) + + -- Widget layout setup + -------------------------------------------------------------------------------- + self.label = wibox.widget.textbox() + self.label:set_align("center") + self.label:set_font(style.font) + + local title_icon = svgbox(self.style.icon.title) + title_icon:set_color(self.style.color.icon) + + self.state_icon = svgbox() + + --self.wibox:set_widget(self.label) + self.wibox:setup({ + wibox.container.margin(title_icon, unpack(self.style.margin.icon.title)), + self.label, + wibox.container.margin(self.state_icon, unpack(self.style.margin.icon.state)), + layout = wibox.layout.align.horizontal + }) + + -- Keygrabber + -------------------------------------------------------------------------------- + self.keygrabber = function(mod, key, event) + if event == "release" then + for _, k in ipairs(self.keys.action) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return end + end + else + for _, k in ipairs(self.keys.all) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return end + end + end + end + + -- First run actions + -------------------------------------------------------------------------------- + self:set_keys() +end + +-- Widget actions +----------------------------------------------------------------------------------------------------------------------- + +local function get_title_state(c) + if not c then return "unknown" end + + local model = redtitle.get_model(c) + local state = not model and "absent" + or model.hidden and "disabled" + or model.cutted and "hidden" + or "active" + + return state, model and model.size or nil +end + +-- Update +-------------------------------------------------------------------------------- +function bartip:update() + local name = self.style.names[redtitle._index] or "Unknown" + local state, size = get_title_state(client.focus) + local size_mark = size and string.format(" [%d]", size) or "" + + self.label:set_markup(string.format( + '%s%s', + self.style.color.text, name, self.style.color.gray, size_mark + )) + + self.state_icon:set_image(self.style.icon[state]) + self.state_icon:set_color(state == "absent" and self.style.color.main or self.style.color.icon) +end + +-- Show +-------------------------------------------------------------------------------- +function bartip:show() + if not self.wibox then self:init() end + + if not self.wibox.visible then + if self.style.set_position then + self.style.set_position(self.wibox) + else + redutil.placement.centered(self.wibox, nil, mouse.screen.workarea) + end + redutil.placement.no_offscreen(self.wibox, self.style.screen_gap, screen[mouse.screen].workarea) + + self:update() + self.wibox.visible = true + awful.keygrabber.run(self.keygrabber) + redtip:set_pack( + "Titlebar", self.tip, self.style.keytip.column, self.style.keytip.geometry, + function() self:hide() end + ) + end +end + +-- Hide +-------------------------------------------------------------------------------- +function bartip:hide() + self.wibox.visible = false + awful.keygrabber.stop(self.keygrabber) + redtip:remove_pack() +end + +-- Set user hotkeys +----------------------------------------------------------------------------------------------------------------------- +function bartip:set_keys(keys, layout) + layout = layout or "all" + if keys then + self.keys[layout] = keys + if layout ~= "all" then self.keys.all = awful.util.table.join(self.keys.bar, self.keys.action) end + end + + self.tip = self.keys.all +end + + +-- End +----------------------------------------------------------------------------------------------------------------------- +return bartip diff --git a/awesome/.config/awesome/redflat/float/brightness.lua b/awesome/.config/awesome/redflat/float/brightness.lua new file mode 100644 index 0000000..e7e36e0 --- /dev/null +++ b/awesome/.config/awesome/redflat/float/brightness.lua @@ -0,0 +1,62 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat brightness control widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Brightness control using xbacklight +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local string = string +local awful = require("awful") +local beautiful = require("beautiful") + +local rednotify = require("redflat.float.notify") +local redutil = require("redflat.util") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local brightness = { dbus_correction = 1 } + +local defaults = { down = false, step = 2 } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + notify = {}, + } + return redutil.table.merge(style, redutil.table.check(beautiful, "float.brightness") or {}) +end + +-- Change brightness level +----------------------------------------------------------------------------------------------------------------------- + +-- Change with xbacklight +------------------------------------------------------------ +function brightness:change_with_xbacklight(args) + args = redutil.table.merge(defaults, args or {}) + + local command = string.format("xbacklight %s %d", args.down and "-dec" or "-inc", args.step) + awful.spawn.easy_async(command, self.info_with_xbacklight) +end + +-- Update brightness level info +----------------------------------------------------------------------------------------------------------------------- + +-- Update from xbacklight +------------------------------------------------------------ +function brightness.info_with_xbacklight() + if not brightness.style then brightness.style = default_style() end + awful.spawn.easy_async( + "xbacklight -get", + function(output) + rednotify:show(redutil.table.merge( + { value = output / 100, text = string.format('%.0f', output) .. "%" }, + brightness.style.notify + )) + end + ) +end + +----------------------------------------------------------------------------------------------------------------------- +return brightness diff --git a/awesome/.config/awesome/redflat/float/clientmenu.lua b/awesome/.config/awesome/redflat/float/clientmenu.lua new file mode 100644 index 0000000..852ede2 --- /dev/null +++ b/awesome/.config/awesome/redflat/float/clientmenu.lua @@ -0,0 +1,383 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat client menu widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Custom float widget that provides client actions like the tasklist's window +-- menu but may be used outside of the tasklist context on any client. Useful +-- for titlebar click action or other custom client-related keybindings for +-- faster access of client actions without traveling to the tasklist. +----------------------------------------------------------------------------------------------------------------------- +-- Authored by M4he +-- Some code was taken from +------ redflat.widget.tasklist +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local ipairs = ipairs +local table = table +local beautiful = require("beautiful") +local awful = require("awful") +local wibox = require("wibox") + +local redutil = require("redflat.util") +local separator = require("redflat.gauge.separator") +local redmenu = require("redflat.menu") +local svgbox = require("redflat.gauge.svgbox") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local clientmenu = { mt = {}, } + +local last = { + client = nil, + screen = mouse.screen, + tag_screen = mouse.screen +} + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + icon = { unknown = redutil.base.placeholder(), + minimize = redutil.base.placeholder(), + close = redutil.base.placeholder() }, + micon = { blank = redutil.base.placeholder({ txt = " " }), + check = redutil.base.placeholder({ txt = "+" }) }, + layout_icon = { unknown = redutil.base.placeholder() }, + actionline = { height = 28 }, + stateline = { height = 35 }, + state_iconsize = { width = 20, height = 20 }, + action_iconsize = { width = 18, height = 18 }, + separator = { marginh = { 3, 3, 5, 5 }, marginv = { 3, 3, 3, 3 } }, + tagmenu = { icon_margin = { 2, 2, 2, 2 } }, + hide_action = { move = true, + add = false, + min = true, + floating = false, + sticky = false, + ontop = false, + below = false, + maximized = false }, + color = { main = "#b1222b", icon = "#a0a0a0", gray = "#404040", highlight = "#eeeeee" }, + } + style.menu = { + ricon_margin = { 2, 2, 2, 2 }, + hide_timeout = 1, + select_first = false, + nohide = true + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "float.clientmenu") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Function to build item list for submenu +-------------------------------------------------------------------------------- +local function tagmenu_items(action, style) + local items = {} + for _, t in ipairs(last.screen.tags) do + if not awful.tag.getproperty(t, "hide") then + table.insert( + items, + { t.name, function() action(t) end, style.micon.blank, style.micon.blank } + ) + end + end + return items +end + +-- Function to rebuild the submenu entries according to current screen's tags +-------------------------------------------------------------------------------- +local function tagmenu_rebuild(menu, submenu_index, style) + for _, index in ipairs(submenu_index) do + local new_items + if index == 1 then + new_items = tagmenu_items(clientmenu.movemenu_action, style) + else + new_items = tagmenu_items(clientmenu.addmenu_action, style) + end + menu.items[index].child:replace_items(new_items) + end +end + +-- Function to update tag submenu icons +-------------------------------------------------------------------------------- +local function tagmenu_update(c, menu, submenu_index, style) + -- if the screen has changed (and thus the tags) since the last time the + -- tagmenu was built, rebuild it first + if last.tag_screen ~= mouse.screen then + tagmenu_rebuild(menu, submenu_index, style) + last.tag_screen = mouse.screen + end + for k, t in ipairs(last.screen.tags) do + if not awful.tag.getproperty(t, "hide") then + + -- set layout icon for every tag + local l = awful.layout.getname(awful.tag.getproperty(t, "layout")) + + local check_icon = style.micon.blank + if c then + local client_tags = c:tags() + check_icon = awful.util.table.hasitem(client_tags, t) and style.micon.check or check_icon + end + + for _, index in ipairs(submenu_index) do + local submenu = menu.items[index].child + if submenu.items[k].icon then + submenu.items[k].icon:set_image(style.layout_icon[l] or style.layout_icon.unknown) + end + + -- set "checked" icon if tag active for given client + -- otherwise set empty icon + if c then + if submenu.items[k].right_icon then + submenu.items[k].right_icon:set_image(check_icon) + end + end + + -- update position of any visible submenu + if submenu.wibox.visible then submenu:show() end + end + end + end +end + +-- Function to construct menu line with state icons +-------------------------------------------------------------------------------- +local function state_line_construct(state_icons, setup_layout, style) + local stateboxes = {} + + for i, v in ipairs(state_icons) do + -- create widget + stateboxes[i] = svgbox(v.icon) + stateboxes[i]:set_forced_width(style.state_iconsize.width) + stateboxes[i]:set_forced_height(style.state_iconsize.height) + + -- set widget in line + local l = wibox.layout.align.horizontal() + l:set_expand("outside") + l:set_second(stateboxes[i]) + setup_layout:add(l) + + -- set mouse action + stateboxes[i]:buttons(awful.util.table.join(awful.button({}, 1, + function() + v.action() + stateboxes[i]:set_color(v.indicator(last.client) and style.color.main or style.color.gray) + end + ))) + end + + return stateboxes +end + +-- Function to construct menu line with action icons (minimize, close) +-------------------------------------------------------------------------------- +local function action_line_construct(setup_layout, style) + local sep = separator.vertical(style.separator) + + local function actionbox_construct(icon, action) + local iconbox = svgbox(icon, nil, style.color.icon) + iconbox:set_forced_width(style.action_iconsize.width) + iconbox:set_forced_height(style.action_iconsize.height) + + -- center iconbox both vertically and horizontally + local vert_wrapper = wibox.layout.align.vertical() + vert_wrapper:set_second(iconbox) + vert_wrapper:set_expand("outside") + local horiz_wrapper = wibox.layout.align.horizontal() + horiz_wrapper:set_second(vert_wrapper) + horiz_wrapper:set_expand("outside") + + -- wrap into a background container to allow bg color change of area + local actionbox = wibox.container.background(horiz_wrapper) + + -- set mouse action + actionbox:buttons(awful.util.table.join(awful.button({}, 1, + function() + action() + end + ))) + actionbox:connect_signal("mouse::enter", + function() + iconbox:set_color(style.color.highlight) + actionbox.bg = style.color.main + end + ) + actionbox:connect_signal("mouse::leave", + function() + iconbox:set_color(style.color.icon) + actionbox.bg = nil + end + ) + return actionbox + end + + -- minimize button + local minimize_box = actionbox_construct( + style.icon.minimize, + function() + last.client.minimized = not last.client.minimized + if style.hide_action["min"] then clientmenu.menu:hide() end + end + ) + setup_layout:set_first(minimize_box) + + -- separator + setup_layout:set_second(sep) + + -- close button + local close_box = actionbox_construct( + style.icon.close, + function() + last.client:kill() + clientmenu.menu:hide() + end + ) + setup_layout:set_third(close_box) +end + +-- Calculate menu position +-------------------------------------------------------------------------------- +local function coords_calc(menu) + local coords = mouse.coords() + coords.x = coords.x - menu.wibox.width / 2 - menu.wibox.border_width + + return coords +end + +-- Initialize window menu widget +----------------------------------------------------------------------------------------------------------------------- +function clientmenu:init(style) + style = redutil.table.merge(default_style(), style or {}) + + self.hide_check = function(action) + if style.hide_action[action] then self.menu:hide() end + end + + -- Create array of state icons + -- associate every icon with action and state indicator + -------------------------------------------------------------------------------- + local function icon_table_generator_prop(property) + return { + icon = style.icon[property] or style.icon.unknown, + action = function() last.client[property] = not last.client[property]; self.hide_check(property) end, + indicator = function(c) return c[property] end + } + end + + local state_icons = { + icon_table_generator_prop("floating"), + icon_table_generator_prop("sticky"), + icon_table_generator_prop("ontop"), + icon_table_generator_prop("below"), + icon_table_generator_prop("maximized"), + } + + -- Construct menu + -------------------------------------------------------------------------------- + + -- Window action line construction + ------------------------------------------------------------ + + local actionline_horizontal = wibox.layout.align.horizontal() + actionline_horizontal:set_expand("outside") + local actionline = wibox.container.constraint(actionline_horizontal, "exact", nil, style.actionline.height) + action_line_construct(actionline_horizontal, style) + + -- Window state line construction + ------------------------------------------------------------ + + -- layouts + local stateline_horizontal = wibox.layout.flex.horizontal() + local stateline_vertical = wibox.layout.align.vertical() + stateline_vertical:set_second(stateline_horizontal) + stateline_vertical:set_expand("outside") + local stateline = wibox.container.constraint(stateline_vertical, "exact", nil, style.stateline.height) + + -- set all state icons in line + local stateboxes = state_line_construct(state_icons, stateline_horizontal, style) + + -- update function for state icons + local function stateboxes_update(c, icons, boxes) + for i, v in ipairs(icons) do + boxes[i]:set_color(v.indicator(c) and style.color.main or style.color.gray) + end + end + + -- Separators config + ------------------------------------------------------------ + local menusep = { widget = separator.horizontal(style.separator) } + + -- menu item actions + self.movemenu_action = function(t) + last.client:move_to_tag(t); awful.layout.arrange(t.screen); self.hide_check("move") + end + + self.addmenu_action = function(t) + last.client:toggle_tag(t); awful.layout.arrange(t.screen); self.hide_check("add") + end + + -- Construct tag submenus ("move" and "add") + ------------------------------------------------------------ + local movemenu_items = tagmenu_items(self.movemenu_action, style) + local addmenu_items = tagmenu_items(self.addmenu_action, style) + + -- Create menu + ------------------------------------------------------------ + self.menu = redmenu({ + theme = style.menu, + items = { + { widget = actionline, focus = true }, + menusep, + { "Move to tag", { items = movemenu_items, theme = style.tagmenu } }, + { "Add to tag", { items = addmenu_items, theme = style.tagmenu } }, + menusep, + { widget = stateline, focus = true } + } + }) + + -- Widget update functions + -------------------------------------------------------------------------------- + function self:update(c) + if self.menu.wibox.visible then + stateboxes_update(c, state_icons, stateboxes) + tagmenu_update(c, self.menu, { 1, 2 }, style) + end + end + + -- Signals setup + -------------------------------------------------------------------------------- + local client_signals = { + "property::ontop", "property::floating", "property::below", "property::maximized", + "tagged", "untagged" -- refresh tagmenu when client's tags change + } + for _, sg in ipairs(client_signals) do + client.connect_signal(sg, function() self:update(last.client) end) + end +end + +-- Show window menu widget +----------------------------------------------------------------------------------------------------------------------- +function clientmenu:show(c) + + -- init menu if needed + if not self.menu then self:init() end + + -- toggle menu + if self.menu.wibox.visible and c == last.client and mouse.screen == last.screen then + self.menu:hide() + else + last.client = c + last.screen = mouse.screen + self.menu:show({ coords = coords_calc(self.menu) }) + + if self.menu.hidetimer.started then self.menu.hidetimer:stop() end + self:update(c) + end +end + +return setmetatable(clientmenu, clientmenu.mt) diff --git a/awesome/.config/awesome/redflat/float/control.lua b/awesome/.config/awesome/redflat/float/control.lua new file mode 100644 index 0000000..b8e75a5 --- /dev/null +++ b/awesome/.config/awesome/redflat/float/control.lua @@ -0,0 +1,358 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat floating window manager -- +----------------------------------------------------------------------------------------------------------------------- +-- Widget to control single flating window size and posioning +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local unpack = unpack or table.unpack + +local beautiful = require("beautiful") +local awful = require("awful") +local wibox = require("wibox") + +local rednotify = require("redflat.float.notify") +local redutil = require("redflat.util") +local redtip = require("redflat.float.hotkeys") +local svgbox = require("redflat.gauge.svgbox") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local control = {} + +-- Resize mode alias +local RESIZE_MODE = { FULL = 1, HORIZONTAL = 2, VERTICAL = 3 } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + geometry = { width = 400, height = 60 }, + border_width = 2, + font = "Sans 14", + set_position = nil, + notify = {}, + keytip = { geometry = { width = 600 } }, + shape = nil, + steps = { 1, 10, 20, 50 }, + default_step = 2, + onscreen = true, + margin = { icon = { onscreen = { 10, 10, 2, 2 }, mode = { 10, 10, 2, 2 } } }, + icon = { + resize = {}, + onscreen = redutil.base.placeholder({ txt = "X" }), + }, + color = { border = "#575757", text = "#aaaaaa", main = "#b1222b", wibox = "#202020", + gray = "#575757", icon = "#a0a0a0" }, + } + + style.icon.resize[RESIZE_MODE.FULL] = redutil.base.placeholder({ txt = "F" }) + style.icon.resize[RESIZE_MODE.HORIZONTAL] = redutil.base.placeholder({ txt = "H" }) + style.icon.resize[RESIZE_MODE.VERTICAL] = redutil.base.placeholder({ txt = "V" }) + + return redutil.table.merge(style, redutil.table.check(beautiful, "float.control") or {}) +end + +-- key bindings +control.keys = {} +control.keys.control = { + { + { "Mod4" }, "c", function() control:center() end, + { description = "Put window at the center", group = "Window control" } + }, + { + { "Mod4" }, "q", function() control:resize() end, + { description = "Increase window size", group = "Window control" } + }, + { + { "Mod4" }, "a", function() control:resize(true) end, + { description = "Decrease window size", group = "Window control" } + }, + { + { "Mod4" }, "l", function() control:move("right") end, + { description = "Move window to right", group = "Window control" } + }, + { + { "Mod4" }, "j", function() control:move("left") end, + { description = "Move window to left", group = "Window control" } + }, + { + { "Mod4" }, "k", function() control:move("bottom") end, + { description = "Move window to bottom", group = "Window control" } + }, + { + { "Mod4" }, "i", function() control:move("top") end, + { description = "Move window to top", group = "Window control" } + }, + { + { "Mod4" }, "n", function() control:switch_resize_mode() end, + { description = "Switch moving/resizing mode", group = "Mode" } + }, + { + { "Mod4" }, "s", function() control:switch_onscreen() end, + { description = "Switch off screen check", group = "Mode" } + }, +} +control.keys.action = { + { + { "Mod4" }, "Super_L", function() control:hide() end, + { description = "Close top list widget", group = "Action" } + }, + { + { "Mod4" }, "F1", function() redtip:show() end, + { description = "Show hotkeys helper", group = "Action" } + }, +} + +control.keys.all = awful.util.table.join(control.keys.control, control.keys.action) + +control._fake_keys = { + { + {}, "N", nil, + { description = "Select move/resize step", group = "Mode", + keyset = { "1", "2", "3", "4", "5", "6", "7", "8", "9" } } + }, +} + + +-- Support function +----------------------------------------------------------------------------------------------------------------------- +local function control_off_screen(window) + local wa = screen[mouse.screen].workarea + local newg = window:geometry() + + if newg.width > wa.width then window:geometry({ width = wa.width, x = wa.x }) end + if newg.height > wa.height then window:geometry({ height = wa.height, y = wa.y }) end + + redutil.placement.no_offscreen(window, nil, wa) +end + +-- Initialize widget +----------------------------------------------------------------------------------------------------------------------- +function control:init() + + -- Initialize vars + -------------------------------------------------------------------------------- + local style = default_style() + self.style = style + self.client = nil + self.step = style.steps[style.default_step] + + self.resize_mode = RESIZE_MODE.FULL + self.onscreen = style.onscreen + + -- Create floating wibox for top widget + -------------------------------------------------------------------------------- + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + + self.wibox:geometry(style.geometry) + + -- Widget layout setup + -------------------------------------------------------------------------------- + self.label = wibox.widget.textbox() + self.label:set_align("center") + self.label:set_font(style.font) + + self.onscreen_icon = svgbox(self.style.icon.onscreen) + self.onscreen_icon:set_color(self.onscreen and self.style.color.main or self.style.color.icon) + + self.mode_icon = svgbox(self.style.icon.resize[self.resize_mode]) + self.mode_icon:set_color(self.style.color.icon) + + self.wibox:setup({ + wibox.container.margin(self.onscreen_icon, unpack(self.style.margin.icon.onscreen)), + self.label, + wibox.container.margin(self.mode_icon, unpack(self.style.margin.icon.mode)), + layout = wibox.layout.align.horizontal + }) + + -- Keygrabber + -------------------------------------------------------------------------------- + self.keygrabber = function(mod, key, event) + if event == "release" then + for _, k in ipairs(self.keys.action) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return end + end + else + for _, k in ipairs(self.keys.all) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return end + end + if string.match("123456789", key) then self:choose_step(tonumber(key)) end + end + end + + -- First run actions + -------------------------------------------------------------------------------- + self:set_keys() +end + +-- Window control +----------------------------------------------------------------------------------------------------------------------- + +-- Put window at center of screen +-------------------------------------------------------------------------------- +function control:center() + if not self.client then return end + redutil.placement.centered(self.client, nil, mouse.screen.workarea) + + if self.onscreen then control_off_screen(self.client) end + self:update() +end + +-- Change window size +-------------------------------------------------------------------------------- +function control:resize(is_shrinking) + if not self.client then return end + + -- calculate new size + local g = self.client:geometry() + local d = self.step * (is_shrinking and -1 or 1) + local newg + + if self.resize_mode == RESIZE_MODE.FULL then + newg = { x = g.x - d, y = g.y - d, width = g.width + 2 * d, height = g.height + 2 * d } + elseif self.resize_mode == RESIZE_MODE.HORIZONTAL then + newg = { x = g.x - d, width = g.width + 2 * d } + elseif self.resize_mode == RESIZE_MODE.VERTICAL then + newg = { y = g.y - d, height = g.height + 2 * d } + end + + -- validate new size + if newg.height and newg.height <= 0 or newg.width and newg.width < 0 then return end + + -- apply new size + self.client:geometry(newg) + if self.onscreen then control_off_screen(self.client) end + self:update() +end + +-- Move by direction +-------------------------------------------------------------------------------- +function control:move(direction) + if not self.client then return end + + local g = self.client:geometry() + local d = self.step * ((direction == "left" or direction == "top") and -1 or 1) + + if direction == "left" or direction == "right" then + self.client:geometry({ x = g.x + d }) + else + self.client:geometry({ y = g.y + d }) + end + + if self.onscreen then control_off_screen(self.client) end +end + + +-- Widget actions +----------------------------------------------------------------------------------------------------------------------- + +-- Update +-------------------------------------------------------------------------------- +function control:update() + if not self.client then return end + + local g = self.client:geometry() + local size_label = string.format("%sx%s", g.width, g.height) + + self.label:set_markup(string.format( + '%s [%d]', + self.style.color.text, size_label, self.style.color.gray, self.step + )) +end + +-- Select move/resize step by index +-------------------------------------------------------------------------------- +function control:choose_step(index) + if self.style.steps[index] then self.step = self.style.steps[index] end + self:update() +end + +-- Switch resize mode +-------------------------------------------------------------------------------- +function control:switch_resize_mode() + self.resize_mode = self.resize_mode + 1 + if not awful.util.table.hasitem(RESIZE_MODE, self.resize_mode) then self.resize_mode = RESIZE_MODE.FULL end + + self.mode_icon:set_image(self.style.icon.resize[self.resize_mode]) +end + +-- Switch onscreen mode +-------------------------------------------------------------------------------- +function control:switch_onscreen() + self.onscreen = not self.onscreen + self.onscreen_icon:set_color(self.onscreen and self.style.color.main or self.style.color.icon) + + if self.onscreen then + control_off_screen(self.client) + self:update() + end +end + +-- Show +-------------------------------------------------------------------------------- +function control:show() + if not self.wibox then self:init() end + + if not self.wibox.visible then + -- check if focused client floating + local layout = awful.layout.get(mouse.screen) + local is_floating = client.focus and (client.focus.floating or layout.name == "floating") + and not client.focus.maximized + + if not is_floating then + rednotify:show(redutil.table.merge({ text = "No floating window focused" }, self.style.notify)) + return + end + self.client = client.focus + + -- show widget + if self.style.set_position then + self.style.set_position(self.wibox) + else + redutil.placement.centered(self.wibox, nil, mouse.screen.workarea) + end + redutil.placement.no_offscreen(self.wibox, self.style.screen_gap, screen[mouse.screen].workarea) + + self:update() + self.wibox.visible = true + awful.keygrabber.run(self.keygrabber) + redtip:set_pack( + "Floating window", self.tip, self.style.keytip.column, self.style.keytip.geometry, + function() self:hide() end + ) + end +end + +-- Hide +-------------------------------------------------------------------------------- +function control:hide() + self.wibox.visible = false + awful.keygrabber.stop(self.keygrabber) + redtip:remove_pack() + self.client = nil +end + +-- Set user hotkeys +----------------------------------------------------------------------------------------------------------------------- +function control:set_keys(keys, layout) + layout = layout or "all" + if keys then + self.keys[layout] = keys + if layout ~= "all" then self.keys.all = awful.util.table.join(self.keys.control, self.keys.action) end + end + + self.tip = awful.util.table.join(self.keys.all, self._fake_keys) +end + + +-- End +----------------------------------------------------------------------------------------------------------------------- +return control diff --git a/awesome/.config/awesome/redflat/float/decoration.lua b/awesome/.config/awesome/redflat/float/decoration.lua new file mode 100644 index 0000000..1b32068 --- /dev/null +++ b/awesome/.config/awesome/redflat/float/decoration.lua @@ -0,0 +1,88 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat decorations -- +----------------------------------------------------------------------------------------------------------------------- +-- +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local awful = require("awful") +local wibox = require("wibox") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local decor = {} + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function button_style() + local style = { + color = { shadow3 = "#1c1c1c", button = "#575757", + shadow4 = "#767676", text = "#cccccc", pressed = "404040" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "float.decoration.button") or {}) +end + +local function field_style() + local style = { + color = { bg = "#161616", shadow1 = "#141414", shadow2 = "#313131" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "float.decoration.field") or {}) +end + +-- Button element +----------------------------------------------------------------------------------------------------------------------- +function decor.button(textbox, action, style) + + style = redutil.table.merge(button_style(), style or {}) + + -- Widget and layouts + -------------------------------------------------------------------------------- + textbox:set_align("center") + local button_widget = wibox.container.background(textbox, style.color.button) + button_widget:set_fg(style.color.text) + + local bord1 = wibox.container.background(wibox.container.margin(button_widget, 1, 1, 1, 1), style.color.shadow4) + local bord2 = wibox.container.background(wibox.container.margin(bord1, 1, 1, 1, 1), style.color.shadow3) + + -- Button + -------------------------------------------------------------------------------- + local press_func = function() + button_widget:set_bg(style.color.pressed) + end + local release_func = function() + button_widget:set_bg(style.color.button) + action() + end + + button_widget:buttons(awful.button({}, 1, press_func, release_func)) + + -- Signals + -------------------------------------------------------------------------------- + button_widget:connect_signal( + "mouse::leave", + function() + button_widget:set_bg(style.color.button) + end + ) + + -------------------------------------------------------------------------------- + return bord2 +end + +-- Input text field +----------------------------------------------------------------------------------------------------------------------- +function decor.textfield(textbox, style) + style = redutil.table.merge(field_style(), style or {}) + local field = wibox.container.background(textbox, style.color.bg) + local bord1 = wibox.container.background(wibox.container.margin(field, 1, 1, 1, 1), style.color.shadow1) + local bord2 = wibox.container.background(wibox.container.margin(bord1, 1, 1, 1, 1), style.color.shadow2) + return bord2 +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return decor diff --git a/awesome/.config/awesome/redflat/float/hotkeys.lua b/awesome/.config/awesome/redflat/float/hotkeys.lua new file mode 100644 index 0000000..a3bb7d1 --- /dev/null +++ b/awesome/.config/awesome/redflat/float/hotkeys.lua @@ -0,0 +1,361 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat hotkeys helper widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Widget with list of hotkeys +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local math = math +local unpack = unpack or table.unpack + +local awful = require("awful") +local beautiful = require("beautiful") +local wibox = require("wibox") +local timer = require("gears.timer") + +local redflat = require("redflat") +local redutil = require("redflat.util") + + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local hotkeys = { keypack = {}, lastkey = nil, cache = {}, boxes = {} } +local hasitem = awful.util.table.hasitem + +-- key bindings +hotkeys.keys = { close = { "Escape" }, close_all = { "Super_L" } } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + geometry = { width = 800 }, + border_margin = { 10, 10, 10, 10 }, + tspace = 5, + delim = " ", + border_width = 2, + ltimeout = 0.05, + font = "Sans 12", + keyfont = "Sans bold 12", + titlefont = "Sans bold 14", + is_align = false, + separator = {}, + heights = { key = 20, title = 24 }, + color = { border = "#575757", text = "#aaaaaa", main = "#b1222b", wibox = "#202020", + gray = "#575757" }, + shape = nil + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "float.hotkeys") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Calculate keytip length +-------------------------------------------------------------------------------- +local function check_key_len(k) + local res = k.key:len() + for _, v in pairs(k.mod) do res = res + v:len() + 1 end + return res +end + +-- Parse raw key table +-------------------------------------------------------------------------------- +local keysort = function(a, b) + if a.length ~= b.length then + return a.length < b.length + else + return a.key < b.key + end +end + +local function parse(rawkeys, columns) + local keys = {} + columns = columns or 1 + + local rk = {} + for _, k in ipairs(rawkeys) do if k[#k].description then table.insert(rk, k) end end + local p = math.ceil(#rk / columns) + + -- dirty trick for raw sorting + local sp = {} + for _, v in ipairs(rk) do + if not hasitem(sp, v[#v].group) then table.insert(sp, v[#v].group) end + end + table.sort(rk, function(a, b) + local ai, bi = hasitem(sp, a[#a].group), hasitem(sp, b[#b].group) + if ai ~= bi then + return ai < bi + else + return a[2] < b[2] + end + end) + + -- split keys to columns + for i = 1, columns do + keys[i] = { groups = {}, length = nil, names = {} } + local chunk = { unpack(rk, 1, p) } + rk = { unpack(rk, p + 1) } + + -- build key column + for _, v in ipairs(chunk) do + local data = v[#v] + local k = { + mod = v[1], key = v[2], + description = data.description, + group = data.group, + keyset = data.keyset or { v[2] }, + length = check_key_len({ mod = v[1], key = v[2] }) + } + + if not keys[i].groups[k.group] then + keys[i].groups[k.group] = {} + table.insert(keys[i].names, k.group) -- sorted names list to save group order + end + table.insert(keys[i].groups[k.group], k) + + -- calculate max tip lenght + if not keys[i].length or keys[i].length < k.length then keys[i].length = k.length end + end + + -- sort key by lenght inside group + for _, group in pairs(keys[i].groups) do table.sort(group, keysort) end + end + + return keys +end + +-- Form hotkeys helper text +-------------------------------------------------------------------------------- +local function build_tip(pack, style, keypressed) + local text = {} + + for i, column in ipairs(pack) do + local coltxt = {} + local height = 0 + + for _, name in pairs(column.names) do + local group = column.groups[name] + + -- set group title + coltxt[#coltxt + 1] = string.format( + '%s', + style.titlefont, style.color.gray, name + ) + height = height + style.heights.title + + -- build key tip line + for _, key in ipairs(group) do + + -- key with align + local line = string.format('%s', key.key) + if style.is_align then + --noinspection StringConcatenationInLoops + line = line .. string.rep(" ", column.length - key.length) + end + + -- key with mods + if #key.mod > 0 then + local fm = {} + for ki, v in ipairs(key.mod) do fm[ki] = string.format('%s', v) end + table.insert(fm, line) + line = table.concat(fm, string.format('+', style.color.gray)) + end + + -- key with description + local clr = keypressed and hasitem(key.keyset, keypressed) and style.color.main or style.color.text + line = string.format( + '%s%s%s', + clr, style.keyfont, line, style.delim, key.description + ) + coltxt[#coltxt + 1] = line + height = height + style.heights.key + end + end + + text[i] = { text = table.concat(coltxt, '\n'), height = height } + end + + return text +end + +-- Initialize widget +----------------------------------------------------------------------------------------------------------------------- +function hotkeys:init() + + -- Initialize vars + -------------------------------------------------------------------------------- + local style = default_style() + self.style = style + self.tip = {} + + -- summary vertical size for all elements except tip textboxes + -- used to auto adjust widget height + self.vertical_pag = style.border_margin[3] + style.border_margin[4] + style.tspace + 2 + if style.separator.marginh then + self.vertical_pag = self.vertical_pag + style.separator.marginh[3] + style.separator.marginh[4] + end + + -- alias + local bm = style.border_margin + + -- Create floating wibox for top widget + -------------------------------------------------------------------------------- + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + + self.wibox:geometry(style.geometry) + + -- Widget layout setup + -------------------------------------------------------------------------------- + self.layout = wibox.layout.flex.horizontal() + + self.title = wibox.widget.textbox("Title") + self.title:set_align("center") + self.title:set_font(style.titlefont) + + local _, th = self.title:get_preferred_size() + self.vertical_pag = self.vertical_pag + th + + local subtitle = wibox.widget.textbox("Press any key to highlight tip, Escape for exit") + subtitle:set_align("center") + + local _, sh = subtitle:get_preferred_size() + self.vertical_pag = self.vertical_pag + sh + + self.wibox:setup({ + { + { + self.title, + subtitle, + redflat.gauge.separator.horizontal(style.separator), + spacing = style.tspace, + layout = wibox.layout.fixed.vertical, + }, + self.layout, + layout = wibox.layout.align.vertical, + }, + left = bm[1], right = bm[2], top = bm[3], bottom = bm[4], + layout = wibox.container.margin, + }) + + -- Highlight timer + -------------------------------------------------------------------------------- + local ltimer = timer({ timeout = style.ltimeout }) + ltimer:connect_signal("timeout", + function() + ltimer:stop() + self:highlight() + end + ) + + -- Keygrabber + -------------------------------------------------------------------------------- + self.keygrabber = function(_, key, event) + if event == "release" then + if hasitem(self.keys.close, key) then + self:hide(); return + end + + if hasitem(self.keys.close_all, key) then + self:hide() + if self.keypack[#self.keypack].on_close then self.keypack[#self.keypack].on_close() end + return + end + end + + self.lastkey = event == "press" and key or nil + ltimer:again() + end +end + + +-- Keypack managment +----------------------------------------------------------------------------------------------------------------------- + +-- Set new keypack +-------------------------------------------------------------------------------- +function hotkeys:set_pack(name, pack, columns, geometry, on_close) + if not self.wibox then self:init() end + + if not self.cache[name] then self.cache[name] = parse(pack, columns) end + table.insert( + self.keypack, + { name = name, pack = self.cache[name], geometry = geometry or self.style.geometry, on_close = on_close } + ) + self.title:set_text(name .. " hotkeys") + self:highlight() + self:update_geometry(self.keypack[#self.keypack].geometry) +end + +-- Remove current keypack +-------------------------------------------------------------------------------- +function hotkeys:remove_pack() + table.remove(self.keypack) + self.title:set_text(self.keypack[#self.keypack].name .. " hotkeys") + self:highlight() + self:update_geometry(self.keypack[#self.keypack].geometry) +end + +-- Calculate and set widget height +-------------------------------------------------------------------------------- +function hotkeys:update_geometry(predefined) + local height = 0 + for _, column in ipairs(self.tip) do height = math.max(height, column.height) end + + self.wibox:geometry({ width = predefined.width, height = predefined.height or height + self.vertical_pag }) +end + +-- Highlight key tip +-------------------------------------------------------------------------------- +function hotkeys:highlight() + self.tip = build_tip(self.keypack[#self.keypack].pack, self.style, self.lastkey) + + self.layout:reset() + for i, column in ipairs(self.tip) do + if not self.boxes[i] then + self.boxes[i] = wibox.widget.textbox() + self.boxes[i]:set_valign("top") + self.boxes[i]:set_font(self.style.font) + end + + self.boxes[i]:set_markup(column.text) + self.layout:add(self.boxes[i]) + end +end + + +-- Show/hide widget +----------------------------------------------------------------------------------------------------------------------- + +-- show +function hotkeys:show() + if not self.wibox then self:init() end + + if not self.wibox.visible then + redutil.placement.centered(self.wibox, nil, mouse.screen.workarea) + self.wibox.visible = true + awful.keygrabber.run(self.keygrabber) + -- else + -- self:hide() + end +end + +-- hide +function hotkeys:hide() + self.wibox.visible = false + self.lastkey = nil + self:highlight() + awful.keygrabber.stop(self.keygrabber) +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return hotkeys diff --git a/awesome/.config/awesome/redflat/float/init.lua b/awesome/.config/awesome/redflat/float/init.lua new file mode 100644 index 0000000..08da504 --- /dev/null +++ b/awesome/.config/awesome/redflat/float/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.float" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/float/keychain.lua b/awesome/.config/awesome/redflat/float/keychain.lua new file mode 100644 index 0000000..d98d24f --- /dev/null +++ b/awesome/.config/awesome/redflat/float/keychain.lua @@ -0,0 +1,202 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat prefix hotkey manager -- +----------------------------------------------------------------------------------------------------------------------- +-- Emacs like key key sequences +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local string = string +local table = table + +local wibox = require("wibox") +local awful = require("awful") +local beautiful = require("beautiful") + +local redflat = require("redflat") +local redutil = require("redflat.util") +local redtip = require("redflat.float.hotkeys") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local keychain = {} +local tip_cache = {} + +local label_pattern = { Mod1 = "A", Mod4 = "M", Control = "C", Shift = "S" } + +-- key bindings +keychain.service = { close = { "Escape" }, help = { "F1" }, stepback = { "BackSpace" } } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + geometry = { width = 220, height = 60 }, + font = "Sans 14 bold", + border_width = 2, + keytip = { geometry = { width = 500 }, exit = false }, + color = { border = "#575757", wibox = "#202020" }, + shape = nil + } + + return redflat.util.table.merge(style, redflat.util.table.check(beautiful, "float.keychain") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function build_label(item) + if #item[1] == 0 then return item[2] end + + local mods = {} + for _, m in ipairs(item[1]) do mods[#mods + 1] = label_pattern[m] end + return string.format("%s-%s", table.concat(mods, '-'), item[2]) +end + +local function build_tip(store, item, prefix) + prefix = prefix or build_label(item) + for _, k in ipairs(item[3]) do + local p = prefix .. " " .. build_label(k) + if type(k[3]) == "table" then + build_tip(store, k, p) + else + table.insert(store, { {}, p, nil, k[#k] }) + end + end + + return store +end + +local function build_fake_keys(keys) + local res = {} + for _, keygroup in ipairs({ + { description = "Undo last key", group = "Action", keyname = "stepback" }, + { description = "Undo sequence", group = "Action", keyname = "close" }, + { description = "Show hotkeys helper", group = "Action", keyname = "help" }, + }) + do + for _, k in ipairs(keys[keygroup.keyname]) do + table.insert(res, { + {}, k, nil, + { description = keygroup.description, group = keygroup.group } + }) + end + end + return res +end + +-- Main widget +----------------------------------------------------------------------------------------------------------------------- + +-- Initialize keychain widget +-------------------------------------------------------------------------------- +function keychain:init(style) + + -- Init vars + ------------------------------------------------------------ + self.active = nil + self.parents = {} + self.sequence = "" + + style = redflat.util.table.merge(default_style(), style or {}) + self.style = style + + -- Wibox + ------------------------------------------------------------ + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + self.wibox:geometry(style.geometry) + + self.label = wibox.widget.textbox() + self.label:set_align("center") + self.wibox:set_widget(self.label) + + self.label:set_font(style.font) + + -- Keygrabber + ------------------------------------------------------------ + self.keygrabber = function(mod, key, event) + if event == "press" then return false end + + -- dirty fix for first key release + if self.actkey == key and #mod == 0 then self.actkey = nil; return end + + if awful.util.table.hasitem(self.service.close, key) then self:hide() + elseif awful.util.table.hasitem(self.service.stepback, key) then self:undo() + elseif awful.util.table.hasitem(self.service.help, key) then redtip:show() + else + for _, item in ipairs(self.active[3]) do + if redutil.key.match_grabber(item, mod, key) then self:activate(item); return end + end + end + end +end + +-- Set current key item +-------------------------------------------------------------------------------- +function keychain:activate(item, keytip) + if not self.wibox then self:init() end + self.actkey = keytip and item[2] + + if type(item[3]) == "function" then + item[3]() + self:hide() + else + if not self.active then + redutil.placement.centered(self.wibox, nil, mouse.screen.workarea) + self.wibox.visible = true + awful.keygrabber.run(self.keygrabber) + else + self.parents[#self.parents + 1] = self.active + end + + self.active = item + local label = build_label(self.active) + self.sequence = self.sequence == "" and label or self.sequence .. " " .. label + self.label:set_text(self.sequence) + end + + -- build keys helper tip + if keytip then + if tip_cache[keytip] then + self.tip = tip_cache[keytip] + else + self.tip = awful.util.table.join(build_tip({}, item), build_fake_keys(self.service)) + tip_cache[keytip] = self.tip + end + redtip:set_pack(keytip .. " keychain", self.tip, self.style.keytip.column, self.style.keytip.geometry) + end +end + +-- Deactivate last key item +-------------------------------------------------------------------------------- +function keychain:undo() + if #self.parents > 0 then + self.sequence = self.sequence:sub(1, - (#build_label(self.active) + 2)) + self.label:set_text(self.sequence) + + self.active = self.parents[#self.parents] + self.parents[#self.parents] = nil + else + self:hide() + end +end + +-- Hide widget +-------------------------------------------------------------------------------- +function keychain:hide() + self.wibox.visible = false + awful.keygrabber.stop(self.keygrabber) + self.active = nil + self.parents = {} + self.sequence = "" + redtip:remove_pack() +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return keychain diff --git a/awesome/.config/awesome/redflat/float/notify.lua b/awesome/.config/awesome/redflat/float/notify.lua new file mode 100644 index 0000000..db62a4f --- /dev/null +++ b/awesome/.config/awesome/redflat/float/notify.lua @@ -0,0 +1,140 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat notify widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Floating widget with icon, text, and progress bar +-- special for volume and brightness indication +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local unpack = unpack or table.unpack +local beautiful = require("beautiful") +local wibox = require("wibox") +local timer = require("gears.timer") + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") +local progressbar = require("redflat.gauge.graph.bar") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local notify = { last = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + geometry = { width = 480, height = 100 }, + screen_gap = 0, + set_position = nil, + border_margin = { 20, 20, 20, 20 }, + elements_margin = { 20, 0, 10, 10 }, + bar_width = 8, + font = "Sans 14", + border_width = 2, + timeout = 5, + icon = nil, + progressbar = {}, + color = { border = "#575757", icon = "#aaaaaa", wibox = "#202020" }, + shape = nil + } + return redutil.table.merge(style, redutil.table.check(beautiful, "float.notify") or {}) +end + +-- Initialize notify widget +----------------------------------------------------------------------------------------------------------------------- +function notify:init() + + local style = default_style() + -- local icon = style.icon + + self.style = style + + -- Construct layouts + -------------------------------------------------------------------------------- + local area = wibox.layout.align.horizontal() + + local bar = progressbar(style.progressbar) + local image = svgbox() + local text = wibox.widget.textbox("100%") + text:set_align("center") + -- text:set_font(style.font) + + local align_vertical = wibox.layout.align.vertical() + bar:set_forced_height(style.bar_width) + + area:set_left(image) + area:set_middle(wibox.container.margin(align_vertical, unpack(style.elements_margin))) + + -- Create floating wibox for notify widget + -------------------------------------------------------------------------------- + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + + self.wibox:set_widget(wibox.container.margin(area, unpack(style.border_margin))) + self.wibox:geometry(style.geometry) + + -- Set info function + -------------------------------------------------------------------------------- + function self:set(args) + args = args or {} + align_vertical:reset() + + if args.value then + bar:set_value(args.value) + align_vertical:set_top(text) + align_vertical:set_bottom(bar) + else + align_vertical:set_middle(text) + end + + if args.text then + text:set_text(args.text) + text:set_font(args.font ~= nil and args.font or style.font) + end + + image:set_image(args.icon ~= nil and args.icon or style.icon) + image:set_color(args.color or style.color.icon) + end + + -- Set autohide timer + -------------------------------------------------------------------------------- + self.hidetimer = timer({ timeout = style.timeout }) + self.hidetimer:connect_signal("timeout", function() self:hide() end) + + -- Signals setup + -------------------------------------------------------------------------------- + self.wibox:connect_signal("mouse::enter", function() self:hide() end) +end + +-- Hide notify widget +----------------------------------------------------------------------------------------------------------------------- +function notify:hide() + self.wibox.visible = false + self.hidetimer:stop() +end + +-- Show notify widget +----------------------------------------------------------------------------------------------------------------------- +function notify:show(args) + if not self.wibox then self:init() end + self:set(args) + + if not self.wibox.visible or mouse.screen.index ~= self.last.screen then + if self.style.set_position then self.style.set_position(self.wibox) end + redutil.placement.no_offscreen(self.wibox, self.style.screen_gap, mouse.screen.workarea) + self.wibox.visible = true + end + + self.last.screen = mouse.screen.index + self.hidetimer:again() +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return notify diff --git a/awesome/.config/awesome/redflat/float/player.lua b/awesome/.config/awesome/redflat/float/player.lua new file mode 100644 index 0000000..33d04d5 --- /dev/null +++ b/awesome/.config/awesome/redflat/float/player.lua @@ -0,0 +1,506 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat audio player widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Music player widget +-- Display audio track information with mpris2 +-- Using dbus-send to control +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local unpack = unpack or table.unpack +local math = math + +local awful = require("awful") +local beautiful = require("beautiful") +local wibox = require("wibox") +local timer = require("gears.timer") + +local redutil = require("redflat.util") +local progressbar = require("redflat.gauge.graph.bar") +local dashcontrol = require("redflat.gauge.graph.dash") +local svgbox = require("redflat.gauge.svgbox") + +-- Initialize and vars for module +----------------------------------------------------------------------------------------------------------------------- +local player = { box = {}, listening = false } + +local dbus_mpris = "dbus-send --print-reply=literal --session --dest=org.mpris.MediaPlayer2.%s " + .. "/org/mpris/MediaPlayer2 " + +local dbus_get = dbus_mpris + .. "org.freedesktop.DBus.Properties.Get " + .. "string:'org.mpris.MediaPlayer2.Player' %s" + +local dbus_getall = dbus_mpris + .. "org.freedesktop.DBus.Properties.GetAll " + .. "string:'org.mpris.MediaPlayer2.Player'" + +local dbus_set = dbus_mpris + .. "org.freedesktop.DBus.Properties.Set " + .. "string:'org.mpris.MediaPlayer2.Player' %s" + +local dbus_action = dbus_mpris + .. "org.mpris.MediaPlayer2.Player." + +-- Helper function to decode URI string format +----------------------------------------------------------------------------------------------------------------------- +local function decodeURI(s) + return string.gsub(s, '%%(%x%x)', function(hex) return string.char(tonumber(hex, 16)) end) +end + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + geometry = { width = 520, height = 150 }, + screen_gap = 0, + set_position = nil, + dashcontrol = {}, + progressbar = {}, + border_margin = { 20, 20, 20, 20 }, + elements_margin = { 20, 0, 0, 0 }, + volume_margin = { 0, 0, 0, 3 }, + controls_margin = { 0, 0, 18, 8 }, + buttons_margin = { 0, 0, 3, 3 }, + pause_margin = { 12, 12, 0, 0 }, + timeout = 5, + line_height = 26, + bar_width = 8, -- progress bar height + volume_width = 50, + titlefont = "Sans 12", + timefont = "Sans 12", + artistfont = "Sans 12", + border_width = 2, + icon = { + cover = redutil.base.placeholder(), + play = redutil.base.placeholder({ txt = "►" }), + pause = redutil.base.placeholder({ txt = "[]" }), + next_tr = redutil.base.placeholder({ txt = "→" }), + prev_tr = redutil.base.placeholder({ txt = "←" }), + }, + color = { border = "#575757", main = "#b1222b", + wibox = "#202020", gray = "#575757", icon = "#a0a0a0" }, + shape = nil + } + return redutil.table.merge(style, redutil.table.check(beautiful, "float.player") or {}) +end + + +-- Initialize player widget +----------------------------------------------------------------------------------------------------------------------- +function player:init(args) + + -- Initialize vars + -------------------------------------------------------------------------------- + args = args or {} + local _player = args.name or "vlc" + local style = default_style() + local show_album = false + + self.info = { artist = "Unknown", album = "Unknown" } + self.style = style + self.last = { status = "Stopped", length = 5 * 60 * 1000000, volume = nil } + + -- dbus vars + self.command = { + get_all = string.format(dbus_getall, _player), + get_position = string.format(dbus_get, _player, "string:'Position'"), + get_volume = string.format(dbus_get, _player, "string:'Volume'"), + set_volume = string.format(dbus_set, _player, "string:'Volume' variant:double:"), + action = string.format(dbus_action, _player), + set_position = string.format(dbus_action, _player) .. "SetPosition objpath:/not/used int64:", + } + + self._actions = { "PlayPause", "Next", "Previous" } + + -- Construct layouts + -------------------------------------------------------------------------------- + + -- progressbar and icon + self.bar = progressbar(style.progressbar) + self.box.image = svgbox(style.icon.cover) + self.box.image:set_color(style.color.gray) + + -- Text lines + ------------------------------------------------------------ + self.box.title = wibox.widget.textbox("Title") + self.box.artist = wibox.widget.textbox("Artist") + self.box.title:set_font(style.titlefont) + self.box.title:set_valign("top") + self.box.artist:set_font(style.artistfont) + self.box.artist:set_valign("top") + + local text_area = wibox.layout.fixed.vertical() + text_area:add(wibox.container.constraint(self.box.title, "exact", nil, style.line_height)) + text_area:add(wibox.container.constraint(self.box.artist, "exact", nil, style.line_height)) + + -- Control line + ------------------------------------------------------------ + + -- playback buttons + local player_buttons = wibox.layout.fixed.horizontal() + local prev_button = svgbox(style.icon.prev_tr, nil, style.color.icon) + player_buttons:add(prev_button) + + self.play_button = svgbox(style.icon.play, nil, style.color.icon) + player_buttons:add(wibox.container.margin(self.play_button, unpack(style.pause_margin))) + + local next_button = svgbox(style.icon.next_tr, nil, style.color.icon) + player_buttons:add(next_button) + + -- time indicator + self.box.time = wibox.widget.textbox("0:00") + self.box.time:set_font(style.timefont) + + -- volume + self.volume = dashcontrol(style.dashcontrol) + local volumespace = wibox.container.margin(self.volume, unpack(style.volume_margin)) + local volume_area = wibox.container.constraint(volumespace, "exact", style.volume_width, nil) + + -- full line + local buttons_align = wibox.layout.align.horizontal() + buttons_align:set_expand("outside") + buttons_align:set_middle(wibox.container.margin(player_buttons, unpack(style.buttons_margin))) + + local control_align = wibox.layout.align.horizontal() + control_align:set_middle(buttons_align) + control_align:set_right(self.box.time) + control_align:set_left(volume_area) + + -- Bring it all together + ------------------------------------------------------------ + local align_vertical = wibox.layout.align.vertical() + align_vertical:set_top(text_area) + align_vertical:set_middle(wibox.container.margin(control_align, unpack(style.controls_margin))) + align_vertical:set_bottom(wibox.container.constraint(self.bar, "exact", nil, style.bar_width)) + local area = wibox.layout.fixed.horizontal() + area:add(self.box.image) + area:add(wibox.container.margin(align_vertical, unpack(style.elements_margin))) + + -- Buttons + ------------------------------------------------------------ + + -- playback controll + self.play_button:buttons(awful.util.table.join(awful.button({}, 1, function() self:action("PlayPause") end))) + next_button:buttons(awful.util.table.join(awful.button({}, 1, function() self:action("Next") end))) + prev_button:buttons(awful.util.table.join(awful.button({}, 1, function() self:action("Previous") end))) + + -- volume + self.volume:buttons(awful.util.table.join( + awful.button({}, 4, function() self:change_volume( 0.05) end), + awful.button({}, 5, function() self:change_volume(-0.05) end) + )) + + -- position + self.bar:buttons(awful.util.table.join( + awful.button( + {}, 1, function() + local coords = { + bar = mouse.current_widget_geometry, + wibox = mouse.current_wibox:geometry(), + mouse = mouse.coords(), + } + + local position = (coords.mouse.x - coords.wibox.x - coords.bar.x) / coords.bar.width + awful.spawn.with_shell(self.command.set_position .. math.floor(self.last.length * position)) + end + ) + )) + + -- switch between artist and album info on mouse click + self.box.artist:buttons(awful.util.table.join( + awful.button({}, 1, + function() + show_album = not show_album + self.update_artist() + end + ) + )) + + -- Create floating wibox for player widget + -------------------------------------------------------------------------------- + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + + self.wibox:set_widget(wibox.container.margin(area, unpack(style.border_margin))) + self.wibox:geometry(style.geometry) + + -- Update info functions + -------------------------------------------------------------------------------- + + -- Function to set play button state + ------------------------------------------------------------ + self.set_play_button = function(state) + self.play_button:set_image(style.icon[state]) + end + + -- Function to set info for artist/album line + ------------------------------------------------------------ + self.update_artist = function() + if show_album then + self.box.artist:set_markup('From ' .. self.info.album) + else + self.box.artist:set_markup('By ' .. self.info.artist) + end + end + + -- Set defs + ------------------------------------------------------------ + self.clear_info = function(is_att) + self.box.image:set_image(style.icon.cover) + self.box.image:set_color(is_att and style.color.main or style.color.gray) + + self.box.time:set_text("0:00") + self.bar:set_value(0) + -- self.box.title:set_text("Stopped") + self.info = { artist = "", album = "" } + self.update_artist() + + self.last.volume = nil + end + + -- Main update function + ------------------------------------------------------------ + function self:update() + if self.last.status ~= "Stopped" then + awful.spawn.easy_async( + self.command.get_position, + function(output, _, _, exit_code) + + -- dirty trick to clean up if player closed + if exit_code ~= 0 then + self.clear_info(true) + self.last.status = "Stopped" + return + end + + -- set progress bar + local position = string.match(output, "int64%s+(%d+)") + local progress = position / self.last.length + self.bar:set_value(progress) + + -- set current time + local ps = math.floor(position / 10^6) + local ct = string.format("%d:%02d", math.floor(ps / 60), ps % 60) + self.box.time:set_text(ct) + end + ) + end + end + + -- Set update timer + -------------------------------------------------------------------------------- + self.updatetimer = timer({ timeout = style.timeout }) + self.updatetimer:connect_signal("timeout", function() self:update() end) + + -- Run dbus servise + -------------------------------------------------------------------------------- + if not self.listening then self:listen() end + self:initialize_info() +end + +-- Initialize all properties via dbus call +-- should be called only once for initialization before the dbus signals trigger the updates +----------------------------------------------------------------------------------------------------------------------- +function player:initialize_info() + awful.spawn.easy_async( + self.command.get_all, + function(output, _, _, exit_code) + + local data = { Metadata = {} } + + local function parse_dbus_value(ident) + local regex = "(" .. ident .. ")%s+([a-z0-9]+)%s+(.-)%s-%)\n" + local _, _, value = output:match(regex) + if not value then return nil end + + -- check for int64 type field + local int64_val = value:match("int64%s+(%d+)") + if int64_val then return tonumber(int64_val) end + + -- check for double type field + local double_val = value:match("double%s+([%d.]+)") + if double_val then return tonumber(double_val) end + + -- check for array type field as table, extract first entry only + local array_val = value:match("array%s%[%s+([^%],]+)") + if array_val then return { array_val } end + + return value + end + + if exit_code == 0 then + data.Metadata["xesam:title"] = parse_dbus_value("xesam:title") + data.Metadata["xesam:artist"] = parse_dbus_value("xesam:artist") + data.Metadata["xesam:album"] = parse_dbus_value("xesam:album") + data.Metadata["mpris:artUrl"] = parse_dbus_value("mpris:artUrl") + data.Metadata["mpris:length"] = parse_dbus_value("mpris:length") + data["Volume"] = parse_dbus_value("Volume") + data["Position"] = parse_dbus_value("Position") + data["PlaybackStatus"] = parse_dbus_value("PlaybackStatus") + self:update_from_metadata(data) + end + end + ) +end + +-- Player playback control +----------------------------------------------------------------------------------------------------------------------- +function player:action(args) + if not awful.util.table.hasitem(self._actions, args) then return end + if not self.wibox then self:init() end + + awful.spawn.with_shell(self.command.action .. args) + self:update() +end + +-- Player volume control +----------------------------------------------------------------------------------------------------------------------- +function player:change_volume(step) + local v = (self.last.volume or 0) + step + if v > 1 then v = 1 + elseif v < 0 then v = 0 end + + self.last.volume = v + awful.spawn.with_shell(self.command.set_volume .. v) +end + +-- Hide player widget +----------------------------------------------------------------------------------------------------------------------- +function player:hide() + self.wibox.visible = false + if self.updatetimer.started then self.updatetimer:stop() end +end + +-- Show player widget +----------------------------------------------------------------------------------------------------------------------- +function player:show(geometry) + if not self.wibox then self:init() end + + if not self.wibox.visible then + self:update() + + if geometry then + self.wibox:geometry(geometry) + elseif self.style.set_position then + self.style.set_position(self.wibox) + else + awful.placement.under_mouse(self.wibox) + end + redutil.placement.no_offscreen(self.wibox, self.style.screen_gap, screen[mouse.screen].workarea) + + self.wibox.visible = true + if self.last.status == "Playing" then self.updatetimer:start() end + else + self:hide() + end +end + +-- Update property values from received metadata +----------------------------------------------------------------------------------------------------------------------- +function player:update_from_metadata(data) + -- empty call + if not data then return end + + -- set track info if playing + if data.Metadata then + -- set song title + self.box.title:set_text(data.Metadata["xesam:title"] or "Unknown") + + -- set album or artist info + + self.info.artist = data.Metadata["xesam:artist"] and data.Metadata["xesam:artist"][1] or "Unknown" + self.info.album = data.Metadata["xesam:album"] or "Unknown" + self.update_artist() + + -- set cover art + local has_cover = false + if data.Metadata["mpris:artUrl"] then + local image = string.match(data.Metadata["mpris:artUrl"], "file://(.+)") + if image then + self.box.image:set_color(nil) + has_cover = self.box.image:set_image(decodeURI(image)) + end + end + if not has_cover then + -- reset to generic icon if no cover available + self.box.image:set_color(self.style.color.gray) + self.box.image:set_image(self.style.icon.cover) + end + + -- track length + if data.Metadata["mpris:length"] then self.last.length = data.Metadata["mpris:length"] end + end + + if data.PlaybackStatus then + + -- check player status and set suitable play/pause button image + local state = data.PlaybackStatus == "Playing" and "pause" or "play" + self.set_play_button(state) + self.last.status = data.PlaybackStatus + + -- stop/start update timer + if data.PlaybackStatus == "Playing" then + if self.wibox.visible then self.updatetimer:start() end + else + if self.updatetimer.started then self.updatetimer:stop() end + self:update() + end + + -- clear track info if stoppped + if data.PlaybackStatus == "Stopped" then + self.clear_info() + end + end + + -- volume + if data.Volume then + self.volume:set_value(data.Volume) + self.last.volume = data.Volume + elseif not self.last.volume then + -- try to grab volume explicitly if not supplied + awful.spawn.easy_async( + self.command.get_volume, + function(output, _, _, exit_code) + + if exit_code ~= 0 then + return + end + + local volume = tonumber(string.match(output, "double%s+([%d.]+)")) + if volume then + self.volume:set_value(volume) + self.last.volume = volume + end + end + ) + end +end + +-- Dbus signal setup +-- update some info which avaliable from dbus signal +----------------------------------------------------------------------------------------------------------------------- +function player:listen() + dbus.request_name("session", "org.freedesktop.DBus.Properties") + dbus.add_match( + "session", + "path=/org/mpris/MediaPlayer2, interface='org.freedesktop.DBus.Properties', member='PropertiesChanged'" + ) + dbus.connect_signal("org.freedesktop.DBus.Properties", + function (_, _, data) + self:update_from_metadata(data) + end + ) + + self.listening = true +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return player diff --git a/awesome/.config/awesome/redflat/float/prompt.lua b/awesome/.config/awesome/redflat/float/prompt.lua new file mode 100644 index 0000000..525e79f --- /dev/null +++ b/awesome/.config/awesome/redflat/float/prompt.lua @@ -0,0 +1,99 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat promt widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Promt widget with his own wibox placed on center of screen +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ awful.widget.prompt v3.5.2 +------ (c) 2009 Julien Danjou +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local type = type +local unpack = unpack or table.unpack + +local awful = require("awful") +local beautiful = require("beautiful") +local wibox = require("wibox") +local naughty = require("naughty") + +local redutil = require("redflat.util") +local decoration = require("redflat.float.decoration") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local floatprompt = {} + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + geometry = { width = 620, height = 120 }, + margin = { 20, 20, 40, 40 }, + border_width = 2, + naughty = {}, + color = { border = "#575757", wibox = "#202020" }, + shape = nil + } + return redutil.table.merge(style, redutil.table.check(beautiful, "float.prompt") or {}) +end + +-- Initialize prompt widget +-- @param prompt Prompt to use +----------------------------------------------------------------------------------------------------------------------- +function floatprompt:init(args) + + args = args or {} + local style = default_style() + self.style = style + + -- Create prompt widget + -------------------------------------------------------------------------------- + self.widget = wibox.widget.textbox() + self.widget:set_ellipsize("start") + self.prompt = args.prompt or " Run: " + self.decorated_widget = decoration.textfield(self.widget, style.field) + + -- Create floating wibox for promt widget + -------------------------------------------------------------------------------- + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + + self.wibox:set_widget(wibox.container.margin(self.decorated_widget, unpack(style.margin))) + self.wibox:geometry(style.geometry) +end + +-- Run method for prompt widget +-- Wibox appears on call and hides after command entered +----------------------------------------------------------------------------------------------------------------------- +function floatprompt:run() + if not self.wibox then self:init() end + redutil.placement.centered(self.wibox, nil, mouse.screen.workarea) + self.wibox.visible = true + + awful.prompt.run({ + prompt = self.prompt, + textbox = self.widget, + exe_callback = function(input) + local result = awful.spawn(input) + if type(result) == "string" then + local notify_args = redutil.table.merge({ title = "Prompt", text = result }, self.style.naughty) + naughty.notify(notify_args) + end + end, + history_path = awful.util.getdir("cache") .. "/history", + history_max = 30, + completion_callback = awful.completion.shell, + done_callback = function () self.wibox.visible = false end, + }) +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return floatprompt diff --git a/awesome/.config/awesome/redflat/float/qlaunch.lua b/awesome/.config/awesome/redflat/float/qlaunch.lua new file mode 100644 index 0000000..813e167 --- /dev/null +++ b/awesome/.config/awesome/redflat/float/qlaunch.lua @@ -0,0 +1,510 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat quick laucnher widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Quick application launch or switch +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local table = table +local unpack = unpack or table.unpack +local string = string +local math = math +local io = io +local os = os + +local wibox = require("wibox") +local awful = require("awful") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redflat = require("redflat") +local redutil = require("redflat.util") +local redtip = require("redflat.float.hotkeys") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local qlaunch = { history = {}, store = {}, keys = {} } + +local sw = redflat.float.appswitcher +local TPI = math.pi * 2 + +local switcher_keys = {} +for i = 1, 9 do switcher_keys[tostring(i)] = { app = "", run = "" } end + +-- key bindings +qlaunch.forcemod = { "Control" } +qlaunch.keys.action = { + { + {}, "Escape", function() qlaunch:hide(true) end, + { description = "Close widget", group = "Action" } + }, + { + {}, "Return", function() qlaunch:run_and_hide() end, + { description = "Run or rise selected app", group = "Action" } + }, + { + {}, "s", function() qlaunch:set_new_app(qlaunch.switcher.selected, client.focus) end, + { description = "Bind focused app to selected key", group = "Action" } + }, + { + {}, "d", function() qlaunch:set_new_app(qlaunch.switcher.selected) end, + { description = "Clear selected key", group = "Action" } + }, + { + {}, "r", function() qlaunch:load_config(true) end, + { description = "Reload config from disk", group = "Action" } + }, + { + { "Mod4" }, "F1", function() redtip:show() end, + { description = "Show hotkeys helper", group = "Action" } + }, +} + +qlaunch.keys.all = awful.util.table.join({}, qlaunch.keys.action) + +qlaunch._fake_keys = { + { + {}, "N", nil, + { description = "Select app (run or rise if selected already) by key", group = "Action", + keyset = { "1", "2", "3", "4", "5", "6", "7", "8", "9" } } + }, + { + {}, "N", nil, + { description = "Select app (launch if selected already) by key", group = "Action" } + }, +} + + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + df_icon = redutil.base.placeholder({ txt = "X" }), + no_icon = redutil.base.placeholder(), + parser = {}, + recoloring = false, + notify = {}, + geometry = { width = 1680, height = 180 }, + border_margin = { 20, 20, 10, 10 }, + appline = { iwidth = 160, im = { 10, 10, 5, 5 }, igap = { 0, 0, 10, 10 }, lheight = 30 }, + state = { gap = 4, radius = 3, size = 10, height = 20, width = 20 }, + configfile = os.getenv("HOME") .. "/.cache/awesome/applist", + label_font = "Sans 14", + border_width = 2, + keytip = { geometry = { width = 500 }, exit = false }, + color = { border = "#575757", text = "#aaaaaa", main = "#b1222b", urgent = "#32882d", + wibox = "#202020", icon = "#a0a0a0", bg = "#161616", gray = "#575757" }, + shape = nil + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "float.qlaunch") or {}) +end + + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Get list of clients with given class +------------------------------------------------------------ +local function get_clients(app) + local clients = {} + for _, c in ipairs(client.get()) do + if c.class:lower() == app then table.insert(clients, c) end + end + return clients +end + +-- Set focus on given client +------------------------------------------------------------ +local function focus_and_raise(c) + if c.minimized then c.minimized = false end + if not c:isvisible() then awful.tag.viewmore(c:tags(), c.screen) end + + client.focus = c + c:raise() +end + +-- Build filter for clients with given class +------------------------------------------------------------ +local function build_filter(app) + return function(c) + return c.class:lower() == app + end +end + +-- Check if file exist +------------------------------------------------------------ +local function is_file_exists(file) + local f = io.open(file, "r") + if f then f:close(); return true else return false end +end + +-- Widget construction functions +----------------------------------------------------------------------------------------------------------------------- + +-- Build application state indicator +-------------------------------------------------------------------------------- +local function build_state_indicator(style) + + -- Initialize vars + ------------------------------------------------------------ + local widg = wibox.widget.base.make_widget() + + local dx = style.state.size + style.state.gap + local ds = style.state.size - style.state.radius + local r = style.state.radius + + -- updating values + local data = { + state = {}, + height = style.state.height or nil, + width = style.state.width or nil + } + + -- User functions + ------------------------------------------------------------ + function widg:setup(clist) + data.state = {} + for _, c in ipairs(clist) do + table.insert(data.state, { focused = client.focus == c, urgent = c.urgent, minimized = c.minimized }) + end + self:emit_signal("widget::redraw_needed") + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + return data.width or width, data.height or height + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + local n = #data.state + local x0 = (width - n * style.state.size - (n - 1) * style.state.gap) / 2 + local y0 = (height - style.state.size) / 2 + + for i = 1, n do + cr:set_source(color( + data.state[i].focused and style.color.main or + data.state[i].urgent and style.color.urgent or + data.state[i].minimized and style.color.gray or style.color.icon + )) + -- draw rounded rectangle + cr:arc(x0 + (i -1) * dx + ds, y0 + r, r, -TPI / 4, 0) + cr:arc(x0 + (i -1) * dx + ds, y0 + ds, r, 0, TPI / 4) + cr:arc(x0 + (i -1) * dx + r, y0 + ds, r, TPI / 4, TPI / 2) + cr:arc(x0 + (i -1) * dx + r, y0 + r, r, TPI / 2, 3 * TPI / 4) + cr:fill() + end + end + + ------------------------------------------------------------ + return widg +end + +-- Build icon with label item +-------------------------------------------------------------------------------- +local function build_item(key, style) + local widg = {} + + -- Label + ------------------------------------------------------------ + local label = wibox.widget({ + markup = string.format('%s', style.color.text, key), + align = "center", + font = style.label_font, + forced_height = style.appline.lheight, + widget = wibox.widget.textbox, + }) + + widg.background = wibox.container.background(label, style.color.bg) + + -- Icon + ------------------------------------------------------------ + widg.svgbox = redflat.gauge.svgbox() + local icon_align = wibox.widget({ + nil, + widg.svgbox, + forced_width = style.appline.iwidth, + expand = "outside", + layout = wibox.layout.align.horizontal, + }) + + -- State + ------------------------------------------------------------ + widg.state = build_state_indicator(style) + + -- Layout setup + ------------------------------------------------------------ + widg.layout = wibox.layout.align.vertical() + widg.layout:set_top(widg.state) + widg.layout:set_middle(wibox.container.margin(icon_align, unpack(style.appline.igap))) + widg.layout:set_bottom(widg.background) + + ------------------------------------------------------------ + return widg +end + +-- Build widget with application list +-------------------------------------------------------------------------------- +local function build_switcher(keys, style) + + -- Init vars + ------------------------------------------------------------ + local widg = { items = {}, selected = nil } + local middle_layout = wibox.layout.fixed.horizontal() + + -- Sorted keys + ------------------------------------------------------------ + local sk = {} + for k in pairs(keys) do table.insert(sk, k) end + table.sort(sk) + + -- Build icon row + ------------------------------------------------------------ + for _, key in ipairs(sk) do + widg.items[key] = build_item(key, style) + middle_layout:add(wibox.container.margin(widg.items[key].layout, unpack(style.appline.im))) + end + + widg.layout = wibox.widget({ + nil, + wibox.container.margin(middle_layout, unpack(style.border_margin)), + expand = "outside", + layout = wibox.layout.align.horizontal, + }) + + -- Winget functions + ------------------------------------------------------------ + function widg:update(store, idb) + self.selected = nil + for key, data in pairs(store) do + local icon = data.app == "" and style.no_icon or idb[data.app] or style.df_icon + self.items[key].svgbox:set_image(icon) + if style.recoloring then self.items[key].svgbox:set_color(style.color.icon) end + end + self:set_state(store) + end + + function widg:set_state(store) + for k, item in pairs(self.items) do + local clist = get_clients(store[k].app) + item.state:setup(clist) + end + end + + function widg:reset() + for _, item in pairs(self.items) do item.background:set_bg(style.color.bg) end + self.selected = nil + end + + function widg:check_key(key, mod) + if self.items[key] then + if self.selected then self.items[self.selected].background:set_bg(style.color.bg) end + self.items[key].background:set_bg(style.color.main) + + if self.selected == key then + local modcheck = #mod == #qlaunch.forcemod + for _, v in ipairs(mod) do modcheck = modcheck and awful.util.table.hasitem(qlaunch.forcemod, v) end + qlaunch:run_and_hide(modcheck) + else + self.selected = key + end + end + end + + ------------------------------------------------------------ + return widg +end + +-- Main widget +----------------------------------------------------------------------------------------------------------------------- + +-- Build widget +-------------------------------------------------------------------------------- +function qlaunch:init(args, style) + + -- Init vars + ------------------------------------------------------------ + args = args or {} + local keys = args.keys or switcher_keys + + style = redutil.table.merge(default_style(), style or {}) + self.style = style + self.default_switcher_keys = keys + self.icon_db = redflat.service.dfparser.icon_list(style.parser) + + self:load_config() + + -- Wibox + ------------------------------------------------------------ + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + self.wibox:geometry(style.geometry) + redutil.placement.centered(self.wibox, nil, screen[mouse.screen].workarea) + + -- Switcher widget + ------------------------------------------------------------ + self.switcher = build_switcher(self.store, style) + self.switcher:update(self.store, self.icon_db) + + self.wibox:set_widget(self.switcher.layout) + self:set_keys() + + -- Keygrabber + ------------------------------------------------------------ + self.keygrabber = function(mod, key, event) + if event == "press" then return false end + for _, k in ipairs(self.keys.all) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return end + end + self.switcher:check_key(key, mod) + end + + -- Connect additional signals + ------------------------------------------------------------ + client.connect_signal("focus", function(c) self:set_last(c) end) + awesome.connect_signal("exit", function() self:save_config() end) +end + +-- Widget show/hide +-------------------------------------------------------------------------------- +function qlaunch:show() + if not self.wibox then self:init() end + + self.switcher:set_state(self.store) + self.wibox.visible = true + awful.keygrabber.run(self.keygrabber) + + redtip:set_pack( + "Quick launch", self.tip, self.style.keytip.column, self.style.keytip.geometry, + self.style.keytip.exit and function() self:hide() end + ) +end + +function qlaunch:hide() + self.wibox.visible = false + awful.keygrabber.stop(self.keygrabber) + self.switcher:reset() + redtip:remove_pack() +end + +function qlaunch:run_and_hide(forced_run) + if self.switcher.selected then + self:run_or_raise(self.switcher.selected, forced_run) + end + self:hide() +end + +-- Switch to app +-------------------------------------------------------------------------------- +function qlaunch:run_or_raise(key, forced_run) + local app = self.store[key].app + if app == "" then return end + + local clients = get_clients(app) + local cnum = #clients + + if cnum == 0 or forced_run then + -- open new application + if self.store[key].run ~= "" then awful.spawn.with_shell(self.store[key].run) end + elseif cnum == 1 then + -- switch to sole app + focus_and_raise(clients[1]) + else + if awful.util.table.hasitem(clients, client.focus) then + -- run selection widget if wanted app focused + sw:show({ filter = build_filter(app), noaction = true }) + else + -- switch to last focused if availible or first in list otherwise + local last = awful.util.table.hasitem(clients, self.history[app]) + if last then + focus_and_raise(self.history[app]) + else + focus_and_raise(clients[1]) + end + end + end +end + +-- Bind new application to given hotkey +-------------------------------------------------------------------------------- +function qlaunch:set_new_app(key, c) + if not key then return end + + if c then + local run_command = redutil.read.output(string.format("tr '\\0' ' ' < /proc/%s/cmdline", c.pid)) + self.store[key] = { app = c.class:lower(), run = run_command } + local note = redutil.table.merge({text = string.format("%s binded with '%s'", c.class, key)}, self.style.notify) + redflat.float.notify:show(note) + else + self.store[key] = { app = "", run = "" } + local note = redutil.table.merge({text = string.format("'%s' key unbinded", key)}, self.style.notify) + redflat.float.notify:show(note) + end + + self.switcher:reset() + self.switcher:update(self.store, self.icon_db) +end + +-- Save information about last focused client in widget store +-------------------------------------------------------------------------------- +function qlaunch:set_last(c) + if not c.class then return end + for _, data in pairs(self.store) do + if c.class:lower() == data.app then + self.history[data.app] = c + break + end + end +end + +-- Application list save/load +-------------------------------------------------------------------------------- +function qlaunch:load_config(need_reset) + if is_file_exists(self.style.configfile) then + for line in io.lines(self.style.configfile) do + local key, app, run = string.match(line, "key=(.+);app=(.*);run=(.*);") + self.store[key] = { app = app, run = run } + end + else + self.store = self.default_switcher_keys + end + + if need_reset then + self.switcher:reset() + self.switcher:update(self.store, self.icon_db) + end +end + +function qlaunch:save_config() + local file = io.open(self.style.configfile, "w+") + for key, data in pairs(self.store) do + file:write(string.format("key=%s;app=%s;run=%s;\n", key, data.app, data.run)) + end + file:close() +end + +-- Set user hotkeys +----------------------------------------------------------------------------------------------------------------------- +function qlaunch:set_keys(keys, layout) + layout = layout or "all" + if keys then + self.keys[layout] = keys + if layout ~= "all" then self.keys.all = awful.util.table.join({}, self.keys.action) end + end + + self._fake_keys[2][1] = self.forcemod + self.tip = awful.util.table.join(self.keys.all, self._fake_keys) +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return qlaunch diff --git a/awesome/.config/awesome/redflat/float/tooltip.lua b/awesome/.config/awesome/redflat/float/tooltip.lua new file mode 100644 index 0000000..c4aac62 --- /dev/null +++ b/awesome/.config/awesome/redflat/float/tooltip.lua @@ -0,0 +1,156 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat tooltip -- +----------------------------------------------------------------------------------------------------------------------- +-- Slightly modded awful tooltip +-- padding added +-- Proper placement on every text update +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ awful.tooltip v3.5.2 +------ (c) 2009 Sébastien Gross +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local ipairs = ipairs +local wibox = require("wibox") +local awful = require("awful") +local beautiful = require("beautiful") +local timer = require("gears.timer") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local tooltip = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + padding = { vertical = 3, horizontal = 5 }, + timeout = 1, + font = "Sans 12", + border_width = 2, + set_position = nil, + color = { border = "#404040", text = "#aaaaaa", wibox = "#202020" }, + shape = nil + } + return redutil.table.merge(style, redutil.table.check(beautiful, "float.tooltip") or {}) +end + +-- Create a new tooltip +----------------------------------------------------------------------------------------------------------------------- +function tooltip.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + args = args or {} + local objects = args.objects or {} + style = redutil.table.merge(default_style(), style or {}) + + -- Construct tooltip window with wibox and textbox + -------------------------------------------------------------------------------- + local ttp = { wibox = wibox({ type = "tooltip" }), tip = nil } + local tb = wibox.widget.textbox() + tb:set_align("center") + + ttp.widget = tb + ttp.wibox:set_widget(tb) + tb:set_font(style.font) + + -- configure wibox properties + ttp.wibox.visible = false + ttp.wibox.ontop = true + ttp.wibox.border_width = style.border_width + ttp.wibox.border_color = style.color.border + ttp.wibox.shape = style.shape + ttp.wibox:set_bg(style.color.wibox) + ttp.wibox:set_fg(style.color.text) + + -- Tooltip size configurator + -------------------------------------------------------------------------------- + function ttp:set_geometry() + local wibox_sizes = self.wibox:geometry() + local w, h = self.widget:get_preferred_size() + local requsted_width = w + 2*style.padding.horizontal + local requsted_height = h + 2*style.padding.vertical + + if wibox_sizes.width ~= requsted_width or wibox_sizes.height ~= requsted_height then + self.wibox:geometry({ + width = requsted_width, + height = requsted_height + }) + end + end + + -- Set timer to make delay before tooltip show + -------------------------------------------------------------------------------- + local show_timer = timer({ timeout = style.timeout }) + show_timer:connect_signal("timeout", + function() + ttp:set_geometry() + if style.set_position then + style.set_position(ttp.wibox) + else + awful.placement.under_mouse(ttp.wibox) + end + awful.placement.no_offscreen(ttp.wibox) + ttp.wibox.visible = true + show_timer:stop() + end) + + -- Tooltip metods + -------------------------------------------------------------------------------- + function ttp.show() + if not show_timer.started then show_timer:start() end + end + + function ttp.hide() + if show_timer.started then show_timer:stop() end + if ttp.wibox.visible then ttp.wibox.visible = false end + end + + function ttp:set_text(text) + if self.tip ~= text then + self.widget:set_text(text) + self.tip = text + + if self.wibox.visible then + self:set_geometry() + self.wibox.x = mouse.coords().x - self.wibox.width / 2 + awful.placement.no_offscreen(self.wibox) + end + end + end + + function ttp:add_to_object(object) + object:connect_signal("mouse::enter", self.show) + object:connect_signal("mouse::leave", self.hide) + end + + function ttp:remove_from_object(object) + object:disconnect_signal("mouse::enter", self.show) + object:disconnect_signal("mouse::leave", self.hide) + end + + -- Add tooltip to objects + -------------------------------------------------------------------------------- + if objects then + for _, object in ipairs(objects) do + ttp:add_to_object(object) + end + end + + -------------------------------------------------------------------------------- + return ttp +end + +-- Config metatable to call tooltip module as function +----------------------------------------------------------------------------------------------------------------------- +function tooltip.mt:__call(...) + return tooltip.new(...) +end + +return setmetatable(tooltip, tooltip.mt) diff --git a/awesome/.config/awesome/redflat/float/top.lua b/awesome/.config/awesome/redflat/float/top.lua new file mode 100644 index 0000000..9c8112a --- /dev/null +++ b/awesome/.config/awesome/redflat/float/top.lua @@ -0,0 +1,379 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat top widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Widget with list of top processes +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local unpack = unpack or table.unpack + +local awful = require("awful") +local beautiful = require("beautiful") +local wibox = require("wibox") +local timer = require("gears.timer") + +local redutil = require("redflat.util") +local system = require("redflat.system") +local decoration = require("redflat.float.decoration") +local redtip = require("redflat.float.hotkeys") + + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local top = { keys = {} } + +-- key bindings +top.keys.management = { + { + {}, "c", function() top:set_sort("cpu") end, + { description = "Sort by CPU usage", group = "Management" } + }, + { + {}, "m", function() top:set_sort("mem") end, + { description = "Sort by RAM usage", group = "Management" } + }, +} + +top.keys.action = { + { + {}, "k", function() top.kill_selected() end, + { description = "Kill process", group = "Action" } + }, + { + {}, "Escape", function() top:hide() end, + { description = "Close top list widget", group = "Action" } + }, + { + { "Mod4" }, "F1", function() redtip:show() end, + { description = "Show hotkeys helper", group = "Action" } + }, +} + +top.keys.all = awful.util.table.join(top.keys.management, top.keys.action) + +top._fake_keys = { + { + {}, "N", nil, + { description = "Select process by key", group = "Management", + keyset = { "1", "2", "3", "4", "5", "6", "7", "8", "9" } } + }, +} + + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + timeout = 2, + screen_gap = 0, + set_position = nil, + geometry = { width = 460, height = 380 }, + border_margin = { 10, 10, 10, 10 }, + labels_width = { num = 30, cpu = 70, mem = 120 }, + title_height = 48, + list_side_gap = 8, + border_width = 2, + bottom_height = 80, + button_margin = { 140, 140, 22, 22 }, + keytip = { geometry = { width = 400 } }, + title_font = "Sans 14 bold", + unit = { { "KB", -1 }, { "MB", 1024 }, { "GB", 1024^2 } }, + color = { border = "#575757", text = "#aaaaaa", highlight = "#eeeeee", main = "#b1222b", + bg = "#161616", bg_second = "#181818", wibox = "#202020" }, + shape = nil + + } + return redutil.table.merge(style, redutil.table.check(beautiful, "float.top") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Sort functions +-------------------------------------------------------------------------------- +local function sort_by_cpu(a, b) + return a.pcpu > b.pcpu +end + +local function sort_by_mem(a, b) + return a.mem > b.mem +end + +-- Fuction to build list item +-------------------------------------------------------------------------------- +local function construct_item(style) + local item = {} + item.label = { + number = wibox.widget.textbox(), + name = wibox.widget.textbox(), + cpu = wibox.widget.textbox(), + mem = wibox.widget.textbox() + } + + item.label.cpu:set_align("right") + item.label.mem:set_align("right") + + local bg = style.color.bg + + -- Construct item layouts + ------------------------------------------------------------ + local mem_label_with_gap = wibox.container.margin(item.label.mem, 0, style.list_side_gap) + local num_label_with_gap = wibox.container.margin(item.label.number, style.list_side_gap) + + local right = wibox.layout.fixed.horizontal() + right:add(wibox.container.constraint(item.label.cpu, "exact", style.labels_width.cpu, nil)) + right:add(wibox.container.constraint(mem_label_with_gap, "exact", style.labels_width.mem, nil)) + + local middle = wibox.layout.align.horizontal() + middle:set_left(item.label.name) + + local left = wibox.container.constraint(num_label_with_gap, "exact", style.labels_width.num, nil) + + local item_horizontal = wibox.layout.align.horizontal() + item_horizontal:set_left(left) + item_horizontal:set_middle(middle) + item_horizontal:set_right(right) + item.layout = wibox.container.background(item_horizontal, bg) + + -- item functions + ------------------------------------------------------------ + function item:set_bg(color) + bg = color + item.layout:set_bg(color) + end + + function item:set_select() + item.layout:set_bg(style.color.main) + item.layout:set_fg(style.color.highlight) + end + + function item:set_unselect() + item.layout:set_bg(bg) + item.layout:set_fg(style.color.text) + end + + function item:set(args) + if args.number then item.label.number:set_text(args.number) end + if args.name then item.label.name:set_text(args.name) end + if args.cpu then item.label.cpu:set_text(args.cpu) end + if args.mem then item.label.mem:set_text(args.mem) end + if args.pid then item.pid = args.pid end + end + + ------------------------------------------------------------ + return item +end + +-- Fuction to build top list +-------------------------------------------------------------------------------- +local function list_construct(n, style, select_function) + local list = {} + + local list_layout = wibox.layout.flex.vertical() + list.layout = wibox.container.background(list_layout, style.color.bg) + + list.items = {} + for i = 1, n do + list.items[i] = construct_item(style) + list.items[i]:set({ number = i}) + list.items[i]:set_bg((i % 2) == 1 and style.color.bg or style.color.bg_second) + list_layout:add(list.items[i].layout) + + list.items[i].layout:buttons(awful.util.table.join( + awful.button({ }, 1, function() select_function(i) end) + )) + end + + return list +end + +-- Initialize top widget +----------------------------------------------------------------------------------------------------------------------- +function top:init() + + -- Initialize vars + -------------------------------------------------------------------------------- + local number_of_lines = 9 -- number of lines in process list + local selected = {} + local cpu_storage = { cpu_total = {}, cpu_active = {} } + local style = default_style() + local sort_function, title, toplist + + self.style = style + + -- Select process function + -------------------------------------------------------------------------------- + local function select_item(i) + if selected.number and selected.number ~= i then toplist.items[selected.number]:set_unselect() end + toplist.items[i]:set_select() + selected.pid = toplist.items[i].pid + selected.number = i + end + + -- Set sorting rule + -------------------------------------------------------------------------------- + function self:set_sort(args) + if args == "cpu" then + sort_function = sort_by_cpu + title:set({ cpu = "↓CPU", mem = "Memory"}) + elseif args == "mem" then + sort_function = sort_by_mem + title:set({ cpu = "CPU", mem = "↓Memory"}) + end + end + + -- Kill selected process + -------------------------------------------------------------------------------- + function self.kill_selected() + if selected.number then awful.spawn.with_shell("kill " .. selected.pid) end + self:update_list() + end + + -- Widget keygrabber + -------------------------------------------------------------------------------- + self.keygrabber = function(mod, key, event) + if event ~= "press" then return end + for _, k in ipairs(self.keys.all) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return end + end + if string.match("123456789", key) then select_item(tonumber(key)) end + end + + -- Build title + -------------------------------------------------------------------------------- + title = construct_item(style) + title:set_bg(style.color.wibox) + title:set({ number = "#", name = "Process Name", cpu = "↓ CPU", mem = "Memory"}) + + for _, txtbox in pairs(title.label) do + txtbox:set_font(style.title_font) + end + + title.label.cpu:buttons(awful.util.table.join( + awful.button({ }, 1, function() self:set_sort("cpu") end) + )) + title.label.mem:buttons(awful.util.table.join( + awful.button({ }, 1, function() self:set_sort("mem") end) + )) + + -- Build top list + -------------------------------------------------------------------------------- + toplist = list_construct(number_of_lines, style, select_item) + + -- Update function + -------------------------------------------------------------------------------- + function self:update_list() + local proc = system.proc_info(cpu_storage) + table.sort(proc, sort_function) + + if selected.number then + toplist.items[selected.number]:set_unselect() + selected.number = nil + end + + for i = 1, number_of_lines do + toplist.items[i]:set({ + name = proc[i].name, + cpu = string.format("%.1f", proc[i].pcpu * 100), + --mem = string.format("%.0f", proc[i].mem) .. " MB", + mem = redutil.text.dformat(proc[i].mem, style.unit, 2, " "), + pid = proc[i].pid + }) + + if selected.pid and selected.pid == proc[i].pid then + toplist.items[i]:set_select() + selected.number = i + end + end + end + + -- Construct widget layouts + -------------------------------------------------------------------------------- + local buttonbox = wibox.widget.textbox("Kill") + + local button_widget = decoration.button(buttonbox, self.kill_selected) + + local button_layout = wibox.container.margin(button_widget, unpack(style.button_margin)) + local bottom_area = wibox.container.constraint(button_layout, "exact", nil, style.bottom_height) + + local area = wibox.layout.align.vertical() + area:set_top(wibox.container.constraint(title.layout, "exact", nil, style.title_height)) + area:set_middle(toplist.layout) + area:set_bottom(bottom_area) + local list_layout = wibox.container.margin(area, unpack(style.border_margin)) + + -- Create floating wibox for top widget + -------------------------------------------------------------------------------- + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + + self.wibox:set_widget(list_layout) + self.wibox:geometry(style.geometry) + + -- Update timer + -------------------------------------------------------------------------------- + self.update_timer = timer({timeout = style.timeout}) + self.update_timer:connect_signal("timeout", function() self:update_list() end) + + -- First run actions + -------------------------------------------------------------------------------- + self:set_keys() + self:set_sort("cpu") + self:update_list() +end + +-- Hide top widget +----------------------------------------------------------------------------------------------------------------------- +function top:hide() + self.wibox.visible = false + self.update_timer:stop() + awful.keygrabber.stop(self.keygrabber) + redtip:remove_pack() +end + +-- Show top widget +----------------------------------------------------------------------------------------------------------------------- +function top:show(srt) + if not self.wibox then self:init() end + if self.wibox.visible then + top:hide() + else + if srt then self:set_sort(srt) end + self:update_list() + + if self.style.set_position then + self.style.set_position(self.wibox) + else + awful.placement.under_mouse(self.wibox) + end + redutil.placement.no_offscreen(self.wibox, self.style.screen_gap, screen[mouse.screen].workarea) + + self.wibox.visible = true + self.update_timer:start() + awful.keygrabber.run(self.keygrabber) + + redtip:set_pack("Top process", self.tip, self.style.keytip.column, self.style.keytip.geometry) + end +end + +-- Set user hotkeys +----------------------------------------------------------------------------------------------------------------------- +function top:set_keys(keys, layout) + layout = layout or "all" + if keys then + self.keys[layout] = keys + if layout ~= "all" then self.keys.all = awful.util.table.join(self.keys.management, self.keys.action) end + end + + self.tip = awful.util.table.join(self.keys.all, self._fake_keys) +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return top diff --git a/awesome/.config/awesome/redflat/gauge/audio/blue.lua b/awesome/.config/awesome/redflat/gauge/audio/blue.lua new file mode 100644 index 0000000..b87ba27 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/audio/blue.lua @@ -0,0 +1,80 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat volume indicator widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Indicator with audio icon +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local unpack = unpack or table.unpack + +local wibox = require("wibox") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") +local reddash = require("redflat.gauge.graph.dash") + + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local audio = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = 100, + icon = redutil.base.placeholder(), + gauge = reddash.new, + dash = {}, + dmargin = { 10, 0, 0, 0 }, + color = { icon = "#a0a0a0", mute = "#404040" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.audio.blue") or {}) +end + +-- Create a new audio widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function audio.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- Construct widget + -------------------------------------------------------------------------------- + local icon = svgbox(style.icon) + + local layout = wibox.layout.fixed.horizontal() + layout:add(icon) + + local dash + if style.gauge then + dash = style.gauge(style.dash) + layout:add(wibox.container.margin(dash, unpack(style.dmargin))) + end + + local widg = wibox.container.constraint(layout, "exact", style.width) + + -- User functions + ------------------------------------------------------------ + function widg:set_value(x) if dash then dash:set_value(x) end end + + function widg:set_mute(mute) + icon:set_color(mute and style.color.mute or style.color.icon) + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call audio module as function +----------------------------------------------------------------------------------------------------------------------- +function audio.mt:__call(...) + return audio.new(...) +end + +return setmetatable(audio, audio.mt) diff --git a/awesome/.config/awesome/redflat/gauge/audio/init.lua b/awesome/.config/awesome/redflat/gauge/audio/init.lua new file mode 100644 index 0000000..a7f844c --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/audio/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.gauge.audio" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/gauge/audio/red.lua b/awesome/.config/awesome/redflat/gauge/audio/red.lua new file mode 100644 index 0000000..bf4eb64 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/audio/red.lua @@ -0,0 +1,93 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat volume indicator widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Indicator with audio icon +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local string = string +local math = math + +local wibox = require("wibox") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") + + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local audio = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + icon = { volume = redutil.base.placeholder(), mute = redutil.base.placeholder() }, + step = 0.05, + color = { main = "#b1222b", icon = "#a0a0a0", mute = "#404040" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.audio.red") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function pattern_string(height, value, c1, c2) + return string.format("linear:0,%s:0,0:0,%s:%s,%s:%s,%s:1,%s", height, c1, value, c1, value, c2, c2) +end + +-- Create a new audio widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function audio.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- Icon widgets + ------------------------------------------------------------ + local icon = {} + icon.ready = svgbox(style.icon.volume) + icon.ready:set_color(style.color.icon) + icon.mute = svgbox(style.icon.mute) + icon.mute:set_color(style.color.mute) + + -- Create widget + -------------------------------------------------------------------------------- + local widg = wibox.container.background(icon.ready) + widg._data = { level = 0 } + + -- User functions + ------------------------------------------------------------ + function widg:set_value(x) + if x > 1 then x = 1 end + + if self.widget._image then + local level = math.floor(x / style.step) * style.step + + if level ~= self._data.level then + self._data.level = level + local h = self.widget._image.height + icon.ready:set_color(pattern_string(h, level, style.color.main, style.color.icon)) + end + end + end + + function widg:set_mute(mute) + widg:set_widget(mute and icon.mute or icon.ready) + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call audio module as function +----------------------------------------------------------------------------------------------------------------------- +function audio.mt:__call(...) + return audio.new(...) +end + +return setmetatable(audio, audio.mt) diff --git a/awesome/.config/awesome/redflat/gauge/graph/bar.lua b/awesome/.config/awesome/redflat/gauge/graph/bar.lua new file mode 100644 index 0000000..f71fba8 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/graph/bar.lua @@ -0,0 +1,80 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat progressbar widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Horizontal progresspar +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local progressbar = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + color = { main = "#b1222b", gray = "#404040" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.graph.bar") or {}) +end + +-- Create a new progressbar widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function progressbar.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + widg._data = { value = 0 } + + -- User functions + ------------------------------------------------------------ + function widg:set_value(x) + local value = x < 1 and x or 1 + if self._data.value ~= value then + self._data.value = value + self:emit_signal("widget::redraw_needed") + end + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + return width, height + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + cr:set_source(color(style.color.gray)) + cr:rectangle(0, 0, width, height) + cr:fill() + cr:set_source(color(style.color.main)) + cr:rectangle(0, 0, self._data.value * width, height) + cr:fill() + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call progressbar module as function +----------------------------------------------------------------------------------------------------------------------- +function progressbar.mt:__call(...) + return progressbar.new(...) +end + +return setmetatable(progressbar, progressbar.mt) diff --git a/awesome/.config/awesome/redflat/gauge/graph/dash.lua b/awesome/.config/awesome/redflat/gauge/graph/dash.lua new file mode 100644 index 0000000..c0bcb00 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/graph/dash.lua @@ -0,0 +1,88 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat dashcontrol widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Horizontal progresspar with stairs form +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local dashcontrol = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + plain = false, + bar = { width = 4, num = 10 }, + color = { main = "#b1222b", gray = "#404040" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.graph.dash") or {}) +end + +-- Create a new dashcontrol widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function dashcontrol.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + widg._data = { value = 0, cnum = 0 } + + -- User functions + ------------------------------------------------------------ + function widg:set_value(x) + self._data.value = x < 1 and x or 1 + local num = math.ceil(widg._data.value * style.bar.num) + + if num ~= self._data.cnum then + self._data.cnum = num + self:emit_signal("widget::redraw_needed") + end + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + return width, height + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + local wstep = (width - style.bar.width) / (style.bar.num - 1) + local hstep = height / style.bar.num + --self._data.cnum = math.ceil(widg._data.value * style.bar.num) + + for i = 1, style.bar.num do + cr:set_source(color(i > self._data.cnum and style.color.gray or style.color.main)) + cr:rectangle((i - 1) * wstep, height, style.bar.width, style.plain and -height or - i * hstep) + cr:fill() + end + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call dashcontrol module as function +----------------------------------------------------------------------------------------------------------------------- +function dashcontrol.mt:__call(...) + return dashcontrol.new(...) +end + +return setmetatable(dashcontrol, dashcontrol.mt) diff --git a/awesome/.config/awesome/redflat/gauge/graph/dots.lua b/awesome/.config/awesome/redflat/gauge/graph/dots.lua new file mode 100644 index 0000000..ac19017 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/graph/dots.lua @@ -0,0 +1,109 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat dotcount widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Simple graphical counter +-- Displaying current value by dots number +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local counter = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + column_num = { 2, 5 }, -- {min, max} + row_num = 3, + dot_size = 5, + dot_gap_h = 5, + color = { main = "#b1222b", gray = "#575757" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.graph.dots") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +--local function round(x) +-- return math.floor(x + 0.5) +--end + +-- Create a new counter widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function counter.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + widg._data = { + count_num = 0, + column_num = style.column_num[1] + } + + -- User functions + ------------------------------------------------------------ + function widg:set_num(num) + if num ~= self._data.count_num then + self._data.count_num = num + self._data.column_num = math.min( + math.max(style.column_num[1], math.ceil(num / style.row_num)), style.column_num[2] + ) + self:emit_signal("widget::redraw_needed") + end + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, _, height) + local width = (style.dot_size + style.dot_gap_h) * self._data.column_num - style.dot_gap_h + return width, height + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) +-- local maxnum = style.row_num * data.column_num + local gap_v = (height - style.row_num * style.dot_size) / (style.row_num - 1) + + cr:translate(0, height) + for i = 1, style.row_num do + for j = 1, self._data.column_num do + local cc = (j + (i - 1) * self._data.column_num) <= self._data.count_num + and style.color.main or style.color.gray + cr:set_source(color(cc)) + + cr:rectangle(0, 0, style.dot_size, - style.dot_size) + cr:fill() + + cr:translate(style.dot_size + style.dot_gap_h, 0) + end + cr:translate(- (style.dot_gap_h + width), - (style.dot_size + gap_v)) + end + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call dotcount module as function +----------------------------------------------------------------------------------------------------------------------- +function counter.mt:__call(...) + return counter.new(...) +end + +return setmetatable(counter, counter.mt) diff --git a/awesome/.config/awesome/redflat/gauge/graph/init.lua b/awesome/.config/awesome/redflat/gauge/graph/init.lua new file mode 100644 index 0000000..40d6aee --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/graph/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.gauge.graph" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/gauge/icon/double.lua b/awesome/.config/awesome/redflat/gauge/icon/double.lua new file mode 100644 index 0000000..487b0b3 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/icon/double.lua @@ -0,0 +1,96 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat indicator widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Double mage indicator +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local string = string +local math = math + +local wibox = require("wibox") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") + + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local dubgicon = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + icon1 = redutil.base.placeholder(), + icon2 = redutil.base.placeholder(), + igap = 8, + step = 0.05, + is_vertical = false, + color = { main = "#b1222b", icon = "#a0a0a0" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.icon.double") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function pattern_string_v(height, value, c1, c2) + return string.format("linear:0,%s:0,0:0,%s:%s,%s:%s,%s:1,%s", height, c1, value, c1, value, c2, c2) +end + +local function pattern_string_h(width, value, c1, c2) + return string.format("linear:0,0:%s,0:0,%s:%s,%s:%s,%s:1,%s", width, c1, value, c1, value, c2, c2) +end + +-- Create a new dubgicon widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function dubgicon.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + local pattern = style.is_vertical and pattern_string_v or pattern_string_h + + -- Create widget + -------------------------------------------------------------------------------- + local fixed = wibox.layout.fixed.horizontal() + local layout = wibox.container.constraint(fixed, "exact", style.width) + layout._icon1 = svgbox(style.icon1) + layout._icon2 = svgbox(style.icon2) + layout._data = { level = { 0, 0 }} + fixed:add(wibox.container.margin(layout._icon1, 0, style.igap, 0, 0)) + fixed:add(layout._icon2) + + -- User functions + ------------------------------------------------------------ + function layout:set_value(value) + local level = { + math.floor((value[1] < 1 and value[1] or 1) / style.step) * style.step, + math.floor((value[2] < 1 and value[2] or 1) / style.step) * style.step + } + + for i, widg in ipairs({ self._icon1, self._icon2 }) do + if widg._image and level[i] ~= layout._data.level[i] then + layout._data.level[i] = level[i] + + local d = style.is_vertical and widg._image.height or widg._image.width + widg:set_color(pattern(d, level[i], style.color.main, style.color.icon)) + end + end + end + + -------------------------------------------------------------------------------- + return layout +end + +-- Config metatable to call dubgicon module as function +----------------------------------------------------------------------------------------------------------------------- +function dubgicon.mt:__call(...) + return dubgicon.new(...) +end + +return setmetatable(dubgicon, dubgicon.mt) diff --git a/awesome/.config/awesome/redflat/gauge/icon/init.lua b/awesome/.config/awesome/redflat/gauge/icon/init.lua new file mode 100644 index 0000000..fa57e49 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/icon/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.gauge.icon" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/gauge/icon/single.lua b/awesome/.config/awesome/redflat/gauge/icon/single.lua new file mode 100644 index 0000000..1cbede4 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/icon/single.lua @@ -0,0 +1,95 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat indicator widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Image indicator +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math +local string = string + +local beautiful = require("beautiful") +local wibox = require("wibox") + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") + + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local gicon = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + icon = redutil.base.placeholder(), + step = 0.05, + is_vertical = false, + color = { main = "#b1222b", icon = "#a0a0a0", urgent = "#32882d" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.icon.single") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function pattern_string_v(height, value, c1, c2) + return string.format("linear:0,%s:0,0:0,%s:%s,%s:%s,%s:1,%s", height, c1, value, c1, value, c2, c2) +end + +local function pattern_string_h(width, value, c1, c2) + return string.format("linear:0,0:%s,0:0,%s:%s,%s:%s,%s:1,%s", width, c1, value, c1, value, c2, c2) +end + +-- Create a new gicon widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function gicon.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + local pattern = style.is_vertical and pattern_string_v or pattern_string_h + + -- Create widget + -------------------------------------------------------------------------------- + local widg = wibox.container.background(svgbox(style.icon)) + widg._data = { + color = style.color.main, + level = 0, + } + + -- User functions + ------------------------------------------------------------ + function widg:set_value(x) + if x > 1 then x = 1 end + + if self.widget._image then + local level = math.floor(x / style.step) * style.step + + if level ~= self._data.level then + self._data.level = level + local d = style.is_vertical and self.widget._image.height or self._image.width + self.widget:set_color(pattern(d, level, self._data.color, style.color.icon)) + end + end + end + + function widg:set_alert(alert) + -- not sure about redraw after alert set + self._data.color = alert and style.color.urgent or style.color.main + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call gicon module as function +----------------------------------------------------------------------------------------------------------------------- +function gicon.mt:__call(...) + return gicon.new(...) +end + +return setmetatable(gicon, gicon.mt) diff --git a/awesome/.config/awesome/redflat/gauge/init.lua b/awesome/.config/awesome/redflat/gauge/init.lua new file mode 100644 index 0000000..b07217e --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.gauge" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/gauge/monitor/circle.lua b/awesome/.config/awesome/redflat/gauge/monitor/circle.lua new file mode 100644 index 0000000..dd1cdbe --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/monitor/circle.lua @@ -0,0 +1,109 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat monitor widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Widget with circle indicator +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local cirmon = { mt = {} } +local TPI = math.pi * 2 + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = nil, + line_width = 4, + radius = 14, + iradius = 6, + step = 0.02, + color = { main = "#b1222b", gray = "#575757", icon = "#a0a0a0" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.monitor.circle") or {}) +end + +-- Create a new monitor widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function cirmon.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + local cs = -TPI / 4 + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + widg._data = { color = style.color.icon, level = 0, alert = false } + + if style.width then widg:set_forced_width(style.width) end + + -- User functions + ------------------------------------------------------------ + function widg:set_value(x) + local value = x < 1 and x or 1 + local level = math.floor(value / style.step) * style.step + + if level ~= self._data.level then + self._data.level = level + self:emit_signal("widget::redraw_needed") + end + end + + function widg:set_alert(alert) + if alert ~= self._data.alert then + self._data.alert = alert + self._data.color = alert and style.color.main or style.color.icon + self:emit_signal("widget::redraw_needed") + end + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + local size = math.min(width, height) + return size, size + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + + -- center circle + cr:set_source(color(self._data.color)) + cr:arc(width / 2, height / 2, style.iradius, 0, TPI) + cr:fill() + + -- progress circle + cr:set_line_width(style.line_width) + local cd = { TPI, TPI * self._data.level } + for i = 1, 2 do + cr:set_source(color(i > 1 and style.color.main or style.color.gray)) + cr:arc(width / 2, height / 2, style.radius, cs, cs + cd[i]) + cr:stroke() + end + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call monitor module as function +----------------------------------------------------------------------------------------------------------------------- +function cirmon.mt:__call(...) + return cirmon.new(...) +end + +return setmetatable(cirmon, cirmon.mt) diff --git a/awesome/.config/awesome/redflat/gauge/monitor/dash.lua b/awesome/.config/awesome/redflat/gauge/monitor/dash.lua new file mode 100644 index 0000000..7c2f3d7 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/monitor/dash.lua @@ -0,0 +1,98 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat monitor widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Widget with dash indicator +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local dashmon = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = 40, + line = { num = 5, height = 4 }, + color = { main = "#b1222b", urgent = "#00725b", gray = "#575757" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.monitor.dash") or {}) +end + +-- Create a new monitor widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function dashmon.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + widg._data = { color = style.color.main, level = 0, alert = false } + + if style.width then widg:set_forced_width(style.width) end + + -- User functions + ------------------------------------------------------------ + function widg:set_value(x) + local value = x < 1 and x or 1 + local level = math.ceil(style.line.num * value) + + if level ~= self._data.level then + self._data.level = level + self:emit_signal("widget::redraw_needed") + end + end + + function widg:set_alert(alert) + if alert ~= self._data.alert then + self._data.alert = alert + self._data.color = alert and style.color.urgent or style.color.main + self:emit_signal("widget::redraw_needed") + end + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + return width, height + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + + local gap = (height - style.line.height * style.line.num) / (style.line.num - 1) + local dy = style.line.height + gap + + for i = 1, style.line.num do + cr:set_source(color(i <= self._data.level and self._data.color or style.color.gray)) + cr:rectangle(0, height - (i - 1) * dy, width, - style.line.height) + cr:fill() + end + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call monitor module as function +----------------------------------------------------------------------------------------------------------------------- +function dashmon.mt:__call(...) + return dashmon.new(...) +end + +return setmetatable(dashmon, dashmon.mt) diff --git a/awesome/.config/awesome/redflat/gauge/monitor/double.lua b/awesome/.config/awesome/redflat/gauge/monitor/double.lua new file mode 100644 index 0000000..243a3c9 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/monitor/double.lua @@ -0,0 +1,130 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat doublemonitor widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Widget with two progressbar and icon +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math +local unpack = unpack or table.unpack + +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local doublemonitor = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + line = { width = 4, v_gap = 6, gap = 4, num = 5 }, + icon = redutil.base.placeholder(), + dmargin = { 10, 0, 0, 0 }, + width = 100, + color = { main = "#b1222b", gray = "#575757", icon = "#a0a0a0", urgent = "#32882d" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.monitor.double") or {}) +end + +-- Create progressbar widget +----------------------------------------------------------------------------------------------------------------------- +local function pbar(style) + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + widg._data = { level = { 0, 0 }} + + -- User functions + ------------------------------------------------------------ + function widg:set_value(value) + local level = { + math.ceil((value[1] < 1 and value[1] or 1) * style.line.num), + math.ceil((value[2] < 1 and value[2] or 1) * style.line.num), + } + + if level[1] ~= self._data.level[1] or level[2] ~= self._data.level[2] then + self._data.level[1] = level[1] + self._data.level[2] = level[2] + self:emit_signal("widget::redraw_needed") + end + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + return width, height + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + local wd = (width + style.line.gap) / style.line.num - style.line.gap + local dy = (height - (2 * style.line.width + style.line.v_gap)) / 2 + + for i = 1, 2 do + for k = 1, style.line.num do + cr:set_source(color(k <= self._data.level[i] and style.color.main or style.color.gray)) + cr:rectangle( + (k - 1) * (wd + style.line.gap), dy + (i - 1) * (style.line.width + style.line.v_gap), + wd, style.line.width + ) + cr:fill() + end + end + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Cunstruct a new doublemonitor widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function doublemonitor.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- Construct layout + -------------------------------------------------------------------------------- + local fixed = wibox.layout.fixed.horizontal() + fixed:set_forced_width(style.width) + local widg = pbar(style) + local icon = svgbox(style.icon) + + icon:set_color(style.color.icon) + + fixed:add(icon) + fixed:add(wibox.container.margin(widg, unpack(style.dmargin))) + + -- User functions + -------------------------------------------------------------------------------- + function fixed:set_value(value) + widg:set_value(value) + end + + function fixed:set_alert(alert) + icon:set_color(alert and style.color.urgent or style.color.icon) + end + + -------------------------------------------------------------------------------- + return fixed +end + +-- Config metatable to call doublemonitor module as function +----------------------------------------------------------------------------------------------------------------------- +function doublemonitor.mt:__call(...) + return doublemonitor.new(...) +end + +return setmetatable(doublemonitor, doublemonitor.mt) diff --git a/awesome/.config/awesome/redflat/gauge/monitor/init.lua b/awesome/.config/awesome/redflat/gauge/monitor/init.lua new file mode 100644 index 0000000..80aacea --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/monitor/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.gauge.monitor" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/gauge/monitor/plain.lua b/awesome/.config/awesome/redflat/gauge/monitor/plain.lua new file mode 100644 index 0000000..82b0e45 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/monitor/plain.lua @@ -0,0 +1,113 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat monitor widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Widget with label and progressbar +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local monitor = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + line = { height = 4, y = 30 }, + font = { font = "Sans", size = 16, face = 0, slant = 0 }, + text_shift = 22, + label = "MON", + width = 100, + step = 0.05, + color = { main = "#b1222b", gray = "#575757", icon = "#a0a0a0" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.monitor.plain") or {}) +end + +-- Create a new monitor widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function monitor.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + widg._data = { color = style.color.icon, level = 0, alert = false, label = style.label } + + if style.width then widg:set_forced_width(style.width) end + + -- User functions + ------------------------------------------------------------ + function widg:set_value(x) + local value = x < 1 and x or 1 + local level = math.floor(value / style.step) * style.step + + if level ~= self._data.level then + self._data.level = level + self:emit_signal("widget::redraw_needed") + end + end + + function widg:set_label(label) + if label ~= self._data.label then + self._data.label = label + self:emit_signal("widget::redraw_needed") + end + end + + function widg:set_alert(alert) + if alert ~= self._data.alert then + self._data.alert = alert + self._data.color = alert and style.color.main or style.color.icon + self:emit_signal("widget::redraw_needed") + end + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + return width, height + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width) + + -- label + cr:set_source(color(self._data.color)) + redutil.cairo.set_font(cr, style.font) + redutil.cairo.textcentre.horizontal(cr, { width/2, style.text_shift }, self._data.label) + + -- progressbar + local wd = { width, width * self._data.level } + for i = 1, 2 do + cr:set_source(color(i > 1 and style.color.main or style.color.gray)) + cr:rectangle(0, style.line.y, wd[i], style.line.height) + cr:fill() + end + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call monitor module as function +----------------------------------------------------------------------------------------------------------------------- +function monitor.mt:__call(...) + return monitor.new(...) +end + +return setmetatable(monitor, monitor.mt) diff --git a/awesome/.config/awesome/redflat/gauge/separator.lua b/awesome/.config/awesome/redflat/gauge/separator.lua new file mode 100644 index 0000000..20d6776 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/separator.lua @@ -0,0 +1,87 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat separatoe widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Simple graphical separator to decorate panel +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local separator = {} + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + marginv = { 0, 0, 0, 0 }, + marginh = { 0, 0, 0, 0 }, + color = { shadow1 = "#141414", shadow2 = "#313131" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.separator") or {}) +end + +-- Create a new separator widget +-- Total size two pixels bigger than sum of margins for general direction +----------------------------------------------------------------------------------------------------------------------- +local function separator_base(horizontal, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + if not style.margin then + style.margin = horizontal and style.marginh or style.marginv + end + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + if horizontal then + return width, 2 + style.margin[3] + style.margin[4] + else + return 2 + style.margin[1] + style.margin[2], height + end + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + local w = width - style.margin[1] - style.margin[2] + local h = height - style.margin[3] - style.margin[4] + + cr:translate(style.margin[1], style.margin[3]) + cr:set_source(color(style.color.shadow1)) + if horizontal then cr:rectangle(0, 0, w, 1) else cr:rectangle(0, 0, 1, h) end + cr:fill() + + cr:set_source(color(style.color.shadow2)) + if horizontal then cr:rectangle(0, 1, w, 1) else cr:rectangle(1, 0, 1, h) end + cr:fill() + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Horizontal and vertial variants +----------------------------------------------------------------------------------------------------------------------- +function separator.vertical(style) + return separator_base(false, style) +end + +function separator.horizontal(style) + return separator_base(true, style) +end + +----------------------------------------------------------------------------------------------------------------------- +return separator diff --git a/awesome/.config/awesome/redflat/gauge/svgbox.lua b/awesome/.config/awesome/redflat/gauge/svgbox.lua new file mode 100644 index 0000000..eef63ee --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/svgbox.lua @@ -0,0 +1,218 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat svg icon widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Imagebox widget modification +-- Use Gtk PixBuf API to resize svg image +-- Color setup added +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ wibox.widget.imagebox v3.5.2 +------ (c) 2010 Uli Schlachter +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local string = string +local type = type +local pcall = pcall +local print = print +local math = math + +-- local Gdk = require("lgi").Gdk +-- local pixbuf = require("lgi").GdkPixbuf +-- local cairo = require("lgi").cairo +local base = require("wibox.widget.base") +local surface = require("gears.surface") +local color = require("gears.color") + +local pixbuf +local function load_pixbuf() + local _ = require("lgi").Gdk + pixbuf = require("lgi").GdkPixbuf +end +local is_pixbuf_loaded = pcall(load_pixbuf) + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local svgbox = { mt = {} } + +-- weak table is useless here +-- TODO: implement mechanics to clear cache +local cache = setmetatable({}, { __mode = 'k' }) + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Check if given argument is SVG file +local function is_svg(args) + return type(args) == "string" and string.match(args, "%.svg") +end + +-- Check if need scale image +local function need_scale(widg, width, height) + return (widg._image.width ~= width or widg._image.height ~= height) and widg.resize_allowed +end + +-- Cache functions +local function get_cache(file, width, height) + return cache[file .. "-" .. width .. "x" .. height] +end + +local function set_cache(file, width, height, surf) + cache[file .. "-" .. width .. "x" .. height] = surf +end + +-- Get cairo pattern +local function get_current_pattern(cr) + cr:push_group() + cr:paint() + return cr:pop_group() +end + +-- Create Gdk PixBuf from SVG file with given sizes +local function pixbuf_from_svg(file, width, height) + local cached = get_cache(file, width, height) + + if cached then + return cached + else + -- naughty.notify({ text = file }) + local buf = pixbuf.Pixbuf.new_from_file_at_scale(file, width, height, true) + set_cache(file, width, height, buf) + return buf + end +end + +-- Returns a new svgbox +----------------------------------------------------------------------------------------------------------------------- +function svgbox.new(image, resize_allowed, newcolor) + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = base.make_widget() + + -- User functions + ------------------------------------------------------------ + function widg:set_image(image_name) + local loaded_image + + if type(image_name) == "string" then + local success, result = pcall(surface.load, image_name) + if not success then + print("Error while reading '" .. image_name .. "': " .. result) + return false + end + self.image_name = image_name + loaded_image = result + else + loaded_image = surface.load(image_name) + end + + if loaded_image and (loaded_image.height <= 0 or loaded_image.width <= 0) then return false end + + self._image = loaded_image + self.is_svg = is_svg(image_name) + + self:emit_signal("widget::redraw_needed") + return true + end + + function widg:set_color(new_color) + if self.color ~= new_color then + self.color = new_color + self:emit_signal("widget::redraw_needed") + end + end + + function widg:set_resize(allowed) + self.resize_allowed = allowed + self:emit_signal("widget::redraw_needed") + end + + function widg:set_vector_resize(allowed) + self.vector_resize_allowed = allowed + self:emit_signal("widget::redraw_needed") + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + local fw, fh = self:get_forced_width(), self:get_forced_height() + if fw or fh then + return fw or width, fh or height + else + if not self._image then return 0, 0 end + + local w, h = self._image.width, self._image.height + + if self.resize_allowed or w > width or h > height then + local aspect = math.min(width / w, height / h) + return w * aspect, h * aspect + end + + return w, h + end + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + if width == 0 or height == 0 or not self._image then return end + + local w, h = self._image.width, self._image.height + local aspect = math.min(width / w, height / h) + + cr:save() + -- let's scale the image so that it fits into (width, height) + if need_scale(self, width, height) then + if self.is_svg and self.vector_resize_allowed and is_pixbuf_loaded then + -- for vector image + local pixbuf_ = pixbuf_from_svg(self.image_name, math.floor(w * aspect), math.floor(h * aspect)) + cr:set_source_pixbuf(pixbuf_, 0, 0) + else + -- for raster image + cr:scale(aspect, aspect) + cr:set_source_surface(self._image, 0, 0) + cr:scale(1/aspect, 1/aspect) -- fix this !!! + end + else + cr:set_source_surface(self._image, 0, 0) + end + + -- set icon color if need + if self.color then + local pattern = get_current_pattern(cr) + cr:scale(aspect, aspect) -- fix this !!! + cr:set_source(color(self.color)) + cr:scale(1/aspect, 1/aspect) -- fix this !!! + cr:mask(pattern, 0, 0) + else + cr:paint() + end + + cr:restore() + end + + -------------------------------------------------------------------------------- + if resize_allowed ~= nil then + widg.resize_allowed = resize_allowed + else + widg.resize_allowed = true + end + + widg.color = newcolor + widg.vector_resize_allowed = true + + if image then widg:set_image(image) end + + return widg +end + +-- Config metatable to call svgbox module as function +----------------------------------------------------------------------------------------------------------------------- +function svgbox.mt:__call(...) + return svgbox.new(...) +end + +return setmetatable(svgbox, svgbox.mt) diff --git a/awesome/.config/awesome/redflat/gauge/tag/blue.lua b/awesome/.config/awesome/redflat/gauge/tag/blue.lua new file mode 100644 index 0000000..a82faa8 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/tag/blue.lua @@ -0,0 +1,125 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat tag widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Custom widget to display tag info +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math + +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local bluetag = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = 80, + font = { font = "Sans", size = 16, face = 0, slant = 0 }, + text_shift = 32, + point = { height = 4, gap = 8, dx = 6, width = 40 }, + show_min = false, + color = { main = "#b1222b", gray = "#575757", icon = "#a0a0a0", urgent = "#32882d" } + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.tag.blue") or {}) +end + + +-- Create a new tag widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function bluetag.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- updating values + local data = { + state = { text = "TEXT" }, + width = style.width or nil + } + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + + -- User functions + ------------------------------------------------------------ + function widg:set_state(state) + data.state = state + self:emit_signal("widget::redraw_needed") + end + + function widg:set_width(width) + data.width = width + self:emit_signal("widget::redraw_needed") + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + if data.width then + return math.min(width, data.width), height + else + return width, height + end + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width) + local n = #data.state.list + + -- text + cr:set_source(color( + data.state.active and style.color.main + or (n == 0 or data.state.minimized) and style.color.gray + or style.color.icon + )) + redutil.cairo.set_font(cr, style.font) + redutil.cairo.textcentre.horizontal(cr, { width / 2, style.text_shift }, data.state.text) + + -- occupied mark + local x = (width - style.point.width) / 2 + + if n > 0 then + local l = (style.point.width - (n - 1) * style.point.dx) / n + + for i = 1, n do + local cl = data.state.list[i].focus and style.color.main or + data.state.list[i].urgent and style.color.urgent or + data.state.list[i].minimized and style.show_min and style.color.gray or + style.color.icon + cr:set_source(color(cl)) + cr:rectangle(x + (i - 1) * (style.point.dx + l), style.point.gap, l, style.point.height) + cr:fill() + end + else + cr:set_source(color(style.color.gray)) + cr:rectangle((width - style.point.width) / 2, style.point.gap, style.point.width, style.point.height) + cr:fill() + end + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call bluetag module as function +----------------------------------------------------------------------------------------------------------------------- +function bluetag.mt:__call(...) + return bluetag.new(...) +end + +return setmetatable(bluetag, bluetag.mt) diff --git a/awesome/.config/awesome/redflat/gauge/tag/green.lua b/awesome/.config/awesome/redflat/gauge/tag/green.lua new file mode 100644 index 0000000..3bbf29e --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/tag/green.lua @@ -0,0 +1,89 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat tag widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Custom widget to display tag info +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local unpack = unpack or table.unpack + +local awful = require("awful") +local wibox = require("wibox") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local greentag = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = 80, + margin = { 2, 2, 2, 2 }, + icon = { unknown = redutil.base.placeholder() }, + color = { main = "#b1222b", gray = "#575757", icon = "#a0a0a0", urgent = "#32882d" }, + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.tag.green") or {}) +end + + +-- Create a new tag widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function greentag.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- updating values + local data = { + state = {}, + width = style.width or nil + } + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.layout.align.horizontal() + widg._svgbox = svgbox() + widg:set_middle(wibox.container.margin(widg._svgbox, unpack(style.margin))) + widg:set_forced_width(data.width) + widg:set_expand("outside") + + -- User functions + ------------------------------------------------------------ + function widg:set_state(state) + data.state = state + local icon = style.icon[awful.layout.getname(state.layout)] or style.icon.unknown + self._svgbox:set_image(icon) + self._svgbox:set_color( + data.state.active and style.color.main + or data.state.urgent and style.color.urgent + or data.state.occupied and style.color.icon + or style.color.gray + ) + end + + function widg:set_width(width) + data.width = width + self.set_forced_width(width) + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call greentag module as function +----------------------------------------------------------------------------------------------------------------------- +function greentag.mt:__call(...) + return greentag.new(...) +end + +return setmetatable(greentag, greentag.mt) diff --git a/awesome/.config/awesome/redflat/gauge/tag/init.lua b/awesome/.config/awesome/redflat/gauge/tag/init.lua new file mode 100644 index 0000000..7148144 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/tag/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.gauge.tag" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/gauge/tag/orange.lua b/awesome/.config/awesome/redflat/gauge/tag/orange.lua new file mode 100644 index 0000000..0e405e5 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/tag/orange.lua @@ -0,0 +1,141 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat tag widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Custom widget to display tag info +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math + +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local orangetag = { mt = {} } +local TPI = math.pi * 2 + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = 50, + line_width = 4, + radius = 14, + iradius = 6, + cgap = TPI / 20, + show_min = true, + min_sections = 1, + text = false, + font = { font = "Sans", size = 16, face = 0, slant = 0 }, + color = { main = "#b1222b", gray = "#575757", icon = "#a0a0a0", urgent = "#32882d" } + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.tag.orange") or {}) +end + +-- Create a new tag widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function orangetag.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- updating values + local data = { + state = { text = "TEXT" }, + width = style.width or nil + } + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + + -- User functions + ------------------------------------------------------------ + function widg:set_state(state) + data.state = state + self:emit_signal("widget::redraw_needed") + end + + function widg:set_width(width) + data.width = width + self:emit_signal("widget::redraw_needed") + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + if data.width then + return math.min(width, data.width), height + else + return width, height + end + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + + local sections = math.max(#data.state.list, style.min_sections) + local step = (TPI - sections * style.cgap) / sections + local cl + + -- active mark + cl = data.state.active and style.color.main or (data.state.occupied and style.color.icon or style.color.gray) + cr:set_source(color(cl)) + + if style.text then + -- text + redutil.cairo.set_font(cr, style.font) + local ext = cr:text_extents(tostring(#data.state.list)) + cr:move_to((width - ext.width - ext.x_bearing) / 2, (height + ext.height + ext.x_bearing) / 2) + cr:show_text(data.state.text) + cr:stroke() + else + -- round + cr:arc(width / 2, height / 2, style.iradius, 0, TPI) + cr:fill() + end + + -- occupied mark + cr:set_line_width(style.line_width) + for i = 1, sections do + local cs = -TPI / 4 + (i - 1) * (step + style.cgap) + style.cgap / 2 + + if data.state.list[i] then + cl = data.state.list[i].focus and style.color.main or + data.state.list[i].urgent and style.color.urgent or + data.state.list[i].minimized and style.show_min and style.color.gray or style.color.icon + else + cl = style.color.gray + end + + cr:set_source(color(cl)) + if sections == 1 then + cr:arc(width / 2, height / 2, style.radius, 0, TPI) + else + cr:arc(width / 2, height / 2, style.radius, cs, cs + step) + end + cr:stroke() + end + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call orangetag module as function +----------------------------------------------------------------------------------------------------------------------- +function orangetag.mt:__call(...) + return orangetag.new(...) +end + +return setmetatable(orangetag, orangetag.mt) diff --git a/awesome/.config/awesome/redflat/gauge/tag/red.lua b/awesome/.config/awesome/redflat/gauge/tag/red.lua new file mode 100644 index 0000000..cf80607 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/tag/red.lua @@ -0,0 +1,191 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat tag widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Custom widget to display tag info +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math + +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local redtag = { mt = {} } + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +local function fill_geometry(width, height, geometry) + local zero_geometry = { x = 0, y = 0, width = width, height = height } + return redutil.table.merge(zero_geometry, geometry) +end + +-- Cairo drawing functions +-------------------------------------------------------------------------------- +local cairo_draw = {} + +-- Tag active mark (line) +------------------------------------------------------------ +function cairo_draw.active(cr, width, height, geometry) + geometry = fill_geometry(width, height, geometry) + + cr:rectangle(geometry.x, geometry.y, geometry.width, geometry.height) + cr:fill() +end + +-- Tag focus mark (rhombus) +------------------------------------------------------------ +function cairo_draw.focus(cr, width, height, geometry) + geometry = fill_geometry(width, height, geometry) + + cr:move_to(geometry.x + geometry.width / 2, geometry.y) + cr:rel_line_to(geometry.width / 2, geometry.height / 2) + cr:rel_line_to(- geometry.width / 2, geometry.height / 2) + cr:rel_line_to(- geometry.width / 2, - geometry.height / 2) + cr:close_path() + cr:fill() +end + +-- Tag occupied mark (label) +------------------------------------------------------------ +function cairo_draw.occupied(cr, width, height, geometry) + geometry = fill_geometry(width, height, geometry) + + cr:move_to(geometry.x, geometry.y) + cr:rel_line_to(0, geometry.height) + cr:rel_line_to(geometry.width / 2, - geometry.width / 2) + cr:rel_line_to(geometry.width / 2, geometry.width / 2) + cr:rel_line_to(0, - geometry.height) + cr:close_path() + cr:fill() +end + + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = 80, + font = { font = "Sans", size = 16, face = 0, slant = 0 }, + text_shift = 22, + counter = { size = 12, margin = 2, coord = { 40, 35 } }, + show_counter = true, + color = { main = "#b1222b", gray = "#575757", icon = "#a0a0a0", urgent = "#32882d", + wibox = "#202020" } + } + + -- functions for state marks + style.marks = cairo_draw + + -- geometry for state marks + style.geometry = { + active = { height = 5, y = 45 }, + focus = { x = 5, y = 10, width = 10, height = 15 }, + occupied = { x = 65, y = 10, width = 10, height = 15 } + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.tag.red") or {}) +end + +-- Create a new tag widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function redtag.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- updating values + local data = { + state = { text = "TEXT" }, + width = style.width or nil + } + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + + -- User functions + ------------------------------------------------------------ + function widg:set_state(state) + data.state = state + self:emit_signal("widget::redraw_needed") + end + + function widg:set_width(width) + data.width = width + self:emit_signal("widget::redraw_needed") + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + if data.width then + return math.min(width, data.width), height + else + return width, height + end + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + + -- text + cr:set_source(color(style.color.icon)) + redutil.cairo.set_font(cr, style.font) + redutil.cairo.textcentre.horizontal(cr, { width/2, style.text_shift }, data.state.text) + + -- active mark + cr:set_source(color(data.state.active and style.color.main or style.color.gray)) + style.marks.active(cr, width, height, style.geometry.active) + + -- occupied mark + if data.state.occupied then + cr:set_source(color(data.state.urgent and style.color.urgent or style.color.main)) + style.marks.occupied(cr, width, height, style.geometry.occupied) + end + + -- focus mark + if data.state.focus then + cr:set_source(color(style.color.main)) + style.marks.focus(cr, width, height, style.geometry.focus) + end + + -- counter + if style.show_counter and #data.state.list > 0 then + cr:set_font_size(style.counter.size) + local ext = cr:text_extents(tostring(#data.state.list)) + cr:set_source(color(style.color.wibox)) + cr:rectangle( + style.counter.coord[1] - ext.width / 2 - style.counter.margin, + style.counter.coord[2] - ext.height / 2 - style.counter.margin, + ext.width + 2 * style.counter.margin, + ext.height + 2 * style.counter.margin + ) + cr:fill() + + cr:set_source(color(style.color.icon)) + redutil.cairo.textcentre.full(cr, style.counter.coord, tostring(#data.state.list)) + end + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call redtag module as function +----------------------------------------------------------------------------------------------------------------------- +function redtag.mt:__call(...) + return redtag.new(...) +end + +return setmetatable(redtag, redtag.mt) diff --git a/awesome/.config/awesome/redflat/gauge/tag/ruby.lua b/awesome/.config/awesome/redflat/gauge/tag/ruby.lua new file mode 100644 index 0000000..acd5e16 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/tag/ruby.lua @@ -0,0 +1,114 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat tag widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Custom widget to display tag info +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math + +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local rubytag = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = 50, + base = { pad = 5, height = 12, thickness = 2 }, + mark = { pad = 10, height = 4 }, + color = { main = "#b1222b", gray = "#575757", icon = "#a0a0a0", urgent = "#32882d" } + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.tag.ruby") or {}) +end + +-- Create a new tag widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function rubytag.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- updating values + local data = { + width = style.width or nil + } + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + + -- User functions + ------------------------------------------------------------ + function widg:set_state(state) + data.state = state + self:emit_signal("widget::redraw_needed") + end + + function widg:set_width(width) + data.width = width + self:emit_signal("widget::redraw_needed") + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + if data.width then + return math.min(width, data.width), height + else + return width, height + end + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + + -- state + local cl = data.state.active and style.color.main or style.color.gray + cr:set_source(color(cl)) + + cr:rectangle( + style.base.pad, math.floor((height - style.base.height) / 2), + width - 2 * style.base.pad, style.base.height + ) + cr:set_line_width(style.base.thickness) + cr:stroke() + + -- focus + cl = data.state.focus and style.color.main + or data.state.urgent and style.color.urgent + or (data.state.occupied and style.color.icon or style.color.gray) + cr:set_source(color(cl)) + + cr:rectangle( + style.mark.pad, math.floor((height - style.mark.height) / 2), + width - 2 * style.mark.pad, style.mark.height + ) + + cr:fill() + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call rubytag module as function +----------------------------------------------------------------------------------------------------------------------- +function rubytag.mt:__call(...) + return rubytag.new(...) +end + +return setmetatable(rubytag, rubytag.mt) diff --git a/awesome/.config/awesome/redflat/gauge/task/green.lua b/awesome/.config/awesome/redflat/gauge/task/green.lua new file mode 100644 index 0000000..f79c638 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/task/green.lua @@ -0,0 +1,116 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat task widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Widget includes colored icon +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local wibox = require("wibox") +local beautiful = require("beautiful") +local unpack = unpack or table.unpack + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") + + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local greentask = { mt = {} } + + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = 40, + margin = { 0, 0, 0, 0 }, + df_icon = redutil.base.placeholder(), + counter = { font = "Sans 10", mask = "%d" }, + color = { main = "#b1222b", gray = "#575757", icon = "#a0a0a0", urgent = "#32882d", wibox = "#202020" }, + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.task.green") or {}) +end + + +-- Create a new greentask widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function greentask.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- updating values + local data = { + state = {}, + width = style.width or nil + } + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.layout.stack() + + widg._svgbox = svgbox() + widg._textbox = wibox.widget{ + align = 'center', + valign = 'bottom', + font = style.counter.font, + widget = wibox.widget.textbox + } + + widg._icon_layout = wibox.widget({ + nil, wibox.container.margin(widg._svgbox, unpack(style.margin)), + layout = wibox.layout.align.horizontal, + expand = "outside", + }) + widg._text_layout = wibox.widget({ + nil, nil, widg._textbox, + -- nil, nil, wibox.container.background(widg._textbox, style.color.wibox), + layout = wibox.layout.align.vertical, + }) + + widg:add(widg._icon_layout) + widg:add(widg._text_layout) + widg:set_forced_width(data.width) + + -- User functions + ------------------------------------------------------------ + function widg:set_state(state) + data.state = redutil.table.merge(data.state, state) + + -- icon + local icon = state.icon or style.df_icon + self._svgbox:set_image(icon) + self._svgbox:set_color( + data.state.focus and style.color.main + or data.state.urgent and style.color.urgent + or data.state.minimized and style.color.gray + or style.color.icon + ) + + -- counter + self._text_layout:set_visible(state.num > 1) + self._textbox:set_markup( + string.format('' .. style.counter.mask .. '', style.color.wibox, state.num) + ) + end + + function widg:set_width(width) + data.width = width + self.set_forced_width(width) + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call greentask module as function +----------------------------------------------------------------------------------------------------------------------- +function greentask.mt:__call(...) + return greentask.new(...) +end + +return setmetatable(greentask, greentask.mt) diff --git a/awesome/.config/awesome/redflat/gauge/task/init.lua b/awesome/.config/awesome/redflat/gauge/task/init.lua new file mode 100644 index 0000000..a33b632 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/task/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.gauge.task" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/gauge/task/red.lua b/awesome/.config/awesome/redflat/gauge/task/red.lua new file mode 100644 index 0000000..2e44fbc --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/task/red.lua @@ -0,0 +1,120 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat task widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Widget includes label and decorative line +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local redtask = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = 40, + line = { height = 4, y = 30 }, + font = { font = "Sans", size = 16, face = 0, slant = 0 }, + text_shift = 22, + counter = { size = 12, margin = 2 }, + color = { main = "#b1222b", gray = "#575757", icon = "#a0a0a0", + urgent = "#32882d", wibox = "#202020" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.task.red") or {}) +end + +-- Create a new redtask widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function redtask.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- updating values + local data = { + state = { text = "TXT" } + } + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + + -- User functions + ------------------------------------------------------------ + function widg:set_state(state) + data.state = redutil.table.merge(data.state, state) + self:emit_signal("widget::redraw_needed") + end + + function widg:set_width(width) + data.width = width + self:emit_signal("widget::redraw_needed") + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + if data.width then + return math.min(width, data.width), height + else + return width, height + end + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width) + + -- label + cr:set_source(color(data.state.minimized and style.color.gray or style.color.icon)) + redutil.cairo.set_font(cr, style.font) + redutil.cairo.textcentre.horizontal(cr, { width / 2, style.text_shift }, data.state.text) + + -- line + local line_color = data.state.focus and style.color.main + or data.state.urgent and style.color.urgent + or style.color.gray + cr:set_source(color(line_color)) + cr:rectangle(0, style.line.y, width, style.line.height) + cr:fill() + + -- counter + if data.state.num > 1 then + cr:set_font_size(style.counter.size) + local ext = cr:text_extents(tostring(data.state.num)) + cr:set_source(color(style.color.wibox)) + cr:rectangle( + (width - ext.width) / 2 - style.counter.margin, style.line.y, + ext.width + 2 * style.counter.margin, style.counter.size + ) + cr:fill() + + cr:set_source(color(style.color.icon)) + local coord = { width / 2, style.line.y + style.counter.size / 2 } + redutil.cairo.textcentre.horizontal(cr, coord, tostring(data.state.num)) + end + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call redtask module as function +----------------------------------------------------------------------------------------------------------------------- +function redtask.mt:__call(...) + return redtask.new(...) +end + +return setmetatable(redtask, redtask.mt) diff --git a/awesome/.config/awesome/redflat/gauge/task/ruby.lua b/awesome/.config/awesome/redflat/gauge/task/ruby.lua new file mode 100644 index 0000000..6727649 --- /dev/null +++ b/awesome/.config/awesome/redflat/gauge/task/ruby.lua @@ -0,0 +1,137 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat tag widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Custom widget to display tag info +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local math = math + +local wibox = require("wibox") +local beautiful = require("beautiful") +local color = require("gears.color") + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local rubytask = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = 80, + font = { font = "Sans", size = 16, face = 0, slant = 0 }, + text_shift = 26, + point = { size = 4, space = 3, gap = 3 }, + underline = { height = 20, thickness = 4, gap = 36, dh = 4 }, + color = { main = "#b1222b", gray = "#575757", icon = "#a0a0a0", urgent = "#32882d" } + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "gauge.task.ruby") or {}) +end + + +-- Create a new tag widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function rubytask.new(style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + -- updating values + local data = { + state = { text = "TEXT" }, + width = style.width or nil + } + + -- Create custom widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + + -- User functions + ------------------------------------------------------------ + function widg:set_state(state) + data.state = state + self:emit_signal("widget::redraw_needed") + end + + function widg:set_width(width) + data.width = width + self:emit_signal("widget::redraw_needed") + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + if data.width then + return math.min(width, data.width), height + else + return width, height + end + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width) + local n = #data.state.list + + -- text + cr:set_source(color( + data.state.active and style.color.main + or data.state.minimized and style.color.gray + or style.color.icon + )) + redutil.cairo.set_font(cr, style.font) + redutil.cairo.textcentre.horizontal(cr, { width / 2, style.text_shift }, data.state.text) + + -- underline + cr:set_source(color( + data.state.focus and style.color.main + or data.state.minimized and style.color.gray + or style.color.icon + )) + + cr:move_to(0, style.underline.gap) + cr:rel_line_to(width, 0) + cr:rel_line_to(0, -style.underline.height) + cr:rel_line_to(-style.underline.thickness, style.underline.dh) + cr:rel_line_to(0, style.underline.height - style.underline.dh - style.underline.thickness) + cr:rel_line_to(2 * style.underline.thickness - width, 0) + cr:rel_line_to(0, style.underline.thickness + style.underline.dh - style.underline.height) + cr:rel_line_to(-style.underline.thickness, - style.underline.dh) + cr:close_path(-style.underline.thickness, 0) + cr:fill() + + -- clients counter + local x = math.floor((width - style.point.size * n - style.point.space * (n - 1)) / 2) + local l = style.point.size + style.point.space + + for i = 1, n do + cr:set_source(color( + data.state.list[i].focus and style.color.main or + data.state.list[i].urgent and style.color.urgent or + data.state.list[i].minimized and style.color.gray or + style.color.icon + )) + cr:rectangle(x + (i - 1) * l, style.point.gap, style.point.size, style.point.size) + cr:fill() + end + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call rubytask module as function +----------------------------------------------------------------------------------------------------------------------- +function rubytask.mt:__call(...) + return rubytask.new(...) +end + +return setmetatable(rubytask, rubytask.mt) diff --git a/awesome/.config/awesome/redflat/init.lua b/awesome/.config/awesome/redflat/init.lua new file mode 100644 index 0000000..77cc904 --- /dev/null +++ b/awesome/.config/awesome/redflat/init.lua @@ -0,0 +1,12 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable + +return setmetatable( + { _NAME = "redflat" }, + { __index = function(table, key) + local module = rawget(table, key) + return module or require(table._NAME .. '.' .. key) + end } +) diff --git a/awesome/.config/awesome/redflat/layout/common.lua b/awesome/.config/awesome/redflat/layout/common.lua new file mode 100644 index 0000000..b447869 --- /dev/null +++ b/awesome/.config/awesome/redflat/layout/common.lua @@ -0,0 +1,371 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat layout shared functions -- +----------------------------------------------------------------------------------------------------------------------- +-- Hotkeys for layout manipulation +-- Handlers adapted for work with redflat navigator widget +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local awful = require("awful") +local navigator = require("redflat.service.navigator") + +local ipairs = ipairs + +local alayout = awful.layout +local redutil = require("redflat.util") +local redtip = require("redflat.float.hotkeys") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local common = { handler = {}, last = {}, tips = {}, keys = {}, mouse = {} } + +common.wfactstep = 0.05 + +-- default keys +common.keys.base = { + { + { "Mod4" }, "c", function() common.action.kill() end, + { description = "Kill application", group = "Action" } + }, + { + {}, "Escape", function() common.action.exit() end, + { description = "Exit navigation mode", group = "Action" } + }, + { + { "Mod4" }, "Escape", function() common.action.exit() end, + {} -- hidden key + }, + { + { "Mod4" }, "Super_L", function() common.action.exit() end, + { description = "Exit navigation mode", group = "Action" } + }, + { + { "Mod4" }, "F1", function() redtip:show() end, + { description = "Show hotkeys helper", group = "Action" } + }, +} + +common.keys.swap = { + { + { "Mod4" }, "Up", function() awful.client.swap.bydirection("up") end, + { description = "Move application up", group = "Movement" } + }, + { + { "Mod4" }, "Down", function() awful.client.swap.bydirection("down") end, + { description = "Move application down", group = "Movement" } + }, + { + { "Mod4" }, "Left", function() awful.client.swap.bydirection("left") end, + { description = "Move application left", group = "Movement" } + }, + { + { "Mod4" }, "Right", function() awful.client.swap.bydirection("right") end, + { description = "Move application right", group = "Movement" } + }, +} + +common.keys.tile = { + { + { "Mod4" }, "l", function () awful.tag.incmwfact( common.wfactstep) end, + { description = "Increase master width factor", group = "Layout" } + }, + { + { "Mod4" }, "h", function () awful.tag.incmwfact(-common.wfactstep) end, + { description = "Decrease master width factor", group = "Layout" } + }, + { + { "Mod4", "Shift" }, "h", function () awful.tag.incnmaster( 1, nil, true) end, + { description = "Increase the number of master clients", group = "Layout" } + }, + { + { "Mod4", "Shift" }, "l", function () awful.tag.incnmaster(-1, nil, true) end, + { description = "Decrease the number of master clients", group = "Layout" } + }, + { + { "Mod4", "Control" }, "h", function () awful.tag.incncol( 1, nil, true) end, + { description = "Increase the number of columns", group = "Layout" } + }, + { + { "Mod4", "Control" }, "l", function () awful.tag.incncol(-1, nil, true) end, + { description = "Decrease the number of columns", group = "Layout" } + }, +} + +common.keys.corner = { + { + { "Mod4" }, "l", function () awful.tag.incmwfact( common.wfactstep) end, + { description = "Increase master width factor", group = "Layout" } + }, + { + { "Mod4" }, "h", function () awful.tag.incmwfact(-common.wfactstep) end, + { description = "Decrease master width factor", group = "Layout" } + }, + { + { "Mod4", "Shift" }, "h", function () awful.tag.incnmaster( 1, nil, true) end, + { description = "Increase the number of master clients", group = "Layout" } + }, + { + { "Mod4", "Shift" }, "l", function () awful.tag.incnmaster(-1, nil, true) end, + { description = "Decrease the number of master clients", group = "Layout" } + }, +} + +common.keys.magnifier = { + { + { "Mod4" }, "l", function () awful.tag.incmwfact( common.wfactstep) end, + { description = "Increase master width factor", group = "Layout" } + }, + { + { "Mod4" }, "h", function () awful.tag.incmwfact(-common.wfactstep) end, + { description = "Decrease master width factor", group = "Layout" } + }, + { + { "Mod4" }, "g", function () awful.client.setmaster(client.focus) end, + { description = "Set focused client as master", group = "Movement" } + }, +} + +-- TODO: set real keyset from navigator theme +common.keys._fake = { + { + { "Mod4" }, "N1 N2", nil, + { + description = "Swap clients by key", group = "Movement", + keyset = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" } + } + }, + { + { "Mod4" }, "N1 N1", nil, + { + description = "Focus client by key", group = "Action", + keyset = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" } + } + }, +} + +-- Common handler actions +----------------------------------------------------------------------------------------------------------------------- +common.action = {} + +function common.action.exit() + navigator:close() + common.last = {} +end + +function common.action.kill() + client.focus:kill() + navigator:restart() + common.last.key = nil +end + +-- Keys setup +----------------------------------------------------------------------------------------------------------------------- + +-- Hotkey tips update functions +-------------------------------------------------------------------------------- +common.updates = {} + +local function build_base_tip() + return awful.util.table.join(common.keys.swap, common.keys.base, common.keys._fake) +end + +local function build_tile_tip() + return awful.util.table.join(common.keys.swap, common.keys.tile, common.keys.base, common.keys._fake) +end + +local function build_corner_tip() + return awful.util.table.join(common.keys.swap, common.keys.corner, common.keys.base, common.keys._fake) +end + +local function build_magnifier_tip() + return awful.util.table.join(common.keys.magnifier, common.keys.base, common.keys._fake) +end + +local function set_corner_tip() + common.tips[alayout.suit.corner.nw] = build_corner_tip() + common.tips[alayout.suit.corner.ne] = build_corner_tip() + common.tips[alayout.suit.corner.sw] = build_corner_tip() + common.tips[alayout.suit.corner.se] = build_corner_tip() +end + +local function set_tile_tip() + common.tips[alayout.suit.tile] = build_tile_tip() + common.tips[alayout.suit.tile.right] = build_tile_tip() + common.tips[alayout.suit.tile.left] = build_tile_tip() + common.tips[alayout.suit.tile.top] = build_tile_tip() + common.tips[alayout.suit.tile.bottom] = build_tile_tip() +end + +common.updates.swap = function() + common.tips[alayout.suit.fair] = build_base_tip() + common.tips[alayout.suit.spiral] = build_base_tip() + common.tips[alayout.suit.spiral.dwindle] = build_base_tip() + set_tile_tip() + set_corner_tip() +end + +common.updates.base = function() + common.tips[alayout.suit.fair] = build_base_tip() + common.tips[alayout.suit.spiral] = build_base_tip() + common.tips[alayout.suit.spiral.dwindle] = build_base_tip() + common.tips[alayout.suit.magnifier] = build_magnifier_tip() + set_tile_tip() + set_corner_tip() +end + +common.updates.magnifier = function() + common.tips[alayout.suit.magnifier] = build_magnifier_tip() +end + +common.updates.tile = function() + set_tile_tip() +end + +common.updates.corner = function() + set_corner_tip() +end + +-- Keys setup function +-------------------------------------------------------------------------------- +function common:set_keys(keys, layout) + if keys then common.keys[layout] = keys end -- update keys + if self.updates[layout] then self.updates[layout]() end -- update tips +end + +-- Shared keyboard handlers +----------------------------------------------------------------------------------------------------------------------- +common.grabbers = {} + +-- Base grabbers +-------------------------------------------------------------------------------- +common.grabbers.base = function(mod, key) + for _, k in ipairs(common.keys.base) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return true end + end + + -- if numkey pressed + local index = awful.util.table.hasitem(navigator.style.num, key) + + -- swap or focus client + if index then + if navigator.data[index] and awful.util.table.hasitem(navigator.cls, navigator.data[index].client) then + if common.last.key then + if common.last.key == index then + client.focus = navigator.data[index].client + client.focus:raise() + else + redutil.client.swap(navigator.data[common.last.key].client, navigator.data[index].client) + end + common.last.key = nil + else + common.last.key = index + end + + return true + end + end +end + +common.grabbers.swap = function(mod, key) + for _, k in ipairs(common.keys.swap) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return true end + end +end + +common.grabbers.tile = function(mod, key) + for _, k in ipairs(common.keys.tile) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return true end + end +end + +common.grabbers.corner = function(mod, key) + for _, k in ipairs(common.keys.corner) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return true end + end +end + +common.grabbers.magnifier = function(mod, key) + for _, k in ipairs(common.keys.magnifier) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return true end + end +end + +-- Grabbers for awful layouts +-------------------------------------------------------------------------------- +local function fair_handler(mod, key, event) + if event == "press" then return end + if common.grabbers.swap(mod, key, event) then return end + if common.grabbers.base(mod, key, event) then return end +end + +local function magnifier_handler(mod, key, event) + if event == "press" then return end + if common.grabbers.magnifier(mod, key, event) then return end + if common.grabbers.base(mod, key, event) then return end +end + +local function tile_handler(mod, key, event) + if event == "press" then return end + if common.grabbers.tile(mod, key, event) then return end + if common.grabbers.swap(mod, key, event) then return end + if common.grabbers.base(mod, key, event) then return end +end + +local function corner_handler(mod, key, event) + if event == "press" then return end + if common.grabbers.corner(mod, key, event) then return end + if common.grabbers.swap(mod, key, event) then return end + if common.grabbers.base(mod, key, event) then return end +end + +-- Handlers table +----------------------------------------------------------------------------------------------------------------------- +common.handler[alayout.suit.fair] = fair_handler +common.handler[alayout.suit.spiral] = fair_handler +common.handler[alayout.suit.magnifier] = magnifier_handler +common.handler[alayout.suit.tile] = tile_handler +common.handler[alayout.suit.tile.right] = tile_handler +common.handler[alayout.suit.tile.left] = tile_handler +common.handler[alayout.suit.tile.top] = tile_handler +common.handler[alayout.suit.tile.bottom] = tile_handler +common.handler[alayout.suit.corner.nw] = corner_handler +common.handler[alayout.suit.corner.ne] = corner_handler +common.handler[alayout.suit.corner.se] = corner_handler +common.handler[alayout.suit.corner.sw] = corner_handler + +common.handler[alayout.suit.spiral.dwindle] = fair_handler + +-- tip dirty setup +common:set_keys(nil, "base") + + +-- Slightly changed awful mouse move handler +----------------------------------------------------------------------------------------------------------------------- +function common.mouse.move(c, context, hints) + -- Quit if it isn't a mouse.move on a tiled layout, that's handled elsewhere (WHERE?) + if c.floating then return end + if context ~= "mouse.move" then return end + + -- move to screen with mouse + if mouse.screen ~= c.screen then c.screen = mouse.screen end + + -- check if cutstom layout hadler availible + local l = c.screen.selected_tag and c.screen.selected_tag.layout or nil + if l == awful.layout.suit.floating then return end + + if l and l.move_handler then + l.move_handler(c, context, hints) + return + end + + -- general handler for tile layouts + local c_u_m = mouse.current_client + if c_u_m and not c_u_m.floating then + if c_u_m ~= c then c:swap(c_u_m) end + end +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return common diff --git a/awesome/.config/awesome/redflat/layout/grid.lua b/awesome/.config/awesome/redflat/layout/grid.lua new file mode 100644 index 0000000..60fb226 --- /dev/null +++ b/awesome/.config/awesome/redflat/layout/grid.lua @@ -0,0 +1,480 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat grid layout -- +----------------------------------------------------------------------------------------------------------------------- +-- Floating layout with discrete geometry +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local beautiful = require("beautiful") + +local ipairs = ipairs +local pairs = pairs +local math = math +local unpack = unpack or table.unpack + +local awful = require("awful") +local common = require("redflat.layout.common") +local redutil = require("redflat.util") + +local hasitem = awful.util.table.hasitem + + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local grid = { data = {} } +grid.name = "grid" + +-- default keys +grid.keys = {} +grid.keys.move = { + { + { "Mod4" }, "Up", function() grid.move_to("up") end, + { description = "Move window up", group = "Movement" } + }, + { + { "Mod4" }, "Down", function() grid.move_to("down") end, + { description = "Move window down", group = "Movement" } + }, + { + { "Mod4" }, "Left", function() grid.move_to("left") end, + { description = "Move window left", group = "Movement" } + }, + { + { "Mod4" }, "Right", function() grid.move_to("right") end, + { description = "Move window right", group = "Movement" } + }, + { + { "Mod4", "Control" }, "Up", function() grid.move_to("up", true) end, + { description = "Move window up by bound", group = "Movement" } + }, + { + { "Mod4", "Control" }, "Down", function() grid.move_to("down", true) end, + { description = "Move window down by bound", group = "Movement" } + }, + { + { "Mod4", "Control" }, "Left", function() grid.move_to("left", true) end, + { description = "Move window left by bound", group = "Movement" } + }, + { + { "Mod4", "Control" }, "Right", function() grid.move_to("right", true) end, + { description = "Move window right by bound", group = "Movement" } + }, +} + +grid.keys.resize = { + { + { "Mod4" }, "k", function() grid.resize_to("up") end, + { description = "Inrease window size to the up", group = "Resize" } + }, + { + { "Mod4" }, "j", function() grid.resize_to("down") end, + { description = "Inrease window size to the down", group = "Resize" } + }, + { + { "Mod4" }, "h", function() grid.resize_to("left") end, + { description = "Inrease window size to the left", group = "Resize" } + }, + { + { "Mod4" }, "l", function() grid.resize_to("right") end, + { description = "Inrease window size to the right", group = "Resize" } + }, + { + { "Mod4", "Shift" }, "k", function() grid.resize_to("up", nil, true) end, + { description = "Decrease window size from the up", group = "Resize" } + }, + { + { "Mod4", "Shift" }, "j", function() grid.resize_to("down", nil, true) end, + { description = "Decrease window size from the down", group = "Resize" } + }, + { + { "Mod4", "Shift" }, "h", function() grid.resize_to("left", nil, true) end, + { description = "Decrease window size from the left", group = "Resize" } + }, + { + { "Mod4", "Shift" }, "l", function() grid.resize_to("right", nil, true) end, + { description = "Decrease window size from the right", group = "Resize" } + }, + { + { "Mod4", "Control" }, "k", function() grid.resize_to("up", true) end, + { description = "Increase window size to the up by bound", group = "Resize" } + }, + { + { "Mod4", "Control" }, "j", function() grid.resize_to("down", true) end, + { description = "Increase window size to the down by bound", group = "Resize" } + }, + { + { "Mod4", "Control" }, "h", function() grid.resize_to("left", true) end, + { description = "Increase window size to the left by bound", group = "Resize" } + }, + { + { "Mod4", "Control" }, "l", function() grid.resize_to("right", true) end, + { description = "Increase window size to the right by bound", group = "Resize" } + }, + { + { "Mod4", "Control", "Shift" }, "k", function() grid.resize_to("up", true, true) end, + { description = "Decrease window size from the up by bound ", group = "Resize" } + }, + { + { "Mod4", "Control", "Shift" }, "j", function() grid.resize_to("down", true, true) end, + { description = "Decrease window size from the down by bound ", group = "Resize" } + }, + { + { "Mod4", "Control", "Shift" }, "h", function() grid.resize_to("left", true, true) end, + { description = "Decrease window size from the left by bound ", group = "Resize" } + }, + { + { "Mod4", "Control", "Shift" }, "l", function() grid.resize_to("right", true, true) end, + { description = "Decrease window size from the right by bound ", group = "Resize" } + }, +} + +grid.keys.all = awful.util.table.join(grid.keys.move, grid.keys.resize) + + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function compare(a ,b) return a < b end + +-- Find all rails for given client +------------------------------------------------------------ +local function get_rail(c) + local wa = screen[c.screen].workarea + local cls = awful.client.visible(c.screen) + + local rail = { x = { wa.x, wa.x + wa.width }, y = { wa.y, wa.y + wa.height } } + table.remove(cls, hasitem(cls, c)) + + for _, v in ipairs(cls) do + local lg = redutil.client.fullgeometry(v) + local xr = lg.x + lg.width + local yb = lg.y + lg.height + + if not hasitem(rail.x, lg.x) then table.insert(rail.x, lg.x) end + if not hasitem(rail.x, xr) then table.insert(rail.x, xr) end + if not hasitem(rail.y, lg.y) then table.insert(rail.y, lg.y) end + if not hasitem(rail.y, yb) then table.insert(rail.y, yb) end + end + + table.sort(rail.x, compare) + table.sort(rail.y, compare) + + return rail +end + +local function update_rail(c) grid.data.rail = get_rail(c) end + +-- Calculate cell geometry +------------------------------------------------------------ +local function make_cell(wa, cellnum) + local cell = { + x = wa.width / cellnum.x, + y = wa.height / cellnum.y + } + + -- adapt cell table to work with geometry prop + cell.width = cell.x + cell.height = cell.y + + return cell +end + +-- Grid rounding +------------------------------------------------------------ +local function round(a, n) + return n * math.floor((a + n / 2) / n) +end + +-- Fit client into grid +------------------------------------------------------------ +local function fit_cell(g, cell) + local ng = {} + + for k, v in pairs(g) do + ng[k] = math.ceil(round(v, cell[k])) + end + + return ng +end + +-- Check geometry difference +------------------------------------------------------------ +local function is_diff(g1, g2, cell) + for k, v in pairs(g1) do + if math.abs(g2[k] - v) >= cell[k] then return true end + end + + return false +end + +-- Move client +----------------------------------------------------------------------------------------------------------------------- +function grid.move_to(dir, is_rail, k) + local ng = {} + local data = grid.data + local c = client.focus + + if not c then return end + if data.last ~= c then + data.last = c + update_rail(c) + end + + local g = redutil.client.fullgeometry(c) + k = k or 1 + + if dir == "left" then + if is_rail then + for i = #data.rail.x, 1, - 1 do + if data.rail.x[i] < g.x then + ng.x = data.rail.x[i] + break + end + end + else + ng.x = g.x - data.cell.x * k + end + elseif dir == "right" then + if is_rail then + for i = 1, #data.rail.x do + if data.rail.x[i] > g.x + g.width + 1 then + ng.x = data.rail.x[i] - g.width + break + end + end + else + ng.x = g.x + data.cell.x * k + end + elseif dir == "up" then + if is_rail then + for i = #data.rail.y, 1, - 1 do + if data.rail.y[i] < g.y then + ng.y = data.rail.y[i] + break + end + end + else + ng.y = g.y - data.cell.y * k + end + elseif dir == "down" then + if is_rail then + for i = 1, #data.rail.y do + if data.rail.y[i] > g.y + g.height + 1 then + ng.y = data.rail.y[i] - g.height + break + end + end + else + ng.y = g.y + data.cell.y * k + end + end + + redutil.client.fullgeometry(c, ng) +end + +-- Resize client +----------------------------------------------------------------------------------------------------------------------- +grid.resize_to = function(dir, is_rail, is_reverse) + local ng = {} + local c = client.focus + local data = grid.data + + if not c then return end + if data.last ~= c then + data.last = c + update_rail(c) + end + + local g = redutil.client.fullgeometry(c) + local sign = is_reverse and -1 or 1 + + if dir == "up" then + if is_rail then + -- select loop direction (from min to max or from max to min) + local f, l, s = unpack(is_reverse and { 1, #data.rail.y, 1 } or { #data.rail.y, 1, - 1 }) + for i = f, l, s do + if is_reverse and data.rail.y[i] > g.y or not is_reverse and data.rail.y[i] < g.y then + ng = { y = data.rail.y[i], height = g.height + g.y - data.rail.y[i] } + break + end + end + else + ng = { y = g.y - sign * data.cell.y, height = g.height + sign * data.cell.y } + end + elseif dir == "down" then + if is_rail then + local f, l, s = unpack(is_reverse and { #data.rail.y, 1, - 1 } or { 1, #data.rail.y, 1 }) + for i = f, l, s do + if is_reverse and data.rail.y[i] < (g.y + g.height - 1) + or not is_reverse and data.rail.y[i] > (g.y + g.height + 1) then + ng = { height = data.rail.y[i] - g.y } + break + end + end + else + ng = { height = g.height + sign * data.cell.y } + end + elseif dir == "left" then + if is_rail then + local f, l, s = unpack(is_reverse and { 1, #data.rail.x, 1 } or { #data.rail.x, 1, - 1 }) + for i = f, l, s do + if is_reverse and data.rail.x[i] > g.x or not is_reverse and data.rail.x[i] < g.x then + ng = { x = data.rail.x[i], width = g.width + g.x - data.rail.x[i] } + break + end + end + else + ng = { x = g.x - sign * data.cell.x, width = g.width + sign * data.cell.x } + end + elseif dir == "right" then + if is_rail then + local f, l, s = unpack(is_reverse and { #data.rail.x, 1, - 1 } or { 1, #data.rail.x, 1 }) + for i = f, l, s do + if is_reverse and data.rail.x[i] < (g.x + g.width) + or not is_reverse and data.rail.x[i] > (g.x + g.width + 1) then + ng = { width = data.rail.x[i] - g.x } + break + end + end + else + ng = { width = g.width + sign * data.cell.x } + end + end + + redutil.client.fullgeometry(c, ng) +end + +-- Keygrabber +----------------------------------------------------------------------------------------------------------------------- +grid.maingrabber = function(mod, key) + for _, k in ipairs(grid.keys.all) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return true end + end +end + +grid.key_handler = function (mod, key, event) + if event == "press" then return end + if grid.maingrabber(mod, key, event) then return end + if common.grabbers.base(mod, key, event) then return end +end + + +-- Tile function +----------------------------------------------------------------------------------------------------------------------- +function grid.arrange(p) + + -- theme vars + local cellnum = beautiful.cellnum or { x = 100, y = 60 } + + -- aliases + local wa = p.workarea + local cls = p.clients + + -- calculate cell + -- fix useless gap correction? + grid.data.cell = make_cell({ width = wa.width + 2 * p.useless_gap, height = wa.height + 2 * p.useless_gap }, cellnum) + + -- nothing to tile here + if #cls == 0 then return end + + -- tile + for _, c in ipairs(cls) do + local g = redutil.client.fullgeometry(c) + + g = fit_cell(g, grid.data.cell) + redutil.client.fullgeometry(c, g) + end +end + + +-- Mouse moving function +----------------------------------------------------------------------------------------------------------------------- +function grid.move_handler(c, _, hints) + local g = redutil.client.fullgeometry(c) + local hg = { x = hints.x, y = hints.y, width = g.width, height = g.height } + if is_diff(hg, g, grid.data.cell) then + redutil.client.fullgeometry(c, fit_cell(hg, grid.data.cell)) + end +end + + +-- Mouse resizing function +----------------------------------------------------------------------------------------------------------------------- +function grid.mouse_resize_handler(c, corner) + local g = redutil.client.fullgeometry(c) + local cg = g + + -- set_mouse_on_corner(g, corner) + + mousegrabber.run( + function (_mouse) + for _, v in ipairs(_mouse.buttons) do + if v then + local ng + if corner == "bottom_right" then + ng = { + width = _mouse.x - g.x, + height = _mouse.y - g.y + } + elseif corner == "bottom_left" then + ng = { + x = _mouse.x, + width = (g.x + g.width) - _mouse.x, + height = _mouse.y - g.y + } + elseif corner == "top_left" then + ng = { + x = _mouse.x, + y = _mouse.y, + width = (g.x + g.width) - _mouse.x, + height = (g.y + g.height) - _mouse.y + } + else + ng = { + y = _mouse.y, + width = _mouse.x - g.x, + height = (g.y + g.height) - _mouse.y + } + end + + if ng.width <= 0 then ng.width = nil end + if ng.height <= 0 then ng.height = nil end + -- if c.maximized_horizontal then ng.width = g.width ng.x = g.x end + -- if c.maximized_vertical then ng.height = g.height ng.y = g.y end + + if is_diff(ng, cg, grid.data.cell) then + cg = redutil.client.fullgeometry(c, fit_cell(ng, grid.data.cell)) + end + + return true + end + end + return false + end, + corner .. "_corner" + ) +end + +-- Redflat navigator support functions +----------------------------------------------------------------------------------------------------------------------- +function grid:set_keys(keys, layout) + layout = layout or "all" + if keys then + self.keys[layout] = keys + if layout ~= "all" then grid.keys.all = awful.util.table.join(grid.keys.move, grid.keys.resize) end + end + + self.tip = awful.util.table.join(self.keys.all, common.keys.base) +end + +function grid.startup() + if not grid.tip then grid:set_keys() end +end + +function grid.cleanup() + grid.data.last = nil +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return grid diff --git a/awesome/.config/awesome/redflat/layout/init.lua b/awesome/.config/awesome/redflat/layout/init.lua new file mode 100644 index 0000000..e7d7975 --- /dev/null +++ b/awesome/.config/awesome/redflat/layout/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.layout" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/layout/map.lua b/awesome/.config/awesome/redflat/layout/map.lua new file mode 100644 index 0000000..e430c81 --- /dev/null +++ b/awesome/.config/awesome/redflat/layout/map.lua @@ -0,0 +1,688 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat map layout -- +----------------------------------------------------------------------------------------------------------------------- +-- Tiling with user defined geometry +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local ipairs = ipairs +local pairs = pairs +local math = math +local unpack = unpack or table.unpack + +local awful = require("awful") +local timer = require("gears.timer") + +local redflat = require("redflat") +local redutil = require("redflat.util") +local common = require("redflat.layout.common") +local rednotify = require("redflat.float.notify") + +local hasitem = awful.util.table.hasitem + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local map = { data = setmetatable({}, { __mode = "k" }), scheme = setmetatable({}, { __mode = "k" }), keys = {} } +map.name = "usermap" +map.notification = true +map.notification_style = {} + +local hitimer +map.hilight_timeout = 0.2 + + +-- default keys +map.keys.layout = { + { + { "Mod4" }, "s", function() map.swap_group() end, + { description = "Change placement direction for group", group = "Layout" } + }, + { + { "Mod4" }, "v", function() map.new_group(true) end, + { description = "Create new vertical group", group = "Layout" } + }, + { + { "Mod4" }, "b", function() map.new_group(false) end, + { description = "Create new horizontal group", group = "Layout" } + }, + { + { "Mod4", "Control" }, "v", function() map.insert_group(true) end, + { description = "Insert new vertical group before active", group = "Layout" } + }, + { + { "Mod4", "Control" }, "b", function() map.insert_group(false) end, + { description = "Insert new horizontal group before active", group = "Layout" } + }, + { + { "Mod4" }, "d", function() map.delete_group() end, + { description = "Destroy group", group = "Layout" } + }, + { + { "Mod4", "Control" }, "d", function() map.clean_groups() end, + { description = "Destroy all empty groups", group = "Layout" } + }, + { + { "Mod4" }, "a", function() map.set_active() end, + { description = "Set active group", group = "Layout" } + }, + { + { "Mod4" }, "f", function() map.move_to_active() end, + { description = "Move focused client to active group", group = "Layout" } + }, + { + { "Mod4", "Control" }, "a", function() map.hilight_active() end, + { description = "Hilight active group", group = "Layout" } + }, + { + { "Mod4" }, ".", function() map.switch_active(1) end, + { description = "Activate next group", group = "Layout" } + }, + { + { "Mod4" }, ",", function() map.switch_active(-1) end, + { description = "Activate previous group", group = "Layout" } + }, + { + { "Mod4" }, "]", function() map.move_group(1) end, + { description = "Move active group to the top", group = "Layout" } + }, + { + { "Mod4" }, "[", function() map.move_group(-1) end, + { description = "Move active group to the bottom", group = "Layout" } + }, + { + { "Mod4" }, "r", function() map.reset_tree() end, + { description = "Reset layout structure", group = "Layout" } + }, +} + +map.keys.resize = { + { + { "Mod4" }, "h", function() map.incfactor(nil, 0.1, false) end, + { description = "Increase window horizontal size factor", group = "Resize" } + }, + { + { "Mod4" }, "l", function() map.incfactor(nil, -0.1, false) end, + { description = "Decrease window horizontal size factor", group = "Resize" } + }, + { + { "Mod4" }, "k", function() map.incfactor(nil, 0.1, true) end, + { description = "Increase window vertical size factor", group = "Resize" } + }, + { + { "Mod4" }, "j", function() map.incfactor(nil, -0.1, true) end, + { description = "Decrease window vertical size factor", group = "Resize" } + }, + { + { "Mod4", "Control" }, "h", function() map.incfactor(nil, 0.1, false, true) end, + { description = "Increase group horizontal size factor", group = "Resize" } + }, + { + { "Mod4", "Control" }, "l", function() map.incfactor(nil, -0.1, false, true) end, + { description = "Decrease group horizontal size factor", group = "Resize" } + }, + { + { "Mod4", "Control" }, "k", function() map.incfactor(nil, 0.1, true, true) end, + { description = "Increase group vertical size factor", group = "Resize" } + }, + { + { "Mod4", "Control" }, "j", function() map.incfactor(nil, -0.1, true, true) end, + { description = "Decrease group vertical size factor", group = "Resize" } + }, +} + + +map.keys.all = awful.util.table.join(map.keys.layout, map.keys.resize) + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Layout action notifications +-------------------------------------------------------------------------------- +local function notify(txt) + if map.notification then rednotify:show(redutil.table.merge({ text = txt }, map.notification_style)) end +end + +-- Calculate geometry for single client or group +-------------------------------------------------------------------------------- +local function cut_geometry(wa, is_vertical, size) + if is_vertical then + local g = { x = wa.x, y = wa.y, width = wa.width, height = size } + wa.y = wa.y + size + return g + else + local g = { x = wa.x, y = wa.y, width = size, height = wa.height } + wa.x = wa.x + size + return g + end +end + +-- Build container for single client or group +-------------------------------------------------------------------------------- +function map.construct_itempack(cls, wa, is_vertical, parent) + local pack = { items = {}, wa = wa, cls = { unpack(cls) }, is_vertical = is_vertical, parent = parent } + + -- Create pack of items with base properties + ------------------------------------------------------------ + for i, c in ipairs(cls) do + pack.items[i] = { client = c, child = nil, factor = 1 } + end + + -- Update pack clients + ------------------------------------------------------------ + function pack:set_cls(clist) + local current = { unpack(clist) } + + -- update existing items, remove overage if need + for i, item in ipairs(self.items) do + if not item.child then + if #current > 0 then + self.items[i].client = current[1] + table.remove(current, 1) + else + self.items[i] = nil + end + end + end + + -- create additional items if need + for _, c in ipairs(current) do + self.items[#self.items + 1] = { client = c, child = nil, factor = 1 } + end + end + + -- Get current pack clients + ------------------------------------------------------------ + function pack:get_cls() + local clist = {} + for _, item in ipairs(self.items) do if not item.child then clist[#clist + 1] = item.client end end + return clist + end + + -- Update pack geometry + ------------------------------------------------------------ + function pack:set_wa(workarea) + self.wa = workarea + end + + -- Get number of items reserved for single client only + ------------------------------------------------------------ + function pack:get_places() + local n = 0 + for _, item in ipairs(self.items) do if not item.child then n = n + 1 end end + return n + end + + -- Get child index + ------------------------------------------------------------ + function pack:get_child_id(pack_) + for i, item in ipairs(self.items) do + if item.child == pack_ then return i end + end + end + + -- Check if container with inheritors keep any real client + ------------------------------------------------------------ + function pack:is_filled() + local filled = false + for _, item in ipairs(self.items) do + if not item.child then + return true + else + filled = filled or item.child:is_filled() + end + end + return filled + end + + -- Increase window size factor for item with index + ------------------------------------------------------------ + function pack:incfacror(index, df, vertical) + if vertical == self.is_vertical then + self.items[index].factor = math.max(self.items[index].factor + df, 0.1) + elseif self.parent then + local pi = self.parent:get_child_id(self) + self.parent:incfacror(pi, df, vertical) + end + end + + -- Recalculate geometry for every item in container + ------------------------------------------------------------ + function pack:rebuild() + -- vars + local geometries = {} + local weight = 0 + local area = awful.util.table.clone(self.wa) + local direction = self.is_vertical and "height" or "width" + + -- check factor norming + for _, item in ipairs(self.items) do + if not item.child or item.child:is_filled() then weight = weight + item.factor end + end + if weight == 0 then return geometries end + + -- geomentry calculation + for i, item in ipairs(self.items) do + if not item.child or item.child:is_filled() then + local size = self.wa[direction] / weight * item.factor + local g = cut_geometry(area, self.is_vertical, size, i) + if item.child then + item.child:set_wa(g) + else + geometries[item.client] = g + end + end + end + + return geometries + end + + return pack +end + +-- Build layout tree +-------------------------------------------------------------------------------- +local function construct_tree(wa, t) + + -- Initial structure on creation + ------------------------------------------------------------ + local tree = map.scheme[t] and map.scheme[t].construct(wa) or map.base_construct(wa) + + -- Find pack contaner for client + ------------------------------------------------------------ + function tree:get_pack(c) + for _, pack in ipairs(self.set) do + for i, item in ipairs(pack.items) do + if not item.child and c == item.client then return pack, i end + end + end + end + + -- Create new contaner in place of client + ------------------------------------------------------------ + function tree:create_group(c, is_vertical) + local parent, index = self:get_pack(c) + local new_pack = map.construct_itempack({}, {}, is_vertical, parent) + + self.set[#self.set + 1] = new_pack + parent.items[index] = { child = new_pack, factor = 1 } + self.active = #self.set + + awful.client.setslave(c) + notify("New " .. (is_vertical and "vertical" or "horizontal") .. " group") + end + + -- Insert new contaner in before active + ------------------------------------------------------------ + function tree:insert_group(is_vertical) + local pack = self.set[self.active] + local new_pack = map.construct_itempack({}, pack.wa, is_vertical, pack.parent) + + if pack.parent then + for _, item in ipairs(pack.parent.items) do + if item.child == pack then item.child = new_pack; break end + end + end + new_pack.items[1] = { child = pack, factor = 1, client = nil } + + table.insert(self.set, self.active, new_pack) + pack.parent = new_pack + notify("New " .. (is_vertical and "vertical" or "horizontal") .. " group") + end + + -- Destroy the given container + ------------------------------------------------------------ + function tree:delete_group(pack) + pack = pack or self.set[self.active] + local index = hasitem(self.set, pack) + local has_child = pack:get_places() < #pack.items + + -- some containers can't be destroyed + -- root container + if #self.set == 1 then + notify("Cant't destroy last group") + return + end + + -- container with many inheritors + if has_child and #pack.items > 1 then + notify("Cant't destroy group with inheritors") + return + end + + -- disconnect container from parent + if pack.parent then + for i, item in ipairs(pack.parent.items) do + if item.child == pack then + if has_child then + -- container in container case + -- inheritor can be transmit to parent without changing geometry + item.child = pack.items[1].child + else + -- container without inheritors can be safaly destroyed + table.remove(pack.parent.items, i) + end + break + end + end + end + + -- destroy container + table.remove(self.set, index) + if not self.set[self.active] then self.active = #self.set end + notify("Group " .. tostring(index) .. " destroyed") + end + + -- Destroy all empty containers + ------------------------------------------------------------ + function tree:cleanup() + for i = #self.set, 1, -1 do + if #self.set[i].items == 0 then + tree:delete_group(self.set[i]) + elseif #self.set[i].items == 1 and self.set[i].items[1].child then + self.set[i].items[1].child.wa = self.set[i].wa + tree:delete_group(self.set[i]) + end + end + end + + -- Recalculate geometry for whole layout + ------------------------------------------------------------ + function tree:rebuild(clist) + local current = { unpack(clist) } + local geometries = {} + + -- distributing clients among existing contaners + for _, pack in ipairs(self.set) do + local n = pack:get_places() + local chunk = { unpack(current, 1, n) } + current = { unpack(current, n + 1) } + pack:set_cls(chunk) + end + + -- distributing clients among existing contaners + if #current > 0 then + for _, c in ipairs(current) do + if self.autoaim then self.active = self:aim() end + local refill = awful.util.table.join(self.set[self.active]:get_cls(), { c }) + self.set[self.active]:set_cls(refill) + end + -- local refill = awful.util.table.join(self.set[self.active]:get_cls(), current) + -- self.set[self.active]:set_cls(refill) + end + + -- recalculate geomery for every container in tree + for _, pack in ipairs(self.set) do + geometries = awful.util.table.join(geometries, pack:rebuild()) + end + + return geometries + end + + return tree +end + +-- Layout manipulation functions +----------------------------------------------------------------------------------------------------------------------- + +-- Change container placement direction +-------------------------------------------------------------------------------- +function map.swap_group() + local c = client.focus + if not c then return end + + local t = c.screen.selected_tag + local pack = map.data[t]:get_pack(c) + pack.is_vertical = not pack.is_vertical + t:emit_signal("property::layout") +end + + +-- Create new container for client +-------------------------------------------------------------------------------- +function map.new_group(is_vertical) + local c = client.focus + if not c then return end + + local t = c.screen.selected_tag + map.data[t].autoaim = false + map.data[t]:create_group(c, is_vertical) + + if hitimer then return end + + hitimer = timer({ timeout = map.hilight_timeout }) + hitimer:connect_signal("timeout", + function() + redflat.service.navigator.hilight.show(map.data[t].set[map.data[t].active].wa) + hitimer:stop() + hitimer = nil + end + ) + hitimer:start() -- autostart option doesn't work? +end + +-- Destroy active container +-------------------------------------------------------------------------------- +function map.delete_group() + local t = mouse.screen.selected_tag + map.data[t].autoaim = false + map.data[t]:delete_group() + t:emit_signal("property::layout") +end + +-- Check if client exist in layout tree +-------------------------------------------------------------------------------- +function map.check_client(c) + if c.sticky then return true end + for _, t in ipairs(c:tags()) do + for k, _ in pairs(map.data) do if k == t then return true end end + end +end + +-- Remove client from layout tree and change tree structure +-------------------------------------------------------------------------------- +function map.clean_client(c) + for t, _ in pairs(map.data) do + local pack, index = map.data[t]:get_pack(c) + if pack then table.remove(pack.items, index) end + end +end + +-- Destroy all empty containers +-------------------------------------------------------------------------------- +function map.clean_groups() + local t = mouse.screen.selected_tag + map.data[t].autoaim = false + map.data[t]:cleanup() + t:emit_signal("property::layout") +end + +-- Set active container (new client will be allocated to this one) +-------------------------------------------------------------------------------- +function map.set_active(c) + c = c or client.focus + if not c then return end + + local t = c.screen.selected_tag + local pack = map.data[t]:get_pack(c) + if pack then + map.data[t].autoaim = false + map.data[t].active = hasitem(map.data[t].set, pack) + redflat.service.navigator.hilight.show(pack.wa) + notify("Active group index: " .. tostring(map.data[t].active)) + end +end + +-- Hilight active container (navigetor widget feature) +-------------------------------------------------------------------------------- +function map.hilight_active() + local t = mouse.screen.selected_tag + local pack = map.data[t].set[map.data[t].active] + redflat.service.navigator.hilight.show(pack.wa) +end + +-- Switch active container by index +-------------------------------------------------------------------------------- +function map.switch_active(n) + local t = mouse.screen.selected_tag + local na = map.data[t].active + n + if map.data[t].set[na] then + map.data[t].autoaim = false + map.data[t].active = na + --local pack = map.data[t].set[na] + notify("Active group index: " .. tostring(na)) + end + redflat.service.navigator.hilight.show(map.data[t].set[map.data[t].active].wa) +end + +-- Move client to active container +-------------------------------------------------------------------------------- +function map.move_to_active(c) + c = c or client.focus + if not c then return end + + local t = c.screen.selected_tag + local pack, index = map.data[t]:get_pack(c) + if pack then + table.remove(pack.items, index) + awful.client.setslave(c) + end +end + +-- Increase window size factor for client +-------------------------------------------------------------------------------- +function map.incfactor(c, df, is_vertical, on_group) + c = c or client.focus + if not c then return end + + local t = c.screen.selected_tag + local pack, index = map.data[t]:get_pack(c) + if not pack then return end -- fix this? + + if on_group and pack.parent then + index = pack.parent:get_child_id(pack) + pack = pack.parent + end + + if pack then + pack:incfacror(index, df, is_vertical) + t:emit_signal("property::layout") + end +end + +-- Move element inside his container +-------------------------------------------------------------------------------- +function map.move_group(dn) + local t = mouse.screen.selected_tag + local pack = map.data[t].set[map.data[t].active] + + if pack.parent then + map.data[t].autoaim = false + local i = pack.parent:get_child_id(pack) + if pack.parent.items[i + dn] then + pack.parent.items[i], pack.parent.items[i + dn] = pack.parent.items[i + dn], pack.parent.items[i] + t:emit_signal("property::layout") + end + end +end + +-- Insert new group before active +-------------------------------------------------------------------------------- +function map.insert_group(is_vertical) + local t = mouse.screen.selected_tag + map.data[t].autoaim = false + map.data[t]:insert_group(is_vertical) + t:emit_signal("property::layout") +end + +-- Reset layout structure +-------------------------------------------------------------------------------- +function map.reset_tree() + local t = mouse.screen.selected_tag + map.data[t] = nil + t:emit_signal("property::layout") +end + + +-- Base layout scheme +----------------------------------------------------------------------------------------------------------------------- +-- TODO: fix unused arg +function map.base_set_new_pack(cls, wa, _, parent, factor) + local pack = map.construct_itempack(cls, wa, true, parent) + table.insert(parent.items, { child = pack, factor = factor or 1 }) + return pack +end + +map.base_autoaim = true + +function map.base_aim(tree) + if #tree.set[2].items == 0 then return 2 end + local active = #tree.set[3].items > #tree.set[2].items and 2 or 3 + return active +end + +function map.base_construct(wa) + local tree = { set = {}, active = 1, autoaim = map.base_autoaim, aim = map.base_aim } + + tree.set[1] = map.construct_itempack({}, wa, false) + tree.set[2] = map.base_set_new_pack({}, wa, true, tree.set[1]) + tree.set[3] = map.base_set_new_pack({}, wa, true, tree.set[1]) + + return tree +end + +-- Tile function +----------------------------------------------------------------------------------------------------------------------- +function map.arrange(p) + local wa = awful.util.table.clone(p.workarea) + local cls = p.clients + local data = map.data + local t = p.tag or screen[p.screen].selected_tag + + -- nothing to tile here + if #cls == 0 then return end + + -- init layout tree + if not data[t] then data[t] = construct_tree(wa, t) end + + -- tile + p.geometries = data[t]:rebuild(cls) +end + + +-- Keygrabber +----------------------------------------------------------------------------------------------------------------------- +map.maingrabber = function(mod, key) + for _, k in ipairs(map.keys.all) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return true end + end +end + +map.key_handler = function (mod, key, event) + if event == "press" then return end + if map.maingrabber(mod, key) then return end + if common.grabbers.swap(mod, key, event) then return end + if common.grabbers.base(mod, key, event) then return end +end + + +-- Redflat navigator support functions +----------------------------------------------------------------------------------------------------------------------- +function map:set_keys(keys, layout) + layout = layout or "all" + if keys then + self.keys[layout] = keys + if layout ~= "all" then self.keys.all = awful.util.table.join(self.keys.layout, map.keys.resize) end + end + + self.tip = awful.util.table.join(self.keys.all, common.keys.swap, common.keys.base, common.keys._fake) +end + +function map.startup() + if not map.tip then map:set_keys() end +end + + +-- End +----------------------------------------------------------------------------------------------------------------------- +return map diff --git a/awesome/.config/awesome/redflat/menu.lua b/awesome/.config/awesome/redflat/menu.lua new file mode 100644 index 0000000..b8e497b --- /dev/null +++ b/awesome/.config/awesome/redflat/menu.lua @@ -0,0 +1,676 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat menu -- +----------------------------------------------------------------------------------------------------------------------- +-- awful.menu modification +-- Custom widget support added +-- Auto hide option added +-- Right icon support added to default item constructor +-- Icon margin added to default item constructor +-- Auto hotkeys for menu items added + +-- Horizontal mode support removed +-- Add to index and delete item functions removed +-- menu:clients function removed +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ awful.menu v3.5.2 +------ (c) 2008, 2011 Damien Leone, Julien Danjou, dodo +----------------------------------------------------------------------------------------------------------------------- + + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local wibox = require("wibox") +local awful = require("awful") +local beautiful = require("beautiful") +local timer = require("gears.timer") + +local setmetatable = setmetatable +local string = string +local ipairs = ipairs +local pcall = pcall +local print = print +local table = table +local type = type +local unpack = unpack or table.unpack + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") +local redtip = require("redflat.float.hotkeys") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local menu = { mt = {}, action = {}, keys = {} } + +local _fake_context = { dpi = beautiful.xresources.get_dpi() } -- fix this + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_theme() + local style = { + border_width = 2, + screen_gap = 0, + submenu_icon = redutil.base.placeholder({ txt = "▶" }), + height = 20, + width = 200, + font = "Sans 12", + icon_margin = { 0, 0, 0, 0 }, -- left icon margin + ricon_margin = { 0, 0, 0, 0 }, -- right icon margin + nohide = false, + auto_expand = true, + auto_hotkey = false, + svg_scale = { false, false }, + hide_timeout = 0, + select_first = true, + keytip = { geometry = { width = 400 } }, + color = { border = "#575757", text = "#aaaaaa", highlight = "#eeeeee", + main = "#b1222b", wibox = "#202020", + submenu_icon = nil, right_icon = nil, left_icon = nil }, + shape = nil + } + return redutil.table.merge(style, beautiful.menu or {}) +end + + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Check if any menu item connected with given key +-- and run menu item command if found +-------------------------------------------------------------------------------- +local function check_access_key(_menu, key) + local num = awful.util.table.hasitem(_menu.keys, key) + if num then + _menu:item_enter(num) + _menu:exec(num) + end +end + +-- Get the elder parent of submenu +-------------------------------------------------------------------------------- +function menu:get_root() + return self.parent and menu.get_root(self.parent) or self +end + +-- Setup case insensitive underline markup to given character in string +-------------------------------------------------------------------------------- +local function make_u(text, key) + local pos = key and string.find(string.lower(text), key) or nil + + if pos then + local rkey = string.sub(text, pos, pos) + return string.gsub(text, rkey, "" .. rkey .. "", 1) + end + + return text +end + +-- Function to set menu or submenu in position +----------------------------------------------------------------------------------------------------------------------- +local function set_coords(_menu, screen_idx, m_coords) + local s_geometry = redutil.placement.add_gap(screen[screen_idx].workarea, _menu.theme.screen_gap) + + local screen_w = s_geometry.x + s_geometry.width + local screen_h = s_geometry.y + s_geometry.height + + local x, y + local b = _menu.wibox.border_width + local w = _menu.wibox.width + 2 * _menu.wibox.border_width + local h = _menu.wibox.height + 2 * _menu.wibox.border_width + + if _menu.parent then + local pw = _menu.parent.wibox.width + 2 * _menu.parent.theme.border_width + local piy = _menu.parent.wibox.y + _menu.position + _menu.parent.theme.border_width + + y = piy - b + x = _menu.parent.wibox.x + pw + + if y + h > screen_h then y = screen_h - h end + if x + w > screen_w then x = _menu.parent.wibox.x - w end + else + if m_coords == nil then + m_coords = mouse.coords() + m_coords.x = m_coords.x - 1 + m_coords.y = m_coords.y - 1 + end + + y = m_coords.y < s_geometry.y and s_geometry.y or m_coords.y + x = m_coords.x < s_geometry.x and s_geometry.x or m_coords.x + + if y + h > screen_h then y = screen_h - h end + if x + w > screen_w then x = screen_w - w end + end + + _menu.wibox.x = x + _menu.wibox.y = y +end + +-- Menu keygrabber +-- A new instance for every submenu should be used +----------------------------------------------------------------------------------------------------------------------- + +-- Menu functions +-------------------------------------------------------------------------------- +function menu.action.up(_menu, sel) + local sel_new = sel - 1 < 1 and #_menu.items or sel - 1 + _menu:item_enter(sel_new) +end + +function menu.action.down(_menu, sel) + local sel_new = sel + 1 > #_menu.items and 1 or sel + 1 + _menu:item_enter(sel_new) +end + +function menu.action.enter(_menu, sel) + if sel > 0 and _menu.items[sel].child then _menu.items[sel].child:show() end +end + +function menu.action.exec(_menu, sel) + if sel > 0 then _menu:exec(sel, { exec = true }) end +end + +function menu.action.back(_menu) + _menu:hide() +end + +function menu.action.close(_menu) + menu.get_root(_menu):hide() +end + +-- Menu keys +-------------------------------------------------------------------------------- +menu.keys.move = { + { + {}, "Down", menu.action.down, + { description = "Select next item", group = "Navigation" } + }, + { + {}, "Up", menu.action.up, + { description = "Select previous item", group = "Navigation" } + }, + { + {}, "Left", menu.action.back, + { description = "Go back", group = "Navigation" } + }, + { + {}, "Right", menu.action.enter, + { description = "Open submenu", group = "Navigation" } + }, +} + +menu.keys.action = { + { + {}, "Escape", menu.action.close, + { description = "Close menu", group = "Action" } + }, + { + {}, "Return", menu.action.exec, + { description = "Activate item", group = "Action" } + }, + { + { "Mod4" }, "F1", function() redtip:show() end, + { description = "Show hotkeys helper", group = "Action" } + }, +} + +menu.keys.all = awful.util.table.join(menu.keys.move, menu.keys.action) + +-- this one only displayed in hotkeys helper +menu._fake_keys = { + { + {}, "_letter", nil, + { description = "Activate item by key", group = "Action" } + }, +} + +-- Menu keygrabber +-------------------------------------------------------------------------------- +local grabber = function(_menu, mod, key, event) + if event ~= "press" then return end + local sel = _menu.sel or 0 + + for _, k in ipairs(menu.keys.all) do + if redutil.key.match_grabber(k, mod, key) then k[3](_menu, sel); return false end + end + + check_access_key(_menu, key) +end + +-- Execute menu item +----------------------------------------------------------------------------------------------------------------------- +function menu:exec(num) + local item = self.items[num] + + if not item then return end + + local cmd = item.cmd + + if type(cmd) == "table" then + item.child:show() + elseif type(cmd) == "string" then + if not item.theme.nohide then menu.get_root(self):hide() end + awful.spawn(cmd) + elseif type(cmd) == "function" then + if not item.theme.nohide then menu.get_root(self):hide() end + cmd() + end +end + +-- Menu item selection functions +----------------------------------------------------------------------------------------------------------------------- + +-- Select item +-------------------------------------------------------------------------------- +function menu:item_enter(num, opts) + opts = opts or {} + local item = self.items[num] + + if item and self.theme.auto_expand and opts.hover and item.child then + self.items[num].child:show() + end + + if num == nil or self.sel == num or not item then + return + elseif self.sel then + self:item_leave(self.sel) + end + + item._background:set_fg(item.theme.color.highlight) + item._background:set_bg(item.theme.color.main) + if item.icon and item.theme.color.left_icon then item.icon:set_color(item.theme.color.highlight) end + if item.right_icon and + (item.child and item.theme.color.submenu_icon or not item.child and item.theme.color.right_icon) then + item.right_icon:set_color(item.theme.color.highlight) + end + self.sel = num +end + +-- Unselect item +-------------------------------------------------------------------------------- +function menu:item_leave(num) + if not num then return end + + local item = self.items[num] + + if item then + item._background:set_fg(item.theme.color.text) + item._background:set_bg(item.theme.color.wibox) + if item.icon and item.theme.color.left_icon then item.icon:set_color(item.theme.color.left_icon) end + if item.right_icon then + if item.child and item.theme.color.submenu_icon then + -- if there's a child menu, this is a submenu icon + item.right_icon:set_color(item.theme.color.submenu_icon) + elseif item.theme.color.right_icon then + item.right_icon:set_color(item.theme.color.right_icon) + end + end + if item.child then item.child:hide() end + end +end + +-- Menu show/hide functions +----------------------------------------------------------------------------------------------------------------------- + +-- Show a menu popup. +-- @param args.coords Menu position defaulting to mouse.coords() +-------------------------------------------------------------------------------- +function menu:show(args) + args = args or {} + local screen_index = mouse.screen + set_coords(self, screen_index, args.coords) + if self.wibox.visible then return end + + -- show menu + awful.keygrabber.run(self._keygrabber) + self.wibox.visible = true + if self.theme.select_first or self.parent then self:item_enter(1) end + + -- check hidetimer + if self.hidetimer and self.hidetimer.started then self.hidetimer:stop() end + + -- hotkeys helper + -- TODO: optimize code to cache helper (do not rebuild on every show) + local tip + if self.theme.auto_hotkey then + local fk = awful.util.table.clone(menu._fake_keys) + fk[1][4].keyset = self.keys + tip = awful.util.table.join(menu.keys.all, fk) + else + tip = menu.keys.all + end + + redtip.cache["Menu"] = nil -- dirty trick to renew helper for every menu instance + redtip:set_pack("Menu", tip, self.theme.keytip.column, self.theme.keytip.geometry) +end + +-- Hide a menu popup. +-------------------------------------------------------------------------------- +function menu:hide() + if not self.wibox.visible then return end + + self:item_leave(self.sel) + + if self.sel and self.items[self.sel].child then self.items[self.sel].child:hide() end + + self.sel = nil + awful.keygrabber.stop(self._keygrabber) + + if self.hidetimer and self.hidetimer.started then self.hidetimer:stop() end + + self.wibox.visible = false + redtip:remove_pack() +end + +-- Toggle menu visibility +-------------------------------------------------------------------------------- +function menu:toggle(args) + if self.wibox.visible then + self:hide() + else + self:show(args) + end +end + +-- Set user hotkeys +-------------------------------------------------------------------------------- +function menu:set_keys(keys, layout) + layout = layout or "all" + if keys then + self.keys[layout] = keys + if layout ~= "all" then self.keys.all = awful.util.table.join(self.keys.move, self.keys.action) end + end +end + +-- Clears all items from the menu +-------------------------------------------------------------------------------- +function menu:clear() + self.add_size = 0 + self.layout:reset() + self.items = {} + self.keys = {} + self.wibox.height = 1 +end + +-- Clears and then refills the menu with the given items +-------------------------------------------------------------------------------- +function menu:replace_items(items) + self:clear() + for _, item in ipairs(items) do + self:add(item) + end + self.wibox.height = self.add_size > 0 and self.add_size or 1 +end + +-- Add a new menu entry. +-- args.new (Default: redflat.menu.entry) The menu entry constructor. +-- args.theme (Optional) The menu entry theme. +-- args.* params needed for the menu entry constructor. +-- @param args The item params +----------------------------------------------------------------------------------------------------------------------- +function menu:add(args) + if not args then return end + + -- If widget instead of text label recieved + -- just add it to layer, don't try to create menu item + ------------------------------------------------------------ + if type(args[1]) ~= "string" and args.widget then + local element = {} + element.width, element.height = args.widget:fit(_fake_context, -1, -1) + self.add_size = self.add_size + element.height + self.layout:add(args.widget) + + if args.focus then + args.widget:connect_signal( + "mouse::enter", + function() + self:item_leave(self.sel) + self.sel = nil + end + ) + end + + return + end + + -- Set theme for currents item + ------------------------------------------------------------ + local theme = redutil.table.merge(self.theme, args.theme or {}) + args.theme = theme + + -- Generate menu item + ------------------------------------------------------------ + args.new = args.new or menu.entry + local success, item = pcall(args.new, self, args) + + if not success then + print("Error while creating menu entry: " .. item) + return + end + + if not item.widget then + print("Error while checking menu entry: no property widget found.") + return + end + + item.parent = self + item.theme = item.theme or theme + item._background = wibox.container.background(item.widget) + item._background:set_fg(item.theme.color.text) + item._background:set_bg(item.theme.color.wibox) + + -- Add item widget to menu layout + ------------------------------------------------------------ + table.insert(self.items, item) + self.layout:add(item._background) + + -- Create bindings + ------------------------------------------------------------ + local num = #self.items + + item._background:buttons(awful.util.table.join( + awful.button({}, 3, function () self:hide() end), + awful.button({}, 1, function () + self:item_enter(num) + self:exec(num) + end) + )) + + item.widget:connect_signal("mouse::enter", function() self:item_enter(num, { hover = true }) end) + + -- Create submenu if needed + ------------------------------------------------------------ + if type(args[2]) == "table" then + if not self.items[#self.items].child then + self.items[#self.items].child = menu.new(args[2], self) + self.items[#self.items].child.position = self.add_size + end + end + + ------------------------------------------------------------ + self.add_size = self.add_size + item.theme.height + + return item +end + +-- Default menu item constructor +-- @param parent The parent menu +-- @param args the item params +-- @return table with all the properties the user wants to change +----------------------------------------------------------------------------------------------------------------------- +function menu.entry(parent, args) + args = args or {} + args.text = args[1] or args.text or "" + args.cmd = args[2] or args.cmd + args.icon = args[3] or args.icon + args.right_icon = args[4] or args.right_icon + + -- Create the item label widget + ------------------------------------------------------------ + local label = wibox.widget.textbox() + label:set_font(args.theme.font) + + -- Set hotkey if needed + ------------------------------------------------------------ + local key + local text = awful.util.escape(args.text) + + if args.key and not awful.util.table.hasitem(parent.keys, args.key) then + key = args.key + elseif parent.theme.auto_hotkey then + for i = 1, #text do + local c = string.sub(string.lower(text), i, i) + + if not awful.util.table.hasitem(parent.keys, c) and string.match(c, "%l") then + key = c + break + end + end + end + + if key then parent.keys[#parent.keys + 1] = key end + + label:set_markup(make_u(text, key)) + + -- Set left icon if needed + ------------------------------------------------------------ + local iconbox + local margin = wibox.container.margin(label) + + if args.icon then + iconbox = svgbox(args.icon, nil, args.theme.color.left_icon) + iconbox:set_vector_resize(args.theme.svg_scale[1]) + else + margin:set_left(args.theme.icon_margin[1]) + end + + -- Set right icon if needed + ------------------------------------------------------------ + local right_iconbox + + if type(args.cmd) == "table" then + right_iconbox = svgbox(args.theme.submenu_icon, nil, args.theme.color.submenu_icon) + right_iconbox:set_vector_resize(args.theme.svg_scale[2]) + elseif args.right_icon then + right_iconbox = svgbox(args.right_icon, nil, args.theme.color.right_icon) + right_iconbox:set_vector_resize(args.theme.svg_scale[2]) + end + + -- Construct item layouts + ------------------------------------------------------------ + local left = wibox.layout.fixed.horizontal() + + if iconbox ~= nil then + left:add(wibox.container.margin(iconbox, unpack(args.theme.icon_margin))) + end + + left:add(margin) + + local layout = wibox.layout.align.horizontal() + layout:set_left(left) + + if right_iconbox ~= nil then + layout:set_right(wibox.container.margin(right_iconbox, unpack(args.theme.ricon_margin))) + end + + local layout_const = wibox.container.constraint(layout, "exact", args.theme.width, args.theme.height) + + ------------------------------------------------------------ + return { + label = label, + icon = iconbox, + widget = layout_const, + cmd = args.cmd, + right_icon = right_iconbox + } +end + +-- Create new menu +----------------------------------------------------------------------------------------------------------------------- +function menu.new(args, parent) + + args = args or {} + + -- Initialize menu object + ------------------------------------------------------------ + local _menu = { + item_enter = menu.item_enter, + item_leave = menu.item_leave, + get_root = menu.get_root, + delete = menu.delete, + toggle = menu.toggle, + hide = menu.hide, + show = menu.show, + exec = menu.exec, + add = menu.add, + clear = menu.clear, + replace_items = menu.replace_items, + items = {}, + keys = {}, + parent = parent, + layout = wibox.layout.fixed.vertical(), + add_size = 0, + theme = redutil.table.merge(parent and parent.theme or default_theme(), args.theme or {}) + } + + -- Create items + ------------------------------------------------------------ + for _, v in ipairs(args) do _menu:add(v) end + + if args.items then + for _, v in ipairs(args.items) do _menu:add(v) end + end + + _menu._keygrabber = function (...) + grabber(_menu, ...) + end + + -- create wibox + ------------------------------------------------------------ + _menu.wibox = wibox({ + type = "popup_menu", + ontop = true, + fg = _menu.theme.color.text, + bg = _menu.theme.color.wibox, + border_color = _menu.theme.color.border, + border_width = _menu.theme.border_width, + shape = _menu.theme.shape + }) + + _menu.wibox.visible = false + _menu.wibox:set_widget(_menu.layout) + + -- set size + _menu.wibox.width = _menu.theme.width + _menu.wibox.height = _menu.add_size > 0 and _menu.add_size or 1 + + -- Set menu autohide timer + ------------------------------------------------------------ + if _menu.theme.hide_timeout > 0 then + local root = _menu:get_root() + + -- timer only for root menu + -- all submenus will be hidden automatically + if root == _menu then + _menu.hidetimer = timer({ timeout = _menu.theme.hide_timeout }) + _menu.hidetimer:connect_signal("timeout", function() _menu:hide() end) + end + + -- enter/leave signals for all menu chain + _menu.wibox:connect_signal("mouse::enter", + function() + if root.hidetimer.started then root.hidetimer:stop() end + end) + _menu.wibox:connect_signal("mouse::leave", function() root.hidetimer:start() end) + end + + ------------------------------------------------------------ + return _menu +end + +-- Config metatable to call menu module as function +----------------------------------------------------------------------------------------------------------------------- +function menu.mt:__call(...) + return menu.new(...) +end + +return setmetatable(menu, menu.mt) diff --git a/awesome/.config/awesome/redflat/service/dfparser.lua b/awesome/.config/awesome/redflat/service/dfparser.lua new file mode 100644 index 0000000..3420fe0 --- /dev/null +++ b/awesome/.config/awesome/redflat/service/dfparser.lua @@ -0,0 +1,412 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat desctop file parser -- +----------------------------------------------------------------------------------------------------------------------- +-- Create application menu analyzing .desktop files in given directories +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ awful.menubar v3.5.2 +------ (c) 2009, 2011-2012 Antonio Terceiro, Alexander Yakushev +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local io = io +local table = table +local ipairs = ipairs +local string = string +local awful = require("awful") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") +local gears = require("gears") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local dfparser = {} +local cache = {} + +dfparser.terminal = 'uxterm' + +local all_icon_folders = { "apps", "actions", "devices", "places", "categories", "status" } +local all_icon_sizes = { '128x128' , '96x96', '72x72', '64x64', '48x48', + '36x36', '32x32', '24x24', '22x22', '16x16', 'scalable' } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + icons = { custom_only = false, scalable_only = false, df_icon = nil, theme = nil }, + desktop_file_dirs = { "/usr/share/applications/" }, + wm_name = nil, + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "service.dfparser") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Cache functions +-------------------------------------------------------------------------------- +local function check_cached(req) + for k, v in pairs(cache) do + local eq = #req == #k + for ck, cv in pairs(k) do eq = eq and cv == req[ck] end + if eq then return v end + end + return nil +end + +-- Check whether the icon format is supported +-------------------------------------------------------------------------------- +local function is_format(icon_file, icon_formats) + for _, f in ipairs(icon_formats) do + if icon_file:match('%.' .. f) then return true end + end + return false +end + +-- Find all possible locations of the icon +-------------------------------------------------------------------------------- +local function all_icon_path(style) + + local icon_theme_paths = {} + + -- add user icon theme + if style.theme then + table.insert(icon_theme_paths, style.theme .. '/') + -- TODO also look in parent icon themes, as in freedesktop.org specification + end + + -- add fallback theme + if not style.custom_only then table.insert(icon_theme_paths, '/usr/share/icons/hicolor/') end + + -- seach only svg icons if need + local current_icon_sizes = style.scalable_only and { 'scalable' } or all_icon_sizes + + -- form all avalible icon dirs + local icon_path = {} + + for _, icon_theme_directory in ipairs(icon_theme_paths) do + for _, size in ipairs(current_icon_sizes) do + for _, folder in ipairs(all_icon_folders) do + table.insert(icon_path, icon_theme_directory .. size .. "/" .. folder .. '/') + end + end + end + + -- lowest priority fallbacks + if not style.custom_only then + table.insert(icon_path, '/usr/share/pixmaps/') + table.insert(icon_path, '/usr/share/icons/') + end + + return icon_path +end + +-- Lookup an icon in different folders of the filesystem +-- @param icon_file Short or full name of the icon +-- @param style.default_icon Return if no icon was found +-- @param style.icon_theme Full path to custom icon theme +-- @param style.custom_only Seach only custom theme icons, ignore system +-- @param style.scalable_only Seach only svg type icons +-- @return full name of the icon +----------------------------------------------------------------------------------------------------------------------- +function dfparser.lookup_icon(icon_file, style) + + style = redutil.table.merge(default_style().icons, style or {}) + + local df_icon + if style.df_icon and gears.filesystem.file_readable(style.df_icon) then + df_icon = style.df_icon + end + + -- No icon name + if not icon_file or icon_file == "" then return df_icon end + + -- Handle full path icons + local icon_formats = style.scalable_only and { "svg" } or { "svg", "png", "gif" } + + if icon_file:sub(1, 1) == '/' then + if is_format(icon_file, icon_formats) then + return gears.filesystem.file_readable(icon_file) and icon_file or df_icon + else + icon_file = string.match(icon_file, "([%a%d%-]+)%.") + if not icon_file then return df_icon end + end + end + + -- Find all possible locations to search + local icon_path = all_icon_path(style) + + -- Icon searching + for _, directory in ipairs(icon_path) do + + -- check if icon format specified and supported + if is_format(icon_file, icon_formats) and awful.util.file_readable(directory .. icon_file) then + return directory .. icon_file + else + + -- check if icon format specified but not supported + if string.match(icon_file, "%.") + and not string.match(icon_file, "org%.gnome%.") -- ignore gnome naming style + and not is_format(icon_file, icon_formats) then + icon_file = string.match(icon_file, "[%a%d%-]+") + end + + -- icon is probably specified without path and format, + -- like 'firefox'. Try to add supported extensions to + -- it and see if such file exists. + for _, format in ipairs(icon_formats) do + local possible_file = directory .. icon_file .. "." .. format + if awful.util.file_readable(possible_file) then + return possible_file + end + end + end + end + + return df_icon +end + +-- Main parsing functions +----------------------------------------------------------------------------------------------------------------------- + +-- Parse a .desktop file +-- @param file The .desktop file +-- @param style Arguments for dfparser.lookup_icon +-- @return A table with file entries +-------------------------------------------------------------------------------- +local function parse(file, style) + + local program = { show = true, file = file } + local desktop_entry = false + + -- Parse the .desktop file. + -- We are interested in [Desktop Entry] group only. + for line in io.lines(file) do + if not desktop_entry and line == "[Desktop Entry]" then + desktop_entry = true + else + if line:sub(1, 1) == "[" and line:sub(-1) == "]" then + -- A declaration of new group - stop parsing + break + end + + -- Grab the values + for key, value in line:gmatch("(%w+)%s*=%s*(.+)") do + program[key] = value + end + end + end + + -- In case [Desktop Entry] was not found + if not desktop_entry then return nil end + + -- Don't show program if NoDisplay attribute is false + if program.NoDisplay and string.lower(program.NoDisplay) == "true" then + program.show = false + end + + -- Only show the program if there is no OnlyShowIn attribute + -- or if it's equal to given wm name + if program.OnlyShowIn ~= nil and style.wm_name and not program.OnlyShowIn:match(style.wm_name) then + program.show = false + end + + -- Look up for a icon. + if program.Icon then + program.icon_path = dfparser.lookup_icon(program.Icon, style.icons) + end + + -- Split categories into a table. Categories are written in one + -- line separated by semicolon. + if program.Categories then + program.categories = {} + + for category in program.Categories:gmatch('[^;]+') do + table.insert(program.categories, category) + end + end + + if program.Exec then + -- Substitute Exec special codes as specified in + -- http://standards.freedesktop.org/desktop-entry-spec/1.1/ar01s06.html + if program.Name == nil then + program.Name = '['.. file:match("([^/]+)%.desktop$") ..']' + end + + local cmdline = program.Exec:gsub('%%c', program.Name) + cmdline = cmdline:gsub('%%[fuFU]', '') + cmdline = cmdline:gsub('%%k', program.file) + + if program.icon_path then + cmdline = cmdline:gsub('%%i', '--icon ' .. program.icon_path) + else + cmdline = cmdline:gsub('%%i', '') + end + + if program.Terminal == "true" then + cmdline = dfparser.terminal .. ' -e ' .. cmdline + end + + program.cmdline = cmdline + end + + return program +end + +-- Parse a directory with .desktop files +-- @param dir The directory +-- @param style Arguments for dfparser.lookup_icon +-- @return A table with all .desktop entries +-------------------------------------------------------------------------------- +local function parse_dir(dir, style) + local req = awful.util.table.join({ path = dir }, style.icons) + local programs = {} + local cached = check_cached(req) + + if not cached then + local files = redutil.read.output('find '.. dir ..' -maxdepth 1 -name "*.desktop" 2>/dev/null') + + for file in string.gmatch(files, "[^\n]+") do + local program = parse(file, style) + if program then table.insert(programs, program) end + end + + cache[req] = programs + else + programs = cached + end + + return programs +end + +-- Create a new application menu +-- @param style.icons Arguments for dfparser.lookup_icon +-- @param style.desktop_file_dirs Table containing all .desktop file directories +-- @return Applications menu table +----------------------------------------------------------------------------------------------------------------------- +function dfparser.menu(style) + + style = redutil.table.merge(default_style(), style or {}) + + -- Categories list + -------------------------------------------------------------------------------- + local categories = { + { app_type = "AudioVideo", name = "Multimedia", icon_name = "applications-multimedia" }, + { app_type = "Development", name = "Development", icon_name = "applications-development" }, + { app_type = "Education", name = "Education", icon_name = "applications-science" }, + { app_type = "Game", name = "Games", icon_name = "applications-games" }, + { app_type = "Graphics", name = "Graphics", icon_name = "applications-graphics" }, + { app_type = "Office", name = "Office", icon_name = "applications-office" }, + { app_type = "Network", name = "Internet", icon_name = "applications-internet" }, + { app_type = "Settings", name = "Settings", icon_name = "applications-utilities" }, + { app_type = "System", name = "System Tools", icon_name = "applications-system" }, + { app_type = "Utility", name = "Accessories", icon_name = "applications-accessories" } + } + + -- Find icons for categories + -------------------------------------------------------------------------------- + for _, v in ipairs(categories) do + v.icon = dfparser.lookup_icon(v.icon_name, style) + end + + -- Find all visible menu items + -------------------------------------------------------------------------------- + local prog_list = {} + for _, path in ipairs(style.desktop_file_dirs) do + local programs = parse_dir(path, style) + + for _, prog in ipairs(programs) do + if prog.show and prog.Name and prog.cmdline then + table.insert(prog_list, prog) + end + end + end + + -- Sort menu items by category and create submenu + -------------------------------------------------------------------------------- + local appmenu = {} + for _, menu_category in ipairs(categories) do + local catmenu = {} + + for i = #prog_list, 1, -1 do + if prog_list[i].categories then + for _, item_category in ipairs(prog_list[i].categories) do + if item_category == menu_category.app_type then + table.insert(catmenu, { prog_list[i].Name, prog_list[i].cmdline, prog_list[i].icon_path }) + table.remove(prog_list, i) + end + end + end + end + if #catmenu > 0 then table.insert(appmenu, { menu_category.name, catmenu, menu_category.icon }) end + end + + -- Collect all items without category to "Other" submenu + -------------------------------------------------------------------------------- + if #prog_list > 0 then + local catmenu = {} + + for _, prog in ipairs(prog_list) do + table.insert(catmenu, { prog.Name, prog.cmdline, prog.icon_path }) + end + + table.insert(appmenu, { "Other", catmenu, dfparser.lookup_icon("applications-other") }) + end + + return appmenu +end + +-- Get list of icons linked with process name +-- @param style.icons Arguments for dfparser.lookup_icon +-- @param style.desktop_file_dirs Table containing all .desktop file directories +-- @return Icon list +----------------------------------------------------------------------------------------------------------------------- +function dfparser.icon_list(style) + + style = redutil.table.merge(default_style(), style or {}) + local list = {} + + for _, path in ipairs(style.desktop_file_dirs) do + local programs = parse_dir(path, style) + + for _, prog in ipairs(programs) do + if prog.Icon and prog.Exec then + local key = string.match(prog.Exec, "[%a%d%.%-/]+") + if string.find(key, "/") then key = string.match(key, "[%a%d%.%-]+$") end + list[key] = prog.icon_path + end + end + end + + return list +end + +-- Generate table with avaliable programs +-- without sorting by categories +-- @param style.icons Arguments for dfparser.lookup_icon +-- @param style.desktop_file_dirs Table containing all .desktop file directories +----------------------------------------------------------------------------------------------------------------------- +function dfparser.program_list(style) + + style = redutil.table.merge(default_style(), style or {}) + local prog_list = {} + + for _, path in ipairs(style.desktop_file_dirs) do + local programs = parse_dir(path, style) + + for _, prog in ipairs(programs) do + if prog.show and prog.Name and prog.cmdline then + table.insert(prog_list, prog) + end + end + end + + return prog_list +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return dfparser diff --git a/awesome/.config/awesome/redflat/service/init.lua b/awesome/.config/awesome/redflat/service/init.lua new file mode 100644 index 0000000..638e98b --- /dev/null +++ b/awesome/.config/awesome/redflat/service/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.service" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/service/navigator.lua b/awesome/.config/awesome/redflat/service/navigator.lua new file mode 100644 index 0000000..4284900 --- /dev/null +++ b/awesome/.config/awesome/redflat/service/navigator.lua @@ -0,0 +1,355 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat focus switch util -- +----------------------------------------------------------------------------------------------------------------------- +-- Visual clinet managment helper +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local math = math + +local awful = require("awful") +local wibox = require("wibox") +local color = require("gears.color") +local beautiful = require("beautiful") +local timer = require("gears.timer") + +local redflat = require("redflat") +local redutil = require("redflat.util") +local redtip = require("redflat.float.hotkeys") +local rednotify = require("redflat.float.notify") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local navigator = { action = {}, data = {}, active = false } + +navigator.ignored = { "dock", "splash", "desktop" } + + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + border_width = 2, + marksize = { width = 200, height = 100, r = 20 }, + gradstep = 100, + linegap = 35, + timeout = 1, + notify = {}, + keytip = { base = { geometry = { width = 600 }, exit = true } }, + titlefont = { font = "Sans", size = 28, face = 1, slant = 0 }, + num = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "F1", "F3", "F4", "F5" }, + font = { font = "Sans", size = 22, face = 1, slant = 0 }, + color = { border = "#575757", wibox = "#00000000", bg1 = "#57575740", bg2 = "#57575720", + fbg1 = "#b1222b40", fbg2 = "#b1222b20", mark = "#575757", text = "#202020", + hbg1 = "#32882d40", hbg2 = "#32882d20" }, + shape = nil + } + return redutil.table.merge(style, redutil.table.check(beautiful, "service.navigator") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Check geometry intersection +-------------------------------------------------------------------------------- +local function is_intersect(a, b) + return (b.x < a.x + a.width and b.x + b.width > a.x and b.y < a.y + a.height and b.y + b.height > a.y) +end + +-- Window painting +-------------------------------------------------------------------------------- +function navigator.make_paint(c) + + -- Initialize vars + ------------------------------------------------------------ + local style = navigator.style + local widg = wibox.widget.base.make_widget() + + widg._data = { + client = c, + alert = false, + } + + -- User functions + ------------------------------------------------------------ + function widg:set_client(client_) + if widg._data.client ~= client_ then + widg._data.client = client_ + self:emit_signal("widget::redraw_needed") + end + end + + function widg:set_alert(value) + if widg._data.alert ~= value then + widg._data.alert = value + self:emit_signal("widget::redraw_needed") + end + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + return width, height + end + + -- Draw + ------------------------------------------------------------ + function widg:draw(_, cr, width, height) + + if not widg._data.client then return end + + -- background + local bg1, bg2 + local num = math.ceil((width + height) / style.gradstep) + + if widg._data.alert then + bg1, bg2 = style.color.hbg1, style.color.hbg2 + else + local is_focused = widg._data.client == client.focus + bg1 = is_focused and style.color.fbg1 or style.color.bg1 + bg2 = is_focused and style.color.fbg2 or style.color.bg2 + end + + for i = 1, num do + local cc = i % 2 == 1 and bg1 or bg2 + local l = i * style.gradstep + + cr:set_source(color(cc)) + cr:move_to(0, (i - 1) * style.gradstep) + cr:rel_line_to(0, style.gradstep) + cr:rel_line_to(l, - l) + cr:rel_line_to(- style.gradstep, 0) + cr:close_path() + cr:fill() + end + + -- rounded rectangle on center + local r = style.marksize.r + local w, h = style.marksize.width - 2 * r, style.marksize.height - 2 * r + + cr:set_source(color(style.color.mark)) + cr:move_to((width - w) / 2 - r, (height - h) / 2) + cr:rel_curve_to(0, -r, 0, -r, r, -r) + cr:rel_line_to(w, 0) + cr:rel_curve_to(r, 0, r, 0, r, r) + cr:rel_line_to(0, h) + cr:rel_curve_to(0, r, 0, r, -r, r) + cr:rel_line_to(-w, 0) + cr:rel_curve_to(-r, 0, -r, 0, -r, -r) + cr:close_path() + cr:fill() + + -- label + local index = navigator.style.num[awful.util.table.hasitem(navigator.cls, widg._data.client)] + local g = redutil.client.fullgeometry(widg._data.client) + + cr:set_source(color(style.color.text)) + redutil.cairo.set_font(cr, style.titlefont) + redutil.cairo.textcentre.full(cr, { width/2, height/2 - style.linegap / 2 }, index) + redutil.cairo.set_font(cr, style.font) + redutil.cairo.textcentre.full(cr, { width/2, height/2 + style.linegap / 2 }, g.width .. " x " .. g.height) + end + + ------------------------------------------------------------ + return widg +end + +-- Construct wibox +-------------------------------------------------------------------------------- +function navigator.make_decor(c) + local object = {} + local style = navigator.style + + -- Create wibox + ------------------------------------------------------------ + object.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + + object.client = c + object.widget = navigator.make_paint(c) + object.wibox:set_widget(object.widget) + + -- User functions + ------------------------------------------------------------ + object.update = { + focus = function() object.widget:emit_signal("widget::redraw_needed") end, + close = function() navigator:restart() end, + geometry = function() redutil.client.fullgeometry(object.wibox, redutil.client.fullgeometry(object.client)) end + } + + function object:set_client(client_) + object.client = client_ + object.widget:set_client(client_) + redutil.client.fullgeometry(object.wibox, redutil.client.fullgeometry(object.client)) + + object.client:connect_signal("focus", object.update.focus) + object.client:connect_signal("unfocus", object.update.focus) + object.client:connect_signal("property::geometry", object.update.geometry) + object.client:connect_signal("unmanage", object.update.close) + end + + function object:clear(no_hide) + object.client:disconnect_signal("focus", object.update.focus) + object.client:disconnect_signal("unfocus", object.update.focus) + object.client:disconnect_signal("property::geometry", object.update.geometry) + object.client:disconnect_signal("unmanage", object.update.close) + object.widget:set_client() + if not no_hide then object.wibox.visible = false end + end + + ------------------------------------------------------------ + object:set_client(c) + return object +end + + +-- Main functions +----------------------------------------------------------------------------------------------------------------------- +function navigator:init() + + -- Style + ------------------------------------------------------------ + self.style = default_style() + + -- Hilight area + ------------------------------------------------------------ + self.hilight = {} + + -- timer + self.hilight.hidetimer = timer({ timeout = self.style.timeout }) + self.hilight.hidetimer:connect_signal("timeout", function() self.hilight.hide() end) + + -- show/hide + function self.hilight.show(g) + for i, c in ipairs(self.cls) do + self.data[i].widget:set_alert(is_intersect(g, c:geometry())) + end + self.hilight.hidetimer:again() + end + + function self.hilight.hide() + for i, _ in ipairs(self.cls) do self.data[i].widget:set_alert(false) end + end + + -- close the navigator on tag switch + tag.connect_signal('property::selected', + function() + if navigator.active then self:close() end + end + ) + + -- update navigator if new client spawns + client.connect_signal('manage', + function() + if navigator.active then self:restart() end + end + ) + + -- update navigator if a client gets minimized or restored + client.connect_signal('property::minimized', + function() + if navigator.active then self:restart() end + end + ) +end + +function navigator:run() + if not self.style then self:init() end + + -- check clients + local s = mouse.screen + self.cls = awful.client.tiled(s) + + if #self.cls == 0 or + not client.focus or + client.focus.fullscreen or + awful.util.table.hasitem(navigator.ignored, client.focus.type) + then + return + end + + -- check handler + local l = awful.layout.get(client.focus.screen) + local handler = l.key_handler or redflat.layout.common.handler[l] + if not handler then + rednotify:show(redutil.table.merge({ text = "Layout not supported" }, self.style.notify)) + return + end + + -- layout setup if needed + if l.startup then l.startup() end + local tip = l.tip or redflat.layout.common.tips[l] + + -- activate navition widgets + for i, c in ipairs(self.cls) do + if not self.data[i] then + self.data[i] = self.make_decor(c) + else + self.data[i]:set_client(c) + end + + self.data[i].wibox.visible = true + end + + -- run key handler + self.grabber_settled = handler + awful.keygrabber.run(self.grabber_settled) + + -- set keys tip + self.tip_settled = tip + if tip then + local tip_style = self.style.keytip[awful.layout.getname(l)] or self.style.keytip.base + redtip:set_pack( + "Layout " .. l.name, tip, tip_style.column, tip_style.geometry, + self.style.keytip.base.exit and function() redflat.layout.common.action.exit() end -- fix this? + ) + end + + navigator.active = true +end + +function navigator:close() + for i, _ in ipairs(self.cls) do + self.data[i]:clear() + end + + awful.keygrabber.stop(self.grabber_settled) + if self.tip_settled then redtip:remove_pack() end + + local l = client.focus and awful.layout.get(client.focus.screen) + if l and l.cleanup then l.cleanup() end + self.cls = {} + + navigator.active = false +end + +function navigator:restart() + -- update decoration + for i, _ in ipairs(self.cls) do self.data[i]:clear(true) end + local newcls = awful.client.tiled(mouse.screen) + for i = 1, math.max(#self.cls, #newcls) do + if newcls[i] then + if not self.data[i] then + self.data[i] = self.make_decor(newcls[i]) + else + self.data[i]:set_client(newcls[i]) + end + + self.data[i].wibox.visible = true + else + self.data[i].wibox.visible = false + end + end + + self.cls = newcls +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return navigator diff --git a/awesome/.config/awesome/redflat/startup.lua b/awesome/.config/awesome/redflat/startup.lua new file mode 100644 index 0000000..490c4c7 --- /dev/null +++ b/awesome/.config/awesome/redflat/startup.lua @@ -0,0 +1,44 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat startup check -- +----------------------------------------------------------------------------------------------------------------------- +-- Save exit reason to file +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local io = io + +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local startup = { locked = false } + +startup.path = "/tmp/awesome-exit-reason" +--startup.bin = "awesome-client" + +local REASON = { RESTART = "restart", EXIT = "exit" } + +-- Stamp functions +----------------------------------------------------------------------------------------------------------------------- + +-- save restart reason +function startup.stamp(reason_restart) + local file = io.open(startup.path, "w") + file:write(reason_restart) + file:close() +end + +function startup:activate() + -- check if it is first start + local reason = redutil.read.file(startup.path) + self.is_startup = (not reason or reason == REASON.EXIT) and not self.locked + + -- save reason on exit + awesome.connect_signal("exit", + function(is_restart) startup.stamp(is_restart and REASON.RESTART or REASON.EXIT) end + ) +end + +----------------------------------------------------------------------------------------------------------------------- +return startup diff --git a/awesome/.config/awesome/redflat/system.lua b/awesome/.config/awesome/redflat/system.lua new file mode 100644 index 0000000..6469c0b --- /dev/null +++ b/awesome/.config/awesome/redflat/system.lua @@ -0,0 +1,753 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat system -- +----------------------------------------------------------------------------------------------------------------------- +-- System monitoring functions collected here +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ vicious module +------ (c) 2010, 2011 Adrian C. +------ (c) 2009, Lucas de Vries +------ (c) 2011, Jörg T. +------ (c) 2011, Adrian C. +----------------------------------------------------------------------------------------------------------------------- + + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local tonumber = tonumber +local io = io +local os = os +local string = string +local math = math + +local timer = require("gears.timer") +local awful = require("awful") +local redutil = require("redflat.util") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local system = { thermal = {}, dformatted = {}, pformatted = {} } + +-- Async settlers generator +----------------------------------------------------------------------------------------------------------------------- +function system.simple_async(command, pattern) + return function(setup) + awful.spawn.easy_async_with_shell(command, + function(output) + local value = tonumber(string.match(output, pattern)) + setup(value and { value } or { 0 }) + end + ) + end +end + +-- Disk usage +----------------------------------------------------------------------------------------------------------------------- +function system.fs_info(args) + local fs_info = {} + args = args or "/" + + -- Get data from df + ------------------------------------------------------------ + local line = redutil.read.output("LC_ALL=C df -kP " .. args .. " | tail -1") + + -- Parse data + ------------------------------------------------------------ + fs_info.size = string.match(line, "^.-[%s]([%d]+)") + fs_info.mount = string.match(line, "%%[%s]([%p%w]+)") + fs_info.used, fs_info.avail, fs_info.use_p = string.match(line, "([%d]+)[%D]+([%d]+)[%D]+([%d]+)%%") + + -- Format output special for redflat desktop widget + ------------------------------------------------------------ + return { tonumber(fs_info.use_p) or 0, tonumber(fs_info.used) or 0} +end + +-- Qemu image check +----------------------------------------------------------------------------------------------------------------------- +local function q_format(size, k) + if not size or not k then return 0 end + return k == "K" and tonumber(size) or k == "M" and size * 1024 or k == "G" and size * 1024^2 or 0 +end + +function system.qemu_image_size(args) + local img_info = {} + + -- Get data from qemu-ima + ------------------------------------------------------------ + local line = redutil.read.output("LC_ALL=C qemu-img info " .. args) + + -- Parse data + ------------------------------------------------------------ + local size, k = string.match(line, "disk%ssize:%s([%.%d]+)%s(%w)") + img_info.size = q_format(size, k) + local vsize, vk = string.match(line, "virtual%ssize:%s([%.%d]+)%s(%w)") + img_info.virtual_size = q_format(vsize, vk) + img_info.use_p = img_info.virtual_size > 0 and math.floor(img_info.size / img_info.virtual_size * 100) or 0 + + -- Format output special for redflat desktop widget + ------------------------------------------------------------ + return { img_info.use_p, img_info.size, off = img_info.size == 0 } +end + +-- Traffic check with vnstat (async) +----------------------------------------------------------------------------------------------------------------------- +local function vnstat_format(value, unit) + if not value or not unit then return 0 end + local v = value:gsub(',', '.') + return unit == "B" and tonumber(v) + or unit == "KiB" and v * 1024 + or unit == "MiB" and v * 1024^2 + or unit == "GiB" and v * 1024^3 +end + +function system.vnstat_check(args) + local command = string.format("vnstat %s | tail -n 3 | head -n 1", args) + return function(setup) + awful.spawn.easy_async_with_shell(command, + function(output) + local x, u = string.match( + output, "%s+%d+,%d+%s%w+%s+%|%s+%d+,%d+%s%w+%s+%|%s+(%d+,%d+)%s(%w+)%s+%|%s+.+" + ) + local total = vnstat_format(x, u) + setup({ total }) + end + ) + end +end + +-- Get network speed +----------------------------------------------------------------------------------------------------------------------- +function system.net_speed(interface, storage) + local up, down = 0, 0 + + -- Get network info + -------------------------------------------------------------------------------- + for line in io.lines("/proc/net/dev") do + + -- Match wmaster0 as well as rt0 (multiple leading spaces) + local name = string.match(line, "^[%s]?[%s]?[%s]?[%s]?([%w]+):") + + -- Calculate speed for given interface + ------------------------------------------------------------ + if name == interface then + -- received bytes, first value after the name + local recv = tonumber(string.match(line, ":[%s]*([%d]+)")) + -- transmited bytes, 7 fields from end of the line + local send = tonumber(string.match(line, "([%d]+)%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d$")) + + local now = os.time() + + if not storage[interface] then + -- default values on the first run + storage[interface] = { recv = 0, send = 0 } + else + -- net stats are absolute, substract our last reading + local interval = now - storage[interface].time + if interval <= 0 then interval = 1 end + + down = (recv - storage[interface].recv) / interval + up = (send - storage[interface].send) / interval + end + + -- store totals + storage[interface].time = now + storage[interface].recv = recv + storage[interface].send = send + end + end + + -------------------------------------------------------------------------------- + return { up, down } +end + +-- Get disk speed +----------------------------------------------------------------------------------------------------------------------- +function system.disk_speed(disk, storage) + local up, down = 0, 0 + + -- Get i/o info + -------------------------------------------------------------------------------- + for line in io.lines("/proc/diskstats") do + + -- parse info + -- linux kernel documentation: Documentation/iostats.txt + local device, read, write = string.match(line, "([^%s]+) %d+ %d+ (%d+) %d+ %d+ %d+ (%d+)") + + -- Calculate i/o for given device + ------------------------------------------------------------ + if device == disk then + local now = os.time() + local stats = { read, write } + + if not storage[disk] then + -- default values on the first run + storage[disk] = { stats = stats } + else + -- check for overflows and counter resets (> 2^32) + if stats[1] < storage[disk].stats[1] or stats[2] < storage[disk].stats[2] then + storage[disk].stats[1], storage[disk].stats[2] = stats[1], stats[2] + end + + -- diskstats are absolute, substract our last reading + -- * divide by timediff because we don't know the timer value + local interval = now - storage[disk].time + if interval <= 0 then interval = 1 end + + up = (stats[1] - storage[disk].stats[1]) / interval + down = (stats[2] - storage[disk].stats[2]) / interval + end + + -- store totals + storage[disk].time = now + storage[disk].stats = stats + end + end + + -------------------------------------------------------------------------------- + return { up, down } +end + +-- Get MEM info +----------------------------------------------------------------------------------------------------------------------- +function system.memory_info() + local mem = { buf = {}, swp = {} } + + -- Get MEM info + ------------------------------------------------------------ + for line in io.lines("/proc/meminfo") do + for k, v in string.gmatch(line, "([%a]+):[%s]+([%d]+).+") do + if k == "MemTotal" then mem.total = math.floor(v/1024) + elseif k == "MemFree" then mem.buf.f = math.floor(v/1024) + elseif k == "Buffers" then mem.buf.b = math.floor(v/1024) + elseif k == "Cached" then mem.buf.c = math.floor(v/1024) + elseif k == "SwapTotal" then mem.swp.t = math.floor(v/1024) + elseif k == "SwapFree" then mem.swp.f = math.floor(v/1024) + end + end + end + + -- Calculate memory percentage + ------------------------------------------------------------ + mem.free = mem.buf.f + mem.buf.b + mem.buf.c + mem.inuse = mem.total - mem.free + mem.bcuse = mem.total - mem.buf.f + mem.usep = math.floor(mem.inuse / mem.total * 100) + + -- calculate swap percentage + mem.swp.inuse = mem.swp.t - mem.swp.f + mem.swp.usep = mem.swp.t > 0 and math.floor(mem.swp.inuse / mem.swp.t * 100) or 0 + + ------------------------------------------------------------ + return mem +end + +-- Get cpu usage info +----------------------------------------------------------------------------------------------------------------------- +--local storage = { cpu_total = {}, cpu_active = {} } -- storage structure + +function system.cpu_usage(storage) + local cpu_lines = {} + local cpu_usage = {} + local diff_time_total + + -- Get CPU stats + ------------------------------------------------------------ + for line in io.lines("/proc/stat") do + if string.sub(line, 1, 3) == "cpu" then + local digits_in_line = {} + + for i in string.gmatch(line, "[%s]+([^%s]+)") do + table.insert(digits_in_line, i) + end + + table.insert(cpu_lines, digits_in_line) + end + end + + -- Calculate usage + ------------------------------------------------------------ + for i, line in ipairs(cpu_lines) do + + -- calculate totals + local total_new = 0 + for _, value in ipairs(line) do total_new = total_new + value end + + local active_new = total_new - (line[4] + line[5]) + + -- calculate percentage + local diff_total = total_new - (storage.cpu_total[i] or 0) + local diff_active = active_new - (storage.cpu_active[i] or 0) + + if i == 1 then diff_time_total = diff_total end + if diff_total == 0 then diff_total = 1E-6 end + + cpu_usage[i] = math.floor((diff_active / diff_total) * 100) + + -- store totals + storage.cpu_total[i] = total_new + storage.cpu_active[i] = active_new + end + + -- Format output special for redflat widgets and other system functions + ------------------------------------------------------------ + local total_usage = cpu_usage[1] + local core_usage = awful.util.table.clone(cpu_usage) + table.remove(core_usage, 1) + + return { total = total_usage, core = core_usage, diff = diff_time_total } +end + +-- Get battery level and charging status +----------------------------------------------------------------------------------------------------------------------- +function system.battery(batname) + if not batname then return end + + -- Initialzie vars + -------------------------------------------------------------------------------- + local battery = {} + local time = "N/A" + + local battery_state = { + ["Full\n"] = "↯", + ["Unknown\n"] = "⌁", + ["Charged\n"] = "↯", + ["Charging\n"] = "+", + ["Discharging\n"] = "-" + } + + local files = { + "present", "status", "charge_now", + "charge_full", "energy_now", "energy_full", + "current_now", "power_now" + } + + -- Read info + -------------------------------------------------------------------------------- + for _, v in pairs(files) do + battery[v] = redutil.read.file("/sys/class/power_supply/" .. batname .. "/" .. v) + end + + -- Check if the battery is present + ------------------------------------------------------------ + if battery.present ~= "1\n" then + return { battery_state["Unknown\n"], 0, "N/A" } + end + + -- Get state information + ------------------------------------------------------------ + local state = battery_state[battery.status] or battery_state["Unknown\n"] + local remaining, capacity + + -- Get capacity information + if battery.charge_now then remaining, capacity = battery.charge_now, battery.charge_full + elseif battery.energy_now then remaining, capacity = battery.energy_now, battery.energy_full + else return {battery_state["Unknown\n"], 0, "N/A"} + end + + -- Calculate percentage (but work around broken BAT/ACPI implementations) + ------------------------------------------------------------ + local percent = math.min(math.floor(remaining / capacity * 100), 100) + + -- Get charge information + ------------------------------------------------------------ + local rate + + if battery.current_now then rate = tonumber(battery.current_now) + elseif battery.power_now then rate = tonumber(battery.power_now) + else return {state, percent, "N/A"} + end + + -- Calculate remaining (charging or discharging) time + ------------------------------------------------------------ + if rate ~= nil and rate ~= 0 then + local timeleft + + if state == "+" then timeleft = (tonumber(capacity) - tonumber(remaining)) / tonumber(rate) + elseif state == "-" then timeleft = tonumber(remaining) / tonumber(rate) + else return {state, percent, time} + end + + -- calculate time + local hoursleft = math.floor(timeleft) + local minutesleft = math.floor((timeleft - hoursleft) * 60 ) + + time = string.format("%02d:%02d", hoursleft, minutesleft) + end + + -------------------------------------------------------------------------------- + return { state, percent, time } +end + +-- Temperature measure +----------------------------------------------------------------------------------------------------------------------- + +-- Using lm-sensors +------------------------------------------------------------ +system.lmsensors = { storage = {}, patterns = {}, delay = 1, time = 0 } + +function system.lmsensors:update(output) + for name, pat in pairs(self.patterns) do + local value = string.match(output, pat.match) + if value and pat.posthook then value = pat.posthook(value) end + value = tonumber(value) + self.storage[name] = value and { value } or { 0 } + end + self.time = os.time() +end + +function system.lmsensors:start(timeout) + if self.timer then return end + + self.timer = timer({ timeout = timeout }) + self.timer:connect_signal("timeout", function() + awful.spawn.easy_async("sensors", function(output) system.lmsensors:update(output) end) + end) + + self.timer:start() + self.timer:emit_signal("timeout") +end + +function system.lmsensors:soft_start(timeout, shift) + if self.timer then return end + + timer({ + timeout = timeout - (shift or 1), + autostart = true, + single_shot = true, + callback = function() self:start(timeout) end + }) +end + +function system.lmsensors.get(name) + if os.time() - system.lmsensors.time > system.lmsensors.delay then + local output = redutil.read.output("sensors") + system.lmsensors:update(output) + end + return system.lmsensors.storage[name] or { 0 } +end + +-- Legacy +------------------------------------------------------------ +--function system.thermal.sensors(args) +-- local args = args or "'Physical id 0'" +-- local output = redutil.read.output("sensors | grep " .. args) +-- +-- local temp = string.match(output, "%+(%d+%.%d)°[CF]") +-- +-- return temp and { math.floor(tonumber(temp)) } or { 0 } +--end +-- +--local sensors_store +-- +--function system.thermal.sensors_core(args) +-- args = args or {} +-- local index = args.index or 0 +-- +-- if args.main then sensors_store = redutil.read.output("sensors | grep Core") end +-- local line = string.match(sensors_store, "Core " .. index .."(.-)\r?\n") +-- +-- if not line then return { 0 } end +-- +-- local temp = string.match(line, "%+(%d+%.%d)°[CF]") +-- return temp and { math.floor(tonumber(temp)) } or { 0 } +--end + +-- Using hddtemp +------------------------------------------------------------ +function system.thermal.hddtemp(args) + args = args or {} + local port = args.port or "7634" + local disk = args.disk or "/dev/sda" + + local output = redutil.read.output("echo | curl --connect-timeout 1 -fsm 3 telnet://127.0.0.1:" .. port) + + for mnt, _, temp, _ in output:gmatch("|(.-)|(.-)|(.-)|(.-)|") do + if mnt == disk then + return temp and { tonumber(temp) } + end + end + + return { 0 } +end + +-- Using nvidia-settings on sysmem with optimus (bumblebee) +-- Async +------------------------------------------------------------ +function system.thermal.nvoptimus(setup) + local nvidia_on = string.find(redutil.read.output("cat /proc/acpi/bbswitch"), "ON") + if not nvidia_on then + setup({ 0, off = true }) + else + awful.spawn.easy_async_with_shell("optirun -b none nvidia-settings -c :8 -q gpucoretemp -t | tail -1", + function(output) + local value = tonumber(string.match(output, "[^\n]+")) + setup({ value or 0, off = false }) + end + ) + end +end + +-- Direct call of nvidia-smi +------------------------------------------------------------ +function system.thermal.nvsmi() + local temp = string.match( + redutil.read.output("nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader"), "%d%d" + ) + -- checks that local temp is not null then returns the convert string to number or if fails returns null + return temp and { tonumber(temp) } or { 0 } +end + +-- Using nvidia-smi on sysmem with optimus (nvidia-prime) +------------------------------------------------------------ +function system.thermal.nvprime() + local temp = 0 + local nvidia_on = string.find(redutil.read.output("prime-select query"), "nvidia") + + if nvidia_on ~= nil then + -- reuse function nvsmi + temp = system.thermal.nvsmi()[1] + end + + return { temp, off = nvidia_on == nil } +end + +-- Get info from transmission-remote client +-- This function adapted special for async reading +----------------------------------------------------------------------------------------------------------------------- +system.transmission = {} + +-- Check if transmission client running +-------------------------------------------------------------------------------- +function system.transmission.is_running(args) + local t_client = args or "transmission-gtk" + return redutil.read.output("pidof -x " .. t_client) ~= "" +end + +-- Function for torrents sorting (downloading and paused first) +-------------------------------------------------------------------------------- +function system.transmission.sort_torrent(a, b) + return a.status == "Downloading" and b.status ~= "Downloading" + or a.status == "Stopped" and b.status ~= "Stopped" and b.status ~= "Downloading" +end + +-- Function to parse 'transmission-remote -l' output +-------------------------------------------------------------------------------- +function system.transmission.parse(output, show_active_only) + + -- Initialize vars + ------------------------------------------------------------ + local torrent = { + seed = { num = 0, speed = 0 }, + dnld = { num = 0, speed = 0 }, + list = {}, + } + + -- Find state and progress for every torrent + -- and total upload and downoad speed + ------------------------------------------------------------ + --local status_pos = string.find(output, "Status") + + -- assuming "Up & Down" and "Downloading" is the same thing + output = string.gsub(output, "Up & Down", "Downloading") + + -- parse every line + for line in string.gmatch(output, "[^\r\n]+") do + + if string.sub(line, 1, 3) == "Sum" then + -- get total speed + local seed, dnld = string.match(line, "Sum:%s+[%d%.]+%s+%a+%s+([%d%.]+)%s+([%d%.]+)") + seed, dnld = tonumber(seed), tonumber(dnld) + if seed and dnld then + torrent.seed.speed, torrent.dnld.speed = seed, dnld + end + else + -- get torrent info + local prog, status, name = string.match( + line, + "%s+%d+%s+(%d+)%%%s+[%d%.]+%s%a+%s+.+%s+[%d%.]+%s+[%d%.]+%s+[%d%.]+%s+(%a+)%s+(.+)" + ) + + if prog and status then + -- if active only is selected then filter + if not show_active_only or (status == "Downloading" or status == "Seeding") then + table.insert(torrent.list, { prog = prog, status = status, name = name }) + end + + if status == "Seeding" then + torrent.seed.num = torrent.seed.num + 1 + elseif status == "Downloading" then + torrent.dnld.num = torrent.dnld.num + 1 + end + end + end + end + + -- Sort torrents + ------------------------------------------------------------ + -- do not need to sort active as transmission-remote automatically sorts + if not show_active_only then + table.sort(torrent.list, system.transmission.sort_torrent) + end + + -- Format output special for redflat desktop widget + ------------------------------------------------------------ + local sorted_prog = {} + for _, t in ipairs(torrent.list) do + table.insert(sorted_prog, { value = t.prog, text = string.format("%d%% %s", t.prog, t.name) }) + end + + return { + bars = sorted_prog, + lines = { { torrent.seed.speed, torrent.seed.num }, { torrent.dnld.speed, torrent.dnld.num } }, + alert = false + } +end + +-- Async transmission meter function +-------------------------------------------------------------------------------- +function system.transmission.info(setup, args) + local command = args.command or "transmission-remote localhost -l" + + awful.spawn.easy_async(command, function(output) + -- rather than check if an instance of transmission is running locally, check if there is actually any output + -- zero torrents or no program equates to same result + if string.len(output) > 0 then + local state = system.transmission.parse(output, args.show_active_only) + + if args.speed_only then + state.lines[1][2] = state.lines[1][1] + state.lines[2][2] = state.lines[2][1] + end + setup(state) + else + setup({ bars = {}, lines = { { 0, 0 }, { 0, 0 } }, alert = true }) + end + end) +end + +-- Get processes list and cpu and memory usage for every process +-- !!! Fixes is needed !!! +----------------------------------------------------------------------------------------------------------------------- +local proc_storage = {} + +function system.proc_info(cpu_storage) + local process = {} + local mem_page_size = 4 + + -- get processes list with ps utility + -- !!! TODO: get processes list from fs directly !!! + local output = redutil.read.output("ps -eo pid | tail -n +2") + + -- get total cpu time diff from previous call + local cpu_diff = system.cpu_usage(cpu_storage).diff + + -- handle every line in ps output + for line in string.gmatch(output, "[^\n]+") do + local pid = tonumber(line) + + -- try to get info from /proc + local stat = redutil.read.file("/proc/" .. pid .. "/stat") + + -- if process with given pid exist in /proc + if stat then + + -- get process name + local name = string.match(stat, ".+%((.+)%).+") + local proc_stat = { name } + + -- remove process name from stat data to simplify following parsing + stat = stat:gsub("%s%(.+%)", "", 1) + + -- the rest of 'stat' data can be splitted by whitespaces + -- first chunk is pid so just skip it + for m in string.gmatch(stat, "[%s]+([^%s]+)") do + table.insert(proc_stat, m) + end + + -- get memory usage (RSS) + -- !!! RSS is a very crude approximation for memory usage !!! + -- !!! TODO: find a more accurate method for real memory usage calculation !!! + local mem = proc_stat[23] * mem_page_size + + -- calculate cpu usage for process + local proc_time = proc_stat[13] + proc_stat[14] + local pcpu = (proc_time - (proc_storage[pid] or 0)) / cpu_diff + + -- save current cpu time for future + proc_storage[pid] = proc_time + + -- save results + table.insert(process, { pid = pid, name = name, mem = mem, pcpu = pcpu }) + end + end + + return process +end + +-- Output format functions +----------------------------------------------------------------------------------------------------------------------- + +-- CPU and memory usage formatted special for desktop widget +-------------------------------------------------------------------------------- +function system.dformatted.cpumem(storage) + local mem = system.memory_info() + local cores = {} + for i, v in ipairs(system.cpu_usage(storage).core) do + table.insert(cores, { value = v, text = string.format("CORE%d %s%%", i - 1, v) }) + end + + return { + bars = cores, + lines = { { mem.usep, mem.inuse }, { mem.swp.usep, mem.swp.inuse } } + } +end + +-- CPU usage formatted special for panel widget +-------------------------------------------------------------------------------- +function system.pformatted.cpu(crit) + crit = crit or 75 + local storage = { cpu_total = {}, cpu_active = {} } + + return function() + local usage = system.cpu_usage(storage).total + return { + value = usage / 100, + text = usage .. "%", + alert = usage > crit + } + end +end + +-- Memory usage formatted special for panel widget +-------------------------------------------------------------------------------- +function system.pformatted.mem(crit) + crit = crit or 75 + + return function() + local usage = system.memory_info().usep + return { + value = usage / 100, + text = usage .. "%", + alert = usage > crit + } + end +end + +-- Battery state formatted special for panel widget +-------------------------------------------------------------------------------- +function system.pformatted.bat(crit) + crit = crit or 15 + + return function(arg) + local state = system.battery(arg) + return { + value = state[2] / 100, + text = state[1] .. " " .. state[2] .. "% " .. state[3], + alert = state[2] < crit + } + end +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return system diff --git a/awesome/.config/awesome/redflat/titlebar.lua b/awesome/.config/awesome/redflat/titlebar.lua new file mode 100644 index 0000000..4964fca --- /dev/null +++ b/awesome/.config/awesome/redflat/titlebar.lua @@ -0,0 +1,442 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat titlebar -- +----------------------------------------------------------------------------------------------------------------------- +-- model titlebar with two view: light and full +-- Only simple indicators avaliable, no buttons +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ awful.titlebar v3.5.2 +------ (c) 2012 Uli Schlachter +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local error = error +local table = table +local unpack = unpack or table.unpack + +local awful = require("awful") +local drawable = require("wibox.drawable") +local color = require("gears.color") +local wibox = require("wibox") + +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local titlebar = { mt = {}, widget = {}, _index = 1, _num = 1 } +titlebar.list = setmetatable({}, { __mode = 'k' }) + + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local default_style = { + size = 8, + position = "top", + font = "Sans 12 bold", + border_margin = { 0, 0, 0, 4 }, + color = { main = "#b1222b", wibox = "#202020", gray = "#575757", + text = "#aaaaaa", icon = "#a0a0a0", urgent = "#32882d" } +} + +local default_mark_style = { + size = 20, + angle = 0, + color = default_style.color +} + +local default_button_style = { + list = { unknown = redutil.base.placeholder({ txt = "X" }) }, + color = default_style.color +} + +local positions = { "left", "right", "top", "bottom" } + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Get titlebar function +------------------------------------------------------------ +local function get_titlebar_function(c, position) + if position == "left" then return c.titlebar_left + elseif position == "right" then return c.titlebar_right + elseif position == "top" then return c.titlebar_top + elseif position == "bottom" then return c.titlebar_bottom + else + error("Invalid titlebar position '" .. position .. "'") + end +end + +-- Get titlebar model +------------------------------------------------------------ +function titlebar.get_model(c, position) + position = position or "top" + return titlebar.list[c] and titlebar.list[c][position] or nil +end + +-- Get titlebar client list +------------------------------------------------------------ +function titlebar.get_clients() + local cl = {} + for c, _ in pairs(titlebar.list) do table.insert(cl, c) end + return cl +end + + +-- Build client titlebar +----------------------------------------------------------------------------------------------------------------------- +function titlebar.new(c, style) + if not titlebar.list[c] then titlebar.list[c] = {} end + style = redutil.table.merge(default_style, style or {}) + + -- Make sure that there is never more than one titlebar for any given client + local ret + if not titlebar.list[c][style.position] then + local tfunction = get_titlebar_function(c, style.position) + local d = tfunction(c, style.size) + + local context = { client = c, position = style.position } + local base = wibox.container.margin(nil, unpack(style.border_margin)) + + ret = drawable(d, context, "redbar") + ret:_inform_visible(true) + ret:set_bg(style.color.wibox) + + -- add info to model + local model = { + layouts = {}, + current = nil, + style = style, + size = style.size, + drawable = ret, + hidden = false, + cutted = false, + tfunction = tfunction, + base = base, + } + + -- set titlebar base layout + ret:set_widget(base) + + -- save titlebar info + titlebar.list[c][style.position] = model + c:connect_signal("unmanage", function() ret:_inform_visible(false) end) + else + ret = titlebar.list[c][style.position].drawable + end + + return ret +end + +-- Titlebar functions +----------------------------------------------------------------------------------------------------------------------- + +-- Show client titlebar +------------------------------------------------------------ +function titlebar.show(c, position) + local model = titlebar.get_model(c, position) + if model and model.hidden then + model.hidden = false + model.tfunction(c, not model.cutted and model.size or 0) + end +end + +-- Hide client titlebar +------------------------------------------------------------ +function titlebar.hide(c, position) + local model = titlebar.get_model(c, position) + if model and not model.hidden then + model.tfunction(c, 0) + model.hidden = true + end +end + +-- Toggle client titlebar +------------------------------------------------------------ +function titlebar.toggle(c, position) + local all_positions = position and { position } or positions + for _, pos in ipairs(all_positions) do + local model = titlebar.get_model(c, pos) + if model then + model.tfunction(c, model.hidden and not model.cutted and model.size or 0) + model.hidden = not model.hidden + end + end +end + +-- Add titlebar view model +------------------------------------------------------------ +function titlebar.add_layout(c, position, layout, size) + local model = titlebar.get_model(c, position) + if not model then return end + + size = size or model.style.size + local l = { layout = layout, size = size } + table.insert(model.layouts, l) + if #model.layouts > titlebar._num then titlebar._num = #model.layouts end + + if not model.current then + model.base:set_widget(layout) + model.current = 1 + if model.size ~= size then + model.tfunction(c, size) + model.size = size + end + end +end + +-- Switch titlebar view model +------------------------------------------------------------ +function titlebar.switch(c, position, index) + local model = titlebar.get_model(c, position) + if not model or #model.layouts == 1 then return end + + if index then + if not model.layouts[index] then return end + model.current = index + else + model.current = (model.current < #model.layouts) and (model.current + 1) or 1 + end + local layout = model.layouts[model.current] + + model.base:set_widget(layout.layout) + if not model.cutted and not model.hidden and model.size ~= layout.size then + model.tfunction(c, layout.size) + end + model.size = layout.size +end + + +-- Titlebar mass actions +----------------------------------------------------------------------------------------------------------------------- + +-- Temporary hide client titlebar +------------------------------------------------------------ +function titlebar.cut_all(cl, position) + cl = cl or titlebar.get_clients() + --local cutted = {} + local all_positions = position and { position } or positions + + for _, pos in ipairs(all_positions) do + for _, c in ipairs(cl) do + local model = titlebar.get_model(c, pos) + if model and not model.cutted then + model.cutted = true + --table.insert(cutted, c) + if not model.hidden then model.tfunction(c, 0) end + end + end + end + --return cutted +end + +-- Restore client titlebar if it was cutted +------------------------------------------------------------ +function titlebar.restore_all(cl, position) + cl = cl or titlebar.get_clients() + local all_positions = position and { position } or positions + for _, pos in ipairs(all_positions) do + for _, c in ipairs(cl) do + local model = titlebar.get_model(c, pos) + if model and model.cutted then + model.cutted = false + if not model.hidden then model.tfunction(c, model.size) end + end + end + end +end + +-- Mass actions +------------------------------------------------------------ +function titlebar.toggle_all(cl, position) + cl = cl or titlebar.get_clients() + for _, c in pairs(cl) do titlebar.toggle(c, position) end +end + +--function titlebar.switch_all(cl, position) +-- cl = cl or titlebar.get_clients() +-- for _, c in pairs(cl) do titlebar.switch(c, position) end +--end + +function titlebar.show_all(cl, position) + cl = cl or titlebar.get_clients() + for _, c in pairs(cl) do titlebar.show(c, position) end +end + +function titlebar.hide_all(cl, position) + cl = cl or titlebar.get_clients() + for _, c in pairs(cl) do titlebar.hide(c, position) end +end + +-- Global layout switch +------------------------------------------------------------ +function titlebar.global_switch(index) + titlebar._index = index or titlebar._index + 1 + if titlebar._index > titlebar._num then titlebar._index = 1 end + + for _, c in pairs(titlebar.get_clients()) do + for _, position in ipairs(positions) do + titlebar.switch(c, position, titlebar._index) + end + end +end + + +-- Titlebar indicators +----------------------------------------------------------------------------------------------------------------------- +titlebar.mark = {} +titlebar.button = {} + +-- Client mark blank +------------------------------------------------------------ +function titlebar.mark.base(_, style) + + -- build widget + local widg = wibox.widget.base.make_widget() + widg._data = { color = style.color.gray } + widg._style = redutil.table.merge(default_mark_style, style or {}) + + -- widget setup + function widg:fit(_, _, width, height) + return width, height + end + + function widg:draw(_, cr, width, height) + local d = math.tan(self._style.angle) * height + + cr:set_source(color(self._data.color)) + cr:move_to(0, height) + cr:rel_line_to(d, - height) + cr:rel_line_to(width - d, 0) + cr:rel_line_to(-d, height) + cr:close_path() + + cr:fill() + end + + -- user function + function widg:set_active(active) + self._data.color = active and style.color.main or style.color.gray + self:emit_signal("widget::redraw_needed") + end + + -- widget width setup + widg:set_forced_width(style.size) + + return widg +end + +-- Client property indicator +------------------------------------------------------------ +function titlebar.mark.property(c, prop, style) + local w = titlebar.mark.base(c, style) + w:set_active(c[prop]) + c:connect_signal("property::" .. prop, function() w:set_active(c[prop]) end) + return w +end + +-- Client focus indicator +------------------------------------------------------------ +function titlebar.mark.focus(c, style) + local w = titlebar.mark.base(c, style) + c:connect_signal("focus", function() w:set_active(true) end) + c:connect_signal("unfocus", function() w:set_active(false) end) + return w +end + +-- Client button blank +------------------------------------------------------------ +function titlebar.button.base(icon, style, is_inactive) + style = redutil.table.merge(default_button_style, style or {}) + + -- widget + local widg = svgbox(style.list[icon] or style.list.unknown) + widg._current_color = style.color.icon + widg.is_under_mouse = false + + -- state + function widg:set_active(active) + widg._current_color = active and style.color.main or style.color.icon + widg:set_color(widg.is_under_mouse and style.color.urgent or widg._current_color) + --self:emit_signal("widget::redraw_needed") + end + + local function update(is_under_mouse) + widg.is_under_mouse = is_under_mouse + widg:set_color(widg.is_under_mouse and style.color.urgent or widg._current_color) + end + + if not is_inactive then + widg:connect_signal("mouse::enter", function() update(true) end) + widg:connect_signal("mouse::leave", function() update(false) end) + end + + widg:set_active(false) + return widg +end + +-- Client focus button +------------------------------------------------------------ +function titlebar.button.focus(c, style) + local w = titlebar.button.base("focus", style, true) + c:connect_signal("focus", function() w:set_active(true) end) + c:connect_signal("unfocus", function() w:set_active(false) end) + return w +end + +-- Client property button +------------------------------------------------------------ +function titlebar.button.property(c, prop, style) + local w = titlebar.button.base(prop, style) + w:set_active(c[prop]) + w:buttons(awful.util.table.join(awful.button({ }, 1, function() c[prop] = not c[prop] end))) + c:connect_signal("property::" .. prop, function() w:set_active(c[prop]) end) + return w +end + +-- Client close button +------------------------------------------------------------ +function titlebar.button.close(c, style) + local w = titlebar.button.base("close", style) + w:buttons(awful.util.table.join(awful.button({ }, 1, function() c:kill() end))) + return w +end + +-- Client name indicator +------------------------------------------------------------ +function titlebar.label(c, style, is_highlighted) + style = redutil.table.merge(default_style, style or {}) + local w = wibox.widget.textbox() + w:set_font(style.font) + w:set_align("center") + w._current_color = style.color.text + + local function update() + local txt = awful.util.escape(c.name or "Unknown") + w:set_markup(string.format('%s', w._current_color, txt)) + end + c:connect_signal("property::name", update) + + if is_highlighted then + c:connect_signal("focus", function() w._current_color = style.color.main; update() end) + c:connect_signal("unfocus", function() w._current_color = style.color.text; update()end) + end + + update() + return w +end + + +-- Remove from list on close +----------------------------------------------------------------------------------------------------------------------- +client.connect_signal("unmanage", function(c) titlebar.list[c] = nil end) + + +-- Config metatable to call titlebar module as function +----------------------------------------------------------------------------------------------------------------------- +function titlebar.mt:__call(...) + return titlebar.new(...) +end + +return setmetatable(titlebar, titlebar.mt) diff --git a/awesome/.config/awesome/redflat/util/base.lua b/awesome/.config/awesome/redflat/util/base.lua new file mode 100644 index 0000000..b666759 --- /dev/null +++ b/awesome/.config/awesome/redflat/util/base.lua @@ -0,0 +1,66 @@ +-- RedFlat util submodule + +local cairo = require("lgi").cairo +local gears = require("gears") +local wibox = require("wibox") +local surface = require("gears.surface") + +local base = {} + +-- Functions +----------------------------------------------------------------------------------------------------------------------- + +-- Advanced buttons setup +-- Copypasted from awful.widget.common +-- (c) 2008-2009 Julien Danjou +-------------------------------------------------------------------------------- +function base.buttons(buttons, object) + if buttons then + local btns = {} + + for _, b in ipairs(buttons) do + -- Create a proxy button object: it will receive the real + -- press and release events, and will propagate them the the + -- button object the user provided, but with the object as + -- argument. + local btn = button { modifiers = b.modifiers, button = b.button } + btn:connect_signal("press", function () b:emit_signal("press", object) end) + btn:connect_signal("release", function () b:emit_signal("release", object) end) + btns[#btns + 1] = btn + end + + return btns + end +end + +-- Create cairo surface from text (useful for themed icons replacement) +-------------------------------------------------------------------------------- +function base.placeholder(args) + args = args or {} + local tb = wibox.widget({ + markup = args.txt or "?", + align = "center", + valign = "center", + widget = wibox.widget.textbox + }) + + return surface.widget_to_surface(tb, args.width or 24, args.height or 24) +end + +-- Create rectangle cairo surface image +-------------------------------------------------------------------------------- +function base.image(width, height, geometry, color) + local image = cairo.ImageSurface.create(cairo.Format.ARGB32, width, height) + local cr = cairo.Context(image) + cr:set_source(gears.color(color or "#000000")) + cr:rectangle(geometry.x, geometry.y, geometry.width, geometry.height) + cr:fill() + + return image +end + + +-- End +----------------------------------------------------------------------------------------------------------------------- +return base + diff --git a/awesome/.config/awesome/redflat/util/cairo.lua b/awesome/.config/awesome/redflat/util/cairo.lua new file mode 100644 index 0000000..fd65afb --- /dev/null +++ b/awesome/.config/awesome/redflat/util/cairo.lua @@ -0,0 +1,35 @@ +-- RedFlat util submodule + + +local cairo = { textcentre = {} } + +-- Functions +----------------------------------------------------------------------------------------------------------------------- + +-- Draw text aligned by center +------------------------------------------------------------ +function cairo.textcentre.full(cr, coord, text) + local ext = cr:text_extents(text) + cr:move_to(coord[1] - (ext.width/2 + ext.x_bearing), coord[2] - (ext.height/2 + ext.y_bearing)) + cr:show_text(text) +end + +-- Draw text aligned by center horizontal only +------------------------------------------------------------ +function cairo.textcentre.horizontal(cr, coord, text) + local ext = cr:text_extents(text) + cr:move_to(coord[1] - (ext.width/2 + ext.x_bearing), coord[2]) + cr:show_text(text) +end + +-- Set font +------------------------------------------------------------ +function cairo.set_font(cr, font) + cr:set_font_size(font.size) + cr:select_font_face(font.font, font.slant, font.face) +end + + +-- End +----------------------------------------------------------------------------------------------------------------------- +return cairo diff --git a/awesome/.config/awesome/redflat/util/client.lua b/awesome/.config/awesome/redflat/util/client.lua new file mode 100644 index 0000000..3d71de6 --- /dev/null +++ b/awesome/.config/awesome/redflat/util/client.lua @@ -0,0 +1,54 @@ +-- RedFlat util submodule + +local awful = require("awful") +local client = { floatset = {} } + +-- Functions +----------------------------------------------------------------------------------------------------------------------- +local function size_correction(c, geometry, is_restore) + local sign = is_restore and - 1 or 1 + local bg = sign * 2 * c.border_width + + if geometry.width then geometry.width = geometry.width - bg end + if geometry.height then geometry.height = geometry.height - bg end +end + +-- Client geometry correction by border width +-------------------------------------------------------------------------------- +function client.fullgeometry(c, g) + local ng + + if g then + if g.width and g.width <= 1 then return end + if g.height and g.height <= 1 then return end + + size_correction(c, g, false) + ng = c:geometry(g) + else + ng = c:geometry() + end + + size_correction(c, ng, true) + + return ng +end + +-- Smart swap include floating layout +-------------------------------------------------------------------------------- +function client.swap(c1, c2) + local lay = awful.layout.get(c1.screen) + if awful.util.table.hasitem(client.floatset, lay) then + local g1, g2 = client.fullgeometry(c1), client.fullgeometry(c2) + + client.fullgeometry(c1, g2) + client.fullgeometry(c2, g1) + end + + c1:swap(c2) +end + + +-- End +----------------------------------------------------------------------------------------------------------------------- +return client + diff --git a/awesome/.config/awesome/redflat/util/desktop.lua b/awesome/.config/awesome/redflat/util/desktop.lua new file mode 100644 index 0000000..bcdae0c --- /dev/null +++ b/awesome/.config/awesome/redflat/util/desktop.lua @@ -0,0 +1,126 @@ +-- RedFlat util submodule + +local wibox = require("wibox") +local awful = require("awful") + +local desktop = { build = {} } + +-- Functions +----------------------------------------------------------------------------------------------------------------------- +local function sum(t, n) + n = n or #t + local s = 0 + for i = 1, n do s = s + t[i] end + return s +end + +local function wposition(grid, n, workarea, dir) + local total = sum(grid[dir]) + local full_gap = sum(grid.edge[dir]) + local gap = #grid[dir] > 1 and (workarea[dir] - total - full_gap) / (#grid[dir] - 1) or 0 + + local current = sum(grid[dir], n - 1) + local pos = grid.edge[dir][1] + (n - 1) * gap + current + + return pos +end + +-- Calculate size and position for desktop widget +------------------------------------------------------------ +function desktop.wgeometry(grid, place, workarea) + return { + x = wposition(grid, place[1], workarea, "width"), + y = wposition(grid, place[2], workarea, "height"), + width = grid.width[place[1]], + height = grid.height[place[2]] + } +end + +-- Edge constructor +------------------------------------------------------------ +function desktop.edge(direction, zone) + local edge = { area = {} } + + edge.wibox = wibox({ + bg = "#00000000", -- transparent without compositing manager + opacity = 0, -- transparent with compositing manager + ontop = true, + visible = true + }) + + edge.layout = wibox.layout.fixed[direction]() + edge.wibox:set_widget(edge.layout) + + if zone then + for i, z in ipairs(zone) do + edge.area[i] = wibox.container.margin(nil, 0, 0, z) + edge.layout:add(edge.area[i]) + end + end + + return edge +end + +-- Desktop widgets pack constructor +------------------------------------------------------------ +function desktop.build.static(objects, buttons) + for _, object in ipairs(objects) do + object.wibox = wibox({ type = "desktop", visible = true, bg = object.body.style.color.wibox }) + object.wibox:geometry(object.geometry) + object.wibox:set_widget(object.body.area) + + if buttons then object.body.area:buttons(buttons) end + end +end + +function desktop.build.dynamic(objects, s, bgimage, buttons) + s = s or mouse.screen + local bg = awful.util.file_readable(bgimage or "") and bgimage or nil + local last = { visible = true } + + -- desktop bg wibox + local dwibox = wibox({ type = "desktop", visible = true, bg = "#00000000", bgimage = bg }) + dwibox:geometry(s.workarea) + dwibox:setup({ + buttons = buttons, + layout = wibox.layout.align.horizontal + }) + + -- individual wiboxes (perfomance wisely) + for _, object in ipairs(objects) do + local clr = object.body.style and object.body.style.color and object.body.style.color.wibox or nil + + object.wibox = wibox({ type = "desktop", visible = true, bg = clr }) + object.wibox:geometry(object.geometry) + object.wibox:set_widget(object.body.area) + + if buttons then object.body.area:buttons(buttons) end + end + + -- show widgets only for empty desktop + local function update_desktop() + local clients = s:get_clients() + local visible = #clients == 0 + if visible ~= last.visible then + last.visible = visible + dwibox.visible = visible + for _, object in ipairs(objects) do object.wibox.visible = visible end + end + end + + -- better way to check visible clients? + local client_signals = { + "property::sticky", "property::minimized", "property::screen", "property::hidden", + "tagged", "untagged", "list" + } + + local tag_signals = { "property::selected", "property::activated" } + + for _, sg in ipairs(client_signals) do client.connect_signal(sg, update_desktop) end + for _, sg in ipairs(tag_signals) do tag.connect_signal(sg, update_desktop) end +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return desktop + diff --git a/awesome/.config/awesome/redflat/util/init.lua b/awesome/.config/awesome/redflat/util/init.lua new file mode 100644 index 0000000..671371a --- /dev/null +++ b/awesome/.config/awesome/redflat/util/init.lua @@ -0,0 +1,14 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = function(table_, key) + local module = rawget(table_, key) + return module or require(table_._NAME .. '.' .. key) +end + +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.util", wrequire = wrequire } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/util/key.lua b/awesome/.config/awesome/redflat/util/key.lua new file mode 100644 index 0000000..fcc195e --- /dev/null +++ b/awesome/.config/awesome/redflat/util/key.lua @@ -0,0 +1,67 @@ +-- RedFlat util submodule + +local unpack = unpack or table.unpack + +local table = table +local awful = require("awful") + +local key = {} + +key.ignored_mod = { "Unknown", "Mod2" } + +-- Functions +----------------------------------------------------------------------------------------------------------------------- + +-- Build awful keys from reflat raw keys table +------------------------------------------------------------ +function key.build(t) + local temp = {} + + for _, v in ipairs(t) do + table.insert(temp, awful.key(unpack(v))) + end + + return awful.util.table.join(unpack(temp)) +end + +-- Check if redflat raw key matched with awful prompt key +------------------------------------------------------------ +function key.match_prompt(rawkey, mod, _key) + for m, _ in pairs(mod) do + if awful.util.table.hasitem(key.ignored_mod, m) then + mod[m] = nil + break + end + end + + local modcheck = true + local count = 0 + + for k, _ in pairs(mod) do + modcheck = modcheck and awful.util.table.hasitem(rawkey[1], k) + count = count + 1 + end + + return #rawkey[1] == count and modcheck and _key:lower() == rawkey[2]:lower() +end + +-- Check if redflat raw key matched with awful prompt key +------------------------------------------------------------ +function key.match_grabber(rawkey, mod, _key) + for i, m in ipairs(mod) do + if awful.util.table.hasitem(key.ignored_mod, m) then + table.remove(mod, i) + break + end + end + + local modcheck = #mod == #rawkey[1] + for _, v in ipairs(mod) do modcheck = modcheck and awful.util.table.hasitem(rawkey[1], v) end + return modcheck and _key:lower() == rawkey[2]:lower() +end + + +-- End +----------------------------------------------------------------------------------------------------------------------- +return key + diff --git a/awesome/.config/awesome/redflat/util/placement.lua b/awesome/.config/awesome/redflat/util/placement.lua new file mode 100644 index 0000000..dd4e89f --- /dev/null +++ b/awesome/.config/awesome/redflat/util/placement.lua @@ -0,0 +1,64 @@ +-- RedFlat util submodule + +local awful = require("awful") + +local placement = {} +local direction = { x = "width", y = "height" } + +-- Functions +----------------------------------------------------------------------------------------------------------------------- +function placement.add_gap(geometry, gap) + return { + x = geometry.x + gap, + y = geometry.y + gap, + width = geometry.width - 2 * gap, + height = geometry.height - 2 * gap + } +end + +function placement.no_offscreen(object, gap, area) + local geometry = object:geometry() + local border = object.border_width + + local screen_idx = object.screen or awful.screen.getbycoord(geometry.x, geometry.y) + area = area or screen[screen_idx].workarea + if gap then area = placement.add_gap(area, gap) end + + for coord, dim in pairs(direction) do + if geometry[coord] + geometry[dim] + 2 * border > area[coord] + area[dim] then + geometry[coord] = area[coord] + area[dim] - geometry[dim] - 2*border + elseif geometry[coord] < area[coord] then + geometry[coord] = area[coord] + end + end + + object:geometry(geometry) +end + +local function centered_base(is_h, is_v) + return function(object, gap, area) + local geometry = object:geometry() + local new_geometry = {} + + local screen_idx = object.screen or awful.screen.getbycoord(geometry.x, geometry.y) + area = area or screen[screen_idx].geometry + if gap then area = placement.add_gap(area, gap) end + + if is_h then new_geometry.x = area.x + (area.width - geometry.width) / 2 - object.border_width end + if is_v then new_geometry.y = area.y + (area.height - geometry.height) / 2 - object.border_width end + + return object:geometry(new_geometry) + end +end + +placement.centered = setmetatable({}, { + __call = function(_, ...) return centered_base(true, true)(...) end +}) +placement.centered.horizontal = centered_base(true, false) +placement.centered.vertical = centered_base(false, true) + + +-- End +----------------------------------------------------------------------------------------------------------------------- +return placement + diff --git a/awesome/.config/awesome/redflat/util/read.lua b/awesome/.config/awesome/redflat/util/read.lua new file mode 100644 index 0000000..b696885 --- /dev/null +++ b/awesome/.config/awesome/redflat/util/read.lua @@ -0,0 +1,32 @@ +-- RedFlat util submodule + +local io = io +local assert = assert + +local read = {} + +-- Functions +----------------------------------------------------------------------------------------------------------------------- +function read.file(path) + local file = io.open(path) + + if file then + local output = file:read("*a") + file:close() + return output + else + return nil + end +end + +function read.output(cmd) + local file = assert(io.popen(cmd, 'r')) + local output = file:read('*all') + file:close() + + return output +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return read diff --git a/awesome/.config/awesome/redflat/util/table.lua b/awesome/.config/awesome/redflat/util/table.lua new file mode 100644 index 0000000..68fe121 --- /dev/null +++ b/awesome/.config/awesome/redflat/util/table.lua @@ -0,0 +1,45 @@ +-- RedFlat util submodule + +local awful = require("awful") + +local table_ = {} + +-- Functions +----------------------------------------------------------------------------------------------------------------------- + +-- Merge two table to new one +------------------------------------------------------------ +function table_.merge(t1, t2) + local ret = awful.util.table.clone(t1) + + for k, v in pairs(t2) do + if type(v) == "table" and ret[k] and type(ret[k]) == "table" then + ret[k] = table_.merge(ret[k], v) + else + ret[k] = v + end + end + + return ret +end + +-- Check if deep key exists +------------------------------------------------------------ +function table_.check(t, s) + local v = t + + for key in string.gmatch(s, "([^%.]+)(%.?)") do + if v[key] then + v = v[key] + else + return nil + end + end + + return v +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return table_ + diff --git a/awesome/.config/awesome/redflat/util/text.lua b/awesome/.config/awesome/redflat/util/text.lua new file mode 100644 index 0000000..3e13e13 --- /dev/null +++ b/awesome/.config/awesome/redflat/util/text.lua @@ -0,0 +1,41 @@ +-- RedFlat util submodule + +local math = math +local string = string + +local text = {} + +-- Functions +----------------------------------------------------------------------------------------------------------------------- + +-- Format string to number with minimum length +------------------------------------------------------------ +function text.oformat(v, w) + local p = math.ceil(math.log10(v)) + local prec = v <= 10 and w - 1 or p > w and 0 or w - p + return string.format('%.' .. prec .. 'f', v) +end + +-- Format output for destop widgets +------------------------------------------------------------ +function text.dformat(value, unit, w, spacer) + w = w or 3 + spacer = spacer or " " + local res = value + local add = "" + + for _, v in pairs(unit) do + if value > v[2] then + res = math.abs(value/v[2]) + add = v[1] + end + end + + return text.oformat(res, w) .. spacer .. add +end + + +-- End +----------------------------------------------------------------------------------------------------------------------- +return text + diff --git a/awesome/.config/awesome/redflat/widget/battery.lua b/awesome/.config/awesome/redflat/widget/battery.lua new file mode 100644 index 0000000..0330b54 --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/battery.lua @@ -0,0 +1,99 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat battery widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Battery charge monitoring widget +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local beautiful = require("beautiful") +local timer = require("gears.timer") + +local rednotify = require("redflat.float.notify") +local tooltip = require("redflat.float.tooltip") +local monitor = require("redflat.gauge.monitor.plain") +local redutil = require("redflat.util") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local battery = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + timeout = 60, + width = nil, + widget = monitor.new, + notify = {}, + levels = { 0.05, 0.1, 0.15 } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.battery") or {}) +end + +-- Support fucntions +----------------------------------------------------------------------------------------------------------------------- +local get_level = function(value, line) + for _, v in ipairs(line) do + if value < v then return v end + end +end + +-- Create a system monitoring widget with additional notification +----------------------------------------------------------------------------------------------------------------------- +function battery.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + args = args or {} + style = redutil.table.merge(default_style(), style or {}) + + -- Create monitor widget + -------------------------------------------------------------------------------- + local widg = style.widget(style.monitor) + widg._last = { value = 1, level = 1 } + + -- Set tooltip + -------------------------------------------------------------------------------- + widg._tp = tooltip({ objects = { widg } }, style.tooltip) + + -- Set update timer + -------------------------------------------------------------------------------- + widg._update = function() + local state = args.func(args.arg) + + widg:set_value(state.value) + widg:set_alert(state.alert) + widg._tp:set_text(state.text) + + if state.value <= widg._last.value then + local level = get_level(state.value, style.levels) + if level and level ~= widg._last.level then + widg._last.level = level + local warning = string.format("Battery charge < %.0f%%", level * 100) + rednotify:show(redutil.table.merge({ text = warning }, style.notify)) + end + else + widg._last.level = nil + end + + widg._last.value = state.value + end + + widg._timer = timer({ timeout = style.timeout }) + widg._timer:connect_signal("timeout", function() widg._update() end) + widg._timer:start() + widg._timer:emit_signal("timeout") + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call module as function +----------------------------------------------------------------------------------------------------------------------- +function battery.mt:__call(...) + return battery.new(...) +end + +return setmetatable(battery, battery.mt) diff --git a/awesome/.config/awesome/redflat/widget/binclock.lua b/awesome/.config/awesome/redflat/widget/binclock.lua new file mode 100644 index 0000000..f976635 --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/binclock.lua @@ -0,0 +1,125 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat binary clock widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Why not? +----------------------------------------------------------------------------------------------------------------------- + +local setmetatable = setmetatable +local os = os + +local wibox = require("wibox") +local beautiful = require("beautiful") +local gears = require("gears") +local color = require("gears.color") + +local tooltip = require("redflat.float.tooltip") +local redutil = require("redflat.util") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local binclock = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + width = 60, + tooltip = {}, + dot = { size = 5 }, + color = { main = "#b1222b", gray = "#575757" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.binclock") or {}) +end + + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function binary_time(num) + local binary = {} + for i = 6, 1, -1 do + local rest = num % 2 + binary[i] = rest + num = math.floor((num - rest) / 2) + end + return binary +end + + +-- Create widget. +----------------------------------------------------------------------------------------------------------------------- +function binclock.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + args = args or {} + local timeout = args.timeout or 60 + style = redutil.table.merge(default_style(), style or {}) + + -- Create widget + -------------------------------------------------------------------------------- + local widg = wibox.widget.base.make_widget() + + widg._data = { + time = { {}, {}, {} }, + width = style.width or nil + } + + -- User functions + ------------------------------------------------------------ + function widg:update() + local date = os.date('*t') + self._data.time = { binary_time(date.hour), binary_time(date.min), binary_time(date.sec) } + + self:emit_signal("widget::redraw_needed") + end + + -- Fit + ------------------------------------------------------------ + function widg:fit(_, width, height) + if self._data.width then + return math.min(width, self._data.width), height + else + return width, height + end + end + + function widg:draw(_, cr, width, height) + local dx = (width - style.dot.size) / 5 + local dy = (height - style.dot.size) / 2 + + for i = 1, 3 do + for j = 1, 6 do + cr:set_source(color(self._data.time[i][j] == 1 and style.color.main or style.color.gray )) + cr:rectangle((j -1) * dx, (i -1) * dy, style.dot.size, style.dot.size) + cr:fill() + end + end + end + + -- Set tooltip if need + -------------------------------------------------------------------------------- + local tp + if args.dateformat then tp = tooltip({ objects = { widg } }, style.tooltip) end + + -- Set update timer + -------------------------------------------------------------------------------- + local timer = gears.timer({ timeout = timeout }) + timer:connect_signal("timeout", + function() + widg:update() + if args.dateformat then tp:set_text(os.date(args.dateformat)) end + end) + timer:start() + timer:emit_signal("timeout") + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call textclock module as function +----------------------------------------------------------------------------------------------------------------------- +function binclock.mt:__call(...) + return binclock.new(...) +end + +return setmetatable(binclock, binclock.mt) diff --git a/awesome/.config/awesome/redflat/widget/init.lua b/awesome/.config/awesome/redflat/widget/init.lua new file mode 100644 index 0000000..470ae6e --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/init.lua @@ -0,0 +1,10 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat library -- +----------------------------------------------------------------------------------------------------------------------- + +local wrequire = require("redflat.util").wrequire +local setmetatable = setmetatable + +local lib = { _NAME = "redflat.widget" } + +return setmetatable(lib, { __index = wrequire }) diff --git a/awesome/.config/awesome/redflat/widget/keyboard.lua b/awesome/.config/awesome/redflat/widget/keyboard.lua new file mode 100644 index 0000000..d9a08cb --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/keyboard.lua @@ -0,0 +1,135 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat keyboard layout indicator widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Indicate and switch keybord layout +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local table = table +local awful = require("awful") +local beautiful = require("beautiful") + +local tooltip = require("redflat.float.tooltip") +local redmenu = require("redflat.menu") +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") + + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local keybd = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + icon = redutil.base.placeholder(), + micon = { blank = redutil.base.placeholder({ txt = " " }), + check = redutil.base.placeholder({ txt = "+" }) }, + menu = { color = { right_icon = "#a0a0a0" } }, + layout_color = { "#a0a0a0", "#b1222b" }, + fallback_color = "#32882d", + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.keyboard") or {}) +end + +-- Initialize layout menu +----------------------------------------------------------------------------------------------------------------------- +function keybd:init(layouts, style) + + -- initialize vars + style = redutil.table.merge(default_style(), style or {}) + self.layouts = layouts or {} + self.style = style + self.objects = {} + + -- tooltip + self.tp = tooltip({ objects = {} }, style.tooltip) + + -- construct list of layouts + local menu_items = {} + for i = 1, #layouts do + local command = function() awesome.xkb_set_layout_group(i - 1) end + table.insert(menu_items, { layouts[i], command, nil, style.micon.blank }) + end + + -- initialize menu + self.menu = redmenu({ hide_timeout = 1, theme = style.menu, items = menu_items }) + if self.menu.items[1].right_icon then + self.menu.items[1].right_icon:set_image(style.micon.check) + end + + -- update layout data + self.update = function() + local layout = awesome.xkb_get_layout_group() + 1 + for _, w in ipairs(self.objects) do + w:set_color(style.layout_color[layout] or style.fallback_color) + end + + -- update tooltip + keybd.tp:set_text(self.layouts[layout] or "Unknown") + + -- update menu + for i = 1, #self.layouts do + local mark = layout == i and style.micon.check or style.micon.blank + keybd.menu.items[i].right_icon:set_image(mark) + end + end + + awesome.connect_signal("xkb::group_changed", self.update) +end + +-- Show layout menu +----------------------------------------------------------------------------------------------------------------------- +function keybd:toggle_menu() + if not self.menu then return end + + if self.menu.wibox.visible then + self.menu:hide() + else + awful.placement.under_mouse(self.menu.wibox) + awful.placement.no_offscreen(self.menu.wibox) + self.menu:show({ coords = { x = self.menu.wibox.x, y = self.menu.wibox.y } }) + end +end + +-- Toggle layout +----------------------------------------------------------------------------------------------------------------------- +function keybd:toggle(reverse) + if not self.layouts then return end + + local layout = awesome.xkb_get_layout_group() + if reverse then + layout = layout > 0 and (layout - 1) or (#self.layouts - 1) + else + layout = layout < (#self.layouts - 1) and (layout + 1) or 0 + end + + awesome.xkb_set_layout_group(layout) +end + +-- Create a new keyboard indicator widget +----------------------------------------------------------------------------------------------------------------------- +function keybd.new(style) + + style = style or {} + if not keybd.menu then keybd:init({}) end + + local widg = svgbox(style.icon or keybd.style.icon) + widg:set_color(keybd.style.layout_color[1]) + table.insert(keybd.objects, widg) + keybd.tp:add_to_object(widg) + + keybd.update() + return widg +end + +-- Config metatable to call keybd module as function +----------------------------------------------------------------------------------------------------------------------- +function keybd.mt:__call(...) + return keybd.new(...) +end + +return setmetatable(keybd, keybd.mt) diff --git a/awesome/.config/awesome/redflat/widget/layoutbox.lua b/awesome/.config/awesome/redflat/widget/layoutbox.lua new file mode 100644 index 0000000..b9a388d --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/layoutbox.lua @@ -0,0 +1,168 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat layoutbox widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Paintbox widget used to display layout +-- Layouts menu added +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ awful.widget.layoutbox v3.5.2 +------ (c) 2009 Julien Danjou +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local ipairs = ipairs +local table = table +local awful = require("awful") +local layout = require("awful.layout") +local beautiful = require("beautiful") + +local redmenu = require("redflat.menu") +local tooltip = require("redflat.float.tooltip") +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") + + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local layoutbox = { mt = {} } + +local last_tag + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + icon = { unknown = redutil.base.placeholder() }, + micon = { blank = redutil.base.placeholder({ txt = " " }), + check = redutil.base.placeholder({ txt = "+" }) }, + name_alias = {}, + menu = { color = { right_icon = "#a0a0a0", left_icon = "#a0a0a0" } }, + color = { icon = "#a0a0a0" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.layoutbox") or {}) +end + +-- Initialize layoutbox +----------------------------------------------------------------------------------------------------------------------- +function layoutbox:init(layouts, style) + + style = style or default_style() + + -- Set tooltip + ------------------------------------------------------------ + layoutbox.tp = tooltip({}) + + -- Construct layout list + ------------------------------------------------------------ + local items = {} + for _, l in ipairs(layouts) do + local layout_name = layout.getname(l) + local icon = style.icon[layout_name] or style.icon.unknown + local text = style.name_alias[layout_name] or layout_name + table.insert(items, { text, function() layout.set (l, last_tag) end, icon, style.micon.blank }) + end + + -- Update tooltip function + ------------------------------------------------------------ + function self:update_tooltip(layout_name) + self.tp:set_text(style.name_alias[layout_name] or layout_name) + end + + -- Update menu function + ------------------------------------------------------------ + function self:update_menu(t) + local cl = awful.tag.getproperty(t, "layout") + for i, l in ipairs(layouts) do + local mark = cl == l and style.micon.check or style.micon.blank + if self.menu.items[i].right_icon then + self.menu.items[i].right_icon:set_image(mark) + end + end + end + + -- Initialize menu + ------------------------------------------------------------ + self.menu = redmenu({ theme = style.menu, items = items }) +end + +-- Show layout menu +----------------------------------------------------------------------------------------------------------------------- +function layoutbox:toggle_menu(t) + if self.menu.wibox.visible and t == last_tag then + self.menu:hide() + else + if self.menu.wibox.visible then self.menu.wibox.visible = false end + awful.placement.under_mouse(self.menu.wibox) + awful.placement.no_offscreen(self.menu.wibox) + + last_tag = t + self.menu:show({coords = {x = self.menu.wibox.x, y = self.menu.wibox.y}}) + self:update_menu(last_tag) + end +end + +-- Create a layoutbox widge +-- @param screen The screen number that the layout will be represented for +-- @param layouts List of layouts +----------------------------------------------------------------------------------------------------------------------- +function layoutbox.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + args = args or {} + local layouts = args.layouts or awful.layout.layouts + local s = args.screen or 1 + style = redutil.table.merge(default_style(), style or {}) + local w = svgbox() + w:set_color(style.color.icon) + + if not layoutbox.menu then layoutbox:init(layouts, style) end + + -- Set tooltip + -------------------------------------------------------------------------------- + layoutbox.tp:add_to_object(w) + + -- Update function + -------------------------------------------------------------------------------- + local function update() + local layout_name = layout.getname(layout.get(s)) + w:set_image(style.icon[layout_name] or style.icon.unknown) + layoutbox:update_tooltip(layout_name) + + if layoutbox.menu.wibox.visible then + layoutbox:update_menu(last_tag) + end + end + + -- Set signals + -------------------------------------------------------------------------------- + tag.connect_signal("property::selected", update) + tag.connect_signal("property::layout", update) + w:connect_signal("mouse::enter", + function() + local layout_name = layout.getname(layout.get(s)) + layoutbox:update_tooltip(layout_name) + end + ) + w:connect_signal("mouse::leave", + function() + if layoutbox.menu.hidetimer and layoutbox.menu.wibox.visible then + layoutbox.menu.hidetimer:start() + end + end + ) + + -------------------------------------------------------------------------------- + update() + return w +end + +-- Config metatable to call layoutbox module as function +----------------------------------------------------------------------------------------------------------------------- +function layoutbox.mt:__call(...) + return layoutbox.new(...) +end + +return setmetatable(layoutbox, layoutbox.mt) diff --git a/awesome/.config/awesome/redflat/widget/mail.lua b/awesome/.config/awesome/redflat/widget/mail.lua new file mode 100644 index 0000000..f9c6dc5 --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/mail.lua @@ -0,0 +1,196 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat mail widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Check if new mail available using python scripts or curl shell command +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local table = table +local tonumber = tonumber + +local awful = require("awful") +local beautiful = require("beautiful") +local timer = require("gears.timer") +local naughty = require("naughty") + +local rednotify = require("redflat.float.notify") +local tooltip = require("redflat.float.tooltip") +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") +local startup = require("redflat.startup") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +local mail = { objects = {}, mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + icon = redutil.base.placeholder(), + notify = {}, + need_notify = true, + firstrun = false, + color = { main = "#b1222b", icon = "#a0a0a0" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.mail") or {}) +end + +-- Mail check functions +----------------------------------------------------------------------------------------------------------------------- +mail.checkers = {} + +mail.checkers["script"] = { type = "spawn" } +mail.checkers["script"].action = function(args) + return args.script +end + +mail.checkers["curl_imap"] = { type = "spawn" } +mail.checkers["curl_imap"].action = function(args) + local port = args.port or 993 + local request = "-X 'STATUS INBOX (UNSEEN)'" + local head_command = "curl --connect-timeout 5 -fsm 5" + + local curl_req = string.format("%s --url imaps://%s:%s/INBOX -u %s:%s %s -k", + head_command, args.server, port, args.mail, args.password, request) + + return curl_req +end + +mail.checkers["imap"] = { type = "plain" } +mail.checkers["imap"].action = function(args) + -- require imap4 library + -- need to luarock install imap4 library + local imap4 = require "imap4" + + -- grab args or set default values + -- gmail requires 'Less secure app access' + local port = args.port or 993 + local url = args.server or "outlook.office365.com" + local mailbox = args.mailbox or "Inbox" + + -- setup connection + local status, connection = pcall(imap4, url, port, {protocol = 'tlsv1_2'}) + + if status ~= false and connection:isCapable('IMAP4rev1') then + connection:login(args.username, args.password) + + -- get information + local stat = connection:status(mailbox, {'UNSEEN'}) + + -- close connection + connection:logout() + + -- return unseen email count + return stat.UNSEEN + else + return "0" + end +end + +-- Initialize mails structure +----------------------------------------------------------------------------------------------------------------------- +function mail:init(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + args = args or {} + local count = 0 + local checks = 0 + local force_notify = false + local update_timeout = args.update_timeout or 3600 + local maillist = args.maillist or {} + style = redutil.table.merge(default_style(), style or {}) + + self.style = style + + -- Set tooltip + -------------------------------------------------------------------------------- + self.tp = tooltip(nil, style.tooltip) + self.tp:set_text("?") + + -- Update info function + -------------------------------------------------------------------------------- + local function mail_count(output) + local c = tonumber(string.match(output, "%d+")) + checks = checks + 1 + + if c then + count = count + c + if style.need_notify and (count > 0 or force_notify and checks == #maillist) then + rednotify:show(redutil.table.merge({ text = count .. " new messages" }, style.notify)) + end + end + + self.tp:set_text(count .. " new messages") + + local color = count > 0 and style.color.main or style.color.icon + for _, widg in ipairs(mail.objects) do widg:set_color(color) end + end + + self.check_updates = function(is_force) + count = 0 + checks = 0 + force_notify = is_force + + for _, cmail in ipairs(maillist) do + if mail.checkers[cmail.checker].type == "plain" then + mail_count(mail.checkers[cmail.checker].action(cmail)) + elseif mail.checkers[cmail.checker].type == "spawn" then + awful.spawn.easy_async(mail.checkers[cmail.checker].action(cmail), mail_count) + end + end + end + + -- Set update timer + -------------------------------------------------------------------------------- + self.timer = timer({ timeout = update_timeout }) + self.timer:connect_signal("timeout", function() self.check_updates() end) + self.timer:start() + + if style.firstrun and startup.is_startup then self.timer:emit_signal("timeout") end +end + + +-- Create a new mail widget +----------------------------------------------------------------------------------------------------------------------- +function mail.new(style) + + if not mail.style then + naughty.notify({ title = "Warning!", text = "Mail widget doesn't configured" }) + mail:init({}) + end + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(mail.style, style or {}) + + -- Create widget + -------------------------------------------------------------------------------- + local widg = svgbox(style.icon) + widg:set_color(style.color.icon) + table.insert(mail.objects, widg) + + -- Set tooltip + -------------------------------------------------------------------------------- + mail.tp:add_to_object(widg) + + -------------------------------------------------------------------------------- + return widg +end + +-- Update mail info for every widget +----------------------------------------------------------------------------------------------------------------------- +function mail:update(is_force) + self.check_updates(is_force) +end + +-- Config metatable to call mail module as function +----------------------------------------------------------------------------------------------------------------------- +function mail.mt:__call(...) + return mail.new(...) +end + +return setmetatable(mail, mail.mt) diff --git a/awesome/.config/awesome/redflat/widget/minitray.lua b/awesome/.config/awesome/redflat/widget/minitray.lua new file mode 100644 index 0000000..def6e8c --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/minitray.lua @@ -0,0 +1,175 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat minitray -- +----------------------------------------------------------------------------------------------------------------------- +-- Tray located on separate wibox +-- minitray:toggle() used to show/hide wibox +-- Widget with graphical counter to show how many apps placed in the system tray +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ minitray +------ http://awesome.naquadah.org/wiki/Minitray +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local table = table + +local wibox = require("wibox") +local awful = require("awful") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") +local dotcount = require("redflat.gauge.graph.dots") +local tooltip = require("redflat.float.tooltip") + +-- Initialize tables and wibox +----------------------------------------------------------------------------------------------------------------------- +local minitray = { widgets = {}, mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + dotcount = {}, + geometry = { height = 40 }, + set_position = nil, + screen_gap = 0, + border_width = 2, + color = { wibox = "#202020", border = "#575757" }, + shape = nil + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.minitray") or {}) +end + +-- Initialize minitray floating window +----------------------------------------------------------------------------------------------------------------------- +function minitray:init(style) + + -- Create wibox for tray + -------------------------------------------------------------------------------- + local wargs = { + ontop = true, + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + } + + self.wibox = wibox(wargs) + self.wibox:geometry(style.geometry) + + self.geometry = style.geometry + self.screen_gap = style.screen_gap + self.set_position = style.set_position + + -- Create tooltip + -------------------------------------------------------------------------------- + self.tp = tooltip({}, style.tooltip) + + -- Set tray + -------------------------------------------------------------------------------- + local l = wibox.layout.align.horizontal() + self.tray = wibox.widget.systray() + l:set_middle(self.tray) + self.wibox:set_widget(l) + + -- update geometry if tray icons change + self.tray:connect_signal('widget::redraw_needed', function() + self:update_geometry() + end) +end + +-- Show/hide functions for wibox +----------------------------------------------------------------------------------------------------------------------- + +-- Update Geometry +-------------------------------------------------------------------------------- +function minitray:update_geometry() + + -- Set wibox size and position + ------------------------------------------------------------ + local items = awesome.systray() + if items == 0 then items = 1 end + + self.wibox:geometry({ width = self.geometry.width or self.geometry.height * items }) + + if self.set_position then + self.set_position(self.wibox) + else + awful.placement.under_mouse(self.wibox) + end + + redutil.placement.no_offscreen(self.wibox, self.screen_gap, mouse.screen.workarea) +end + +-- Show +-------------------------------------------------------------------------------- +function minitray:show() + self:update_geometry() + self.wibox.visible = true +end + +-- Hide +-------------------------------------------------------------------------------- +function minitray:hide() + self.wibox.visible = false +end + +-- Toggle +-------------------------------------------------------------------------------- +function minitray:toggle() + if self.wibox.visible then + self:hide() + else + self:show() + end +end + +-- Create a new tray widget +-- @param args.timeout Update interval +-- @param style Settings for dotcount widget +----------------------------------------------------------------------------------------------------------------------- +function minitray.new(_, style) + + -- Initialize vars + -------------------------------------------------------------------------------- +-- args = args or {} -- usesless now, leave it be for backward compatibility and future cases + style = redutil.table.merge(default_style(), style or {}) + + -- Initialize minitray window + -------------------------------------------------------------------------------- + if not minitray.wibox then + minitray:init(style) + end + + -- Create tray widget + -------------------------------------------------------------------------------- + local widg = dotcount(style.dotcount) + table.insert(minitray.widgets, widg) + + -- Set tooltip + -------------------------------------------------------------------------------- + minitray.tp:add_to_object(widg) + + -- Set update timer + -------------------------------------------------------------------------------- + function widg:update() + local appcount = awesome.systray() + self:set_num(appcount) + minitray.tp:set_text(appcount .. " apps") + end + + minitray.tray:connect_signal('widget::redraw_needed', function() widg:update() end) + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call minitray module as function +----------------------------------------------------------------------------------------------------------------------- +function minitray.mt:__call(...) + return minitray.new(...) +end + +return setmetatable(minitray, minitray.mt) diff --git a/awesome/.config/awesome/redflat/widget/net.lua b/awesome/.config/awesome/redflat/widget/net.lua new file mode 100644 index 0000000..2de7b83 --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/net.lua @@ -0,0 +1,101 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat network widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Network speed monitoring widget +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local beautiful = require("beautiful") +local timer = require("gears.timer") + +local redutil = require("redflat.util") +local monitor = require("redflat.gauge.icon.double") +local tooltip = require("redflat.float.tooltip") +local system = require("redflat.system") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local net = { mt = {} } + +local default_args = { + speed = { up = 10*1024, down = 10*1024 }, + autoscale = true, + interface = "eth0" +} + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + widget = monitor.new, + timeout = 5, + digits = 2 + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.net") or {}) +end + +-- Create a new network widget +-- @param args.timeout Update interval +-- @param args.interface Network interface +-- @param args.autoscale Scale bars, true by default +-- @param args.speed.up Max upload speed in bytes +-- @param args.speed.down Max download speed in bytes +-- @param style Settings for doublebar widget +----------------------------------------------------------------------------------------------------------------------- +function net.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + local storage = {} + local unit = {{ "B", 1 }, { "KB", 1024 }, { "MB", 1024^2 }, { "GB", 1024^3 }} + args = redutil.table.merge(default_args, args or {}) + style = redutil.table.merge(default_style(), style or {}) + + -- Create monitor widget + -------------------------------------------------------------------------------- + local widg = style.widget(style.monitor) + + -- Set tooltip + -------------------------------------------------------------------------------- + local tp = tooltip({ objects = { widg } }, style.tooltip) + + -- Set update timer + -------------------------------------------------------------------------------- + local t = timer({ timeout = style.timeout }) + t:connect_signal("timeout", + function() + local state = system.net_speed(args.interface, storage) + widg:set_value({1,1}) + + if args.autoscale then + if state[1] > args.speed.up then args.speed.up = state[1] end + if state[2] > args.speed.down then args.speed.down = state[2] end + end + + if args.alert then + widg:set_alert(state[1] > args.alert.up or state[2] > args.alert.down) + end + + widg:set_value({ state[2]/args.speed.down, state[1]/args.speed.up }) + tp:set_text( + "↓" .. redutil.text.dformat(state[2], unit, style.digits, " ") + .. " ↑" .. redutil.text.dformat(state[1], unit, style.digits, " ") + ) + end + ) + t:start() + t:emit_signal("timeout") + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call net module as function +----------------------------------------------------------------------------------------------------------------------- +function net.mt:__call(...) + return net.new(...) +end + +return setmetatable(net, net.mt) diff --git a/awesome/.config/awesome/redflat/widget/pulse.lua b/awesome/.config/awesome/redflat/widget/pulse.lua new file mode 100644 index 0000000..39279a1 --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/pulse.lua @@ -0,0 +1,215 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat pulseaudio volume control widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Indicate and change volume level using pacmd +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ Pulseaudio volume control +------ https://github.com/orofarne/pulseaudio-awesome/blob/master/pulseaudio.lua +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local math = math +local table = table +local tonumber = tonumber +local string = string +local setmetatable = setmetatable +local awful = require("awful") +local beautiful = require("beautiful") +local gears = require("gears") +local naughty = require("naughty") + +local tooltip = require("redflat.float.tooltip") +local audio = require("redflat.gauge.audio.blue") +local rednotify = require("redflat.float.notify") +local redutil = require("redflat.util") + + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local pulse = { widgets = {}, mt = {} } +pulse.startup_time = 4 + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + notify = {}, + widget = audio.new, + audio = {} + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.pulse") or {}) +end + +local change_volume_default_args = { + down = false, + step = math.floor(65536 / 100 * 5 + 0.5), + show_notify = false +} + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function get_default_sink(args) + args = args or {} + local type_ = args.type or "sink" + + local cmd = string.format("pacmd dump | grep 'set-default-%s'", type_) + local output = redutil.read.output(cmd) + local def_sink = string.match(output, "set%-default%-%w+%s(.+)\r?\n") + + return def_sink +end + +-- Change volume level +----------------------------------------------------------------------------------------------------------------------- +function pulse:change_volume(args) + + -- initialize vars + args = redutil.table.merge(change_volume_default_args, args or {}) + local diff = args.down and -args.step or args.step + + -- get current volume + local v = redutil.read.output(string.format("pacmd dump | grep 'set-%s-volume %s'", self._type, self._sink)) + local parsed = string.match(v, "0x%x+") + + -- catch possible problems with pacmd output + if not parsed then + naughty.notify({ title = "Warning!", text = "PA widget can't parse pacmd output" }) + return + end + + local volume = tonumber(parsed) + + -- calculate new volume + local new_volume = volume + diff + + if new_volume > 65536 then + new_volume = 65536 + elseif new_volume < 0 then + new_volume = 0 + end + + -- show notify if need + if args.show_notify then + local vol = new_volume / 65536 + rednotify:show( + redutil.table.merge({ value = vol, text = string.format('%.0f', vol*100) .. "%" }, self._style.notify) + ) + end + + -- set new volume + awful.spawn(string.format("pacmd set-%s-volume %s %s", self._type, self._sink, new_volume)) + + -- update volume indicators + self:update_volume() +end + +-- Set mute +----------------------------------------------------------------------------------------------------------------------- +function pulse:mute(args) + args = args or {} + if not self._type or not self._sink then return end + + local mute = redutil.read.output(string.format("pacmd dump | grep 'set-%s-mute %s'", self._type, self._sink)) + + if string.find(mute, "no", -4) then + awful.spawn(string.format("pacmd set-%s-mute %s yes", self._type, self._sink)) + else + awful.spawn(string.format("pacmd set-%s-mute %s no", self._type, self._sink)) + end + + self:update_volume() +end + +-- Update volume level info +----------------------------------------------------------------------------------------------------------------------- +function pulse:update_volume(args) + args = args or {} + if not self._type or not self._sink then return end + + -- initialize vars + local volmax = 65536 + local volume = 0 + + -- get current volume and mute state + local v = redutil.read.output(string.format("pacmd dump | grep 'set-%s-volume %s'", self._type, self._sink)) + local m = redutil.read.output(string.format("pacmd dump | grep 'set-%s-mute %s'", self._type, self._sink)) + + if v then + local pv = string.match(v, "0x%x+") + if pv then volume = math.floor(tonumber(pv) * 100 / volmax + 0.5) end + end + + local mute = not (m and string.find(m, "no", -4)) + + -- update widgets value + self:set_value(volume / 100) + self:set_mute(mute) + self._tooltip:set_text(volume .. "%") +end + +-- Create a new pulse widget +-- @param timeout Update interval +----------------------------------------------------------------------------------------------------------------------- +function pulse.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(default_style(), style or {}) + + args = args or {} + local timeout = args.timeout or 5 + local autoupdate = args.autoupdate or false + + -- create widget + -------------------------------------------------------------------------------- + local widg = style.widget(style.audio) + gears.table.crush(widg, pulse, true) -- dangerous since widget have own methods, but let it be by now + + widg._type = args.type or "sink" + widg._sink = args.sink + widg._style = style + + table.insert(pulse.widgets, widg) + + -- Set tooltip + -------------------------------------------------------------------------------- + widg._tooltip = tooltip({ objects = { widg } }, style.tooltip) + + -- Set update timer + -------------------------------------------------------------------------------- + if autoupdate then + local t = gears.timer({ timeout = timeout }) + t:connect_signal("timeout", function() widg:update_volume() end) + t:start() + end + + -- Set startup timer + -- This is workaround if module activated bofore pulseaudio servise start + -------------------------------------------------------------------------------- + if not widg._sink then + local st = gears.timer({ timeout = 1 }) + local counter = 0 + st:connect_signal("timeout", function() + counter = counter + 1 + widg._sink = get_default_sink({ type = widg._type }) + if widg._sink then widg:update_volume() end + if counter > pulse.startup_time or widg._sink then st:stop() end + end) + st:start() + else + widg:update_volume() + end + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call pulse module as function +----------------------------------------------------------------------------------------------------------------------- +function pulse.mt:__call(...) + return pulse.new(...) +end + +return setmetatable(pulse, pulse.mt) \ No newline at end of file diff --git a/awesome/.config/awesome/redflat/widget/sysmon.lua b/awesome/.config/awesome/redflat/widget/sysmon.lua new file mode 100644 index 0000000..fc6d3c5 --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/sysmon.lua @@ -0,0 +1,73 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat sysmon widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Monitoring widget +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local beautiful = require("beautiful") +local timer = require("gears.timer") + +local monitor = require("redflat.gauge.monitor.plain") +local tooltip = require("redflat.float.tooltip") +local redutil = require("redflat.util") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local sysmon = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + timeout = 5, + width = nil, + widget = monitor.new + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.sysmon") or {}) +end + +-- Create a new cpu monitor widget +----------------------------------------------------------------------------------------------------------------------- +function sysmon.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + args = args or {} + style = redutil.table.merge(default_style(), style or {}) + + -- Create monitor widget + -------------------------------------------------------------------------------- + local widg = style.widget(style.monitor) + + -- Set tooltip + -------------------------------------------------------------------------------- + widg._tp = tooltip({ objects = { widg } }, style.tooltip) + + -- Set update timer + -------------------------------------------------------------------------------- + widg._update = function() + local state = args.func(args.arg) + widg:set_value(state.value) + widg:set_alert(state.alert) + widg._tp:set_text(state.text) + end + + widg._timer = timer({ timeout = style.timeout }) + widg._timer:connect_signal("timeout", function() widg._update() end) + widg._timer:start() + widg._timer:emit_signal("timeout") + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call module as function +----------------------------------------------------------------------------------------------------------------------- +function sysmon.mt:__call(...) + return sysmon.new(...) +end + +return setmetatable(sysmon, sysmon.mt) diff --git a/awesome/.config/awesome/redflat/widget/taglist.lua b/awesome/.config/awesome/redflat/widget/taglist.lua new file mode 100644 index 0000000..fb034d7 --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/taglist.lua @@ -0,0 +1,210 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat taglist widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Custom widget used to display tag info +-- Separators added +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ awful.widget.taglist v3.5.2 +------ (c) 2008-2009 Julien Danjou +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local pairs = pairs +local ipairs = ipairs +local table = table +local string = string +local awful = require("awful") +local wibox = require("wibox") +local beautiful = require("beautiful") +local timer = require("gears.timer") + +local redutil = require("redflat.util") +local basetag = require("redflat.gauge.tag") +local tooltip = require("redflat.float.tooltip") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local taglist = { filter = {}, mt = {} , queue = setmetatable({}, { __mode = 'k' }) } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + tag = {}, + widget = basetag.blue.new, + show_tip = false, + timeout = 0.05, + separator = nil + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.taglist") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Get info about tag +-------------------------------------------------------------------------------- +local function get_state(t) + local state = { focus = false, urgent = false, list = {} } + local client_list = t:clients() + local client_count = 0 + + for _, c in pairs(client_list) do + state.focus = state.focus or client.focus == c + state.urgent = state.urgent or c.urgent + if not c.skip_taskbar then + client_count = client_count + 1 + table.insert(state.list, { focus = client.focus == c, urgent = c.urgent, minimized = c.minimized }) + end + end + + state.active = t.selected + state.occupied = client_count > 0 and not (client_count == 1 and state.focus) + state.text = string.upper(t.name) + state.layout = awful.tag.getproperty(t, "layout") + + return state +end + +-- Generate tooltip string +-------------------------------------------------------------------------------- +local function make_tip(t) + return string.format("%s (%d apps)", t.name, #(t:clients())) +end + +-- Find all tag to be shown +-------------------------------------------------------------------------------- +local function filtrate_tags(screen, filter) + local tags = {} + for _, t in ipairs(screen.tags) do + if not awful.tag.getproperty(t, "hide") and filter(t) then + table.insert(tags, t) + end + end + return tags +end + +-- Layout composition +-------------------------------------------------------------------------------- +local function base_pack(layout, widg, i, tags, style) + layout:add(widg) + if style.separator and i < #tags then + layout:add(style.separator) + end +end + + +-- Create a new taglist widget +----------------------------------------------------------------------------------------------------------------------- +function taglist.new(args, style) + + if not taglist.queue then taglist:init() end + + -- Initialize vars + -------------------------------------------------------------------------------- + local cs = args.screen + local layout = args.layout or wibox.layout.fixed.horizontal() + local data = setmetatable({}, { __mode = 'k' }) + local filter = args.filter or taglist.filter.all + local hint = args.hint or make_tip + local pack = args.pack or base_pack + + style = redutil.table.merge(default_style(), style or {}) + + -- Set tooltip + -------------------------------------------------------------------------------- + if not taglist.tp then taglist.tp = tooltip() end + + -- Update function + -------------------------------------------------------------------------------- + local update = function(s) + if s ~= cs then return end + local tags = filtrate_tags(s, filter) + + -- Construct taglist + ------------------------------------------------------------ + layout:reset() + for i, t in ipairs(tags) do + local cache = data[t] + local widg + + -- use existing widgets or create new one + if cache then + widg = cache + else + widg = style.widget(style.tag) + if args.buttons then widg:buttons(redutil.base.buttons(args.buttons, t)) end + data[t] = widg + + -- set optional tooltip (what about removing?) + if style.show_tip then + taglist.tp:add_to_object(widg) + widg:connect_signal("mouse::enter", function() taglist.tp:set_text(widg.tip) end) + end + end + + -- set tag state info to widget + local state = get_state(t) + widg:set_state(state) + widg.tip = hint(t) + + -- add widget and separator to base layout + pack(layout, widg, i, tags, style) + end + ------------------------------------------------------------ + + if taglist.queue[s] and taglist.queue[s].started then taglist.queue[s]:stop() end + end + + -- Create timer to prevent multiply call + -------------------------------------------------------------------------------- + taglist.queue[cs] = timer({ timeout = style.timeout }) + taglist.queue[cs]:connect_signal("timeout", function() update(cs) end) + + local uc = function (c) if taglist.queue[c.screen] then taglist.queue[c.screen]:again() end end + local ut = function (t) if taglist.queue[t.screen] then taglist.queue[t.screen]:again() end end + + -- Signals setup + -------------------------------------------------------------------------------- + local tag_signals = { + "property::selected", "property::icon", "property::hide", + "property::activated", "property::name", "property::screen", + "property::index", "property::layout" + } + local client_signals = { + "focus", "unfocus", "property::urgent", + "tagged", "untagged", "unmanage" + } + + for _, sg in ipairs(tag_signals) do awful.tag.attached_connect_signal(nil, sg, ut) end + for _, sg in ipairs(client_signals) do client.connect_signal(sg, uc) end + + client.connect_signal("property::screen", function() update(cs) end) -- dirty + + -------------------------------------------------------------------------------- + update(cs) -- create taglist widget + return layout -- return taglist widget +end + +-- Filtering functions +-- @param t The awful.tag +-- @param args unused list of extra arguments +----------------------------------------------------------------------------------------------------------------------- +function taglist.filter.noempty(t) -- to include all nonempty tags on the screen. + return #t:clients() > 0 or t.selected +end + +function taglist.filter.all() -- to include all tags on the screen. + return true +end + +-- Config metatable to call taglist module as function +----------------------------------------------------------------------------------------------------------------------- +function taglist.mt:__call(...) + return taglist.new(...) +end + +return setmetatable(taglist, taglist.mt) diff --git a/awesome/.config/awesome/redflat/widget/tasklist.lua b/awesome/.config/awesome/redflat/widget/tasklist.lua new file mode 100644 index 0000000..b434901 --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/tasklist.lua @@ -0,0 +1,895 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat tasklist widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Custom widget used to show apps, see redtask.lua for more info +-- No icons; labels can be customized in beautiful theme file +-- Same class clients grouped into one object +-- Pop-up tooltip with task names +-- Pop-up menu with window state info +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ awful.widget.tasklist v3.5.2 +------ (c) 2008-2009 Julien Danjou +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local pairs = pairs +local ipairs = ipairs +local table = table +local string = string +local math = math +local unpack = unpack or table.unpack + +local beautiful = require("beautiful") +local tag = require("awful.tag") +local awful = require("awful") +local wibox = require("wibox") +local timer = require("gears.timer") + +local basetask = require("redflat.gauge.tag.blue") +local redutil = require("redflat.util") +local separator = require("redflat.gauge.separator") +local redmenu = require("redflat.menu") +local svgbox = require("redflat.gauge.svgbox") +local dfparser = require("redflat.service.dfparser") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local redtasklist = { filter = {}, winmenu = {}, tasktip = {}, action = {}, mt = {}, } + +local last = { + client = nil, + group = nil, + client_list = nil, + screen = mouse.screen, + tag_screen = mouse.screen, + screen_clients = {} +} + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + appnames = {}, + iconnames = {}, + widget = basetask.new, + width = 40, + char_digit = 3, + need_group = true, + parser = {}, + icons = {}, + timeout = 0.05, + custom_icon = false, + task = {}, + task_margin = { 5, 5, 0, 0 } + } + style.winmenu = { + icon = { unknown = redutil.base.placeholder() }, + micon = { blank = redutil.base.placeholder({ txt = " " }), + check = redutil.base.placeholder({ txt = "+" }) }, + layout_icon = { unknown = redutil.base.placeholder() }, + titleline = { font = "Sans 16 bold", height = 35 }, + stateline = { height = 35 }, + state_iconsize = { width = 20, height = 20 }, + separator = { marginh = { 3, 3, 5, 5 } }, + tagmenu = { icon_margin = { 2, 2, 2, 2 } }, + hide_action = { min = true, + move = true, + max = false, + add = false, + floating = false, + sticky = false, + ontop = false, + below = false, + maximized = false }, + color = { main = "#b1222b", icon = "#a0a0a0", gray = "#404040" } + } + style.tasktip = { + border_width = 2, + margin = { 10, 10, 5, 5 }, + timeout = 0.5, + sl_highlight = false, -- single line highlight + color = { border = "#575757", text = "#aaaaaa", main = "#b1222b", highlight = "#eeeeee", + wibox = "#202020", gray = "#575757", urgent = "#32882d" }, + shape = nil + + } + style.winmenu.menu = { + ricon_margin = { 2, 2, 2, 2 }, + hide_timeout = 1, + -- color = { submenu_icon = "#a0a0a0", right_icon = "#a0a0a0", left_icon = "#a0a0a0" } + nohide = true + } + + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.tasklist") or {}) +end + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- + +-- Get info about client group +-------------------------------------------------------------------------------- +local function get_state(c_group, style) + + style = style or {} + local names = style.appnames or {} + local chars = style.char_digit + + local state = { focus = false, urgent = false, minimized = true, list = {} } + + for _, c in pairs(c_group) do + state.focus = state.focus or client.focus == c + state.urgent = state.urgent or c.urgent + state.minimized = state.minimized and c.minimized + + table.insert(state.list, { focus = client.focus == c, urgent = c.urgent, minimized = c.minimized }) + end + + local class = c_group[1].class or "Undefined" + state.text = names[class] or string.upper(string.sub(class, 1, chars)) + state.num = #c_group + state.icon = style.custom_icon and style.icons[style.iconnames[class] or string.lower(class)] + + return state +end + +-- Function to build item list for submenu +-------------------------------------------------------------------------------- +local function tagmenu_items(action, style) + local items = {} + for _, t in ipairs(last.screen.tags) do + if not awful.tag.getproperty(t, "hide") then + table.insert( + items, + { t.name, function() action(t) end, style.micon.blank, style.micon.blank } + ) + end + end + return items +end + +-- Function to rebuild the submenu entries according to current screen's tags +-------------------------------------------------------------------------------- +local function tagmenu_rebuild(menu, submenu_index, style) + for _, index in ipairs(submenu_index) do + local new_items + if index == 1 then + new_items = tagmenu_items(redtasklist.winmenu.movemenu_action, style) + else + new_items = tagmenu_items(redtasklist.winmenu.addmenu_action, style) + end + menu.items[index].child:replace_items(new_items) + end +end + +-- Function to update tag submenu icons +-------------------------------------------------------------------------------- +local function tagmenu_update(c, menu, submenu_index, style) + -- if the screen has changed (and thus the tags) since the last time the + -- tagmenu was built, rebuild it first + if last.tag_screen ~= mouse.screen then + tagmenu_rebuild(menu, submenu_index, style) + last.tag_screen = mouse.screen + end + for k, t in ipairs(last.screen.tags) do + if not awful.tag.getproperty(t, "hide") then + + -- set layout icon for every tag + local l = awful.layout.getname(awful.tag.getproperty(t, "layout")) + + local check_icon = style.micon.blank + if c then + local client_tags = c:tags() + check_icon = awful.util.table.hasitem(client_tags, t) and style.micon.check or check_icon + end + + for _, index in ipairs(submenu_index) do + local submenu = menu.items[index].child + if submenu.items[k].icon then + submenu.items[k].icon:set_image(style.layout_icon[l] or style.layout_icon.unknown) + end + + -- set "checked" icon if tag active for given client + -- otherwise set empty icon + if c then + if submenu.items[k].right_icon then + submenu.items[k].right_icon:set_image(check_icon) + end + end + + -- update position of any visible submenu + if submenu.wibox.visible then submenu:show() end + end + end + end +end + +-- Function to construct menu line with state icons +-------------------------------------------------------------------------------- +local function state_line_construct(state_icons, setup_layout, style) + local stateboxes = {} + + for i, v in ipairs(state_icons) do + -- create widget + stateboxes[i] = svgbox(v.icon) + stateboxes[i]:set_forced_width(style.state_iconsize.width) + stateboxes[i]:set_forced_height(style.state_iconsize.height) + + -- set widget in line + local l = wibox.layout.align.horizontal() + l:set_expand("outside") + l:set_second(stateboxes[i]) + setup_layout:add(l) + + -- set mouse action + stateboxes[i]:buttons(awful.util.table.join(awful.button({}, 1, + function() + v.action() + stateboxes[i]:set_color(v.indicator(last.client) and style.color.main or style.color.gray) + end + ))) + end + + return stateboxes +end + +-- Calculate menu position +-- !!! Bad code is here !!! +-- !!! TODO: make variant when panel place on top of screen !!! +-------------------------------------------------------------------------------- +local function coords_calc(menu, tip_wibox, gap) + local coords = {} + + if gap then + coords.x = tip_wibox.x + (tip_wibox.width - menu.wibox.width) / 2 + coords.y = tip_wibox.y - menu.wibox.height - 2 * menu.wibox.border_width + tip_wibox.border_width + gap + else + coords = mouse.coords() + coords.x = coords.x - menu.wibox.width / 2 - menu.wibox.border_width + end + + return coords +end + +-- Create tasklist object +-------------------------------------------------------------------------------- +local function new_task(c_group, style) + local task = {} + task.widg = style.widget(style.task) + task.group = c_group + task.l = wibox.container.margin(task.widg, unpack(style.task_margin)) + + task.widg:connect_signal("mouse::enter", function() redtasklist.tasktip:show(task.group) end) + task.widg:connect_signal("mouse::leave", + function() + redtasklist.tasktip.hidetimer:start() + if not redtasklist.winmenu.menu.hidetimer.started then redtasklist.winmenu.menu.hidetimer:start() end + end + ) + return task +end + +-- Find all clients to be shown +-------------------------------------------------------------------------------- +local function visible_clients(filter, screen) + local clients = {} + + for _, c in ipairs(client.get()) do + local hidden = c.skip_taskbar or c.hidden or c.type == "splash" or c.type == "dock" or c.type == "desktop" + + if not hidden and filter(c, screen) then + table.insert(clients, c) + end + end + + return clients +end + +-- Split tasks into groups by class +-------------------------------------------------------------------------------- +local function group_task(clients, need_group) + local client_groups = {} + local classes = {} + + for _, c in ipairs(clients) do + if need_group then + local index = awful.util.table.hasitem(classes, c.class or "Undefined") + if index then + table.insert(client_groups[index], c) + else + table.insert(classes, c.class or "Undefined") + table.insert(client_groups, { c }) + end + else + table.insert(client_groups, { c }) + end + end + + return client_groups +end + +-- Form ordered client list special for switch function +-------------------------------------------------------------------------------- +local function sort_list(client_groups) + local list = {} + + for _, g in ipairs(client_groups) do + for _, c in ipairs(g) do + if not c.minimized then table.insert(list, c) end + end + end + + return list +end + +-- Create tasktip line +-------------------------------------------------------------------------------- +local function tasktip_line(style) + local line = {} + + -- text + line.tb = wibox.widget.textbox() + + -- horizontal align wlayout + local horizontal = wibox.layout.fixed.horizontal() + horizontal:add(wibox.container.margin(line.tb, unpack(style.margin))) + + -- background for client state mark + line.field = wibox.container.background(horizontal) + + -- tasktip line metods + function line:set_text(text) + line.tb:set_markup(text) + + if style.max_width then + line.tb:set_ellipsize("middle") + local _, line_h = line.tb:get_preferred_size() + line.tb:set_forced_height(line_h) + line.tb:set_forced_width(style.max_width) + end + + line.field:set_fg(style.color.text) + line.field:set_bg(style.color.wibox) + end + + function line:mark_focused() + line.field:set_bg(style.color.main) + line.field:set_fg(style.color.highlight) + end + + function line:mark_urgent() + line.field:set_bg(style.color.urgent) + line.field:set_fg(style.color.highlight) + end + + function line:mark_minimized() + line.field:set_fg(style.color.gray) + end + + return line +end + +-- Switch task +-------------------------------------------------------------------------------- +local function switch_focus(list, is_reverse) + local diff = is_reverse and - 1 or 1 + + if #list == 0 then return end + + local index = (awful.util.table.hasitem(list, client.focus) or 1) + diff + + if index < 1 then index = #list + elseif index > #list then index = 1 + end + + -- set focus to new task + client.focus = list[index] + list[index]:raise() +end + +local function client_group_sort_by_class(a, b) + return (a[1].class or "Undefined") < (b[1].class or "Undefined") +end + +-- Build or update tasklist. +-------------------------------------------------------------------------------- +local function tasklist_construct(client_groups, layout, data, buttons, style) + + layout:reset() + local task_full_width = style.width + style.task_margin[1] + style.task_margin[2] + layout:set_max_widget_size(task_full_width) + layout:set_forced_width(task_full_width * #client_groups) + + -- construct tasklist + for i, c_group in ipairs(client_groups) do + local task + + -- use existing widgets or create new + if data[i] then + task = data[i] + task.group = c_group + else + task = new_task(c_group, style) + data[i] = task + end + + -- set info and buttons to widget + local state = get_state(c_group, style) + task.widg:set_state(state) + task.widg:buttons(redutil.base.buttons(buttons, { group = c_group })) + + -- construct + layout:add(task.l) + end +end + +-- Construct or update tasktip +-------------------------------------------------------------------------------- +local function construct_tasktip(c_group, layout, data, buttons, style) + layout:reset() + local tb_w, tb_h + local tip_width = 1 + + for i, c in ipairs(c_group) do + local line + + -- use existing widgets or create new + if data[i] then + line = data[i] + else + line = tasktip_line(style) + data[i] = line + end + + line:set_text(awful.util.escape(c.name) or "Untitled") + tb_w, tb_h = line.tb:get_preferred_size() + if line.tb.forced_width then + tb_w = math.min(line.tb.forced_width, tb_w) + end + + -- set state highlight only for grouped tasks + if #c_group > 1 or style.sl_highlight then + local state = get_state({ c }) + + if state.focus then line:mark_focused() end + if state.minimized then line:mark_minimized() end + if state.urgent then line:mark_urgent() end + end + + -- set buttons + local gap = (i - 1) * (tb_h + style.margin[3] + style.margin[4]) + if buttons then line.field:buttons(redutil.base.buttons(buttons, { group = { c }, gap = gap })) end + + -- add line widget to tasktip layout + tip_width = math.max(tip_width, tb_w) + layout:add(line.field) + end + + -- return tasktip size + return { + width = tip_width + style.margin[1] + style.margin[2], + height = #c_group * (tb_h + style.margin[3] + style.margin[4]) + } +end + + +-- Initialize window menu widget +----------------------------------------------------------------------------------------------------------------------- +function redtasklist.winmenu:init(style) + + -- Window managment functions + -------------------------------------------------------------------------------- + self.hide_check = function(action) + if style.hide_action[action] then self.menu:hide() end + end + + local close = function() last.client:kill(); self.menu:hide() end + local minimize = function() last.client.minimized = not last.client.minimized; self.hide_check("min") end + -- local maximize = function() last.client.maximized = not last.client.maximized; self.hide_check("max")end + + -- Create array of state icons + -- associate every icon with action and state indicator + -------------------------------------------------------------------------------- + local function icon_table_ganerator(property) + return { + icon = style.icon[property] or style.icon.unknown, + action = function() last.client[property] = not last.client[property]; self.hide_check(property) end, + indicator = function(c) return c[property] end + } + end + + local state_icons = { + icon_table_ganerator("floating"), + icon_table_ganerator("sticky"), + icon_table_ganerator("ontop"), + icon_table_ganerator("below"), + icon_table_ganerator("maximized"), + } + + -- Construct menu + -------------------------------------------------------------------------------- + + -- Client class line (menu title) construction + ------------------------------------------------------------ + local classbox = wibox.widget.textbox() + classbox:set_font(style.titleline.font) + classbox:set_align ("center") + + local classline = wibox.container.constraint(classbox, "exact", nil, style.titleline.height) + + -- Window state line construction + ------------------------------------------------------------ + + -- layouts + local stateline_horizontal = wibox.layout.flex.horizontal() + local stateline_vertical = wibox.layout.align.vertical() + stateline_vertical:set_second(stateline_horizontal) + stateline_vertical:set_expand("outside") + local stateline = wibox.container.constraint(stateline_vertical, "exact", nil, style.stateline.height) + + -- set all state icons in line + local stateboxes = state_line_construct(state_icons, stateline_horizontal, style) + + -- update function for state icons + local function stateboxes_update(c, icons, boxes) + for i, v in ipairs(icons) do + boxes[i]:set_color(v.indicator(c) and style.color.main or style.color.gray) + end + end + + -- Separators config + ------------------------------------------------------------ + local menusep = { widget = separator.horizontal(style.separator) } + + -- Construct tag submenus ("move" and "add") + ------------------------------------------------------------ + + -- menu item actions + self.movemenu_action = function(t) + last.client:move_to_tag(t); awful.layout.arrange(t.screen); self.hide_check("move") + end + + self.addmenu_action = function(t) + last.client:toggle_tag(t); awful.layout.arrange(t.screen); self.hide_check("add") + end + + -- menu items + local movemenu_items = tagmenu_items(self.movemenu_action, style) + local addmenu_items = tagmenu_items(self.addmenu_action, style) + + -- Create menu + ------------------------------------------------------------ + self.menu = redmenu({ + theme = style.menu, + items = { + { widget = classline }, + menusep, + { "Move to tag", { items = movemenu_items, theme = style.tagmenu } }, + { "Add to tag", { items = addmenu_items, theme = style.tagmenu } }, + + { "Minimize", minimize, nil, style.icon.minimize or style.icon.unknown }, + { "Close", close, nil, style.icon.close or style.icon.unknown }, + menusep, + { widget = stateline, focus = true } + } + }) + + -- Widget update functions + -------------------------------------------------------------------------------- + function self:update(c) + if self.menu.wibox.visible then + classbox:set_text(c.class or "Undefined") + stateboxes_update(c, state_icons, stateboxes) + tagmenu_update(c, self.menu, { 1, 2 }, style) + end + end + + -- Signals setup + -- Signals which affect window menu only + -- and does not connected to tasklist + -------------------------------------------------------------------------------- + local client_signals = { + "property::ontop", "property::floating", "property::below", "property::maximized", + } + for _, sg in ipairs(client_signals) do + client.connect_signal(sg, function() self:update(last.client) end) + end +end + +-- Show window menu widget +----------------------------------------------------------------------------------------------------------------------- +function redtasklist.winmenu:show(c_group, gap) + + -- do nothing if group of task received + -- show state only for single task + if #c_group > 1 then return end + + local c = c_group[1] + + -- toggle menu + if self.menu.wibox.visible and c == last.client and mouse.screen == last.screen then + self.menu:hide() + else + last.client = c + last.screen = mouse.screen + self.menu:show({ coords = coords_calc(self.menu, redtasklist.tasktip.wibox, gap) }) + + if self.menu.hidetimer.started then self.menu.hidetimer:stop() end + self:update(c) + end +end + + +-- Initialize a tasktip +----------------------------------------------------------------------------------------------------------------------- +function redtasklist.tasktip:init(buttons, style) + + local tippat = {} + + -- Create wibox + -------------------------------------------------------------------------------- + self.wibox = wibox({ + type = "tooltip", + bg = style.color.wibox, + border_width = style.border_width, + border_color = style.color.border, + shape = style.shape + }) + + self.wibox.ontop = true + + self.layout = wibox.layout.fixed.vertical() + self.wibox:set_widget(self.layout) + + -- Update function + -------------------------------------------------------------------------------- + function self:update(c_group) + + if not self.wibox.visible then return end + + local wg = construct_tasktip(c_group, self.layout, tippat, buttons, style) + self.wibox:geometry(wg) + end + + -- Set tasktip autohide timer + -------------------------------------------------------------------------------- + self.hidetimer = timer({ timeout = style.timeout }) + self.hidetimer:connect_signal("timeout", + function() + self.wibox.visible = false + if self.hidetimer.started then self.hidetimer:stop() end + end + ) + self.hidetimer:emit_signal("timeout") + + -- Signals setup + -------------------------------------------------------------------------------- + self.wibox:connect_signal("mouse::enter", + function() + if self.hidetimer.started then self.hidetimer:stop() end + end + ) + + self.wibox:connect_signal("mouse::leave", + function() + self.hidetimer:start() + if not redtasklist.winmenu.menu.hidetimer.started then redtasklist.winmenu.menu.hidetimer:start() end + end + ) +end + +-- Show tasktip +----------------------------------------------------------------------------------------------------------------------- +function redtasklist.tasktip:show(c_group) + + if self.hidetimer.started then self.hidetimer:stop() end + + if not self.wibox.visible or last.group ~= c_group then + self.wibox.visible = true + last.group = c_group + self:update(c_group) + awful.placement.under_mouse(self.wibox) + awful.placement.no_offscreen(self.wibox) + end +end + +-- Create a new tasklist widget +----------------------------------------------------------------------------------------------------------------------- +function redtasklist.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + local cs = args.screen + local filter = args.filter or redtasklist.filter.currenttags + + style = redutil.table.merge(default_style(), style or {}) + if style.custom_icon then style.icons = dfparser.icon_list(style.parser) end + if style.task.width then style.width = style.task.width end + + redtasklist.winmenu:init(style.winmenu) + redtasklist.tasktip:init(args.buttons, style.tasktip) + + local tasklist = wibox.layout.flex.horizontal() + local data = {} + + -- Update tasklist + -------------------------------------------------------------------------------- + + -- Tasklist update function + ------------------------------------------------------------ + local function tasklist_update() + local clients = visible_clients(filter, cs) + local client_groups = group_task(clients, style.need_group) + + table.sort(client_groups, client_group_sort_by_class) + last.screen_clients[cs] = sort_list(client_groups) + + tasklist_construct(client_groups, tasklist, data, args.buttons, style) + end + + -- Full update including pop-up widgets + ------------------------------------------------------------ + local function update() + tasklist_update() + redtasklist.tasktip:update(last.group) + redtasklist.winmenu:update(last.client) + end + + -- Create timer to prevent multiply call + -------------------------------------------------------------------------------- + tasklist.queue = timer({ timeout = style.timeout }) + tasklist.queue:connect_signal("timeout", function() update(cs); tasklist.queue:stop() end) + + -- Signals setup + -------------------------------------------------------------------------------- + local client_signals = { + "property::urgent", "property::sticky", "property::minimized", + "property::name", " property::icon", "property::skip_taskbar", + "property::screen", "property::hidden", + "tagged", "untagged", "list", "focus", "unfocus" + } + + local tag_signals = { "property::selected", "property::activated" } + + -- for _, sg in ipairs(client_signals) do client.connect_signal(sg, update) end + -- for _, sg in ipairs(tag_signals) do tag.attached_connect_signal(cs, sg, update) end + for _, sg in ipairs(client_signals) do client.connect_signal(sg, function() tasklist.queue:again() end) end + for _, sg in ipairs(tag_signals) do tag.attached_connect_signal(cs, sg, function() tasklist.queue:again() end) end + + -- force hide pop-up widgets if any client was closed + -- because last vars may be no actual anymore + client.connect_signal("unmanage", + function() + tasklist_update() + redtasklist.tasktip.wibox.visible = false + redtasklist.winmenu.menu:hide() + last.client = nil + last.group = nil + end + ) + + -- Construct + -------------------------------------------------------------------------------- + update() + + return tasklist +end + +-- Mouse action functions +----------------------------------------------------------------------------------------------------------------------- + +-- focus/minimize +function redtasklist.action.select(args) + args = args or {} + local state = get_state(args.group) + + if state.focus then + for _, c in ipairs(args.group) do c.minimized = true end + else + if state.minimized then + for _, c in ipairs(args.group) do c.minimized = false end + end + + client.focus = args.group[1] + args.group[1]:raise() + end +end + +-- close all in group +function redtasklist.action.close(args) + args = args or {} + for _, c in ipairs(args.group) do c:kill() end +end + +-- show/close winmenu +function redtasklist.action.menu(args) + args = args or {} + redtasklist.winmenu:show(args.group, args.gap) +end + +-- switch to next task +function redtasklist.action.switch_next() + switch_focus(last.screen_clients[mouse.screen]) +end + +-- switch to previous task +function redtasklist.action.switch_prev() + switch_focus(last.screen_clients[mouse.screen], true) +end + + +-- Filtering functions +-- @param c The client +-- @param screen The screen we are drawing on +----------------------------------------------------------------------------------------------------------------------- + +-- To include all clients +-------------------------------------------------------------------------------- +function redtasklist.filter.allscreen() + return true +end + +-- To include the clients from all tags on the screen +-------------------------------------------------------------------------------- +function redtasklist.filter.alltags(c, screen) + return c.screen == screen +end + +-- To include only the clients from currently selected tags +-------------------------------------------------------------------------------- +function redtasklist.filter.currenttags(c, screen) + if c.screen ~= screen then return false end + if c.sticky then return true end + + local tags = screen.tags + + for _, t in ipairs(tags) do + if t.selected then + local ctags = c:tags() + + for _, v in ipairs(ctags) do + if v == t then return true end + end + end + end + + return false +end + +-- To include only the minimized clients from currently selected tags +-------------------------------------------------------------------------------- +function redtasklist.filter.minimizedcurrenttags(c, screen) + if c.screen ~= screen then return false end + if not c.minimized then return false end + if c.sticky then return true end + + local tags = screen.tags + + for _, t in ipairs(tags) do + if t.selected then + local ctags = c:tags() + + for _, v in ipairs(ctags) do + if v == t then return true end + end + end + end + + return false +end + +-- To include only the currently focused client +-------------------------------------------------------------------------------- +function redtasklist.filter.focused(c, screen) + return c.screen == screen and client.focus == c +end + +-- Config metatable to call redtasklist module as function +----------------------------------------------------------------------------------------------------------------------- +function redtasklist.mt:__call(...) + return redtasklist.new(...) +end + +return setmetatable(redtasklist, redtasklist.mt) diff --git a/awesome/.config/awesome/redflat/widget/textclock.lua b/awesome/.config/awesome/redflat/widget/textclock.lua new file mode 100644 index 0000000..69cf34b --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/textclock.lua @@ -0,0 +1,80 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat clock widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Text clock widget with date in tooltip (optional) +----------------------------------------------------------------------------------------------------------------------- +-- Some code was taken from +------ awful.widget.textclock v3.5.2 +------ (c) 2009 Julien Danjou +----------------------------------------------------------------------------------------------------------------------- + +local setmetatable = setmetatable +local os = os +local textbox = require("wibox.widget.textbox") +local beautiful = require("beautiful") +local gears = require("gears") + +local tooltip = require("redflat.float.tooltip") +local redutil = require("redflat.util") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local textclock = { mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + font = "Sans 12", + tooltip = {}, + color = { text = "#aaaaaa" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.textclock") or {}) +end + +-- Create a textclock widget. It draws the time it is in a textbox. +-- @param format The time format. Default is " %a %b %d, %H:%M ". +-- @param timeout How often update the time. Default is 60. +-- @return A textbox widget +----------------------------------------------------------------------------------------------------------------------- +function textclock.new(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + args = args or {} + local timeformat = args.timeformat or " %a %b %d, %H:%M " + local timeout = args.timeout or 60 + style = redutil.table.merge(default_style(), style or {}) + + -- Create widget + -------------------------------------------------------------------------------- + local widg = textbox() + widg:set_font(style.font) + + -- Set tooltip if need + -------------------------------------------------------------------------------- + local tp + if args.dateformat then tp = tooltip({ objects = { widg } }, style.tooltip) end + + -- Set update timer + -------------------------------------------------------------------------------- + local timer = gears.timer({ timeout = timeout }) + timer:connect_signal("timeout", + function() + widg:set_markup('' .. os.date(timeformat) .. "") + if args.dateformat then tp:set_text(os.date(args.dateformat)) end + end) + timer:start() + timer:emit_signal("timeout") + + -------------------------------------------------------------------------------- + return widg +end + +-- Config metatable to call textclock module as function +----------------------------------------------------------------------------------------------------------------------- +function textclock.mt:__call(...) + return textclock.new(...) +end + +return setmetatable(textclock, textclock.mt) diff --git a/awesome/.config/awesome/redflat/widget/updates.lua b/awesome/.config/awesome/redflat/widget/updates.lua new file mode 100644 index 0000000..779a2e0 --- /dev/null +++ b/awesome/.config/awesome/redflat/widget/updates.lua @@ -0,0 +1,413 @@ +----------------------------------------------------------------------------------------------------------------------- +-- RedFlat updates widget -- +----------------------------------------------------------------------------------------------------------------------- +-- Show if system updates available using apt-get +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +----------------------------------------------------------------------------------------------------------------------- +local setmetatable = setmetatable +local table = table +local string = string +local os = os +local unpack = unpack or table.unpack + +local beautiful = require("beautiful") +local wibox = require("wibox") +local awful = require("awful") +local timer = require("gears.timer") + +local rednotify = require("redflat.float.notify") +local tooltip = require("redflat.float.tooltip") +local redutil = require("redflat.util") +local svgbox = require("redflat.gauge.svgbox") +local separator = require("redflat.gauge.separator") +local redtip = require("redflat.float.hotkeys") +local startup = require("redflat.startup") + +-- Initialize tables for module +----------------------------------------------------------------------------------------------------------------------- +-- TODO: weak table for all multi panel widget +local updates = { objects = {}, mt = {} } + +-- Generate default theme vars +----------------------------------------------------------------------------------------------------------------------- +local function default_style() + local style = { + wibox = { + geometry = { width = 400, height = 200 }, + border_width = 2, + title_font = "Sans 14 bold", + tip_font = "Sans 10", + set_position = nil, + separator = {}, + shape = nil, + icon = { + package = redutil.base.placeholder(), + close = redutil.base.placeholder({ txt = "X" }), + daily = redutil.base.placeholder(), + weekly = redutil.base.placeholder(), + normal = redutil.base.placeholder(), + silent = redutil.base.placeholder(), + }, + height = { title = 40, state = 50, tip = 20 }, + margin = { close = { 0, 0, 0, 0 }, state = { 0, 0, 0, 0 }, title = { 0, 0, 0, 0 }, image = { 0, 0, 0, 0 } } + }, + icon = redutil.base.placeholder(), + keytip = { geometry = { width = 400 } }, + notify = {}, + firstrun = false, + need_notify = true, + tooltip = { + base = {}, + state = { + timeout = 3, + set_position = function(w) awful.placement.under_mouse(w) w.y = w.y - 30 end, + } + }, + color = { main = "#b1222b", icon = "#a0a0a0", wibox = "#202020", border = "#575757", gray = "#404040", + urgent = "#32882d" } + } + return redutil.table.merge(style, redutil.table.check(beautiful, "widget.updates") or {}) +end + + +local STATE = setmetatable( + { keywords = { "NORMAL", "DAILY", "WEEKLY", "SILENT" } }, + { __index = function(table_, key) + return awful.util.table.hasitem(table_.keywords, key) or rawget(table_, key) + end } +) + +local tips = {} +tips[STATE.NORMAL] = "regular notifications" +tips[STATE.DAILY] = "postponed for a day" +tips[STATE.WEEKLY] = "postponed for a week" +tips[STATE.SILENT] = "notifications disabled" + +-- key bindings +updates.keys = {} +updates.keys.control = { + { + {}, "1", function() updates.set_mode(STATE.NORMAL) end, + { description = "Regular notifications", group = "Notifications" } + }, + { + {}, "2", function() updates.set_mode(STATE.DAILY) end, + { description = "Postponed for a day", group = "Notifications" } + }, + { + {}, "3", function() updates.set_mode(STATE.WEEKLY) end, + { description = "Postponed for a week", group = "Notifications" } + }, + { + {}, "4", function() updates.set_mode(STATE.SILENT) end, + { description = "Notifications disabled", group = "Notifications" } + }, +} +updates.keys.action = { + { + {}, "u", function() updates:update(true) end, + { description = "Check updates", group = "Action" } + }, + { + {}, "Escape", function() updates:hide() end, + { description = "Close updates widget", group = "Action" } + }, + { + { "Mod4" }, "F1", function() redtip:show() end, + { description = "Show hotkeys helper", group = "Action" } + }, +} + +updates.keys.all = awful.util.table.join(updates.keys.control, updates.keys.action) + + +-- Initialize notify widbox +----------------------------------------------------------------------------------------------------------------------- +function updates:init(args, style) + + -- Initialize vars + -------------------------------------------------------------------------------- + args = args or {} + local update_timeout = args.update_timeout or 3600 + local command = args.command or "echo 0" + local force_notify = false + style = redutil.table.merge(default_style(), style or {}) + + + self.style = style + self.is_updates = false + self.config = awful.util.getdir("cache") .. "/updates" + + -- Create floating wibox for updates widget + -------------------------------------------------------------------------------- + self.wibox = wibox({ + ontop = true, + bg = style.color.wibox, + shape = style.shape, + border_width = style.wibox.border_width, + border_color = style.color.border + }) + + self.wibox:geometry(style.wibox.geometry) + + -- Floating widget structure + -------------------------------------------------------------------------------- + + -- main image + self.packbox = svgbox(style.wibox.icon.package, nil, style.color.icon) + + -- titlebar + self.titlebox = wibox.widget.textbox("0 UPDATES") + self.titlebox:set_font(style.wibox.title_font) + self.titlebox:set_align("center") + + -- tip line + --self.tipbox = wibox.widget.textbox() + --self.tipbox:set_font(style.wibox.tip_font) + --self.tipbox:set_align("center") + --self.tipbox:set_forced_height(style.wibox.height.tip) + + -- close button + local closebox = svgbox(style.wibox.icon.close, nil, style.color.icon) + closebox:buttons(awful.util.table.join(awful.button({}, 1, function() self:hide() end))) + closebox:connect_signal("mouse::enter", function() closebox:set_color(style.color.main) end) + closebox:connect_signal("mouse::leave", function() closebox:set_color(style.color.icon) end) + + -- Control buttons + ------------------------------------------------------------ + local statebox = {} + local statearea = wibox.layout.flex.horizontal() + statearea:set_forced_height(style.wibox.height.state) + + -- color update fucntions + local function update_state() + for k, box in pairs(statebox) do + box:set_color(STATE[k] == self.state and style.color.main or style.color.gray) + end + --self.tipbox:set_markup(string.format('%s', style.color.gray, tips[self.state])) + end + + local function check_alert() + local time = os.time() + return self.is_updates and + ( self.state == STATE.NORMAL + or self.state == STATE.DAILY and (time - self.time > 24 * 3600) + or self.state == STATE.WEEKLY and (time - self.time > 7 * 24 * 3600)) + end + + local function update_widget_colors() + local is_alert = check_alert() + local color = is_alert and style.color.main or style.color.icon + for _, w in ipairs(updates.objects) do w:set_color(color) end + end + + -- create control buttons + function self.set_mode(state) + if self.state ~= state then + self.state = state + self.time = (state == STATE.DAILY or state == STATE.WEEKLY) and os.time() or 0 + update_state() + update_widget_colors() + end + end + + for state, k in pairs(STATE.keywords) do + statebox[k] = svgbox(style.wibox.icon[k:lower()], nil, style.color.gray) + local tp = tooltip({ objects = { statebox[k] } }, style.tooltip.state) + tp:set_text(tips[state]) + + statebox[k]:buttons(awful.util.table.join( + awful.button({}, 1, function() self.set_mode(state) end) + )) + + local area = wibox.layout.align.horizontal() + area:set_middle(statebox[k]) + area:set_expand("outside") + + statearea:add(area) + end + + -- Setup wibox layouts + ------------------------------------------------------------ + local titlebar = wibox.widget({ + nil, + self.titlebox, + wibox.container.margin(closebox, unpack(style.wibox.margin.close)), + forced_height = style.wibox.height.title, + layout = wibox.layout.align.horizontal + }) + + self.wibox:setup({ + { + nil, + wibox.container.margin(titlebar, unpack(style.wibox.margin.title)), + separator.horizontal(style.wibox.separator), + layout = wibox.layout.align.vertical + }, + { + nil, + { + nil, wibox.container.margin(self.packbox, unpack(style.wibox.margin.image)), nil, + expand = "outside", + layout = wibox.layout.align.horizontal + }, + --self.tipbox, + nil, + layout = wibox.layout.align.vertical + }, + wibox.container.margin(statearea, unpack(style.wibox.margin.state)), + layout = wibox.layout.align.vertical + }) + + -- Widget keygrabber + -------------------------------------------------------------------------------- + self.keygrabber = function(mod, key, event) + if event ~= "press" then return end + for _, k in ipairs(self.keys.all) do + if redutil.key.match_grabber(k, mod, key) then k[3](); return end + end + end + + -- Start up setup + ------------------------------------------------------------ + self:load_state() + self:set_keys() + update_state() + + -- Set tooltip + -------------------------------------------------------------------------------- + self.tp = tooltip(nil, style.tooltip.base) + self.tp:set_text("?") + + -- Update info function + -------------------------------------------------------------------------------- + local function update_count(output) + local c = string.match(output, "(%d+)") + + self.is_updates = tonumber(c) > 0 + local is_alert = check_alert() + + if style.need_notify and (is_alert or force_notify) then + rednotify:show(redutil.table.merge({ text = c .. " updates available" }, style.notify)) + end + self.titlebox:set_text(c .. " UPDATES") + self.packbox:set_color(tonumber(c) > 0 and style.color.main or style.color.icon) + + if self.tp then self.tp:set_text(c .. " updates") end + update_widget_colors() + end + + -- Set update timer + -------------------------------------------------------------------------------- + self.check_updates = function(is_force) + force_notify = is_force + awful.spawn.easy_async_with_shell(command, update_count) + end + + updates.timer = timer({ timeout = update_timeout }) + updates.timer:connect_signal("timeout", function() self.check_updates() end) + updates.timer:start() + + if style.firstrun and startup.is_startup then updates.timer:emit_signal("timeout") end + + -- Connect additional signals + ------------------------------------------------------------ + awesome.connect_signal("exit", function() self:save_state() end) +end + +-- Create a new updates widget +-- @param style Table containing colors and geometry parameters for all elemets +----------------------------------------------------------------------------------------------------------------------- +function updates.new(style) + + if not updates.wibox then updates:init({}) end + + -- Initialize vars + -------------------------------------------------------------------------------- + style = redutil.table.merge(updates.style, style or {}) + + local widg = svgbox(style.icon) + widg:set_color(style.color.icon) + table.insert(updates.objects, widg) + + updates.tp:add_to_object(widg) + + -------------------------------------------------------------------------------- + return widg +end + +-- Show/hide updates wibox +----------------------------------------------------------------------------------------------------------------------- +function updates:show() + if self.style.wibox.set_position then + self.style.wibox.set_position(self.wibox) + else + redutil.placement.centered(self.wibox, nil, mouse.screen.workarea) + end + redutil.placement.no_offscreen(self.wibox, self.style.screen_gap, screen[mouse.screen].workarea) + + self.wibox.visible = true + awful.keygrabber.run(self.keygrabber) + redtip:set_pack("System updates", self.tip, self.style.keytip.column, self.style.keytip.geometry) +end + +function updates:hide() + self.wibox.visible = false + awful.keygrabber.stop(self.keygrabber) + redtip:remove_pack() +end + +function updates:toggle() + if self.wibox.visible then + self:hide() + else + self:show() + end +end + +-- Save/restore state between sessions +----------------------------------------------------------------------------------------------------------------------- +function updates:load_state() + local info = redutil.read.file(self.config) + if info then + local state, time = string.match(info, "(%d)=(%d+)") + self.state, self.time = tonumber(state), tonumber(time) + else + self.state = STATE.NORMAL + self.time = 0 + end +end + +function updates:save_state() + local file = io.open(self.config, "w") + file:write(string.format("%d=%d", self.state, self.time)) + file:close() +end + +-- Set user hotkeys +----------------------------------------------------------------------------------------------------------------------- +function updates:set_keys(keys, layout) + layout = layout or "all" + if keys then + self.keys[layout] = keys + if layout ~= "all" then self.keys.all = awful.util.table.join(self.keys.control, self.keys.action) end + end + + self.tip = self.keys.all +end + +-- Update updates info for every widget +----------------------------------------------------------------------------------------------------------------------- +function updates:update(is_force) + self.check_updates(is_force) +end + +-- Config metatable to call updates module as function +----------------------------------------------------------------------------------------------------------------------- +function updates.mt:__call(...) + return updates.new(...) +end + +return setmetatable(updates, updates.mt) diff --git a/awesome/.config/awesome/rules-config.lua b/awesome/.config/awesome/rules-config.lua new file mode 100644 index 0000000..59d7360 --- /dev/null +++ b/awesome/.config/awesome/rules-config.lua @@ -0,0 +1,114 @@ +----------------------------------------------------------------------------------------------------------------------- +-- Rules config -- +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +local awful =require("awful") +local beautiful = require("beautiful") +local redtitle = require("redflat.titlebar") + +-- Initialize tables and vars for the module +----------------------------------------------------------------------------------------------------------------------- +local rules = {} + +rules.base_properties = { + border_width = beautiful.border_width, + border_color = beautiful.border_normal, + focus = awful.client.focus.filter, + raise = true, + size_hints_honor = false, + screen = awful.screen.preferred, +} + +rules.floating_any = { + class = { + "Clipflap", "Run.py", "Arandr", + "Gpick", + "Kruler", + "MessageWin", -- kalarm. + "Sxiv", + "Wpa_gui", + "pinentry", + "veromix", + "xtightvncviewer", + "keepassxc", + }, + name={ "Calculatrice","Event Tester",}, + role = { "AlarmWindow", "pop-up", }, + type = { "dialog" } +} + +rules.titlebar_exceptions = { + class = { "Cavalcade", "Clipflap", "Steam", "Qemu-system-x86_64" } +} + +rules.maximized = { + class = { "Emacs24" } +} + +-- Build rule table +----------------------------------------------------------------------------------------------------------------------- +function rules:init(args) + + args = args or {} + self.base_properties.keys = args.hotkeys.keys.client + self.base_properties.buttons = args.hotkeys.mouse.client + self.env = args.env or {} + + + -- Build rules + -------------------------------------------------------------------------------- + self.rules = { + { + rule = {}, + properties = args.base_properties or self.base_properties + }, + { + rule_any = args.floating_any or self.floating_any, + properties = { floating = true, ontop=true } + }, + { + rule_any = self.maximized, + callback = function(c) + c.maximized = true + redtitle.cut_all({ c }) + c.height = c.screen.workarea.height - 2 * c.border_width + end + }, + { + rule_any = { type = { "normal", "dialog" }}, + except_any = self.titlebar_exceptions, + properties = { titlebars_enabled = true } + }, + { + rule_any = { type = { "normal" }}, + properties = { placement = awful.placement.no_overlap + awful.placement.no_offscreen } + }, + + -- Tags placement + { + rule = { instance = "Xephyr" }, + properties = { tag = self.env.theme == "ruby" and "Test" or "Free", fullscreen = true } + }, + + -- Jetbrains splash screen fix + { + rule_any = { class = { "jetbrains-%w+", "java-lang-Thread" } }, + callback = function(jetbrains) + if jetbrains.skip_taskbar then jetbrains.floating = true end + end + }, + -- Set Firefox to always map on the tag named "2" on screen 1. + -- { rule = { class = "Firefox" }, + -- properties = { screen = 1, tag = "2" } }, + } + + + -- Set rules + -------------------------------------------------------------------------------- + awful.rules.rules = rules.rules +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return rules \ No newline at end of file diff --git a/awesome/.config/awesome/signals-config.lua b/awesome/.config/awesome/signals-config.lua new file mode 100644 index 0000000..56c609e --- /dev/null +++ b/awesome/.config/awesome/signals-config.lua @@ -0,0 +1,103 @@ +----------------------------------------------------------------------------------------------------------------------- +-- Signals config -- +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +local awful = require("awful") +local beautiful = require("beautiful") + +local redutil = require("redflat.util") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local signals = {} + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function do_sloppy_focus(c) + if awful.layout.get(c.screen) ~= awful.layout.suit.magnifier and awful.client.focus.filter(c) then + client.focus = c + end +end + +local function fixed_maximized_geometry(c, context) + if c.maximized and context ~= "fullscreen" then + c:geometry({ + x = c.screen.workarea.x, + y = c.screen.workarea.y, + height = c.screen.workarea.height - 2 * c.border_width, + width = c.screen.workarea.width - 2 * c.border_width + }) + end +end + +-- Build table +----------------------------------------------------------------------------------------------------------------------- +function signals:init(args) + + args = args or {} + local env = args.env + + -- actions on every application start + client.connect_signal( + "manage", + function(c) + -- put client at the end of list + if env.set_slave then awful.client.setslave(c) end + + -- startup placement + if awesome.startup + and not c.size_hints.user_position + and not c.size_hints.program_position + then + awful.placement.no_offscreen(c) + end + + -- put new floating windows to the center of screen + if env.set_center and c.floating and not (c.maximized or c.fullscreen) then + redutil.placement.centered(c, nil, mouse.screen.workarea) + end + end + ) + + -- add missing borders to windows that get unmaximized + client.connect_signal( + "property::maximized", + function(c) + if not c.maximized then + c.border_width = beautiful.border_width + end + end + ) + + -- don't allow maximized windows move/resize themselves + client.connect_signal( + "request::geometry", fixed_maximized_geometry + ) + + -- enable sloppy focus, so that focus follows mouse + if env.sloppy_focus then + client.connect_signal("mouse::enter", do_sloppy_focus) + end + + -- hilight border of focused window + -- can be disabled since focus indicated by titlebars in current config + if env.color_border_focus then + client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end) + client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end) + end + + -- wallpaper update on screen geometry change + screen.connect_signal("property::geometry", env.wallpaper) + + -- Awesome v4.0 introduce screen handling without restart. + -- All redflat panel widgets was designed in old fashioned way and doesn't support this fature properly. + -- Since I'm using single monitor setup I have no will to rework panel widgets by now, + -- so restart signal added here is simple and dirty workaround. + -- You can disable it on your own risk. + screen.connect_signal("list", awesome.restart) +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return signals diff --git a/awesome/.config/awesome/themes/colorless/common/awesome.svg b/awesome/.config/awesome/themes/colorless/common/awesome.svg new file mode 100644 index 0000000..bb623bd --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/common/awesome.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/common/blank.svg b/awesome/.config/awesome/themes/colorless/common/blank.svg new file mode 100644 index 0000000..33e5d39 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/common/blank.svg @@ -0,0 +1 @@ + diff --git a/awesome/.config/awesome/themes/colorless/common/check.svg b/awesome/.config/awesome/themes/colorless/common/check.svg new file mode 100644 index 0000000..934b0dc --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/common/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/common/submenu.svg b/awesome/.config/awesome/themes/colorless/common/submenu.svg new file mode 100644 index 0000000..6b2f849 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/common/submenu.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/common/system.svg b/awesome/.config/awesome/themes/colorless/common/system.svg new file mode 100644 index 0000000..769aa74 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/common/system.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/common/unknown.svg b/awesome/.config/awesome/themes/colorless/common/unknown.svg new file mode 100644 index 0000000..cdb0adf --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/common/unknown.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/common/warning.svg b/awesome/.config/awesome/themes/colorless/common/warning.svg new file mode 100644 index 0000000..5d25727 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/common/warning.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/cornerne.svg b/awesome/.config/awesome/themes/colorless/layouts/cornerne.svg new file mode 100644 index 0000000..d09d526 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/cornerne.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/cornernw.svg b/awesome/.config/awesome/themes/colorless/layouts/cornernw.svg new file mode 100644 index 0000000..a2b9152 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/cornernw.svg @@ -0,0 +1,4 @@ + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/cornerse.svg b/awesome/.config/awesome/themes/colorless/layouts/cornerse.svg new file mode 100644 index 0000000..8c985a3 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/cornerse.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/cornersw.svg b/awesome/.config/awesome/themes/colorless/layouts/cornersw.svg new file mode 100644 index 0000000..2d299c7 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/cornersw.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/fair.svg b/awesome/.config/awesome/themes/colorless/layouts/fair.svg new file mode 100644 index 0000000..dc79076 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/fair.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/floating.svg b/awesome/.config/awesome/themes/colorless/layouts/floating.svg new file mode 100644 index 0000000..d6b0d65 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/floating.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/fullscreen.svg b/awesome/.config/awesome/themes/colorless/layouts/fullscreen.svg new file mode 100644 index 0000000..573a267 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/fullscreen.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/grid.svg b/awesome/.config/awesome/themes/colorless/layouts/grid.svg new file mode 100644 index 0000000..e5593f6 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/grid.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/magnifier.svg b/awesome/.config/awesome/themes/colorless/layouts/magnifier.svg new file mode 100644 index 0000000..6160621 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/magnifier.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/map.svg b/awesome/.config/awesome/themes/colorless/layouts/map.svg new file mode 100644 index 0000000..651ce9c --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/map.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/max.svg b/awesome/.config/awesome/themes/colorless/layouts/max.svg new file mode 100644 index 0000000..8ff9b21 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/max.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/spiral.svg b/awesome/.config/awesome/themes/colorless/layouts/spiral.svg new file mode 100644 index 0000000..d9c1c07 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/spiral.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/tile.svg b/awesome/.config/awesome/themes/colorless/layouts/tile.svg new file mode 100644 index 0000000..2cc7262 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/tile.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/tilebottom.svg b/awesome/.config/awesome/themes/colorless/layouts/tilebottom.svg new file mode 100644 index 0000000..c31b69d --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/tilebottom.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/tileleft.svg b/awesome/.config/awesome/themes/colorless/layouts/tileleft.svg new file mode 100644 index 0000000..054c63f --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/tileleft.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/layouts/tiletop.svg b/awesome/.config/awesome/themes/colorless/layouts/tiletop.svg new file mode 100644 index 0000000..0520432 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/layouts/tiletop.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/player/cover.svg b/awesome/.config/awesome/themes/colorless/player/cover.svg new file mode 100644 index 0000000..b98bcac --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/player/cover.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/player/next.svg b/awesome/.config/awesome/themes/colorless/player/next.svg new file mode 100644 index 0000000..9a8df9c --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/player/next.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/player/pause.svg b/awesome/.config/awesome/themes/colorless/player/pause.svg new file mode 100644 index 0000000..fe62800 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/player/pause.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/player/play.svg b/awesome/.config/awesome/themes/colorless/player/play.svg new file mode 100644 index 0000000..7677394 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/player/play.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/player/previous.svg b/awesome/.config/awesome/themes/colorless/player/previous.svg new file mode 100644 index 0000000..7e4e2e6 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/player/previous.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/theme.lua b/awesome/.config/awesome/themes/colorless/theme.lua new file mode 100644 index 0000000..43392c4 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/theme.lua @@ -0,0 +1,1454 @@ +----------------------------------------------------------------------------------------------------------------------- +-- Colorless theme -- +----------------------------------------------------------------------------------------------------------------------- +local awful = require("awful") + +local theme = {} +--local wa = mouse.screen.workarea + +-- Color scheme +----------------------------------------------------------------------------------------------------------------------- +theme.color = { + -- main colors + main = "#02606D", + gray = "#575757", + bg = "#161616", + bg_second = "#181818", + wibox = "#202020", + icon = "#a0a0a0", + text = "#aaaaaa", + urgent = "#B25500", + highlight = "#e0e0e0", + border = "#404040", + + -- secondary colors + shadow1 = "#141414", + shadow2 = "#313131", + shadow3 = "#1c1c1c", + shadow4 = "#767676", + + button = "#575757", + pressed = "#404040", + + desktop_gray = "#404040", + desktop_icon = "#606060", +} + +-- Common +----------------------------------------------------------------------------------------------------------------------- +theme.path = awful.util.get_configuration_dir() .. "themes/colorless" +theme.base = awful.util.get_configuration_dir() .. "themes/colorless" +theme.homedir = os.getenv("HOME") + +-- Main config +------------------------------------------------------------ + +theme.panel_height = 36 -- panel height +theme.border_width = 4 -- window border width +theme.useless_gap = 4 -- useless gap + +theme.cellnum = { x = 96, y = 58 } -- grid layout property + +theme.wallpaper = theme.path .. "/wallpaper/primary.png" -- wallpaper file + +-- Fonts +------------------------------------------------------------ +theme.fonts = { + main = "sans 12", -- main font + menu = "sans 12", -- main menu font + tooltip = "sans 12", -- tooltip font + notify = "sans bold 14", -- redflat notify popup font + clock = "sans bold 12", -- textclock widget font + qlaunch = "sans bold 14", -- quick launch key label font + title = "sans bold 12", -- widget titles font + tiny = "sans bold 10", -- smallest font for widgets + keychain = "sans bold 14", -- key sequence tip font + titlebar = "sans bold 12", -- client titlebar font + hotkeys = { + main = "sans 12", -- hotkeys helper main font + key = "mono 12", -- hotkeys helper key font (use monospace for align) + title = "sans bold 14", -- hotkeys helper group title font + }, + player = { + main = "sans bold 12", -- player widget main font + time = "sans bold 14", -- player widget current time font + }, +} + +theme.cairo_fonts = { + tag = { font = "Sans", size = 16, face = 1 }, -- tag widget font + appswitcher = { font = "Sans", size = 22, face = 1 }, -- appswitcher widget font + navigator = { + title = { font = "Sans", size = 28, face = 1, slant = 0 }, -- window navigation title font + main = { font = "Sans", size = 22, face = 1, slant = 0 } -- window navigation main font + }, + + desktop = { + textbox = { font = "Sans", size = 24, face = 1 }, + }, +} + +-- Shared icons +-------------------------------------------------------------------------------- +theme.icon = { + check = theme.path .. "/common/check.svg", + blank = theme.path .. "/common/blank.svg", + submenu = theme.path .. "/common/submenu.svg", + warning = theme.path .. "/common/warning.svg", + awesome = theme.path .. "/common/awesome.svg", + system = theme.path .. "/common/system.svg", + unknown = theme.path .. "/common/unknown.svg", +} + + +-- Main theme settings +-- Make it updatabele since it may depends on common +----------------------------------------------------------------------------------------------------------------------- +function theme:init() + + -- Service utils config + ---------------------------------------------------------------------------------- + self.service = {} + + -- Window control mode appearance + -------------------------------------------------------------------------------- + self.service.navigator = { + border_width = 0, -- window placeholder border width + gradstep = 60, -- window placeholder background stripes width + marksize = { -- window information plate size + width = 160, -- width + height = 80, -- height + r = 20 -- corner roundness + }, + linegap = 32, -- gap between two lines on window information plate + timeout = 1, -- highlight duration + notify = {}, -- redflat notify style (see theme.float.notify) + titlefont = self.cairo_fonts.navigator.title, -- first line font on window information plate + font = self.cairo_fonts.navigator.main, -- second line font on window information plate + + -- array of hot key marks for window placeholders + num = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "F1", "F3", "F4", "F5" }, + + -- colors + color = { + border = self.color.main, -- window placeholder border color + mark = self.color.gray, -- window information plate background color + text = self.color.wibox, -- window information plate text color + fbg1 = self.color.main .. "40", -- first background color for focused window placeholder + fbg2 = self.color.main .. "20", -- second background color for focused window placeholder + hbg1 = self.color.urgent .. "40", -- first background color for highlighted window placeholder + hbg2 = self.color.urgent .. "20", -- second background color for highlighted window placeholder + bg1 = self.color.gray .. "40", -- first background color for window placeholder + bg2 = self.color.gray .. "20" -- second background color for window placeholder + } + } + + -- layout hotkeys helper settings + self.service.navigator.keytip = {} + + -- this one used as fallback when style for certain layout missed + self.service.navigator.keytip["base"] = { geometry = { width = 600 }, exit = true } + + -- styles for certain layouts + self.service.navigator.keytip["fairv"] = { geometry = { width = 600}, exit = true } + self.service.navigator.keytip["fairh"] = self.service.navigator.keytip["fairv"] + self.service.navigator.keytip["spiral"] = self.service.navigator.keytip["fairv"] + self.service.navigator.keytip["dwindle"] = self.service.navigator.keytip["fairv"] + + self.service.navigator.keytip["tile"] = { geometry = { width = 600 }, exit = true } + self.service.navigator.keytip["tileleft"] = self.service.navigator.keytip["tile"] + self.service.navigator.keytip["tiletop"] = self.service.navigator.keytip["tile"] + self.service.navigator.keytip["tilebottom"] = self.service.navigator.keytip["tile"] + + self.service.navigator.keytip["cornernw"] = { geometry = { width = 600 }, exit = true } + self.service.navigator.keytip["cornerne"] = self.service.navigator.keytip["cornernw"] + self.service.navigator.keytip["cornerse"] = self.service.navigator.keytip["cornernw"] + self.service.navigator.keytip["cornersw"] = self.service.navigator.keytip["cornernw"] + + self.service.navigator.keytip["magnifier"] = { geometry = { width = 600}, exit = true } + + self.service.navigator.keytip["grid"] = { geometry = { width = 1400 }, column = 2, exit = true } + self.service.navigator.keytip["usermap"] = { geometry = { width = 1400 }, column = 2, exit = true } + + -- Desktop file parser + -------------------------------------------------------------------------------- + self.service.dfparser = { + -- list of path to check desktop files + desktop_file_dirs = { + '/usr/share/applications/', + '/usr/local/share/applications/', + '~/.local/share/applications', + }, + -- icon theme settings + icons = { + theme = nil, -- user icon theme path + --theme = "/usr/share/icons/ACYLS", -- for example + df_icon = self.icon.system, -- default (fallback) icon + custom_only = false, -- use icons from user theme (no system fallback like 'hicolor' allowed) only + scalable_only = false -- use vector(svg) icons (no raster icons allowed) only + }, + wm_name = nil -- window manager name + } + + + -- Menu config + -------------------------------------------------------------------------------- + self.menu = { + border_width = 4, -- menu border width + screen_gap = self.useless_gap + self.border_width, -- minimal space from screen edge on placement + height = 32, -- menu item height + width = 250, -- menu item width + icon_margin = { 8, 8, 8, 8 }, -- space around left icon in menu item + ricon_margin = { 9, 9, 9, 9 }, -- space around right icon in menu item + nohide = false, -- do not hide menu after item activation + auto_expand = true, -- show submenu on item selection (without item activation) + auto_hotkey = false, -- automatically set hotkeys for all menu items + select_first = true, -- auto select first item when menu shown + hide_timeout = 1, -- auto hide timeout (auto hide disables if this set to 0) + font = self.fonts.menu, -- menu font + submenu_icon = self.icon.submenu, -- icon for submenu items + keytip = { geometry = { width = 400 } }, -- hotkeys helper settings + shape = nil, -- wibox shape + svg_scale = { false, false }, -- use vector scaling for left, right icons in menu item + } + + self.menu.color = { + border = self.color.wibox, -- menu border color + text = self.color.text, -- menu text color + highlight = self.color.highlight, -- menu text and icons color for selected item + main = self.color.main, -- menu selection color + wibox = self.color.wibox, -- menu background color + submenu_icon = self.color.icon, -- submenu icon color + right_icon = nil, -- right icon color in menu item + left_icon = nil, -- left icon color in menu item + } + + + -- Gauge (various elements that used as component for other widgets) style + -------------------------------------------------------------------------------- + self.gauge = { tag = {}, task = {}, icon = {}, audio = {}, monitor = {}, graph = {} } + + + -- Plain progressbar element + ------------------------------------------------------------ + self.gauge.graph.bar = { + color = self.color -- colors (main used) + } + + -- Plain monitor (label and progressbar below) + -------------------------------------------------------------- + self.gauge.monitor.plain = { + width = 50, -- widget width + font = self.cairo_fonts.tag, -- widget font + text_shift = 19, -- shift from upper border of widget to lower border of text + label = "MON", -- widget text + step = 0.05, -- progressbar painting step + line = { height = 4, y = 27 }, -- progressbar style + color = self.color -- colors (main used) + } + + -- Simple monitor with sigle vertical dashed progressbar + ------------------------------------------------------------ + self.gauge.monitor.dash = { + width = 10, -- widget width + color = self.color, -- colors (main used) + + -- progressbar line style + line = { + num = 5, -- number of chunks in progressbar + height = 3 -- height of progressbar chunk + }, + } + + -- Icon indicator (decoration in some panel widgets) + ------------------------------------------------------------ + self.gauge.icon.single = { + icon = self.icon.system, -- default icon + is_vertical = false, -- use vertical gradient (horizontal if false) + step = 0.02, -- icon painting step + color = self.color -- colors (main used) + } + + -- Double icon indicator + -------------------------------------------------------------- + self.gauge.icon.double = { + icon1 = self.icon.system, -- first icon + icon2 = self.icon.system, -- second icon + is_vertical = true, -- use vertical gradient (horizontal if false) + igap = 4, -- gap between icons + step = 0.02, -- icon painting step + color = self.color -- colors (main used) + } + + -- Double value monitor (double progressbar with icon) + -------------------------------------------------------------- + self.gauge.monitor.double = { + icon = self.icon.system, -- default icon + width = 90, -- widget width + dmargin = { 10, 0, 0, 0 }, -- margins around progressbar area + color = self.color, -- colors (main used) + + -- progressbar style + line = { + width = 4, -- progressbar height + v_gap = 6, -- space between progressbar + gap = 4, -- gap between progressbar dashes + num = 5 -- number of progressbar dashes + }, + } + + -- Separator (decoration used on panel, menu and some other widgets) + ------------------------------------------------------------ + self.gauge.separator = { + marginv = { 2, 2, 4, 4 }, -- margins for vertical separator + marginh = { 6, 6, 3, 3 }, -- margins for horizontal separator + color = self.color -- color (secondary used) + } + + -- Step like dash bar (user for volume widgets) + ------------------------------------------------------------ + self.gauge.graph.dash = { + bar = { + width = 4, -- dash element width + num = 10 -- number of dash elements + }, + color = self.color -- color (main used) + } + + -- Volume indicator + ------------------------------------------------------------ + self.gauge.audio.blue = { + width = 75, -- widget width + dmargin = { 10, 0, 2, 2 }, -- margins around dash area + icon = self.icon.system, -- volume icon + + -- colors + color = { icon = self.color.icon, mute = self.color.urgent }, + + -- dash style + dash = { bar = { num = 5, width = 4 } }, + } + + self.gauge.audio.red = { + icon = { volume = self.icon.system, mute = self.icon.warning }, -- icons + step = 0.05, -- icon painting step + color = { main = self.color.main, icon = self.color.icon, mute = self.color.gray } -- custom colors + } + + -- Dotcount (used in minitray widget) + ------------------------------------------------------------ + self.gauge.graph.dots = { + column_num = { 3, 5 }, -- amount of dot columns (min/max) + row_num = 3, -- amount of dot rows + dot_size = 5, -- dots size + dot_gap_h = 4, -- horizontal gap between dot (with columns number it'll define widget width) + color = self.color -- colors (main used) + } + + -- Circle shaped monitor + -------------------------------------------------------------- + self.gauge.monitor.circle = { + width = 32, -- widget width + line_width = 4, -- width of circle + iradius = 5, -- radius for center point + radius = 11, -- circle radius + step = 0.05, -- circle painting step + color = self.color -- colors (main used) + } + + -- Tag (base element of taglist) + ------------------------------------------------------------ + self.gauge.tag.orange = { + width = 38, -- widget width + line_width = self.gauge.monitor.circle.line_width, -- width of arcs + iradius = self.gauge.monitor.circle.iradius, -- radius for center point + radius = self.gauge.monitor.circle.radius, -- arcs radius + cgap = 0.314, -- gap between arcs in radians + min_sections = 1, -- minimal amount of arcs + show_min = false, -- indicate minimized apps by color + text = false, -- replace middle circle by text + font = self.cairo_fonts.tag, -- font for text + color = self.color -- colors (main used) + } + + self.gauge.tag.ruby = { + width = 40, -- widget width + color = self.color, -- colors (main used) + + -- tag state mark + base = { + pad = 6, -- left/right padding + height = 9, -- mark height + thickness = 2 -- mark lines thickness + }, + + -- client focus mark + mark = { + pad = 10, -- left/right padding + height = 3 -- mark height + }, + } + + self.gauge.tag.blue = { + width = 103, -- widget width + show_min = false, -- indicate minimized apps by color + text_shift = 20, -- shift from upper border of widget to lower border of text + color = self.color, -- colors (main used) + font = self.cairo_fonts.tag, -- font + + -- apps indicator + point = { + width = 80, -- apps indicator total width + height = 3, -- apps indicator total height + gap = 27, -- shift from upper border of widget to apps indicator + dx = 5 -- gap between apps indicator parts + }, + } + + self.gauge.tag.red = { + width = 80, -- widget width + text_shift = 19, -- shift from upper border of widget to lower border of text + font = self.cairo_fonts.tag, -- font + show_counter = true, -- visible/hidden apps counter + color = self.color, -- colors (main used) + + -- apps counter + counter = { + size = 12, -- counter font size + margin = 2, -- margin around counter + coord = { 40, 28 } -- counter position + }, + + -- functions for state marks + marks = nil, + + -- geometry for state marks + geometry = { + active = { height = 4, y = 27 }, -- active tag mark + focus = { x = 5, y = 7, width = 10, height = 12 }, -- focused tag mark + occupied = { x = 68, y = 7, width = 8, height = 12 } -- occupied tag mark + } + } + + self.gauge.tag.green = { + width = 44, -- widget width + margin = { 0, 0, 8, 8 }, -- margin around tag icon + icon = nil, -- layouts icon list (will be defined below) + color = self.color -- colors (main used) + } + + -- Task (base element of tasklist) + ------------------------------------------------------------ + + -- the same structure as blue tag + self.gauge.task.blue = { + width = 70, + show_min = true, + text_shift = 20, + color = self.color, + font = self.cairo_fonts.tag, + point = { width = 70, height = 3, gap = 27, dx = 5 }, + } + + self.gauge.task.ruby = { + width = 76, + text_shift = 26, + color = self.color, + font = self.cairo_fonts.tag, + + point = { size = 5, space = 5, gap = 4 }, + underline = { height = 10, thickness = 3, gap = 34, dh = 0 }, + } + + self.gauge.task.red = { + width = 40, -- widget width + text_shift = 19, -- shift from upper border of widget to lower border of text + font = self.cairo_fonts.tag, -- font + line = { height = 4, y = 27 }, -- application state indicator + color = self.color, -- colors (main used) + + -- applications counter + counter = { + size = 12, -- counter font size + margin = 2 -- margin around counter + }, + } + + self.gauge.task.green = { + width = 40, -- widget width + df_icon = self.icon.system, -- fallback icon + margin = { 0, 0, 2, 2 }, -- margin around icon + color = self.color -- colors (main used) + } + + -- Panel widgets + -------------------------------------------------------------------------------- + self.widget = {} + + -- individual margins for panel widgets + ------------------------------------------------------------ + self.widget.wrapper = { + mainmenu = { 12, 10, 6, 6 }, + layoutbox = { 10, 10, 6, 6 }, + textclock = { 12, 12, 0, 0 }, + taglist = { 4, 4, 0, 0 }, + tray = { 10, 12, 7, 7 }, + -- tasklist = { 0, 70, 0, 0 }, -- centering tasklist widget + } + + -- Textclock + ------------------------------------------------------------ + self.widget.textclock = { + font = self.fonts.clock, -- font + tooltip = {}, -- redflat tooltip style (see theme.float.tooltip) + color = { text = self.color.icon } -- colors + } + + -- Binary clock + ------------------------------------------------------------ + self.widget.binclock = { + width = 52, -- widget width + tooltip = {}, -- redflat tooltip style (see theme.float.tooltip) + dot = { size = 5 }, -- mark size + color = self.color, -- colors (main used) + } + + -- Battery indicator + ------------------------------------------------------------ + self.widget.battery = { + timeout = 30, -- update timeout + notify = {}, -- redflat notify style (see theme.float.notify) + + -- notification levels + levels = { 0.05, 0.1, 0.15, 0.20, 0.25 } + } + + -- Minitray + ------------------------------------------------------------ + self.widget.minitray = { + dotcount = {}, -- redflat dotcount style (see theme.gauge.graph.dots) + border_width = 0, -- floating widget border width + geometry = { height = 40 }, -- floating widget size + screen_gap = 2 * self.useless_gap, -- minimal space from screen edge on floating widget placement + shape = nil, -- wibox shape + color = { wibox = self.color.wibox, border = self.color.wibox }, + + -- function to define floating widget position when shown + set_position = function(wibox) + local geometry = { x = mouse.screen.workarea.x + mouse.screen.workarea.width, + y = mouse.screen.workarea.y + mouse.screen.workarea.height } + wibox:geometry(geometry) + end, + } + + -- Pulseaudio volume control + ------------------------------------------------------------ + self.widget.pulse = { + notify = {}, -- redflat notify style (see theme.float.notify) + widget = nil, -- audio gauge (usually setted by rc file) + audio = {} -- style for gauge + } + + -- Keyboard layout indicator + ------------------------------------------------------------ + self.widget.keyboard = { + icon = self.icon.system, -- widget icon + micon = self.icon, -- some common menu icons + + -- list of colors associated with keyboard layouts + layout_color = { self.color.icon, self.color.main }, + + -- redflat menu style (see theme.menu) + menu = { width = 180, color = { right_icon = self.color.icon }, nohide = true } + } + + -- Mail indicator + ------------------------------------------------------------ + self.widget.mail = { + icon = self.icon.system, -- widget icon + notify = {}, -- redflat notify style (see theme.float.notify) + need_notify = true, -- show notification on new mail + firstrun = true, -- check mail on wm start/restart + color = self.color, -- colors (main used) + } + + -- System updates indicator + ------------------------------------------------------------ + self.widget.updates = { + icon = self.icon.system, -- widget icon + notify = {}, -- redflat notify style (see theme.float.notify) + need_notify = true, -- show notification on updates + firstrun = true, -- check updates on wm start/restart + color = self.color, -- colors (main used) + + -- redflat key tip settings + keytip = { geometry = { width = 400 } }, + + -- tooltips style + tooltip = { base = {}, state = { timeout = 1 } }, + + -- wibox style settings + wibox = { + geometry = { width = 250, height = 160 }, -- widget size + border_width = 0, -- widget border width + title_font = self.fonts.title, -- widget title font + tip_font = self.fonts.tiny, -- widget state tip font + separator = {}, -- redflat separator style (see theme.gauge.separator) + shape = nil, -- wibox shape + set_position = nil, -- set_position + + -- wibox icons + icon = { + package = self.icon.system, -- main wibox image + close = self.base .. "/titlebar/close.svg", -- close button + normal = self.icon.system, -- regular notification + daily = self.icon.system, -- defer notification for day + weekly = self.icon.system, -- defer notification for 7 day + silent = self.icon.system, -- disable notification + }, + + -- widget areas height + height = { + title = 28, -- titlebar + state = 34, -- control icon area + }, + + -- widget element margins + margin = { + close = { 0, 0, 6, 6 }, -- close button + title = { 16 + 2*6, 16, 4, 0 }, -- titlebar area + state = { 4, 4, 4, 12 }, -- control icon area + image = { 0, 0, 2, 4 }, -- main wibox image area + }, + } + } + + -- Layoutbox + ------------------------------------------------------------ + self.widget.layoutbox = { + micon = self.icon, -- some common menu icons (used: 'blank', 'check') + color = self.color -- colors (main used) + } + + -- layout icons + self.widget.layoutbox.icon = { + floating = self.base .. "/layouts/floating.svg", + max = self.base .. "/layouts/max.svg", + fullscreen = self.base .. "/layouts/fullscreen.svg", + tilebottom = self.base .. "/layouts/tilebottom.svg", + tileleft = self.base .. "/layouts/tileleft.svg", + tile = self.base .. "/layouts/tile.svg", + tiletop = self.base .. "/layouts/tiletop.svg", + fairv = self.base .. "/layouts/fair.svg", + fairh = self.base .. "/layouts/fair.svg", + grid = self.base .. "/layouts/grid.svg", + usermap = self.base .. "/layouts/map.svg", + magnifier = self.base .. "/layouts/magnifier.svg", + spiral = self.base .. "/layouts/spiral.svg", + cornerne = self.base .. "/layouts/cornerne.svg", + cornernw = self.base .. "/layouts/cornernw.svg", + cornerse = self.base .. "/layouts/cornerse.svg", + cornersw = self.base .. "/layouts/cornersw.svg", + unknown = self.icon.unknown, -- this one used as fallback + } + + -- redflat menu style (see theme.menu) + self.widget.layoutbox.menu = { + icon_margin = { 8, 12, 8, 8 }, + width = 260, + auto_hotkey = true, + nohide = false, + color = { right_icon = self.color.icon, left_icon = self.color.icon } + } + + -- human readable aliases for layout names (displayed in menu and tooltip) + self.widget.layoutbox.name_alias = { + floating = "Floating", + fullscreen = "Fullscreen", + max = "Maximized", + grid = "Grid", + usermap = "User Map", + tile = "Right Tile", + fairv = "Fair Tile", + tileleft = "Left Tile", + tiletop = "Top Tile", + tilebottom = "Bottom Tile", + magnifier = "Magnifier", + spiral = "Spiral", + cornerne = "Corner NE", + cornernw = "Corner NW", + cornerse = "Corner SE", + cornersw = "Corner SW", + } + + -- green tag icons + self.gauge.tag.green.icon = self.widget.layoutbox.icon + + -- Tasklist + -------------------------------------------------------------- + + -- main settings + self.widget.tasklist = { + custom_icon = false, -- use custom applications icons (not every gauge task widget support icons) + iconnames = {}, -- icon name aliases for custom applications icons + widget = nil, -- task gauge widget (usually setted by rc file) + width = 40, -- width of task element in tasklist + char_digit = 4, -- number of characters in task element text + need_group = true, -- group application instances into one task element + parser = {}, -- redlat desktop file parser settings (see theme.service.dfparser) + task_margin = { 5, 5, 0, 0 }, -- margins around task element + task = self.gauge.task.blue -- style for task gauge widget + } + + -- menu settings + self.widget.tasklist.winmenu = { + micon = self.icon, -- some common menu icons + titleline = { + font = self.fonts.title, -- menu title height + height = 25 -- menu title font + }, + stateline = { height = 30 }, -- height of menu item with state icons + state_iconsize = { width = 18, height = 18 }, -- size for state icons + layout_icon = self.widget.layoutbox.icon, -- list of layout icons + separator = { marginh = { 3, 3, 5, 5 } }, -- redflat separator style (see theme.gauge.separator) + color = self.color, -- colors (main used) + + -- main menu style (see theme.menu) + menu = { width = 200, color = { right_icon = self.color.icon }, ricon_margin = { 9, 9, 9, 9 } }, + + -- tag action submenu style (see theme.menu) + tagmenu = { width = 160, color = { right_icon = self.color.icon, left_icon = self.color.icon }, + icon_margin = { 9, 9, 9, 9 } }, + + -- set which action will hide menu after activate + hide_action = { min = true, move = true, max = false, add = false, floating = false, sticky = false, + ontop = false, below = false, maximized = false }, + } + + -- menu icons + self.widget.tasklist.winmenu.icon = { + floating = self.base .. "/titlebar/floating.svg", + sticky = self.base .. "/titlebar/pin.svg", + ontop = self.base .. "/titlebar/ontop.svg", + below = self.base .. "/titlebar/below.svg", + close = self.base .. "/titlebar/close.svg", + minimize = self.base .. "/titlebar/minimize.svg", + maximized = self.base .. "/titlebar/maximized.svg", + + unknown = self.icon.unknown, -- this one used as fallback + } + + -- multiline task element tip + self.widget.tasklist.tasktip = { + border_width = 2, -- tip border width + margin = { 10, 10, 5, 5 }, -- margins around text in tip lines + timeout = 0.5, -- hide timeout + shape = nil, -- wibox shape + sl_highlight = false, -- highlight application state when it's single line tip + color = self.color, -- colors (main used) + } + + -- task text aliases + self.widget.tasklist.appnames = {} + self.widget.tasklist.appnames["Firefox" ] = "FIFOX" + self.widget.tasklist.appnames["Gnome-terminal" ] = "GTERM" + + + -- Floating widgets + -------------------------------------------------------------------------------- + self.float = { decoration = {} } + + -- Brightness control + ------------------------------------------------------------ + self.float.brightness = { + notify = {}, -- redflat notify style (see theme.float.notify) + } + + -- Client menu + ------------------------------------------------------------ + self.float.clientmenu = { + actionline = { height = 28 }, -- height of menu item with action icons + action_iconsize = { width = 18, height = 18 }, -- size for action icons + stateline = { height = 30 }, -- height of menu item with state icons + + -- redflat separator style(see theme.gauge.separator) + separator = { marginh = { 3, 3, 5, 5 }, marginv = { 3, 3, 3, 3 } }, + + -- same elements as for task list menu + icon = self.widget.tasklist.winmenu.icon, + micon = self.widget.tasklist.winmenu.micon, + layout_icon = self.widget.layoutbox.icon, + menu = self.widget.tasklist.winmenu.menu, + state_iconsize = self.widget.tasklist.winmenu.state_iconsize, + tagmenu = self.widget.tasklist.winmenu.tagmenu, + hide_action = self.widget.tasklist.winmenu.hide_action, + color = self.color, + } + + -- Audio player + ------------------------------------------------------------ + self.float.player = { + geometry = { width = 490, height = 130 }, -- widget size + screen_gap = 2 * self.useless_gap, -- minimal space from screen edge on floating widget placement + border_margin = { 15, 15, 15, 15 }, -- margins around widget content + elements_margin = { 15, 0, 0, 0 }, -- margins around main player elements (exclude cover art) + controls_margin = { 0, 0, 14, 6 }, -- margins around control player elements + volume_margin = { 0, 0, 0, 3 }, -- margins around volume element + buttons_margin = { 0, 0, 3, 3 }, -- margins around buttons area + pause_margin = { 12, 12, 0, 0 }, -- margins around pause button + line_height = 26, -- text lines height + bar_width = 6, -- progressbar width + volume_width = 50, -- volume element width + titlefont = self.fonts.player.main, -- track font + artistfont = self.fonts.player.main, -- artist/album font + timefont = self.fonts.player.time, -- track progress time font + border_width = 0, -- widget border width + timeout = 1, -- widget update timeout + set_position = nil, -- set_position + shape = nil, -- wibox shape + color = self.color, -- color (main used) + + -- volume dash style (see theme.gauge.graph.dash) + dashcontrol = { color = self.color, bar = { num = 7 } }, + + -- progressbar style (see theme.gauge.graph.bar) + progressbar = { color = self.color }, + } + + -- widget icons + self.float.player.icon = { + cover = self.base .. "/player/cover.svg", + next_tr = self.base .. "/player/next.svg", + prev_tr = self.base .. "/player/previous.svg", + play = self.base .. "/player/play.svg", + pause = self.base .. "/player/pause.svg" + } + + -- Top processes + ------------------------------------------------------------ + self.float.top = { + geometry = { width = 460, height = 400 }, -- widget size + screen_gap = 2 * self.useless_gap, -- minimal space from screen edge on floating widget placement + border_margin = { 20, 20, 10, 0 }, -- margins around widget content + button_margin = { 140, 140, 18, 18 }, -- margins around kill button + title_height = 40, -- widget title height + border_width = 0, -- widget border width + bottom_height = 70, -- kill button area height + list_side_gap = 8, -- left/rigth borger margin for processes list + title_font = self.fonts.title, -- widget title font + timeout = 2, -- widget update timeout + shape = nil, -- wibox shape + color = self.color, -- color (main used) + + -- list columns width + labels_width = { num = 30, cpu = 70, mem = 120 }, + + -- redflat key tip settings + keytip = { geometry = { width = 400 } }, + + -- placement function + set_position = nil, + } + + -- Application runner + ------------------------------------------------------------ + self.float.apprunner = { + itemnum = 6, -- number of visible items + geometry = { width = 620, height = 480 }, -- widget size + border_margin = { 24, 24, 24, 24 }, -- margin around widget content + icon_margin = { 8, 16, 0, 0 }, -- margins around widget icon + title_height = 48, -- height of title (promt and icon) area + prompt_height = 35, -- prompt line height + title_icon = self.icon.system, -- widget icon + border_width = 0, -- widget border width + parser = {}, -- desktop file parser settings (see theme.service.dfparser) + field = nil, -- redflat text field style(see theme.float.decoration.field) + shape = nil, -- wibox shape + color = self.color, -- colors (main used) + + name_font = self.fonts.title, -- application title font + comment_font = self.fonts.main, -- application comment font + list_text_vgap = 4, -- space between application title and comment + list_icon_margin = { 6, 12, 6, 6 }, -- margins around applications icons + dimage = self.icon.unknown, -- fallback icon for applications + + keytip = { geometry = { width = 400 } }, -- redflat key tip settings + } + + -- Application swit`cher + ------------------------------------------------------------ + self.float.appswitcher = { + wibox_height = 240, -- widget height + label_height = 28, -- height of the area with application mark(key) + title_height = 40, -- height of widget title line (application name and tag name) + icon_size = 96, -- size of the application icon in preview area + preview_gap = 20, -- gap between preview areas + shape = nil, -- wibox shape + + -- desktop file parser settings (see theme.service.dfparser) + parser = { + desktop_file_dirs = awful.util.table.join( + self.service.dfparser.desktop_file_dirs, + { '~/.local/share/applications-fake' } + ) + }, + + border_margin = { 10, 10, 0, 10 }, -- margins around widget content + preview_margin = { 15, 15, 15, 15 }, -- margins around application preview + preview_format = 16 / 10, -- preview acpect ratio + title_font = self.fonts.title, -- font of widget title line + border_width = 0, -- widget border width + update_timeout = 1 / 12, -- application preview update timeout + min_icon_number = 4, -- this one will define the minimal widget width + -- (widget will not shrink if number of apps items less then this) + color = self.color, -- colors (main used) + font = self.cairo_fonts.appswitcher, -- font of application mark(key) + + -- redflat key tip settings + keytip = { geometry = { width = 400 }, exit = true }, + } + + -- additional color + self.float.appswitcher.color.preview_bg = self.color.main .. "12" + + -- application marks(keys) list + self.float.appswitcher.hotkeys = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", + "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12" } + + -- Quick launcher + ------------------------------------------------------------ + self.float.qlaunch = { + geometry = { width = 1400, height = 170 }, -- widget size + + border_width = 0, -- widget border width + border_margin = { 5, 5, 12, 15 }, -- margins around widget content + notify = {}, -- redflat notify style (see theme.float.notify) + shape = nil, -- wibox shape + recoloring = false, -- apply redflat recoloring feature on application icons + label_font = self.fonts.qlaunch, -- font of application mark(key) + color = self.color, -- colors (main used) + df_icon = self.icon.system, -- fallback application icon + no_icon = self.icon.unknown, -- icon for unused application slot + + -- desktop file parser settings (see theme.service.dfparser) + parser = { + desktop_file_dirs = awful.util.table.join( + self.service.dfparser.desktop_file_dirs, + { '~/.local/share/applications-fake' } + ) + }, + + appline = { + iwidth = 140, -- application item width + im = { 5, 5, 0, 0 }, -- margins around application item area + igap = { 0, 0, 5, 15 }, -- margins around application icon itself (will affect icon size) + lheight = 26 -- height of application mark(key) area + }, + state = { + gap = 5, -- space between application state marks + radius = 5, -- application state mark radius + size = 10, -- application state mark size + height = 14 -- height of application state marks area + }, + + -- redflat key tip settings + keytip = { geometry = { width = 600 } }, + + -- file to store widget data + -- this widget is rare one which need to keep settings between sessions + configfile = os.getenv("HOME") .. "/.cache/awesome/applist", + } + + -- Hotkeys helper + ------------------------------------------------------------ + self.float.hotkeys = { + geometry = { width = 1400 }, -- widget size + border_margin = { 20, 20, 8, 10 }, -- margins around widget content + border_width = 0, -- widget border width + delim = " ", -- text separator between key and description + tspace = 5, -- space between lines in widget title + is_align = true, -- align keys description (monospace font required) + separator = { marginh = { 0, 0, 3, 6 } }, -- redflat separator style (see theme.gauge.separator) + font = self.fonts.hotkeys.main, -- keys description font + keyfont = self.fonts.hotkeys.key, -- keys font + titlefont = self.fonts.hotkeys.title, -- widget title font + shape = nil, -- wibox shape + color = self.color, -- colors (main used) + + -- manual setup for expected text line heights + -- used for auto adjust widget height + heights = { + key = 20, -- hotkey tip line height + title = 22 -- group title height + }, + } + + -- Titlebar helper + ------------------------------------------------------------ + self.float.bartip = { + geometry = { width = 260, height = 40 }, -- widget size + border_margin = { 10, 10, 10, 10 }, -- margins around widget content + border_width = 0, -- widget border widthj + font = self.fonts.title, -- widget font + set_position = nil, -- placement function + shape = nil, -- wibox shape + names = { "Mini", "Plain", "Full" }, -- titlebar layout names + color = self.color, -- colors (main used) + + -- margin around widget elements + margin = { icon = { title = { 10, 10, 8, 8 }, state = { 10, 10, 8, 8 } } }, + + -- widget icons + icon = { + title = self.base .. "/titlebar/title.svg", + active = self.base .. "/titlebar/active.svg", + hidden = self.base .. "/titlebar/hidden.svg", + disabled = self.base .. "/titlebar/disabled.svg", + absent = self.base .. "/titlebar/absent.svg", + unknown = self.icon.unknown, + }, + + -- redflat key tip settings + keytip = { geometry = { width = 540 } }, + } + + -- Floating window control helper + ------------------------------------------------------------ + self.float.control = { + geometry = { width = 260, height = 48 }, -- widget size + border_margin = { 10, 10, 10, 10 }, -- margins around widget content + border_width = 0, -- widget border widthj + font = self.fonts.title, -- widget font + steps = { 1, 10, 25, 50, 200 }, -- move/resize step + default_step = 3, -- select default step by index + onscreen = true, -- no off screen for window placement + set_position = nil, -- widget placement function + shape = nil, -- wibox shape + color = self.color, -- colors (main used) + + -- margin around widget elements + margin = { icon = { onscreen = { 10, 10, 8, 8 }, mode = { 10, 10, 8, 8 } } }, + + -- widget icons + icon = { + onscreen = self.icon.system, + resize = {}, + }, + + -- redflat key tip settings + keytip = { geometry = { width = 540 } }, + } + + -- Key sequence tip + ------------------------------------------------------------ + self.float.keychain = { + geometry = { width = 250, height = 56 }, -- default widget size + font = self.fonts.keychain, -- widget font + border_width = 2, -- widget border width + shape = nil, -- wibox shape + color = self.color, -- colors (main used) + + -- redflat key tip settings + keytip = { geometry = { width = 600 }, column = 1 }, + } + + -- Tooltip + ------------------------------------------------------------ + self.float.tooltip = { + timeout = 0, -- show delay + shape = nil, -- wibox shapea + font = self.fonts.tooltip, -- widget font + border_width = 2, -- widget border width + set_position = nil, -- function to setup tooltip position when shown + color = self.color, -- colors (main used) + + -- padding around widget content + padding = { vertical = 3, horizontal = 6 }, + } + + -- Floating prompt + ------------------------------------------------------------ + self.float.prompt = { + geometry = { width = 620, height = 120 }, -- widget size + border_width = 0, -- widget border width + margin = { 20, 20, 40, 40 }, -- margins around widget content + field = nil, -- redflat text field style (see theme.float.decoration.field) + shape = nil, -- wibox shape + naughty = {}, -- awesome notification style + color = self.color, -- colors (main used) + } + + -- Notify (redflat notification widget) + ------------------------------------------------------------ + self.float.notify = { + geometry = { width = 484, height = 106 }, -- widget size + screen_gap = 2 * self.useless_gap, -- screen edges gap on placement + border_margin = { 20, 20, 20, 20 }, -- margins around widget content + elements_margin = { 20, 0, 10, 10 }, -- margins around main elements (text and bar) + font = self.fonts.notify, -- widget font + icon = self.icon.warning, -- default widget icon + border_width = 0, -- widget border width + timeout = 5, -- hide timeout + shape = nil, -- wibox shape + color = self.color, -- colors (main used) + + -- progressbar is optional element used for some notifications + bar_width = 8, -- progressbar width + progressbar = {}, -- redflat progressbar style (see theme.gauge.graph.bar) + + -- placement function + set_position = function(wibox) + wibox:geometry({ x = mouse.screen.workarea.x + mouse.screen.workarea.width, y = mouse.screen.workarea.y }) + end, + } + + -- Decoration (various elements that used as component for other widgets) style + -------------------------------------------------------------------------------- + self.float.decoration.button = { + color = self.color -- colors (secondary used) + } + + self.float.decoration.field = { + color = self.color -- colors (secondary used) + } + + + -- Titlebar + -------------------------------------------------------------------------------- + self.titlebar = {} + + self.titlebar.base = { + position = "top", -- titlebar position + font = self.fonts.titlebar, -- titlebar font + border_margin = { 0, 0, 0, 4 }, -- margins around titlebar active area + color = self.color, -- colors (main used) + } + + -- application state marks settings + self.titlebar.mark = { + color = self.color, -- colors (main used) + } + + -- application control icon settings + self.titlebar.icon = { + color = self.color, -- colors (main used) + + -- icons list + list = { + focus = self.base .. "/titlebar/focus.svg", + floating = self.base .. "/titlebar/floating.svg", + ontop = self.base .. "/titlebar/ontop.svg", + below = self.base .. "/titlebar/below.svg", + sticky = self.base .. "/titlebar/pin.svg", + maximized = self.base .. "/titlebar/maximized.svg", + minimized = self.base .. "/titlebar/minimize.svg", + close = self.base .. "/titlebar/close.svg", + menu = self.base .. "/titlebar/menu.svg", + + unknown = self.icon.unknown, -- this one used as fallback + } + } + + -- Desktop config + -------------------------------------------------------------------------------- + self.desktop = { common = { bar = {}, pack = {} }, speedmeter = {} } + + self.desktop.line_height = 18 -- text and progressbar height for desktop wodgets + + -- desktop widget colors + self.desktop.color = { + main = self.color.main, + gray = self.color.desktop_gray, + icon = self.color.desktop_icon, + urgent = self.color.urgent, + wibox = self.color.bg .. "00" + } + + -- Common (various elem, 0.20, 0.25ents that used as component for desktop widgets) + -------------------------------------------------------------------------------- + + -- Textbox + ------------------------------------------------------------ + self.desktop.common.textbox = { + width = nil, -- widget width + height = nil, -- widget height + draw = "by_left", -- align method ("by_left", "by_right", "by_edges", "by_width") + color = self.desktop.color.gray, -- text color + + -- font style + font = self.cairo_fonts.desktop.textbox, + } + + -- Dashed progressbar + ------------------------------------------------------------ + self.desktop.common.bar.plain = { + width = nil, -- widget width + height = nil, -- widget height + autoscale = false, -- normalize progressbar value + maxm = 1, -- the maximum allowed value + + -- color (desktop used) + color = self.desktop.color, + + -- progressbar settings + chunk = { + width = 6, -- bar width + gap = 6 -- space between bars + } + } + + -- Time chart + ------------------------------------------------------------ + self.desktop.common.chart = { + width = nil, -- widget width + height = nil, -- widget height + autoscale = true, -- normalize chart values + maxm = 1, -- the maximum allowed value + zero_height = 4, -- height for zero value point in chart + color = self.desktop.color.gray, -- chart bars color + + -- chart bars settings + bar = { + width = 5, -- bar width + gap = 5 -- space between bars + } + } + + + -- Custom shaped vertical progressbar + ------------------------------------------------------------ + self.desktop.common.bar.shaped = { + width = nil, -- widget width + height = nil, -- widget height + autoscale = true, -- normalize chart values + maxm = 1, -- the maximum allowed value + shape = "corner", -- progressbar chunk shape + show = { tooltip = false }, -- show tooltip + color = self.desktop.color, -- color (desktop used) + + -- element style + chunk = { + num = 10, -- number of elements + line = 5, -- element line width + height = 10 -- element height + }, + + -- tooltip style + tooltip = {}, + } + + -- Lines (group of progressbars with label in front and text value after it) + ------------------------------------------------------------ + self.desktop.common.pack.lines = { + label = { width = 80, draw = "by_width" }, -- label style (see theme.desktop.common.textbox) + text = { width = 92, draw = "by_edges" }, -- value style (see theme.desktop.common.textbox) + progressbar = {}, -- progressbar style (see theme.desktop.common.bar.plain) + line = { height = self.desktop.line_height }, -- text/progressbar height + tooltip = {}, -- redflat tooltip style (see theme.float.tooltip) + color = self.desktop.color, -- color (desktop used) + + -- show/hide line elements + show = { text = true, label = true, tooltip = false }, + + -- space between label/text and progressbar + gap = { text = 22, label = 16 }, + } + + -- Widgets + -------------------------------------------------------------------------------- + + --Custom aligned text block + ------------------------------------------------------------ + self.desktop.textset = { + font = "Sans 12", -- font + spacing = 0, -- space between lines + color = self.desktop.color -- color (desktop used) + } + + -- Speed widget (double progressbar with time chart for each of it) + ------------------------------------------------------------ + self.desktop.speedmeter.normal = { + barvalue_height = 32, -- height of the area with progressbar and text + digits = 2, -- minimal number of digits for progressbar value + fullchart_height = 80, -- height of the each area with progressbar, text and chart + image_gap = 16, -- space between direction icon and progressbar/chart + color = self.desktop.color, -- color (desktop used) + + -- direction icons + images = { + self.icon.system, -- up + self.icon.system -- down + }, + + -- !!! WARNING some missed style settings for elemets below will be overwritten by widget + -- do not try to use full style settings from 'theme.desktop.commom' here + + -- time chart style (see theme.desktop.common.chart) + chart = { bar = { width = 6, gap = 3 }, height = 40, zero_height = 4 }, + + -- progressbar label and value (see theme.desktop.common.textbox) + label = { height = self.desktop.line_height }, + + -- progressbar style (see theme.desktop.common.bar.plain) + progressbar = { chunk = { width = 16, gap = 6 }, height = 6 }, + } + + self.desktop.speedmeter.compact = { + margins = { label = {}, chart = {} }, -- extra margins for some elements + height = { chart = 50 }, -- height of the each area with progressbar, text and chart + digits = 2, -- minimal number of digits for progressbar value + color = self.desktop.color, -- color (desktop used) + + -- direction icons + icon = { + up = self.icon.system, -- up + down = self.icon.system, -- down + margin = { 4, 4, 2, 2 }, -- margins around icon + }, + + -- !!! WARNING some style settings for elemets below will be overwritten by widget + chart = { zero_height = 0 }, -- time chart style (see theme.desktop.common.chart) + label = {}, -- progressbar value (see theme.desktop.common.textbox) + progressbar = {}, -- double progressbar style (see theme.desktop.common.bar.plain) + } + + -- Widget with multiple horizontal and vertical progress bars + ------------------------------------------------------------ + self.desktop.multimeter = { + digits = 3, -- minimal number of digits for horizontal progressbar values + color = self.desktop.color, -- color (desktop used) + labels = {}, -- list of optional labels for horizontal bars + + -- area height + height = { + upright = 80, -- vertical progressbars height + lines = 58, -- horizontal progressbar area height + }, + + -- widget icon + icon = { + image = self.icon.system, -- widget icon + margin = { 0, 16, 0, 0 }, -- margins around icon + full = false -- draw icon in full height of widget + }, + -- !!! WARNING some missed style settings for elemets below will be overwritten by widget + + -- vertical progressbars style (see theme.desktop.common.bar.shaped) + upbar = { width = 34, chunk = { height = 17, num = 10, line = 4 } }, + + -- horizontal progressbars style (see theme.desktop.common.pack.lines) + lines = {}, + } + + -- Widget with multiple progress bars + ------------------------------------------------------------ + self.desktop.multiline = { + digits = 3, -- minimal number of digits for progressbar value + margin = { 0, 0, 0, 0 }, -- margin around progressbar list + color = self.desktop.color, -- color (desktop used) + + -- widget icon settings + icon = { image = nil, margin = { 0, 0, 0, 0 } }, + + -- !!! WARNING some missed style settings for elemets below will be overwritten by widget + + -- progressbars style (see theme.desktop.common.pack.lines) + lines = { progressbar = {}, tooltip = {} }, + } + + -- Widget with several text groups in single line + -- every group has label and value and icon in the middle + ------------------------------------------------------------ + self.desktop.singleline = { + lbox = { draw = "by_width", width = 50 }, -- label style (see theme.desktop.common.textbox) + rbox = { draw = "by_edges", width = 60 }, -- value style (see theme.desktop.common.textbox) + digits = 2, -- minimal number of digits for value + icon = self.icon.system, -- group icon + iwidth = 142, -- width for every text group + color = self.desktop.color -- color (desktop used) + } + + -- Calendar widget with lined up marks + ------------------------------------------------------------ + self.desktop.calendar = { + show_pointer = true, -- show date under mouse + color = self.desktop.color, -- color (desktop used) + -- TODO: check for better font + -- today label style + label = { + gap = 8, -- space between label and pointer + sep = "-", -- day/month separator + font = { font = "Play", size = 16, face = 1, slant = 0 }, -- font + }, + + -- days marks style + mark = { + height = 12, -- mark height + width = 25, -- mark width + dx = 6, -- pointer arrow width + line = 2, -- stroke line width for next month marks + }, + } + + -- Individual styles for certain widgets + -------------------------------------------------------------------------------- + self.individual = { desktop = {} } + + -- Default awesome theme vars + -------------------------------------------------------------------------------- + + -- colors + self.bg_normal = self.color.wibox + self.bg_focus = self.color.main + self.bg_urgent = self.color.urgent + self.bg_minimize = self.color.gray + + self.fg_normal = self.color.text + self.fg_focus = self.color.highlight + self.fg_urgent = self.color.highlight + self.fg_minimize = self.color.highlight + + self.border_normal = self.color.wibox + self.border_focus = self.color.wibox + self.border_marked = self.color.main + + -- font + self.font = self.fonts.main + + -- standart awesome notification widget + self.naughty = {} + + self.naughty.base = { + timeout = 10, + margin = 12, + icon_size = 80, + font = self.fonts.main, + bg = self.color.wibox, + fg = self.color.text, + + border_width = 4, + border_color = self.color.wibox + } + + self.naughty.normal = { + height = self.float.notify.geometry.height, + width = self.float.notify.geometry.width, + } + + self.naughty.low = { + timeout = 5, + height = self.float.notify.geometry.height, + width = self.float.notify.geometry.width, + } + + self.naughty.critical = { + timeout = 0, + border_color = self.color.main + } +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +theme:init() + +return theme diff --git a/awesome/.config/awesome/themes/colorless/titlebar/absent.svg b/awesome/.config/awesome/themes/colorless/titlebar/absent.svg new file mode 100644 index 0000000..8775f60 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/absent.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/active.svg b/awesome/.config/awesome/themes/colorless/titlebar/active.svg new file mode 100644 index 0000000..94e2201 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/active.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/below.svg b/awesome/.config/awesome/themes/colorless/titlebar/below.svg new file mode 100644 index 0000000..8ca14c0 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/below.svg @@ -0,0 +1,4 @@ + + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/close.svg b/awesome/.config/awesome/themes/colorless/titlebar/close.svg new file mode 100644 index 0000000..d3c16d2 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/close.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/disabled.svg b/awesome/.config/awesome/themes/colorless/titlebar/disabled.svg new file mode 100644 index 0000000..b749cf0 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/floating.svg b/awesome/.config/awesome/themes/colorless/titlebar/floating.svg new file mode 100644 index 0000000..d6b0d65 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/floating.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/focus.svg b/awesome/.config/awesome/themes/colorless/titlebar/focus.svg new file mode 100644 index 0000000..9831a7c --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/focus.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/hidden.svg b/awesome/.config/awesome/themes/colorless/titlebar/hidden.svg new file mode 100644 index 0000000..cfafd70 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/hidden.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/maximized.svg b/awesome/.config/awesome/themes/colorless/titlebar/maximized.svg new file mode 100644 index 0000000..8ff5e34 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/maximized.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/menu.svg b/awesome/.config/awesome/themes/colorless/titlebar/menu.svg new file mode 100644 index 0000000..6a712a0 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/menu.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/minimize.svg b/awesome/.config/awesome/themes/colorless/titlebar/minimize.svg new file mode 100644 index 0000000..b749cf0 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/minimize.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/ontop.svg b/awesome/.config/awesome/themes/colorless/titlebar/ontop.svg new file mode 100644 index 0000000..d39e016 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/ontop.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/pin.svg b/awesome/.config/awesome/themes/colorless/titlebar/pin.svg new file mode 100644 index 0000000..da5b938 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/pin.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/colorless/titlebar/title.svg b/awesome/.config/awesome/themes/colorless/titlebar/title.svg new file mode 100644 index 0000000..707c683 --- /dev/null +++ b/awesome/.config/awesome/themes/colorless/titlebar/title.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/default/icons/audio.svg b/awesome/.config/awesome/themes/default/icons/audio.svg new file mode 100644 index 0000000..ab3fd84 --- /dev/null +++ b/awesome/.config/awesome/themes/default/icons/audio.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/default/layouts/cornerne.svg b/awesome/.config/awesome/themes/default/layouts/cornerne.svg new file mode 100644 index 0000000..d09d526 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/cornerne.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/cornernw.svg b/awesome/.config/awesome/themes/default/layouts/cornernw.svg new file mode 100644 index 0000000..a2b9152 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/cornernw.svg @@ -0,0 +1,4 @@ + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/cornerse.svg b/awesome/.config/awesome/themes/default/layouts/cornerse.svg new file mode 100644 index 0000000..8c985a3 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/cornerse.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/cornersw.svg b/awesome/.config/awesome/themes/default/layouts/cornersw.svg new file mode 100644 index 0000000..2d299c7 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/cornersw.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/fair.svg b/awesome/.config/awesome/themes/default/layouts/fair.svg new file mode 100644 index 0000000..dc79076 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/fair.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/floating.svg b/awesome/.config/awesome/themes/default/layouts/floating.svg new file mode 100644 index 0000000..d6b0d65 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/floating.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/default/layouts/fullscreen.svg b/awesome/.config/awesome/themes/default/layouts/fullscreen.svg new file mode 100644 index 0000000..573a267 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/fullscreen.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/grid.svg b/awesome/.config/awesome/themes/default/layouts/grid.svg new file mode 100644 index 0000000..e5593f6 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/grid.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/magnifier.svg b/awesome/.config/awesome/themes/default/layouts/magnifier.svg new file mode 100644 index 0000000..6160621 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/magnifier.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/map.svg b/awesome/.config/awesome/themes/default/layouts/map.svg new file mode 100644 index 0000000..651ce9c --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/map.svg @@ -0,0 +1,3 @@ + + + diff --git a/awesome/.config/awesome/themes/default/layouts/max.svg b/awesome/.config/awesome/themes/default/layouts/max.svg new file mode 100644 index 0000000..8ff9b21 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/max.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/spiral.svg b/awesome/.config/awesome/themes/default/layouts/spiral.svg new file mode 100644 index 0000000..d9c1c07 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/spiral.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/tile.svg b/awesome/.config/awesome/themes/default/layouts/tile.svg new file mode 100644 index 0000000..2cc7262 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/tile.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/tilebottom.svg b/awesome/.config/awesome/themes/default/layouts/tilebottom.svg new file mode 100644 index 0000000..c31b69d --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/tilebottom.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/tileleft.svg b/awesome/.config/awesome/themes/default/layouts/tileleft.svg new file mode 100644 index 0000000..054c63f --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/tileleft.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/awesome/.config/awesome/themes/default/layouts/tiletop.svg b/awesome/.config/awesome/themes/default/layouts/tiletop.svg new file mode 100644 index 0000000..0520432 --- /dev/null +++ b/awesome/.config/awesome/themes/default/layouts/tiletop.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/awesome/.config/awesome/titlebar-config.lua b/awesome/.config/awesome/titlebar-config.lua new file mode 100644 index 0000000..c39d878 --- /dev/null +++ b/awesome/.config/awesome/titlebar-config.lua @@ -0,0 +1,150 @@ +----------------------------------------------------------------------------------------------------------------------- +-- Titlebar config -- +----------------------------------------------------------------------------------------------------------------------- + +-- Grab environment +local awful = require("awful") +local wibox = require("wibox") +local beautiful = require("beautiful") + +-- local redflat = require("redflat") +local redtitle = require("redflat.titlebar") +local redutil = require("redflat.util") +local clientmenu = require("redflat.float.clientmenu") + +-- Initialize tables and vars for module +----------------------------------------------------------------------------------------------------------------------- +local titlebar = {} + +-- Support functions +----------------------------------------------------------------------------------------------------------------------- +local function title_buttons(c) + return awful.util.table.join( + awful.button( + { }, 1, + function() + client.focus = c; c:raise() + awful.mouse.client.move(c) + end + ), + awful.button( + { }, 3, + function() + client.focus = c; c:raise() + clientmenu:show(c) + end + ) + ) +end + +local function on_maximize(c) + -- hide/show title bar + local is_max = c.maximized_vertical or c.maximized + local action = is_max and "cut_all" or "restore_all" + redtitle[action]({ c }) + + -- dirty size correction + local model = redtitle.get_model(c) + if model and not model.hidden then + c.height = c:geometry().height + (is_max and model.size or -model.size) + if is_max then c.y = c.screen.workarea.y end + end +end + +-- Connect titlebar building signal +----------------------------------------------------------------------------------------------------------------------- +function titlebar:init() + + local style = {} + + -- titlebar schemes + style.base = redutil.table.merge(redutil.table.check(beautiful, "titlebar.base") or {}, { size = 8 }) + style.iconic = redutil.table.merge(style.base, { size = 24 }) + + -- titlebar elements styles + style.mark_mini = redutil.table.merge( + redutil.table.check(beautiful, "titlebar.mark") or {}, + { size = 30, gap = 10, angle = 0 } + ) + style.icon = redutil.table.merge( + redutil.table.check(beautiful, "titlebar.icon") or {}, + { gap = 10 } + ) + + -- titlebar setup for clients + client.connect_signal( + "request::titlebars", + function(c) + -- build titlebar and mouse buttons for it + local buttons = title_buttons(c) + redtitle(c, style.base) + + -- build mini titlebar model + local base = wibox.widget({ + nil, + { + right = style.mark_mini.gap, + redtitle.mark.focus(c, style.mark_mini), + layout = wibox.container.margin, + }, + { + redtitle.mark.property(c, "floating", style.mark_mini), + redtitle.mark.property(c, "sticky", style.mark_mini), + redtitle.mark.property(c, "ontop", style.mark_mini), + spacing = style.mark_mini.gap, + layout = wibox.layout.fixed.horizontal() + }, + buttons = buttons, + layout = wibox.layout.align.horizontal, + }) + + -- build titlebar model with control buttons + local title = redtitle.label(c, style.iconic, true) + title:buttons(buttons) + + local iconic = wibox.widget({ + { + { + redtitle.button.focus(c, style.icon), + redtitle.button.property(c, "ontop", style.icon), + redtitle.button.property(c, "below", style.icon), + spacing = style.icon.gap, + layout = wibox.layout.fixed.horizontal() + }, + top = 1, bottom = 1, left = 4, right = style.icon.gap + 2 * 18, + widget = wibox.container.margin + }, + title, + { + { + redtitle.button.property(c, "floating", style.icon), + redtitle.button.property(c, "sticky", style.icon), + redtitle.button.property(c, "minimized", style.icon), + redtitle.button.property(c, "maximized", style.icon), + redtitle.button.close(c, style.icon), + spacing = style.icon.gap, + layout = wibox.layout.fixed.horizontal() + }, + top = 1, bottom = 1, right = 4, + widget = wibox.container.margin + }, + layout = wibox.layout.align.horizontal, + }) + + -- Set both models to titlebar + redtitle.add_layout(c, nil, base, style.base.size) + redtitle.add_layout(c, nil, iconic, style.iconic.size) + redtitle.switch(c, nil, redtitle._index) + + -- hide titlebar when window maximized + if c.maximized_vertical or c.maximized then on_maximize(c) end + + c:connect_signal("property::maximized_vertical", on_maximize) + c:connect_signal("property::maximized", on_maximize) + end + ) +end + +-- End +----------------------------------------------------------------------------------------------------------------------- +return titlebar