mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 00:02:23 +00:00
Merge pull request #188 from l0ng-ai/fix/windows-detail-panel-top
fix(right-panel): put the window controls back in the window's corner
This commit is contained in:
+101
-60
@@ -166,6 +166,12 @@ pub(crate) fn tile_trailing_inset_sm() -> f32 {
|
||||
/// here, not at the window edge.
|
||||
pub(crate) const TITLE_BAR_LEAD: f32 = if cfg!(target_os = "macos") { 80. } else { 12. };
|
||||
|
||||
/// What the bar reserves at its *trailing* edge for the window controls: three
|
||||
/// 34px tiles (─ ▢ ✕) off macOS, nothing on macOS (the traffic lights are on the
|
||||
/// left, and `TITLE_BAR_LEAD` covers them). Anything in the bar that has to line
|
||||
/// up with a column below it measures from the window edge minus this.
|
||||
pub(crate) const WINDOW_CONTROLS_W: f32 = if cfg!(target_os = "macos") { 0. } else { 102. };
|
||||
|
||||
/// Left offset for the tile group that sits beside the window controls.
|
||||
///
|
||||
/// On macOS the thing that can collide with the traffic lights is the tile's
|
||||
@@ -5165,68 +5171,103 @@ impl Render for Tty7App {
|
||||
pair.into_iter().filter_map(|(_, el)| el).collect()
|
||||
};
|
||||
|
||||
// The two layouts. Horizontal: a column of [title bar / body].
|
||||
// Vertical (default): the rail is a full-height *left column* that reaches the very
|
||||
// top of the window — the traffic lights sit on its surface — with the
|
||||
// title strip and terminal stacked in the right column. That way the rail
|
||||
// surface has no seam with the title bar and reads as one continuous
|
||||
// panel.
|
||||
// The right detail panel is a full-height column, not a box under the title
|
||||
// bar: it carries its own title-bar-height top zone (tab row + the window's
|
||||
// corner chrome) exactly like the rail does on the left, so its surface
|
||||
// runs unbroken from the very top of the window. Anything less leaves a
|
||||
// horizontal seam where the panel's grey starts under the terminal's bar.
|
||||
// The layout. The rail (vertical mode) is a full-height *left column* that
|
||||
// reaches the very top of the window — the traffic lights sit on its
|
||||
// surface — with the title strip and terminal stacked in the right column.
|
||||
// That way the rail surface has no seam with the title bar and reads as one
|
||||
// continuous panel.
|
||||
//
|
||||
// The right detail panel does the same on macOS: a full-height column
|
||||
// carrying its own title-bar-height top zone (tab row + the window's corner
|
||||
// chrome), so its surface runs unbroken from the very top of the window.
|
||||
//
|
||||
// Off macOS it can't. The window controls (─ ▢ ✕) are laid out by the title
|
||||
// bar itself, at *its* right end, so a full-height panel beside the bar
|
||||
// strands them mid-window with the panel's grey to their right. There the
|
||||
// bar spans the panel too — reaching the real top-right corner, where
|
||||
// Windows and Linux users expect the controls — and the panel hangs below
|
||||
// it, VS Code style. The panel's tab row then sits on the panel (no longer
|
||||
// in the caption row), and the corner chrome stays in the strip.
|
||||
let right_panel = self.render_right_panel(window, cx);
|
||||
let main_layout = match sidebar {
|
||||
Some(sidebar) => div()
|
||||
.flex_1()
|
||||
.min_h_0()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.child(sidebar)
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
// Anchor for the code overlay: it fills this column
|
||||
// (title strip + body) — and, since the panel is a sibling
|
||||
// rather than a child, stops short of the panel for free.
|
||||
.relative()
|
||||
.child(title_bar)
|
||||
.child(body_area)
|
||||
.children(overlays),
|
||||
)
|
||||
.when_some(right_panel, |this, panel| this.child(panel))
|
||||
.into_any_element(),
|
||||
// Horizontal-tabs mode has no rail, but the panel is still a column
|
||||
// beside the stacked [title bar / body], for the same reason: it has to
|
||||
// own its own top zone to read as one surface.
|
||||
None => div()
|
||||
.flex_1()
|
||||
.min_h_0()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.relative()
|
||||
.child(title_bar)
|
||||
.child(body_area)
|
||||
// Both overlays cover the whole window face here (their
|
||||
// content pads down past the traffic lights — see
|
||||
// `render_code_overlay` and `diff_header`).
|
||||
.children(overlays),
|
||||
)
|
||||
.when_some(right_panel, |this, panel| this.child(panel))
|
||||
.into_any_element(),
|
||||
let panel_below_title_bar = right_panel.is_some() && !cfg!(target_os = "macos");
|
||||
// Which host the bar goes to: the terminal column's first child, or the
|
||||
// spanning row above [terminal | panel].
|
||||
let (column_title_bar, spanning_title_bar) = if panel_below_title_bar {
|
||||
(None, Some(title_bar))
|
||||
} else {
|
||||
(Some(title_bar), None)
|
||||
};
|
||||
// The terminal column, and the anchor for both overlays: they fill it —
|
||||
// and, since the panel is a sibling rather than a child, stop short of the
|
||||
// panel for free. With the bar spanning above, they stop short of it too,
|
||||
// which keeps the native controls clickable while an overlay is open and
|
||||
// lines the overlay's own header row up with the panel's tab row.
|
||||
let terminal_column = div()
|
||||
.flex_1()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.relative()
|
||||
.when_some(column_title_bar, |this, bar| this.child(bar))
|
||||
.child(body_area)
|
||||
.children(overlays);
|
||||
let panel_row = div()
|
||||
.flex_1()
|
||||
.min_h_0()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.child(terminal_column)
|
||||
.when_some(right_panel, |this, panel| this.child(panel));
|
||||
let main_layout = div()
|
||||
.flex_1()
|
||||
.min_h_0()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.when_some(sidebar, |this, sidebar| this.child(sidebar))
|
||||
.child(match spanning_title_bar {
|
||||
Some(bar) => div()
|
||||
.flex_1()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.child(
|
||||
// The bar's own band over the panel, painted in the panel's
|
||||
// surface so the column still reads as one continuous
|
||||
// sidebar from the very top of the window — the rail's
|
||||
// trick, kept now that the tab row moved off the caption
|
||||
// line. Without it the panel's grey started 40px down and
|
||||
// the corner tore into two colours.
|
||||
//
|
||||
// A sibling *under* the transparent bar rather than padding
|
||||
// inside it: the ─ ▢ ✕ group is the bar's own last child, so
|
||||
// nothing laid out in the bar can get behind the controls,
|
||||
// and only a layer below can carry a surface under them.
|
||||
// Same width and left border as the panel, both read from
|
||||
// `right_panel_px`, so the edge stays in register through a
|
||||
// resize drag.
|
||||
div()
|
||||
.relative()
|
||||
.flex_none()
|
||||
.child(
|
||||
div()
|
||||
.absolute()
|
||||
.top_0()
|
||||
.bottom_0()
|
||||
.right_0()
|
||||
.w(px(self.right_panel_px(window, cx)))
|
||||
.bg(cx.theme().sidebar)
|
||||
.border_l_1()
|
||||
.border_color(cx.theme().sidebar_border),
|
||||
)
|
||||
.child(bar),
|
||||
)
|
||||
.child(panel_row)
|
||||
.into_any_element(),
|
||||
None => panel_row.into_any_element(),
|
||||
})
|
||||
.into_any_element();
|
||||
|
||||
// The real window background paint: gradient-aware and opacity-carrying
|
||||
// (see `theme::window_background`), plus the theme's optional background
|
||||
|
||||
+38
-2
@@ -1247,9 +1247,18 @@ impl Tty7App {
|
||||
let app = cx.entity().downgrade();
|
||||
let path = path.clone();
|
||||
let is_root = row.is_root;
|
||||
let show_hidden = self.file_tree.show_hidden;
|
||||
move |menu, _window, cx| {
|
||||
let danger = cx.theme().danger;
|
||||
Self::tree_row_context_menu(menu, &path, is_dir, is_root, danger, &app)
|
||||
Self::tree_row_context_menu(
|
||||
menu,
|
||||
&path,
|
||||
is_dir,
|
||||
is_root,
|
||||
show_hidden,
|
||||
danger,
|
||||
&app,
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1279,12 +1288,16 @@ impl Tty7App {
|
||||
out
|
||||
}
|
||||
|
||||
/// The per-row right-click menu, mirroring Warp's Project Explorer set.
|
||||
/// The per-row right-click menu, mirroring Warp's Project Explorer set, plus
|
||||
/// the tree's one view option (dotfiles) — which lives here rather than as a
|
||||
/// header button: it is set once and then forgotten, and a tile in the header
|
||||
/// spends the panel's scarcest row on it forever.
|
||||
fn tree_row_context_menu(
|
||||
menu: PopupMenu,
|
||||
path: &Path,
|
||||
is_dir: bool,
|
||||
is_root: bool,
|
||||
show_hidden: bool,
|
||||
danger: gpui::Hsla,
|
||||
app: &gpui::WeakEntity<Self>,
|
||||
) -> PopupMenu {
|
||||
@@ -1379,6 +1392,8 @@ impl Tty7App {
|
||||
}
|
||||
}));
|
||||
|
||||
menu = menu.separator().item(dotfiles_menu_item(show_hidden, app));
|
||||
|
||||
if !is_root {
|
||||
menu = menu.separator().item(
|
||||
PopupMenuItem::element(move |_window, _cx| {
|
||||
@@ -1397,6 +1412,27 @@ impl Tty7App {
|
||||
}
|
||||
}
|
||||
|
||||
/// The tree's dotfile switch as a row of the tree's existing right-click menu.
|
||||
///
|
||||
/// The label states what the click will do rather than checking off the current
|
||||
/// state: a single checked item makes `PopupMenu` reserve a left icon gutter on
|
||||
/// *every* row in the menu (see `tab_strip::window_chrome`), and the menu has a
|
||||
/// dozen rows with nothing to put in one.
|
||||
fn dotfiles_menu_item(show_hidden: bool, app: &gpui::WeakEntity<Tty7App>) -> PopupMenuItem {
|
||||
let app = app.clone();
|
||||
PopupMenuItem::new(if show_hidden {
|
||||
"Hide Dotfiles"
|
||||
} else {
|
||||
"Show Dotfiles"
|
||||
})
|
||||
.on_click(move |_, _window, cx| {
|
||||
let _ = app.update(cx, |this, cx| {
|
||||
this.file_tree.show_hidden = !this.file_tree.show_hidden;
|
||||
cx.notify();
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/// The little drag ghost shown while a row is dragged toward a terminal.
|
||||
struct DragGhost {
|
||||
name: String,
|
||||
|
||||
+76
-55
@@ -2,12 +2,14 @@
|
||||
//! rather than what it's printing — session facts, its working-tree diff, and
|
||||
//! its file tree.
|
||||
//!
|
||||
//! It splits across two hosts on purpose. The **tab row lives in the title bar**
|
||||
//! (built in [`tab_strip`](crate::ui::tab_strip)), so the panel's controls sit on
|
||||
//! the same line as the window's own chrome instead of stacking a second 40px bar
|
||||
//! under it; the **body** is this module's column inside `body_area`. The two are
|
||||
//! kept in register by both measuring from `Config::right_panel_width`, so the
|
||||
//! tabs sit exactly over the content they switch.
|
||||
//! Its tab row has two homes. On macOS it is the panel's own title-bar-height top
|
||||
//! zone, level with the window's chrome, so the column runs unbroken from the top
|
||||
//! of the window. Off macOS the title bar has to span the panel (the window
|
||||
//! controls live at its right end), so the row drops to the panel's second line —
|
||||
//! Cursor-style — while the caption row above is painted in the panel's surface
|
||||
//! so the column still reads as one colour.
|
||||
//! Either way the tiles themselves are built in
|
||||
//! [`tab_strip`](crate::ui::tab_strip), beside the rest of the window's tiles.
|
||||
//!
|
||||
//! No new source of truth: Info reads the same `TerminalView`/`Tab` accessors the
|
||||
//! sidebar row does, Changes probes the same `git_diff` the diff overlay does, and
|
||||
@@ -182,14 +184,15 @@ impl Tty7App {
|
||||
// bolted under the title bar: its surface runs the full height of
|
||||
// the window, and the tab row sits *on* it rather than on the
|
||||
// terminal's bar above a seam.
|
||||
.child({
|
||||
// The top zone sits level with the real `TitleBar`, but the
|
||||
// bar only spans the terminal column — so, exactly like the
|
||||
// rail's top strip (`tab_sidebar`), make this one act like the
|
||||
// title bar it aligns with: drag to move, double-click to zoom.
|
||||
// A press arms a flag and the first *move* starts the window
|
||||
// move, so a plain click on a tab — and a double-click — still
|
||||
// lands intact; the tabs and corner chrome take their own.
|
||||
//
|
||||
// macOS only. Off macOS the bar spans the panel — it has to, or the
|
||||
// window controls end up stranded mid-window (see `app::render`) —
|
||||
// and a row of tiles under that caption row was one chrome row too
|
||||
// many: the panel opened with three stacked headers (caption chrome,
|
||||
// tab tiles, section title) before any content. So there the tiles
|
||||
// move into the section header instead (`panel_title`), which is a
|
||||
// row the panel was drawing anyway.
|
||||
.children(cfg!(target_os = "macos").then(|| {
|
||||
let should_move = Rc::new(Cell::new(false));
|
||||
h_flex()
|
||||
.id("right-panel-titlebar-drag")
|
||||
@@ -204,6 +207,14 @@ impl Tty7App {
|
||||
// down a physical pixel the moment the panel opens.
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().transparent)
|
||||
// The top zone sits level with the real `TitleBar`, but the
|
||||
// bar only spans the terminal column — so, exactly like the
|
||||
// rail's top strip (`tab_sidebar`), make this one act like
|
||||
// the title bar it aligns with: drag to move, double-click
|
||||
// to zoom. A press arms a flag and the first *move* starts
|
||||
// the window move, so a plain click on a tab — and a
|
||||
// double-click — still lands intact; the tabs and corner
|
||||
// chrome take their own.
|
||||
.window_control_area(WindowControlArea::Drag)
|
||||
.on_mouse_down(MouseButton::Left, {
|
||||
let should_move = should_move.clone();
|
||||
@@ -227,7 +238,7 @@ impl Tty7App {
|
||||
// The panel is what reaches the window's right edge while
|
||||
// it's open, so it carries the corner chrome.
|
||||
.child(self.window_chrome(window, cx))
|
||||
})
|
||||
}))
|
||||
.child(body)
|
||||
// The transfers footer is a sibling of the body, not part of any
|
||||
// tab: an SFTP transfer belongs to the pane, so reading Info or
|
||||
@@ -333,14 +344,20 @@ impl Tty7App {
|
||||
(backing, handle)
|
||||
}
|
||||
|
||||
/// A section label inside the panel body — the small caps line that names
|
||||
/// what the icon-only tab row can't. `trailing` carries a tab's own controls
|
||||
/// where it has any, so they sit on the label's line rather than earning a
|
||||
/// second header row.
|
||||
/// A tab's header: the name in a weightier small-caps than the old faint
|
||||
/// label, plus an optional live count trailing it (files, commands, changed
|
||||
/// files) so the header states scale at a glance, and an optional control on
|
||||
/// the right. The count is the quiet mono tally the sidebar group headers use.
|
||||
/// `trailing` carries a tab's own controls where it has any, so they sit on
|
||||
/// the label's line rather than earning a second header row.
|
||||
///
|
||||
/// Off macOS this row is also the panel's tab switcher: the four tiles ride
|
||||
/// at its trailing edge, and the row takes the full title-bar height with a
|
||||
/// hairline under it. The panel there hangs below a caption row that already
|
||||
/// carries chrome (see `render_right_panel`), and a tile row of its own on top
|
||||
/// of this one meant three stacked headers before a single line of content —
|
||||
/// so the two that were saying "this is a header" merge into one that also
|
||||
/// says which tab you are on.
|
||||
pub(crate) fn panel_title(
|
||||
&self,
|
||||
text: &str,
|
||||
@@ -348,23 +365,37 @@ impl Tty7App {
|
||||
trailing: Option<AnyElement>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> AnyElement {
|
||||
let tabs = (!cfg!(target_os = "macos")).then(|| self.right_panel_tabs(cx));
|
||||
let has_trailing = trailing.is_some();
|
||||
h_flex()
|
||||
.flex_none()
|
||||
.h(px(32.))
|
||||
// Tall enough to seat the chrome-scale tiles when it carries them;
|
||||
// otherwise the compact label line it has always been.
|
||||
.h(px(if tabs.is_some() {
|
||||
crate::ui::app::TITLE_BAR_HEIGHT
|
||||
} else {
|
||||
32.
|
||||
}))
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.pl(px(CONTENT_INSET))
|
||||
// Trailing tiles align on the glyph like every other control in the
|
||||
// window; a label-only header just takes the plain inset. `_SM`
|
||||
// because what hangs here is a body-scale tile, whose glyph sits a
|
||||
// different distance inside its box than the chrome's does.
|
||||
.pr(px(if trailing.is_some() {
|
||||
tile_trailing_inset_sm()
|
||||
} else {
|
||||
CONTENT_INSET
|
||||
// window; a label-only header just takes the plain inset. `_SM` for a
|
||||
// tab's own control, whose glyph sits a different distance inside its
|
||||
// box than the chrome-scale tab tiles do.
|
||||
.pr(px(match (&tabs, has_trailing) {
|
||||
(Some(_), _) => tile_trailing_inset(),
|
||||
(None, true) => tile_trailing_inset_sm(),
|
||||
(None, false) => CONTENT_INSET,
|
||||
}))
|
||||
// The line that separates the header from the tab's content. Only
|
||||
// where the header is the switcher: a label alone doesn't need ruling
|
||||
// off from the band it introduces.
|
||||
.when(tabs.is_some(), |this| {
|
||||
this.border_b_1().border_color(cx.theme().sidebar_border)
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_shrink_0()
|
||||
.items_baseline()
|
||||
.gap(px(7.))
|
||||
.child(
|
||||
@@ -384,35 +415,23 @@ impl Tty7App {
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(div().flex_1().min_w_0())
|
||||
.when_some(trailing, |this, t| this.child(t))
|
||||
.when_some(tabs, |this, tiles| {
|
||||
this.child(
|
||||
h_flex()
|
||||
.flex_shrink_0()
|
||||
.items_center()
|
||||
.gap(px(2.))
|
||||
// Clear of a tab's own control where there is one; flush
|
||||
// against the label's spring where there isn't.
|
||||
.when(has_trailing, |this| this.ml(px(6.)))
|
||||
.children(tiles),
|
||||
)
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
/// The Files header's one control. No refresh button: the tree runs a
|
||||
/// recursive filesystem watcher over its roots and invalidates its own caches,
|
||||
/// so a manual refresh is a button that does what already happened.
|
||||
fn files_controls(&self, cx: &mut Context<Self>) -> AnyElement {
|
||||
let show_hidden = self.file_tree.show_hidden;
|
||||
crate::ui::tab_strip::chrome_tile_sized(
|
||||
Button::new("panel-tree-hidden").icon(Icon::new(IconName::Eye)),
|
||||
TILE_SIZE_SM,
|
||||
TILE_GLYPH_SM,
|
||||
show_hidden,
|
||||
cx,
|
||||
)
|
||||
.rounded_md()
|
||||
.tooltip(if show_hidden {
|
||||
"Hide dotfiles"
|
||||
} else {
|
||||
"Show dotfiles"
|
||||
})
|
||||
.on_click(cx.listener(|this, _, _w, cx| {
|
||||
this.file_tree.show_hidden = !this.file_tree.show_hidden;
|
||||
cx.notify();
|
||||
}))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
/// A tab's filter box — the same borderless magnifier + input the tab rail
|
||||
/// uses, so everything in the window searches the same way. Sits under the
|
||||
/// header rather than in it: it's a full-width control, not a trailing tile.
|
||||
@@ -1290,8 +1309,10 @@ impl Tty7App {
|
||||
return self.render_panel_sftp(host.unwrap_or_default(), window, cx);
|
||||
}
|
||||
|
||||
let controls = self.files_controls(cx);
|
||||
let title = self.panel_title("Files", None, Some(controls), cx);
|
||||
// No header control: the tree's one view option (dotfiles) is a
|
||||
// right-click away in the tree itself (`file_tree::dotfiles_menu_item`),
|
||||
// which is where you are when you want it.
|
||||
let title = self.panel_title("Files", None, None, cx);
|
||||
let search = self.panel_search(&self.file_search.clone(), cx);
|
||||
let rows = self.render_file_tree_rows(window, cx);
|
||||
v_flex()
|
||||
|
||||
+45
-9
@@ -631,14 +631,23 @@ impl Tty7App {
|
||||
.when(!cfg!(target_os = "macos"), |this| this.pr_1())
|
||||
.child(
|
||||
div().occlude().flex_shrink_0().child(
|
||||
// Never drawn selected, exactly like the rail's own toggle
|
||||
// (`tab_sidebar`): the panel being open is already on screen —
|
||||
// it *is* the panel — so a lit capsule only restates it, and
|
||||
// the two panel toggles would disagree about what a chrome tile
|
||||
// means. The state lives in the tooltip's verb instead.
|
||||
chrome_tile(
|
||||
Button::new("titlebar-right-panel")
|
||||
.icon(Icon::empty().path("icons/panel-right.svg")),
|
||||
panel_open,
|
||||
false,
|
||||
cx,
|
||||
)
|
||||
.rounded_lg()
|
||||
.tooltip("Detail Panel")
|
||||
.tooltip(if panel_open {
|
||||
"Hide Detail Panel"
|
||||
} else {
|
||||
"Show Detail Panel"
|
||||
})
|
||||
.on_click(cx.listener(|this, _, _window, cx| {
|
||||
this.toggle_right_panel(cx);
|
||||
})),
|
||||
@@ -1107,12 +1116,21 @@ impl Tty7App {
|
||||
} else {
|
||||
(window.viewport_size().width - px(114.)).max(px(140.))
|
||||
};
|
||||
// Off macOS the bar spans the detail panel (see `app::render`). The corner
|
||||
// chrome then sits *over the panel's surface*, so it stops hugging the
|
||||
// window controls and aligns with the column it is painted on: a block as
|
||||
// wide as the slice of the panel the bar can reach — panel width less the
|
||||
// control group, less the panel's own left border — with the tiles packed
|
||||
// at its leading edge, on the same inset as everything else in the panel.
|
||||
let chrome_band_w = (!cfg!(target_os = "macos") && self.right_panel_open(cx)).then(|| {
|
||||
(self.right_panel_px(window, cx) - crate::ui::app::WINDOW_CONTROLS_W - 1.).max(0.)
|
||||
});
|
||||
// The "+" and the right-edge overflow "⋯" (30px each), their surrounding
|
||||
// gaps, and the strip's own left/right padding all live *outside* the
|
||||
// clipped chip row — reserve that whole footprint here so the fixed chrome
|
||||
// never overflows the strip box (which would eat the "⋯"'s right inset and
|
||||
// shove it into the window corner) and cap the chip row at the remainder.
|
||||
let chips_avail = (strip_w - px(100.)).max(px(80.));
|
||||
let chips_avail = (strip_w - px(100.) - px(chrome_band_w.unwrap_or(0.))).max(px(80.));
|
||||
// Only the chip row clips; a crowded row shrinks its chips (down to their
|
||||
// `min_w`) and truncates their labels rather than pushing the "+" away.
|
||||
let mut chips = h_flex()
|
||||
@@ -1532,11 +1550,14 @@ impl Tty7App {
|
||||
});
|
||||
|
||||
let panel_open = self.right_panel_open(cx);
|
||||
// The window's right-corner chrome. When the panel is open it lives on the
|
||||
// *panel's* top zone (the panel is what reaches the window's right edge
|
||||
// then) exactly like the rail's controls live on the rail; the strip only
|
||||
// carries it while the panel is closed.
|
||||
let right_chrome = (!panel_open).then(|| self.window_chrome(window, cx));
|
||||
// The window's right-corner chrome. On macOS, when the panel is open it
|
||||
// lives on the *panel's* top zone (the panel is what reaches the window's
|
||||
// right edge then) exactly like the rail's controls live on the rail; the
|
||||
// strip only carries it while the panel is closed. Off macOS the bar spans
|
||||
// the panel (the window controls are at its right end — see `app::render`),
|
||||
// so the strip always reaches the right edge and always carries the chrome.
|
||||
let right_chrome =
|
||||
(!panel_open || !cfg!(target_os = "macos")).then(|| self.window_chrome(window, cx));
|
||||
|
||||
// Outer strip: the clipping chip row and the always-visible "+" anchored
|
||||
// left, the overflow "⋯" pushed to the right edge by a flexible spacer.
|
||||
@@ -1567,7 +1588,22 @@ impl Tty7App {
|
||||
// leaving just the "⋯" overflow menu on a thin strip.
|
||||
.when(show_chips, move |this| this.child(add_button))
|
||||
.child(div().flex_1())
|
||||
.when_some(right_chrome, |this, chrome| this.child(chrome))
|
||||
.when_some(right_chrome, |this, chrome| match chrome_band_w {
|
||||
// Over the panel: left-aligned on the panel's content edge, and
|
||||
// wide enough that its own right edge lands where the window
|
||||
// controls start.
|
||||
Some(w) => this.child(
|
||||
h_flex()
|
||||
.flex_none()
|
||||
.w(px(w))
|
||||
.items_center()
|
||||
.pl(px(tile_trailing_inset()))
|
||||
.child(chrome),
|
||||
),
|
||||
// Over the terminal: pinned to the strip's trailing edge, which is
|
||||
// the window's right edge (or the controls' left edge off macOS).
|
||||
None => this.child(chrome),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user