Match split pane chrome to design
Each split pane now opens with the design's titlebar layout: an accent status dot, a lock/globe glyph keyed off the URL scheme, the host in ink-1 + the path in ink-4, and a close action on the right that selects and closes the pane's tab. The body sits below a 1px hairline on the panel cream so the panes feel like miniature browser windows instead of borderless cards. The pane click target stays on the outer container so clicking anywhere in a pane focuses it without intercepting the close glyph.
This commit is contained in:
@@ -183,8 +183,12 @@ impl ElyShell {
|
||||
) -> AnyElement {
|
||||
let active = tab.id() == &snapshot.active_tab_id;
|
||||
let tab_id = tab.id().clone();
|
||||
let close_tab_id = tab.id().clone();
|
||||
let border = if active { colors::ACCENT } else { colors::HAIRLINE_STRONG };
|
||||
let title_color = if active { colors::INK } else { colors::INK_2 };
|
||||
let host = pane_host_label(tab);
|
||||
let path = pane_path_label(tab);
|
||||
let secure = pane_url_is_secure(tab);
|
||||
let dot_color = if active { colors::ACCENT } else { colors::INK_4 };
|
||||
|
||||
div()
|
||||
.id(SharedString::from(format!("split-pane-{}", tab.id().as_str())))
|
||||
@@ -197,38 +201,14 @@ impl ElyShell {
|
||||
.rounded(px(10.0))
|
||||
.border_1()
|
||||
.border_color(rgb(border))
|
||||
.bg(rgba(colors::GLASS_2))
|
||||
.bg(rgb(0xffffff))
|
||||
.cursor_pointer()
|
||||
.hover(|style| style.bg(rgba(colors::GLASS_3)))
|
||||
.hover(|style| style.opacity(0.98))
|
||||
.active(|style| style.opacity(0.92))
|
||||
.on_click(cx.listener(move |shell, _, window, cx| {
|
||||
shell.select_tab(&tab_id, window, cx);
|
||||
}))
|
||||
.child(
|
||||
div()
|
||||
.h(px(30.0))
|
||||
.px(px(10.0))
|
||||
.gap(px(6.0))
|
||||
.flex()
|
||||
.items_center()
|
||||
.flex_shrink_0()
|
||||
.child(
|
||||
div()
|
||||
.size(px(6.0))
|
||||
.rounded_full()
|
||||
.bg(rgb(if active { colors::ACCENT } else { colors::INK_4 })),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.min_w_0()
|
||||
.truncate()
|
||||
.text_size(px(12.0))
|
||||
.font_weight(gpui::FontWeight(500.0))
|
||||
.text_color(rgb(title_color))
|
||||
.child(tab.title().to_string()),
|
||||
),
|
||||
)
|
||||
.child(self.render_split_pane_header(host, path, secure, dot_color, close_tab_id, cx))
|
||||
.child(div().flex_1().min_h_0().overflow_hidden().child(if compact_canvas {
|
||||
render_compact_split_canvas(tab)
|
||||
} else {
|
||||
@@ -237,6 +217,74 @@ impl ElyShell {
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_split_pane_header(
|
||||
&mut self,
|
||||
host: String,
|
||||
path: String,
|
||||
secure: bool,
|
||||
dot_color: u32,
|
||||
close_tab_id: ely_domain::TabId,
|
||||
cx: &mut Context<Self>,
|
||||
) -> AnyElement {
|
||||
let lock_or_globe = if secure { IconName::Search } else { IconName::Globe };
|
||||
|
||||
div()
|
||||
.h(px(30.0))
|
||||
.px(px(10.0))
|
||||
.gap(px(8.0))
|
||||
.flex()
|
||||
.items_center()
|
||||
.flex_shrink_0()
|
||||
.border_b_1()
|
||||
.border_color(rgb(colors::HAIRLINE))
|
||||
.bg(rgb(0xf6f4ef))
|
||||
.child(
|
||||
div()
|
||||
.size(px(8.0))
|
||||
.rounded_full()
|
||||
.bg(rgb(dot_color)),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_color(rgb(colors::INK_3))
|
||||
.text_size(px(11.0))
|
||||
.child(lock_or_globe),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(11.0))
|
||||
.font_weight(gpui::FontWeight(500.0))
|
||||
.text_color(rgb(colors::INK))
|
||||
.child(host),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.min_w_0()
|
||||
.truncate()
|
||||
.text_size(px(11.0))
|
||||
.text_color(rgb(colors::INK_4))
|
||||
.child(path),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.id(SharedString::from(format!(
|
||||
"split-pane-close-{}",
|
||||
close_tab_id.as_str()
|
||||
)))
|
||||
.text_color(rgb(colors::INK_4))
|
||||
.text_size(px(11.0))
|
||||
.cursor_pointer()
|
||||
.hover(|style| style.text_color(rgb(colors::INK)))
|
||||
.on_click(cx.listener(move |shell, _, window, cx| {
|
||||
shell.select_tab(&close_tab_id, window, cx);
|
||||
shell.close_active_tab(window, cx);
|
||||
}))
|
||||
.child(IconName::Close),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_split_controls(
|
||||
&mut self,
|
||||
active_axis: &SplitAxis,
|
||||
@@ -480,3 +528,24 @@ fn render_compact_split_canvas(tab: &BrowserTab) -> AnyElement {
|
||||
fn split_canvas_status(tab: &BrowserTab) -> String {
|
||||
if tab.url().as_str() == "ely://new-tab" { "Ready".to_string() } else { tab.display_url() }
|
||||
}
|
||||
|
||||
fn pane_host_label(tab: &BrowserTab) -> String {
|
||||
tab.url()
|
||||
.host()
|
||||
.map(|host| host.to_string())
|
||||
.unwrap_or_else(|| tab.title().to_string())
|
||||
}
|
||||
|
||||
fn pane_path_label(tab: &BrowserTab) -> String {
|
||||
let url = tab.url().as_str();
|
||||
let path_start = url.find("://").map(|prefix| prefix + 3).unwrap_or(0);
|
||||
let from_path = &url[path_start..];
|
||||
match from_path.find('/') {
|
||||
Some(slash) => from_path[slash..].to_string(),
|
||||
None => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn pane_url_is_secure(tab: &BrowserTab) -> bool {
|
||||
tab.url().as_str().starts_with("https://") || tab.url().as_str().starts_with("ely://")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user