试用跨平台 GUI 框架 HaxeUI

HaxeUI 是一款跨平台 GUI 框架,支持网页、桌面和移动端。 框架前端使用 Hexo 语言开发。框架后端使用图形引擎或者原生跨平台 GUI 框架,比如 OpenFL, NME, Kha,PixiJS, wxWidgets, Winforms 等。最终生成 C++ 代码编译成本地可执行文件或者 HTML 代码。在各种后端中,除了 wxWidgets 和 Winforms 使用原生组件外,其他都是使用图形引擎绘制或者生成 HTML 代码。

img

Haxe 也是一个比较有意思的语言。它本身不能进行本地编译,而是转译成其他语言,比如 Lua, PHP, Java, Python, C++, C# 等,也可以编译成 Neko 或 HashLink 虚拟机的字节码(详见 Haxe Compiler Targets)。Haxe 语言诞生于 2005 年,算是历史久远了,然而其社区却一直不温不火,网上也很难找到中文资料。 Haxelib 上的开发者提交的三方库数量和 Python 的 PyPi 相比,只能说是聊胜于无。

看来也不能对 HaxeUI 抱有太大的希望,让我先在 Windows 上尝试看看。

安装开发环境

首先安装 Haxe 和 HaxeUI(此处使用 Scoop 进行安装):

SHELLscoop install haxe
haxelib install haxeui-core

通过 haxelib run haxeui-core install 命令可以查看 HaxeUI 支持的后端:

 ERROR: no backend specified

 Please use one of the following:

     html5 | hxwidgets | openfl | nme | pixijs | kha | winforms | android | flixel | electron | qt | heaps | raylib | pdcurses | tauri

然而奇怪的是,命令返回的可选后端和官网文档不一致,而且 android, flixel, electron, qt, heaps, raylib, pdcurses, tauri 这几个后端都不可用。不知道是操作系统的平台限制还是这玩意儿根本就是半成品。

本文仅测试跨平台桌面版,选用 haxeui-hxwidgets, haxeui-openfl, haxeui-nme 和 haxeui-kha 四个后端。

haxeui-hxwidgets

haxeui-hxwidgets 是 HaxeUI 的 wxWidgets 后端。 它生成一个通过 wxWidgets 框架从本机组件构建的 GUI 。

可以通过 haxeui-core 自动安装 hxWidgets 后端所依赖的库:

SHELLhaxelib run haxeui-core install hxwidgets

也可以手动安装:

SHELLhaxelib install hxcpp
haxelib install hxwidgets
haxelib install haxeui-hxwidgets

此外还需要下载并编译 wxWidgets 的 C++ 库。我尝试了使用 vcpkg install wxwidgets 来安装编译,但是 HaxeUI 似乎无法识别,只能通过手动安装。

  1. https://www.wxwidgets.org/downloads/ 下载 wxWidgets 最新版本,目前最新版本是 3.2.4 ;
  2. 解压后保存在任意路径下,此处路径为 C:\wxWidgets-3.2.4
  3. 开始菜单中的 Visual Studio 项目下,选择 x86 Native Tools Command Prompt for VS 2022 ,打开编译环境命令行窗口;
  4. 执行命令编译:
    SHELLcd /d C:\wxWidgets-3.2.4\build\msw\
    nmake -f makefile.vc BUILD=release
    nmake -f makefile.vc BUILD=release SHARED=1

如果编译成功,则会在 C:\wxWidgets-3.2.4\lib 目录下生成 vc_lib 和 vc_dll 两个目录。

使用下面的命令在当前目录下创建一个 haxeui-hxwidgets 项目:

SHELLhaxelib run haxeui-core create hxwidgets

在编译项目之前需要先设置一个名为 WXWIN 的环境变量指示 wxWidgets 的安装路径:

SHELLset WXWIN=C:\wxWidgets-3.2.4

在 PowerShell 命令行下则使用:

POWERBASH$ENV:WXWIN="C:\wxWidgets-3.2.4"

使用如下命令编译 haxeui-hxwidgets 项目:

SHELLhaxelib run haxeui-core build hxwidgets

默认配置下,编译会链接 wxWidgets 静态库。可以编辑项目根目录下的 hxwidgets.hxml 配置文件,将 -D WXSTATIC 修改为 -D WXDLL 来启用动态库链接。

需要注意的是,默认编译出来的程序无法正确显示中文字符。其原因是 hxWidgets 会将用户界面上的字符串都转换成 UTF-8 编码,而 Windows 不同于 Linux 系统,中文环境默认使用 GBK (CP936) 编码。要解决这个问题,还需要通过 Manifest 来指定程序的编码。

