图标主题规范

原文链接

概观

图标主题是一组共享外观和感觉的图标。 然后,用户可以选择他们想要使用的图标主题,并且所有应用程序都使用主题中的图标。 图标主题的最初使用是在桌面文件规范的Icon字段,但将来它可以有其他用途(例如mimetype图标)。

从程序员的角度来看,图标主题只是一个映射。 给定一组要查找图标的目录和主题名称,它将图标名称和标称图标大小映射到图标文件名。

定义

图标主题

图标主题是一组命名的图标。 它用于从图标名称和大小映射到文件。 主题可以继承其他主题作为扩展它们的方式。

图标文件

图标文件是可以加载并用作图标的图像。 支持的图像文件格式为PNG,XPM和SVG。 PNG是推荐的位图格式,SVG用于矢量化图标。 由于向后兼容性的原因,支持XPM,并且不建议新主题使用XPM文件。 支持SVG是可选的。

基本目录

在一组目录中搜索图标和主题,称为基本目录。 主题存储在基本目录的子目录中。

图标比例

在非常高密度(高dpi)的屏幕上,UI通常被缩放以避免UI太小而难以看到。 为了支持这个图标,可以有一个目标比例,描述它们的设计比例因子。

例如,目录大小为48但缩放2x的图标将为96x96像素,但设计为具有与1x的48x48图标相同的细节级别。 这可用于高密度显示器,其中48x48图标太小(或丑陋的放大),而普通的96x96图标会有很多细节很难看到。

目录布局

在一组目录中查找图标和主题。 默认情况下,应用应在$HOME/.icons(为了向后兼容),$XDG_DATA_DIRS/icons和/usr/share/pixmaps(按此顺序)中查找。 应用程序可以进一步将它们自己的图标目录添加到该列表中,并且用户可以扩展或更改列表(以应用程序/桌面特定的方式)。在每个目录中,主题被存储为子目录。 通过具有相同名称的子目录,主题可以分布在几个基本目录中。 这样,用户可以扩展和覆盖系统主题。

为了让第三方应用程序安装他们的图标,应该总是存在一个名为“hicolor”的主题。 有关hicolor主题的数据可从以下网址下载:http://www.freedesktop.org/software/icon-theme/。 如果在当前主题中找不到图标,则实现需要去查看“hicolor”主题。

每个主题都存储为基本目录的子目录。 主题的内部名称是子目录的名称,尽管主题指定的用户可见名称可能不同。 因此,主题名称区分大小写,并且仅限于ASCII字符。 主题名称也可能不包含逗号或空格。

在至少一个主题目录中必须有一个名为index.theme的文件来描述主题。 使用在按顺序搜索基本目录时找到的第一个index.theme。 该文件描述了主题的一般属性。

在主题目录中也是一组包含图像文件的子目录。 每个目录包含为特定标称图标大小和比例设计的图标,如index.theme文件所述。 子目录可以是几个级别,例如,主题为“hicolor”的子目录“48x48/ apps”就是$basedir/hicolor/48x48/apps。

图像文件必须是以下类型之一:PNG,XPM或SVG,扩展名必须为“.png”,“.xpm”或“.svg”(小写)。 对SVG文件的支持是可选的。 不支持SVG的实现应该忽略任何“.svg”文件。 除此之外,可能还有一个附加文件,每个文件都有额外的图标数据。 它应该与图像文件具有相同的基本名称,扩展名为“.icon”。 例如,如果图标文件名为“mime_source_c.png”,则相应的文件将命名为“mime_source_c.icon”。

文件格式

图标主题描述文件和图标数据文件都是ini样式的文本文件,如桌面文件规范中所述。 它们没有任何编码字段。 相反,它们必须始终以UTF-8编码存储。

index.theme文件必须以名为Icon Theme的段开头,内容根据下面的表1。 所有列表都以逗号分隔。

表1.标准键

