1use crate::{pane_group::element::PaneAxisElement, AppState, FollowerState, Pane, Workspace};
2use anyhow::{anyhow, Result};
3use call::{ActiveCall, ParticipantLocation};
4use collections::HashMap;
5use gpui::{
6 elements::*,
7 geometry::{rect::RectF, vector::Vector2F},
8 platform::{CursorStyle, MouseButton},
9 AnyViewHandle, Axis, ModelHandle, ViewContext, ViewHandle,
10};
11use project::Project;
12use serde::Deserialize;
13use std::{cell::RefCell, rc::Rc, sync::Arc};
14use theme::Theme;
15
16const HANDLE_HITBOX_SIZE: f32 = 4.0;
17const HORIZONTAL_MIN_SIZE: f32 = 80.;
18const VERTICAL_MIN_SIZE: f32 = 100.;
19
20#[derive(Clone, Debug, PartialEq)]
21pub struct PaneGroup {
22 pub(crate) root: Member,
23}
24
25impl PaneGroup {
26 pub(crate) fn with_root(root: Member) -> Self {
27 Self { root }
28 }
29
30 pub fn new(pane: ViewHandle<Pane>) -> Self {
31 Self {
32 root: Member::Pane(pane),
33 }
34 }
35
36 pub fn split(
37 &mut self,
38 old_pane: &ViewHandle<Pane>,
39 new_pane: &ViewHandle<Pane>,
40 direction: SplitDirection,
41 ) -> Result<()> {
42 match &mut self.root {
43 Member::Pane(pane) => {
44 if pane == old_pane {
45 self.root = Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
46 Ok(())
47 } else {
48 Err(anyhow!("Pane not found"))
49 }
50 }
51 Member::Axis(axis) => axis.split(old_pane, new_pane, direction),
52 }
53 }
54
55 pub fn bounding_box_for_pane(&self, pane: &ViewHandle<Pane>) -> Option<RectF> {
56 match &self.root {
57 Member::Pane(_) => None,
58 Member::Axis(axis) => axis.bounding_box_for_pane(pane),
59 }
60 }
61
62 pub fn pane_at_pixel_position(&self, coordinate: Vector2F) -> Option<&ViewHandle<Pane>> {
63 match &self.root {
64 Member::Pane(pane) => Some(pane),
65 Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
66 }
67 }
68
69 /// Returns:
70 /// - Ok(true) if it found and removed a pane
71 /// - Ok(false) if it found but did not remove the pane
72 /// - Err(_) if it did not find the pane
73 pub fn remove(&mut self, pane: &ViewHandle<Pane>) -> Result<bool> {
74 match &mut self.root {
75 Member::Pane(_) => Ok(false),
76 Member::Axis(axis) => {
77 if let Some(last_pane) = axis.remove(pane)? {
78 self.root = last_pane;
79 }
80 Ok(true)
81 }
82 }
83 }
84
85 pub fn swap(&mut self, from: &ViewHandle<Pane>, to: &ViewHandle<Pane>) {
86 match &mut self.root {
87 Member::Pane(_) => {}
88 Member::Axis(axis) => axis.swap(from, to),
89 };
90 }
91
92 pub(crate) fn render(
93 &self,
94 project: &ModelHandle<Project>,
95 theme: &Theme,
96 follower_states: &HashMap<ViewHandle<Pane>, FollowerState>,
97 active_call: Option<&ModelHandle<ActiveCall>>,
98 active_pane: &ViewHandle<Pane>,
99 zoomed: Option<&AnyViewHandle>,
100 app_state: &Arc<AppState>,
101 cx: &mut ViewContext<Workspace>,
102 ) -> AnyElement<Workspace> {
103 self.root.render(
104 project,
105 0,
106 theme,
107 follower_states,
108 active_call,
109 active_pane,
110 zoomed,
111 app_state,
112 cx,
113 )
114 }
115
116 pub(crate) fn panes(&self) -> Vec<&ViewHandle<Pane>> {
117 let mut panes = Vec::new();
118 self.root.collect_panes(&mut panes);
119 panes
120 }
121}
122
123#[derive(Clone, Debug, PartialEq)]
124pub(crate) enum Member {
125 Axis(PaneAxis),
126 Pane(ViewHandle<Pane>),
127}
128
129impl Member {
130 fn new_axis(
131 old_pane: ViewHandle<Pane>,
132 new_pane: ViewHandle<Pane>,
133 direction: SplitDirection,
134 ) -> Self {
135 use Axis::*;
136 use SplitDirection::*;
137
138 let axis = match direction {
139 Up | Down => Vertical,
140 Left | Right => Horizontal,
141 };
142
143 let members = match direction {
144 Up | Left => vec![Member::Pane(new_pane), Member::Pane(old_pane)],
145 Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)],
146 };
147
148 Member::Axis(PaneAxis::new(axis, members))
149 }
150
151 fn contains(&self, needle: &ViewHandle<Pane>) -> bool {
152 match self {
153 Member::Axis(axis) => axis.members.iter().any(|member| member.contains(needle)),
154 Member::Pane(pane) => pane == needle,
155 }
156 }
157
158 pub fn render(
159 &self,
160 project: &ModelHandle<Project>,
161 basis: usize,
162 theme: &Theme,
163 follower_states: &HashMap<ViewHandle<Pane>, FollowerState>,
164 active_call: Option<&ModelHandle<ActiveCall>>,
165 active_pane: &ViewHandle<Pane>,
166 zoomed: Option<&AnyViewHandle>,
167 app_state: &Arc<AppState>,
168 cx: &mut ViewContext<Workspace>,
169 ) -> AnyElement<Workspace> {
170 enum FollowIntoExternalProject {}
171
172 match self {
173 Member::Pane(pane) => {
174 let pane_element = if Some(&**pane) == zoomed {
175 Empty::new().into_any()
176 } else {
177 ChildView::new(pane, cx).into_any()
178 };
179
180 let leader = follower_states.get(pane).and_then(|state| {
181 let room = active_call?.read(cx).room()?.read(cx);
182 room.remote_participant_for_peer_id(state.leader_id)
183 });
184
185 let mut leader_border = Border::default();
186 let mut leader_status_box = None;
187 if let Some(leader) = &leader {
188 let leader_color = theme
189 .editor
190 .selection_style_for_room_participant(leader.participant_index.0)
191 .cursor;
192 leader_border = Border::all(theme.workspace.leader_border_width, leader_color);
193 leader_border
194 .color
195 .fade_out(1. - theme.workspace.leader_border_opacity);
196 leader_border.overlay = true;
197
198 leader_status_box = match leader.location {
199 ParticipantLocation::SharedProject {
200 project_id: leader_project_id,
201 } => {
202 if Some(leader_project_id) == project.read(cx).remote_id() {
203 None
204 } else {
205 let leader_user = leader.user.clone();
206 let leader_user_id = leader.user.id;
207 Some(
208 MouseEventHandler::new::<FollowIntoExternalProject, _>(
209 pane.id(),
210 cx,
211 |_, _| {
212 Label::new(
213 format!(
214 "Follow {} to their active project",
215 leader_user.github_login,
216 ),
217 theme
218 .workspace
219 .external_location_message
220 .text
221 .clone(),
222 )
223 .contained()
224 .with_style(
225 theme.workspace.external_location_message.container,
226 )
227 },
228 )
229 .with_cursor_style(CursorStyle::PointingHand)
230 .on_click(MouseButton::Left, move |_, this, cx| {
231 crate::join_remote_project(
232 leader_project_id,
233 leader_user_id,
234 this.app_state().clone(),
235 cx,
236 )
237 .detach_and_log_err(cx);
238 })
239 .aligned()
240 .bottom()
241 .right()
242 .into_any(),
243 )
244 }
245 }
246 ParticipantLocation::UnsharedProject => Some(
247 Label::new(
248 format!(
249 "{} is viewing an unshared Zed project",
250 leader.user.github_login
251 ),
252 theme.workspace.external_location_message.text.clone(),
253 )
254 .contained()
255 .with_style(theme.workspace.external_location_message.container)
256 .aligned()
257 .bottom()
258 .right()
259 .into_any(),
260 ),
261 ParticipantLocation::External => Some(
262 Label::new(
263 format!(
264 "{} is viewing a window outside of Zed",
265 leader.user.github_login
266 ),
267 theme.workspace.external_location_message.text.clone(),
268 )
269 .contained()
270 .with_style(theme.workspace.external_location_message.container)
271 .aligned()
272 .bottom()
273 .right()
274 .into_any(),
275 ),
276 };
277 }
278
279 Stack::new()
280 .with_child(pane_element.contained().with_border(leader_border))
281 .with_children(leader_status_box)
282 .into_any()
283 }
284 Member::Axis(axis) => axis.render(
285 project,
286 basis + 1,
287 theme,
288 follower_states,
289 active_call,
290 active_pane,
291 zoomed,
292 app_state,
293 cx,
294 ),
295 }
296 }
297
298 fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a ViewHandle<Pane>>) {
299 match self {
300 Member::Axis(axis) => {
301 for member in &axis.members {
302 member.collect_panes(panes);
303 }
304 }
305 Member::Pane(pane) => panes.push(pane),
306 }
307 }
308}
309
310#[derive(Clone, Debug, PartialEq)]
311pub(crate) struct PaneAxis {
312 pub axis: Axis,
313 pub members: Vec<Member>,
314 pub flexes: Rc<RefCell<Vec<f32>>>,
315 pub bounding_boxes: Rc<RefCell<Vec<Option<RectF>>>>,
316}
317
318impl PaneAxis {
319 pub fn new(axis: Axis, members: Vec<Member>) -> Self {
320 let flexes = Rc::new(RefCell::new(vec![1.; members.len()]));
321 let bounding_boxes = Rc::new(RefCell::new(vec![None; members.len()]));
322 Self {
323 axis,
324 members,
325 flexes,
326 bounding_boxes,
327 }
328 }
329
330 pub fn load(axis: Axis, members: Vec<Member>, flexes: Option<Vec<f32>>) -> Self {
331 let flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]);
332 debug_assert!(members.len() == flexes.len());
333
334 let flexes = Rc::new(RefCell::new(flexes));
335 let bounding_boxes = Rc::new(RefCell::new(vec![None; members.len()]));
336 Self {
337 axis,
338 members,
339 flexes,
340 bounding_boxes,
341 }
342 }
343
344 fn split(
345 &mut self,
346 old_pane: &ViewHandle<Pane>,
347 new_pane: &ViewHandle<Pane>,
348 direction: SplitDirection,
349 ) -> Result<()> {
350 for (mut idx, member) in self.members.iter_mut().enumerate() {
351 match member {
352 Member::Axis(axis) => {
353 if axis.split(old_pane, new_pane, direction).is_ok() {
354 return Ok(());
355 }
356 }
357 Member::Pane(pane) => {
358 if pane == old_pane {
359 if direction.axis() == self.axis {
360 if direction.increasing() {
361 idx += 1;
362 }
363
364 self.members.insert(idx, Member::Pane(new_pane.clone()));
365 *self.flexes.borrow_mut() = vec![1.; self.members.len()];
366 } else {
367 *member =
368 Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
369 }
370 return Ok(());
371 }
372 }
373 }
374 }
375 Err(anyhow!("Pane not found"))
376 }
377
378 fn remove(&mut self, pane_to_remove: &ViewHandle<Pane>) -> Result<Option<Member>> {
379 let mut found_pane = false;
380 let mut remove_member = None;
381 for (idx, member) in self.members.iter_mut().enumerate() {
382 match member {
383 Member::Axis(axis) => {
384 if let Ok(last_pane) = axis.remove(pane_to_remove) {
385 if let Some(last_pane) = last_pane {
386 *member = last_pane;
387 }
388 found_pane = true;
389 break;
390 }
391 }
392 Member::Pane(pane) => {
393 if pane == pane_to_remove {
394 found_pane = true;
395 remove_member = Some(idx);
396 break;
397 }
398 }
399 }
400 }
401
402 if found_pane {
403 if let Some(idx) = remove_member {
404 self.members.remove(idx);
405 *self.flexes.borrow_mut() = vec![1.; self.members.len()];
406 }
407
408 if self.members.len() == 1 {
409 let result = self.members.pop();
410 *self.flexes.borrow_mut() = vec![1.; self.members.len()];
411 Ok(result)
412 } else {
413 Ok(None)
414 }
415 } else {
416 Err(anyhow!("Pane not found"))
417 }
418 }
419
420 fn swap(&mut self, from: &ViewHandle<Pane>, to: &ViewHandle<Pane>) {
421 for member in self.members.iter_mut() {
422 match member {
423 Member::Axis(axis) => axis.swap(from, to),
424 Member::Pane(pane) => {
425 if pane == from {
426 *member = Member::Pane(to.clone());
427 } else if pane == to {
428 *member = Member::Pane(from.clone())
429 }
430 }
431 }
432 }
433 }
434
435 fn bounding_box_for_pane(&self, pane: &ViewHandle<Pane>) -> Option<RectF> {
436 debug_assert!(self.members.len() == self.bounding_boxes.borrow().len());
437
438 for (idx, member) in self.members.iter().enumerate() {
439 match member {
440 Member::Pane(found) => {
441 if pane == found {
442 return self.bounding_boxes.borrow()[idx];
443 }
444 }
445 Member::Axis(axis) => {
446 if let Some(rect) = axis.bounding_box_for_pane(pane) {
447 return Some(rect);
448 }
449 }
450 }
451 }
452 None
453 }
454
455 fn pane_at_pixel_position(&self, coordinate: Vector2F) -> Option<&ViewHandle<Pane>> {
456 debug_assert!(self.members.len() == self.bounding_boxes.borrow().len());
457
458 let bounding_boxes = self.bounding_boxes.borrow();
459
460 for (idx, member) in self.members.iter().enumerate() {
461 if let Some(coordinates) = bounding_boxes[idx] {
462 if coordinates.contains_point(coordinate) {
463 return match member {
464 Member::Pane(found) => Some(found),
465 Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
466 };
467 }
468 }
469 }
470 None
471 }
472
473 fn render(
474 &self,
475 project: &ModelHandle<Project>,
476 basis: usize,
477 theme: &Theme,
478 follower_states: &HashMap<ViewHandle<Pane>, FollowerState>,
479 active_call: Option<&ModelHandle<ActiveCall>>,
480 active_pane: &ViewHandle<Pane>,
481 zoomed: Option<&AnyViewHandle>,
482 app_state: &Arc<AppState>,
483 cx: &mut ViewContext<Workspace>,
484 ) -> AnyElement<Workspace> {
485 debug_assert!(self.members.len() == self.flexes.borrow().len());
486
487 let mut pane_axis = PaneAxisElement::new(
488 self.axis,
489 basis,
490 self.flexes.clone(),
491 self.bounding_boxes.clone(),
492 );
493 let mut active_pane_ix = None;
494
495 let mut members = self.members.iter().enumerate().peekable();
496 while let Some((ix, member)) = members.next() {
497 let last = members.peek().is_none();
498
499 if member.contains(active_pane) {
500 active_pane_ix = Some(ix);
501 }
502
503 let mut member = member.render(
504 project,
505 (basis + ix) * 10,
506 theme,
507 follower_states,
508 active_call,
509 active_pane,
510 zoomed,
511 app_state,
512 cx,
513 );
514
515 if !last {
516 let mut border = theme.workspace.pane_divider;
517 border.left = false;
518 border.right = false;
519 border.top = false;
520 border.bottom = false;
521
522 match self.axis {
523 Axis::Vertical => border.bottom = true,
524 Axis::Horizontal => border.right = true,
525 }
526
527 member = member.contained().with_border(border).into_any();
528 }
529
530 pane_axis = pane_axis.with_child(member.into_any());
531 }
532 pane_axis.set_active_pane(active_pane_ix);
533 pane_axis.into_any()
534 }
535}
536
537#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
538pub enum SplitDirection {
539 Up,
540 Down,
541 Left,
542 Right,
543}
544
545impl SplitDirection {
546 pub fn all() -> [Self; 4] {
547 [Self::Up, Self::Down, Self::Left, Self::Right]
548 }
549
550 pub fn edge(&self, rect: RectF) -> f32 {
551 match self {
552 Self::Up => rect.min_y(),
553 Self::Down => rect.max_y(),
554 Self::Left => rect.min_x(),
555 Self::Right => rect.max_x(),
556 }
557 }
558
559 // Returns a new rectangle which shares an edge in SplitDirection and has `size` along SplitDirection
560 pub fn along_edge(&self, rect: RectF, size: f32) -> RectF {
561 match self {
562 Self::Up => RectF::new(rect.origin(), Vector2F::new(rect.width(), size)),
563 Self::Down => RectF::new(
564 rect.lower_left() - Vector2F::new(0., size),
565 Vector2F::new(rect.width(), size),
566 ),
567 Self::Left => RectF::new(rect.origin(), Vector2F::new(size, rect.height())),
568 Self::Right => RectF::new(
569 rect.upper_right() - Vector2F::new(size, 0.),
570 Vector2F::new(size, rect.height()),
571 ),
572 }
573 }
574
575 pub fn axis(&self) -> Axis {
576 match self {
577 Self::Up | Self::Down => Axis::Vertical,
578 Self::Left | Self::Right => Axis::Horizontal,
579 }
580 }
581
582 pub fn increasing(&self) -> bool {
583 match self {
584 Self::Left | Self::Up => false,
585 Self::Down | Self::Right => true,
586 }
587 }
588}
589
590mod element {
591 use std::{cell::RefCell, iter::from_fn, ops::Range, rc::Rc};
592
593 use gpui::{
594 geometry::{
595 rect::RectF,
596 vector::{vec2f, Vector2F},
597 },
598 json::{self, ToJson},
599 platform::{CursorStyle, MouseButton},
600 scene::MouseDrag,
601 AnyElement, Axis, CursorRegion, Element, EventContext, MouseRegion, RectFExt,
602 SizeConstraint, Vector2FExt, ViewContext,
603 };
604
605 use crate::{
606 pane_group::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE},
607 Workspace, WorkspaceSettings,
608 };
609
610 pub struct PaneAxisElement {
611 axis: Axis,
612 basis: usize,
613 active_pane_ix: Option<usize>,
614 flexes: Rc<RefCell<Vec<f32>>>,
615 children: Vec<AnyElement<Workspace>>,
616 bounding_boxes: Rc<RefCell<Vec<Option<RectF>>>>,
617 }
618
619 impl PaneAxisElement {
620 pub fn new(
621 axis: Axis,
622 basis: usize,
623 flexes: Rc<RefCell<Vec<f32>>>,
624 bounding_boxes: Rc<RefCell<Vec<Option<RectF>>>>,
625 ) -> Self {
626 Self {
627 axis,
628 basis,
629 flexes,
630 bounding_boxes,
631 active_pane_ix: None,
632 children: Default::default(),
633 }
634 }
635
636 pub fn set_active_pane(&mut self, active_pane_ix: Option<usize>) {
637 self.active_pane_ix = active_pane_ix;
638 }
639
640 fn layout_children(
641 &mut self,
642 active_pane_magnification: f32,
643 constraint: SizeConstraint,
644 remaining_space: &mut f32,
645 remaining_flex: &mut f32,
646 cross_axis_max: &mut f32,
647 view: &mut Workspace,
648 cx: &mut ViewContext<Workspace>,
649 ) {
650 let flexes = self.flexes.borrow();
651 let cross_axis = self.axis.invert();
652 for (ix, child) in self.children.iter_mut().enumerate() {
653 let flex = if active_pane_magnification != 1. {
654 if let Some(active_pane_ix) = self.active_pane_ix {
655 if ix == active_pane_ix {
656 active_pane_magnification
657 } else {
658 1.
659 }
660 } else {
661 1.
662 }
663 } else {
664 flexes[ix]
665 };
666
667 let child_size = if *remaining_flex == 0.0 {
668 *remaining_space
669 } else {
670 let space_per_flex = *remaining_space / *remaining_flex;
671 space_per_flex * flex
672 };
673
674 let child_constraint = match self.axis {
675 Axis::Horizontal => SizeConstraint::new(
676 vec2f(child_size, constraint.min.y()),
677 vec2f(child_size, constraint.max.y()),
678 ),
679 Axis::Vertical => SizeConstraint::new(
680 vec2f(constraint.min.x(), child_size),
681 vec2f(constraint.max.x(), child_size),
682 ),
683 };
684 let child_size = child.layout(child_constraint, view, cx);
685 *remaining_space -= child_size.along(self.axis);
686 *remaining_flex -= flex;
687 *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
688 }
689 }
690
691 fn handle_resize(
692 flexes: Rc<RefCell<Vec<f32>>>,
693 axis: Axis,
694 preceding_ix: usize,
695 child_start: Vector2F,
696 drag_bounds: RectF,
697 ) -> impl Fn(MouseDrag, &mut Workspace, &mut EventContext<Workspace>) {
698 let size = move |ix, flexes: &[f32]| {
699 drag_bounds.length_along(axis) * (flexes[ix] / flexes.len() as f32)
700 };
701
702 move |drag, workspace: &mut Workspace, cx| {
703 if drag.end {
704 // TODO: Clear cascading resize state
705 return;
706 }
707 let min_size = match axis {
708 Axis::Horizontal => HORIZONTAL_MIN_SIZE,
709 Axis::Vertical => VERTICAL_MIN_SIZE,
710 };
711 let mut flexes = flexes.borrow_mut();
712
713 // Don't allow resizing to less than the minimum size, if elements are already too small
714 if min_size - 1. > size(preceding_ix, flexes.as_slice()) {
715 return;
716 }
717
718 let mut proposed_current_pixel_change = (drag.position - child_start).along(axis)
719 - size(preceding_ix, flexes.as_slice());
720
721 let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
722 let flex_change = pixel_dx / drag_bounds.length_along(axis);
723 let current_target_flex = flexes[target_ix] + flex_change;
724 let next_target_flex =
725 flexes[(target_ix as isize + next) as usize] - flex_change;
726 (current_target_flex, next_target_flex)
727 };
728
729 let mut successors = from_fn({
730 let forward = proposed_current_pixel_change > 0.;
731 let mut ix_offset = 0;
732 let len = flexes.len();
733 move || {
734 let result = if forward {
735 (preceding_ix + 1 + ix_offset < len).then(|| preceding_ix + ix_offset)
736 } else {
737 (preceding_ix as isize - ix_offset as isize >= 0)
738 .then(|| preceding_ix - ix_offset)
739 };
740
741 ix_offset += 1;
742
743 result
744 }
745 });
746
747 while proposed_current_pixel_change.abs() > 0. {
748 let Some(current_ix) = successors.next() else {
749 break;
750 };
751
752 let next_target_size = f32::max(
753 size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change,
754 min_size,
755 );
756
757 let current_target_size = f32::max(
758 size(current_ix, flexes.as_slice())
759 + size(current_ix + 1, flexes.as_slice())
760 - next_target_size,
761 min_size,
762 );
763
764 let current_pixel_change =
765 current_target_size - size(current_ix, flexes.as_slice());
766
767 let (current_target_flex, next_target_flex) =
768 flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice());
769
770 flexes[current_ix] = current_target_flex;
771 flexes[current_ix + 1] = next_target_flex;
772
773 proposed_current_pixel_change -= current_pixel_change;
774 }
775
776 workspace.schedule_serialize(cx);
777 cx.notify();
778 }
779 }
780 }
781
782 impl Extend<AnyElement<Workspace>> for PaneAxisElement {
783 fn extend<T: IntoIterator<Item = AnyElement<Workspace>>>(&mut self, children: T) {
784 self.children.extend(children);
785 }
786 }
787
788 impl Element<Workspace> for PaneAxisElement {
789 type LayoutState = f32;
790 type PaintState = ();
791
792 fn layout(
793 &mut self,
794 constraint: SizeConstraint,
795 view: &mut Workspace,
796 cx: &mut ViewContext<Workspace>,
797 ) -> (Vector2F, Self::LayoutState) {
798 debug_assert!(self.children.len() == self.flexes.borrow().len());
799
800 let active_pane_magnification =
801 settings::get::<WorkspaceSettings>(cx).active_pane_magnification;
802
803 let mut remaining_flex = 0.;
804
805 if active_pane_magnification != 1. {
806 let active_pane_flex = self
807 .active_pane_ix
808 .map(|_| active_pane_magnification)
809 .unwrap_or(1.);
810 remaining_flex += self.children.len() as f32 - 1. + active_pane_flex;
811 } else {
812 for flex in self.flexes.borrow().iter() {
813 remaining_flex += flex;
814 }
815 }
816
817 let mut cross_axis_max: f32 = 0.0;
818 let mut remaining_space = constraint.max_along(self.axis);
819
820 if remaining_space.is_infinite() {
821 panic!("flex contains flexible children but has an infinite constraint along the flex axis");
822 }
823
824 self.layout_children(
825 active_pane_magnification,
826 constraint,
827 &mut remaining_space,
828 &mut remaining_flex,
829 &mut cross_axis_max,
830 view,
831 cx,
832 );
833
834 let mut size = match self.axis {
835 Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
836 Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
837 };
838
839 if constraint.min.x().is_finite() {
840 size.set_x(size.x().max(constraint.min.x()));
841 }
842 if constraint.min.y().is_finite() {
843 size.set_y(size.y().max(constraint.min.y()));
844 }
845
846 if size.x() > constraint.max.x() {
847 size.set_x(constraint.max.x());
848 }
849 if size.y() > constraint.max.y() {
850 size.set_y(constraint.max.y());
851 }
852
853 (size, remaining_space)
854 }
855
856 fn paint(
857 &mut self,
858 bounds: RectF,
859 visible_bounds: RectF,
860 remaining_space: &mut Self::LayoutState,
861 view: &mut Workspace,
862 cx: &mut ViewContext<Workspace>,
863 ) -> Self::PaintState {
864 let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.;
865 let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
866
867 let overflowing = *remaining_space < 0.;
868 if overflowing {
869 cx.scene().push_layer(Some(visible_bounds));
870 }
871
872 let mut child_origin = bounds.origin();
873
874 let mut bounding_boxes = self.bounding_boxes.borrow_mut();
875 bounding_boxes.clear();
876
877 let mut children_iter = self.children.iter_mut().enumerate().peekable();
878 while let Some((ix, child)) = children_iter.next() {
879 let child_start = child_origin.clone();
880 child.paint(child_origin, visible_bounds, view, cx);
881
882 bounding_boxes.push(Some(RectF::new(child_origin, child.size())));
883
884 match self.axis {
885 Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
886 Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
887 }
888
889 if can_resize && children_iter.peek().is_some() {
890 cx.scene().push_stacking_context(None, None);
891
892 let handle_origin = match self.axis {
893 Axis::Horizontal => child_origin - vec2f(HANDLE_HITBOX_SIZE / 2., 0.0),
894 Axis::Vertical => child_origin - vec2f(0.0, HANDLE_HITBOX_SIZE / 2.),
895 };
896
897 let handle_bounds = match self.axis {
898 Axis::Horizontal => RectF::new(
899 handle_origin,
900 vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()),
901 ),
902 Axis::Vertical => RectF::new(
903 handle_origin,
904 vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE),
905 ),
906 };
907
908 let style = match self.axis {
909 Axis::Horizontal => CursorStyle::ResizeLeftRight,
910 Axis::Vertical => CursorStyle::ResizeUpDown,
911 };
912
913 cx.scene().push_cursor_region(CursorRegion {
914 bounds: handle_bounds,
915 style,
916 });
917
918 enum ResizeHandle {}
919 let mut mouse_region = MouseRegion::new::<ResizeHandle>(
920 cx.view_id(),
921 self.basis + ix,
922 handle_bounds,
923 );
924 mouse_region = mouse_region
925 .on_drag(
926 MouseButton::Left,
927 Self::handle_resize(
928 self.flexes.clone(),
929 self.axis,
930 ix,
931 child_start,
932 visible_bounds.clone(),
933 ),
934 )
935 .on_click(MouseButton::Left, {
936 let flexes = self.flexes.clone();
937 move |e, v: &mut Workspace, cx| {
938 if e.click_count >= 2 {
939 let mut borrow = flexes.borrow_mut();
940 *borrow = vec![1.; borrow.len()];
941 v.schedule_serialize(cx);
942 cx.notify();
943 }
944 }
945 });
946 cx.scene().push_mouse_region(mouse_region);
947
948 cx.scene().pop_stacking_context();
949 }
950 }
951
952 if overflowing {
953 cx.scene().pop_layer();
954 }
955 }
956
957 fn rect_for_text_range(
958 &self,
959 range_utf16: Range<usize>,
960 _: RectF,
961 _: RectF,
962 _: &Self::LayoutState,
963 _: &Self::PaintState,
964 view: &Workspace,
965 cx: &ViewContext<Workspace>,
966 ) -> Option<RectF> {
967 self.children
968 .iter()
969 .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
970 }
971
972 fn debug(
973 &self,
974 bounds: RectF,
975 _: &Self::LayoutState,
976 _: &Self::PaintState,
977 view: &Workspace,
978 cx: &ViewContext<Workspace>,
979 ) -> json::Value {
980 serde_json::json!({
981 "type": "PaneAxis",
982 "bounds": bounds.to_json(),
983 "axis": self.axis.to_json(),
984 "flexes": *self.flexes.borrow(),
985 "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
986 })
987 }
988 }
989}