Adwaita

今天,来自 GNOME 设计团队的 Jakub Steiner 将会谈论 Adwaita,GTK+ 的默认主题;设计师可以用来样式化 GTK+ 的工具;以及工具包如何改变以允许更好的设计工作流程。

Adwaita 是 GTK+ 面向用户的外观。过去,GTK+ 没有外观;工具包没有明确定义的外观。像 FOSS 世界中的许多事物一样,它是自带的。有一个 Raleigh,一个后备皮肤,只有在主题或系统设置出现问题时才会显示。你真的不想看到它。

Adwaita

CSS

随着 GTK+ 3.0 的发布,一项大胆的新努力开始了。这项努力旨在让视觉设计师负责视觉设计,使用他们理解的工具。不再使用主题引擎来绘制独特的控件,而是选择了在 Web 上使用的样式引擎。“一切皆为盒子”的 CSS 模型非常适合 GTK+。这花费了大量的努力,主要由 Benjamin Otte 承担,多年来他设法为我们提供了我们梦寐以求的东西:一个类似 CSS 的盒子模型,允许我们使用 padding、margins、borders 和最小宽度等巧妙的功能来间隔元素/控件。在选择器方面,我们没有处理每个版本都会更改的直接嵌套小部件结构,而是获得了抽象的、类似 HTML 的DOM结构,带有节点和类。节点也始终如一地携带状态,并且更容易动画化。

SCSS

在 GTK+ 中,有很多看起来像按钮但不是按钮的控件。每个程序员都是懒惰的,这是一件好事。设计师也不例外。它是如此积极,以至于有一个首字母缩写词,DRY——不要重复自己。所以在旧的 Adwaita 中,当我们设计一些看起来相同的东西的外观时,我们只有一个属性块和大量的选择器——该外观的目标。按钮、下拉菜单,等等。不需要太多的输入,但更改起来却很疯狂。

SASS 通过提供一种方法来定义一次通用的绘图过程,但在结构良好的样式表中重用它,从而得以拯救。你可以绘制“像按钮一样”的东西,但不要将其定义为按钮。你仍然会在下拉菜单部分找到语义组织良好的下拉菜单。SASS 将这些宏称为 mixins,你会在 src/gtk/theme/Adwaita/_drawing.scss 中找到我们的绘图宏。

/* Switch Slider being a button */
slider {
/* ... */
@include button(normal, $edge: $shadow_color);
}
检查器

设计师工作流程的一个巨大改进是引入了 检查器。检查器是一个宝贵的工具,可以交互式地测试新样式或找出为什么某个特定的选择器不起作用。它提供了几个强大的工具

  • 小部件选择器。你可以交互式地指向一个小部件,以了解其属性或它在小部件树堆栈中的位置。自 3.20 版本以来,你还可以了解其 CSS 节点,了解它可以进入哪些状态,了解已分配给它的所有类。它还可以告诉你样式表中已定义设置属性的位置。这可以帮助你找出为什么你的选择器不起作用。有点。如果能够看到所有匹配的选择器,甚至是那些被优先选择器覆盖的选择器,那就太好了。
  • 交互式 CSS 样式表。你可以编写 CSS 规则并使其实时应用。这不仅有助于找出合适的选择器,还可以直接使用 GTK+ 而不是使用 Inkscape 等工具来试验绘图。能够快速迭代并尝试事物可以带来更好的设计。

如果这一切听起来很像现代浏览器提供的功能,那并不是巧合。

CSS Nodes in the Inspector
检查器中的 CSS 节点
未来改进

在改变 Adwaita 方面使我们不太灵活的一个主要因素是图形资源。我们仍然有一些事情必须使用图像资源。这些实际上在一个大型资源 SVG 表格中,我们有一些脚本可以分割多种尺寸的图像(用于 HiDPI)。添加或更改特定位仍然很麻烦。

为了让这不那么枯燥,这里有一个小型的网络演示,展示了我们如何可能避免使用图像资源来绘制 GtkScale 滑块,并使用简单的 CSS 框代替