首先,创建一个 Manifest.xml 文件,内容如下:

XML<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity type="win32" name="MyHaxeUIApp" version="6.0.0.0"/>
  <application>
    <windowsSettings>
      <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
    </windowsSettings>
  </application>
</assembly>

然后执行:

SHELLmt -manifest Manifest.xml -outputresource:Main.exe;#1

mt.exe 并不在默认的 PATH 路径下,而是由 Windows Kits 提供。要在命令行中执行该命令,可以在之前的 x86 Native Tools Command Prompt for VS 2022 命令行窗口中执行。

由于 hxWidgets 使用了原生控件,因此其控件样式无法随意更改,比如不支持 font-style 设置控件的字体样式等。

haxeui-openfl

haxeui-openfl 是 HaxeUI 的 OpenFL 后端。

可以通过 haxeui-core 自动安装 OpenFL 后端所依赖的库:

SHELLhaxelib run haxeui-core install openfl
haxelib run openfl setup

也可以手动安装:

SHELLhaxelib install haxeui-openfl
haxelib install openfl
haxelib run openfl setup

使用下面的命令在当前目录下创建一个 haxeui-openfl 项目:

SHELLhaxelib run haxeui-core create openfl

使用如下命令编译 haxeui-openfl 项目:

SHELLhaxelib run haxeui-core build openfl windows

同样需要注意的是,默认编译出来的程序也无法显示中文字符。和 hxWidgets 不同的是, OpenFL 不是由于编码问题,而是由于 OpenFL 无法使用系统的中文字体导致的。我们可以打包一个中文字体来显示中文。

OpenFL 支持 OTF 和 TTF 格式的字体文件,不支持 TTC 格式的文件文件。TTC 是一系列字体的打包集合文件,可以转换成 TTF 格式。

下载思源宋体,将 source-han-serif.otf 字体文件存放在项目 assets/fonts 路径下。编辑项目根目录下 application.xml 文件,在 <project>...</project> 中添加一行:

XML<assets path="assets/fonts" rename="fonts" />

main-view.xml 中使用中文字体:

XML<style>
    .button {
        font-size: 12px;
        font-name: "fonts/source-han-serif.otf";
    }
</style>

另一个比较严重的问题是,OpenFL 后端的文本框在开启输入法时无法显示输入法候选窗口。虽然能够输入中文,但这相当于在盲打了。这个缺陷导致该后端几乎无法在生产环境中使用。

OpenFL 的本地编译的后端是 lime ,而 lime 的后端又是 SDL ……这层层套娃的关系导致想要定位问题都比较困难。

除此之外,偶尔发生菜单排版错乱,同时按钮失效的问题(遇到过几次,暂时还不清楚触发的条件);还有偶尔会触发 TextField 崩溃弹框提示 Invalid object 的问题。

invalid-object

haxeui-nme

haxeui-nme 是 HaxeUI 的 NME 后端。

可以通过 haxeui-core 自动安装 NME 后端所依赖的库:

SHELLhaxelib run haxeui-core install nme
haxelib run nme setup

也可以手动安装:

SHELLhaxelib install haxeui-nme
haxelib install nme
haxelib run nme setup

使用下面的命令在当前目录下创建一个 haxeui-nme 项目:

SHELLhaxelib run haxeui-core create nme

使用如下命令编译 haxeui-nme 项目:

SHELLhaxelib run haxeui-core build nme windows

与 hxWidgets 和 OpenFL 这两个后端相比,这个后端还算省心,默认编译就能显示中文,只是默认字体有点丑。也可以像 haxeui-openfl 一样,通过编辑 project.nmml 项目配置文件来添加自定义字体。

此外,和 OpenFL 一样,NME 后端的文本框在开启输入法时也无法显示输入法候选窗口,而且一次只能输入一个中文字符,无法输入词语短句。另外还存在输入光标丢失、选中文本后文本不可见等诸多问题。

haxeui-kha

haxeui-kha 是 HaxeUI 的 Kha 后端。安装此后端需要从 GitHub 上下载几百兆的编译环境。最后编译失败,原因不明,也懒得折腾。放弃。

haxeui-flixel

haxeui-flixel 是 HaxeUI 的 flixel 后端。

奇怪的是,haxeui-flixel 无法通过 haxelib run haxeui-core install flixel 命令进行安装,提示 Install not found for "flixel" 错误。

手动安装 haxeui-flixel 后端:

SHELLhaxelib install flixel
haxelib install haxeui-core
haxelib install haxeui-flixel
haxelib run lime setup flixel

使用下面的命令在当前目录下创建一个 haxeui-flixel 项目:

SHELLhaxelib run haxeui-core create flixel

