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, 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        AnyElement, Axis, CursorRegion, Element, LayoutContext, MouseRegion, RectFExt,
535        SceneBuilder, SizeConstraint, Vector2FExt, ViewContext,
536    };
537
538    use crate::{
539        pane_group::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE},
540        Workspace, WorkspaceSettings,
541    };
542
543    pub struct PaneAxisElement {
544        axis: Axis,
545        basis: usize,
546        active_pane_ix: Option<usize>,
547        flexes: Rc<RefCell<Vec<f32>>>,
548        children: Vec<AnyElement<Workspace>>,
549    }
550
551    impl PaneAxisElement {
552        pub fn new(axis: Axis, basis: usize, flexes: Rc<RefCell<Vec<f32>>>) -> Self {
553            Self {
554                axis,
555                basis,
556                flexes,
557                active_pane_ix: None,
558                children: Default::default(),
559            }
560        }
561
562        pub fn set_active_pane(&mut self, active_pane_ix: Option<usize>) {
563            self.active_pane_ix = active_pane_ix;
564        }
565
566        fn layout_children(
567            &mut self,
568            active_pane_magnification: f32,
569            constraint: SizeConstraint,
570            remaining_space: &mut f32,
571            remaining_flex: &mut f32,
572            cross_axis_max: &mut f32,
573            view: &mut Workspace,
574            cx: &mut LayoutContext<Workspace>,
575        ) {
576            let flexes = self.flexes.borrow();
577            let cross_axis = self.axis.invert();
578            for (ix, child) in self.children.iter_mut().enumerate() {
579                let flex = if active_pane_magnification != 1. {
580                    if let Some(active_pane_ix) = self.active_pane_ix {
581                        if ix == active_pane_ix {
582                            active_pane_magnification
583                        } else {
584                            1.
585                        }
586                    } else {
587                        1.
588                    }
589                } else {
590                    flexes[ix]
591                };
592
593                let child_size = if *remaining_flex == 0.0 {
594                    *remaining_space
595                } else {
596                    let space_per_flex = *remaining_space / *remaining_flex;
597                    space_per_flex * flex
598                };
599
600                let child_constraint = match self.axis {
601                    Axis::Horizontal => SizeConstraint::new(
602                        vec2f(child_size, constraint.min.y()),
603                        vec2f(child_size, constraint.max.y()),
604                    ),
605                    Axis::Vertical => SizeConstraint::new(
606                        vec2f(constraint.min.x(), child_size),
607                        vec2f(constraint.max.x(), child_size),
608                    ),
609                };
610                let child_size = child.layout(child_constraint, view, cx);
611                *remaining_space -= child_size.along(self.axis);
612                *remaining_flex -= flex;
613                *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
614            }
615        }
616    }
617
618    impl Extend<AnyElement<Workspace>> for PaneAxisElement {
619        fn extend<T: IntoIterator<Item = AnyElement<Workspace>>>(&mut self, children: T) {
620            self.children.extend(children);
621        }
622    }
623
624    impl Element<Workspace> for PaneAxisElement {
625        type LayoutState = f32;
626        type PaintState = ();
627
628        fn layout(
629            &mut self,
630            constraint: SizeConstraint,
631            view: &mut Workspace,
632            cx: &mut LayoutContext<Workspace>,
633        ) -> (Vector2F, Self::LayoutState) {
634            debug_assert!(self.children.len() == self.flexes.borrow().len());
635
636            let active_pane_magnification =
637                settings::get::<WorkspaceSettings>(cx).active_pane_magnification;
638
639            let mut remaining_flex = 0.;
640
641            if active_pane_magnification != 1. {
642                let active_pane_flex = self
643                    .active_pane_ix
644                    .map(|_| active_pane_magnification)
645                    .unwrap_or(1.);
646                remaining_flex += self.children.len() as f32 - 1. + active_pane_flex;
647            } else {
648                for flex in self.flexes.borrow().iter() {
649                    remaining_flex += flex;
650                }
651            }
652
653            let mut cross_axis_max: f32 = 0.0;
654            let mut remaining_space = constraint.max_along(self.axis);
655
656            if remaining_space.is_infinite() {
657                panic!("flex contains flexible children but has an infinite constraint along the flex axis");
658            }
659
660            self.layout_children(
661                active_pane_magnification,
662                constraint,
663                &mut remaining_space,
664                &mut remaining_flex,
665                &mut cross_axis_max,
666                view,
667                cx,
668            );
669
670            let mut size = match self.axis {
671                Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
672                Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
673            };
674
675            if constraint.min.x().is_finite() {
676                size.set_x(size.x().max(constraint.min.x()));
677            }
678            if constraint.min.y().is_finite() {
679                size.set_y(size.y().max(constraint.min.y()));
680            }
681
682            if size.x() > constraint.max.x() {
683                size.set_x(constraint.max.x());
684            }
685            if size.y() > constraint.max.y() {
686                size.set_y(constraint.max.y());
687            }
688
689            (size, remaining_space)
690        }
691
692        fn paint(
693            &mut self,
694            scene: &mut SceneBuilder,
695            bounds: RectF,
696            visible_bounds: RectF,
697            remaining_space: &mut Self::LayoutState,
698            view: &mut Workspace,
699            cx: &mut ViewContext<Workspace>,
700        ) -> Self::PaintState {
701            let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.;
702            let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
703
704            let overflowing = *remaining_space < 0.;
705            if overflowing {
706                scene.push_layer(Some(visible_bounds));
707            }
708
709            let mut child_origin = bounds.origin();
710
711            let mut children_iter = self.children.iter_mut().enumerate().peekable();
712            while let Some((ix, child)) = children_iter.next() {
713                let child_start = child_origin.clone();
714                child.paint(scene, child_origin, visible_bounds, view, cx);
715
716                match self.axis {
717                    Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
718                    Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
719                }
720
721                if let Some(Some((next_ix, next_child))) = can_resize.then(|| children_iter.peek())
722                {
723                    scene.push_stacking_context(None, None);
724
725                    let handle_origin = match self.axis {
726                        Axis::Horizontal => child_origin - vec2f(HANDLE_HITBOX_SIZE / 2., 0.0),
727                        Axis::Vertical => child_origin - vec2f(0.0, HANDLE_HITBOX_SIZE / 2.),
728                    };
729
730                    let handle_bounds = match self.axis {
731                        Axis::Horizontal => RectF::new(
732                            handle_origin,
733                            vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()),
734                        ),
735                        Axis::Vertical => RectF::new(
736                            handle_origin,
737                            vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE),
738                        ),
739                    };
740
741                    let style = match self.axis {
742                        Axis::Horizontal => CursorStyle::ResizeLeftRight,
743                        Axis::Vertical => CursorStyle::ResizeUpDown,
744                    };
745
746                    scene.push_cursor_region(CursorRegion {
747                        bounds: handle_bounds,
748                        style,
749                    });
750
751                    let axis = self.axis;
752                    let child_size = child.size();
753                    let next_child_size = next_child.size();
754                    let drag_bounds = visible_bounds.clone();
755                    let flexes = self.flexes.clone();
756                    let current_flex = flexes.borrow()[ix];
757                    let next_ix = *next_ix;
758                    let next_flex = flexes.borrow()[next_ix];
759                    enum ResizeHandle {}
760                    let mut mouse_region = MouseRegion::new::<ResizeHandle>(
761                        cx.view_id(),
762                        self.basis + ix,
763                        handle_bounds,
764                    );
765                    mouse_region = mouse_region.on_drag(
766                        MouseButton::Left,
767                        move |drag, workspace: &mut Workspace, cx| {
768                            let min_size = match axis {
769                                Axis::Horizontal => HORIZONTAL_MIN_SIZE,
770                                Axis::Vertical => VERTICAL_MIN_SIZE,
771                            };
772                            // Don't allow resizing to less than the minimum size, if elements are already too small
773                            if min_size - 1. > child_size.along(axis)
774                                || min_size - 1. > next_child_size.along(axis)
775                            {
776                                return;
777                            }
778
779                            let mut current_target_size = (drag.position - child_start).along(axis);
780
781                            let proposed_current_pixel_change =
782                                current_target_size - child_size.along(axis);
783
784                            if proposed_current_pixel_change < 0. {
785                                current_target_size = f32::max(current_target_size, min_size);
786                            } else if proposed_current_pixel_change > 0. {
787                                // TODO: cascade this change to other children if current item is at min size
788                                let next_target_size = f32::max(
789                                    next_child_size.along(axis) - proposed_current_pixel_change,
790                                    min_size,
791                                );
792                                current_target_size = f32::min(
793                                    current_target_size,
794                                    child_size.along(axis) + next_child_size.along(axis)
795                                        - next_target_size,
796                                );
797                            }
798
799                            let current_pixel_change = current_target_size - child_size.along(axis);
800                            let flex_change = current_pixel_change / drag_bounds.length_along(axis);
801                            let current_target_flex = current_flex + flex_change;
802                            let next_target_flex = next_flex - flex_change;
803
804                            let mut borrow = flexes.borrow_mut();
805                            *borrow.get_mut(ix).unwrap() = current_target_flex;
806                            *borrow.get_mut(next_ix).unwrap() = next_target_flex;
807
808                            workspace.schedule_serialize(cx);
809                            cx.notify();
810                        },
811                    );
812                    scene.push_mouse_region(mouse_region);
813
814                    scene.pop_stacking_context();
815                }
816            }
817
818            if overflowing {
819                scene.pop_layer();
820            }
821        }
822
823        fn rect_for_text_range(
824            &self,
825            range_utf16: Range<usize>,
826            _: RectF,
827            _: RectF,
828            _: &Self::LayoutState,
829            _: &Self::PaintState,
830            view: &Workspace,
831            cx: &ViewContext<Workspace>,
832        ) -> Option<RectF> {
833            self.children
834                .iter()
835                .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
836        }
837
838        fn debug(
839            &self,
840            bounds: RectF,
841            _: &Self::LayoutState,
842            _: &Self::PaintState,
843            view: &Workspace,
844            cx: &ViewContext<Workspace>,
845        ) -> json::Value {
846            serde_json::json!({
847                "type": "PaneAxis",
848                "bounds": bounds.to_json(),
849                "axis": self.axis.to_json(),
850                "flexes": *self.flexes.borrow(),
851                "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
852            })
853        }
854    }
855}