Files
crylia-theme/awesome/src/modules/crylia_bar/dock.lua

494 lines
14 KiB
Lua

--------------------------------------------------------------------------------------------------------------
-- This is the statusbar, every widget, module and so on is combined to all the stuff you see on the screen --
--------------------------------------------------------------------------------------------------------------
-- Awesome Libs
local awful = require("awful")
local dpi = require("beautiful").xresources.apply_dpi
local Gio = require("lgi").Gio
local gears = require("gears")
local wibox = require("wibox")
local capi = {
awesome = awesome,
client = client,
mouse = mouse,
}
local json = require("src.lib.json-lua.json-lua")
local icondir = awful.util.getdir("config") .. "src/assets/icons/context_menu/"
local cm = require("src.modules.context_menu")
return function(screen)
local cm_open = false
local dock_element_ammount = 0
---Creates a new program widget for the dock
---@param program string | function The name of the .desktop file
---@param size number The size of the widget
---@return widox.widget | nil The widget or nil if the program is not found
local function create_dock_element(program, size)
local dock_element = wibox.widget {
{
{
{
{
resize = true,
widget = wibox.widget.imagebox,
image = program.icon or "",
valign = "center",
halign = "center",
id = "icon",
},
id = "icon_container",
strategy = "exact",
width = dpi(size),
height = dpi(size),
widget = wibox.container.constraint
},
margins = dpi(5),
widget = wibox.container.margin,
id = "margin"
},
shape = function(cr, width, height)
gears.shape.rounded_rect(cr, width, height, dpi(10))
end,
bg = Theme_config.dock.element_bg,
fg = "#000000",
widget = wibox.container.background,
id = "background"
},
top = dpi(5),
left = dpi(5),
right = dpi(5),
widget = wibox.container.margin
}
Hover_signal(dock_element.background, Theme_config.dock.element_focused_bg .. "dd")
local DAI = Gio.DesktopAppInfo.new_from_filename(program.desktop_file)
if not DAI then return end
local action_entries = {}
for _, action in ipairs(program.actions) do
table.insert(action_entries, {
name = Gio.DesktopAppInfo.get_action_name(DAI, action) or "",
icon = action.icon or icondir .. "entry.svg",
callback = function()
Gio.DesktopAppInfo.launch_action(DAI, action)
end
})
end
table.insert(action_entries, {
name = "Remove from Dock",
icon = icondir .. "entry.svg",
callback = function()
local data = io.open("/home/crylia/.config/awesome/src/config/dock.json", "r")
if not data then
return
end
local dock = json:decode(data:read("a"))
data:close()
for i, v in ipairs(dock) do
if v.desktop_file == program.desktop_file then
if type(dock) == "table" then
table.remove(dock, i)
end
break
end
end
data = io.open("/home/crylia/.config/awesome/src/config/dock.json", "w")
if not data then
return
end
data:write(json:encode(dock))
data:close()
capi.awesome.emit_signal("dock::changed")
end
})
local context_menu = cm({
entries = action_entries
})
dock_element:buttons(gears.table.join(
awful.button({
modifiers = {},
button = 1,
on_release = function()
Gio.AppInfo.launch_uris_async(Gio.AppInfo.create_from_commandline(program.exec, nil, 0))
end
}),
awful.button({
modifiers = {},
button = 3,
on_release = function()
if not context_menu then
return
end
-- add offset so mouse is above widget, this is so the mouse::leave event triggers always
context_menu.x = capi.mouse.coords().x - 10
context_menu.y = capi.mouse.coords().y + 10 - context_menu.height
context_menu.visible = not context_menu.visible
cm_open = context_menu.visible
end
})
))
capi.awesome.connect_signal(
"context_menu::hide",
function()
cm_open = false
capi.awesome.emit_signal("dock::check_for_dock_hide")
end
)
awful.tooltip {
objects = { dock_element },
text = program.name,
mode = "outside",
preferred_alignments = "middle",
margins = dpi(10)
}
dock_element_ammount = dock_element_ammount + 1
return dock_element
end
--- Indicators under the elements to indicate various open states
local function create_incicator_widget()
local container = { layout = wibox.layout.flex.horizontal }
local data = io.open("/home/crylia/.config/awesome/src/config/dock.json", "r")
if not data then
return
end
local prog = json:decode(data:read("a"))
data:close()
for _, pr in ipairs(prog) do
local indicators = { layout = wibox.layout.flex.horizontal, spacing = dpi(5) }
local col = Theme_config.dock.indicator_bg
for _, c in ipairs(capi.client.get()) do
local icon_name = string.lower(pr.icon)
if not c or not c.valid then return end
local cls = c.class or ""
local class = string.lower(cls)
icon_name = string.match(icon_name, ".*/(.*)%.[svg|png]")
if class == icon_name or class:match(icon_name) or icon_name:match(class) then
if c == capi.client.focus then
col = Theme_config.dock.indicator_focused_bg
elseif c.urgent then
col = Theme_config.dock.indicator_urgent_bg
elseif c.maximized then
col = Theme_config.dock.indicator_maximized_bg
elseif c.minimized then
col = Theme_config.dock.indicator_minimized_bg
elseif c.fullscreen then
col = Theme_config.dock.indicator_fullscreen_bg
else
col = Theme_config.dock.indicator_bg
end
table.insert(indicators, wibox.widget {
widget = wibox.container.background,
shape = gears.shape.rounded_rect,
forced_height = dpi(3),
bg = col,
forced_width = dpi(5),
})
end
end
table.insert(container, wibox.widget {
indicators,
forced_height = dpi(5),
forced_width = dpi(User_config.dock_icon_size),
left = dpi(5),
right = dpi(5),
widget = wibox.container.margin,
})
end
return wibox.widget {
container,
bottom = dpi(5),
widget = wibox.container.margin,
}
end
--- The container bar where the elements/program widgets sit in
local dock = awful.popup {
widget = wibox.container.background,
ontop = true,
bg = Theme_config.dock.bg,
visible = true,
screen = screen,
type = "dock",
height = dpi(User_config.dock_icon_size + 10),
placement = function(c) awful.placement.bottom(c, { margins = dpi(10) }) end,
shape = function(cr, width, height)
gears.shape.rounded_rect(cr, width, height, dpi(16))
end
}
--- A fakedock to send a signal when the mouse is over it
local fakedock = awful.popup {
widget = wibox.container.background,
ontop = true,
bg = '#00000000',
visible = true,
screen = screen,
type = "dock",
id = "fakedock",
height = dpi(10),
placement = function(c) awful.placement.bottom(c) end,
}
--- List of all elements/program widgets
local dock_elements = { layout = wibox.layout.fixed.horizontal }
--- This function creates a list with all dock elements/program widgets
---@return table|nil string list of widgets
local function get_dock_elements()
dock_element_ammount = 0
dock_elements = { layout = wibox.layout.fixed.horizontal }
local data = io.open("/home/crylia/.config/awesome/src/config/dock.json", "r")
if not data then
return
end
local dock_data = json:decode(data:read("a"))
data:close()
for _, program in ipairs(dock_data) do
table.insert(dock_elements, create_dock_element(program, User_config.dock_icon_size))
end
dock:setup {
dock_elements,
create_incicator_widget(),
layout = wibox.layout.fixed.vertical
}
end
get_dock_elements()
--- Function to get an empty list with the same ammount as dock_element
local function get_fake_elements()
local fake_elements = { layout = wibox.layout.fixed.horizontal }
for i = 0, dock_element_ammount, 1 do
fake_elements[i] = wibox.widget {
bg = '00000000',
forced_width = User_config.dock_icon_size + dpi(20),
forced_height = dpi(10),
id = "fake",
widget = wibox.container.background
}
end
return fake_elements
end
fakedock:setup {
get_fake_elements(),
type = 'dock',
layout = wibox.layout.fixed.vertical
}
---Check if the dock needs to be hidden, I also put the topbar check here since it shares that logic
---@param s screen The screen to check for hide
local function check_for_dock_hide(s)
local clients_on_tag = s.selected_tag:clients()
-- If there is no client on the current tag show the dock
if #clients_on_tag < 1 then
dock.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
fakedock.visible = false
capi.awesome.emit_signal("notification_center_activation::toggle", s, false)
end
elseif not client.fullscreen then
fakedock.visible = true
capi.awesome.emit_signal("notification_center_activation::toggle", s, true)
end
end
if s == 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) >= s.geometry.height - User_config.dock_icon_size - 35 then
dock.visible = false
return
else
dock.visible = true
end
end
end
if minimized then
dock.visible = true
end
else
dock.visible = false
end
end
-- Call the function every second to check if the dock needs to be hidden
local dock_intelligent_hide = gears.timer {
timeout = 1,
autostart = true,
call_now = true,
callback = function()
check_for_dock_hide(screen)
end
}
--- Hover function to show the dock
fakedock:connect_signal(
"mouse::enter",
function()
if #screen.clients < 1 then
dock.visible = true
dock_intelligent_hide:stop()
return
end
for _, c in ipairs(screen.clients) do
if not c.fullscreen then
dock.visible = true
dock_intelligent_hide:stop()
end
end
end
)
capi.client.connect_signal(
"manage",
function()
check_for_dock_hide(screen)
dock:setup {
dock_elements,
create_incicator_widget(),
layout = wibox.layout.fixed.vertical
}
fakedock:setup {
get_fake_elements(),
type = 'dock',
layout = wibox.layout.fixed.vertical
}
end
)
capi.client.connect_signal(
"property::minimized",
function()
check_for_dock_hide(screen)
dock:setup {
dock_elements,
create_incicator_widget(),
layout = wibox.layout.fixed.vertical
}
fakedock:setup {
get_fake_elements(),
type = 'dock',
layout = wibox.layout.fixed.vertical
}
end
)
capi.client.connect_signal(
"unmanage",
function()
check_for_dock_hide(screen)
dock:setup {
dock_elements,
create_incicator_widget(),
layout = wibox.layout.fixed.vertical
}
fakedock:setup {
get_fake_elements(),
type = 'dock',
layout = wibox.layout.fixed.vertical
}
end
)
capi.client.connect_signal(
"focus",
function()
check_for_dock_hide(screen)
dock:setup {
dock_elements,
create_incicator_widget(),
layout = wibox.layout.fixed.vertical
}
fakedock:setup {
get_fake_elements(),
type = 'dock',
layout = wibox.layout.fixed.vertical
}
end
)
capi.awesome.connect_signal(
"dock::changed",
function()
get_dock_elements()
dock:setup {
dock_elements,
create_incicator_widget(),
layout = wibox.layout.fixed.vertical
}
fakedock:setup {
get_fake_elements(),
type = 'dock',
layout = wibox.layout.fixed.vertical
}
end
)
capi.awesome.connect_signal(
"dock::check_for_dock_hide",
function()
dock_intelligent_hide:again()
end
)
dock:connect_signal(
"mouse::enter",
function()
dock_intelligent_hide:stop()
end
)
dock:connect_signal(
"mouse::leave",
function()
if cm_open then
return
end
check_for_dock_hide(screen)
dock_intelligent_hide:again()
end
)
dock:setup {
dock_elements,
create_incicator_widget(),
layout = wibox.layout.fixed.vertical
}
end