本周 GTK+ – 7

在上周,GTK+ 的 master 分支有 29 次提交,增加了 4744 行代码,删除了 4340 行代码。

计划和状态
  • GTK+ 3.21.3 已发布
  • Matthias 继续在 GLibGTK+ 的 portal 分支上工作。
  • William Hua 正在根据黑客马拉松的讨论 重新设计 来自 bug 756579 的菜单定位 API。
  • Emmanuele 推送了 gsk 分支的新版本。
  • Carlos Soriano 正在为文件选择器和 nautilus 开发一个新的路径栏 实现
值得注意的更改
  • Emmanuele 修复了因引入 GdkDrawingContext 而导致的 firefox 中的一些问题。
  • Ray Strode 清理了一些 headerbar 代码,并增加了对扩展子元素的支持。
  • Matthias 修复了 GtkColorChooser 中的一个崩溃问题,以及 GtkInspector 中的另一个崩溃问题。
已修复的 Bug
  • Bug 767851 – 弹出窗口箭头在某些方向上错乱
  • Bug 767849 – 焦点处理中的崩溃
  • Bug 724332 – GtkHeaderBar 需要支持扩展属性
  • Bug 768025 – entry.warning & entry.error 损坏
参与其中

有兴趣参与 GTK+ 的开发吗?查看新手 bug 列表,并加入 irc.gnome.org 上的 #gtk+ IRC 频道。

Adwaita

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

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

Adwaita

CSS

随着 GTK+ 3.0 的发布,一项大胆的新努力开始了。这项努力是为了让视觉设计师负责视觉设计,使用他们理解的工具。不再求助于主题引擎来绘制独特的控件,而是选择了一种在网络上使用的样式引擎。“一切皆盒子”的 CSS 模型很好地应用于 GTK+。这花费了大量的努力,主要由 Benjamin Otte 承担,多年来他设法给了我们我们梦想的东西:一个类似 CSS 的盒子模型,允许我们使用 padding、margins、borders 和一些巧妙的功能(如最小宽度)来间隔元素/控件。在选择器方面,我们不是在处理从一个版本到另一个版本都会发生变化的直接嵌套的 widget 结构,而是给出了一个抽象的、类似 HTML 的DOM结构,带有节点和类。节点也始终保持状态,并且更容易动画化。

SCSS

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

SASS 通过提供一次定义通用绘图过程的方法来解救,但在结构良好的样式表中重用它。您将能够绘制“像按钮一样”的东西,但不将其定义为按钮。您仍然可以在下拉菜单部分中找到组织良好的下拉菜单。SASS 将这些宏称为mixin,您将在 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);
}

本周 GTK+ – 6

在上周,GTK+ 有 20 次提交,增加了 1852 行代码,删除了 1234 行代码。

计划和状态
  • 由于大部分时间都在进行 GTK+ 黑客马拉松,Git 仓库中没有发生太多事情。
  • 黑客马拉松的每一天在 wiki 上都有自己的笔记:1, 2, 3, 4
  • 路线图已清理并更新。
值得注意的更改
  • Ray Strode 致力于 GDK Wayland 后端,以确保它可以与不支持 memfd 的旧 Linux 内核一起构建和使用。
  • Philip Chimento 增加了 GtkStyleContext 层次结构包含多个上下文的能力;这是允许样式上下文在小部件子项中正确级联的第一步。
已修复的 Bug
  • Bug 766341 不要依赖 memfd,因为它需要相当新的内核
  • Bug 767766 configure 错误地检测到 CUPS 2.X
  • Bug 767795 当 GtkShortcutsShortcut 的“accelerator”属性设置为“less”时发出警告
  • Bug 751409 gtk_style_context_add_provider() 不会传播到子项
  • Bug 767705 GtkActionHelper:将消息更改为警告
  • Bug 767468 树状视图单元格渲染器上的弹出窗口在显示后立即隐藏
参与其中

