pane_group.rs

   1use crate::{pane_group::element::pane_axis, AppState, FollowerState, Pane, Workspace};
   2use anyhow::{anyhow, Result};
   3use call::{ActiveCall, ParticipantLocation};
   4use collections::HashMap;
   5use gpui::{
   6    point, size, AnyWeakView, Axis, Bounds, Entity as _, IntoElement, Model, Pixels, Point, View,
   7    ViewContext,
   8};
   9use parking_lot::Mutex;
  10use project::Project;
  11use serde::Deserialize;
  12use std::sync::Arc;
  13use ui::{prelude::*, Button};
  14
  15const HANDLE_HITBOX_SIZE: f32 = 10.0; //todo!(change this back to 4)
  16const HORIZONTAL_MIN_SIZE: f32 = 80.;
  17const VERTICAL_MIN_SIZE: f32 = 100.;
  18
  19#[derive(Clone, PartialEq)]
  20pub struct PaneGroup {
  21    pub(crate) root: Member,
  22}
  23
  24impl PaneGroup {
  25    pub(crate) fn with_root(root: Member) -> Self {
  26        Self { root }
  27    }
  28
  29    pub fn new(pane: View<Pane>) -> Self {
  30        Self {
  31            root: Member::Pane(pane),
  32        }
  33    }
  34
  35    pub fn split(
  36        &mut self,
  37        old_pane: &View<Pane>,
  38        new_pane: &View<Pane>,
  39        direction: SplitDirection,
  40    ) -> Result<()> {
  41        match &mut self.root {
  42            Member::Pane(pane) => {
  43                if pane == old_pane {
  44                    self.root = Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
  45                    Ok(())
  46                } else {
  47                    Err(anyhow!("Pane not found"))
  48                }
  49            }
  50            Member::Axis(axis) => axis.split(old_pane, new_pane, direction),
  51        }
  52    }
  53
  54    pub fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
  55        match &self.root {
  56            Member::Pane(_) => None,
  57            Member::Axis(axis) => axis.bounding_box_for_pane(pane),
  58        }
  59    }
  60
  61    pub fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&View<Pane>> {
  62        match &self.root {
  63            Member::Pane(pane) => Some(pane),
  64            Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
  65        }
  66    }
  67
  68    /// Returns:
  69    /// - Ok(true) if it found and removed a pane
  70    /// - Ok(false) if it found but did not remove the pane
  71    /// - Err(_) if it did not find the pane
  72    pub fn remove(&mut self, pane: &View<Pane>) -> Result<bool> {
  73        match &mut self.root {
  74            Member::Pane(_) => Ok(false),
  75            Member::Axis(axis) => {
  76                if let Some(last_pane) = axis.remove(pane)? {
  77                    self.root = last_pane;
  78                }
  79                Ok(true)
  80            }
  81        }
  82    }
  83
  84    pub fn swap(&mut self, from: &View<Pane>, to: &View<Pane>) {
  85        match &mut self.root {
  86            Member::Pane(_) => {}
  87            Member::Axis(axis) => axis.swap(from, to),
  88        };
  89    }
  90
  91    pub(crate) fn render(
  92        &self,
  93        project: &Model<Project>,
  94        follower_states: &HashMap<View<Pane>, FollowerState>,
  95        active_call: Option<&Model<ActiveCall>>,
  96        active_pane: &View<Pane>,
  97        zoomed: Option<&AnyWeakView>,
  98        app_state: &Arc<AppState>,
  99        cx: &mut ViewContext<Workspace>,
 100    ) -> impl IntoElement {
 101        self.root.render(
 102            project,
 103            0,
 104            follower_states,
 105            active_call,
 106            active_pane,
 107            zoomed,
 108            app_state,
 109            cx,
 110        )
 111    }
 112
 113    pub(crate) fn panes(&self) -> Vec<&View<Pane>> {
 114        let mut panes = Vec::new();
 115        self.root.collect_panes(&mut panes);
 116        panes
 117    }
 118
 119    pub(crate) fn first_pane(&self) -> View<Pane> {
 120        self.root.first_pane()
 121    }
 122}
 123
 124#[derive(Clone, PartialEq)]
 125pub(crate) enum Member {
 126    Axis(PaneAxis),
 127    Pane(View<Pane>),
 128}
 129
 130impl Member {
 131    fn new_axis(old_pane: View<Pane>, new_pane: View<Pane>, direction: SplitDirection) -> Self {
 132        use Axis::*;
 133        use SplitDirection::*;
 134
 135        let axis = match direction {
 136            Up | Down => Vertical,
 137            Left | Right => Horizontal,
 138        };
 139
 140        let members = match direction {
 141            Up | Left => vec![Member::Pane(new_pane), Member::Pane(old_pane)],
 142            Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)],
 143        };
 144
 145        Member::Axis(PaneAxis::new(axis, members))
 146    }
 147
 148    fn contains(&self, needle: &View<Pane>) -> bool {
 149        match self {
 150            Member::Axis(axis) => axis.members.iter().any(|member| member.contains(needle)),
 151            Member::Pane(pane) => pane == needle,
 152        }
 153    }
 154
 155    fn first_pane(&self) -> View<Pane> {
 156        match self {
 157            Member::Axis(axis) => axis.members[0].first_pane(),
 158            Member::Pane(pane) => pane.clone(),
 159        }
 160    }
 161
 162    pub fn render(
 163        &self,
 164        project: &Model<Project>,
 165        basis: usize,
 166        follower_states: &HashMap<View<Pane>, FollowerState>,
 167        active_call: Option<&Model<ActiveCall>>,
 168        active_pane: &View<Pane>,
 169        zoomed: Option<&AnyWeakView>,
 170        app_state: &Arc<AppState>,
 171        cx: &mut ViewContext<Workspace>,
 172    ) -> impl IntoElement {
 173        match self {
 174            Member::Pane(pane) => {
 175                let leader = follower_states.get(pane).and_then(|state| {
 176                    let room = active_call?.read(cx).room()?.read(cx);
 177                    room.remote_participant_for_peer_id(state.leader_id)
 178                });
 179
 180                let mut leader_border = None;
 181                let mut leader_status_box = None;
 182                if let Some(leader) = &leader {
 183                    let mut leader_color = cx
 184                        .theme()
 185                        .players()
 186                        .color_for_participant(leader.participant_index.0)
 187                        .cursor;
 188                    leader_color.fade_out(0.3);
 189                    leader_border = Some(leader_color);
 190
 191                    leader_status_box = match leader.location {
 192                        ParticipantLocation::SharedProject {
 193                            project_id: leader_project_id,
 194                        } => {
 195                            if Some(leader_project_id) == project.read(cx).remote_id() {
 196                                None
 197                            } else {
 198                                let leader_user = leader.user.clone();
 199                                let leader_user_id = leader.user.id;
 200                                Some(
 201                                    Button::new(
 202                                        ("leader-status", pane.entity_id()),
 203                                        format!(
 204                                            "Follow {} to their active project",
 205                                            leader_user.github_login,
 206                                        ),
 207                                    )
 208                                    .on_click(cx.listener(
 209                                        move |this, _, cx| {
 210                                            crate::join_remote_project(
 211                                                leader_project_id,
 212                                                leader_user_id,
 213                                                this.app_state().clone(),
 214                                                cx,
 215                                            )
 216                                            .detach_and_log_err(cx);
 217                                        },
 218                                    )),
 219                                )
 220                            }
 221                        }
 222                        ParticipantLocation::UnsharedProject => Some(Button::new(
 223                            ("leader-status", pane.entity_id()),
 224                            format!(
 225                                "{} is viewing an unshared Zed project",
 226                                leader.user.github_login
 227                            ),
 228                        )),
 229                        ParticipantLocation::External => Some(Button::new(
 230                            ("leader-status", pane.entity_id()),
 231                            format!(
 232                                "{} is viewing a window outside of Zed",
 233                                leader.user.github_login
 234                            ),
 235                        )),
 236                    };
 237                }
 238
 239                div()
 240                    .relative()
 241                    .size_full()
 242                    .child(pane.clone())
 243                    .when_some(leader_border, |this, color| {
 244                        this.border_2().border_color(color)
 245                    })
 246                    .when_some(leader_status_box, |this, status_box| {
 247                        this.child(
 248                            div()
 249                                .absolute()
 250                                .w_96()
 251                                .bottom_3()
 252                                .right_3()
 253                                .z_index(1)
 254                                .child(status_box),
 255                        )
 256                    })
 257                    .into_any()
 258
 259                // let el = div()
 260                //     .flex()
 261                //     .flex_1()
 262                //     .gap_px()
 263                //     .w_full()
 264                //     .h_full()
 265                //     .bg(cx.theme().colors().editor)
 266                //     .children();
 267            }
 268            Member::Axis(axis) => axis
 269                .render(
 270                    project,
 271                    basis + 1,
 272                    follower_states,
 273                    active_pane,
 274                    zoomed,
 275                    app_state,
 276                    cx,
 277                )
 278                .into_any(),
 279        }
 280
 281        // enum FollowIntoExternalProject {}
 282
 283        // match self {
 284        //     Member::Pane(pane) => {
 285        //         let pane_element = if Some(&**pane) == zoomed {
 286        //             Empty::new().into_any()
 287        //         } else {
 288        //             ChildView::new(pane, cx).into_any()
 289        //         };
 290
 291        //         let leader = follower_states.get(pane).and_then(|state| {
 292        //             let room = active_call?.read(cx).room()?.read(cx);
 293        //             room.remote_participant_for_peer_id(state.leader_id)
 294        //         });
 295
 296        //         let mut leader_border = Border::default();
 297        //         let mut leader_status_box = None;
 298        //         if let Some(leader) = &leader {
 299        //             let leader_color = theme
 300        //                 .editor
 301        //                 .selection_style_for_room_participant(leader.participant_index.0)
 302        //                 .cursor;
 303        //             leader_border = Border::all(theme.workspace.leader_border_width, leader_color);
 304        //             leader_border
 305        //                 .color
 306        //                 .fade_out(1. - theme.workspace.leader_border_opacity);
 307        //             leader_border.overlay = true;
 308
 309        //             leader_status_box = match leader.location {
 310        //                 ParticipantLocation::SharedProject {
 311        //                     project_id: leader_project_id,
 312        //                 } => {
 313        //                     if Some(leader_project_id) == project.read(cx).remote_id() {
 314        //                         None
 315        //                     } else {
 316        //                         let leader_user = leader.user.clone();
 317        //                         let leader_user_id = leader.user.id;
 318        //                         Some(
 319        //                             MouseEventHandler::new::<FollowIntoExternalProject, _>(
 320        //                                 pane.id(),
 321        //                                 cx,
 322        //                                 |_, _| {
 323        //                                     Label::new(
 324        //                                         format!(
 325        //                                             "Follow {} to their active project",
 326        //                                             leader_user.github_login,
 327        //                                         ),
 328        //                                         theme
 329        //                                             .workspace
 330        //                                             .external_location_message
 331        //                                             .text
 332        //                                             .clone(),
 333        //                                     )
 334        //                                     .contained()
 335        //                                     .with_style(
 336        //                                         theme.workspace.external_location_message.container,
 337        //                                     )
 338        //                                 },
 339        //                             )
 340        //                             .with_cursor_style(CursorStyle::PointingHand)
 341        //                             .on_click(MouseButton::Left, move |_, this, cx| {
 342        //                                 crate::join_remote_project(
 343        //                                     leader_project_id,
 344        //                                     leader_user_id,
 345        //                                     this.app_state().clone(),
 346        //                                     cx,
 347        //                                 )
 348        //                                 .detach_and_log_err(cx);
 349        //                             })
 350        //                             .aligned()
 351        //                             .bottom()
 352        //                             .right()
 353        //                             .into_any(),
 354        //                         )
 355        //                     }
 356        //                 }
 357        //                 ParticipantLocation::UnsharedProject => Some(
 358        //                     Label::new(
 359        //                         format!(
 360        //                             "{} is viewing an unshared Zed project",
 361        //                             leader.user.github_login
 362        //                         ),
 363        //                         theme.workspace.external_location_message.text.clone(),
 364        //                     )
 365        //                     .contained()
 366        //                     .with_style(theme.workspace.external_location_message.container)
 367        //                     .aligned()
 368        //                     .bottom()
 369        //                     .right()
 370        //                     .into_any(),
 371        //                 ),
 372        //                 ParticipantLocation::External => Some(
 373        //                     Label::new(
 374        //                         format!(
 375        //                             "{} is viewing a window outside of Zed",
 376        //                             leader.user.github_login
 377        //                         ),
 378        //                         theme.workspace.external_location_message.text.clone(),
 379        //                     )
 380        //                     .contained()
 381        //                     .with_style(theme.workspace.external_location_message.container)
 382        //                     .aligned()
 383        //                     .bottom()
 384        //                     .right()
 385        //                     .into_any(),
 386        //                 ),
 387        //             };
 388        //         }
 389
 390        //         Stack::new()
 391        //             .with_child(pane_element.contained().with_border(leader_border))
 392        //             .with_children(leader_status_box)
 393        //             .into_any()
 394        //     }
 395        //     Member::Axis(axis) => axis.render(
 396        //         project,
 397        //         basis + 1,
 398        //         theme,
 399        //         follower_states,
 400        //         active_call,
 401        //         active_pane,
 402        //         zoomed,
 403        //         app_state,
 404        //         cx,
 405        //     ),
 406        // }
 407    }
 408
 409    fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a View<Pane>>) {
 410        match self {
 411            Member::Axis(axis) => {
 412                for member in &axis.members {
 413                    member.collect_panes(panes);
 414                }
 415            }
 416            Member::Pane(pane) => panes.push(pane),
 417        }
 418    }
 419}
 420
 421#[derive(Clone)]
 422pub(crate) struct PaneAxis {
 423    pub axis: Axis,
 424    pub members: Vec<Member>,
 425    pub flexes: Arc<Mutex<Vec<f32>>>,
 426    pub bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
 427}
 428
 429impl PartialEq for PaneAxis {
 430    fn eq(&self, other: &Self) -> bool {
 431        todo!()
 432    }
 433}
 434
 435impl PaneAxis {
 436    pub fn new(axis: Axis, members: Vec<Member>) -> Self {
 437        let flexes = Arc::new(Mutex::new(vec![1.; members.len()]));
 438        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
 439        Self {
 440            axis,
 441            members,
 442            flexes,
 443            bounding_boxes,
 444        }
 445    }
 446
 447    pub fn load(axis: Axis, members: Vec<Member>, flexes: Option<Vec<f32>>) -> Self {
 448        let flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]);
 449        debug_assert!(members.len() == flexes.len());
 450
 451        let flexes = Arc::new(Mutex::new(flexes));
 452        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
 453        Self {
 454            axis,
 455            members,
 456            flexes,
 457            bounding_boxes,
 458        }
 459    }
 460
 461    fn split(
 462        &mut self,
 463        old_pane: &View<Pane>,
 464        new_pane: &View<Pane>,
 465        direction: SplitDirection,
 466    ) -> Result<()> {
 467        for (mut idx, member) in self.members.iter_mut().enumerate() {
 468            match member {
 469                Member::Axis(axis) => {
 470                    if axis.split(old_pane, new_pane, direction).is_ok() {
 471                        return Ok(());
 472                    }
 473                }
 474                Member::Pane(pane) => {
 475                    if pane == old_pane {
 476                        if direction.axis() == self.axis {
 477                            if direction.increasing() {
 478                                idx += 1;
 479                            }
 480
 481                            self.members.insert(idx, Member::Pane(new_pane.clone()));
 482                            *self.flexes.lock() = vec![1.; self.members.len()];
 483                        } else {
 484                            *member =
 485                                Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
 486                        }
 487                        return Ok(());
 488                    }
 489                }
 490            }
 491        }
 492        Err(anyhow!("Pane not found"))
 493    }
 494
 495    fn remove(&mut self, pane_to_remove: &View<Pane>) -> Result<Option<Member>> {
 496        let mut found_pane = false;
 497        let mut remove_member = None;
 498        for (idx, member) in self.members.iter_mut().enumerate() {
 499            match member {
 500                Member::Axis(axis) => {
 501                    if let Ok(last_pane) = axis.remove(pane_to_remove) {
 502                        if let Some(last_pane) = last_pane {
 503                            *member = last_pane;
 504                        }
 505                        found_pane = true;
 506                        break;
 507                    }
 508                }
 509                Member::Pane(pane) => {
 510                    if pane == pane_to_remove {
 511                        found_pane = true;
 512                        remove_member = Some(idx);
 513                        break;
 514                    }
 515                }
 516            }
 517        }
 518
 519        if found_pane {
 520            if let Some(idx) = remove_member {
 521                self.members.remove(idx);
 522                *self.flexes.lock() = vec![1.; self.members.len()];
 523            }
 524
 525            if self.members.len() == 1 {
 526                let result = self.members.pop();
 527                *self.flexes.lock() = vec![1.; self.members.len()];
 528                Ok(result)
 529            } else {
 530                Ok(None)
 531            }
 532        } else {
 533            Err(anyhow!("Pane not found"))
 534        }
 535    }
 536
 537    fn swap(&mut self, from: &View<Pane>, to: &View<Pane>) {
 538        for member in self.members.iter_mut() {
 539            match member {
 540                Member::Axis(axis) => axis.swap(from, to),
 541                Member::Pane(pane) => {
 542                    if pane == from {
 543                        *member = Member::Pane(to.clone());
 544                    } else if pane == to {
 545                        *member = Member::Pane(from.clone())
 546                    }
 547                }
 548            }
 549        }
 550    }
 551
 552    fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
 553        debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
 554
 555        for (idx, member) in self.members.iter().enumerate() {
 556            match member {
 557                Member::Pane(found) => {
 558                    if pane == found {
 559                        return self.bounding_boxes.lock()[idx];
 560                    }
 561                }
 562                Member::Axis(axis) => {
 563                    if let Some(rect) = axis.bounding_box_for_pane(pane) {
 564                        return Some(rect);
 565                    }
 566                }
 567            }
 568        }
 569        None
 570    }
 571
 572    fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&View<Pane>> {
 573        debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
 574
 575        let bounding_boxes = self.bounding_boxes.lock();
 576
 577        for (idx, member) in self.members.iter().enumerate() {
 578            if let Some(coordinates) = bounding_boxes[idx] {
 579                if coordinates.contains(&coordinate) {
 580                    return match member {
 581                        Member::Pane(found) => Some(found),
 582                        Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
 583                    };
 584                }
 585            }
 586        }
 587        None
 588    }
 589
 590    fn render(
 591        &self,
 592        project: &Model<Project>,
 593        basis: usize,
 594        follower_states: &HashMap<View<Pane>, FollowerState>,
 595        active_pane: &View<Pane>,
 596        zoomed: Option<&AnyWeakView>,
 597        app_state: &Arc<AppState>,
 598        cx: &mut ViewContext<Workspace>,
 599    ) -> gpui::AnyElement {
 600        debug_assert!(self.members.len() == self.flexes.lock().len());
 601        let mut active_pane_ix = None;
 602
 603        pane_axis(
 604            self.axis,
 605            basis,
 606            self.flexes.clone(),
 607            self.bounding_boxes.clone(),
 608        )
 609        .children(self.members.iter().enumerate().map(|(ix, member)| {
 610            if member.contains(active_pane) {
 611                active_pane_ix = Some(ix);
 612            }
 613
 614            match member {
 615                Member::Axis(axis) => axis
 616                    .render(
 617                        project,
 618                        (basis + ix) * 10,
 619                        follower_states,
 620                        active_pane,
 621                        zoomed,
 622                        app_state,
 623                        cx,
 624                    )
 625                    .into_any_element(),
 626                Member::Pane(pane) => div()
 627                    .size_full()
 628                    .border()
 629                    .child(pane.clone())
 630                    .into_any_element(),
 631            }
 632        }))
 633        .with_active_pane(active_pane_ix)
 634        .into_any_element()
 635    }
 636}
 637
 638#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
 639pub enum SplitDirection {
 640    Up,
 641    Down,
 642    Left,
 643    Right,
 644}
 645
 646impl SplitDirection {
 647    pub fn all() -> [Self; 4] {
 648        [Self::Up, Self::Down, Self::Left, Self::Right]
 649    }
 650
 651    pub fn edge(&self, rect: Bounds<Pixels>) -> Pixels {
 652        match self {
 653            Self::Up => rect.origin.y,
 654            Self::Down => rect.lower_left().y,
 655            Self::Left => rect.lower_left().x,
 656            Self::Right => rect.lower_right().x,
 657        }
 658    }
 659
 660    pub fn along_edge(&self, bounds: Bounds<Pixels>, length: Pixels) -> Bounds<Pixels> {
 661        match self {
 662            Self::Up => Bounds {
 663                origin: bounds.origin,
 664                size: size(bounds.size.width, length),
 665            },
 666            Self::Down => Bounds {
 667                origin: point(bounds.lower_left().x, bounds.lower_left().y - length),
 668                size: size(bounds.size.width, length),
 669            },
 670            Self::Left => Bounds {
 671                origin: bounds.origin,
 672                size: size(length, bounds.size.height),
 673            },
 674            Self::Right => Bounds {
 675                origin: point(bounds.lower_right().x - length, bounds.lower_left().y),
 676                size: size(length, bounds.size.height),
 677            },
 678        }
 679    }
 680
 681    pub fn axis(&self) -> Axis {
 682        match self {
 683            Self::Up | Self::Down => Axis::Vertical,
 684            Self::Left | Self::Right => Axis::Horizontal,
 685        }
 686    }
 687
 688    pub fn increasing(&self) -> bool {
 689        match self {
 690            Self::Left | Self::Up => false,
 691            Self::Down | Self::Right => true,
 692        }
 693    }
 694}
 695
 696mod element {
 697
 698    use std::{cell::RefCell, iter, rc::Rc, sync::Arc};
 699
 700    use gpui::{
 701        px, relative, Along, AnyElement, Axis, Bounds, CursorStyle, Element, IntoElement,
 702        MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Style, WindowContext,
 703    };
 704    use parking_lot::Mutex;
 705    use smallvec::SmallVec;
 706
 707    use super::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE};
 708
 709    pub fn pane_axis(
 710        axis: Axis,
 711        basis: usize,
 712        flexes: Arc<Mutex<Vec<f32>>>,
 713        bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
 714    ) -> PaneAxisElement {
 715        PaneAxisElement {
 716            axis,
 717            basis,
 718            flexes,
 719            bounding_boxes,
 720            children: SmallVec::new(),
 721            active_pane_ix: None,
 722        }
 723    }
 724
 725    pub struct PaneAxisElement {
 726        axis: Axis,
 727        basis: usize,
 728        flexes: Arc<Mutex<Vec<f32>>>,
 729        bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
 730        children: SmallVec<[AnyElement; 2]>,
 731        active_pane_ix: Option<usize>,
 732    }
 733
 734    impl PaneAxisElement {
 735        pub fn with_active_pane(mut self, active_pane_ix: Option<usize>) -> Self {
 736            self.active_pane_ix = active_pane_ix;
 737            self
 738        }
 739
 740        fn compute_resize(
 741            flexes: &Arc<Mutex<Vec<f32>>>,
 742            e: &MouseMoveEvent,
 743            ix: usize,
 744            axis: Axis,
 745            axis_bounds: Bounds<Pixels>,
 746            cx: &mut WindowContext,
 747        ) {
 748            let min_size = match axis {
 749                Axis::Horizontal => px(HORIZONTAL_MIN_SIZE),
 750                Axis::Vertical => px(VERTICAL_MIN_SIZE),
 751            };
 752            let mut flexes = flexes.lock();
 753            debug_assert!(flex_values_in_bounds(flexes.as_slice()));
 754
 755            let size = move |ix, flexes: &[f32]| {
 756                axis_bounds.size.along(axis) * (flexes[ix] / flexes.len() as f32)
 757            };
 758
 759            // Don't allow resizing to less than the minimum size, if elements are already too small
 760            if min_size - px(1.) > size(ix, flexes.as_slice()) {
 761                return;
 762            }
 763
 764            let mut proposed_current_pixel_change =
 765                (e.position - axis_bounds.origin).along(axis) - size(ix, flexes.as_slice());
 766
 767            let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
 768                let flex_change = pixel_dx / axis_bounds.size.along(axis);
 769                let current_target_flex = flexes[target_ix] + flex_change;
 770                let next_target_flex = flexes[(target_ix as isize + next) as usize] - flex_change;
 771                (current_target_flex, next_target_flex)
 772            };
 773
 774            let mut successors = iter::from_fn({
 775                let forward = proposed_current_pixel_change > px(0.);
 776                let mut ix_offset = 0;
 777                let len = flexes.len();
 778                move || {
 779                    let result = if forward {
 780                        (ix + 1 + ix_offset < len).then(|| ix + ix_offset)
 781                    } else {
 782                        (ix as isize - ix_offset as isize >= 0).then(|| ix - ix_offset)
 783                    };
 784
 785                    ix_offset += 1;
 786
 787                    result
 788                }
 789            });
 790
 791            while proposed_current_pixel_change.abs() > px(0.) {
 792                let Some(current_ix) = successors.next() else {
 793                    break;
 794                };
 795
 796                let next_target_size = Pixels::max(
 797                    size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change,
 798                    min_size,
 799                );
 800
 801                let current_target_size = Pixels::max(
 802                    size(current_ix, flexes.as_slice()) + size(current_ix + 1, flexes.as_slice())
 803                        - next_target_size,
 804                    min_size,
 805                );
 806
 807                let current_pixel_change =
 808                    current_target_size - size(current_ix, flexes.as_slice());
 809
 810                let (current_target_flex, next_target_flex) =
 811                    flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice());
 812
 813                flexes[current_ix] = current_target_flex;
 814                flexes[current_ix + 1] = next_target_flex;
 815
 816                proposed_current_pixel_change -= current_pixel_change;
 817            }
 818
 819            // todo!(reserialize workspace)
 820            // workspace.schedule_serialize(cx);
 821            cx.notify();
 822        }
 823
 824        fn push_handle(
 825            flexes: Arc<Mutex<Vec<f32>>>,
 826            dragged_handle: Rc<RefCell<Option<usize>>>,
 827            axis: Axis,
 828            ix: usize,
 829            pane_bounds: Bounds<Pixels>,
 830            axis_bounds: Bounds<Pixels>,
 831            cx: &mut WindowContext,
 832        ) {
 833            let handle_bounds = Bounds {
 834                origin: pane_bounds.origin.apply_along(axis, |o| {
 835                    o + pane_bounds.size.along(axis) - Pixels(HANDLE_HITBOX_SIZE / 2.)
 836                }),
 837                size: pane_bounds
 838                    .size
 839                    .apply_along(axis, |_| Pixels(HANDLE_HITBOX_SIZE)),
 840            };
 841
 842            cx.with_z_index(3, |cx| {
 843                if handle_bounds.contains(&cx.mouse_position()) {
 844                    cx.set_cursor_style(match axis {
 845                        Axis::Vertical => CursorStyle::ResizeUpDown,
 846                        Axis::Horizontal => CursorStyle::ResizeLeftRight,
 847                    })
 848                }
 849
 850                cx.add_opaque_layer(handle_bounds);
 851
 852                cx.on_mouse_event({
 853                    let dragged_handle = dragged_handle.clone();
 854                    move |e: &MouseDownEvent, phase, cx| {
 855                        if phase.bubble() && handle_bounds.contains(&e.position) {
 856                            dragged_handle.replace(Some(ix));
 857                        }
 858                    }
 859                });
 860                cx.on_mouse_event(move |e: &MouseMoveEvent, phase, cx| {
 861                    let dragged_handle = dragged_handle.borrow();
 862                    if *dragged_handle == Some(ix) {
 863                        Self::compute_resize(&flexes, e, ix, axis, axis_bounds, cx)
 864                    }
 865                });
 866            });
 867        }
 868    }
 869
 870    impl IntoElement for PaneAxisElement {
 871        type Element = Self;
 872
 873        fn element_id(&self) -> Option<ui::prelude::ElementId> {
 874            Some(self.basis.into())
 875        }
 876
 877        fn into_element(self) -> Self::Element {
 878            self
 879        }
 880    }
 881
 882    impl Element for PaneAxisElement {
 883        type State = Rc<RefCell<Option<usize>>>;
 884
 885        fn layout(
 886            &mut self,
 887            state: Option<Self::State>,
 888            cx: &mut ui::prelude::WindowContext,
 889        ) -> (gpui::LayoutId, Self::State) {
 890            let mut style = Style::default();
 891            style.size.width = relative(1.).into();
 892            style.size.height = relative(1.).into();
 893            let layout_id = cx.request_layout(&style, None);
 894            let dragged_pane = state.unwrap_or_else(|| Rc::new(RefCell::new(None)));
 895            (layout_id, dragged_pane)
 896        }
 897
 898        fn paint(
 899            self,
 900            bounds: gpui::Bounds<ui::prelude::Pixels>,
 901            state: &mut Self::State,
 902            cx: &mut ui::prelude::WindowContext,
 903        ) {
 904            let flexes = self.flexes.lock().clone();
 905            let len = self.children.len();
 906            debug_assert!(flexes.len() == len);
 907            debug_assert!(flex_values_in_bounds(flexes.as_slice()));
 908
 909            let mut origin = bounds.origin;
 910            let space_per_flex = bounds.size.along(self.axis) / len as f32;
 911
 912            let mut bounding_boxes = self.bounding_boxes.lock();
 913            bounding_boxes.clear();
 914
 915            for (ix, child) in self.children.into_iter().enumerate() {
 916                //todo!(active_pane_magnification)
 917                // If usign active pane magnification, need to switch to using
 918                // 1 for all non-active panes, and then the magnification for the
 919                // active pane.
 920                let child_size = bounds
 921                    .size
 922                    .apply_along(self.axis, |_| space_per_flex * flexes[ix]);
 923
 924                let child_bounds = Bounds {
 925                    origin,
 926                    size: child_size,
 927                };
 928                bounding_boxes.push(Some(child_bounds));
 929                cx.with_z_index(0, |cx| {
 930                    child.draw(origin, child_size.into(), cx);
 931                });
 932                cx.with_z_index(1, |cx| {
 933                    if ix < len - 1 {
 934                        Self::push_handle(
 935                            self.flexes.clone(),
 936                            state.clone(),
 937                            self.axis,
 938                            ix,
 939                            child_bounds,
 940                            bounds,
 941                            cx,
 942                        );
 943                    }
 944                });
 945
 946                origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
 947            }
 948
 949            cx.with_z_index(1, |cx| {
 950                cx.on_mouse_event({
 951                    let state = state.clone();
 952                    move |e: &MouseUpEvent, phase, cx| {
 953                        if phase.bubble() {
 954                            state.replace(None);
 955                        }
 956                    }
 957                });
 958            })
 959        }
 960    }
 961
 962    impl ParentElement for PaneAxisElement {
 963        fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement; 2]> {
 964            &mut self.children
 965        }
 966    }
 967
 968    fn flex_values_in_bounds(flexes: &[f32]) -> bool {
 969        (flexes.iter().copied().sum::<f32>() - flexes.len() as f32).abs() < 0.001
 970    }
 971    //     // use std::{cell::RefCell, iter::from_fn, ops::Range, rc::Rc};
 972
 973    //     // use gpui::{
 974    //     //     geometry::{
 975    //     //         rect::Bounds<Pixels>,
 976    //     //         vector::{vec2f, Vector2F},
 977    //     //     },
 978    //     //     json::{self, ToJson},
 979    //     //     platform::{CursorStyle, MouseButton},
 980    //     //     scene::MouseDrag,
 981    //     //     AnyElement, Axis, CursorRegion, Element, EventContext, MouseRegion, Bounds<Pixels>Ext,
 982    //     //     SizeConstraint, Vector2FExt, ViewContext,
 983    //     // };
 984
 985    //     use crate::{
 986    //         pane_group::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE},
 987    //         Workspace, WorkspaceSettings,
 988    //     };
 989
 990    //     pub struct PaneAxisElement {
 991    //         axis: Axis,
 992    //         basis: usize,
 993    //         active_pane_ix: Option<usize>,
 994    //         flexes: Rc<RefCell<Vec<f32>>>,
 995    //         children: Vec<AnyElement<Workspace>>,
 996    //         bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
 997    //     }
 998
 999    //     impl PaneAxisElement {
