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_ARROW 和 RIGHT_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: 50 和 height: 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_icon 的 svg_file 属性,使用了之前定义的箭头图标变量。

检查我们目前的进展

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

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

1cargo run --release

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

下一步

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