add(desktop_decoration) Customize the desktop bar

This commit is contained in:
Aire-One 2021-07-04 17:24:00 +02:00
parent 8c158dd6c3
commit baeb76de22
8 changed files with 442 additions and 87 deletions

View File

@ -12,6 +12,8 @@ rc_configuration.menu = require 'rc.configuration.menu'
rc_configuration.rules = require 'rc.configuration.rules' rc_configuration.rules = require 'rc.configuration.rules'
rc_configuration.prompt_commands = require 'rc.configuration.prompt_commands'
rc_configuration.tag_layouts = require 'rc.configuration.tag_layouts' rc_configuration.tag_layouts = require 'rc.configuration.tag_layouts'
return rc_configuration return rc_configuration

View File

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

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path style="fill:#ECEFF1;" d="M16,20H20V16H16M16,14H20V10H16M10,8H14V4H10M16,8H20V4H16M10,14H14V10H10M4,14H8V10H4M4,20H8V16H4M10,20H14V16H10M4,8H8V4H4V8Z" /></svg>

After

Width:  |  Height:  |  Size: 441 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24" fill="#ECEFF1" opacity="1"><path d="M16,20H8V6H16M16.67,4H15V2H9V4H7.33A1.33,1.33 0 0,0 6,5.33V20.67C6,21.4 6.6,22 7.33,22H16.67A1.33,1.33 0 0,0 18,20.67V5.33C18,4.6 17.4,4 16.67,4Z"></path></svg>

After

Width:  |  Height:  |  Size: 337 B

View File

@ -7,13 +7,52 @@ local icons_dir = assets_dir .. 'icons/'
local theme = {} local theme = {}
theme.border_color_normal = '#0000ff' --- Basic
theme.font = 'Noto Mono 9'
--- Tags --- Tags
theme.hometag_master_width_factor = 0.65 theme.hometag_master_width_factor = 0.65
--- Icons --- Icons
theme.icon_hometag = icons_dir .. 'home-circle.svg' theme.icon_hometag = icons_dir .. 'home-circle.svg'
theme.icon_apps = icons_dir .. 'apps.svg'
theme.icon_battery_outline = icons_dir .. 'battery-outline.svg'
--- Wibar
-- My wibar is a transparent dock at the bottom of the screen.
-- Height and width ahve to be set by the constructor to use screen DPI and
-- screen percentage values.
theme.wibar_stretch = false
theme.wibar_border_width = 0
theme.wibar_border_color = nil
theme.wibar_ontop = false
theme.wibar_opacity = 1
theme.wibar_type = 'dock'
theme.wibar_bg = '#0000'
--- Spacing and margins
-- Naming convention:
-- <rc.ui.desktop_decoration element>_<widget element>_<property>
theme.spacing = 8
theme.margin = 4
theme.padding = 4
theme.bar_height = 32 + (theme.margin * 2) -- This is the outer size of the bar
theme.bar_width = '80%' -- Make the bar size relative to the screen
theme.bar_margin_x = theme.margin * 2
theme.bar_margin_y = theme.margin
theme.bar_padding_x = theme.padding * 2
theme.bar_padding_y = 0
theme.bar_box_margin_x = 0
theme.bar_box_margin_y = theme.margin
theme.bar_box_padding_x = theme.padding * 2
theme.bar_box_padding_y = theme.padding
theme.bar_box_spacing = theme.spacing
--- Widgets specific
theme.bg_systray = '#455A64'
theme.systray_icon_spacing = 3
--- Wallpaper --- Wallpaper
theme.wallpaper = function (screen) theme.wallpaper = function (screen)

View File

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

View File

@ -0,0 +1,100 @@
---------------------------------------------------------------------------
-- An implementation of my battery_widget widget with a dynamicly generated
-- icon based on the battery percentage level.
--
-- Find more about my battery_widget widget at
-- https://github.com/Aire-One/awesome-battery_widget
--
-- @author Aire-One
-- @copyright 2020 Aire-One
---------------------------------------------------------------------------
local lgi = require 'lgi'
local cairo = lgi.cairo
local rsvg = lgi.Rsvg
local atooltip = require 'awful.tooltip'
local beautiful = require 'beautiful'
local gcolor = require 'gears.color'
local imagebox = require 'wibox.widget.imagebox'
local battery_widget = require 'battery-widget'
local my_battery = {}
local mt = {}
--- Generate a drawing for the battery icon.
-- @tparam number percentage The percentage of battery remaining.
-- @tparam gears.color|string color The color of the drawing.
-- @treturn cairo.ImageSurface The generated surface for the drawing.
function my_battery.draw_battery (percentage, color)
local svg_handle = rsvg.Handle.new_from_file(beautiful.icon_battery_outline)
if not svg_handle then return end
local surface = cairo.ImageSurface.create(cairo.Format.ARGB32, 24, 24)
local cr = cairo.Context(surface)
svg_handle:render_cairo(cr)
local max_height = 14
local x = 8
local width = 8
local height = percentage / 100 * max_height
local y = 6 + max_height - height
cr:set_source(gcolor(color))
cr:rectangle(x, y, width, height)
cr:fill()
return surface
end
--- Update handler for the battery widget. This is the function called
-- by the signal system on battery update.
-- @tparam my_battery widget The battery widget to update.
-- @tparam UPowerGLib.Device device The UPower device to monitor.
function my_battery.update (widget, device)
widget.image = my_battery.draw_battery(device.percentage, widget.color)
end
--- Give the widget template for the battery widget.
-- (It's a basic `wibox.widget.imagebox` for my battery implementation)
function my_battery.widget_template ()
local widget = imagebox()
widget.resize = true
return widget
end
--- Constructor of my battery widget!
-- @tparam table args Table of parameters.
-- @tparam screen|number args.screen The screen of the wodget.
-- @tparam string args.device_path The path of the UPower device to monitor.
-- @tparam gears.color|string args.color Color to use to draw the battery.
-- @treturn my_battery The instantiated widget.
function my_battery.new (args)
local widget = battery_widget {
screen = args.screen,
device_path = args.device_path,
widget_template = my_battery.widget_template(),
instant_update = true
}
widget.color = args.color
widget:connect_signal('upower::update', function (w, device)
my_battery.update(w, device)
end)
widget.tooltip = atooltip {
objects = { widget },
timer_function = function ()
return string.format('%3d', widget.device.percentage) .. '%'
end
}
return widget
end
function mt.__call (self, ...)
return my_battery.new(...)
end
return setmetatable(my_battery, mt)

View File

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