1000    //         pub fn new(
1001    //             axis: Axis,
1002    //             basis: usize,
1003    //             flexes: Rc<RefCell<Vec<f32>>>,
1004    //             bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
1005    //         ) -> Self {
1006    //             Self {
1007    //                 axis,
1008    //                 basis,
1009    //                 flexes,
1010    //                 bounding_boxes,
1011    //                 active_pane_ix: None,
1012    //                 children: Default::default(),
1013    //             }
1014    //         }
1015
1016    //         pub fn set_active_pane(&mut self, active_pane_ix: Option<usize>) {
1017    //             self.active_pane_ix = active_pane_ix;
1018    //         }
1019
1020    //         fn layout_children(
1021    //             &mut self,
1022    //             active_pane_magnification: f32,
1023    //             constraint: SizeConstraint,
1024    //             remaining_space: &mut f32,
1025    //             remaining_flex: &mut f32,
1026    //             cross_axis_max: &mut f32,
1027    //             view: &mut Workspace,
1028    //             cx: &mut ViewContext<Workspace>,
1029    //         ) {
1030    //             let flexes = self.flexes.borrow();
1031    //             let cross_axis = self.axis.invert();
1032    //             for (ix, child) in self.children.iter_mut().enumerate() {
1033    //                 let flex = if active_pane_magnification != 1. {
1034    //                     if let Some(active_pane_ix) = self.active_pane_ix {
1035    //                         if ix == active_pane_ix {
1036    //                             active_pane_magnification
1037    //                         } else {
1038    //                             1.
1039    //                         }
1040    //                     } else {
1041    //                         1.
1042    //                     }
1043    //                 } else {
1044    //                     flexes[ix]
1045    //                 };
1046
1047    //                 let child_size = if *remaining_flex == 0.0 {
1048    //                     *remaining_space
1049    //                 } else {
1050    //                     let space_per_flex = *remaining_space / *remaining_flex;
1051    //                     space_per_flex * flex
1052    //                 };
1053
1054    //                 let child_constraint = match self.axis {
1055    //                     Axis::Horizontal => SizeConstraint::new(
1056    //                         vec2f(child_size, constraint.min.y()),
1057    //                         vec2f(child_size, constraint.max.y()),
1058    //                     ),
1059    //                     Axis::Vertical => SizeConstraint::new(
1060    //                         vec2f(constraint.min.x(), child_size),
1061    //                         vec2f(constraint.max.x(), child_size),
1062    //                     ),
1063    //                 };
1064    //                 let child_size = child.layout(child_constraint, view, cx);
1065    //                 *remaining_space -= child_size.along(self.axis);
1066    //                 *remaining_flex -= flex;
1067    //                 *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
1068    //             }
1069    //         }
1070
1071    //         fn handle_resize(
1072    //             flexes: Rc<RefCell<Vec<f32>>>,
1073    //             axis: Axis,
1074    //             preceding_ix: usize,
1075    //             child_start: Vector2F,
1076    //             drag_bounds: Bounds<Pixels>,
1077    //         ) -> impl Fn(MouseDrag, &mut Workspace, &mut EventContext<Workspace>) {
1078    //             let size = move |ix, flexes: &[f32]| {
1079    //                 drag_bounds.length_along(axis) * (flexes[ix] / flexes.len() as f32)
1080    //             };
1081
1082    //             move |drag, workspace: &mut Workspace, cx| {
1083    //                 if drag.end {
1084    //                     // TODO: Clear cascading resize state
1085    //                     return;
1086    //                 }
1087    //                 let min_size = match axis {
1088    //                     Axis::Horizontal => HORIZONTAL_MIN_SIZE,
1089    //                     Axis::Vertical => VERTICAL_MIN_SIZE,
1090    //                 };
1091    //                 let mut flexes = flexes.borrow_mut();
1092
1093    //                 // Don't allow resizing to less than the minimum size, if elements are already too small
1094    //                 if min_size - 1. > size(preceding_ix, flexes.as_slice()) {
1095    //                     return;
1096    //                 }
1097
1098    //                 let mut proposed_current_pixel_change = (drag.position - child_start).along(axis)
1099    //                     - size(preceding_ix, flexes.as_slice());
1100
1101    //                 let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
1102    //                     let flex_change = pixel_dx / drag_bounds.length_along(axis);
1103    //                     let current_target_flex = flexes[target_ix] + flex_change;
1104    //                     let next_target_flex =
1105    //                         flexes[(target_ix as isize + next) as usize] - flex_change;
1106    //                     (current_target_flex, next_target_flex)
1107    //                 };
1108
1109    //                 let mut successors = from_fn({
1110    //                     let forward = proposed_current_pixel_change > 0.;
1111    //                     let mut ix_offset = 0;
1112    //                     let len = flexes.len();
1113    //                     move || {
1114    //                         let result = if forward {
1115    //                             (preceding_ix + 1 + ix_offset < len).then(|| preceding_ix + ix_offset)
1116    //                         } else {
1117    //                             (preceding_ix as isize - ix_offset as isize >= 0)
1118    //                                 .then(|| preceding_ix - ix_offset)
1119    //                         };
1120
1121    //                         ix_offset += 1;
1122
1123    //                         result
1124    //                     }
1125    //                 });
1126
1127    //                 while proposed_current_pixel_change.abs() > 0. {
1128    //                     let Some(current_ix) = successors.next() else {
1129    //                         break;
1130    //                     };
1131
1132    //                     let next_target_size = f32::max(
1133    //                         size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change,
1134    //                         min_size,
1135    //                     );
1136
1137    //                     let current_target_size = f32::max(
1138    //                         size(current_ix, flexes.as_slice())
1139    //                             + size(current_ix + 1, flexes.as_slice())
1140    //                             - next_target_size,
1141    //                         min_size,
1142    //                     );
1143
1144    //                     let current_pixel_change =
1145    //                         current_target_size - size(current_ix, flexes.as_slice());
1146
1147    //                     let (current_target_flex, next_target_flex) =
1148    //                         flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice());
1149
1150    //                     flexes[current_ix] = current_target_flex;
1151    //                     flexes[current_ix + 1] = next_target_flex;
1152
1153    //                     proposed_current_pixel_change -= current_pixel_change;
1154    //                 }
1155
1156    //                 workspace.schedule_serialize(cx);
1157    //                 cx.notify();
1158    //             }
1159    //         }
1160    //     }
1161
1162    //     impl Extend<AnyElement<Workspace>> for PaneAxisElement {
1163    //         fn extend<T: IntoIterator<Item = AnyElement<Workspace>>>(&mut self, children: T) {
1164    //             self.children.extend(children);
1165    //         }
1166    //     }
1167
1168    //     impl Element<Workspace> for PaneAxisElement {
1169    //         type LayoutState = f32;
1170    //         type PaintState = ();
1171
1172    //         fn layout(
1173    //             &mut self,
1174    //             constraint: SizeConstraint,
1175    //             view: &mut Workspace,
1176    //             cx: &mut ViewContext<Workspace>,
1177    //         ) -> (Vector2F, Self::LayoutState) {
1178    //             debug_assert!(self.children.len() == self.flexes.borrow().len());
1179
1180    //             let active_pane_magnification =
1181    //                 settings::get::<WorkspaceSettings>(cx).active_pane_magnification;
1182
1183    //             let mut remaining_flex = 0.;
1184
1185    //             if active_pane_magnification != 1. {
1186    //                 let active_pane_flex = self
1187    //                     .active_pane_ix
1188    //                     .map(|_| active_pane_magnification)
1189    //                     .unwrap_or(1.);
1190    //                 remaining_flex += self.children.len() as f32 - 1. + active_pane_flex;
1191    //             } else {
1192    //                 for flex in self.flexes.borrow().iter() {
1193    //                     remaining_flex += flex;
1194    //                 }
1195    //             }
1196
1197    //             let mut cross_axis_max: f32 = 0.0;
1198    //             let mut remaining_space = constraint.max_along(self.axis);
1199
1200    //             if remaining_space.is_infinite() {
1201    //                 panic!("flex contains flexible children but has an infinite constraint along the flex axis");
1202    //             }
1203
1204    //             self.layout_children(
1205    //                 active_pane_magnification,
1206    //                 constraint,
1207    //                 &mut remaining_space,
1208    //                 &mut remaining_flex,
1209    //                 &mut cross_axis_max,
1210    //                 view,
1211    //                 cx,
1212    //             );
1213
1214    //             let mut size = match self.axis {
1215    //                 Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
1216    //                 Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
1217    //             };
1218
1219    //             if constraint.min.x().is_finite() {
1220    //                 size.set_x(size.x().max(constraint.min.x()));
1221    //             }
1222    //             if constraint.min.y().is_finite() {
1223    //                 size.set_y(size.y().max(constraint.min.y()));
1224    //             }
1225
1226    //             if size.x() > constraint.max.x() {
1227    //                 size.set_x(constraint.max.x());
1228    //             }
1229    //             if size.y() > constraint.max.y() {
1230    //                 size.set_y(constraint.max.y());
1231    //             }
1232
1233    //             (size, remaining_space)
1234    //         }
1235
1236    //         fn paint(
1237    //             &mut self,
1238    //             bounds: Bounds<Pixels>,
1239    //             visible_bounds: Bounds<Pixels>,
1240    //             remaining_space: &mut Self::LayoutState,
1241    //             view: &mut Workspace,
1242    //             cx: &mut ViewContext<Workspace>,
1243    //         ) -> Self::PaintState {
1244    //             let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.;
1245    //             let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
1246
1247    //             let overflowing = *remaining_space < 0.;
1248    //             if overflowing {
1249    //                 cx.scene().push_layer(Some(visible_bounds));
1250    //             }
1251
1252    //             let mut child_origin = bounds.origin();
1253
1254    //             let mut bounding_boxes = self.bounding_boxes.borrow_mut();
1255    //             bounding_boxes.clear();
1256
1257    //             let mut children_iter = self.children.iter_mut().enumerate().peekable();
1258    //             while let Some((ix, child)) = children_iter.next() {
1259    //                 let child_start = child_origin.clone();
1260    //                 child.paint(child_origin, visible_bounds, view, cx);
1261
1262    //                 bounding_boxes.push(Some(Bounds<Pixels>::new(child_origin, child.size())));
1263
1264    //                 match self.axis {
1265    //                     Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
1266    //                     Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
1267    //                 }
1268
1269    //                 if can_resize && children_iter.peek().is_some() {
1270    //                     cx.scene().push_stacking_context(None, None);
1271
1272    //                     let handle_origin = match self.axis {
1273    //                         Axis::Horizontal => child_origin - vec2f(HANDLE_HITBOX_SIZE / 2., 0.0),
1274    //                         Axis::Vertical => child_origin - vec2f(0.0, HANDLE_HITBOX_SIZE / 2.),
1275    //                     };
1276
1277    //                     let handle_bounds = match self.axis {
1278    //                         Axis::Horizontal => Bounds<Pixels>::new(
1279    //                             handle_origin,
1280    //                             vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()),
1281    //                         ),
1282    //                         Axis::Vertical => Bounds<Pixels>::new(
1283    //                             handle_origin,
1284    //                             vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE),
1285    //                         ),
1286    //                     };
1287
1288    //                     let style = match self.axis {
1289    //                         Axis::Horizontal => CursorStyle::ResizeLeftRight,
1290    //                         Axis::Vertical => CursorStyle::ResizeUpDown,
1291    //                     };
1292
1293    //                     cx.scene().push_cursor_region(CursorRegion {
1294    //                         bounds: handle_bounds,
1295    //                         style,
1296    //                     });
1297
1298    //                     enum ResizeHandle {}
1299    //                     let mut mouse_region = MouseRegion::new::<ResizeHandle>(
1300    //                         cx.view_id(),
1301    //                         self.basis + ix,
1302    //                         handle_bounds,
1303    //                     );
1304    //                     mouse_region = mouse_region
1305    //                         .on_drag(
1306    //                             MouseButton::Left,
1307    //                             Self::handle_resize(
1308    //                                 self.flexes.clone(),
1309    //                                 self.axis,
1310    //                                 ix,
1311    //                                 child_start,
1312    //                                 visible_bounds.clone(),
1313    //                             ),
1314    //                         )
1315    //                         .on_click(MouseButton::Left, {
1316    //                             let flexes = self.flexes.clone();
1317    //                             move |e, v: &mut Workspace, cx| {
1318    //                                 if e.click_count >= 2 {
1319    //                                     let mut borrow = flexes.borrow_mut();
1320    //                                     *borrow = vec![1.; borrow.len()];
1321    //                                     v.schedule_serialize(cx);
1322    //                                     cx.notify();
1323    //                                 }
1324    //                             }
1325    //                         });
1326    //                     cx.scene().push_mouse_region(mouse_region);
1327
1328    //                     cx.scene().pop_stacking_context();
1329    //                 }
1330    //             }
1331
1332    //             if overflowing {
1333    //                 cx.scene().pop_layer();
1334    //             }
1335    //         }
1336
1337    //         fn rect_for_text_range(
1338    //             &self,
1339    //             range_utf16: Range<usize>,
1340    //             _: Bounds<Pixels>,
1341    //             _: Bounds<Pixels>,
1342    //             _: &Self::LayoutState,
1343    //             _: &Self::PaintState,
1344    //             view: &Workspace,
1345    //             cx: &ViewContext<Workspace>,
1346    //         ) -> Option<Bounds<Pixels>> {
1347    //             self.children
1348    //                 .iter()
1349    //                 .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
1350    //         }
1351
1352    //         fn debug(
1353    //             &self,
1354    //             bounds: Bounds<Pixels>,
1355    //             _: &Self::LayoutState,
1356    //             _: &Self::PaintState,
1357    //             view: &Workspace,
1358    //             cx: &ViewContext<Workspace>,
1359    //         ) -> json::Value {
1360    //             serde_json::json!({
1361    //                 "type": "PaneAxis",
1362    //                 "bounds": bounds.to_json(),
1363    //                 "axis": self.axis.to_json(),
1364    //                 "flexes": *self.flexes.borrow(),
1365    //                 "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
1366    //             })
1367    //         }
1368    //     }
1369}