有兴趣参与 GTK+ 的开发吗?查看新手 bug 列表,并加入 irc.gnome.org 上的 #gtk+ IRC 频道。

GTK+ 中的绘图

GTK+ 如何绘制窗口内容是一个相当复杂的主题;它涉及到从 GtkWidgetGdkWindow,到 Cairo,再到当前使用的窗口系统的深入研究。即使对于那些从应用程序开发角度熟悉 GTK+ API 的人来说,这项任务也可能显得有些令人生畏,因此我决定写一个关于 GTK+ 如何绘图的简要介绍,从窗口小部件到窗口,到表面,到本机窗口资源。

它是如何开始的

GTK+ 总是会进行绘制,因为某些东西请求它这样做。此请求可能来自窗口系统——例如,因为窗口管理器向用户呈现了您的应用程序窗口,或者因为用户调整了其大小——但更常见的情况是它来自更新其内容的窗口小部件。例如,一个进度条从 50% 变为 60%;或者一个标签,更改其文本;或者一个微调器,执行新的迭代。此请求会使窗口小部件的后备 GdkWindow 失效——通常它是包含该窗口小部件的顶层 GtkWindowGdkWindow。每次失效都会携带窗口的失效区域(即“损坏”),以便当我们实际进行绘制时,我们知道窗口的哪些部分需要更新,并且我们可以避免在损坏区域之外进行绘制。

与时间赛跑

第一次失效将启动“帧时钟”;此时钟是一个对象,用于跟踪帧内的每个阶段,如绘制窗口、布局窗口小部件或处理事件队列。这使得 GTK+ 可以与窗口系统合成器等事物同步,并避免执行用户看不到的不必要的工作——例如,当您的显示器只能以 60Hz 运行时,以每秒 1000 帧的速度绘制某些内容。

一旦时钟到达“绘制”阶段,我们就会处理窗口上所有计划的更新;这将导致发出 GDK_EXPOSE 事件。GDK_EXPOSE 事件包含需要更新的 GdkWindow,以及所有失效区域的并集。需要注意的是,通常只有顶层窗口才会接收到 GDK_EXPOSE 事件;但是,由于历史原因,一些窗口小部件可能会应用一个特定的事件掩码,这也会导致 GDK_EXPOSE 事件传递给它们。您不应编写依赖于此的代码,如果您有从旧版本的 GTK+ 2.x 移植的旧代码,您真的应该考虑从事件掩码中删除 GDK_EXPOSURE_MASK

渲染

GTK+ 从 GDK_EXPOSE 事件中取出窗口和失效区域,并确定它们属于哪个顶层窗口小部件。一旦找到,GTK+ 将开始实际的渲染过程。首先,GTK+ 将要求 GdkWindow 创建一个缓冲区,用于绘制窗口的内容;该缓冲区将被裁剪到需要绘制的区域,并将使用窗口的背景色清除。GDK 将创建一个“绘图上下文”——一个临时对象,用于跟踪诸如 OpenGL 和 Cairo 绘图之类的内容。然后,GTK+ 将要求窗口小部件使用 Cairo 上下文绘制自身。对于叶子窗口小部件,这意味着在该上下文上绘制自身;对于容器窗口小部件,这还意味着递归遍历其所有子窗口小部件。在此过程结束时,GTK+ 将通过告诉 GDK 获取包含所有渲染的窗口小部件的缓冲区并使用它来替换窗口的当前内容来结束帧。然后,GDK 将在更合适的时候要求窗口系统将窗口呈现给用户。

改变历史

上述过程有各种注意事项,并且在 GDK 内部处理窗口的失效和验证的代码相当复杂;它也历史悠久,这意味着其 API 充斥着过去时代的墓碑。

