Enhance menubar icon path lookup
The way of icon path lookup for `menubar` is enhanced so that it is based on a theme-oriented way as described in the specification: Icon Theme Specification, Ver. 0.12 http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html To accomplish this: * Add the two new files `icon_theme.lua` and `index_theme.lua`. The former implements an icon lookup algorithm suggested in the URL above. The latter implements a helper object to parse the cache file `index.theme` of which data is used by the former. * Modify `menu_gen.lua` to use the new algorithm. - The implementation of `lookup_category_icons` is changed accordingly. - The values of the field `all_categories.icon_name` are changed file names to icon names, i.e., file extensions which are used to indicate image file formats are removed. * Add the new file `icon_theme_spec.lua` for a unit test for checking if `icon_theme.lua` together with `index_theme.lua` works as expected.
This commit is contained in:
parent
bf630de74e
commit
9ae945c931
|
@ -0,0 +1,262 @@
|
|||
---------------------------------------------------------------------------
|
||||
--- Class module for icon lookup for menubar
|
||||
--
|
||||
-- @author Kazunobu Kuriyama
|
||||
-- @copyright 2015 Kazunobu Kuriyama
|
||||
-- @release @AWESOME_VERSION@
|
||||
-- @classmod menubar.icon_theme
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
-- This implementation is based on the specifications:
|
||||
-- Icon Theme Specification 0.12
|
||||
-- http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-0.12.html
|
||||
|
||||
local beautiful = require("beautiful")
|
||||
local awful = require("awful")
|
||||
local GLib = require("lgi").GLib
|
||||
local Gio = require("lgi").Gio
|
||||
local index_theme = require("menubar.index_theme")
|
||||
|
||||
local ipairs = ipairs
|
||||
local setmetatable = setmetatable
|
||||
local string = string
|
||||
local table = table
|
||||
local math = math
|
||||
local io = io
|
||||
|
||||
-- Returns a table whose element is a path used for icon lookup.
|
||||
-- The names of the directories and the order of them are based on the spec.
|
||||
local get_default_base_directories = function()
|
||||
local dirs = {}
|
||||
|
||||
table.insert(dirs, GLib.get_home_dir() .. "/.icons")
|
||||
for _, dir in ipairs(GLib.get_system_data_dirs()) do
|
||||
table.insert(dirs, dir .. "/icons")
|
||||
end
|
||||
table.insert(dirs, "/usr/share/pixmaps")
|
||||
|
||||
return dirs
|
||||
end
|
||||
|
||||
local is_readable_directory = function(path)
|
||||
local gfile = Gio.File.new_for_path(path)
|
||||
local gfileinfo = gfile:query_info("standard::type,access::can-read",
|
||||
Gio.FileQueryInfoFlags.NONE)
|
||||
if not gfileinfo then return false end -- practically ENOENT
|
||||
local is_dir = (gfileinfo:get_file_type() == "DIRECTORY")
|
||||
local is_readable = gfileinfo:get_attribute_boolean("access::can-read")
|
||||
return is_dir and is_readable
|
||||
end
|
||||
|
||||
local get_pragmatic_base_directories = function()
|
||||
local dirs = {}
|
||||
|
||||
local dir = GLib.get_home_dir() .. "/.icons"
|
||||
if is_readable_directory(dir) then
|
||||
table.insert(dirs, dir)
|
||||
end
|
||||
|
||||
dir = GLib.get_user_data_dir() .. "/icons"
|
||||
if is_readable_directory(dir) then
|
||||
table.insert(dirs, dir)
|
||||
end
|
||||
|
||||
for _, v in ipairs(GLib.get_system_data_dirs()) do
|
||||
dir = v .. "/icons"
|
||||
if is_readable_directory(dir) then
|
||||
table.insert(dirs, dir)
|
||||
end
|
||||
end
|
||||
|
||||
local need_usr_share_pixmaps = true
|
||||
for _, v in ipairs(GLib.get_system_data_dirs()) do
|
||||
dir = v .. "/pixmaps"
|
||||
if is_readable_directory(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 need_usr_share_pixmaps and is_readable_directory(dir) then
|
||||
table.insert(dirs, dir)
|
||||
end
|
||||
|
||||
return dirs
|
||||
end
|
||||
|
||||
local get_default_icon_theme_name = function()
|
||||
local icon_theme_names = { "Adwaita", "gnome", "hicolor" }
|
||||
for _, dir in ipairs(get_pragmatic_base_directories()) do
|
||||
for _, icon_theme_name in ipairs(icon_theme_names) do
|
||||
local filename = string.format("%s/%s/index.theme", dir, icon_theme_name)
|
||||
if awful.util.file_readable(filename) then
|
||||
return icon_theme_name
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local icon_theme = { mt = {} }
|
||||
|
||||
--- Class constructor of `icon_theme`
|
||||
-- @tparam string icon_theme_name Internal name of icon theme
|
||||
-- @tparam table base_directories Paths used for lookup
|
||||
-- @treturn table An instance of the class `icon_theme`
|
||||
icon_theme.new = function(cls, icon_theme_name, base_directories)
|
||||
local icon_theme_name = icon_theme_name or beautiful.icon_theme or get_default_icon_theme_name()
|
||||
local base_directories = base_directories or get_pragmatic_base_directories()
|
||||
|
||||
local self = {}
|
||||
self.icon_theme_name = icon_theme_name
|
||||
self.base_directories = base_directories
|
||||
self.extensions = { "png", "svg", "xpm" }
|
||||
self.index_theme = index_theme(self.icon_theme_name, self.base_directories)
|
||||
|
||||
return setmetatable(self, { __index = cls })
|
||||
end
|
||||
|
||||
local directory_matches_size = function(self, subdirectory, icon_size)
|
||||
local kind, size, min_size, max_size, threshold = self.index_theme:get_per_directory_keys(subdirectory)
|
||||
|
||||
if kind == "Fixed" then
|
||||
return icon_size == size
|
||||
elseif kind == "Scaled" then
|
||||
return icon_size >= min_size and icon_size <= max_size
|
||||
elseif kind == "Threshold" then
|
||||
return icon_size >= size - threshold and icon_size <= size + threshold
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local directory_size_distance = function(self, subdirectory, icon_size)
|
||||
local kind, size, min_size, max_size, threshold = self.index_theme:get_per_directory_keys(subdirectory)
|
||||
|
||||
if kind == "Fixed" then
|
||||
return math.abs(icon_size - size)
|
||||
elseif kind == "Scaled" then
|
||||
if icon_size < min_size then
|
||||
return min_size - icon_size
|
||||
elseif icon_size > max_size then
|
||||
return icon_size - max_size
|
||||
end
|
||||
return 0
|
||||
elseif kind == "Threshold" then
|
||||
if icon_size < size - threshold then
|
||||
return min_size - icon_size
|
||||
elseif icon_size > size + threshold then
|
||||
return icon_size - max_size
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
return 0xffffffff -- Any large number will do.
|
||||
end
|
||||
|
||||
local lookup_icon = function(self, icon_name, icon_size)
|
||||
for _, subdir in ipairs(self.index_theme:get_subdirectories()) do
|
||||
for _, basedir in ipairs(self.base_directories) do
|
||||
for _, ext in ipairs(self.extensions) do
|
||||
if directory_matches_size(self, subdir, icon_size) then
|
||||
local filename = string.format("%s/%s/%s/%s.%s",
|
||||
basedir, self.icon_theme_name, subdir,
|
||||
icon_name, ext)
|
||||
if awful.util.file_readable(filename) then
|
||||
return filename
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local minimal_size = 0xffffffff -- Any large number will do.
|
||||
local closest_filename = nil
|
||||
for _, subdir in ipairs(self.index_theme:get_subdirectories()) do
|
||||
for _, basedir in ipairs(self.base_directories) do
|
||||
for _, ext in ipairs(self.extensions) do
|
||||
local filename = string.format("%s/%s/%s/%s.%s",
|
||||
basedir, self.icon_theme_name, subdir,
|
||||
icon_name, ext)
|
||||
local dist = directory_size_distance(self, subdir, icon_size)
|
||||
if awful.util.file_readable(filename) and dist < minimal_size then
|
||||
closest_filename = filename
|
||||
minimal_size = dist
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if closest_filename then
|
||||
return closest_filename
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local find_icon_path_helper
|
||||
find_icon_path_helper = function(self, icon_name, icon_size)
|
||||
local filename = lookup_icon(self, icon_name, icon_size)
|
||||
if filename then
|
||||
return filename
|
||||
end
|
||||
|
||||
for _, parent in ipairs(self.index_theme:get_inherits()) do
|
||||
local parent_icon_theme = icon_theme(parent, self.base_directories)
|
||||
filename = find_icon_path_helper(parent_icon_theme, icon_name, icon_size)
|
||||
if filename then
|
||||
return filename
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local lookup_fallback_icon = function(self, icon_name)
|
||||
for _, dir in ipairs(self.base_directories) do
|
||||
for _, ext in ipairs(self.extensions) do
|
||||
local filename = string.format("%s/%s.%s",
|
||||
dir,
|
||||
icon_name, ext)
|
||||
if awful.util.file_readable(filename) then
|
||||
return filename
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Look up an image file based on a given icon name and/or a preferable size.
|
||||
-- @tparam string icon_name Icon name to be looked up
|
||||
-- @tparam number icon_size Prefereable icon size
|
||||
-- @treturn string Absolute path to the icon file, or nil if not found
|
||||
icon_theme.find_icon_path = function(self, icon_name, icon_size)
|
||||
if not icon_name or icon_name == "" then
|
||||
return nil
|
||||
end
|
||||
local icon_size = icon_size or 16
|
||||
|
||||
local filename = find_icon_path_helper(self, icon_name, icon_size)
|
||||
if filename then
|
||||
return filename
|
||||
end
|
||||
|
||||
if self.icon_theme_name ~= "hicolor" then
|
||||
filaname = find_icon_path_helper(icon_theme("hicolor", self.base_directories), icon_name, icon_size)
|
||||
if filename then
|
||||
return filename
|
||||
end
|
||||
end
|
||||
|
||||
return lookup_fallback_icon(self, icon_name)
|
||||
end
|
||||
|
||||
icon_theme.mt.__call = function(cls, icon_theme_name, base_directories)
|
||||
return icon_theme.new(cls, icon_theme_name, base_directories)
|
||||
end
|
||||
|
||||
return setmetatable(icon_theme, icon_theme.mt)
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
|
@ -0,0 +1,164 @@
|
|||
---------------------------------------------------------------------------
|
||||
--- Class module for parsing an index.theme file
|
||||
--
|
||||
-- @author Kazunobu Kuriyama
|
||||
-- @copyright 2015 Kazunobu Kuriyama
|
||||
-- @release @AWESOME_VERSION@
|
||||
-- @classmod menubar.index_theme
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
-- This implementation is based on the specifications:
|
||||
-- Icon Theme Specification 0.12
|
||||
-- http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-0.12.html
|
||||
|
||||
local ipairs = ipairs
|
||||
local setmetatable = setmetatable
|
||||
local string = string
|
||||
local table = table
|
||||
local io = io
|
||||
|
||||
-- index.theme groups
|
||||
local ICON_THEME = "Icon Theme"
|
||||
-- index.theme keys
|
||||
local DIRECTORIES = "Directories"
|
||||
local INHERITS = "Inherits"
|
||||
-- per-directory subkeys
|
||||
local TYPE = "Type"
|
||||
local SIZE = "Size"
|
||||
local MINSIZE = "MinSize"
|
||||
local MAXSIZE = "MaxSize"
|
||||
local THRESHOLD = "Threshold"
|
||||
|
||||
local index_theme = { mt = {} }
|
||||
|
||||
--- Class constructor of `index_theme`
|
||||
-- @tparam string icon_theme_name Internal name of icon theme
|
||||
-- @tparam table base_directories Paths used for lookup
|
||||
-- @treturn table An instance of the class `index_theme`
|
||||
index_theme.new = function(cls, icon_theme_name, base_directories)
|
||||
local self = {}
|
||||
setmetatable(self, { __index = cls })
|
||||
|
||||
-- Initialize the fields
|
||||
self.icon_theme_name = icon_theme_name
|
||||
self.base_directory = nil
|
||||
self[DIRECTORIES] = {}
|
||||
self[INHERITS] = {}
|
||||
self.per_directory_keys = {}
|
||||
|
||||
-- base_directory
|
||||
local basedir = nil
|
||||
local handler = nil
|
||||
for _, dir in ipairs(base_directories) do
|
||||
basedir = dir .. "/" .. self.icon_theme_name
|
||||
handler = io.open(basedir .. "/index.theme", "r")
|
||||
if handler then
|
||||
-- Use the index.theme which is found first.
|
||||
break
|
||||
end
|
||||
end
|
||||
if not handler then
|
||||
return self
|
||||
end
|
||||
self.base_directory = basedir
|
||||
|
||||
-- Parse index.theme.
|
||||
while true do
|
||||
local line = handler:read()
|
||||
if not line then
|
||||
break
|
||||
end
|
||||
|
||||
local group_header = "^%[(.+)%]$"
|
||||
local group = line:match(group_header)
|
||||
if group then
|
||||
if group == ICON_THEME then
|
||||
while true do
|
||||
local item = handler:read()
|
||||
if not item then
|
||||
break
|
||||
end
|
||||
if item:match(group_header) then
|
||||
handler:seek("cur", -string.len(item) - 1)
|
||||
break
|
||||
end
|
||||
|
||||
local k, v = item:match("^(%w+)=(.*)$")
|
||||
if k == DIRECTORIES or k == INHERITS then
|
||||
string.gsub(v, "([^,]+),?", function(match)
|
||||
table.insert(self[k], match)
|
||||
end)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- This must be a 'per-directory keys' group
|
||||
local keys = {}
|
||||
|
||||
while true do
|
||||
local item = handler:read()
|
||||
if not item then
|
||||
break
|
||||
end
|
||||
if item:match(group_header) then
|
||||
handler:seek("cur", -string.len(item) - 1)
|
||||
break
|
||||
end
|
||||
|
||||
local k, v = item:match("^(%w+)=(%w+)$")
|
||||
if k == SIZE or k == MINSIZE or k == MAXSIZE or k == THRESHOLD then
|
||||
keys[k] = tonumber(v)
|
||||
elseif k == TYPE then
|
||||
keys[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- Size is a must. Other keys are optional.
|
||||
if keys[SIZE] then
|
||||
-- Set unset keys to the default values.
|
||||
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[group] = keys
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
handler:close()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Table of the values of the `Directories` key
|
||||
-- @treturn table Values of the `Directories` key
|
||||
index_theme.get_subdirectories = function(self)
|
||||
return self[DIRECTORIES]
|
||||
end
|
||||
|
||||
--- Table of the values of the `Inherits` key
|
||||
-- @treturn table Values of the `Inherits` key
|
||||
index_theme.get_inherits = function(self)
|
||||
return self[INHERITS]
|
||||
end
|
||||
|
||||
--- Query (part of) per-directory keys of a given subdirectory name.
|
||||
-- @tparam table subdirectory Icon theme's subdirectory
|
||||
-- @treturn[1] string Value of the `Type` key
|
||||
-- @treturn[2] number Value of the `Size` key
|
||||
-- @treturn[3] number VAlue of the `MinSize` key
|
||||
-- @treturn[4] number Value of the `MaxSize` key
|
||||
-- @treturn[5] number Value of the `Threshold` key
|
||||
index_theme.get_per_directory_keys = function(self, subdirectory)
|
||||
local keys = self.per_directory_keys[subdirectory]
|
||||
return keys[TYPE], keys[SIZE], keys[MINSIZE], keys[MAXSIZE], keys[THRESHOLD]
|
||||
end
|
||||
|
||||
index_theme.mt.__call = function(cls, icon_theme_name, base_directories)
|
||||
return index_theme.new(cls, icon_theme_name, base_directories)
|
||||
end
|
||||
|
||||
return setmetatable(index_theme, index_theme.mt)
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
-- Grab environment
|
||||
local utils = require("menubar.utils")
|
||||
local icon_theme = require("menubar.icon_theme")
|
||||
local pairs = pairs
|
||||
local ipairs = ipairs
|
||||
local string = string
|
||||
|
@ -32,31 +33,31 @@ menu_gen.all_menu_dirs = { data_dir .. 'applications/', '/usr/share/applications
|
|||
-- the applications that fall only to this category will not be shown.
|
||||
menu_gen.all_categories = {
|
||||
multimedia = { app_type = "AudioVideo", name = "Multimedia",
|
||||
icon_name = "applications-multimedia.png", use = true },
|
||||
icon_name = "applications-multimedia", use = true },
|
||||
development = { app_type = "Development", name = "Development",
|
||||
icon_name = "applications-development.png", use = true },
|
||||
icon_name = "applications-development", use = true },
|
||||
education = { app_type = "Education", name = "Education",
|
||||
icon_name = "applications-science.png", use = true },
|
||||
icon_name = "applications-science", use = true },
|
||||
games = { app_type = "Game", name = "Games",
|
||||
icon_name = "applications-games.png", use = true },
|
||||
icon_name = "applications-games", use = true },
|
||||
graphics = { app_type = "Graphics", name = "Graphics",
|
||||
icon_name = "applications-graphics.png", use = true },
|
||||
icon_name = "applications-graphics", use = true },
|
||||
office = { app_type = "Office", name = "Office",
|
||||
icon_name = "applications-office.png", use = true },
|
||||
icon_name = "applications-office", use = true },
|
||||
internet = { app_type = "Network", name = "Internet",
|
||||
icon_name = "applications-internet.png", use = true },
|
||||
icon_name = "applications-internet", use = true },
|
||||
settings = { app_type = "Settings", name = "Settings",
|
||||
icon_name = "applications-utilities.png", use = true },
|
||||
icon_name = "applications-utilities", use = true },
|
||||
tools = { app_type = "System", name = "System Tools",
|
||||
icon_name = "applications-system.png", use = true },
|
||||
icon_name = "applications-system", use = true },
|
||||
utility = { app_type = "Utility", name = "Accessories",
|
||||
icon_name = "applications-accessories.png", use = true }
|
||||
icon_name = "applications-accessories", use = true }
|
||||
}
|
||||
|
||||
--- Find icons for category entries.
|
||||
function menu_gen.lookup_category_icons()
|
||||
for _, v in pairs(menu_gen.all_categories) do
|
||||
v.icon = utils.lookup_icon(v.icon_name)
|
||||
v.icon = icon_theme():find_icon_path(v.icon_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- @author Kazunobu Kuriyama
|
||||
-- @copyright 2015 Kazunobu Kuriyama
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
-- Hack so that beautiful can be loaded
|
||||
_G.awesome = {
|
||||
xrdb_get_value = function() end,
|
||||
connect_signal = function(...) end,
|
||||
register_xproperty = function(...) end
|
||||
}
|
||||
-- Additional hacks to load menubar
|
||||
_G.screen = {
|
||||
add_signal = function(...) end,
|
||||
count = function() return 0 end
|
||||
}
|
||||
_G.client = {
|
||||
connect_signal = function(...) end,
|
||||
add_signal = function(...) end
|
||||
}
|
||||
_G.tag = {
|
||||
connect_signal = function(...) end,
|
||||
add_signal = function(...) end
|
||||
}
|
||||
_G.root = {
|
||||
cursor = function(...) end
|
||||
}
|
||||
|
||||
local os = os
|
||||
local string = string
|
||||
local icon_theme = require("menubar.icon_theme")
|
||||
|
||||
local base_directories = {
|
||||
os.getenv("PWD") .. "/spec/menubar/icons",
|
||||
os.getenv("PWD") .. "/icons"
|
||||
}
|
||||
|
||||
describe("menubar.icon_theme find_icon_path", function()
|
||||
local obj
|
||||
before_each(function()
|
||||
obj = icon_theme("awesome", base_directories)
|
||||
end)
|
||||
|
||||
-- Invalid arguments for `icon_name`
|
||||
it("nil", function()
|
||||
assert.is_nil(obj:find_icon_path(nil))
|
||||
end)
|
||||
it ('""', function()
|
||||
assert.is.equal(obj:find_icon_path(""), nil)
|
||||
end)
|
||||
|
||||
-- hierarchical folder (icon theme) cases
|
||||
for _, v in ipairs({16, 32, 48, 64}) do
|
||||
it(v, function()
|
||||
local expected = string.format("%s/awesome/%dx%d/apps/awesome.png",
|
||||
base_directories[1], v, v)
|
||||
assert.is.same(expected, obj:find_icon_path("awesome", v))
|
||||
end)
|
||||
end
|
||||
|
||||
-- hierarchical folder (icon theme) cases w/ fallback
|
||||
for _, v in ipairs({16, 32, 48, 64}) do
|
||||
it(v, function()
|
||||
local expected = string.format("%s/fallback.png",
|
||||
base_directories[1])
|
||||
assert.is.same(expected, obj:find_icon_path("fallback", v))
|
||||
end)
|
||||
end
|
||||
|
||||
-- flat folder/fallback cases
|
||||
local fallback_cases = {
|
||||
"awesome16",
|
||||
"awesome32",
|
||||
"awesome48",
|
||||
"awesome64"
|
||||
}
|
||||
for _, v in ipairs(fallback_cases) do
|
||||
it(v, function()
|
||||
local expected = string.format("%s/%s.png",
|
||||
base_directories[2], v)
|
||||
assert.is.same(expected, obj:find_icon_path(v))
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
|
@ -0,0 +1 @@
|
|||
../../../../../../icons/awesome16.png
|
|
@ -0,0 +1 @@
|
|||
../../../../../../icons/awesome32.png
|
|
@ -0,0 +1 @@
|
|||
../../../../../../icons/awesome48.png
|
|
@ -0,0 +1 @@
|
|||
../../../../../../icons/awesome64.png
|
|
@ -0,0 +1,16 @@
|
|||
[Icon Theme]
|
||||
Name=awesome
|
||||
|
||||
Directories=16x16/apps,32x32/apps,48x48/apps,64x64/apps,
|
||||
|
||||
[16x16/apps]
|
||||
Size=16
|
||||
|
||||
[32x32/apps]
|
||||
Size=32
|
||||
|
||||
[48x48/apps]
|
||||
Size=48
|
||||
|
||||
[64x64/apps]
|
||||
Size=64
|
|
@ -0,0 +1 @@
|
|||
../../../icons/awesome16.png
|
Loading…
Reference in New Issue