Rerun 的 re_ui:基于 egui 的 Viewer 主题系统与 UI 组件库深度指南
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
re_ui 是 Rerun 生态中负责"外观"与"交互组件"的 Rust crate:它围绕egui定义了 Rerun Viewer 的完整主题(Design Tokens)、窗口装饰、字体图标,并为列表项、模态框、通知、过滤框、命令面板等高频 UI 元素提供了一致的构建辅助。读完本文,你将掌握 re_ui 的整体架构、主题加载与热重载机制、图标/字体体系、核心组件用法,以及如何在自己的 egui 应用中复用这套主题。
re_ui 在 Rerun 中的定位
re_ui 的官方定位(见 crates/viewer/re_ui/README.md)可以概括为三件事:
- 定义 Rerun Viewer 的主题:即整套颜色、尺寸、字体、间距等设计令牌(design tokens);
- 提供构建 UI 元素的辅助工具:从按钮、列表项到模态框、通知,都有现成的 helper 可用;
- 包含 Rerun 使用的字体与图标资源:字体文件、PNG/SVG 图标随 crate 一起分发。
它是reruncrate 家族的一员,许可为(MIT OR Apache-2.0) AND OFL-1.1(其中 OFL 部分对应随附的 Inter 字体),由 crates/viewer/re_ui/Cargo.toml 可确认。从 crates/viewer/re_ui/src/lib.rs 的模块清单可以看出它的覆盖范围非常广:主题令牌(design_tokens)、图标(icons)、命令系统(command、command_palette)、列表项(list_item)、模态框(modal)、通知(notifications)、过滤框(filter_widget)、文本编辑(text_edit)、标签栏(tab_bar)、菜单(menu)、拖拽(drag_and_drop)、语法高亮(syntax_highlighting)、相对时间范围(relative_time_range)等三十余个模块。
一句话总结:re_ui 是 Rerun Viewer 的"设计系统层",任何需要与 Rerun 保持视觉一致的 egui 界面,都可以直接依赖它。
快速上手:运行官方示例
README 给出的唯一一条命令即可启动官方示例:
cargo r -p re_ui --example re_ui_example该示例位于 crates/viewer/re_ui/examples/re_ui_example/main.rs,是一个完整的eframe应用,几乎演示了 re_ui 的全部能力:
- 顶部栏(top bar)与原生/自定义窗口装饰切换;
- 左、右、底部三个可折叠面板;
- 日志消息以 toast 通知形式弹出;
- 模态框与"全宽内容"模态框;
- 可折叠分区头(
section_collapsing_header); - 过滤框(
filter_state)驱动的实时列表过滤与匹配文本高亮; - 图标按钮、切换开关、加载指示器、帮助气泡(
Help)、语法高亮、ComboItem、多种尺寸的文本编辑框等。
示例启动时还会注册一个日志接收器(re_log::add_log_msg_receiver),点击 "Log info / warn / error" 按钮即可看到不同级别的日志如何被渲染成带颜色与图标的通知,是观察 re_ui 主题效果最直接的方式。
示例主程序的初始化流程本身也是"如何在 eframe 中使用 re_ui"的标准范式:
let native_options = eframe::NativeOptions { viewport: re_ui::viewport_with_window_chrome( egui::ViewportBuilder::default() .with_app_id("re_ui_example") .with_inner_size([1200.0, 800.0]), re_ui::custom_window_decorations_default(), ), ..Default::default() }; eframe::run_native( "re_ui example app", native_options, Box::new(move |cc| { re_ui::apply_style_and_install_loaders(&cc.egui_ctx); Ok(Box::new(ExampleApp::new(cc.egui_ctx.clone()))) }), )其中re_ui::apply_style_and_install_loaders(&cc.egui_ctx)一行就完成了主题安装与图片加载器的注册(见下文"主题安装"一节)。
主题系统:DesignTokens 与 RON 配置文件
re_ui 主题的核心数据结构是DesignTokens,定义于 crates/viewer/re_ui/src/design_tokens.rs。它覆盖了 egui 默认Style之外、Rerun 特有的视觉决策,典型字段包括:
| 字段 | 含义 | 默认值来源 |
|---|---|---|
large_button_size | 大按钮尺寸 | dark_theme.ron/light_theme.ron |
large_button_icon_size | 大按钮图标尺寸 | 同上 |
large_button_corner_radius | 大按钮圆角 | 同上(6.0) |
small_icon_size | 小图标尺寸 | 同上(14.0) |
modal_button_width/default_modal_width | 模态框按钮宽度 / 默认模态框宽度 | 同上(50 / 400) |
top_bar_color/bottom_bar_color | 顶栏 / 底栏背景色 | 同上 |
shadow_gradient_dark_start | 阴影渐变起始色 | 同上 |
spatial_label_bg_opacity | 空间视图 2D 标签背景透明度 | 同上(0.5) |
slow_animation_duration_sec | 平滑动画时长(秒) | 同上(0.2) |
viewport_background | 视口背景色 | 同上 |
success_text_color/info_text_color | 状态文本色 | 同上 |
除颜色外,还有一组针对具体控件结构的视觉描述:AlertVisuals(告警的填充/图标/文字三色)、Outlines(卡片状态描边:pending/error)、TabVisuals(标签页文字在普通/悬停/选中三态下的颜色)、MetaLineVisuals(元信息行的 label 与 value 配色)、ButtonVisuals(按钮 resting/hovered/pressed 的填充、文字色与描边)、TextEditVisuals(文本编辑框 resting/hovered/focused 的填充、文字、占位符与描边),以及WindowFrameConfig与TableStyle两个枚举。
TableStyle很有意思,它用于表格类 UI 的两种密度:
pub enum TableStyle { /// 展示大量信息、不浪费纵向空间,例如日志输出。 #[default] Dense, /// 需要在单元格内放下大号可点击按钮时使用。 Spacious, }主题文件如何组织
主题数据以 RON 格式存放在 crates/viewer/re_ui/data 目录下,三者配合:
- data/color_table.ron:全局色板,按
Global.Color.<Name>.<Step>定义(例如Gray.0 = #000000、Gray.50 = #080808、Gray.100 = #0d0d0d),是主题文件引用的"颜色字典"; - data/dark_theme.ron:暗色主题令牌,直接给出尺寸数值,颜色通过
{Gray.250}、{Green.700}这类别名引用色板,例如native_frame_stroke取{Gray.250}、top_bar_color取{Gray.100}、tab_bar_color取{Gray.200}; - data/light_theme.ron:亮色主题令牌,结构与暗色版一一对应。
这种"色板 + 主题"分离的设计,正是DesignTokens::load与load_with_color_table两个构造函数的由来(见 design_tokens.rs):
pub fn load(theme: Theme, tokens_ron: &str) -> anyhow::Result<Self> { Self::load_with_color_table(theme, tokens_ron, include_str!("../data/color_table.ron")) } pub fn load_with_color_table( theme: Theme, tokens_ron: &str, color_table_ron: &str, ) -> anyhow::Result<Self> { // 解析色板 → 解析主题 → 把颜色别名解析为 Color32 }load_with_color_table的意义在于:下游 crate 可以不 fork re_ui 就换掉整套色板——只要传入与内置color_table.ron相同结构(同 key、同type: "color")的自定义色板即可。与之配套的是全局入口 lib.rs 中的try_set_design_tokens:它允许在主题初始化之前覆盖暗/亮两套DesignTokens,若令牌已被初始化则返回DesignTokensAlreadyInitializedError。
主题安装:apply_style_and_install_loaders
apply_style_and_install_loaders是初始化入口(lib.rs),它依次完成:
- 调用
egui_extras::install_image_loaders安装图片加载器; - 把暗/亮两版 Rerun logo 以
bytes://logo_dark_mode、bytes://logo_light_mode注册进 egui 的字节图缓存(数据来自 data/logo_dark_mode.png 与 data/logo_light_mode.png); - 将 egui 的
fallback_theme设为Dark(系统主题未知时的兜底); - 对暗/亮两个主题分别执行
design_tokens_of(theme).apply(&mut style)并写回egui_ctx(set_themes内部还会统一设置字体、关闭warn_if_rect_changes_id调试警告,因为滚动表格等控件本来就会按 rect 变化 ID); - 在开启
hot_reload_design_tokens特性(仅 workspace 内构建启用)时,安装文件监视器——主题文件改动后自动重新加载令牌、重设主题并请求重绘。
按需取令牌:HasDesignTokens
代码中随时可以用极简的方式拿到当前主题令牌。HasDesignTokenstrait 为egui::Context、egui::Style、egui::Visuals都提供了tokens()方法(lib.rs),内部按visuals.dark_mode分派到对应的DesignTokens:
let tokens = ui.tokens(); // Ui 层通过 ContextExt / UiExt 提供 tokens.title_bar_height(); // 24.0 tokens.view_padding(); // 12 tokens.panel_margin(); // 左右 12、上下 0 的 Margin窗口外观与平台适配
re_ui 把"窗口本身的观感"也纳入主题范畴,集中在 lib.rs 的几个平台相关函数中:
fullsize_content(os):仅 macOS 返回true,即让内容填满整个窗口、仅左上角保留关闭/最小化/最大化按钮;supports_custom_decorations(os):Windows 与 Linux(Nix)返回true,这两类平台支持自绘窗口装饰;viewport_with_window_chrome(viewport, custom_decorations):根据是否自绘装饰,配置with_decorations、with_fullsize_content_view、with_title_shown,并对支持自定义装饰的平台请求透明表面(Linux 上圆角需要透明背景,Windows 上透明能让缩放更平滑);custom_window_decorations_default():决定当前系统默认是否自绘装饰。逻辑值得注意:- Linux:若没有
WAYLAND_DISPLAY或已有WAYLAND_SOCKET(意味着无法自建 Wayland 连接去探测合成器),直接返回true;否则通过xdg-decoration-unstable-v1协议与合成器协商,只有合成器承诺绘制服务端装饰时才返回false,结果按进程生命周期缓存; - Windows:始终自绘(
true),egui 仍负责投影阴影等效果; - macOS:使用原生装饰但内容绘制进标题栏(
false); - 其他平台:跟随平台默认(
false)。
- Linux:若没有
WindowFrameConfig(Native/Custom { is_maximized })把"原生还是自绘"的决定传递给DesignTokens::top_panel_frame/bottom_panel_frame(design_tokens.rs),后者在自绘模式下会补上圆角(native_window_corner_radius)、去掉右侧内边距(让给窗口控制按钮)、并在最大化时取消圆角。
字体与图标
字体
re_ui 随 crate 携带 Inter 字体(data/Inter-Medium.otf),并附有 OFL 许可证(data/OFL.txt 与 data/Inter-README.txt)。set_themes中无论暗/亮主题都使用同一套字体设置:
// 暗/亮模式字体相同: design_tokens_of(egui::Theme::Dark).set_fonts(egui_ctx);图标:编译期内嵌的 Icon
图标系统定义在 crates/viewer/re_ui/src/icons.rs。核心类型Icon只包含两个字段:人类可读的唯一uri(通常以.png/.svg结尾)与原始图片字节,图标文件在编译期通过include_bytes!内嵌进二进制(因此Icon::new是const fn)。
pub struct Icon { uri: &'static str, image_bytes: &'static [u8], }批量声明借助icon_from_path!宏,以文件路径作为 id,从而避免手写 id 重复导致静默显示 bug:
macro_rules! icon_from_path { ($path:literal) => { Icon::new($path, include_bytes!($path)) }; } pub const PLAY: Icon = icon_from_path!("../data/icons/play.svg"); pub const RERUN_WORDMARK: Icon = icon_from_path!("../data/icons/rerun_wordmark.svg"); pub const RERUN_LOGO: Icon = icon_from_path!("../data/icons/rerun_logo.png");图标源文件位于 data/icons 目录,覆盖播放控制(PLAY、PAUSE、SKIP_TO_END、LOOP)、导航(ARROW_LEFT/RIGHT/UP/DOWN、BACK_SMALL、FORWARD_SMALL)、面板切换(LEFT_PANEL_TOGGLE、RIGHT_PANEL_TOGGLE、BOTTOM_PANEL_TOGGLE)、窗口控制(MINIMIZE、MAXIMIZE、EXPAND)、通用操作(ADD、REMOVE、TRASH、SEARCH、FILTER、SETTINGS、FOLDER)、状态(VISIBLE、INVISIBLE、NOTIFICATION、HELP)、Agent 计划状态(PLAN_PENDING、PLAN_IN_PROGRESS、PLAN_COMPLETED)等,全部以pub const暴露,可在代码里直接引用。
Icon提供多种用法(官方文档注释中的示例):
// 纯图标 ui.add(re_ui::icons::PLAY.as_image()); // 跟随文字颜色的可点击按钮 if ui.add(re_ui::icons::PLAY.as_button()).clicked() { // … } // 作为 Atom 与文字并列 ui.add(egui::Button::new((re_ui::icons::PLAY, "Play"))); // 推荐:走 UiExt helper,自动套用设计令牌 ui.small_icon_button(&re_ui::icons::PLAY, "Play"); ui.small_icon(&re_ui::icons::PLAY, None);细节上,as_image()对 SVG 使用 1.0 缩放,而对 PNG 使用 0.5(因为仓库内 PNG 图标统一按 2x 保存);as_button()开启image_tint_follows_text_color,让图标自动跟随文字颜色。
高频 UI 组件与扩展
除了主题,re_ui 还提供了一批开箱即用的 UI 辅助,按用途可分为几类(模块入口见 lib.rs 的pub mod/pub use):
- 列表与分区:
list_item(ListItem、LabelContent、list_item_scope、show_flat)、section_collapsing_header、panel_title_bar; - 弹层与反馈:
modal(ModalHandler、ModalWrapper,支持full_span_content模式)、notifications(NotificationUi,能把re_log::LogMsg直接渲染成 toast)、loading_indicator、alert; - 输入与选择:
filter_widget(FilterState+format_matching_text高亮匹配文本)、text_edit(ReTextEdit,支持Filled/Outlined变体与Normal/Small/Tiny尺寸)、time_drag_value、combo_item(ComboItem/ComboItemHeader)、relative_time_range; - 结构与导航:
tab_bar(含TAB_TOOLBAR_HEIGHT常量)、menu(menu_style)、drag_and_drop(含层级拖拽示例)、help(Help,为控件生成"鼠标、快捷键"帮助气泡); - 内容渲染:
syntax_highlighting(SyntaxHighlightedBuilder,示例中用于把 EntityPath/InstancePath 高亮)、markdown_utils、url_decorator; - 命令体系:
command定义了UICommand、TableCommand、RecordingCommand、RedapServerCommand四类命令及其 Sender/Kind,并提供listen_for_kb_shortcuts键盘快捷键监听;command_palette则实现了类似 IDE 的模糊命令面板(CommandPalette、FuzzyMatch/FuzzyQuery,基于nucleo-matcher)。
在示例 re_ui_example/main.rs 中可以看到大量真实组合用法,例如用UICommand+ crossbeam 通道实现命令分发,处理ZoomIn/ZoomOut/ZoomReset;用ui.tokens()的title_bar_height、view_padding构造面板;以及用section_collapsing_header构建"Data / Blueprint"分区。
面板结构的推荐写法在示例注释里也写得很明确(见 main.rs 的 RIGHT PANEL 一节):顶层SidePanel不带内边距并负责裁剪,所有内容(标题栏、列表)包在带panel_margin的Frame中,滚动区域放在Frame之外,这样内容既能正确缩进、又能获得完整的全宽作用域而不被滚动条干扰。
测试与快照:主题质量的保障
re_ui 的视觉质量由 crates/viewer/re_ui/tests 目录下的egui_kittest快照测试保障:
- 测试源文件:
command_palette_test.rs、filter_widget_test.rs、help_ui_test.rs、list_item_tests.rs、modal_tests.rs、notification_test.rs; - 快照图片:
snapshots/下按场景输出 PNG,例如tab_bar_dark.png/tab_bar_light.png、alert_dark.png/alert_light.png、modal_normal.png、help_Windows.png/help_Mac.png、以及覆盖duration/sequence/timestamp三类时间轴与五种边界的relative_time_range_*系列快照。
这种"渲染出图 + 逐像素比对"的方式意味着:只要主题令牌或组件布局发生回归,CI 就能立即发现。想复现或更新这些快照,可在仓库中运行相应 crate 的测试命令。此外 re_ui 还提供testingfeature(启用egui_kittest与re_analytics/testing),供依赖它的 crate 复用这套测试基础设施。
在自己的应用中使用 re_ui
虽然 re_ui 随 Rerun workspace 一起开发,但它是独立发布的 crate(publish = true),依赖项以 workspace 成员为主,核心依赖为eframe(wgpu 后端)、egui、egui_extras、egui_commonmark等(见 crates/viewer/re_ui/Cargo.toml)。要在自己的项目中使用,标准流程为:
- 添加依赖(在 Cargo.toml 中加入
re_ui及egui、eframe); - 创建窗口:用
viewport_with_window_chrome+custom_window_decorations_default配置eframe::NativeOptions; - 安装主题:应用创建时调用一次
apply_style_and_install_loaders(&cc.egui_ctx); - 可选覆盖主题:若想自定义色板,用
DesignTokens::load_with_color_table构造暗/亮令牌,并在任何代码路径触发令牌初始化之前调用try_set_design_tokens(dark, light); - 在界面中取用:通过
ui.tokens()(UiExt/ContextExt)或ui.tokens().top_panel_frame(window_frame)等方法获得令牌驱动的 frame、尺寸与颜色,用ui.small_icon_button(...)、list_item::ListItem、modal::ModalHandler、notifications::NotificationUi等组件搭建界面。
注意两点边界:第一,hot_reload_design_tokens特性只在 rerun workspace 内部启用,独立使用 re_ui 时文件热重载不会生效,需自行处理主题更新;第二,部分模块(如命令系统中的RedapServerCommand、语法高亮中的EntityPath/InstancePath)依赖re_uri、re_log_types、re_entity_db等 Rerun 内部 crate,如果你的应用只关心主题与通用组件,可以只取DesignTokens、icons、list_item、modal、notifications等自包含模块。
小结
re_ui 把"Rerun Viewer 的观感"沉淀为一份可复用、可热重载、可测试的设计系统:DesignTokens+ RON 色板负责主题,Icon+include_bytes!负责图标,UiExt/ContextExt/list_item/modal/notifications等模块负责组件,apply_style_and_install_loaders一行完成初始化,快照测试守护长期一致性。无论你是想深入理解 Rerun Viewer 的界面实现,还是想为自己的 egui 应用引入一套成熟的企业级主题,cargo r -p re_ui --example re_ui_example都是最好的起点。
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考