例如,在 GTK+ 3.0 之前,您应该自己处理“expose”事件,并使用 gdk_cairo_create() 在窗口小部件上创建 Cairo 上下文进行绘制;但这早已没有必要,因为 GtkWidget::draw 虚函数已经为我们提供了用于绘制的 Cairo 上下文。但是,gdk_cairo_create() 函数已在 GTK+ 3.22 中被弃用,不应在新编写的代码中使用;如果您需要 Cairo 上下文,您应该创建类似的 Cairo 表面,在其上调用 cairo_create(),然后使用该表面作为 GTK+ 在绘制窗口小部件时提供给您的 Cairo 上下文的源。另一方面,如果您使用 gdk_cairo_create() 来响应 GDK_EXPOSE 事件,在顶层本地 GdkWindow 上进行绘制,那么您应该使用新添加的 gdk_window_begin_draw_frame()gdk_window_end_draw_frame()GdkDrawingContext API。

塑造未来

多年来,GTK+ 中绘图代码的内部结构已逐步更新,以应对诸如 新的窗口系统以及 其他渲染 API 之类的内容。可以肯定的是,它们会再次改变,尤其是在提高渲染性能方面。许多看起来很随意的更改实际上是减少每帧在工具包内部花费的时间的垫脚石,并为应用程序逻辑留出更多的时间。

本周 GTK+ – 5

在过去的一周中,GTK+ 已经有 35 次提交,添加了 3140 行代码,删除了 2353 行代码。

计划和状态
  • GTK+ 黑客节今天开始;主题包括:CSS、布局管理、沙盒门户和 GDK。
  • Matthias Clasen 和 Alex Larsson 正在研究在沙盒应用程序中打开文件的“门户”的初始实现
值得注意的更改
  • Emmanuele Bassi 合并了他简化 GdkWindow 绘图入口点的分支;此分支引入了一些新函数和一个 GdkDrawingContext 类,同时弃用了旧的 gdk_window_begin_paint* 系列函数;gdk_window_end_paint()gdk_cairo_create();和 gtk_widget_send_expose()
  • Tristan Van Berkom 致力于修复在 GtkScrolledWindow 中引入新内容大小调整属性带来的影响,并确保大小调整请求是一致的。
已修复的 Bug
  • 错误 767312 gtk_widget_path_append_for_widget() 缺少类,除非已调用 gtk_widget_get_style_context()
  • 错误 79229 具有大量数字且值位置设置为 GTK_POS_TOP/BOTTOM 的 GtkScale 绘制不正确
  • 错误 118959 GtkScale 值 '-0'
  • 错误 710471 使 gtk_scrolled_window_remove() 更智能
  • 错误 767310 高对比度主题在默认按钮中不显示焦点矩形
  • 错误 766860 Wayland 中平铺(吸附、半最大化)的窗口不是 GDK_WINDOW_STATE_TILED
  • 错误 766675 向 GdkWindow 添加适当的框架绘制 API
参与其中

有兴趣参与 GTK+ 的开发吗?查看新手 bug 列表,并加入 irc.gnome.org 上的 #gtk+ IRC 频道。

控制 GtkScrolledWindow 中的内容大小

GtkScrolledWindow 窗口小部件是 Gtk+ 应用程序开发人员的老朋友;其目的是允许通过使用滚动条将大型窗口小部件放入小空间中。

GtkScrolledWindow Example
运行中的垂直 GtkScrolledWindow

自 Gtk+ 3.0 以来,GtkScrolledWindow 能够通过 GtkScrolledWindow:min-content-widthGtkScrolledWindow:min-content-height 属性及其相关函数来设置最小内容大小(宽度和高度)。

从下一个稳定版本开始,Gtk+ 还将提供这些属性的最大尺寸对应项。

它们的作用是什么?

顾名思义,最小尺寸属性定义了可滚动区域将具有的最小尺寸,无论是宽度还是高度——即使其子窗口小部件没有完全填充可用空间。

scrolledwindow min-content-height
即使子窗口小部件没有填充可用空间,也会分配滚动窗口。

另一方面,最大内容大小定义了可滚动区域在开始滚动其内容之前允许增长多少。

让我们看看它的实际效果

