Reworked a whole lot. New config file and theme config file for easier changes. Did #19. And much more

This commit is contained in:
Kievits Rene
2022-06-14 06:33:33 +02:00
parent fd74ab9fb4
commit 521d392769
60 changed files with 2216 additions and 1450 deletions

View File

@@ -0,0 +1,74 @@
local gears = require("gears")
local GLib = require("lgi").GLib
---Will return every $XDG_DATA_DIRS
---@return table
local function get_paths()
local dirs = {}
local dir
for _, value in ipairs(GLib.get_system_data_dirs()) do
dir = GLib.build_filenamev({ value, "applications" })
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
end
dir = GLib.build_filenamev({ GLib.get_user_data_dir(), "applications" })
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
return dirs
end
---Returns every .desktop file into a table
---@param file table .desktop files
---@return table
return function(file)
if not file or file == "" then
return
end
local handler = nil
for _, dir in ipairs(get_paths()) do
handler = io.open(dir .. "/" .. file, "r")
if handler then
break
end
end
if not handler then
return
end
local desktop_table = {}
while true do
local line = handler:read()
if not line then
break
end
if line:match("[Desktop Entry]") then
while true do
local property = handler:read()
if not property then
break
end
if property:match("^%[(.+)%]") then
return desktop_table
end
if property:match("^Name=") then
desktop_table["Name"] = property:match("Name=(.+)")
elseif property:match("^Exec") then
-- Second match is to remove the %u and %F some applications use to open a URI. It's not needed here
desktop_table["Exec"] = property:match("Exec=(.+)"):gsub("%%u", ""):gsub("%%F", "")
elseif property:match("^Icon=") then
desktop_table["Icon"] = property:match("Icon=(.+)")
end
end
end
end
return desktop_table
end

View File

