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    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::<FollowIntoExternalProject, _>::new(
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, 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        AnyElement, Axis, CursorRegion, Element, LayoutContext, MouseRegion, RectFExt,
597        SceneBuilder, SizeConstraint, Vector2FExt, ViewContext,
598    };
599
600    use crate::{
601        pane_group::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE},
602        Workspace, WorkspaceSettings,
603    };
604
605    pub struct PaneAxisElement {
606        axis: Axis,
607        basis: usize,
608        active_pane_ix: Option<usize>,
609        flexes: Rc<RefCell<Vec<f32>>>,
610        children: Vec<AnyElement<Workspace>>,
611        bounding_boxes: Rc<RefCell<Vec<Option<RectF>>>>,
612    }
613
614    impl PaneAxisElement {
615        pub fn new(
616            axis: Axis,
617            basis: usize,
618            flexes: Rc<RefCell<Vec<f32>>>,
619            bounding_boxes: Rc<RefCell<Vec<Option<RectF>>>>,
620        ) -> Self {
621            Self {
622                axis,
623                basis,
624                flexes,
625                bounding_boxes,
626                active_pane_ix: None,
627                children: Default::default(),
628            }
629        }
630
631        pub fn set_active_pane(&mut self, active_pane_ix: Option<usize>) {
632            self.active_pane_ix = active_pane_ix;
633        }
634
635        fn layout_children(
636            &mut self,
637            active_pane_magnification: f32,
638            constraint: SizeConstraint,
639            remaining_space: &mut f32,
640            remaining_flex: &mut f32,
641            cross_axis_max: &mut f32,
642            view: &mut Workspace,
643            cx: &mut LayoutContext<Workspace>,
644        ) {
645            let flexes = self.flexes.borrow();
646            let cross_axis = self.axis.invert();
647            for (ix, child) in self.children.iter_mut().enumerate() {
648                let flex = if active_pane_magnification != 1. {
649                    if let Some(active_pane_ix) = self.active_pane_ix {
650                        if ix == active_pane_ix {
651                            active_pane_magnification
652                        } else {
653                            1.
654                        }
655                    } else {
656                        1.
657                    }
658                } else {
659                    flexes[ix]
660                };
661
662                let child_size = if *remaining_flex == 0.0 {
663                    *remaining_space
664                } else {
665                    let space_per_flex = *remaining_space / *remaining_flex;
666                    space_per_flex * flex
667                };
668
669                let child_constraint = match self.axis {
670                    Axis::Horizontal => SizeConstraint::new(
671                        vec2f(child_size, constraint.min.y()),
672                        vec2f(child_size, constraint.max.y()),
673                    ),
674                    Axis::Vertical => SizeConstraint::new(
675                        vec2f(constraint.min.x(), child_size),
676                        vec2f(constraint.max.x(), child_size),
677                    ),
678                };
679                let child_size = child.layout(child_constraint, view, cx);
680                *remaining_space -= child_size.along(self.axis);
681                *remaining_flex -= flex;
682                *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
683            }
684        }
685    }
686
687    impl Extend<AnyElement<Workspace>> for PaneAxisElement {
688        fn extend<T: IntoIterator<Item = AnyElement<Workspace>>>(&mut self, children: T) {
689            self.children.extend(children);
690        }
691    }
692
693    impl Element<Workspace> for PaneAxisElement {
694        type LayoutState = f32;
695        type PaintState = ();
696
697        fn layout(
698            &mut self,
699            constraint: SizeConstraint,
700            view: &mut Workspace,
701            cx: &mut LayoutContext<Workspace>,
702        ) -> (Vector2F, Self::LayoutState) {
703            debug_assert!(self.children.len() == self.flexes.borrow().len());
704
705            let active_pane_magnification =
706                settings::get::<WorkspaceSettings>(cx).active_pane_magnification;
707
708            let mut remaining_flex = 0.;
709
710            if active_pane_magnification != 1. {
711                let active_pane_flex = self
712                    .active_pane_ix
713                    .map(|_| active_pane_magnification)
714                    .unwrap_or(1.);
715                remaining_flex += self.children.len() as f32 - 1. + active_pane_flex;
716            } else {
717                for flex in self.flexes.borrow().iter() {
718                    remaining_flex += flex;
719                }
720            }
721
722            let mut cross_axis_max: f32 = 0.0;
723            let mut remaining_space = constraint.max_along(self.axis);
724
725            if remaining_space.is_infinite() {
726                panic!("flex contains flexible children but has an infinite constraint along the flex axis");
727            }
728
729            self.layout_children(
730                active_pane_magnification,
731                constraint,
732                &mut remaining_space,
733                &mut remaining_flex,
734                &mut cross_axis_max,
735                view,
736                cx,
737            );
738
739            let mut size = match self.axis {
740                Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
741                Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
742            };
743
744            if constraint.min.x().is_finite() {
745                size.set_x(size.x().max(constraint.min.x()));
746            }
747            if constraint.min.y().is_finite() {
748                size.set_y(size.y().max(constraint.min.y()));
749            }
750
751            if size.x() > constraint.max.x() {
752                size.set_x(constraint.max.x());
753            }
754            if size.y() > constraint.max.y() {
755                size.set_y(constraint.max.y());
756            }
757
758            (size, remaining_space)
759        }
760
761        fn paint(
762            &mut self,
763            scene: &mut SceneBuilder,
764            bounds: RectF,
765            visible_bounds: RectF,
766            remaining_space: &mut Self::LayoutState,
767            view: &mut Workspace,
768            cx: &mut ViewContext<Workspace>,
769        ) -> Self::PaintState {
770            let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.;
771            let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
772
773            let overflowing = *remaining_space < 0.;
774            if overflowing {
775                scene.push_layer(Some(visible_bounds));
776            }
777
778            let mut child_origin = bounds.origin();
779
780            let mut bounding_boxes = self.bounding_boxes.borrow_mut();
781            bounding_boxes.clear();
782
783            let mut children_iter = self.children.iter_mut().enumerate().peekable();
784            while let Some((ix, child)) = children_iter.next() {
785                let child_start = child_origin.clone();
786                child.paint(scene, child_origin, visible_bounds, view, cx);
787
788                bounding_boxes.push(Some(RectF::new(child_origin, child.size())));
789
790                match self.axis {
791                    Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
792                    Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
793                }
794
795                if let Some(Some((next_ix, next_child))) = can_resize.then(|| children_iter.peek())
796                {
797                    scene.push_stacking_context(None, None);
798
799                    let handle_origin = match self.axis {
800                        Axis::Horizontal => child_origin - vec2f(HANDLE_HITBOX_SIZE / 2., 0.0),
801                        Axis::Vertical => child_origin - vec2f(0.0, HANDLE_HITBOX_SIZE / 2.),
802                    };
803
804                    let handle_bounds = match self.axis {
805                        Axis::Horizontal => RectF::new(
806                            handle_origin,
807                            vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()),
808                        ),
809                        Axis::Vertical => RectF::new(
810                            handle_origin,
811                            vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE),
812                        ),
813                    };
814
815                    let style = match self.axis {
816                        Axis::Horizontal => CursorStyle::ResizeLeftRight,
817                        Axis::Vertical => CursorStyle::ResizeUpDown,
818                    };
819
820                    scene.push_cursor_region(CursorRegion {
821                        bounds: handle_bounds,
822                        style,
823                    });
824
825                    let axis = self.axis;
826                    let child_size = child.size();
827                    let next_child_size = next_child.size();
828                    let drag_bounds = visible_bounds.clone();
829                    let flexes = self.flexes.borrow();
830                    let current_flex = flexes[ix];
831                    let next_ix = *next_ix;
832                    let next_flex = flexes[next_ix];
833                    drop(flexes);
834                    enum ResizeHandle {}
835                    let mut mouse_region = MouseRegion::new::<ResizeHandle>(
836                        cx.view_id(),
837                        self.basis + ix,
838                        handle_bounds,
839                    );
840                    mouse_region = mouse_region
841                        .on_drag(MouseButton::Left, {
842                            let flexes = self.flexes.clone();
843                            move |drag, workspace: &mut Workspace, cx| {
844                                let min_size = match axis {
845                                    Axis::Horizontal => HORIZONTAL_MIN_SIZE,
846                                    Axis::Vertical => VERTICAL_MIN_SIZE,
847                                };
848                                // Don't allow resizing to less than the minimum size, if elements are already too small
849                                if min_size - 1. > child_size.along(axis)
850                                    || min_size - 1. > next_child_size.along(axis)
851                                {
852                                    return;
853                                }
854
855                                let mut current_target_size =
856                                    (drag.position - child_start).along(axis);
857
858                                let proposed_current_pixel_change =
859                                    current_target_size - child_size.along(axis);
860
861                                if proposed_current_pixel_change < 0. {
862                                    current_target_size = f32::max(current_target_size, min_size);
863                                } else if proposed_current_pixel_change > 0. {
864                                    // TODO: cascade this change to other children if current item is at min size
865                                    let next_target_size = f32::max(
866                                        next_child_size.along(axis) - proposed_current_pixel_change,
867                                        min_size,
868                                    );
869                                    current_target_size = f32::min(
870                                        current_target_size,
871                                        child_size.along(axis) + next_child_size.along(axis)
872                                            - next_target_size,
873                                    );
874                                }
875
876                                let current_pixel_change =
877                                    current_target_size - child_size.along(axis);
878                                let flex_change =
879                                    current_pixel_change / drag_bounds.length_along(axis);
880                                let current_target_flex = current_flex + flex_change;
881                                let next_target_flex = next_flex - flex_change;
882
883                                let mut borrow = flexes.borrow_mut();
884                                *borrow.get_mut(ix).unwrap() = current_target_flex;
885                                *borrow.get_mut(next_ix).unwrap() = next_target_flex;
886
887                                workspace.schedule_serialize(cx);
888                                cx.notify();
889                            }
890                        })
891                        .on_click(MouseButton::Left, {
892                            let flexes = self.flexes.clone();
893                            move |e, v: &mut Workspace, cx| {
894                                if e.click_count >= 2 {
895                                    let mut borrow = flexes.borrow_mut();
896                                    *borrow = vec![1.; borrow.len()];
897                                    v.schedule_serialize(cx);
898                                    cx.notify();
899                                }
900                            }
901                        });
902                    scene.push_mouse_region(mouse_region);
903
904                    scene.pop_stacking_context();
905                }
906            }
907
908            if overflowing {
909                scene.pop_layer();
910            }
911        }
912
913        fn rect_for_text_range(
914            &self,
915            range_utf16: Range<usize>,
916            _: RectF,
917            _: RectF,
918            _: &Self::LayoutState,
919            _: &Self::PaintState,
920            view: &Workspace,
921            cx: &ViewContext<Workspace>,
922        ) -> Option<RectF> {
923            self.children
924                .iter()
925                .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
926        }
927
928        fn debug(
929            &self,
930            bounds: RectF,
931            _: &Self::LayoutState,
932            _: &Self::PaintState,
933            view: &Workspace,
934            cx: &ViewContext<Workspace>,
935        ) -> json::Value {
936            serde_json::json!({
937                "type": "PaneAxis",
938                "bounds": bounds.to_json(),
939                "axis": self.axis.to_json(),
940                "flexes": *self.flexes.borrow(),
941                "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
942            })
943        }
944    }
945}