scroll animation
演示最小和最大内容大小的示例。滚动窗口永远不会小于 110 像素,并且永远不会高于 250 像素。
在哪里以及如何使用它们

只要您想限制可滚动区域的大小,就应该使用新属性。例如,GtkPopover 总是将其子窗口小部件缩小到其最小尺寸。以下部分举例说明如何使内容在宽度和高度方面最多增长到 300 像素

<template>
  <object class="GtkPopover">
    <child>
      <object class="GtkScrolledWindow">
        <property name="visible">True</property>
        <property name="max-content-width">300</property>
        <property name="max-content-height">300</property>
      </object>
    </child>
  </object>
</template>

或者,如果您想通过编程方式实现相同的功能,可以调用gtk_scrolled_window_set_max_content_width()gtk_scrolled_window_set_max_content_height()

本周 GTK+ 更新 – 4

在过去的一周里,GTK+ 进行了 55 次提交,增加了 2378 行代码,删除了 1493 行代码。

计划和状态
  • Carlos Soriano 一直在各种 主题 分支上对新的路径栏窗口部件进行进一步的实验。
  • Emmanuele Bassi 一直在一个新的 API上工作,该 API 用于简化GtkWidget内部的绘制入口点,为 GSK 中的新渲染 API 做准备。
值得注意的更改
  • Timm Bäder 为 GtkStack 添加了一个可访问的表示,使其在辅助工具中仅显示当前可见的子项。
  • Matthias Clasen 添加了一个警告,以防我们在没有分配空间的情况下对窗口部件发出 GtkWidget::draw 调用;这种情况不应该发生,并且警告允许跟踪行为不端的窗口部件。
  • Lapo Calamandrei 修复了 Adwaita 在菜单项、信息栏和可选择标签的插入符颜色方面的问题。
  • Matthias Clasen 弃用了 GtkSizeGroup:ignore-hidden 属性,并将其记录为已损坏;不可见窗口部件的尺寸调整实际上是不可能的,因为它们无法访问窗口系统和样式资源。建议改用 GtkStack 为隐藏的窗口部件保留空间。
  • 添加了一个新的 GDK 设备源类型 GDK_SOURCE_TRACKPOINT,以表示 Trackpoint/指点杆指向设备;这种新的设备类型可用于在窗口部件中实现特定于设备的的行为。
  • 现在,CSS 文本样式属性可以用于 GtkScaleGtkProgressbar 窗口部件的值和标记。
  • Georges Basile Stavracas Neto 在 GtkScrolledWindow 中实现了 max-content-widthmax-content-height 属性;Tristan Van Berkom 修复了现有的 min-content-widthmin-content-height 属性长期存在的尺寸问题。
已修复的 Bug
  • 错误 745622 在 GtkInfoBar 中未高亮显示选定的文本
  • 错误 767058 GtkInfoBar:右键单击/上下文菜单全白
  • 错误 767052 Wayland:将模态对话框图标化会导致应用程序无法使用
  • 错误 767100 为指点杆添加输入源类型
  • 错误 767108 分隔符在 GtkPopover 中的位置不正确
  • 错误 767093 wayland:提供有关滚动设备的信息
  • 错误 753202 更改点击滚动的光标
  • 错误 767165 更新 GDK_GRAB_FAILED 的可用性文档
  • 错误 742281 GtkScrolledWindow 应该具有 max-content-height 和 max-content-width 属性
  • 错误 674215 更新工具提示的回归
  • 错误 556254 在“object”测试中测试 GObject 类型的属性
  • 错误 708148 gtk_tree_view_get_path_at_pos 错误地识别初始像素的列
  • 错误 765595 当焦点离开时,模态弹出窗口不会关闭
  • 错误 766569 更好地为 GTK_SCROLL_NATURAL 子项调整大小
  • 错误 767238 修复 min-content-width/min-content-height 中长期存在的回归
参与其中

有兴趣参与 GTK+ 的开发吗?查看新手 bug 列表,并加入 irc.gnome.org 上的 #gtk+ IRC 频道。