ui/components/pane_group/
render.rs1use gpui::{AnyElement, Axis, Entity, MouseButton, canvas};
8
9use super::{Member, PaneAxis, PaneGroup, SplitDirection};
10use crate::{
11 ClosePane, FocusDown, FocusLeft, FocusRight, FocusUp, Pane, ResizablePanel, SplitDown,
12 SplitLeft, SplitRight, SplitUp, prelude::*,
13};
14
15impl PaneGroup {
16 fn render_member(
17 &self,
18 member: &Member,
19 path: Vec<usize>,
20 window: &mut Window,
21 cx: &mut Context<Self>,
22 ) -> AnyElement {
23 match member {
24 Member::Leaf(pane) => self.render_leaf(pane, cx),
25 Member::Split(axis) => self.render_axis(axis, path, window, cx),
26 }
27 }
28
29 fn render_leaf(&self, pane: &Entity<Pane>, cx: &mut Context<Self>) -> AnyElement {
30 let is_active = pane.entity_id() == self.active_pane.entity_id();
31 let focus_target = pane.clone();
32 let leaf_id = pane.entity_id().as_u64();
33
34 div()
35 .id(("pane-leaf", leaf_id))
36 .debug_selector(move || format!("PANE-LEAF-{leaf_id}"))
41 .size_full()
42 .overflow_hidden()
43 .border_2()
44 .border_color(if is_active {
45 semantic::border_focused(cx)
46 } else {
47 gpui::transparent_black()
48 })
49 .on_mouse_down(
50 MouseButton::Left,
51 cx.listener(move |this, _, window, cx| {
52 this.active_pane = focus_target.clone();
53 window.focus(&this.focus_handle, cx);
54 cx.notify();
55 }),
56 )
57 .child(pane.clone())
58 .into_any_element()
59 }
60
61 fn render_axis(
62 &self,
63 pane_axis: &PaneAxis,
64 path: Vec<usize>,
65 window: &mut Window,
66 cx: &mut Context<Self>,
67 ) -> AnyElement {
68 let axis = pane_axis.axis;
69 let mut container = match axis {
70 Axis::Horizontal => h_flex(),
71 Axis::Vertical => v_flex(),
72 }
73 .id(format!("pane-axis-{path:?}"))
74 .size_full()
75 .overflow_hidden();
76
77 let measure = pane_axis.bounds.clone();
78 container = container.child(
79 canvas(
80 move |bounds, _, _| measure.set(Some(bounds)),
81 |_, _, _, _| {},
82 )
83 .absolute()
84 .size_full(),
85 );
86
87 let n = pane_axis.members.len();
88 for (ix, child) in pane_axis.members.iter().enumerate() {
89 let mut child_path = path.clone();
90 child_path.push(ix);
91 let child_el = self.render_member(child, child_path, window, cx);
92
93 container = container.child(
94 ResizablePanel::new()
95 .axis(axis)
96 .fraction(pane_axis.flexes[ix])
97 .child(child_el),
98 );
99
100 if ix + 1 < n {
101 container = container.child(self.render_divider(
102 path.clone(),
103 ix,
104 axis,
105 pane_axis.flexes[ix],
106 pane_axis.flexes[ix + 1],
107 window,
108 cx,
109 ));
110 }
111 }
112
113 container.into_any_element()
114 }
115}
116
117impl Render for PaneGroup {
118 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
119 let content = self.render_member(&self.root, Vec::new(), window, cx);
120
121 div()
122 .id("pane-group")
123 .key_context("PaneGroup")
124 .track_focus(&self.focus_handle)
125 .size_full()
126 .on_action(cx.listener(|this, _: &SplitRight, _, cx| {
127 this.split(SplitDirection::Right, cx);
128 }))
129 .on_action(cx.listener(|this, _: &SplitDown, _, cx| {
130 this.split(SplitDirection::Down, cx);
131 }))
132 .on_action(cx.listener(|this, _: &SplitLeft, _, cx| {
133 this.split(SplitDirection::Left, cx);
134 }))
135 .on_action(cx.listener(|this, _: &SplitUp, _, cx| {
136 this.split(SplitDirection::Up, cx);
137 }))
138 .on_action(cx.listener(|this, _: &ClosePane, _, cx| {
139 let _ = this.close_active(cx);
140 }))
141 .on_action(cx.listener(|this, _: &FocusLeft, _, cx| {
142 this.focus(SplitDirection::Left, cx);
143 }))
144 .on_action(cx.listener(|this, _: &FocusRight, _, cx| {
145 this.focus(SplitDirection::Right, cx);
146 }))
147 .on_action(cx.listener(|this, _: &FocusUp, _, cx| {
148 this.focus(SplitDirection::Up, cx);
149 }))
150 .on_action(cx.listener(|this, _: &FocusDown, _, cx| {
151 this.focus(SplitDirection::Down, cx);
152 }))
153 .child(content)
154 }
155}