描述值类型必需 
Name图标主题的简称,用于例如 选择主题时列出。localestring是 
Comment更长的字符串描述主题localestring是 
Inherits此主题继承的主题的名称。 如果在当前主题中找不到图标名称,则在继承的主题中搜索它(并在所有继承的主题中递归搜索)。<br> 如果未指定主题,则需要实现将“hicolor”主题添加到继承树。 实现可以可选地在最后指定的主题和hicolor主题之间添加其他默认主题。strings否 
Directories此主题的子目录列表。 对于每个子目录,index.theme文件中必须有一个描述该目录的段。strings是 
ScaledDirectories除了目录中的子目录之外,还包含此主题的其他子目录列表。 这些目录应仅由支持缩放目录的实现读取,并且已添加以保持与不支持这些目录的旧实现的兼容性。
Hidden是否在主题选择用户界面中隐藏主题。 这用于诸如用户不应该看到的后备主题之类的内容。boolean
Example应该用作此主题外观的示例的图标名称。string

Directory键中指定的每个目录都有一个与目录同名的相应段。 该段的内容列于下表2中。

表2.每个目录的键

描述值类型必需 Type
Size此目录中图标的标称(未缩放)大小。integer
Scale此目录中图标的目标比例。 如果不存在,则默认值为1。 任何比例不是1的目录都应该列在ScaledDirectories列表中,而不是目录中,以便向后兼容。integer
Context图标通常用于的上下文。这在“上下文”一节中详细讨论。string
Type此目录中图标的图标大小类型。 有效类型是Fixed固定,Scalable可扩展和Threshold阈值。 该类型决定使用该部分中的其他键。 如果未指定,则默认为Threshold阈值。string
MaxSize指定此目录中的图标可以缩放到的最大(未缩放)大小。 如果不存在,则默认为Size的值。integerScalable
MinSize指定可以缩放此目录中的图标的最小(未缩放)大小。 如果不存在,则默认为Size的值。integerScalable
Threshold如果大小与所需(未缩放的)大小相差很大,则可以使用此目录中的图标。 如果不存在,则默认为2。integerThreshold

除了这些组之外,您还可以向index.theme文件添加额外的组以扩展它。 这些扩展必须以“X-”开头,并可用于向主题文件添加桌面特定信息。 示例组名称将是“X-KDE Icon Theme”或“X-Gnome Icon Theme”。

可选的.icon文件包含一个名为“Icon Data”的组,其内容列于表3中。

表3. 图标数据键

描述值类型必需 
DisplayName翻译的UTF8字符串,比如当在用户界面中列出图标时,可以使用该字符串代替图标名称。localestring
EmbeddedTextRectangle如果存在,则指定矩形的四个角,显示图标的程序可以嵌入文本。 这通常用于例如 想要在图标中显示文本文件内容预览的文件管理器。 拐角由四个值的列表指定:x0,y0,x1,y1。 值是图标左上角的像素坐标,SVG文件除外,它们在1000x1000坐标空间中指定,缩放到图标的最终渲染大小。integers
AttachPoints由“|”分隔的点列表,可用作徽章/叠加层的锚点。 这些点是图标左上角的像素坐标,SVG文件除外,它们在1000x1000坐标空间中指定,该坐标空间缩放到图标的最终渲染大小。points
允许扩展.icon文件,但键必须以“X-”开头,以避免与此格式的未来标准化扩展冲突。

上下文

上下文允许设计者在概念层面上对图标进行分组。 它不作为文件系统中的命名空间,例如,图标可以具有相同的名称,但允许实现按它进行分类和排序。

这些是可用的上下文:

  • Actions: 表示用户启动的操作的图标,例如“另存为”。
  • Devices: 表示真实世界设备的图标,例如打印机和鼠标。 它不适用于文件系统节点,如字符或块设备。
  • FileSystem: 表示文件系统的一部分的对象的图标。 例如,本地网络,“主页”和“桌面”文件夹。
  • MimeTypes: 代表MIME类型的图标。

图标查找

图标查找机制有两个全局设置,即基本目录列表和当前主题的内部名称。 鉴于这些,我们需要指定如何从图标名称,名义大小和比例查找图标文件。

查找首先在当前主题中完成,然后在每个当前主题的父项中递归,最后在默认主题中称为“hicolor”(实现可能在“hicolor”之前添加更多默认主题,但“hicolor”必须是最后一个)。 只要在主题中有任何大小的图标,搜索就会停止。 即使在继承的主题中可能存在尺寸接近正确的图标,我们也不想使用它。 这样做可能会导致更改图标大小(例如放大)时图标发生不一致的更改。

