VS Code的树形视图获取当前聚焦节点的信息
如何将TreeView的选中状态与键盘焦点导航同步?
在我的VS Code扩展中,我用 vscode.window.createTreeView 创建了一个自定义视图 vortex-explorer。我在鼠标与键盘交互之间发现了不一致:
- 鼠标交互:单击一个节点时,
treeView.selection正确更新,逻辑按预期触发。 - 键盘导航:使用箭头键(向上/向下箭头)时,视觉焦点(Focus)在项之间移动,但
treeView.selection数组不会更新。它始终卡在最后一次用鼠标明确单击的项上。
我追求的是一个 “所见即所得” 的导航体验。
- 无论用户是点击还是仅仅用键盘移动高亮,扩展都应能够实时获取当前“聚焦”节点的数据。
- 这对于实现诸如“实时预览”的功能很重要,当用户用键盘滚动列表时,侧边栏的内容可以即时更新。
看起来VS Code将 Focus 与 Selection 视为两个不同的状态:
- 键盘箭头默认会移动Focus。
treeView.selection只有在发生Selection事件时才会更新(通常通过点击或回车)。 因为我依赖selection属性,我的扩展对仅通过键盘导航引发的聚焦变化没有显式的“按下回车”就触发的情况时就“看不见”。
问题:
- VS Code API是否提供类似
onDidChangeFocus的钩子用于捕捉键盘驱动的移动? - 在
TreeViewOptions中是否有配置可以强制“Focus”自动触发“Selection”? - 在不强制用户按Enter的情况下,实现“按箭头键预览”功能的标准做法是什么?
解决方案
如果这个 vscode.proposed.treeViewActiveItem 最终被添加,那就都好办了。它将直接暴露聚焦项(与选中项相对)。
如果你对该功能感兴趣,应将你的用例添加到 Async data tree doesn't maintain consistency on focus and selection events,在拟议API合并之前必须解决这个问题。
“VS Code API是否提供类似onDidChangeFocus的钩子以捕捉键盘驱动的移动?”
没有,但起初我以为有另一种方法——拦截在你对树进行鼠标按下时触发的命令 list.focusDown。大致如下:
commands.registerCommand( 'list.focusDown', async ( treeItem ) => {
console.log( treeItem ); // undefined
// change tree selection, this will fire onDidChangeSelection()
await symbolView.reveal( symbolView.selection[ 0 ].children[ 0 ] as any, {
select: true,
focus: true,
expand: true
} );
} ),
这确实可行,但有两个问题。
第一,命令不会返回参数——treeItem 总是未定义。这可以解决,因为你知道树的选中项是前一个项,可以据此查找。
第二,list.focusDown 在vscode UI的很多地方都会被使用,你希望只有在树获得焦点时才触发它。但那并不容易判断,也不值得去跟踪。显然,在vscode里似乎永远不会有 getContext() API……
于是我认为你应该能够在你的 package.json 中创建一个可以利用上下文值的快捷键绑定。把下面的放在 package.json 里试试:
"commands": [
{
"command": "symbolsTree.focusDown",
"title": "Mouse down in the SymbolsTree",
"enablement": "focusedView == 'symbolsTree' && listFocus"
}
]
// symbolsTree is the id of my TreeView
"keybindings": [
{
"key": "down",
"command": "symbolsTree.focusDown",
"when": "focusedView == 'symbolsTree' && listFocus"
}
在你的代码中:
commands.registerCommand( 'symbolsTree.focusDown', async ( treeItem: any ) => {
console.log( treeItem ); // still undefined so of no use
// change tree selection, this will fire your onDidChangeSelection()
await symbolView.reveal( symbolView.selection[ 0 ].children[ 0 ] as any, {
select: true,
focus: true,
expand: true
} );
} )
这似乎效果相当不错。你需要以类似的方式处理鼠标 up,也许还要处理像PageDown/PageUp这样的其他按键。
AI提示你创建一个扁平数组(若TreeItems有子项)来保存TreeItem及其子项——遍历TreeItems并将它们推入数组。然后你可以从Treeview.selection找到选中的TreeItem,并获取下一个数组项。
实现起来应该不难,但你需要考虑如何处理折叠的TreeItems(若有)并在TreeItem发生任何变化时更新扁平化数组。