13 个不稳定版本

0.8.3 2023年2月28日
0.8.2 2023年1月28日
0.7.0 2021年1月2日
0.6.0 2020年6月1日
0.1.1 2018年11月2日

#95GUI 中排名

Download history 949/week @ 2024-04-23 874/week @ 2024-04-30 841/week @ 2024-05-07 819/week @ 2024-05-14 819/week @ 2024-05-21 815/week @ 2024-05-28 728/week @ 2024-06-04 618/week @ 2024-06-11 795/week @ 2024-06-18 787/week @ 2024-06-25 251/week @ 2024-07-02 577/week @ 2024-07-09 706/week @ 2024-07-16 681/week @ 2024-07-23 856/week @ 2024-07-30 746/week @ 2024-08-06

3,110 每月下载量
用于 30 个 crate (28 个直接使用)

Apache-2.0

2MB
42K 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 项目正在停止,我们仍然接受所有贡献,但我们更倾向于优先考虑错误修复和文档改进,而不是新功能。

在 #druid-help 和 #druid 频道中,我们的 Zulip 聊天实例 是一个非常好的提问和讨论开发工作的地方。

我们乐于接受通过 GitHub pull request 的贡献。有关更多详细信息,请参阅 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-shellpietkurbo部分)

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
  • 在针对Web时支持将渲染到HTML。(IcedMoxie

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

概念

druid-shell

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

虽然druid-shell是在Druid工具包的背景下开发的,但它旨在足够通用,可以由其他对实验Rust GUI感兴趣的项目重用。druid-shell软件包包含一些druid示例

piet

Druid 使用 Piet 库 进行绘图和文本布局。Piet 是一个支持多个后端的二维图形抽象库:目前有 piet-direct2dpiet-coregraphicspiet-cairopiet-webpiet-svg。在 Druid 平台支持方面,macOS 使用 piet-coregraphics,Linux/OpenBSD/FreeBSD 使用 piet-cairo,Windows 使用 piet-direct2d,而 Web 使用 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,
}

透镜

透镜数据类型 可以访问大型数据结构的一部分。与 Data 类似,这也可以派生。派生的透镜可以通过与字段同名的一致常量来访问。

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

要使用透镜,请将您的部件用 LensWrap 包装(注意 CamelCase 到 snake_case 的转换)

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

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

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

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

作者

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

依赖关系

~5–23MB
~345K SLoC