#desktop-applications #gui-applications #rendering-engine #cross-platform-gui #user-interface #svg

sys azul

Azul GUI是一个免费的、函数式、响应式的GUI框架,用于用Rust和C快速开发桌面应用程序,使用Mozilla WebRender渲染引擎

5个版本

1.0.0-alpha42021年8月5日
1.0.0-alpha32021年8月4日
1.0.0-alpha22021年7月31日
1.0.0-alpha12021年7月20日
0.1.0 2018年11月18日

#560 in GUI


用于 pangolin

MIT 许可证

715KB
12K SLoC

Azul - 桌面GUI框架

Build Status Linux / macOS Build status Windows Coverage Status LICENSE Rust Compiler Version

Azul是一个免费的、函数式、响应式的GUI框架,用于Rust和C++,使用WebRender渲染引擎和类似于CSS/HTML的文档对象模型,快速开发美观的本地桌面应用程序

网站 | 用户指南 | API文档 | 视频演示 | Matrix聊天

关于

Azul是一个用于在Rust和C中创建图形用户界面的库。它结合了函数式、响应式和数据导向编程的范式,并提供了一个适合开发跨平台桌面应用程序的API。Azul的两个核心原则是不渲染不可见的对象,以及通过DOM树组合而不是继承来使用。

Azul通过不允许UI/渲染逻辑以可变方式访问应用程序数据来分离业务逻辑/回调、数据模型和UI渲染/样式的关注点。在Azul中,渲染视图是一个纯函数,它将你的应用程序数据映射到样式化的DOM。"小部件"只是渲染特定状态的函数,更复杂的组件使用函数组合。

由于重新创建DOM对象代价昂贵(注意:"昂贵"= 3毫秒),Azul缓存DOM对象,并且不在每一帧都重新创建它 - 只在回调请求重新创建时才创建。

应用程序和小部件数据使用引用计数的箱式类型(RefAny)进行管理,如果需要,可以将其向下转换为具体类型。需要在帧之间保留的小部件本地数据存储在DOM节点上,类似于HTML dataset属性可以用来存储小部件数据。

当前进度

最新的Windows版本可以在这里找到。

目前我正在使用这个框架来构建跨平台的GUI应用程序:虽然这个框架还没有完成,但至少可以构建出适用于Windows的本地、美观的应用程序——例如,这里有一个我正在制作的Excel克隆[点击查看]

image

创建这个演示大约花费了两小时。正如您所看到的,布局系统已经相当成熟。要运行此XML文件,请下载examples.zip文件夹,将“ui.xml”文件替换为链接文件,并重新启动xml.exe演示。XML本身可以热重载,并且可以后来编译成本地Rust代码——这既给您提供了快速的设计迭代时间,又提供了本地性能。

应用程序目前运行需要大约40MB的内存,当然CPU占用几乎为零。

使用正确的CSS样式,窗口与本地应用程序几乎无法区分。

image

