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