mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 00:02:23 +00:00
fix(settings): stack a row instead of squeezing its label to a letter per line
The control never shrinks and the label column must keep min_w_0 or long
descriptions stop wrapping, so on a narrow pane the label was squeezed to
nothing: at 600px the Terminal page rendered "Program" as a vertical
column of single letters, and the SSH form did the same below about
1000px, which 4275bb1 left open as the real remaining bug.
flex_wrap was the wrong answer — it let the label column size to its own
description, which then ran out past the row on every wide page. Measure
instead: render_settings knows the viewport and which page it is drawing,
so it can say how much width a row will actually get, and the row lays
itself out side by side or stacked from that. Nothing moves above the
breakpoint.
This commit is contained in:
@@ -393,6 +393,11 @@ pub struct Tty7App {
|
||||
pub(crate) editor: crate::ui::code_editor::EditorPanelState,
|
||||
pub(crate) sidebar_width: Rc<Cell<f32>>,
|
||||
pub(crate) sidebar_dragging: Rc<Cell<bool>>,
|
||||
/// How much width a settings row will actually get, measured once per
|
||||
/// render. `settings_row` is called from page builders that never see the
|
||||
/// window, and the answer differs per page — the SSH page spends 500px on
|
||||
/// its two lists first.
|
||||
pub(crate) settings_row_width: Cell<f32>,
|
||||
pub(crate) right_panel_width: Rc<Cell<f32>>,
|
||||
pub(crate) right_panel_dragging: Rc<Cell<bool>>,
|
||||
pub(crate) right_panel_visible: bool,
|
||||
@@ -806,6 +811,7 @@ impl Tty7App {
|
||||
editor,
|
||||
sidebar_width: Rc::new(Cell::new(sidebar_width)),
|
||||
sidebar_dragging: Rc::new(Cell::new(false)),
|
||||
settings_row_width: Cell::new(f32::MAX),
|
||||
right_panel_width: Rc::new(Cell::new(right_panel_width)),
|
||||
right_panel_dragging: Rc::new(Cell::new(false)),
|
||||
right_panel_visible,
|
||||
|
||||
+76
-24
@@ -37,6 +37,29 @@ use crate::ui::presets;
|
||||
use crate::ui::rounding;
|
||||
use crate::ui::rounding::RoundedCorners as _;
|
||||
|
||||
/// The settings nav, the SSH host list, and the padding each page sets — the
|
||||
/// chrome a row has to share the window with.
|
||||
const NAV_W: f32 = 220.;
|
||||
const SSH_LIST_W: f32 = 280.;
|
||||
const SSH_DETAIL_PAD: f32 = 64.;
|
||||
const PAGE_PAD: f32 = 80.;
|
||||
|
||||
/// What a row on this page really has to lay out in.
|
||||
///
|
||||
/// The nav is always in front of it; the SSH page puts its host list there too,
|
||||
/// and only the scrolled pages cap the reading column at 640.
|
||||
fn settings_row_width(section: SettingsSection, viewport: f32) -> f32 {
|
||||
match section {
|
||||
SettingsSection::Ssh => (viewport - NAV_W - SSH_LIST_W - SSH_DETAIL_PAD).max(0.),
|
||||
_ => (viewport - NAV_W - PAGE_PAD).clamp(0., 640.),
|
||||
}
|
||||
}
|
||||
|
||||
/// Width a settings row needs before its label and its control fit side by
|
||||
/// side: a 260px control, the `gap_8` between them, and enough left for a
|
||||
/// description to read as prose rather than as a column of words.
|
||||
const STACK_ROW_BELOW: f32 = 500.;
|
||||
|
||||
fn settings_row_id(label: &str, _desc: &str) -> SharedString {
|
||||
SharedString::from(format!("settings-row-{label}"))
|
||||
}
|
||||
@@ -766,6 +789,9 @@ impl Tty7App {
|
||||
let query = search.read(cx).value().trim().to_lowercase();
|
||||
let show_theme_panel = theme_panel_open && section == SettingsSection::Appearance;
|
||||
|
||||
self.settings_row_width
|
||||
.set(settings_row_width(section, window.viewport_size().width.as_f32()));
|
||||
|
||||
let prof = crate::ui::perf::enabled()
|
||||
.then(|| (std::time::Instant::now(), section.profile_label()));
|
||||
|
||||
@@ -1078,11 +1104,38 @@ impl Tty7App {
|
||||
}
|
||||
None => (false, false),
|
||||
};
|
||||
h_flex()
|
||||
// The control never shrinks, so on a narrow pane it takes the width and
|
||||
// the label column — which must keep `min_w_0` or long descriptions
|
||||
// stop wrapping — is squeezed to a letter per line. Below the width
|
||||
// where both still fit, put the control on its own line instead.
|
||||
// Measured, not `flex_wrap`: wrapping made the label column size to its
|
||||
// description, which then ran out past the row on every wide page.
|
||||
let stacked = self.settings_row_width.get() < STACK_ROW_BELOW;
|
||||
let labels = v_flex()
|
||||
.gap_0p5()
|
||||
.min_w_0()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.font_weight(FontWeight::MEDIUM)
|
||||
.text_color(theme.foreground)
|
||||
.child(label),
|
||||
)
|
||||
.when(!desc.is_empty(), |col| {
|
||||
col.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(theme.muted_foreground)
|
||||
.child(desc),
|
||||
)
|
||||
});
|
||||
div()
|
||||
.id(element_id)
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.gap_8()
|
||||
.flex()
|
||||
.when(stacked, |row| row.flex_col().items_start().gap_2())
|
||||
.when(!stacked, |row| {
|
||||
row.flex_row().items_center().justify_between().gap_8()
|
||||
})
|
||||
.py_2()
|
||||
.px_2p5()
|
||||
.mx_neg_2p5()
|
||||
@@ -1091,26 +1144,7 @@ impl Tty7App {
|
||||
.when(miss, |row| row.opacity(0.45))
|
||||
.hover(|h| h.bg(gpui::rgb(cx.global::<presets::Surfaces>().window.hover)))
|
||||
.on_hover(cx.listener(|_this, _hovered, _window, cx| cx.notify()))
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_0p5()
|
||||
.min_w_0()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.font_weight(FontWeight::MEDIUM)
|
||||
.text_color(theme.foreground)
|
||||
.child(label),
|
||||
)
|
||||
.when(!desc.is_empty(), |col| {
|
||||
col.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(theme.muted_foreground)
|
||||
.child(desc),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(labels)
|
||||
.child(h_flex().flex_shrink_0().child(control))
|
||||
}
|
||||
|
||||
@@ -5448,6 +5482,24 @@ impl Tty7App {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// The row keeps its side-by-side shape while both halves fit, and stacks
|
||||
/// once they do not. The SSH page reaches that point first — it spends 500px
|
||||
/// on two lists before the row gets any.
|
||||
#[test]
|
||||
fn a_row_stacks_once_its_label_and_control_stop_fitting() {
|
||||
use SettingsSection::*;
|
||||
assert!(settings_row_width(Terminal, 1440.) >= STACK_ROW_BELOW);
|
||||
assert!(settings_row_width(Terminal, 900.) >= STACK_ROW_BELOW);
|
||||
assert!(settings_row_width(Terminal, 700.) < STACK_ROW_BELOW);
|
||||
// Capped at the reading column, so a wider window never widens the row.
|
||||
assert_eq!(settings_row_width(Terminal, 4000.), 640.);
|
||||
// SSH crosses over while the window is still wide.
|
||||
assert!(settings_row_width(Ssh, 1440.) >= STACK_ROW_BELOW);
|
||||
assert!(settings_row_width(Ssh, 1000.) < STACK_ROW_BELOW);
|
||||
// And never goes negative on a window narrower than its own chrome.
|
||||
assert_eq!(settings_row_width(Ssh, 100.), 0.);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_row_is_marked_by_its_label_or_by_the_keywords_behind_it() {
|
||||
// Straight label hit.
|
||||
|
||||
Reference in New Issue
Block a user