pane_group.rs

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