pane_group.rs

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