Skip to content

Commit 1eb83dd

Browse files
authored
fix(client): the category search field is measured, not counted (UI/UX v3 N-6a) (#109)
設定パネルのサイドバー検索フィールドが cell path に残っていた。設定パネル 全体で唯一、ユーザー入力を扱う描画経路。 `truncate_to_width` はピクセル予算を `cell_w` で割って表示列数に直し、 `UnicodeWidthChar` で数えて切る。フィールドはグリッドではなく、テキストは そもそもセルサイズで描かれていないため、日本語で入力した検索文字列は 意図した位置とは違うところで切れていた。 `truncate_run_to_width` + `add_run_verts` に移行し、垂直位置も他のラベルと 同じく実測 line_height で中央揃えにする。ramp step は body(同ファイルの カテゴリラベルと同じ)。 sidebar.rs は既に add_run_verts / truncate_run_to_width / metrics を使って おり、この 1 箇所だけが取り残されていた。 本番から `truncate_to_width` の呼び出しが消えたが、関数自体は残す。N-6b で `wrap_text` も同じ状態になるため、未使用となった 2 つの cell path 系関数の 扱いは N-6c でまとめて判断する。 Spec: docs/plans/2026-08-30-n4-menus-and-dialogs.md §8(settings 残件の①)
1 parent abd8a96 commit 1eb83dd

1 file changed

Lines changed: 44 additions & 8 deletions

File tree

  • nexterm-client-gpu/src/renderer/overlay/settings

nexterm-client-gpu/src/renderer/overlay/settings/sidebar.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ use crate::font::FontManager;
44
use crate::glyph_atlas::{BgVertex, GlyphAtlas, TextVertex};
55
use crate::settings_panel::SettingsPanel;
66
use crate::vertex_util::{
7-
add_icon_verts, add_px_rect, add_run_verts, add_string_verts, icon_size_for_slot,
8-
truncate_run_to_width, truncate_to_width,
7+
add_icon_verts, add_px_rect, add_run_verts, icon_size_for_slot, truncate_run_to_width,
98
};
109
use nexterm_config::SurfaceLevel;
1110

@@ -14,7 +13,7 @@ use nexterm_config::SurfaceLevel;
1413
///
1514
/// Sidebar width is kept as a fixed `cell_w * 18.0` (wide enough to fit the
1615
/// longest translated category names); category labels are still truncated
17-
/// defensively via [`truncate_to_width`] in case a future locale overflows it.
16+
/// defensively via [`truncate_run_to_width`] in case a future locale overflows it.
1817
#[allow(clippy::too_many_arguments)]
1918
pub(in crate::renderer) fn draw_sidebar(
2019
sp: &SettingsPanel,
@@ -120,17 +119,24 @@ pub(in crate::renderer) fn draw_sidebar(
120119
tokens.text_on(SurfaceLevel::S2).primary,
121120
)
122121
};
122+
// UI/UX v3 N-6a. This was the last cell-path text in the sidebar, and the
123+
// only one on a live input path: `truncate_to_width` divides by `cell_w`
124+
// and then counts display columns, so a query typed in Japanese was cut at
125+
// the wrong character — the field is not a grid, and the text was never
126+
// drawn at the cell size to begin with. Measured now, like every other
127+
// label in this file.
128+
let search_style = metrics.type_ramp.body;
123129
let search_max_w = sidebar_w - search_pad * 2.0 - cell_w * 0.3;
124-
let search_text = truncate_to_width(&search_text, search_max_w, cell_w);
125-
add_string_verts(
130+
let search_text = truncate_run_to_width(&search_text, &search_style, search_max_w, font);
131+
let (_size, search_line_h, _bold) = font.chrome_metrics(&search_style);
132+
add_run_verts(
126133
&search_text,
134+
&search_style,
127135
px + search_pad + cell_w * 0.3,
128-
search_box_y + cell_h * 0.05,
136+
search_box_y + (cell_h - search_line_h) * 0.5,
129137
search_fg,
130-
false,
131138
sw,
132139
sh,
133-
cell_w,
134140
font,
135141
atlas,
136142
queue,
@@ -251,3 +257,33 @@ pub(in crate::renderer) fn draw_sidebar(
251257
);
252258
}
253259
}
260+
261+
#[cfg(test)]
262+
mod tests {
263+
/// UI/UX v3 N-6a: the sidebar draws no text on the cell path.
264+
///
265+
/// The search field was the last one, and the only one in the settings
266+
/// panel fed by user input. `truncate_to_width` divides a pixel budget by
267+
/// `cell_w` and then counts display columns, so a Japanese query was cut
268+
/// at the wrong character — the field is not a grid and its text was never
269+
/// drawn at the cell size.
270+
///
271+
/// Scoped to this file. Other settings modules still draw prose on the
272+
/// cell path; N-6b and N-6c take those.
273+
#[test]
274+
fn the_sidebar_draws_no_text_on_the_cell_path() {
275+
let src = include_str!("sidebar.rs");
276+
let body = src
277+
.split("#[cfg(test)]")
278+
.next()
279+
.expect("the file has a body before its tests");
280+
assert!(
281+
!body.contains("add_string_verts"),
282+
"the sidebar draws text on the cell path again"
283+
);
284+
assert!(
285+
!body.contains("truncate_to_width("),
286+
"the sidebar truncates by cell count again; it measures now"
287+
);
288+
}
289+
}

0 commit comments

Comments
 (0)