feat(gpui, gpui_widgets): i18n string-table hook + node-graph fit API

- gpui::i18n: minimal string-table override (set_table/tr/clear_table) with
  built-in defaults; effect-stack and viewer strings now go through it so
  hosts can localize widget-baked labels without a full i18n framework.
- gpui_widgets::i18n re-exports the hook (gpui_widgets::i18n::set_table).
- node_graph: GraphViewState::fit_to_rect for fit-window/initial viewports
  (unit tested); NodeGraphView::viewport_size accessor for fit targets.
This commit is contained in:
2026-08-10 16:52:39 +08:00
parent 16ae7c42df
commit 49e471ae64
8 changed files with 222 additions and 8 deletions
+31
View File
@@ -0,0 +1,31 @@
//! Localization hook for widget-baked strings.
//!
//! Re-exports the [`gpui::i18n`] string-table override API so hosts can
//! localize the widgets' built-in strings as
//! `gpui_widgets::i18n::set_table(..)`. See [`gpui::i18n`] for the full
//! contract: [`tr`] returns the installed override for a key, or the
//! widget's built-in default when none is set.
//!
//! [`tr`]: gpui::i18n::tr
pub use gpui::i18n::*;
#[cfg(test)]
mod tests {
use super::*;
/// The hook is a thin re-export: installing a table through the widget
/// path is visible to `gpui::i18n::tr` and vice versa.
#[test]
fn widget_path_shares_the_gpui_table() {
clear_table();
assert_eq!(tr("viewer.safe_frames", "安全框"), "安全框");
let mut table = StringTable::new();
table.insert("viewer.safe_frames".into(), "Safe Frames".into());
set_table(table);
assert_eq!(gpui::i18n::tr("viewer.safe_frames", "安全框"), "Safe Frames");
clear_table();
}
}
+1
View File
@@ -23,6 +23,7 @@ pub mod color;
pub mod combo_box;
pub mod curve_editor;
pub mod dialog;
pub mod i18n;
pub mod keyable;
pub mod menu;
pub mod project_explorer;
+4 -4
View File
@@ -221,7 +221,7 @@ impl<C: PlaybackClock> Render for ViewerWidget<C> {
.items_center()
.justify_center()
.text_color(colors.disabled)
.child("No frame source"),
.child(crate::i18n::tr("viewer.no_frame_source", "No frame source")),
);
}
@@ -326,7 +326,7 @@ impl<C: PlaybackClock> Render for ViewerWidget<C> {
)
.child(button(
"gpui-widgets-viewer-safe",
"安全框",
crate::i18n::tr("viewer.safe_frames", "安全框"),
cx.listener(|this, _event: &ClickEvent, _window, cx| {
this.show_safe_frames = !this.show_safe_frames;
this.emit(
@@ -337,7 +337,7 @@ impl<C: PlaybackClock> Render for ViewerWidget<C> {
))
.child(button(
"gpui-widgets-viewer-zoom",
"缩放",
crate::i18n::tr("viewer.zoom", "缩放"),
cx.listener(|this, _event: &ClickEvent, _window, cx| {
this.zoom = !this.zoom;
this.emit(ViewerEvent::ToggleZoomRequested { control: this.control }, cx);
@@ -351,7 +351,7 @@ impl<C: PlaybackClock> Render for ViewerWidget<C> {
/// A small labeled button.
fn button(
id: &'static str,
label: &'static str,
label: impl IntoElement,
on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
) -> impl IntoElement {
div()