主题内的查找分三个阶段完成。 首先扫描所有目录以进行精确匹配,例如, 允许的图标文件大小与查找的大小匹配的一个。 然后扫描所有目录以查找与名称匹配的任何图标。 如果失败了,我们最终会回到无主题的图标上。 如果我们根本找不到任何图标,则由应用程序选择一个好的后备,因为正确的选择取决于上下文。

在主题中查找图标的精确算法(伪代码)(如果实现支持SVG)是:

FindIcon(icon, size, scale) {
  filename = FindIconHelper(icon, size, scale, user selected theme);
  if filename != none
    return filename

  filename = FindIconHelper(icon, size, scale, "hicolor");
  if filename != none
    return filename

  return LookupFallbackIcon (icon)
}

FindIconHelper(icon, size, scale, theme) {
  filename = LookupIcon (icon, size, scale, theme)
  if filename != none
    return filename

  if theme has parents
    parents = theme.parents

  for parent in parents {
    filename = FindIconHelper (icon, size, scale, parent)
    if filename != none
      return filename
  }
  return none
}

使用以下辅助函数:

LookupIcon (iconname, size, scale, theme) {
  for each subdir in $(theme subdir list) {
    for each directory in $(basename list) {
      for extension in ("png", "svg", "xpm") {
        if DirectoryMatchesSize(subdir, size, scale) {
          filename = directory/$(themename)/subdir/iconname.extension
          if exist filename
            return filename
        }
      }
    }
  }
  minimal_size = MAXINT
  for each subdir in $(theme subdir list) {
    for each directory in $(basename list) {
      for extension in ("png", "svg", "xpm") {
        filename = directory/$(themename)/subdir/iconname.extension
        if exist filename and DirectorySizeDistance(subdir, size, scale) < minimal_size {
           closest_filename = filename
           minimal_size = DirectorySizeDistance(subdir, size, scale)
        }
      }
    }
  }
  if closest_filename set
     return closest_filename
  return none
}

LookupFallbackIcon (iconname) {
  for each directory in $(basename list) {
    for extension in ("png", "svg", "xpm") {
      if exists directory/iconname.extension
        return directory/iconname.extension
    }
  }
  return none
}

DirectoryMatchesSize(subdir, iconsize, iconscale) {
  read Type and size data from subdir
  if Scale != iconscale
     return False;
  if Type is Fixed
    return Size == iconsize
  if Type is Scaled
    return MinSize <= iconsize <= MaxSize
  if Type is Threshold
    return Size - Threshold <= iconsize <= Size + Threshold
}

DirectorySizeDistance(subdir, iconsize, iconscale) {
  read Type and size data from subdir
  if Type is Fixed
    return abs(Size*Scale - iconsize*iconscale)
  if Type is Scaled
    if iconsize*iconscale < MinSize*Scale
        return MinSize*Scale - iconsize*iconscale
    if iconsize*iconscale > MaxSize*Scale
        return iconsize*iconscale - MaxSize*Scale
    return 0
  if Type is Threshold
    if iconsize*iconscale < (Size - Threshold)*Scale
        return MinSize*Scale - iconsize*iconscale
    if iconsize*iconsize > (Size + Threshold)*Scale
        return iconsize*iconsize - MaxSize*Scale
    return 0
}

在某些情况下,您并不总是希望回退到继承主题中的图标。 例如,有时您会在使用继承主题中的图标之前查找一组图标,而不是其中任何图标。 为了支持这样的操作,实现可以包含一个函数,该函数在继承层次结构中查找第一个图标名称列表。也就是,它看起来像这样:

FindBestIcon(iconList, size, scale) {
  filename = FindBestIconHelper(iconList, size, scale, user selected theme);
  if filename != none
    return filename

  filename = FindBestIconHelper(iconList, size, scale, "hicolor");
  if filename != none
    return filename

  for icon in iconList {
    filename = LookupFallbackIcon (icon)
    if filename != none
      return filename
  }
  return none;
}
FindBestIconHelper(iconList, size, scale, theme) {
  for icon in iconList {
    filename = LookupIcon (icon, size, theme)
    if filename != none
      return filename
  }

  if theme has parents
    parents = theme.parents

  for parent in parents {
    filename = FindBestIconHelper (iconList, size, scale, parent)
    if filename != none
      return filename
  }
  return none
}

