d中声明式gui
原文
几个月前,我用D展示了白噪声应用并用了一点,但后来想添加其他噪声颜色和图界.
我在网上搜索了些代码来复制/粘贴噪音(并找到了以下代码:https://noisehack.com/generate-noise-web-audio-api/,D的一个优点是它很容易从其他语言移植代码.复制/粘贴,js几乎可在D中工作,
并且很容易用minigui.d给界面加东西.
但是,当输入代码来实例化类,并附加事件监听器时,我想使它更自动一些.
用户端
所有这些都是预发布,如有更改,恕不通知.
下面是完整程序(依赖于arsd git master)
import arsd.simpleaudio;
import arsd.minigui;
import std.random;
void main() {
auto window = new MainWindow("Noise App");
auto ao = AudioOutputThread(true);
enum Algorithm {
brown,
pink,
white
}
@Container!HorizontalLayout(
Container!VerticalLayout("default"),
Container!(Style.maxWidth(32))("volume")
)
struct Control {
private bool paused;
Algorithm algorithm;
@ControlledBy!Button("Start / Stop")
void pause() {
if(paused)
ao.unpause();
else
ao.pause();
paused = !paused;
}
@ControlledBy!VerticalSlider(0, 32000, 800)
int volume = 3200; // really a short
}
Control control;
window.addDataControllerWidget(&control);
// 粉红
float b0 = 0.0, b1 = 0.0, b2 = 0.0, b3 = 0.0, b4 = 0.0, b5 = 0.0, b6 = 0.0;
// 棕色
float lastOut = 0.0;
ao.addChannel = delegate(short[] buffer) {
const algorithm = control.algorithm;
const volume = control.volume;
foreach(ref item; buffer) {
final switch(algorithm) with(Algorithm) {
case white:
item = cast(short) uniform(-volume, volume);
break;
case pink:
float white = uniform(-1.0, 1.0);
b0 = 0.99886 * b0 + white * 0.0555179;
b1 = 0.99332 * b1 + white * 0.0750759;
b2 = 0.96900 * b2 + white * 0.1538520;
b3 = 0.86650 * b3 + white * 0.3104856;
b4 = 0.55000 * b4 + white * 0.5329522;
b5 = -0.7616 * b5 - white * 0.0168980;
auto output = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
output *= 0.11;//(粗略)补偿增益
b6 = white * 0.115926;
item = cast(short) (output * volume);
break;
case brown:
float white = uniform(-1.0, 1.0);
float output = (lastOut + (0.02 * white)) / 1.02;
lastOut = output;
output *= 3.5; //(粗略)补偿增益
item = cast(short) (output * volume);
break;
}
}
return true;
};
window.loop();
}
// https://noisehack.com/generate-noise-web-audio-api/
当然,在窗口上,minigui使用本地控件,所以它就像它们一样.
现在,更深入研究代码.
@Container!HorizontalLayout(
Container!VerticalLayout("default"),
Container!(Style.maxWidth(32))("volume")
)
struct Control {
Algorithm algorithm;
@ControlledBy!Button("Start / Stop")
void pause() {}
@ControlledBy!VerticalSlider(0, 32000, 800)
int volume = 3200;
}
Control control;
window.addDataControllerWidget(&control);
这段代码是用户端.底部,加数据控制器小部件(addDataControllerWidget)是使用UDA创建UI并拼接起来的必要的事件处理器.
第一,构上的@Container,UDA允许定义布局.如果没有该布局,它就用给定父控件的默认容器(在此窗口中,垂直排列子控件).
工作原理是按模板参数覆盖类名和/或风格,然后把串名和子项列表作为其他参数,可任意改造树.
然后,构内部,有普通方法和数据成员,但又有额外的UDA.因为它是枚举,Algorithm有默认的组件(下拉式选取器).
我用@控件由!按钮(@ControlledBy!Button)代码覆盖了方法的默认值,指示按钮小部件应触发此调用.在此,按模板参数再次传递一个类,然后传递其构造器的一些参数.注意,没有传递parent参数,稍后调用添加数据控制器小部件(addDataControllerWidget)会处理.类似,没有设置事件处理器,因为稍后库会设置它.
小部件位置当前与名字关联.叫"volume"的Container接收小部件的体积变量.由于其他文件没有指定位置,因此位于"默认"容器中(或如果未指定默认值,就使用最后).
未来发展方向是从静态反射中提取越来越多的数据,并在自动默认不充分时,使其容易自定义.
小提示:
Container!(Style.maxWidth(32))
是特例.我没有定义类,而是给予列举了我想在模板基类上覆盖的各个方法的一个列表.风格只是opDispatch构,产生稍后用来插件的方法.
不应用虚函数来处理.匿名嵌套类,script.d子类和其他各种技巧就够了,但仍然假设每个类都有些合适的静态值,一般是正确的,但并不总是正确的.而且它限制了像运行时加载css等.
内部,该覆盖列表用来按需生成新类.
内部
至少在早期阶段,实现是相当简单的.
一般想法是UDA分解为简单的运行时构:UI定义表和类工厂函数.复杂工作由处理这些数据的普通运行时函数来完成.,也可在运行时数据定义中使用这些工具.(minigui应该既是小库,又允许用少量代码轻松添加gui特性),并且可透明地生成并使用附加工厂.
数据绑定也是自动生成的,同时自动注册必要事件.目前按传统工作:它根据识别的小部件设置事件,根据识别的类型设置值.需要以后扩展.
深入了解一下代码.
加数据控制器小部件(addDataControllerWidget)是个只是数据控制器小部件模板(DataControllerWidget)的UFCS函数.它是接受某个数据构和父小部件指针的Widget的子类(稍后会添加一些其他接口).它检查指针类型,来提取注释并构建数据树.
控制由(ControlledBy)也是造拥有工厂(私有构建(construct)方法)和参数(构成员)的ControlledBy_构的工厂函数.
更复杂的是容器(Container).它是继承给定基类,插件进给定类,然后定义调用操作(opCall)的模板类.在UDA中调用opCall来返回包括实例化完整容器类的工厂函数指针的静态数据.
当然可用不同方法来实现该点.开始想法是按UDA,附加类自身,如:
@VerticalLayout
struct Control {
@Button
void pause() {}
}
可用调用操作(opCall)重载,但这表明改造所有的旧类.(哦,我希望static opCall(this This)()工作).
所以添加了外部项,容器(Container)和控制由(ControlledBy),但保持了相同的调用操作接口.有趣的是,也可在过程代码中新建容器(new Container!(...))!(...),及潜在地在更多环境中重用它.
静态调用操作(static opCall)返回ContainerMeta.这里没有模板,所有运行时数据都是在编译时创建的.可用简单可变函数语法创建树,并在稍后处理它.不值得创建完整编译时布局器,使用运行时数据表,让我重用现有代码,并可能用(如UI设计器工具)运行时文件和脚本语言链接起来.
一个开放问题是,不用设置数据值,如何改变UI,及反之.目前,UI可改变构,但构不能改变UI.我正在考虑使DataControllerWidget接口,在代码中提供它,及手动属性来额外定制其他勾挂.
D在GUI方面的真实的潜力并不在于实现底层原语和小部件,而是在于利用D的内省功能来创建新的方便的API.
浙公网安备 33010602011771号