<div id="scale" class="scale">
  <div class="trough"></div>
  <div id="slider" class="slider run-animation"></div>
</div>

<style type="text/css">
.scale {
  position: relative;
  width: 100%;
  height: 64px;
}
.scale:hover { }
.trough {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  height: 8px;
  border-style: solid;
  border-width: 2px;
  border-radius: 4px;
  left: 0; right: 0;
  border-color: #a7a7a7;
  background-color: #b1b3b1;
  background-image: linear-gradient(to bottom, #a7a7a7, #bebebe);
  box-shadow: 0 1px 0 rgba(255,255,255,0.8);
}
.slider {
  position: absolute;
  width: 48px; height: 48px;
  top: 50%;
  left: 0%;
  transform: translateY(-50%) translateX(0%) rotate(0deg);
  border-style: solid;
  border-width: 2px;
  border-color: #a7a7a7;
  border-radius: 0;
  background-color: #e0e0e0;
  background-image: linear-gradient(135deg, #ededed, #d3d3d3);
  box-shadow: inset 0 0 0 2px rgba(255,255,255,0.2),
              2px 2px 2px rgba(0,0,0,0.1);
}
.slider.run-animation {
  animation-name: morph, progress;
  animation-delay: 6s,10s;
  animation-duration: 3s,3s;
  animation-iteration-count:1,infinite;
  animation-direction: normal, alternate;
  animation-timing-function: ease-in-out;
  animation-fill-mode: forwards;
}
.slider.run-animation:hover {
  /* the best way to reset CSS animations is switching between identical keyframes */
  animation-name: morphClone, progressClone;
}
@keyframes morph {
  0% {
    border-radius: 0;
    transform: translateY(-50%) translateX(0%) rotate(0deg);
  }
  90% {
    border-radius: 50% 50% 0 50%;
    transform: translateY(-50%) translateX(0%) rotate(0deg);
  }
  100% {
    border-radius: 50% 50% 0 50%;
    transform: translateY(-60%) translateX(0%) rotate(45deg);
  }
}
@keyframes morphClone {
  0% {
    border-radius: 0;
    transform: translateY(-50%) translateX(0%) rotate(0deg);
  }
  90% {
    border-radius: 50% 50% 0 50%;
    transform: translateY(-50%) translateX(0%) rotate(0deg);
  }
  100% {
    border-radius: 50% 50% 0 50%;
    transform: translateY(-60%) translateX(0%) rotate(45deg);
  }
}
@keyframes progress {
  0% {
    left: 0%;
    transform: translateY(-60%) translateX(0%) rotate(45deg);
  }
  100% {
    left: 100%;
    transform: translateY(-60%) translateX(-100%) rotate(45deg);
  }
}
@keyframes progressClone {
  0% {
    left: 0%;
    transform: translateY(-60%) translateX(0%) rotate(45deg);
  }
  100% {
    left: 100%;
    transform: translateY(-60%) translateX(-100%) rotate(45deg);
  }
}
</style>

本质上,滑块是一个具有 3 个圆角并旋转 45 度的盒子。我们所需要的只是盒子。

    /* transforms-based scale slider on the web */
.slider {
  position: absolute;
  width: 48px; height: 48px;
  top: 50%;
  left: 0%;
  /* move up slightly after rotation, thus not 50% */
  transform: translateY(-60%) rotate(45deg);
  border-style: solid;
  border-width: 2px;
  border-color: #a7a7a7;
  border-radius: 50% 50% 0 50%;
  background-color: #e0e0e0;
  background-image: linear-gradient(135deg, #ededed, #d3d3d3);
  box-shadow: inset 0 0 0 2px rgba(255,255,255,0.2),
              2px 2px 2px rgba(0,0,0,0.1);
}

关于“Adwaita”的 2 条想法

  1. 顺便问一下,从哪里可以找到一个看起来像 Motif 的 gtk3 主题?(或者更好,像 SunOS 或 Irix-4dwm 上使用的 Motif?)

评论已关闭。