Add split layout controls
This commit is contained in:
@@ -6,7 +6,7 @@ use gpui::{
|
|||||||
StatefulInteractiveElement, Styled, Window, div, px, rgb,
|
StatefulInteractiveElement, Styled, Window, div, px, rgb,
|
||||||
};
|
};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
IconName, Sizable, StyledExt,
|
IconName, Selectable, Sizable, StyledExt,
|
||||||
button::{Button, ButtonVariants},
|
button::{Button, ButtonVariants},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -101,6 +101,14 @@ impl ElyShell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_active_split_axis(&mut self, axis: SplitAxis, cx: &mut Context<Self>) {
|
||||||
|
if let ShellState::Ready(core) = &mut self.state
|
||||||
|
&& core.set_active_split_axis(axis).is_ok_and(|split_id| split_id.is_some())
|
||||||
|
{
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_split_canvas(
|
fn render_split_canvas(
|
||||||
&mut self,
|
&mut self,
|
||||||
snapshot: &BrowserSnapshot,
|
snapshot: &BrowserSnapshot,
|
||||||
@@ -130,23 +138,23 @@ impl ElyShell {
|
|||||||
.bg(rgb(colors::CANVAS));
|
.bg(rgb(colors::CANVAS));
|
||||||
let pane_area = div().flex_1().min_h_0().gap_3().overflow_hidden();
|
let pane_area = div().flex_1().min_h_0().gap_3().overflow_hidden();
|
||||||
|
|
||||||
|
let compact_canvas = layout.axis() == &SplitAxis::Vertical && layout.pane_count() >= 4;
|
||||||
let panes = match layout.axis() {
|
let panes = match layout.axis() {
|
||||||
SplitAxis::Horizontal => pane_area.flex().children(
|
SplitAxis::Horizontal => pane_area.flex().children(
|
||||||
panes
|
panes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, tab)| self.render_split_pane(index, tab, snapshot, cx)),
|
.map(|(index, tab)| self.render_split_pane(index, tab, snapshot, false, cx)),
|
||||||
),
|
|
||||||
SplitAxis::Vertical => pane_area.flex().flex_col().children(
|
|
||||||
panes
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(index, tab)| self.render_split_pane(index, tab, snapshot, cx)),
|
|
||||||
),
|
),
|
||||||
|
SplitAxis::Vertical => {
|
||||||
|
pane_area.flex().flex_col().children(panes.into_iter().enumerate().map(
|
||||||
|
|(index, tab)| self.render_split_pane(index, tab, snapshot, compact_canvas, cx),
|
||||||
|
))
|
||||||
|
}
|
||||||
SplitAxis::Grid => self.render_split_grid(pane_area, panes, snapshot, cx),
|
SplitAxis::Grid => self.render_split_grid(pane_area, panes, snapshot, cx),
|
||||||
};
|
};
|
||||||
|
|
||||||
body.child(panes).child(self.render_split_controls(cx)).into_any_element()
|
body.child(panes).child(self.render_split_controls(layout.axis(), cx)).into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_split_grid(
|
fn render_split_grid(
|
||||||
@@ -160,7 +168,7 @@ impl ElyShell {
|
|||||||
div().flex().flex_1().min_h(px(180.0)).gap_3().children(row.iter().enumerate().map(
|
div().flex().flex_1().min_h(px(180.0)).gap_3().children(row.iter().enumerate().map(
|
||||||
|(column_index, tab)| {
|
|(column_index, tab)| {
|
||||||
let pane_index = row_index * 2 + column_index;
|
let pane_index = row_index * 2 + column_index;
|
||||||
self.render_split_pane(pane_index, tab, snapshot, cx)
|
self.render_split_pane(pane_index, tab, snapshot, false, cx)
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}))
|
}))
|
||||||
@@ -171,6 +179,7 @@ impl ElyShell {
|
|||||||
index: usize,
|
index: usize,
|
||||||
tab: &BrowserTab,
|
tab: &BrowserTab,
|
||||||
snapshot: &BrowserSnapshot,
|
snapshot: &BrowserSnapshot,
|
||||||
|
compact_canvas: bool,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let active = tab.id() == &snapshot.active_tab_id;
|
let active = tab.id() == &snapshot.active_tab_id;
|
||||||
@@ -226,17 +235,19 @@ impl ElyShell {
|
|||||||
.child(tab.title().to_string()),
|
.child(tab.title().to_string()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(div().flex_1().min_h_0().overflow_hidden().child(if compact_canvas {
|
||||||
div()
|
render_compact_split_canvas(tab)
|
||||||
.flex_1()
|
} else {
|
||||||
.min_h_0()
|
self.render_web_canvas(tab, snapshot, cx)
|
||||||
.overflow_hidden()
|
}))
|
||||||
.child(self.render_web_canvas(tab, snapshot, cx)),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_split_controls(&mut self, cx: &mut Context<Self>) -> AnyElement {
|
fn render_split_controls(
|
||||||
|
&mut self,
|
||||||
|
active_axis: &SplitAxis,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> AnyElement {
|
||||||
div()
|
div()
|
||||||
.min_h(px(36.0))
|
.min_h(px(36.0))
|
||||||
.px_2()
|
.px_2()
|
||||||
@@ -255,6 +266,43 @@ impl ElyShell {
|
|||||||
.text_color(rgb(colors::MUTED))
|
.text_color(rgb(colors::MUTED))
|
||||||
.child("Split Controls"),
|
.child("Split Controls"),
|
||||||
)
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.gap_1()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_xs()
|
||||||
|
.font_semibold()
|
||||||
|
.text_color(rgb(colors::MUTED))
|
||||||
|
.child("Layout"),
|
||||||
|
)
|
||||||
|
.child(self.render_split_axis_button(
|
||||||
|
"split-axis-horizontal-control",
|
||||||
|
SplitAxis::Horizontal,
|
||||||
|
active_axis,
|
||||||
|
IconName::PanelRight,
|
||||||
|
"Horizontal",
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
.child(self.render_split_axis_button(
|
||||||
|
"split-axis-vertical-control",
|
||||||
|
SplitAxis::Vertical,
|
||||||
|
active_axis,
|
||||||
|
IconName::PanelBottom,
|
||||||
|
"Vertical",
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
.child(self.render_split_axis_button(
|
||||||
|
"split-axis-grid-control",
|
||||||
|
SplitAxis::Grid,
|
||||||
|
active_axis,
|
||||||
|
IconName::LayoutDashboard,
|
||||||
|
"Grid",
|
||||||
|
cx,
|
||||||
|
)),
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("swap-split-pane-control")
|
Button::new("swap-split-pane-control")
|
||||||
.ghost()
|
.ghost()
|
||||||
@@ -313,6 +361,29 @@ impl ElyShell {
|
|||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_split_axis_button(
|
||||||
|
&mut self,
|
||||||
|
id: &'static str,
|
||||||
|
axis: SplitAxis,
|
||||||
|
active_axis: &SplitAxis,
|
||||||
|
icon: IconName,
|
||||||
|
label: &'static str,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> AnyElement {
|
||||||
|
let selected = active_axis == &axis;
|
||||||
|
Button::new(id)
|
||||||
|
.ghost()
|
||||||
|
.xsmall()
|
||||||
|
.selected(selected)
|
||||||
|
.icon(icon)
|
||||||
|
.label(label)
|
||||||
|
.tooltip(format!("{label} Split Layout"))
|
||||||
|
.on_click(cx.listener(move |shell, _, _, cx| {
|
||||||
|
shell.set_active_split_axis(axis.clone(), cx);
|
||||||
|
}))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn render_saved_split_row(
|
pub(super) fn render_saved_split_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: &SplitLayout,
|
layout: &SplitLayout,
|
||||||
@@ -375,3 +446,51 @@ fn active_split_layout<'a>(
|
|||||||
let split_id = active_tab.split_id()?;
|
let split_id = active_tab.split_id()?;
|
||||||
snapshot.split_layouts.iter().find(|layout| layout.id() == split_id)
|
snapshot.split_layouts.iter().find(|layout| layout.id() == split_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_compact_split_canvas(tab: &BrowserTab) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.min_h_0()
|
||||||
|
.p_2()
|
||||||
|
.bg(rgb(colors::CANVAS_SOFT))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.size_full()
|
||||||
|
.min_h_0()
|
||||||
|
.rounded_md()
|
||||||
|
.border_1()
|
||||||
|
.border_color(rgb(colors::HAIRLINE))
|
||||||
|
.bg(rgb(colors::SURFACE_CARD))
|
||||||
|
.px_3()
|
||||||
|
.py_2()
|
||||||
|
.gap_2()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.child(div().text_color(rgb(colors::MUTED)).child(IconName::Globe))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.min_w_0()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.truncate()
|
||||||
|
.text_sm()
|
||||||
|
.font_semibold()
|
||||||
|
.child(tab.title().to_string()),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.truncate()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(rgb(colors::MUTED))
|
||||||
|
.child(split_canvas_status(tab)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_canvas_status(tab: &BrowserTab) -> String {
|
||||||
|
if tab.url().as_str() == "ely://new-tab" { "Ready".to_string() } else { tab.display_url() }
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user