8 - 创建幻灯片导航按钮

在之前的几个步骤中,我们为应用构建了一个图片网格。图片网格允许你同时查看多张图片。但是有时候,我们并不想一次看多张图片,而是想一张一张地浏览,这就是幻灯片的作用。接下来的几个步骤里,我们将为应用创建一个幻灯片功能。

我们的幻灯片结构如下:

  • 幻灯片由一个单独的 Image 组成,顶部覆盖一个透明的 SlideshowOverlay
  • SlideshowOverlay 包含两个 SlideshowNavigateButton,分别位于两侧。
  • 这两个导航按钮用于通过鼠标操作切换幻灯片。

为了保持最初实现的简单性,幻灯片目前只会显示一个占位符图片,暂时不会响应用户事件。我们将在后续步骤中去掉这些限制。

本步骤中,我们将从创建这两个 SlideshowNavigateButton 开始。

注意:如果你不想手动输入代码,可以在这里找到本步骤的完整代码:https://github.com/makepad/image_viewer/tree/main/step_8

你将学到的内容

在本步骤中,你将学习到:

  • 如何使用按钮(Button)。
  • Makepad 中的继承机制是如何工作的。

添加箭头图标

对于两个 SlideshowNavigateButton,我们需要一些箭头图标,因此首先需要将它们作为资源添加到应用中。

进入 resources 目录,下载以下文件到该目录:left_arrow.svg, right_arrow.svg

我们将使用这些文件作为箭头图标。

定义变量

既然我们已经添加了箭头图标,现在我们需要在 DSL 代码中定义一些变量来引用这些图标。

app.rs 中,将以下代码添加到 live design 块的顶部:

1LEFT_ARROW = dep("crate://self/resources/left_arrow.svg");
2RIGHT_ARROW = dep("crate://self/resources/right_arrow.svg");

这段代码定义了两个变量:LEFT_ARROWRIGHT_ARROW。我们将在 DSL 代码的其他地方使用这两个变量来引用箭头图标。

定义 SlideshowNavigateButton

现在我们已经有了引用箭头图标的方法,接下来可以添加 SlideshowNavigateButton 的定义。SlideshowNavigateButton 是一个高而窄的条形按钮,占据其容器的整个高度,并在中间显示一个箭头图标。

app.rs 中,将以下代码添加到 live design 块中,位置在 App 定义之前:

1SlideshowNavigateButton = <Button> {
2        width: 50,
3        height: Fill,
4        draw_bg: {
5            color: #fff0,
6            color_down: #fff2,
7        }
8        icon_walk: { width: 9 },
9        text: "",
10        grab_key_focus: false,
11    }

该定义创建了一个具有以下属性的 SlideshowNavigateButton

  • width: 50height: Fill:确保按钮具有期望的宽度并填满容器的高度。
  • draw_bg { ... }:控制按钮背景的绘制方式。
    • color: #fff0:默认情况下按钮完全透明。
    • color_down: #fff2:当按钮被按下时稍微可见一些。
  • icon_walk { ... }:控制按钮中图标的布局方式。
    • width: 9:将图标宽度设为 9 像素。
  • text: "":不显示按钮的文本标签。
  • grab_key_focus: false:点击按钮时不会获取键盘焦点(我们将在讲解键盘焦点工作原理时进一步说明)。

继承基础简介

现在是一个很好的时机来简单了解一下 Makepad 中的继承机制。

Makepad 中的继承非常类似于 JavaScript 等语言中的原型继承(Prototypal Inheritance):

  • 语法 { ... } 用于定义一个对象。对象是属性的集合,每个属性都有名称和值。
  • 语法 Object = { ... } 用于为一个对象赋予名称。
  • 顶层命名对象可以被其他对象作为基类继承。
  • 语法 <Base> { ... } 用于定义一个继承自 Base 对象的新对象。
  • 当一个对象继承另一个对象时,它会复制该对象的所有属性。
  • 子对象可以覆盖已有属性,从而更改其值。
  • 子对象还可以添加原始对象中未定义的新属性。

我们刚刚定义的 SlideshowNavigateButton 就是一个例子。它的定义如下:

1SlideshowNavigationButton = <Button> {
2        ...
3    }

这意味着 SlideshowNavigateButton 派生自 Button。还记得我们通过 use link::widgets::*; 导入的内建组件吗?Button 就是其中之一。SlideshowNavigateButton 会复制 Button 的所有属性,并覆盖其中的一些属性以实现自定义行为。

你可能注意到,在我们定义 SlideshowNavigateButton 时,并没有为图标指定具体的图像。这是有意为之的,因为 SlideshowNavigateButton 本身是作为一个基类来使用的 —— 每当我们创建它的一个实例时,我们会为该实例单独指定一个图像作为图标。你马上就会在更新 App 的时候看到这个用法的例子。

更新 App

现在我们已经有了 SlideshowNavigateButton,接下来我们要更新 App 的定义,让它显示两个 SlideshowNavigateButton,而不是之前的 ImageGrid(稍后我们会在讲解如何在不同视图之间切换时,把 ImageGrid 放回来)。

app.rs 文件的 live design 代码块中,用以下代码替换 App 的定义部分

1App = {{App}} {
2        ui: <Root> {
3            <Window> {
4                body = <View> {
5                    <SlideshowNavigateButton> {
6                        draw_icon: { svg_file: (LEFT_ARROW) }
7                    }
8                    <SlideshowNavigateButton> {
9                        draw_icon: { svg_file: (RIGHT_ARROW) }
10                    }
11                }
12            }
13        }
14    }

如你所见,我们创建了两个 SlideshowNavigateButton 的实例。对于每个 SlideshowNavigateButton,我们都重写了 draw_iconsvg_file 属性,使用了之前定义的箭头图标变量。

检查我们目前的进展

让我们检查一下目前的进展情况。

请确保你当前位于项目的包目录中,然后运行以下命令:

1cargo run --release

如果一切正常运行,你应该会看到两个按钮 —— 一个带有左箭头图标,另一个带有右箭头图标:

下一步

在这一步,我们创建了两个 SlideshowNavigateButton。下一步,我们将创建 SlideshowOverlay