2 个版本

使用旧的 Rust 2015

0.1.1 2018 年 11 月 2 日
0.1.0 2018 年 11 月 2 日

#783 in GUI

Apache-2.0

71KB
1.5K SLoC

druid banner

以数据为中心的 Rust 原生 UI 工具包。

crates.io docs.rs license chat

Druid 是一个实验性的 Rust 原生 UI 工具包。其主要目标是提供优质的用户体验。实现这一目标有许多因素,包括性能、丰富的交互集(因此有一个支持这些交互的控件库),以及与原生平台良好协作。更多信息请参阅目标部分

我们已经在 crates.io 上定期发布 Druid,但 API 仍然不稳定。所有更改均记录在变更日志中。

有关一些关键概念的概述,请参阅(工作正在进行中)的Druid 书籍

项目状态

Druid 项目将由核心开发团队停止。

新的开发工作集中在Xilem上,它进行了许多根本性的改变,以允许创建更多样化的应用程序,并具有更好的性能,但它也大量继承了 Druid。我们将Xilem视为 Druid 的未来。

Druid 对于某些应用程序子集来说是可用的,并且具有丰富的测试历史,这确保了稳定性和正确性。但我们不期望在 Druid 中添加任何主要的新功能。因此,我们不推荐使用 Druid 开发全新的应用程序。如果您坚持使用,请确保您的应用程序不需要 Druid 没有的功能,例如可访问性或 3D 支持。

贡献

由于 Druid 项目正在停止,我们仍将接受所有贡献,但我们更倾向于优先考虑错误修复和文档改进,而不是新功能。

Zulip 聊天实例的 #druid-help 和 #druid 频道中,询问问题和讨论开发工作是很好的地方。

我们非常欢迎通过 GitHub pull requests 的贡献。有关更多详细信息,请参阅CONTRIBUTING.md

示例

以下是一个简单的计数器示例应用程序

use druid::widget::{Button, Flex, Label};
use druid::{AppLauncher, LocalizedString, PlatformError, Widget, WidgetExt, WindowDesc};

fn main() -> Result<(), PlatformError> {
    let main_window = WindowDesc::new(ui_builder());
    let data = 0_u32;
    AppLauncher::with_window(main_window)
        .log_to_console()
        .launch(data)
}

fn ui_builder() -> impl Widget<u32> {
    // The label text will be computed dynamically based on the current locale and count
    let text =
        LocalizedString::new("hello-counter").with_arg("count", |data: &u32, _env| (*data).into());
    let label = Label::new(text).padding(5.0).center();
    let button = Button::new("increment")
        .on_click(|_ctx, data, _env| *data += 1)
        .padding(5.0);

    Flex::column().with_child(label).with_child(button)
}

查看示例文件夹,以了解Druid现有功能和组件的更全面演示。查看druid_widget_nursery获取更多组件。

屏幕截图

calc.rs example flex.rs example custom_widget.rs example

使用Druid

Druid的一个明确目标是易于构建,如果您遇到任何困难,请提出问题。Druid可在crates.io找到,应作为独立依赖项工作(它导出您需要的所有druid-shell、piet和kurbo组件)

druid = "0.8.3"

由于Druid目前处于快速发展阶段,您可能更喜欢直接接触源码

druid = { git = "https://github.com/linebender/druid.git" }

平台说明

Linux

在Linux上,Druid需要gtk+3;请参阅GTK安装页面。(在基于ubuntu的发行版上,从终端运行sudo apt-get install libgtk-3-dev即可完成任务。)

OpenBSD

在OpenBSD上,Druid需要gtk+3;从软件包中安装

pkg_add gtk+3

另外,还有一个X11后端可用,尽管它目前缺少很多功能。缺少一些功能。您可以使用--features=x11尝试它。

目标

Druid的目标是使编写和部署高质量桌面应用程序变得容易,在各种常见平台上提供平滑和精致的用户体验。为了实现这一目标,我们努力做到以下几点

  • 使所有支持的平台都易于构建和打包。
  • 实现抽象以避免特定平台的问题。
  • 尊重平台约定和期望。
  • 以最小的努力可靠地处理显示分辨率和缩放。
  • 实现易于使用且强大的国际化。
  • 提供强大的辅助功能支持。
  • 生成小型、快速且内存使用低的二进制文件。
  • 拥有一个小型依赖项树、高质量的代码库和良好的组织。
  • 专注于强大的桌面级应用程序。
  • 提供灵活的布局和常用组件。
  • 根据需要轻松创建自定义组件和应用逻辑。

非目标