@@ -1,95 +0,0 @@
-----------------------------------------------------
-- Helper to get icons from a program/program name --
-----------------------------------------------------
local icon_cache = {}
-- tries to find a matching file name in /usr/share/icons/THEME/RESOLUTION/apps/ and if not found tried with first letter
-- as uppercase, this should get almost all icons to work with the papirus theme atleast
-- TODO: try with more icon themes
function Get_icon(theme, client, program_string, class_string, is_steam)
client = client or nil
program_string = program_string or nil
class_string = class_string or nil
is_steam = is_steam or nil
if theme and (client or program_string or class_string) then
local clientName
if is_steam then
clientName = "steam_icon_" .. tostring(client) .. ".svg"
elseif client then
if client.class then
clientName = string.lower(client.class:gsub(" ", "")) .. ".svg"
elseif client.name then
clientName = string.lower(client.name:gsub(" ", "")) .. ".svg"
else
if client.icon then
return client.icon
else
return "/usr/share/icons/Papirus-Dark/128x128/apps/application-default-icon.svg"
end
end
else
if program_string then
clientName = program_string .. ".svg"
else
clientName = class_string .. ".svg"
end
end
for _, icon in ipairs(icon_cache) do
if icon:match(clientName) then
return icon
end
end
local resolutions = { "128x128", "96x96", "64x64", "48x48", "42x42", "32x32", "24x24", "16x16" }
for _, res in ipairs(resolutions) do
local iconDir = "/usr/share/icons/" .. theme .. "/" .. res .. "/apps/"
local ioStream = io.open(iconDir .. clientName, "r")
if ioStream ~= nil then
icon_cache[#icon_cache + 1] = iconDir .. clientName
return iconDir .. clientName
else
clientName = clientName:gsub("^%l", string.upper)
iconDir = "/usr/share/icons/" .. theme .. "/" .. res .. "/apps/"
ioStream = io.open(iconDir .. clientName, "r")
if ioStream ~= nil then
icon_cache[#icon_cache + 1] = iconDir .. clientName
return iconDir .. clientName
elseif not class_string then
return "/usr/share/icons/Papirus-Dark/128x128/apps/application-default-icon.svg"
else
clientName = class_string .. ".svg"
iconDir = "/usr/share/icons/" .. theme .. "/" .. res .. "/apps/"
ioStream = io.open(iconDir .. clientName, "r")
if ioStream ~= nil then
icon_cache[#icon_cache + 1] = iconDir .. clientName
return iconDir .. clientName
else
return "/usr/share/icons/Papirus-Dark/128x128/apps/application-default-icon.svg"
end
end
end
end
if client then
return "/usr/share/icons/Papirus-Dark/128x128/apps/application-default-icon.svg"
end
end
end
--———————————No swiches?———————————
--⠀⣞⢽⢪⢣⢣⢣⢫⡺⡵⣝⡮⣗⢷⢽⢽⢽⣮⡷⡽⣜⣜⢮⢺⣜⢷⢽⢝⡽⣝
--⠸⡸⠜⠕⠕⠁⢁⢇⢏⢽⢺⣪⡳⡝⣎⣏⢯⢞⡿⣟⣷⣳⢯⡷⣽⢽⢯⣳⣫⠇
--⠀⠀⢀⢀⢄⢬⢪⡪⡎⣆⡈⠚⠜⠕⠇⠗⠝⢕⢯⢫⣞⣯⣿⣻⡽⣏⢗⣗⠏⠀
--⠀⠪⡪⡪⣪⢪⢺⢸⢢⢓⢆⢤⢀⠀⠀⠀⠀⠈⢊⢞⡾⣿⡯⣏⢮⠷⠁⠀⠀
--⠀⠀⠀⠈⠊⠆⡃⠕⢕⢇⢇⢇⢇⢇⢏⢎⢎⢆⢄⠀⢑⣽⣿⢝⠲⠉⠀⠀⠀⠀
--⠀⠀⠀⠀⠀⡿⠂⠠⠀⡇⢇⠕⢈⣀⠀⠁⠡⠣⡣⡫⣂⣿⠯⢪⠰⠂⠀⠀⠀⠀
--⠀⠀⠀⠀⡦⡙⡂⢀⢤⢣⠣⡈⣾⡃⠠⠄⠀⡄⢱⣌⣶⢏⢊⠂⠀⠀⠀⠀⠀⠀
--⠀⠀⠀⠀⢝⡲⣜⡮⡏⢎⢌⢂⠙⠢⠐⢀⢘⢵⣽⣿⡿⠁⠁⠀⠀⠀⠀⠀⠀⠀
--⠀⠀⠀⠀⠨⣺⡺⡕⡕⡱⡑⡆⡕⡅⡕⡜⡼⢽⡻⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
--⠀⠀⠀⠀⣼⣳⣫⣾⣵⣗⡵⡱⡡⢣⢑⢕⢜⢕⡝⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
--⠀⠀⠀⣴⣿⣾⣿⣿⣿⡿⡽⡑⢌⠪⡢⡣⣣⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
--⠀⠀⠀⡟⡾⣿⢿⢿⢵⣽⣾⣼⣘⢸⢸⣞⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
--⠀⠀⠀⠀⠁⠇⠡⠩⡫⢿⣝⡻⡮⣒⢽⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
--—————————————————————————————

View File

@@ -0,0 +1,106 @@
local theme_index = { mt = {} }
theme_index.new = function(cls, theme, basedirs)
local self = {}
setmetatable(self, { __index = cls })
self.icon_theme_name = theme or User_config.icon_theme
self.base_dir = nil
self["Directories"] = {}
self["Inherits"] = {}
self.per_directory_keys = {}
local basedir, handler = nil, nil
for _, dir in ipairs(basedirs) do
basedir = dir .. "/" .. self.icon_theme_name
handler = io.open(basedir .. "/index.theme", "r")
if handler then
break
end
end
if not handler then
return self
end
self.base_dir = basedir
while true do
local line = handler:read()
if not line then
break
end
local header = line:match("^%[(.+)%]$")
if header then
if header == "Icon Theme" then
while true do
local property = handler:read()
if not property then
break
end
if property:match("^%[(.+)%]$") then
handler:seek("cur", -string.len(property) - 1)
break
end
local key, value = property:match("^(%w+)=(.*)$")
if key == "Directories" or key == "Inherits" then
string.gsub(value, "([^,]+),?", function(match)
table.insert(self[key], match)
end)
end
end
else
local keys = {}
while true do
local property = handler:read()
if not property then
break
end
if property:match("^%[(.+)%]$") then
handler:seek("cur", -string.len(property) - 1)
break
end
local key, value = property:match("^(%w+)=(%w+)$")
if key == "Size" or key == "MinSize" or key == "MaxSize" or key == "Threshold" then
keys[key] = tonumber(value)
elseif key == "Type" then
keys[key] = value
end
end
if keys["Size"] then
if not keys["Type"] then keys["Type"] = "Threshold" end
if not keys["MinSize"] then keys["MinSize"] = keys["Size"] end
if not keys["MaxSize"] then keys["MaxSize"] = keys["Size"] end
if not keys["Threshold"] then keys["Threshold"] = 2 end
self.per_directory_keys[header] = keys
end
end
end
end
handler:close()
return self
end
theme_index.get_subdirectories = function(self)
return self["Directories"]
end
theme_index.get_inherits = function(self)
return self["Inherits"]
end
theme_index.mt.__call = function(cls, theme, basedirs)
return theme_index.new(cls, theme, basedirs)
end
return setmetatable(theme_index, theme_index.mt)

View File

@@ -0,0 +1,236 @@
-----------------------------------------------------
-- Helper to get icons from a program/program name --
-----------------------------------------------------
local gears = require("gears")
local GLib = require("lgi").GLib
local theme_index = require("src.tools.theme_index")
local function get_basedir()
local dirs = {}
local dir = GLib.build_filenamev({ GLib.get_home_dir(), ".icons" })
if gears.filesystem.dir_readable(dir) then
table.insert(dirs, dir)
end
dir = GLib.build_filenamev({ GLib.get_user_data_dir(), "icons" })
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
for _, value in ipairs(GLib.get_system_data_dirs()) do
dir = GLib.build_filenamev({ value, ".icons" })
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
end
local need_usr_share_pixmaps = true
for _, value in ipairs(GLib.get_system_data_dirs()) do
dir = GLib.build_filenamev({ value, ".icons" })
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
if dir == "/usr/share/pixmaps" then
need_usr_share_pixmaps = false
end
end
dir = "/usr/share/pixmaps"
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
if need_usr_share_pixmaps and gears.filesystem.dir_readable(dir) then
table.insert(dirs, dir)
end
table.insert(dirs, "/usr/share/icons")
return dirs
end
local xdg_icon_lookup = { mt = {} }
local icon_cache = {}
xdg_icon_lookup.new = function(theme, base_dirs)
local self = {}
self.icon_theme = theme or User_config.icon_theme
self.base_directories = base_dirs or get_basedir()
self.file_extension = { "svg", "png", "xpm" }
if not icon_cache[self.icon_theme] then
icon_cache[self.icon_theme] = {}
end
local cache_key = table.concat(self.base_directories, ":")
if not icon_cache[self.icon_theme][cache_key] then
icon_cache[self.icon_theme][cache_key] = theme_index(self.icon_theme, self.base_directories)
end
self.theme_index = icon_cache[self.icon_theme][cache_key]
return setmetatable(self, { __index = xdg_icon_lookup })
end
---Look for an fallback icon
---@param iconname any
---@return string
local function lookup_fallback_icon(self, iconname)
for _, dir in ipairs(self.base_directories) do
for _, ext in ipairs(self.file_extension) do
local filename = string.format("%s/%s.%s", dir, iconname, ext)
if gears.filesystem.file_readable(filename) then
return filename
end
end
end
end
---Checkes if the size equals the actual icon size
---@param subdir any
---@param iconsize any
---@return boolean
local function directory_matches_size(self, subdir, iconsize)
local type, size, min_size, max_size, threshold = self.theme_index.per_directory_keys[subdir]["Type"],
self.theme_index.per_directory_keys[subdir]["Size"], self.theme_index.per_directory_keys[subdir]["MinSize"],
self.theme_index.per_directory_keys[subdir]["MaxSize"], self.theme_index.per_directory_keys[subdir]["Threshold"]
if type == "Fixed" then
return iconsize == size
elseif type == "Scalable" then
return iconsize >= min_size and iconsize <= max_size
elseif type == "Threshold" then
return iconsize >= size - threshold and iconsize <= size + threshold
end
return false
end
---Returns how far off the size is from the actual icon size
---@param subdir table
---@param iconsize number
---@return number
local function directory_size_distance(self, subdir, iconsize)
local type, size, min_size, max_size, threshold = self.theme_index.per_directory_keys[subdir]["Type"],
self.theme_index.per_directory_keys[subdir]["Size"], self.theme_index.per_directory_keys[subdir]["MinSize"],
self.theme_index.per_directory_keys[subdir]["MaxSize"], self.theme_index.per_directory_keys[subdir]["Threshold"]
if type and min_size and max_size and threshold then
if type == "Fixed" then
return math.abs(size - iconsize)
elseif type == "Scalable" then
if iconsize < min_size then
return min_size - iconsize
elseif iconsize > max_size then
return iconsize - max_size
end
return 0
elseif type == "Threshold" then
if iconsize < size - threshold then
return min_size - iconsize
elseif iconsize > size + threshold then
return iconsize - max_size
end
return 0
end
end
return 0xffffffff
end
---Checks each and every sub directory for an icon
---@param iconname any
---@param size any
---@return string path_to_icon
local function lookup_icon(self, iconname, size)
local already_checked = {}
for _, subdir in ipairs(self.theme_index:get_subdirectories()) do
for _, dir in ipairs(self.base_directories) do
for _, ext in ipairs(self.file_extension) do
if directory_matches_size(self, subdir, size) then
local filename = string.format("%s/%s/%s/%s.%s", dir, self.icon_theme, subdir, iconname, ext)
if gears.filesystem.file_readable(filename) then
return filename
else
already_checked[filename] = true
end
end
end
end
end
local min_size = 0xffffffff
local closest_filename = nil
for _, subdir in ipairs(self.theme_index:get_subdirectories()) do
local dist = directory_size_distance(self, subdir, size)
if dist < min_size then
for _, dir in ipairs(self.base_directories) do
for _, ext in ipairs(self.file_extension) do
local filename = string.format("%s/%s/%s/%s.%s", dir, self.icon_theme, subdir, iconname, ext)
if not already_checked[filename] then
if gears.filesystem.file_readable(filename) then
closest_filename = filename
min_size = dist
end
end
end
end
end
end
return closest_filename or nil
end
---Check if the icon inherits from another icon theme and search that for an icon
---@param icon any
---@param size any
---@param self any
---@return string path_to_icon
local function find_icon_helper(self, icon, size)
local filename = lookup_icon(self, icon, size)
if filename then return filename end
filename = lookup_icon(self, icon:lower(), size)
if filename then return filename end
for _, parent in ipairs(self.theme_index:get_inherits()) do
filename = find_icon_helper(xdg_icon_lookup(parent, self.base_directories), icon, size)
if filename then return filename end
end
return nil
end
local iconcache = {}
---Takes an icon and its props and theme to search for it inside the theme
---@param icon any
---@param size any
---@return string path_to_icon
function xdg_icon_lookup:find_icon(icon, size)
if iconcache[icon] then
return iconcache[icon]
end
size = size or 64
if not icon or icon == "" then return nil end
local filename = find_icon_helper(self, icon, size)
if filename then
iconcache[icon] = filename
return filename
end
filename = find_icon_helper(xdg_icon_lookup("hicolor", self.base_directories), icon, size)
if filename then
iconcache[icon] = filename
return filename
end
filename = lookup_fallback_icon(self, icon)
if filename then
iconcache[icon] = filename
return filename
end
local fallback = gears.color.recolor_image(require("awful").util.getdir("config") .. "src/assets/icons/fallback.svg",
"#ffffff")
iconcache[icon] = fallback
return fallback
end
xdg_icon_lookup.mt.__call = function(_, ...)
return xdg_icon_lookup.new(...)
end
return setmetatable(xdg_icon_lookup, xdg_icon_lookup.mt)