这在例如处理mimetype图标时非常有用,图标有或多或少”特定“版本。

例子

这是一个示例index.theme文件:

[Icon Theme]
Name=Birch
Name[sv]=Björk
Comment=Icon theme with a wooden look
Comment[sv]=Träinspirerat ikontema
Inherits=wood,default
Directories=48x48/apps,48x48@2/apps48x48/mimetypes,32x32/apps,32x32@2/apps,scalable/apps,scalable/mimetypes

[scalable/apps]
Size=48
Type=Scalable
MinSize=1
MaxSize=256
Context=Applications

[scalable/mimetypes]
Size=48
Type=Scalable
MinSize=1
MaxSize=256
Context=MimeTypes

[32x32/apps]
Size=32
Type=Fixed
Context=Applications

[32x32@2/apps]
Size=32
Scale=2
Type=Fixed
Context=Applications

[48x48/apps]
Size=48
Type=Fixed
Context=Applications

[48x48@2/apps]
Size=48
Scale=2
Type=Fixed
Context=Applications

[48x48/mimetypes]
Size=48
Type=Fixed
Context=MimeTypes

/usr/share/icons目录中的相应目录树可能如下所示:

birch/index.theme
birch/scalable/apps/mozilla.svg
birch/scalable/mimetypes/mime_text_plain.svg
birch/scalable/mimetypes/mime_text_plain.icon
birch/48x48/apps/mozilla.png
birch/48x48@2/apps/mozilla.png
birch/32x32/apps/mozilla.png
birch/32x32@2/apps/mozilla.png
birch/48x48/mimetypes/mime_text_plain.png
birch/48x48/mimetypes/mime_text_plain.icon

其中birch/scalable/mimetypes/mime_text_plain.icon包含:

[Icon Data]
DisplayName=Mime text/plain
EmbeddedTextRectangle=100,100,900,900
AttachPoints=200,200|800,200|500,500|200,800|800,800

并且 birch/48x48/mimetypes/mime_text_plain.icon 包含:

[Icon Data]
DisplayName=Mime text/plain
EmbeddedTextRectangle=8,8,40,40
AttachPoints=20,20|40,40|50,10|10,50

在此示例中,由于目录的顺序,查找“mozilla”将在SVG图标之前获得预渲染的48x48和32x32图标。

安装应用程序图标

因此,您是应用程序作者,并希望安装应用程序图标,以便它们在KDE和Gnome菜单中工作。 至少你应该在hicolor主题中安装一个48x48图标。 这意味着在$prefix/share/icons/hicolor/48x48/apps中安装PNG文件。 您可以选择安装不同大小的图标。 例如,在$prefix/share/icons/hicolor/scalable/apps中安装svg图标意味着大多数桌面都有一个适用于所有尺寸的图标。 您甚至可能希望安装具有与其他众所周知的主题相匹配的图标,以便您的应用程序适合某些特定的桌面环境。

建议在hicolor主题中安装的图标看起来是中性的,因为它是一个后备主题,将与一些非常不同的主题结合使用。 但如果您没有任何中性图标,请在hicolor主题中安装您拥有的任何图标,以便所有应用程序在所有主题中至少获得一些图标。

实施说明

本文档中描述的算法通常始终在目录中查找文件名(unix术语中的stat)。 一个好的实现应该一次读取目录,并使用该信息在内存中进行所有查找。

此缓存可以使用户无需重新启动应用程序即可添加图标。 为了处理这个问题,任何执行缓存的实现都需要在执行缓存查找时查看顶层图标目录的mtime,除非它在不到5秒之前就已经这样做了。 这意味着任何图标编辑器或主题安装程序只需要更改更改主题的顶级目录的mtime,以确保最终将使用新图标。

转载于:https://my.oschina.net/u/1248114/blog/3065697

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值