滚动与焦点
滚动:Overflow 与 ScrollPosition
Bevy UI 的滚动不需要额外插件——它内置于布局系统。给一个 Node 设置 overflow: Overflow::scroll_y()(垂直滚动)或 Overflow::scroll()(双向滚动),超出的内容就会被裁切,并且可以通过 ScrollPosition 组件控制滚动偏移。
fn setup(mut commands: Commands, assets: Res<AssetServer>) {
let font = assets.load("fonts/FiraSans-Bold.ttf");
commands.spawn(Camera2d);
commands.spawn((
Node {
width: percent(100),
height: percent(100),
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
children![(
Node {
flex_direction: FlexDirection::Column,
width: px(250),
height: px(200),
overflow: Overflow::scroll_y(),
..default()
},
BackgroundColor(Color::srgb(0.1, 0.1, 0.1)),
Children::spawn(SpawnIter((0..30).map(move |i| {
(
Node {
min_height: px(28),
padding: UiRect::axes(px(12), px(4)),
align_items: AlignItems::Center,
..default()
},
children![(
Text(format!("第 {} 行", i + 1)),
TextFont {
font: FontSource::Handle(font.clone()),
font_size: FontSize::Px(16.0),
..default()
},
TextColor(Color::srgb(0.85, 0.85, 0.85)),
)],
)
}))),
)],
));
}Listing 29-5:可滚动列表——Overflow::scroll_y + Children::spawn(SpawnIter)
关键点:
- Overflow:
Overflow::scroll_y()只允许垂直滚动,Overflow::scroll()允许双向,Overflow::scroll_x()只允许水平。默认是Overflow::visible(不裁切)。 - ScrollPosition:
ScrollPosition(Vec2::new(x, y))控制当前滚动偏移。你可以直接修改它来程序化滚动。 - 内容溢出:子节点的总高度超过容器高度时,滚动才有意义。这里 30 行 × 28px = 840px,容器只有 200px,所以可以滚动。
- SpawnIter:
Children::spawn(SpawnIter(iter))是生成大量同类子节点的惯用法——传入一个迭代器,每项是一个 Bundle。注意必须传迭代器本身,不要先.collect::<Vec<_>>()。
Bevy 的滚动默认支持鼠标滚轮(通过 bevy_input 的 MouseWheel 事件和 ui_focus_system 自动处理)。触摸滚动需要 bevy_winit 的触摸事件支持。
滚动条控件
bevy_ui_widgets 还提供了 Scrollbar 控件——一个可以绑定到任意滚动容器的独立滑块。它的用法比手动实现滚动条复杂一些(需要 Grid 布局、CoreScrollbarThumb 标记、CoreScrollbarDragState 等),官方示例 scrollbars.rs 有完整参考。本章不展开。
焦点与键盘导航
bevy_input_focus crate 提供了 UI 焦点管理:
- InputFocus:Resource,存储当前获得焦点的实体;
- InputFocusVisible:Resource,控制焦点环(focus ring)是否可见;
- TabNavigationPlugin:提供 Tab 键导航支持;
- InputDispatchPlugin:将键盘事件分发给焦点实体。
焦点系统对无障碍访问至关重要。bevy_ui_widgets 的 Button、Slider、Checkbox 都内置了键盘处理(Enter/Space 触发按钮,方向键控制滑条),但前提是有焦点系统将键盘事件路由到正确的实体。
使用焦点系统只需两步:
app.add_plugins((InputDispatchPlugin, TabNavigationPlugin));然后给可聚焦的实体添加 TabIndex(0) 组件(来自 bevy::input_focus::tab_navigation)。Tab 键会在 TabIndex 不为 -1 的实体间循环切换焦点。
对于简单的桌面应用,焦点系统不是必须的——鼠标交互不依赖它。但如果你的目标平台包括移动端或需要无障碍支持,焦点系统是基础设施。