Skip to content

GraphView ​

GraphView 是 NGT 图的底层可编辑 UI。

它包含 header、可平移画布、图元素、Item Library、Blackboard、Inspector、Preview 面板、history stack、命令处理和诊断 footer。

TIP

实际图编辑器优先使用 GraphEditorView。它包装 GraphView,并提供保存、dirty 状态、面包屑路径、子图 dive 和外部子图编辑支持。

GraphView with its header, canvas, blackboard, inspector, and graph log
GraphView / GraphEditorView 中的 header 开关、画布、Blackboard、Inspector、Preview 和日志区域。

直接加载 ​

java
var graphView = new GraphView();
graphView.loadGraph(graph);

这适合测试 screen、预览,或嵌入式图显示。

在普通 UI 中:

java
root.addChild(graphView.layout(layout -> {
    layout.widthPercent(100);
    layout.heightPercent(100);
}));

完整编辑器 view 使用:

java
var editorView = new GraphEditorView();
editorView.loadGraph(graph, savedTag -> saveGraph(savedTag));

内置工具 ​

GraphView 创建这些工具:

工具字段说明
Item LibraryitemLibrary用于创建节点、常量、blocks 和子图的弹出库。
Blackboardblackboard变量声明。
Inspectorinspector选中模型的属性。
Previewpreview图级预览工具。
Dock ManagerdockManager可浮动和停靠到角落的图面板。
HistoryhistoryStack图命令的 undo 和 redo。

默认面板放在画布周围:

  • Blackboard:左上。
  • Inspector:右上。
  • Preview:右下。

Item Library 推荐列表 ​

自 2.2.38

从端口拖出连线并松开在画布空白处时,Item Library 会打开推荐列表,列出所有带有兼容端口的节点。端口类型与连线类型完全一致的节点排在前面,只能通过可赋值关系接收该类型的节点排在后面。两组内部都保持 Item Library 的常规顺序。

Commands and History ​

大多数图编辑都会通过 GraphView.dispatchCommand(...) 派发命令。

当命令可撤销时,会记录到 view history stack。Header 提供 undo、redo、snap-to-grid、对齐附近元素和 fit 按钮。从 2.2.40 起,undo 和 redo 按钮的 tooltip 会显示编辑器 keymap 中当前绑定的快捷键。

GraphView 处理常见编辑器操作:

  • 创建节点,
  • 创建连线,
  • 删除,
  • cut/copy/paste,
  • duplicate,
  • move,
  • rename,
  • recolor,
  • 创建 placemat,
  • 从选区创建子图,
  • 将连线转换为 portals,
  • 在连线上插入中继点,
  • 自动布局。

子图 dive 由 GraphEditorView 处理,单独使用裸 GraphView 不支持完整流程。

快捷键 ​

快捷键操作
Ctrl+A选中画布上的所有元素,不包括 Blackboard。自 2.2.42
Ctrl+C / Ctrl+X / Ctrl+V复制、剪切、粘贴选区。
Ctrl+D复制一份选区(duplicate)。
Ctrl+Z撤销。
Ctrl+Y / Ctrl+Shift+Z重做。
Ctrl+S保存(仅当 view 位于 GraphEditorView 中)。
Delete删除选区。
拖动时按住 Alt元素精确落在光标处,不做网格吸附和元素对齐。
双击连线插入中继点。

在 LDLib2 Editor 中,这些 Ctrl 组合键来自编辑器 keymap,可以重新绑定,见 Keymap。Delete 由 GraphView 自己处理。

Layers ​

图元素会渲染到命名 layer 中。

java
graphView.setLayers(List.of(
        PlacematElement.PLACEMAT_LAYER,
        WireElement.WIRE_LAYER,
        NodeElement.NODE_LAYER,
        StickyNoteElement.STICKY_NOTE_LAYER
));

自定义元素需要特定画布 layer 时,使用 getLayer(name)。

视图偏好 ​

自 2.2.41

网格吸附开关、网格大小、元素对齐开关和连线样式会按图类型记住。它们保存在 config/ldlib2/graph_view.json 中,以图的类全名为 key,不属于图文件本身。

WARNING

loadGraph(...) 会应用该图类型记住的偏好。

  • 在 loadGraph(...) 之前调用的 setter 会被覆盖。
  • 在 loadGraph(...) 之后调用的 setter 会改写用户为该图类型保存的偏好。

如果只想设置当前 view 而不写入偏好文件,在 loadGraph(...) 之后调用 applyPreferences(...)。

java
graphView.loadGraph(graph);
graphView.applyPreferences(new GraphViewPreferences.Entry(
        true,                    // 吸附到网格
        16f,                     // 网格大小
        true,                    // 对齐附近元素
        WireRouteStyle.CURVED));

currentPreferences() 返回 view 当前的设置。GraphViewPreferences.Entry.DEFAULTS 为:网格吸附开启、大小 16、元素对齐开启、WireRouteStyle.DEFAULT。

Snap and Fit ​

网格吸附默认启用。

java
graphView.setSnapToGrid(true);
graphView.setGridSnapSize(16f);

使用 fit 按钮或调用:

