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 let _ = is_active;
35 div()
36 .id(("pane-leaf", leaf_id))
37 .debug_selector(move || format!("PANE-LEAF-{leaf_id}"))
42 .size_full()
43 .overflow_hidden()
44 .on_mouse_down(
47 MouseButton::Left,
48 cx.listener(move |this, _, window, cx| {
49 this.active_pane = focus_target.clone();
50 this.sync_focus(cx);
51 window.focus(&this.focus_handle, cx);
52 cx.notify();
53 }),
54 )
55 .child(pane.clone())
56 .into_any_element()
57 }
58
59 fn render_axis(
60 &self,
61 pane_axis: &PaneAxis,
62 path: Vec<usize>,
63 window: &mut Window,
64 cx: &mut Context<Self>,
65 ) -> AnyElement {
66 let axis = pane_axis.axis;
67 let mut container = match axis {
68 Axis::Horizontal => h_flex(),
69 Axis::Vertical => v_flex(),
70 }
71 .id(format!("pane-axis-{path:?}"))
72 .size_full()
73 .overflow_hidden();
74
75 let measure = pane_axis.bounds.clone();
76 container = container.child(
77 canvas(
78 move |bounds, _, _| measure.set(Some(bounds)),
79 |_, _, _, _| {},
80 )
81 .absolute()
82 .size_full(),
83 );
84
85 let n = pane_axis.members.len();
86 for (ix, child) in pane_axis.members.iter().enumerate() {
87 let mut child_path = path.clone();
88 child_path.push(ix);
89 let child_el = self.render_member(child, child_path, window, cx);
90
91 container = container.child(
92 ResizablePanel::new()
93 .axis(axis)
94 .fraction(pane_axis.flexes[ix])
95 .child(child_el),
96 );
97
98 if ix + 1 < n {
99 container = container.child(self.render_divider(
100 path.clone(),
101 ix,
102 axis,
103 pane_axis.flexes[ix],
104 pane_axis.flexes[ix + 1],
105 window,
106 cx,
107 ));
108 }
109 }
110
111 container.into_any_element()
112 }
113}
114
115impl Render for PaneGroup {
116 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
117 let content = self.render_member(&self.root, Vec::new(), window, cx);
118
119 div()
120 .id("pane-group")
121 .key_context("PaneGroup")
122 .track_focus(&self.focus_handle)
123 .size_full()
124 .on_action(cx.listener(|this, _: &SplitRight, _, cx| {
125 this.split(SplitDirection::Right, cx);
126 }))
127 .on_action(cx.listener(|this, _: &SplitDown, _, cx| {
128 this.split(SplitDirection::Down, cx);
129 }))
130 .on_action(cx.listener(|this, _: &SplitLeft, _, cx| {
131 this.split(SplitDirection::Left, cx);
132 }))
133 .on_action(cx.listener(|this, _: &SplitUp, _, cx| {
134 this.split(SplitDirection::Up, cx);
135 }))
136 .on_action(cx.listener(|this, _: &ClosePane, _, cx| {
137 let _ = this.close_active(cx);
138 }))
139 .on_action(cx.listener(|this, _: &FocusLeft, _, cx| {
140 this.focus(SplitDirection::Left, cx);
141 }))
142 .on_action(cx.listener(|this, _: &FocusRight, _, cx| {
143 this.focus(SplitDirection::Right, cx);
144 }))
145 .on_action(cx.listener(|this, _: &FocusUp, _, cx| {
146 this.focus(SplitDirection::Up, cx);
147 }))
148 .on_action(cx.listener(|this, _: &FocusDown, _, cx| {
149 this.focus(SplitDirection::Down, cx);
150 }))
151 .child(content)
152 }
153}