另外,使用 haxeui-flixel 后端的项目也无法使用 haxelib run haxeui-core build flixel windows 之类的命令进行构建,提示如下错误:

 ERROR: no build found
 Invalid field access : execute

Flixel 底层基于 Lime,因此可以用如下命令构建:

SHELLlime build windows

需要注意的是,Flixel 和 OpenFL 一样使用 Project.xml 作为项目配置文件,因此同一个项目需要同时使用 OpenFL 和 Flixel 后端会比较麻烦。

Flixel 主要用于开发像素类游戏,因此内置的是一种像素风的字体,不太适合作为通用应用的后端。且和 OpenFL 后端有同样的问题——默认不支持渲染非英文字符。

haxeui-heaps

haxeui-heaps 是 HaxeUI 的 Heaps 后端。

Heaps 也是一款游戏引擎框架,不过它不支持转译成 C++ 编译。只能编译为 HTML 5 或者 HashLink 虚拟机字节码。该后端的实用性不大,因此本文不做测试。

haxeui-raylib

haxeui-raylib 是 HaxeUI 的 Raylib 后端。

特别地,使用命令 haxelib run haxeui-core install raylib 无法安装 haxeui-raylib,而使用 haxelib install haxeui-raylib 会提示如下错误:

Error: Failed with error: No such Project : haxeui-raylib

另外,使用 haxeui-raylib 后端还需要依赖 raylib-haxe。但是通过 haxelib 安装的 raylib-haxe 版本太旧,无法正确编译。我们只能从 GitHub 安装最新的版本:

SHELLhaxelib git raylib-haxe https://github.com/haxeui/raylib-haxe
haxelib git haxeui-raylib https://github.com/haxeui/haxeui-raylib

不得不说,这个 HaxeUI 还真是个半成品呀~

使用下面的命令在当前目录下创建一个 haxeui-raylib 项目:

SHELLhaxelib run haxeui-core create raylib

使用如下命令编译 haxeui-raylib 项目:

SHELLhaxe raylib.hxml

和 Flixel 相似,Raylib 后端默认也是用像素风格的字体,且默认不支持渲染非英文字符。

总结

HaxeUI 内置的控件十分丰富,在使用图形引擎后端的情况下,基本能保持不同平台下界面布局和样式的一致性。但是各种小问题也很多,特别是对 i18n 的支持有很多问题。

下面列举了一些主要问题:

对于桌面应用,在这些后端里唯二可用的便是 OpenFL 和 wxWidgets,其他后端可以不用考虑。


浅析 OpenFL 中文显示问题

后续稍微分析了一下 OpenFL 的源代码,大致弄清楚 OpenFL 为何默认无法显示中文了。

查看 src/openfl/text/_internal/TextEngine.hx 中的 getDefaultFont() 函数实现。OpenFL 内置了三组字体,分别是 _sans, _serif_typewriter 。这三组字体在 Windows 中分别对应了 Arial, Times New RomanCourier New 。这三组字体都是英文字体,不包含中文字形。而 haxeui-openfl 组件默认使用 _sans 字体,因此无法渲染中文字符。

HAXE#if windows
__defaultFonts.set("_sans",
    new DefaultFontSet(findFont(systemFontDirectory + "/arial.ttf"), findFont(systemFontDirectory + "/arialbd.ttf"),
        findFont(systemFontDirectory + "/ariali.ttf"), findFont(systemFontDirectory + "/arialbi.ttf")));

__defaultFonts.set("_serif",
    new DefaultFontSet(findFont(systemFontDirectory + "/times.ttf"), findFont(systemFontDirectory + "/timesbd.ttf"),
        findFont(systemFontDirectory + "/timesi.ttf"), findFont(systemFontDirectory + "/timesbi.ttf")));

__defaultFonts.set("_typewriter",
    new DefaultFontSet(findFont(systemFontDirectory + "/cour.ttf"), findFont(systemFontDirectory + "/courbd.ttf"),
        findFont(systemFontDirectory + "/couri.ttf"), findFont(systemFontDirectory + "/courbi.ttf")));
TextEngine.hx

如果 OpenFL 要使用中文字体,可以在代码中添加:

HAXEopenfl.text.Font.registerFont(openfl.text.Font.fromFile(lime.system.System.fontsDirectory + "/simhei.ttf"));

然后还要在视图中显式定义字体:

HTML<style>
* {
    font-name: SimHei;
}
</style>

暂时没有通用的解决方案,针对不同的语言环境,必须显式定义不同的字体。

而 NME 后端之所以能正常显示中文是因为 NME 使用了本地 C++ 代码,直接调用 Windows 字体渲染接口,实现了 Fallback Font 的特性。