java
graphView.fitGraphChildren();

吸附与对齐 ​

自 2.2.41

拖动元素时,整个选区按同一个偏移量移动。选区离网格最近的那条边会吸附到网格,并与附近元素的边和中线对齐,每次对齐都会显示一条参考线。按住 Alt 可以让选区精确落在光标处。

Header 中的磁铁按钮用于开关元素对齐。在代码中:

java
graphView.setSnapToElements(true);
graphView.setElementSnapThreshold(7f); // 屏幕像素,默认 7
graphView.setElementSnapRange(600f);   // 屏幕像素,默认 600

这两个距离都以屏幕像素为单位,使用前会除以画布缩放,因此在任何缩放级别下吸附手感一致。

连线样式 ​

自 2.2.41

右键画布,打开 连线样式(Wire Style),选择当前 view 中所有连线的走线方式。

WireRouteStyle外观
DEFAULT直线连接,两端各有一小段引出线,拐角圆滑。
CURVED三次贝塞尔曲线,沿端口方向引出。
OCTILINEAR沿轴向的线段之间用 45° 斜线衔接。
ORTHOGONAL只有水平和竖直线段,拐角为圆角直角。
STRAIGHT端口到端口的直线,没有引出线。
Curved wire styleOctilinear wire style
CURVEDOCTILINEAR
Orthogonal wire styleStraight wire style
ORTHOGONALSTRAIGHT

样式作用于整个 view:同一个 graph view 中的所有连线使用同一种画法。

java
graphView.setWireRouteStyle(WireRouteStyle.ORTHOGONAL);

自动布局 ​

自 2.2.41

右键画布,打开 自动布局(Auto Layout):

菜单项GraphLayoutAlgorithm用途
分层布局 (Sugiyama)LAYERED数据流图。沿连线方向把节点排成若干列。
网格布局GRID忽略连线的均匀网格。
力导向布局FORCE_DIRECTED没有明显流向的图。每个连通的簇单独布局。

有选区时只排列选中的元素;没有选区时排列整张图。流向由相关连线上的端口决定:竖向端口占多数时从上到下排列,否则从左到右。

Placemat 会改变"一个单元"的含义:

  • 选中了 placemat 本身:placemat 及其内部所有内容作为一个整体移动,内部相对位置不变。
  • 只选中了 placemat 内的节点:这些节点在 placemat 内部重新排列。placemat 会撑大以容纳结果,但不会缩小。

自动布局是普通的可撤销命令:

java
graphView.dispatchCommand(new LayoutCommands.AutoLayoutCommand(
        selectedElements, GraphLayoutAlgorithm.LAYERED));

连线中继点 ​

自 2.2.37

中继点(reroute point)让连线经过一个固定位置再折向目标。它只改变连线的画法,连线连接的仍然是原来的两个端口。

Wires bent through reroute points, with one point branching into two wires
经过中继点的连线。每个中继点左侧是输入点,右侧是输出点。
操作结果
双击连线,或右键连线选择 添加中继点在光标处插入中继点。
从中继点右侧(输出)点拖到某个输入端口新增一条分支,共用中继点之前的主干。
从中继点左侧(输入)点拖到某个输出端口,或把输出端口拖出的连线放到它上面经过该中继点的所有连线都改由这个端口出发。
拖动中继点本体移动中继点,经过它的连线跟着移动。
删除中继点去掉这个折点,连线保持连接。

由于走线和拓扑是分开的,WireModel.getFromPort() 和 getToPort() 不会变化,图的执行逻辑不需要关心中继点。

常用 API:

API用途
WireModel.getReroutePoints()连线经过的中继点,从输出端口一侧到输入端口一侧排列。
GraphModel.insertReroutePointOnWire(wire, index, position)在指定线段处折弯连线。
WireCommands.InsertReroutePointCommand可撤销的插入。
WireCommands.ReSourceReroutePointCommand可撤销的改接来源。

中继点保存在图 NBT 的 reroutePoints 下,复制粘贴时也会一并带上。

只读 view ​

自 2.2.38

setReadOnly(true) 把 graph view 变成查看器。

java
var graphView = new GraphView();
graphView.setReadOnly(true);
graphView.loadGraph(graph);

WARNING

必须在 loadGraph(...) 之前调用 setReadOnly(...)。端口的内嵌编辑器在构建 UI 树时读取这个标记,之后再修改,已经显示的编辑器仍然可编辑。

只读 view 中:

  • dispatchCommand(...) 拒绝所有命令,
  • Blackboard 和 Inspector 仍然显示,但不接受输入,
  • undo 和 redo 被禁用,
  • 右键不会弹出菜单,只用于平移画布,
  • 只有 Ctrl+A 和 Ctrl+C 仍然可用,方便用户把内置图复制到自己的图中。

GraphEditorView.setReadOnly(...) 会对子图栈中的每一层做同样的设置,并隐藏保存按钮。当 provider 无法编辑某个图资源时,该资源就会以这种方式打开,见 Editor Resources。

Graph.onGraphChanged(GraphLogger) 会向 graph log footer 提供数据。

用于显示编辑时可见但不序列化的图校验消息。

Released under the MIT License.