为了实现这些目标,我们无法支持每个用例。幸运的是,Rust社区正在开发各种不同目标的不同库,以下是一些Druid的非目标以及可能的替代方案,它们可以提供这些功能

  • 使用平台原生的组件或模仿它们。(RelmSlint
  • 轻松嵌入到自定义渲染管线。(Conrod
  • 遵循特定的架构风格,例如Elm。(IcedRelm
  • 支持将渲染输出到HTML,以针对网络。(IcedMoxie

Druid只是众多正在进行的Rust原生GUI实验之一。如果它不适合您的用例,也许其他其中之一会适合!

概念

druid-shell

Druid工具包使用druid-shell来提供一个平台抽象的应用程序外壳。druid-shell负责启动原生平台运行循环,监听事件,将它们转换为平台无关的表示,并使用它们调用用户提供的处理程序。

在开发时,druid-shell 与 Druid 工具箱一起进行,旨在足够通用,以便其他对 Rust GUI 进行实验的项目可以重用它。druid-shell crate 包含了一些非 druid 的示例

piet

Druid 依赖于 Piet 库 来进行绘制和文本布局。Piet 是一个具有多个后端的 2D 图形抽象库:目前有 piet-direct2dpiet-coregraphicspiet-cairopiet-webpiet-svg 可用。在 Druid 平台支持方面,macOS 使用 piet-coregraphics,Linux/OpenBSD/FreeBSD 使用 piet-cairo,Windows 使用 piet-direct2d,而网络使用 piet-web

use druid::kurbo::{BezPath, Point, Rect};
use druid::piet::Color;

// Create an arbitrary bezier path
// (ctx.size() returns the size of the layout rect we're painting in)
let mut path = BezPath::new();
path.move_to(Point::ORIGIN);
path.quad_to(
    (80.0, 90.0),
    (ctx.size().width, ctx.size().height),
);
// Create a color
let stroke_color = Color::rgb8(0x00, 0x80, 0x00);
// Stroke the path with thickness 1.0
ctx.stroke(path, &stroke_color, 1.0);

// Rectangles: the path for practical people
let rect = Rect::from_origin_size((10., 10.), (100., 100.));
// Note the Color:rgba8 which includes an alpha channel (7F in this case)
let fill_color = Color::rgba8(0x00, 0x00, 0x00, 0x7F);
ctx.fill(rect, &fill_color);

小部件

Druid 中的小部件(文本框、按钮、布局组件等)是实现 Widget 特质 的对象。该特质由一个类型(T)用于关联数据。所有特质方法(eventlifecycleupdatepaintlayout)都提供了对数据的访问,在 event 的情况下,引用是可变的,以便事件可以直接更新数据。

每次应用程序数据发生变化时,框架都会通过 update 方法遍历小部件层次结构。

所有的小部件特质方法都提供了一个相应的上下文(EventCtxLifeCycleCtxUpdateCtxLayoutCtxPaintCtx)。小部件可以通过调用该上下文的方法来请求事物并触发动作。

此外,所有特质方法都提供了一个环境 Env,其中包含当前的主题参数(颜色、尺寸等)。

impl<T: Data> Widget<T> for Button<T> {
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
      ...
    }

    fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
      ...
    }

    fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) {
      ...
    }

    fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
      ...
    }

    fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
      ...
    }
}

Druid 提供了许多 基本实用和布局小部件,并且实现自己的小部件也很容易。您还可以将小部件组合成新的小部件。

fn build_widget() -> impl Widget<u32> {
    let mut col = Flex::column();
    for i in 0..30 {
        let button = Button::new(format!("Button {}", i).padding(5.0);
        col.add_child(button);
    }
    Scroll::new(col)
}

布局

Druid 的布局协议受到 Flutter 的框布局模型 的强烈启发。在 Druid 中,小部件会收到一个 BoxConstraint,它为布局提供了最小和最大尺寸。如果适用,小部件还负责计算其子小部件的适当约束。

数据

Druid 使用 Data 特质 来表示 值类型。这些类型应该易于比较且易于克隆。

通常,您可以使用 derive 来为您自己的类型生成一个 Data 实现器。

#[derive(Clone, Data)]
struct AppState {
    which: bool,
    value: f64,
}

透镜

Lens 数据类型 提供了对更大数据结构中某个部分的访问。与 Data 类似,这也可以生成。派生的透镜可以通过与字段同名的关系常量来访问。

#[derive(Clone, Data, Lens)]
struct AppState {
    which: bool,
    value: f64,
}

要使用透镜,请将您的小部件包装在 LensWrap 中(注意将驼峰式命名法转换为蛇形命名法)

LensWrap::new(WidgetThatExpectsf64::new(), AppState::value);

或者,可以使用 lens 宏按需构建结构体、元组和可索引容器上的透镜

LensWrap::new(WidgetThatExpectsf64::new(), lens!(AppState, value));

这在处理另一个crate中定义的类型时特别有用。

作者

主要作者是Raph Levien和Colin Rofls,得到了一个活跃而友好的社区的很大支持。更多信息请参阅AUTHORS文件。

依赖

~1.4–2MB
~28K SLoC