pane_group.rs

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