Azul目前具有以下功能

  • 文本输入/文本输入(请参阅“widgets”演示)
  • 60+ FPS的动画(使用webrender
  • 支持CSS 查看支持的CSS键列表
  • 解码/编码图像和字体(TTF,WOFF,OTF)。
  • 跨平台文本形状(通过allsorts
  • 解析和渲染SVG(通过resvg
  • 使用OpenGL纹理渲染/嵌入OpenGL内容
  • 将形状拼接到三角形中(通过lyon
  • 异步管理运行中的背景线程以进行文件I/O
  • 解析XML(通过xmlparser
  • 稳定的API

当前可用的控件

  • 按钮
  • TextInput(问题:尚无光标/文本选择功能)
  • 复选框
  • ColorInput(打开本地颜色选择器对话框)
  • NumberInput(与TextInput相同,但仅接受数字输入)

所有控件都可通过CSS进行样式化。正在进行中的控件

  • 进度条
  • 滑块
  • 下拉列表
  • 单选选择
  • 功能区

此外,Azul还提供了跨平台的MsgBoxFileDialog对话框。

注意事项

  • 由于X11和Cocoa上的渲染问题,Azul目前只能在Windows上运行
  • 非拉丁字体以及回退字体的文本形状非常基础/不存在
  • 滚动,尤其是平滑滚动尚未完全实现
  • 滚动条不会自动插入
  • 富文本布局需要手动计算,而不是自动完成
  • C++ API仍在开发中
  • 布局引擎可能存在错误(但通常可以解决这些问题)
  • 二进制ABI尚不稳定
  • 无限滚动/延迟加载DOM内容尚不支持
  • 菜单/上下文菜单尚未工作(stub API)

安装

由于相对较大的尺寸(以及提供C/C++互操作),Azul作为动态库构建在azul-dll包中。您可以从azul.rs/releases下载预构建的二进制文件。

使用预构建的二进制文件

  1. azul.rs/releases下载库
  2. 设置链接器以链接到库
    • Rust:将AZUL_LINK_PATH环境变量设置为库的路径
    • C/C++:将发布页面上的azul.h复制到您的项目头文件,并将azul.dll复制到您的IDE项目。

Rust、C++和其他语言的API完全相同,因为API是由build.py脚本自动生成的。如果您想为您的语言生成语言绑定,可以使用api.json文件来生成。

在Linux上运行程序时,您可能还需要将libazul.so复制到/usr/lib最终,一旦所有主要错误都得到解决,将通过将库合并到仓库中解决这个问题。

从源代码构建(crates.io)

默认情况下,您应该能够运行

cargo install --version 1.0.0 azul-dll

来从crates.io编译DLL。库将在$AZUL_LINK_PATH目录中构建和安装,默认为$CARGO_HOME_DIR/lib/azul-dll-0.1.0/target/release/

从源代码构建(git)

从源代码构建库需要clang以及上述所有先决条件。

git clone https://github.com/fschutt/azul
cd azul-dll
cargo build --release

此命令应在/target/release文件夹中生成一个azul.dll文件,为了使用它,您还需要将AZUL_LINK_PATH设置为$BUILD_DIR/target/release/

如果您正在开发库,您可能还需要重新生成Rust / C API,在这种情况下,您应该首选使用build.py脚本

python3 ./build.py

示例

注意:小部件是针对每种编程语言定制的。所有回调都必须使用extern "C"才能与库兼容。所有API类型的二进制布局在api.json文件中描述。

有关不同语言的示例代码,请参阅 /examples 文件夹

Hello World Application

Rust

use azul::prelude::*;
use azul_widgets::{button::Button, label::Label};

struct DataModel {
    counter: usize,
}

// Model -> View
extern "C" fn render_my_view(data: &RefAny, _: LayoutInfo) -> StyledDom {

    let mut result = StyledDom::default();

    let data = match data.downcast_ref::<DataModel>() {
        Some(s) => s,
        None => return result,
    };

    let label = Label::new(format!("{}", data.counter)).dom();
    let button = Button::with_label("Update counter")
        .onmouseup(update_counter, data.clone())
        .dom();

    result
    .append(label)
    .append(button)
}

// View updates model
extern "C" fn update_counter(data: &mut RefAny, event: CallbackInfo) -> UpdateScreen {
    let mut data = match data.downcast_mut::<DataModel>() {
        Some(s) => s,
        None => return UpdateScreen::DoNothing,
    };
    data.counter += 1;
    UpdateScreen::RegenerateDomForCurrentWindow
}

fn main() {
    let app = App::new(RefAny::new(DataModel { counter: 0 }), AppConfig::default());
    app.run(WindowCreateOptions::new(render_my_view));
}

C++

#include "azul.h"
#include "azul-widgets.h"

using namespace azul;
using namespace azul.widgets.button;
using namespace azul.widgets.label;

struct DataModel {
    counter: uint32_t
}

// Model -> View
StyledDom render_my_view(const RefAny& data, LayoutInfo info) {

    auto result = StyledDom::default();

    const DataModel* data = data.downcast_ref();
    if !(data) {
        return result;
    }

    auto label = Label::new(String::format("{counter}", &[data.counter])).dom();
    auto button = Button::with_label("Update counter")
       .onmouseup(update_counter, data.clone())
       .dom();

    result = result
        .append(label)
        .append(button);

    return result;
}

UpdateScreen update_counter(RefAny& data, CallbackInfo event) {
    DataModel data = data.downcast_mut().unwrap();
    data.counter += 1;
    return UpdateScreen::RegenerateDomForCurrentWindow;
}

int main() {
    auto app = App::new(RefAny::new(DataModel { .counter = 0 }), AppConfig::default());
    app.run(WindowCreateOptions::new(render_my_view));
}

C

#include "azul.h"

typedef struct {
    uint32_t counter;
} DataModel;

void DataModel_delete(DataModel* restrict A) { }
AZ_REFLECT(DataModel, DataModel_delete);

AzStyledDom render_my_view(AzRefAny* restrict data, AzLayoutInfo info) {

    AzString counter_string;

    DataModelRef d = DataModelRef_create(data);
    if (DataModel_downcastRef(data, &d)) {
        AzFmtArgVec fmt_args = AzFmtArgVec_fromConstArray({{
            .key = AzString_fromConstStr("counter"),
            .value = AzFmtValue_Uint(d.ptr->counter)
        }});
        counter_string = AzString_format(AzString_fromConstStr("{counter}"), fmt_args);
    } else {
        return AzStyledDom_empty();
    }
    DataModelRef_delete(&d);

    AzDom const html = {
        .root = AzNodeData_new(AzNodeType_Body),
        .children = AzDomVec_fromConstArray({AzDom_new(AzNodeType_Label(counter_string))}),
        .total_children = 1, // len(children)
    };
    AzCss const css = AzCss_fromString(AzString_fromConstStr("body { font-size: 50px; }"));
    return AzStyledDom_new(html, css);
}

UpdateScreen update_counter(RefAny& data, CallbackInfo event) {
    DataModelRefMut d = DataModelRefMut_create(data);
    if !(DataModel_downcastRef(data, &d)) {
        return UpdateScreen_DoNothing;
    }
    d->ptr.counter += 1;
    DataModelRefMut_delete(&d);
    return UpdateScreen_RegenerateDomForCurrentWindow;
}

int main() {
    DataModel model = { .counter = 5 };
    AzApp app = AzApp_new(DataModel_upcast(model), AzAppConfig_default());
    AzApp_run(app, AzWindowCreateOptions_new(render_my_view));
    return 0;
}

文档

使用build.py脚本构建文档,该脚本将在/target/html目录中生成整个azul.rs网站

python3 ./build.py

注意:如果需要,类文档也可以打印成PDF格式。

许可证

此库根据LGPL 3.0许可证发布,但静态链接有例外。这意味着与FLTK和wxWidgets许可证类似,您可以在不发布源代码的情况下构建专有应用程序:您只需发布对azul库本身所做的更改即可。静态链接例外允许您在不发布代码的情况下静态链接Azul。

版权 2017 - 当前 Felix Schütt

依赖项