【发布时间】:2015-03-10 14:48:05
【问题描述】:
我正在尝试为原理图、布局和绘图设计一个桌面 UI。只是从实际的软件设计师那里寻求高水平的建议。
假设有一个内存中的“数据库”(所有用户数据的任意深度的 clojure 映射,可能还有另一个用于应用程序首选项的映射等),我正在研究如何在模型-视图-控制器上进行操作这些,其中数据可能由以下任何一项或多项呈现和修改:
- 显示单个参数(例如框宽)的独立文本字段。
- 一种“检查器”类型的视图,显示选定对象的多个参数,例如框宽度、高度、颜色、复选框等。
- 一种表格/电子表格类型的视图,显示多个对象的多个参数,可能是整个数据库
- 整个事物的图形渲染,例如示意图和布局视图。
修改其中任何一个都应立即显示在其他所有活动视图中,包括文本和图形,而不是在单击“确定”之后...因此不允许使用模式框。如果由于某种原因表格视图、检查器视图和图形渲染都在视图中,则以图形方式拖动框的角应该立即显示在文本中,等等。
有问题的平台是 JavaFX,但我希望 UI 和其他所有内容之间有一个清晰的分离,所以我想避免 JFX 意义上的binding,因为这将我的设计数据与 JFX 属性紧密联系在一起,增加了模型的粒度,并迫使我在处理数据的标准 clojure 函数之外工作,和/或大量处理整个getValue/setValue 世界。
我仍然假设至少一些有状态/可变性,并使用内置的 Clojure 功能,例如在 atom/var/ref 上 add-watch 并让运行时信号相关函数。
平台特定的交互将与实际的 UI 紧密结合,例如具体化ActionListeners,以及处理ObservableValues 等,并将尽量减少对JavaFX Property 等实际应用程序的依赖数据。我不会为此而取悦 FRP。
我不介意扩展 JFX 接口或编写自己的协议以使用特定于应用程序的defrecords,但我更希望应用程序数据保持为直接 Clojure 数据,不受平台污染。
问题是如何在最接近不可变模型的情况下设置这一切。我看到了几个选项:
- 细粒度:每个参数值/基元(即 Long、Double、Boolean 或 String)都是一个原子,每个可以修改值的视图“到达”数据库中的任何需要更改的值价值。这可能很糟糕,因为可能有数千个单独的值(例如手绘曲线上的点),并且需要大量
(deref...)垃圾。我相信这就是 JFX 想要做到这一点的方式,在叶节点等处有巨大的属性数组,感觉很臃肿。使用这种方法似乎并不比仅使用 Java/C++ 编码好多少。 - 中粒度:数据库中的每个对象/记录都是 Clojure 映射的一个原子。当其中任何一个值发生更改时,整个地图都会被替换。需要处理的原子总数更少,并且允许例如用于各种事物的长数组。但是,当数据库中的某些对象需要比其他对象更多的嵌套时,这会变得复杂。
- 粗粒度:只有一个原子:数据库。任何时候发生任何变化,整个数据库都会被替换,并且每个视图都需要重新渲染其特定部分。这感觉有点像用锤子拍打苍蝇,天真的实现需要一直重新渲染所有内容。但我仍然认为这是最好的权衡,因为任何原语都有从根节点开始的清晰访问路径,无论是在每个基元级别还是在每个记录级别访问。
我还需要一个数据模板多次实例化的能力。因此,例如,如果用户更改了在多个地方使用的符号或形状,则单个编辑将适用于所有地方。我相信这也需要某种类型的类似“指针”的行为。我想我可以将原子存储到模型中,然后根据需要进行实例化,它可以在上述任何一种颗粒模型中工作。
还有其他方法吗?试图用函数式语言做一个类似 GUI 编辑器的工具只是愚蠢的吗? 谢谢
【问题讨论】:
-
当您将 JavaFX 定位为 GUI 工具包时,也许研究 SceneBuilderKit(用于图形化编辑 JavaFX 场景的库框架)及其 source code 可能对您有所帮助。它的某些功能与您所描述的相似,但有所不同。不过,它绝对不是基于 clojure 类型的函数模型。
-
是的,我的目标是 JavaFX。在工作、交通和尖叫的婴儿之间偶尔敲击键盘几个月后,我终于设法获得了一个匿名/本地
ChangeListener来更改原子并触发 Clojure 手表。尼托。我还没有见过 SceneBuilderKit(虽然我玩过他们的 SceneBuilder 桌面应用程序),但我会看看。另一个考虑因素是是否允许 JFX 使用自己的图元拥有整个场景图,或者是否以标准的“OnPaint”样式在画布上绘制我自己的形状,但这对于这个 SO 问题来说是题外话。 -
是的,索尼克有很多选择。这是一个很好的问题,但很难回答。我的猜测是,如果您不使用场景图和一些预构建的控件(例如 [ControlsFX PropertySheet]),工作量会更大。可以给clojurefx 开发者发邮件,听听他对你的问题的看法。
-
忘记 MVC 模式,转而采用带有 core.async 通道作为事件总线的 Flux 架构怎么样?只是我的 2 美分。
-
谢谢,我尽量不依赖“模式”,但我使用了这个术语,以便读者立即知道我在做什么。在最低级别,用户交互程序的状态是可变的,UI 平台有需要注册的回调。如何使用 Flux、React、core.async、compojure、datomic、node.js……啊……东西太多了!我仍然不在clojurescript火车上。我只剩下几个神经元了,所以我想尽量减少过载。