597 lines
16 KiB
Lua
597 lines
16 KiB
Lua
local setmetatable = setmetatable
|
|
local table = table
|
|
local ipairs = ipairs
|
|
local pairs = pairs
|
|
|
|
-- Awesome Libs
|
|
local abutton = require('awful.button')
|
|
local aplacement = require('awful.placement')
|
|
local apopup = require('awful.popup')
|
|
local awidget = require('awful.widget')
|
|
local beautiful = require('beautiful')
|
|
local dpi = require('beautiful').xresources.apply_dpi
|
|
local gcolor = require('gears.color')
|
|
local gfilesystem = require('gears.filesystem')
|
|
local gtable = require('gears.table')
|
|
local gtimer = require('gears.timer')
|
|
local lgi = require('lgi')
|
|
local Gio = lgi.Gio
|
|
local wibox = require('wibox')
|
|
|
|
-- Local libs
|
|
local config = require('src.tools.config')
|
|
local context_menu = require('src.modules.context_menu')
|
|
local hover = require('src.tools.hover')
|
|
local icon_lookup = require('src.tools.gio_icon_lookup')()
|
|
|
|
local capi = {
|
|
awesome = awesome,
|
|
client = client,
|
|
mouse = mouse,
|
|
screen = screen,
|
|
}
|
|
|
|
local icondir = gfilesystem.get_configuration_dir() .. 'src/assets/icons/'
|
|
|
|
local elements = {
|
|
['pinned'] = {},
|
|
['applauncher_starter'] = {},
|
|
['running'] = {},
|
|
}
|
|
|
|
local instances = {}
|
|
|
|
local dock = { mt = {} }
|
|
|
|
--[[ Json format of dock.json, running apps won't be saved
|
|
[
|
|
{
|
|
"applauncher_starter": {
|
|
{
|
|
"name": "Application Launcher",
|
|
"icon": "/usr/share/icons/Papirus/48x48/apps/xfce4-appfinder.svg",
|
|
}
|
|
},
|
|
"pinned": [
|
|
{
|
|
"name": "firefox",
|
|
"icon": "/usr/share/icons/Papirus/48x48/apps/firefox.svg",
|
|
"exec": "firefox",
|
|
"desktop_file": "/usr/share/applications/firefox.desktop"
|
|
},
|
|
{
|
|
"name": "discord",
|
|
"icon": "/usr/share/icons/Papirus/48x48/apps/discord.svg",
|
|
"exec": "discord",
|
|
"desktop_file": "/usr/share/applications/discord.desktop"
|
|
}
|
|
],
|
|
}
|
|
]
|
|
]]
|
|
|
|
function dock:toggle()
|
|
self.popup.visible = not self.popup.visible
|
|
end
|
|
|
|
function dock:write_elements_to_file_async(callback)
|
|
--create a local copy of the elements["pinned"] table and only set the desktop_file key from its children
|
|
local elements_copy = { pinned = {} }
|
|
for _, element in ipairs(elements['pinned']) do
|
|
table.insert(elements_copy['pinned'], { desktop_file = element.desktop_file })
|
|
end
|
|
config.write_json(gfilesystem.get_configuration_dir() .. 'src/config/dock_' .. self.screen.index .. '.json', elements_copy['pinned'], callback)
|
|
end
|
|
|
|
---Read the content of dock.json and get the content as a table
|
|
---@param callback function Called after the elements have been set, no values are passed
|
|
function dock:read_elements_from_file_async(callback)
|
|
local data = config.read_json(gfilesystem.get_configuration_dir() .. 'src/config/dock_' .. self.screen.index .. '.json')
|
|
-- Make sure to not set the running key to nil on accident
|
|
for _, v in ipairs(data) do
|
|
local w = self:get_element_widget(v.desktop_file)
|
|
table.insert(elements['pinned'], w)
|
|
end
|
|
if callback then
|
|
callback()
|
|
end
|
|
end
|
|
|
|
---Creates a pinned widget for the dock and adds it into the elements table
|
|
---@param desktop_file string .desktop file path
|
|
---@return nil
|
|
function dock:get_element_widget(desktop_file)
|
|
if not desktop_file then return end
|
|
|
|
local GDesktopAppInfo = Gio.DesktopAppInfo.new_from_filename(desktop_file)
|
|
|
|
local icon = icon_lookup:get_gicon_path(nil, GDesktopAppInfo.get_string(GDesktopAppInfo, 'Icon')) or
|
|
icon_lookup:get_gicon_path(nil, Gio.DesktopAppInfo.get_string(GDesktopAppInfo, 'X-AppImage-Old-Icon')) or ''
|
|
|
|
local widget = wibox.widget {
|
|
{
|
|
{
|
|
{
|
|
{
|
|
widget = wibox.widget.imagebox,
|
|
image = icon,
|
|
valign = 'center',
|
|
halign = 'center',
|
|
resize = true,
|
|
id = 'icon_role',
|
|
},
|
|
widget = wibox.container.constraint,
|
|
width = beautiful.user_config.dock_icon_size,
|
|
height = beautiful.user_config.dock_icon_size,
|
|
},
|
|
widget = wibox.container.margin,
|
|
margins = dpi(5),
|
|
},
|
|
widget = wibox.container.constraint,
|
|
width = beautiful.user_config.dock_icon_size + dpi(10), -- + margins
|
|
height = beautiful.user_config.dock_icon_size + dpi(10),
|
|
strategy = 'exact',
|
|
},
|
|
bg = beautiful.colorscheme.bg1,
|
|
shape = beautiful.shape[8],
|
|
widget = wibox.container.background,
|
|
}
|
|
|
|
local action_entries = {}
|
|
for _, action in ipairs(Gio.DesktopAppInfo.list_actions(GDesktopAppInfo)) do
|
|
table.insert(action_entries, {
|
|
name = Gio.DesktopAppInfo.get_action_name(GDesktopAppInfo, action) or '',
|
|
icon = icon_lookup:get_gicon_path(nil, GDesktopAppInfo.get_string(GDesktopAppInfo, 'Icon')) or
|
|
icon_lookup:get_gicon_path(nil, Gio.DesktopAppInfo.get_string(GDesktopAppInfo, 'X-AppImage-Old-Icon')) or
|
|
gcolor.recolor_image(icondir .. 'entry.svg', beautiful.colorscheme.bg_yellow),
|
|
callback = function()
|
|
Gio.DesktopAppInfo.launch_action(GDesktopAppInfo, action)
|
|
end,
|
|
})
|
|
end
|
|
|
|
table.insert(action_entries, {
|
|
name = 'Remove from Dock',
|
|
icon = gcolor.recolor_image(icondir .. 'context_menu/entry.svg', beautiful.colorscheme.bg_yellow),
|
|
callback = function()
|
|
local data = config.read_json(gfilesystem.get_configuration_dir() .. 'src/config/dock_' .. self.screen.index .. '.json')
|
|
for i, v in ipairs(data) do
|
|
if v.desktop_file == desktop_file then
|
|
if type(data) == 'table' then
|
|
table.remove(data, i)
|
|
end
|
|
break
|
|
end
|
|
end
|
|
config.write_json(gfilesystem.get_configuration_dir() .. 'src/config/dock_' .. self.screen.index .. '.json', data)
|
|
self:remove_element_widget(widget)
|
|
end,
|
|
})
|
|
|
|
widget.cm = context_menu {
|
|
widget_template = wibox.widget {
|
|
{
|
|
{
|
|
{
|
|
{
|
|
widget = wibox.widget.imagebox,
|
|
resize = true,
|
|
valign = 'center',
|
|
halign = 'center',
|
|
id = 'icon_role',
|
|
},
|
|
widget = wibox.container.constraint,
|
|
stragety = 'exact',
|
|
width = dpi(24),
|
|
height = dpi(24),
|
|
id = 'const',
|
|
},
|
|
{
|
|
widget = wibox.widget.textbox,
|
|
valign = 'center',
|
|
halign = 'left',
|
|
id = 'text_role',
|
|
},
|
|
layout = wibox.layout.fixed.horizontal,
|
|
},
|
|
widget = wibox.container.margin,
|
|
},
|
|
widget = wibox.container.background,
|
|
}, spacing = dpi(10),
|
|
entries = action_entries,
|
|
}
|
|
|
|
widget.cm:connect_signal('mouse::leave', function()
|
|
widget.cm.visible = false
|
|
self.cm_open = widget.cm.visible
|
|
end)
|
|
|
|
hover.bg_hover {
|
|
widget = widget,
|
|
overlay = 12,
|
|
press_overlay = 24,
|
|
}
|
|
|
|
local exec = Gio.DesktopAppInfo.get_string(GDesktopAppInfo, 'Exec')
|
|
|
|
widget:buttons(gtable.join {
|
|
abutton({}, 1, function()
|
|
Gio.AppInfo.launch_uris_async(Gio.AppInfo.create_from_commandline(exec, nil, 0))
|
|
end),
|
|
abutton({}, 3, function()
|
|
widget.cm:toggle()
|
|
self.cm_open = widget.cm.visible
|
|
end),
|
|
})
|
|
|
|
table.insert(elements['pinned'], widget)
|
|
self:emit_signal('dock::element_added', widget)
|
|
end
|
|
|
|
---Removes a given widget from the dock
|
|
---@param widget wibox.widget The widget to remove
|
|
function dock:remove_element_widget(widget)
|
|
if not widget then return end
|
|
for k, v in pairs(elements['pinned']) do
|
|
if v == widget then
|
|
table.remove(elements['pinned'], k)
|
|
self:emit_signal('dock::element_removed', widget)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
---Pins an element to the dock by adding it to the pinned table, then writes the table to the file
|
|
---emits the signal `dock::pin_element` then successfully added to the table
|
|
---@param args {desktop_file: string} The path to the .desktop file
|
|
function dock:pin_element(args)
|
|
if not args then return end
|
|
local e = args.desktop_file
|
|
|
|
self:emit_signal('dock::pin_element', e)
|
|
|
|
self:write_elements_to_file_async()
|
|
end
|
|
|
|
function dock:add_start_element()
|
|
local widget = wibox.widget {
|
|
{
|
|
{
|
|
{
|
|
{
|
|
widget = wibox.widget.imagebox,
|
|
image = gfilesystem.get_configuration_dir() .. 'src/assets/CT.svg',
|
|
valign = 'center',
|
|
halign = 'center',
|
|
resize = true,
|
|
id = 'icon_role',
|
|
},
|
|
widget = wibox.container.constraint,
|
|
width = beautiful.user_config.dock_icon_size,
|
|
height = beautiful.user_config.dock_icon_size,
|
|
},
|
|
widget = wibox.container.margin,
|
|
margins = dpi(5),
|
|
},
|
|
widget = wibox.container.constraint,
|
|
width = beautiful.user_config.dock_icon_size + dpi(10), -- + margins
|
|
height = beautiful.user_config.dock_icon_size + dpi(10),
|
|
strategy = 'exact',
|
|
},
|
|
bg = beautiful.colorscheme.bg1,
|
|
shape = beautiful.shape[8],
|
|
widget = wibox.container.background,
|
|
}
|
|
|
|
hover.bg_hover {
|
|
widget = widget,
|
|
overlay = 12,
|
|
press_overlay = 24,
|
|
}
|
|
|
|
widget:buttons(gtable.join {
|
|
abutton({}, 1, function()
|
|
capi.awesome.emit_signal('application_launcher::show')
|
|
end),
|
|
})
|
|
return widget
|
|
end
|
|
|
|
function dock:unpin_element(args)
|
|
if not args then return end
|
|
|
|
for index, value in ipairs(elements['pinned']) do
|
|
if value == args.desktop_file then
|
|
table.remove(elements['pinned'], index)
|
|
break;
|
|
end
|
|
end
|
|
self:emit_signal('dock::unpin_element', args.desktop_file)
|
|
|
|
self:write_elements_to_file_async()
|
|
end
|
|
|
|
function dock:get_all_elements()
|
|
return elements
|
|
end
|
|
|
|
function dock:get_applauncher_starter_element()
|
|
return elements['applauncher_starter']
|
|
end
|
|
|
|
function dock:get_pinned_elements()
|
|
return elements['pinned']
|
|
end
|
|
|
|
function dock:get_running_elements()
|
|
return elements['running']
|
|
end
|
|
|
|
function dock:get_dock_for_screen(screen)
|
|
return instances[screen]
|
|
end
|
|
|
|
local function check_for_dock_hide(self, a_popup)
|
|
if self.cm_open then return end
|
|
local clients_on_tag = self.screen.selected_tag:clients()
|
|
|
|
-- If there is no client on the current tag show the dock
|
|
if #clients_on_tag < 1 then
|
|
self.visible = true
|
|
return
|
|
end
|
|
|
|
-- If there is a maximized client hide the dock and if fullscreened hide the activation area
|
|
for _, client in ipairs(clients_on_tag) do
|
|
if client.maximized or client.fullscreen then
|
|
dock.visible = false
|
|
if client.fullscreen then
|
|
a_popup.visible = false
|
|
capi.awesome.emit_signal('notification_center_activation::toggle', self.screen, false)
|
|
end
|
|
elseif not client.fullscreen then
|
|
a_popup.visible = true
|
|
capi.awesome.emit_signal('notification_center_activation::toggle', self.screen, true)
|
|
end
|
|
end
|
|
|
|
if self.screen == capi.mouse.screen then
|
|
local minimized = false
|
|
for _, c in ipairs(clients_on_tag) do
|
|
if c.minimized then
|
|
minimized = true
|
|
else
|
|
minimized = false
|
|
local y = c:geometry().y
|
|
local h = c.height
|
|
if (y + h) >= self.screen.geometry.height - beautiful.user_config.dock_icon_size - 35 then
|
|
self.visible = false
|
|
return
|
|
else
|
|
self.visible = true
|
|
end
|
|
end
|
|
end
|
|
if minimized then
|
|
self.visible = true
|
|
end
|
|
else
|
|
self.visible = false
|
|
end
|
|
end
|
|
|
|
function dock:activation_area()
|
|
local activation = apopup {
|
|
widget = {
|
|
width = self.screen.geometry.width / 4,
|
|
height = 1,
|
|
strategy = 'exact',
|
|
widget = wibox.container.constraint,
|
|
},
|
|
ontop = true,
|
|
bg = gcolor.transparent,
|
|
visible = false,
|
|
screen = self.screen,
|
|
type = 'dock',
|
|
placement = function(c) aplacement.bottom(c) end,
|
|
}
|
|
|
|
-- Call the function every second to check if the dock needs to be hidden
|
|
local dock_hide = gtimer {
|
|
timeout = 1,
|
|
autostart = true,
|
|
call_now = true,
|
|
callback = function()
|
|
check_for_dock_hide(self, activation)
|
|
end,
|
|
}
|
|
|
|
activation:connect_signal('mouse::enter', function()
|
|
if #self.screen.clients < 1 then
|
|
self.visible = true
|
|
dock_hide:stop()
|
|
return
|
|
end
|
|
for _, c in ipairs(self.screen.clients) do
|
|
if not c.fullscreen then
|
|
self.visible = true
|
|
dock_hide:stop()
|
|
end
|
|
end
|
|
end)
|
|
|
|
self:connect_signal('mouse::enter', function()
|
|
dock_hide:stop()
|
|
end)
|
|
|
|
self:connect_signal('mouse::leave', function()
|
|
--[[ if cm_open then
|
|
return
|
|
end ]]
|
|
check_for_dock_hide(self, activation)
|
|
dock_hide:again()
|
|
end)
|
|
end
|
|
|
|
function dock.new(args)
|
|
args = args or {}
|
|
|
|
local w = apopup {
|
|
widget = {
|
|
{
|
|
{
|
|
spacing = dpi(5),
|
|
id = 'applauncher_starter',
|
|
layout = wibox.layout.fixed.horizontal,
|
|
},
|
|
wibox.widget.separator {
|
|
forced_width = dpi(2),
|
|
forced_height = dpi(20),
|
|
thickness = dpi(2),
|
|
color = beautiful.colorscheme.border_color,
|
|
},
|
|
{
|
|
spacing = dpi(5),
|
|
id = 'pinned',
|
|
layout = wibox.layout.fixed.horizontal,
|
|
},
|
|
{
|
|
id = 'running',
|
|
spacing = dpi(5),
|
|
layout = wibox.layout.fixed.horizontal,
|
|
},
|
|
spacing = dpi(10),
|
|
id = 'elements',
|
|
layout = wibox.layout.fixed.horizontal,
|
|
},
|
|
widget = wibox.container.margin,
|
|
margins = dpi(5),
|
|
},
|
|
ontop = true,
|
|
visible = true,
|
|
placement = function(c) aplacement.bottom(c, { margins = dpi(10) }) end,
|
|
bg = beautiful.colorscheme.bg,
|
|
screen = args.screen,
|
|
}
|
|
|
|
gtable.crush(w, dock)
|
|
|
|
instances[args.screen] = w
|
|
|
|
w:activation_area()
|
|
|
|
w.task_list = awidget.tasklist {
|
|
screen = args.screen,
|
|
layout = wibox.layout.fixed.horizontal,
|
|
filter = awidget.tasklist.filter.alltags,
|
|
update_function = function(widget, _, _, _, clients)
|
|
widget:reset()
|
|
|
|
if #clients == 0 then
|
|
return widget
|
|
end
|
|
|
|
widget:add {
|
|
{
|
|
widget = wibox.widget.separator,
|
|
forced_height = dpi(20),
|
|
forced_width = dpi(2),
|
|
thickness = dpi(2),
|
|
color = beautiful.colorscheme.border_color,
|
|
},
|
|
widget = wibox.container.margin,
|
|
right = dpi(5),
|
|
}
|
|
|
|
for _, client in ipairs(clients) do
|
|
local element = wibox.widget {
|
|
{
|
|
{
|
|
{
|
|
{
|
|
widget = wibox.widget.imagebox,
|
|
image = client.icon,
|
|
valign = 'center',
|
|
halign = 'center',
|
|
resize = true,
|
|
id = 'icon_role',
|
|
},
|
|
widget = wibox.container.constraint,
|
|
width = beautiful.user_config.dock_icon_size,
|
|
height = beautiful.user_config.dock_icon_size,
|
|
},
|
|
widget = wibox.container.margin,
|
|
margins = dpi(5),
|
|
},
|
|
widget = wibox.container.constraint,
|
|
width = beautiful.user_config.dock_icon_size + dpi(10), -- + margins
|
|
height = beautiful.user_config.dock_icon_size + dpi(10),
|
|
strategy = 'exact',
|
|
},
|
|
bg = beautiful.colorscheme.bg1,
|
|
shape = beautiful.shape[8],
|
|
widget = wibox.container.background,
|
|
}
|
|
|
|
hover.bg_hover {
|
|
widget = element,
|
|
overlay = 12,
|
|
press_overlay = 24,
|
|
}
|
|
|
|
element:buttons(gtable.join(
|
|
abutton({}, 1, function()
|
|
if client == client.focus then
|
|
client.minimized = true
|
|
else
|
|
if client.first_tag then
|
|
client.first_tag:view_only()
|
|
end
|
|
client:emit_signal('request::activate')
|
|
client:raise()
|
|
end
|
|
end),
|
|
abutton({}, 3, function()
|
|
--TODO: Add context menu with options
|
|
end)
|
|
))
|
|
|
|
widget:add(element)
|
|
widget:set_spacing(dpi(5))
|
|
end
|
|
|
|
return widget
|
|
end,
|
|
}
|
|
|
|
w.widget.elements.applauncher_starter:add(w:add_start_element())
|
|
|
|
w.widget.elements.running:add(w.task_list)
|
|
|
|
w:connect_signal('dock::element_added', function(_, widget)
|
|
w.widget.elements.pinned:add(widget)
|
|
end)
|
|
|
|
w:connect_signal('dock::element_removed', function(_, widget)
|
|
w.widget.elements.pinned:remove_widgets(widget)
|
|
end)
|
|
|
|
w:connect_signal('dock::pin_element', function(_, element)
|
|
w:get_element_widget(element)
|
|
end)
|
|
|
|
capi.awesome.connect_signal('dock::pin_element', function(args)
|
|
w:pin_element(args)
|
|
end)
|
|
|
|
w:connect_signal('dock::unpin_element', function(_, widget)
|
|
w:remove_element_widget(widget)
|
|
end)
|
|
|
|
w:read_elements_from_file_async()
|
|
|
|
return w
|
|
end
|
|
|
|
return setmetatable(dock, { __call = function(_, ...) return dock.new(...) end })
|