pane_group.rs

   1use crate::{
   2    AnyActiveCall, AppState, CollaboratorId, FollowerState, Pane, ParticipantLocation, Workspace,
   3    WorkspaceSettings,
   4    pane_group::element::pane_axis,
   5    workspace_settings::{PaneSplitDirectionHorizontal, PaneSplitDirectionVertical},
   6};
   7use anyhow::Result;
   8use collections::HashMap;
   9use gpui::{
  10    Along, AnyView, AnyWeakView, Axis, Bounds, Entity, Hsla, IntoElement, MouseButton, Pixels,
  11    Point, StyleRefinement, WeakEntity, Window, point, size,
  12};
  13use parking_lot::Mutex;
  14use project::Project;
  15use schemars::JsonSchema;
  16use serde::Deserialize;
  17use settings::Settings;
  18use std::sync::Arc;
  19use ui::prelude::*;
  20
  21pub const HANDLE_HITBOX_SIZE: f32 = 4.0;
  22const HORIZONTAL_MIN_SIZE: f32 = 80.;
  23const VERTICAL_MIN_SIZE: f32 = 100.;
  24
  25/// One or many panes, arranged in a horizontal or vertical axis due to a split.
  26/// Panes have all their tabs and capabilities preserved, and can be split again or resized.
  27/// Single-pane group is a regular pane.
  28#[derive(Clone)]
  29pub struct PaneGroup {
  30    pub root: Member,
  31    pub is_center: bool,
  32    root_axis_flexes: Arc<Mutex<Vec<f32>>>,
  33    root_axis_bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
  34}
  35
  36pub struct PaneRenderResult {
  37    pub element: gpui::AnyElement,
  38    pub contains_active_pane: bool,
  39}
  40
  41impl PaneGroup {
  42    pub fn with_root(root: Member) -> Self {
  43        Self {
  44            root,
  45            is_center: false,
  46            root_axis_flexes: Arc::new(Mutex::new(Vec::new())),
  47            root_axis_bounding_boxes: Arc::new(Mutex::new(Vec::new())),
  48        }
  49    }
  50
  51    pub fn new(pane: Entity<Pane>) -> Self {
  52        Self {
  53            root: Member::Pane(pane),
  54            is_center: false,
  55            root_axis_flexes: Arc::new(Mutex::new(Vec::new())),
  56            root_axis_bounding_boxes: Arc::new(Mutex::new(Vec::new())),
  57        }
  58    }
  59
  60    pub fn set_is_center(&mut self, is_center: bool) {
  61        self.is_center = is_center;
  62    }
  63
  64    pub fn split(
  65        &mut self,
  66        old_pane: &Entity<Pane>,
  67        new_pane: &Entity<Pane>,
  68        direction: SplitDirection,
  69        cx: &mut App,
  70    ) {
  71        let found = match &mut self.root {
  72            Member::Pane(pane) => {
  73                if pane == old_pane {
  74                    self.root = Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
  75                    true
  76                } else {
  77                    false
  78                }
  79            }
  80            Member::Axis(axis) => axis.split(old_pane, new_pane, direction),
  81        };
  82
  83        // If the pane wasn't found, fall back to splitting the first pane in the tree.
  84        if !found {
  85            let first_pane = self.root.first_pane();
  86            match &mut self.root {
  87                Member::Pane(_) => {
  88                    self.root = Member::new_axis(first_pane, new_pane.clone(), direction);
  89                }
  90                Member::Axis(axis) => {
  91                    let _ = axis.split(&first_pane, new_pane, direction);
  92                }
  93            }
  94        }
  95
  96        self.mark_positions(cx);
  97    }
  98
  99    pub fn bounding_box_for_pane(&self, pane: &Entity<Pane>) -> Option<Bounds<Pixels>> {
 100        match &self.root {
 101            Member::Pane(_) => None,
 102            Member::Axis(axis) => axis.bounding_box_for_pane(pane),
 103        }
 104    }
 105
 106    pub fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&Entity<Pane>> {
 107        match &self.root {
 108            Member::Pane(pane) => Some(pane),
 109            Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
 110        }
 111    }
 112
 113    /// Moves active pane to span the entire border in the given direction,
 114    /// similar to Vim ctrl+w shift-[hjkl] motion.
 115    ///
 116    /// Returns:
 117    /// - Ok(true) if it found and moved a pane
 118    /// - Ok(false) if it found but did not move the pane
 119    /// - Err(_) if it did not find the pane
 120    pub fn move_to_border(
 121        &mut self,
 122        active_pane: &Entity<Pane>,
 123        direction: SplitDirection,
 124        cx: &mut App,
 125    ) -> Result<bool> {
 126        if let Some(pane) = self.find_pane_at_border(direction)
 127            && pane == active_pane
 128        {
 129            return Ok(false);
 130        }
 131
 132        if !self.remove_internal(active_pane)? {
 133            return Ok(false);
 134        }
 135
 136        if let Member::Axis(root) = &mut self.root
 137            && direction.axis() == root.axis
 138        {
 139            let idx = if direction.increasing() {
 140                root.members.len()
 141            } else {
 142                0
 143            };
 144            root.insert_pane(idx, active_pane);
 145            self.mark_positions(cx);
 146            return Ok(true);
 147        }
 148
 149        let members = if direction.increasing() {
 150            vec![self.root.clone(), Member::Pane(active_pane.clone())]
 151        } else {
 152            vec![Member::Pane(active_pane.clone()), self.root.clone()]
 153        };
 154        self.root = Member::Axis(PaneAxis::new(direction.axis(), members));
 155        self.mark_positions(cx);
 156        Ok(true)
 157    }
 158
 159    fn find_pane_at_border(&self, direction: SplitDirection) -> Option<&Entity<Pane>> {
 160        match &self.root {
 161            Member::Pane(pane) => Some(pane),
 162            Member::Axis(axis) => axis.find_pane_at_border(direction),
 163        }
 164    }
 165
 166    /// Returns:
 167    /// - Ok(true) if it found and removed a pane
 168    /// - Ok(false) if it found but did not remove the pane
 169    /// - Err(_) if it did not find the pane
 170    pub fn remove(&mut self, pane: &Entity<Pane>, cx: &mut App) -> Result<bool> {
 171        let result = self.remove_internal(pane);
 172        if let Ok(true) = result {
 173            self.mark_positions(cx);
 174        }
 175        result
 176    }
 177
 178    fn remove_internal(&mut self, pane: &Entity<Pane>) -> Result<bool> {
 179        match &mut self.root {
 180            Member::Pane(_) => Ok(false),
 181            Member::Axis(axis) => {
 182                if let Some(last_pane) = axis.remove(pane)? {
 183                    self.root = last_pane;
 184                }
 185                Ok(true)
 186            }
 187        }
 188    }
 189
 190    pub fn resize(
 191        &mut self,
 192        pane: &Entity<Pane>,
 193        direction: Axis,
 194        amount: Pixels,
 195        bounds: &Bounds<Pixels>,
 196        cx: &mut App,
 197    ) {
 198        match &mut self.root {
 199            Member::Pane(_) => {}
 200            Member::Axis(axis) => {
 201                let _ = axis.resize(pane, direction, amount, bounds);
 202            }
 203        };
 204        self.mark_positions(cx);
 205    }
 206
 207    pub fn reset_pane_sizes(&mut self, cx: &mut App) {
 208        match &mut self.root {
 209            Member::Pane(_) => {}
 210            Member::Axis(axis) => {
 211                let _ = axis.reset_pane_sizes();
 212            }
 213        };
 214        self.mark_positions(cx);
 215    }
 216
 217    pub fn swap(&mut self, from: &Entity<Pane>, to: &Entity<Pane>, cx: &mut App) {
 218        match &mut self.root {
 219            Member::Pane(_) => {}
 220            Member::Axis(axis) => axis.swap(from, to),
 221        };
 222        self.mark_positions(cx);
 223    }
 224
 225    pub fn mark_positions(&mut self, cx: &mut App) {
 226        self.root.mark_positions(self.is_center, cx);
 227    }
 228
 229    pub fn render(
 230        &mut self,
 231        zoomed: Option<&AnyWeakView>,
 232        left_item: Option<AnyView>,
 233        render_cx: &dyn PaneLeaderDecorator,
 234        window: &mut Window,
 235        cx: &mut App,
 236    ) -> gpui::AnyElement {
 237        let Some(left_item) = left_item else {
 238            return self.root.render(0, zoomed, render_cx, window, cx).element;
 239        };
 240
 241        let left_element = div()
 242            .relative()
 243            .flex_1()
 244            .size_full()
 245            .child(left_item)
 246            .into_any_element();
 247
 248        let mut children: Vec<AnyElement> = vec![left_element];
 249        let mut is_leaf_pane_mask: Vec<bool> = vec![false];
 250
 251        match &self.root {
 252            Member::Axis(axis) if axis.axis == Axis::Horizontal => {
 253                for (ix, member) in axis.members.iter().enumerate() {
 254                    let result = member.render((0 + ix) * 10, zoomed, render_cx, window, cx);
 255                    children.push(result.element.into_any_element());
 256                    is_leaf_pane_mask.push(matches!(member, Member::Pane(_)));
 257                }
 258            }
 259            _ => {
 260                let result = self.root.render(0, zoomed, render_cx, window, cx);
 261                children.push(result.element.into_any_element());
 262                is_leaf_pane_mask.push(false);
 263            }
 264        }
 265
 266        let child_count = children.len();
 267
 268        {
 269            let mut flexes = self.root_axis_flexes.lock();
 270            if flexes.len() != child_count {
 271                *flexes = vec![1.0; child_count];
 272            }
 273        }
 274        {
 275            let mut bounding_boxes = self.root_axis_bounding_boxes.lock();
 276            if bounding_boxes.len() != child_count {
 277                *bounding_boxes = vec![None; child_count];
 278            }
 279        }
 280
 281        pane_axis(
 282            Axis::Horizontal,
 283            usize::MAX / 2,
 284            self.root_axis_flexes.clone(),
 285            self.root_axis_bounding_boxes.clone(),
 286            render_cx.workspace().clone(),
 287        )
 288        .with_is_leaf_pane_mask(is_leaf_pane_mask)
 289        .children(children)
 290        .into_any_element()
 291    }
 292
 293    pub fn panes(&self) -> Vec<&Entity<Pane>> {
 294        let mut panes = Vec::new();
 295        self.root.collect_panes(&mut panes);
 296        panes
 297    }
 298
 299    pub fn first_pane(&self) -> Entity<Pane> {
 300        self.root.first_pane()
 301    }
 302
 303    pub fn last_pane(&self) -> Entity<Pane> {
 304        self.root.last_pane()
 305    }
 306
 307    pub fn find_pane_in_direction(
 308        &mut self,
 309        active_pane: &Entity<Pane>,
 310        direction: SplitDirection,
 311        cx: &App,
 312    ) -> Option<&Entity<Pane>> {
 313        let bounding_box = self.bounding_box_for_pane(active_pane)?;
 314        let cursor = active_pane.read(cx).pixel_position_of_cursor(cx);
 315        let center = match cursor {
 316            Some(cursor) if bounding_box.contains(&cursor) => cursor,
 317            _ => bounding_box.center(),
 318        };
 319
 320        let distance_to_next = crate::HANDLE_HITBOX_SIZE;
 321
 322        let target = match direction {
 323            SplitDirection::Left => {
 324                Point::new(bounding_box.left() - distance_to_next.into(), center.y)
 325            }
 326            SplitDirection::Right => {
 327                Point::new(bounding_box.right() + distance_to_next.into(), center.y)
 328            }
 329            SplitDirection::Up => {
 330                Point::new(center.x, bounding_box.top() - distance_to_next.into())
 331            }
 332            SplitDirection::Down => {
 333                Point::new(center.x, bounding_box.bottom() + distance_to_next.into())
 334            }
 335        };
 336        self.pane_at_pixel_position(target)
 337    }
 338
 339    pub fn invert_axies(&mut self, cx: &mut App) {
 340        self.root.invert_pane_axies();
 341        self.mark_positions(cx);
 342    }
 343}
 344
 345#[derive(Debug, Clone)]
 346pub enum Member {
 347    Axis(PaneAxis),
 348    Pane(Entity<Pane>),
 349}
 350
 351impl Member {
 352    pub fn mark_positions(&mut self, in_center_group: bool, cx: &mut App) {
 353        match self {
 354            Member::Axis(pane_axis) => {
 355                for member in pane_axis.members.iter_mut() {
 356                    member.mark_positions(in_center_group, cx);
 357                }
 358            }
 359            Member::Pane(entity) => entity.update(cx, |pane, _| {
 360                pane.in_center_group = in_center_group;
 361            }),
 362        }
 363    }
 364}
 365
 366#[derive(Clone, Copy)]
 367pub struct PaneRenderContext<'a> {
 368    pub project: &'a Entity<Project>,
 369    pub follower_states: &'a HashMap<CollaboratorId, FollowerState>,
 370    pub active_call: Option<&'a dyn AnyActiveCall>,
 371    pub active_pane: &'a Entity<Pane>,
 372    pub app_state: &'a Arc<AppState>,
 373    pub workspace: &'a WeakEntity<Workspace>,
 374}
 375
 376#[derive(Default)]
 377pub struct LeaderDecoration {
 378    border: Option<Hsla>,
 379    status_box: Option<AnyElement>,
 380}
 381
 382pub trait PaneLeaderDecorator {
 383    fn decorate(&self, pane: &Entity<Pane>, cx: &App) -> LeaderDecoration;
 384    fn active_pane(&self) -> &Entity<Pane>;
 385    fn workspace(&self) -> &WeakEntity<Workspace>;
 386}
 387
 388pub struct ActivePaneDecorator<'a> {
 389    active_pane: &'a Entity<Pane>,
 390    workspace: &'a WeakEntity<Workspace>,
 391}
 392
 393impl<'a> ActivePaneDecorator<'a> {
 394    pub fn new(active_pane: &'a Entity<Pane>, workspace: &'a WeakEntity<Workspace>) -> Self {
 395        Self {
 396            active_pane,
 397            workspace,
 398        }
 399    }
 400}
 401
 402impl PaneLeaderDecorator for ActivePaneDecorator<'_> {
 403    fn decorate(&self, _: &Entity<Pane>, _: &App) -> LeaderDecoration {
 404        LeaderDecoration::default()
 405    }
 406    fn active_pane(&self) -> &Entity<Pane> {
 407        self.active_pane
 408    }
 409
 410    fn workspace(&self) -> &WeakEntity<Workspace> {
 411        self.workspace
 412    }
 413}
 414
 415impl PaneLeaderDecorator for PaneRenderContext<'_> {
 416    fn decorate(&self, pane: &Entity<Pane>, cx: &App) -> LeaderDecoration {
 417        let follower_state = self.follower_states.iter().find_map(|(leader_id, state)| {
 418            if state.center_pane == *pane {
 419                Some((*leader_id, state))
 420            } else {
 421                None
 422            }
 423        });
 424        let Some((leader_id, follower_state)) = follower_state else {
 425            return LeaderDecoration::default();
 426        };
 427
 428        let mut leader_color;
 429        let status_box;
 430        match leader_id {
 431            CollaboratorId::PeerId(peer_id) => {
 432                let Some(leader) = self
 433                    .active_call
 434                    .as_ref()
 435                    .and_then(|call| call.remote_participant_for_peer_id(peer_id, cx))
 436                else {
 437                    return LeaderDecoration::default();
 438                };
 439
 440                let is_in_unshared_view = follower_state.active_view_id.is_some_and(|view_id| {
 441                    !follower_state
 442                        .items_by_leader_view_id
 443                        .contains_key(&view_id)
 444                });
 445
 446                let mut leader_join_data = None;
 447                let leader_status_box = match leader.location {
 448                    ParticipantLocation::SharedProject {
 449                        project_id: leader_project_id,
 450                    } => {
 451                        if Some(leader_project_id) == self.project.read(cx).remote_id() {
 452                            is_in_unshared_view.then(|| {
 453                                Label::new(format!(
 454                                    "{} is in an unshared pane",
 455                                    leader.user.github_login
 456                                ))
 457                            })
 458                        } else {
 459                            leader_join_data = Some((leader_project_id, leader.user.id));
 460                            Some(Label::new(format!(
 461                                "Follow {} to their active project",
 462                                leader.user.github_login,
 463                            )))
 464                        }
 465                    }
 466                    ParticipantLocation::UnsharedProject => Some(Label::new(format!(
 467                        "{} is viewing an unshared Zed project",
 468                        leader.user.github_login
 469                    ))),
 470                    ParticipantLocation::External => Some(Label::new(format!(
 471                        "{} is viewing a window outside of Zed",
 472                        leader.user.github_login
 473                    ))),
 474                };
 475                status_box = leader_status_box.map(|status| {
 476                    div()
 477                        .absolute()
 478                        .w_96()
 479                        .bottom_3()
 480                        .right_3()
 481                        .elevation_2(cx)
 482                        .p_1()
 483                        .child(status)
 484                        .when_some(
 485                            leader_join_data,
 486                            |this, (leader_project_id, leader_user_id)| {
 487                                let app_state = self.app_state.clone();
 488                                this.cursor_pointer().on_mouse_down(
 489                                    MouseButton::Left,
 490                                    move |_, _, cx| {
 491                                        crate::join_in_room_project(
 492                                            leader_project_id,
 493                                            leader_user_id,
 494                                            app_state.clone(),
 495                                            cx,
 496                                        )
 497                                        .detach_and_log_err(cx);
 498                                    },
 499                                )
 500                            },
 501                        )
 502                        .into_any_element()
 503                });
 504                leader_color = cx
 505                    .theme()
 506                    .players()
 507                    .color_for_participant(leader.participant_index.0)
 508                    .cursor;
 509            }
 510            CollaboratorId::Agent => {
 511                status_box = None;
 512                leader_color = cx.theme().players().agent().cursor;
 513            }
 514        }
 515
 516        let is_in_panel = follower_state.dock_pane.is_some();
 517        if is_in_panel {
 518            leader_color.fade_out(0.75);
 519        } else {
 520            leader_color.fade_out(0.3);
 521        }
 522
 523        LeaderDecoration {
 524            status_box,
 525            border: Some(leader_color),
 526        }
 527    }
 528
 529    fn active_pane(&self) -> &Entity<Pane> {
 530        self.active_pane
 531    }
 532
 533    fn workspace(&self) -> &WeakEntity<Workspace> {
 534        self.workspace
 535    }
 536}
 537
 538impl Member {
 539    fn new_axis(old_pane: Entity<Pane>, new_pane: Entity<Pane>, direction: SplitDirection) -> Self {
 540        use Axis::*;
 541        use SplitDirection::*;
 542
 543        let axis = match direction {
 544            Up | Down => Vertical,
 545            Left | Right => Horizontal,
 546        };
 547
 548        let members = match direction {
 549            Up | Left => vec![Member::Pane(new_pane), Member::Pane(old_pane)],
 550            Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)],
 551        };
 552
 553        Member::Axis(PaneAxis::new(axis, members))
 554    }
 555
 556    fn first_pane(&self) -> Entity<Pane> {
 557        match self {
 558            Member::Axis(axis) => axis.members[0].first_pane(),
 559            Member::Pane(pane) => pane.clone(),
 560        }
 561    }
 562
 563    fn last_pane(&self) -> Entity<Pane> {
 564        match self {
 565            Member::Axis(axis) => axis.members.last().unwrap().last_pane(),
 566            Member::Pane(pane) => pane.clone(),
 567        }
 568    }
 569
 570    pub fn render(
 571        &self,
 572        basis: usize,
 573        zoomed: Option<&AnyWeakView>,
 574        render_cx: &dyn PaneLeaderDecorator,
 575        window: &mut Window,
 576        cx: &mut App,
 577    ) -> PaneRenderResult {
 578        match self {
 579            Member::Pane(pane) => {
 580                if zoomed == Some(&pane.downgrade().into()) {
 581                    return PaneRenderResult {
 582                        element: div().into_any(),
 583                        contains_active_pane: false,
 584                    };
 585                }
 586
 587                let decoration = render_cx.decorate(pane, cx);
 588                let is_active = pane == render_cx.active_pane();
 589
 590                PaneRenderResult {
 591                    element: div()
 592                        .relative()
 593                        .flex_1()
 594                        .size_full()
 595                        .child(
 596                            AnyView::from(pane.clone())
 597                                .cached(StyleRefinement::default().v_flex().size_full()),
 598                        )
 599                        .when_some(decoration.border, |this, color| {
 600                            this.child(
 601                                div()
 602                                    .absolute()
 603                                    .size_full()
 604                                    .left_0()
 605                                    .top_0()
 606                                    .border_2()
 607                                    .border_color(color),
 608                            )
 609                        })
 610                        .children(decoration.status_box)
 611                        .into_any(),
 612                    contains_active_pane: is_active,
 613                }
 614            }
 615            Member::Axis(axis) => axis.render(basis + 1, zoomed, render_cx, window, cx),
 616        }
 617    }
 618
 619    fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a Entity<Pane>>) {
 620        match self {
 621            Member::Axis(axis) => {
 622                for member in &axis.members {
 623                    member.collect_panes(panes);
 624                }
 625            }
 626            Member::Pane(pane) => panes.push(pane),
 627        }
 628    }
 629
 630    fn invert_pane_axies(&mut self) {
 631        match self {
 632            Self::Axis(axis) => {
 633                axis.axis = axis.axis.invert();
 634                for member in axis.members.iter_mut() {
 635                    member.invert_pane_axies();
 636                }
 637            }
 638            Self::Pane(_) => {}
 639        }
 640    }
 641}
 642
 643#[derive(Debug, Clone)]
 644pub struct PaneAxis {
 645    pub axis: Axis,
 646    pub members: Vec<Member>,
 647    pub flexes: Arc<Mutex<Vec<f32>>>,
 648    pub bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
 649}
 650
 651impl PaneAxis {
 652    pub fn new(axis: Axis, members: Vec<Member>) -> Self {
 653        let flexes = Arc::new(Mutex::new(vec![1.; members.len()]));
 654        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
 655        Self {
 656            axis,
 657            members,
 658            flexes,
 659            bounding_boxes,
 660        }
 661    }
 662
 663    pub fn load(axis: Axis, members: Vec<Member>, flexes: Option<Vec<f32>>) -> Self {
 664        let mut flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]);
 665        if flexes.len() != members.len()
 666            || (flexes.iter().copied().sum::<f32>() - flexes.len() as f32).abs() >= 0.001
 667        {
 668            flexes = vec![1.; members.len()];
 669        }
 670
 671        let flexes = Arc::new(Mutex::new(flexes));
 672        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
 673        Self {
 674            axis,
 675            members,
 676            flexes,
 677            bounding_boxes,
 678        }
 679    }
 680
 681    fn split(
 682        &mut self,
 683        old_pane: &Entity<Pane>,
 684        new_pane: &Entity<Pane>,
 685        direction: SplitDirection,
 686    ) -> bool {
 687        for (mut idx, member) in self.members.iter_mut().enumerate() {
 688            match member {
 689                Member::Axis(axis) => {
 690                    if axis.split(old_pane, new_pane, direction) {
 691                        return true;
 692                    }
 693                }
 694                Member::Pane(pane) => {
 695                    if pane == old_pane {
 696                        if direction.axis() == self.axis {
 697                            if direction.increasing() {
 698                                idx += 1;
 699                            }
 700                            self.insert_pane(idx, new_pane);
 701                        } else {
 702                            *member =
 703                                Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
 704                        }
 705                        return true;
 706                    }
 707                }
 708            }
 709        }
 710        false
 711    }
 712
 713    fn insert_pane(&mut self, idx: usize, new_pane: &Entity<Pane>) {
 714        self.members.insert(idx, Member::Pane(new_pane.clone()));
 715        *self.flexes.lock() = vec![1.; self.members.len()];
 716    }
 717
 718    fn find_pane_at_border(&self, direction: SplitDirection) -> Option<&Entity<Pane>> {
 719        if self.axis != direction.axis() {
 720            return None;
 721        }
 722        let member = if direction.increasing() {
 723            self.members.last()
 724        } else {
 725            self.members.first()
 726        };
 727        member.and_then(|e| match e {
 728            Member::Pane(pane) => Some(pane),
 729            Member::Axis(_) => None,
 730        })
 731    }
 732
 733    fn remove(&mut self, pane_to_remove: &Entity<Pane>) -> Result<Option<Member>> {
 734        let mut found_pane = false;
 735        let mut remove_member = None;
 736        for (idx, member) in self.members.iter_mut().enumerate() {
 737            match member {
 738                Member::Axis(axis) => {
 739                    if let Ok(last_pane) = axis.remove(pane_to_remove) {
 740                        if let Some(last_pane) = last_pane {
 741                            *member = last_pane;
 742                        }
 743                        found_pane = true;
 744                        break;
 745                    }
 746                }
 747                Member::Pane(pane) => {
 748                    if pane == pane_to_remove {
 749                        found_pane = true;
 750                        remove_member = Some(idx);
 751                        break;
 752                    }
 753                }
 754            }
 755        }
 756
 757        if found_pane {
 758            if let Some(idx) = remove_member {
 759                self.members.remove(idx);
 760                *self.flexes.lock() = vec![1.; self.members.len()];
 761            }
 762
 763            if self.members.len() == 1 {
 764                let result = self.members.pop();
 765                *self.flexes.lock() = vec![1.; self.members.len()];
 766                Ok(result)
 767            } else {
 768                Ok(None)
 769            }
 770        } else {
 771            anyhow::bail!("Pane not found");
 772        }
 773    }
 774
 775    fn reset_pane_sizes(&self) {
 776        *self.flexes.lock() = vec![1.; self.members.len()];
 777        for member in self.members.iter() {
 778            if let Member::Axis(axis) = member {
 779                axis.reset_pane_sizes();
 780            }
 781        }
 782    }
 783
 784    fn resize(
 785        &mut self,
 786        pane: &Entity<Pane>,
 787        axis: Axis,
 788        amount: Pixels,
 789        bounds: &Bounds<Pixels>,
 790    ) -> Option<bool> {
 791        let container_size = self
 792            .bounding_boxes
 793            .lock()
 794            .iter()
 795            .filter_map(|e| *e)
 796            .reduce(|acc, e| acc.union(&e))
 797            .unwrap_or(*bounds)
 798            .size;
 799
 800        let found_pane = self
 801            .members
 802            .iter()
 803            .any(|member| matches!(member, Member::Pane(p) if p == pane));
 804
 805        if found_pane && self.axis != axis {
 806            return Some(false); // pane found but this is not the correct axis direction
 807        }
 808        let mut found_axis_index: Option<usize> = None;
 809        if !found_pane {
 810            for (i, pa) in self.members.iter_mut().enumerate() {
 811                if let Member::Axis(pa) = pa
 812                    && let Some(done) = pa.resize(pane, axis, amount, bounds)
 813                {
 814                    if done {
 815                        return Some(true); // pane found and operations already done
 816                    } else if self.axis != axis {
 817                        return Some(false); // pane found but this is not the correct axis direction
 818                    } else {
 819                        found_axis_index = Some(i); // pane found and this is correct direction
 820                    }
 821                }
 822            }
 823            found_axis_index?; // no pane found
 824        }
 825
 826        let min_size = match axis {
 827            Axis::Horizontal => px(HORIZONTAL_MIN_SIZE),
 828            Axis::Vertical => px(VERTICAL_MIN_SIZE),
 829        };
 830        let mut flexes = self.flexes.lock();
 831
 832        let ix = if found_pane {
 833            self.members.iter().position(|m| {
 834                if let Member::Pane(p) = m {
 835                    p == pane
 836                } else {
 837                    false
 838                }
 839            })
 840        } else {
 841            found_axis_index
 842        };
 843
 844        if ix.is_none() {
 845            return Some(true);
 846        }
 847
 848        let ix = ix.unwrap_or(0);
 849
 850        let size = move |ix, flexes: &[f32]| {
 851            container_size.along(axis) * (flexes[ix] / flexes.len() as f32)
 852        };
 853
 854        // Don't allow resizing to less than the minimum size, if elements are already too small
 855        if min_size - px(1.) > size(ix, flexes.as_slice()) {
 856            return Some(true);
 857        }
 858
 859        let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
 860            let flex_change = flexes.len() as f32 * pixel_dx / container_size.along(axis);
 861            let current_target_flex = flexes[target_ix] + flex_change;
 862            let next_target_flex = flexes[(target_ix as isize + next) as usize] - flex_change;
 863            (current_target_flex, next_target_flex)
 864        };
 865
 866        let apply_changes =
 867            |current_ix: usize, proposed_current_pixel_change: Pixels, flexes: &mut [f32]| {
 868                let next_target_size = Pixels::max(
 869                    size(current_ix + 1, flexes) - proposed_current_pixel_change,
 870                    min_size,
 871                );
 872                let current_target_size = Pixels::max(
 873                    size(current_ix, flexes) + size(current_ix + 1, flexes) - next_target_size,
 874                    min_size,
 875                );
 876
 877                let current_pixel_change = current_target_size - size(current_ix, flexes);
 878
 879                let (current_target_flex, next_target_flex) =
 880                    flex_changes(current_pixel_change, current_ix, 1, flexes);
 881
 882                flexes[current_ix] = current_target_flex;
 883                flexes[current_ix + 1] = next_target_flex;
 884            };
 885
 886        if ix + 1 == flexes.len() {
 887            apply_changes(ix - 1, -1.0 * amount, flexes.as_mut_slice());
 888        } else {
 889            apply_changes(ix, amount, flexes.as_mut_slice());
 890        }
 891        Some(true)
 892    }
 893
 894    fn swap(&mut self, from: &Entity<Pane>, to: &Entity<Pane>) {
 895        for member in self.members.iter_mut() {
 896            match member {
 897                Member::Axis(axis) => axis.swap(from, to),
 898                Member::Pane(pane) => {
 899                    if pane == from {
 900                        *member = Member::Pane(to.clone());
 901                    } else if pane == to {
 902                        *member = Member::Pane(from.clone())
 903                    }
 904                }
 905            }
 906        }
 907    }
 908
 909    fn bounding_box_for_pane(&self, pane: &Entity<Pane>) -> Option<Bounds<Pixels>> {
 910        debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
 911
 912        for (idx, member) in self.members.iter().enumerate() {
 913            match member {
 914                Member::Pane(found) => {
 915                    if pane == found {
 916                        return self.bounding_boxes.lock()[idx];
 917                    }
 918                }
 919                Member::Axis(axis) => {
 920                    if let Some(rect) = axis.bounding_box_for_pane(pane) {
 921                        return Some(rect);
 922                    }
 923                }
 924            }
 925        }
 926        None
 927    }
 928
 929    fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&Entity<Pane>> {
 930        debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
 931
 932        let bounding_boxes = self.bounding_boxes.lock();
 933
 934        for (idx, member) in self.members.iter().enumerate() {
 935            if let Some(coordinates) = bounding_boxes[idx]
 936                && coordinates.contains(&coordinate)
 937            {
 938                return match member {
 939                    Member::Pane(found) => Some(found),
 940                    Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
 941                };
 942            }
 943        }
 944        None
 945    }
 946
 947    fn render(
 948        &self,
 949        basis: usize,
 950        zoomed: Option<&AnyWeakView>,
 951        render_cx: &dyn PaneLeaderDecorator,
 952        window: &mut Window,
 953        cx: &mut App,
 954    ) -> PaneRenderResult {
 955        debug_assert!(self.members.len() == self.flexes.lock().len());
 956        let mut active_pane_ix = None;
 957        let mut contains_active_pane = false;
 958        let mut is_leaf_pane = vec![false; self.members.len()];
 959
 960        let rendered_children = self
 961            .members
 962            .iter()
 963            .enumerate()
 964            .map(|(ix, member)| {
 965                match member {
 966                    Member::Pane(pane) => {
 967                        is_leaf_pane[ix] = true;
 968                        if pane == render_cx.active_pane() {
 969                            active_pane_ix = Some(ix);
 970                            contains_active_pane = true;
 971                        }
 972                    }
 973                    Member::Axis(_) => {
 974                        is_leaf_pane[ix] = false;
 975                    }
 976                }
 977
 978                let result = member.render((basis + ix) * 10, zoomed, render_cx, window, cx);
 979                if result.contains_active_pane {
 980                    contains_active_pane = true;
 981                }
 982                result.element.into_any_element()
 983            })
 984            .collect::<Vec<_>>();
 985
 986        let element = pane_axis(
 987            self.axis,
 988            basis,
 989            self.flexes.clone(),
 990            self.bounding_boxes.clone(),
 991            render_cx.workspace().clone(),
 992        )
 993        .with_is_leaf_pane_mask(is_leaf_pane)
 994        .children(rendered_children)
 995        .with_active_pane(active_pane_ix)
 996        .into_any_element();
 997
 998        PaneRenderResult {
 999            element,
1000            contains_active_pane,
1001        }
1002    }
1003}
1004
1005#[derive(Clone, Copy, Debug, Deserialize, PartialEq, JsonSchema)]
1006#[serde(rename_all = "snake_case")]
1007pub enum SplitDirection {
1008    Up,
1009    Down,
1010    Left,
1011    Right,
1012}
1013
1014impl std::fmt::Display for SplitDirection {
1015    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1016        match self {
1017            SplitDirection::Up => write!(f, "up"),
1018            SplitDirection::Down => write!(f, "down"),
1019            SplitDirection::Left => write!(f, "left"),
1020            SplitDirection::Right => write!(f, "right"),
1021        }
1022    }
1023}
1024
1025impl SplitDirection {
1026    pub fn all() -> [Self; 4] {
1027        [Self::Up, Self::Down, Self::Left, Self::Right]
1028    }
1029
1030    pub fn vertical(cx: &mut App) -> Self {
1031        match WorkspaceSettings::get_global(cx).pane_split_direction_vertical {
1032            PaneSplitDirectionVertical::Left => SplitDirection::Left,
1033            PaneSplitDirectionVertical::Right => SplitDirection::Right,
1034        }
1035    }
1036
1037    pub fn horizontal(cx: &mut App) -> Self {
1038        match WorkspaceSettings::get_global(cx).pane_split_direction_horizontal {
1039            PaneSplitDirectionHorizontal::Down => SplitDirection::Down,
1040            PaneSplitDirectionHorizontal::Up => SplitDirection::Up,
1041        }
1042    }
1043
1044    pub fn edge(&self, rect: Bounds<Pixels>) -> Pixels {
1045        match self {
1046            Self::Up => rect.origin.y,
1047            Self::Down => rect.bottom_left().y,
1048            Self::Left => rect.bottom_left().x,
1049            Self::Right => rect.bottom_right().x,
1050        }
1051    }
1052
1053    pub fn along_edge(&self, bounds: Bounds<Pixels>, length: Pixels) -> Bounds<Pixels> {
1054        match self {
1055            Self::Up => Bounds {
1056                origin: bounds.origin,
1057                size: size(bounds.size.width, length),
1058            },
1059            Self::Down => Bounds {
1060                origin: point(bounds.bottom_left().x, bounds.bottom_left().y - length),
1061                size: size(bounds.size.width, length),
1062            },
1063            Self::Left => Bounds {
1064                origin: bounds.origin,
1065                size: size(length, bounds.size.height),
1066            },
1067            Self::Right => Bounds {
1068                origin: point(bounds.bottom_right().x - length, bounds.bottom_left().y),
1069                size: size(length, bounds.size.height),
1070            },
1071        }
1072    }
1073
1074    pub fn axis(&self) -> Axis {
1075        match self {
1076            Self::Up | Self::Down => Axis::Vertical,
1077            Self::Left | Self::Right => Axis::Horizontal,
1078        }
1079    }
1080
1081    pub fn increasing(&self) -> bool {
1082        match self {
1083            Self::Left | Self::Up => false,
1084            Self::Down | Self::Right => true,
1085        }
1086    }
1087
1088    pub fn opposite(&self) -> SplitDirection {
1089        match self {
1090            Self::Down => Self::Up,
1091            Self::Up => Self::Down,
1092            Self::Left => Self::Right,
1093            Self::Right => Self::Left,
1094        }
1095    }
1096}
1097
1098mod element {
1099    use std::mem;
1100    use std::{cell::RefCell, iter, rc::Rc, sync::Arc};
1101
1102    use gpui::{
1103        Along, AnyElement, App, Axis, BorderStyle, Bounds, Element, GlobalElementId,
1104        HitboxBehavior, IntoElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement,
1105        Pixels, Point, Size, Style, WeakEntity, Window, px, relative, size,
1106    };
1107    use gpui::{CursorStyle, Hitbox};
1108    use parking_lot::Mutex;
1109    use settings::Settings;
1110    use smallvec::SmallVec;
1111    use ui::prelude::*;
1112    use util::ResultExt;
1113
1114    use crate::Workspace;
1115
1116    use crate::WorkspaceSettings;
1117
1118    use super::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE};
1119
1120    const DIVIDER_SIZE: f32 = 1.0;
1121
1122    pub(super) fn pane_axis(
1123        axis: Axis,
1124        basis: usize,
1125        flexes: Arc<Mutex<Vec<f32>>>,
1126        bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
1127        workspace: WeakEntity<Workspace>,
1128    ) -> PaneAxisElement {
1129        PaneAxisElement {
1130            axis,
1131            basis,
1132            flexes,
1133            bounding_boxes,
1134            children: SmallVec::new(),
1135            active_pane_ix: None,
1136            workspace,
1137            is_leaf_pane_mask: Vec::new(),
1138        }
1139    }
1140
1141    pub struct PaneAxisElement {
1142        axis: Axis,
1143        basis: usize,
1144        /// Equivalent to ColumnWidths (but in terms of flexes instead of percentages)
1145        /// For example, flexes "1.33, 1, 1", instead of "40%, 30%, 30%"
1146        flexes: Arc<Mutex<Vec<f32>>>,
1147        bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
1148        children: SmallVec<[AnyElement; 2]>,
1149        active_pane_ix: Option<usize>,
1150        workspace: WeakEntity<Workspace>,
1151        // Track which children are leaf panes (Member::Pane) vs axes (Member::Axis)
1152        is_leaf_pane_mask: Vec<bool>,
1153    }
1154
1155    pub struct PaneAxisLayout {
1156        dragged_handle: Rc<RefCell<Option<usize>>>,
1157        children: Vec<PaneAxisChildLayout>,
1158    }
1159
1160    struct PaneAxisChildLayout {
1161        bounds: Bounds<Pixels>,
1162        element: AnyElement,
1163        handle: Option<PaneAxisHandleLayout>,
1164        is_leaf_pane: bool,
1165    }
1166
1167    struct PaneAxisHandleLayout {
1168        hitbox: Hitbox,
1169        divider_bounds: Bounds<Pixels>,
1170    }
1171
1172    impl PaneAxisElement {
1173        pub fn with_active_pane(mut self, active_pane_ix: Option<usize>) -> Self {
1174            self.active_pane_ix = active_pane_ix;
1175            self
1176        }
1177
1178        pub fn with_is_leaf_pane_mask(mut self, mask: Vec<bool>) -> Self {
1179            self.is_leaf_pane_mask = mask;
1180            self
1181        }
1182
1183        fn compute_resize(
1184            flexes: &Arc<Mutex<Vec<f32>>>,
1185            e: &MouseMoveEvent,
1186            ix: usize,
1187            axis: Axis,
1188            child_start: Point<Pixels>,
1189            container_size: Size<Pixels>,
1190            workspace: WeakEntity<Workspace>,
1191            window: &mut Window,
1192            cx: &mut App,
1193        ) {
1194            let min_size = match axis {
1195                Axis::Horizontal => px(HORIZONTAL_MIN_SIZE),
1196                Axis::Vertical => px(VERTICAL_MIN_SIZE),
1197            };
1198            let mut flexes = flexes.lock();
1199            debug_assert!(flex_values_in_bounds(flexes.as_slice()));
1200
1201            // Math to convert a flex value to a pixel value
1202            let size = move |ix, flexes: &[f32]| {
1203                container_size.along(axis) * (flexes[ix] / flexes.len() as f32)
1204            };
1205
1206            // Don't allow resizing to less than the minimum size, if elements are already too small
1207            if min_size - px(1.) > size(ix, flexes.as_slice()) {
1208                return;
1209            }
1210
1211            // This is basically a "bucket" of pixel changes that need to be applied in response to this
1212            // mouse event. Probably a small, fractional number like 0.5 or 1.5 pixels
1213            let mut proposed_current_pixel_change =
1214                (e.position - child_start).along(axis) - size(ix, flexes.as_slice());
1215
1216            // This takes a pixel change, and computes the flex changes that correspond to this pixel change
1217            // as well as the next one, for some reason
1218            let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
1219                let flex_change = pixel_dx / container_size.along(axis);
1220                let current_target_flex = flexes[target_ix] + flex_change;
1221                let next_target_flex = flexes[(target_ix as isize + next) as usize] - flex_change;
1222                (current_target_flex, next_target_flex)
1223            };
1224
1225            // Generate the list of flex successors, from the current index.
1226            // If you're dragging column 3 forward, out of 6 columns, then this code will produce [4, 5, 6]
1227            // If you're dragging column 3 backward, out of 6 columns, then this code will produce [2, 1, 0]
1228            let mut successors = iter::from_fn({
1229                let forward = proposed_current_pixel_change > px(0.);
1230                let mut ix_offset = 0;
1231                let len = flexes.len();
1232                move || {
1233                    let result = if forward {
1234                        (ix + 1 + ix_offset < len).then(|| ix + ix_offset)
1235                    } else {
1236                        (ix as isize - ix_offset as isize >= 0).then(|| ix - ix_offset)
1237                    };
1238
1239                    ix_offset += 1;
1240
1241                    result
1242                }
1243            });
1244
1245            // Now actually loop over these, and empty our bucket of pixel changes
1246            while proposed_current_pixel_change.abs() > px(0.) {
1247                let Some(current_ix) = successors.next() else {
1248                    break;
1249                };
1250
1251                let next_target_size = Pixels::max(
1252                    size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change,
1253                    min_size,
1254                );
1255
1256                let current_target_size = Pixels::max(
1257                    size(current_ix, flexes.as_slice()) + size(current_ix + 1, flexes.as_slice())
1258                        - next_target_size,
1259                    min_size,
1260                );
1261
1262                let current_pixel_change =
1263                    current_target_size - size(current_ix, flexes.as_slice());
1264
1265                let (current_target_flex, next_target_flex) =
1266                    flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice());
1267
1268                flexes[current_ix] = current_target_flex;
1269                flexes[current_ix + 1] = next_target_flex;
1270
1271                proposed_current_pixel_change -= current_pixel_change;
1272            }
1273
1274            workspace
1275                .update(cx, |this, cx| this.serialize_workspace(window, cx))
1276                .log_err();
1277            cx.stop_propagation();
1278            window.refresh();
1279        }
1280
1281        fn layout_handle(
1282            axis: Axis,
1283            pane_bounds: Bounds<Pixels>,
1284            window: &mut Window,
1285            _cx: &mut App,
1286        ) -> PaneAxisHandleLayout {
1287            let handle_bounds = Bounds {
1288                origin: pane_bounds.origin.apply_along(axis, |origin| {
1289                    origin + pane_bounds.size.along(axis) - px(HANDLE_HITBOX_SIZE / 2.)
1290                }),
1291                size: pane_bounds
1292                    .size
1293                    .apply_along(axis, |_| px(HANDLE_HITBOX_SIZE)),
1294            };
1295            let divider_bounds = Bounds {
1296                origin: pane_bounds
1297                    .origin
1298                    .apply_along(axis, |origin| origin + pane_bounds.size.along(axis)),
1299                size: pane_bounds.size.apply_along(axis, |_| px(DIVIDER_SIZE)),
1300            };
1301
1302            PaneAxisHandleLayout {
1303                hitbox: window.insert_hitbox(handle_bounds, HitboxBehavior::BlockMouse),
1304                divider_bounds,
1305            }
1306        }
1307    }
1308
1309    impl IntoElement for PaneAxisElement {
1310        type Element = Self;
1311
1312        fn into_element(self) -> Self::Element {
1313            self
1314        }
1315    }
1316
1317    impl Element for PaneAxisElement {
1318        type RequestLayoutState = ();
1319        type PrepaintState = PaneAxisLayout;
1320
1321        fn id(&self) -> Option<ElementId> {
1322            Some(self.basis.into())
1323        }
1324
1325        fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
1326            None
1327        }
1328
1329        fn request_layout(
1330            &mut self,
1331            _global_id: Option<&GlobalElementId>,
1332            _inspector_id: Option<&gpui::InspectorElementId>,
1333            window: &mut Window,
1334            cx: &mut App,
1335        ) -> (gpui::LayoutId, Self::RequestLayoutState) {
1336            let style = Style {
1337                flex_grow: 1.,
1338                flex_shrink: 1.,
1339                flex_basis: relative(0.).into(),
1340                size: size(relative(1.).into(), relative(1.).into()),
1341                ..Style::default()
1342            };
1343            (window.request_layout(style, None, cx), ())
1344        }
1345
1346        fn prepaint(
1347            &mut self,
1348            global_id: Option<&GlobalElementId>,
1349            _inspector_id: Option<&gpui::InspectorElementId>,
1350            bounds: Bounds<Pixels>,
1351            _state: &mut Self::RequestLayoutState,
1352            window: &mut Window,
1353            cx: &mut App,
1354        ) -> PaneAxisLayout {
1355            let dragged_handle = window.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
1356                global_id.unwrap(),
1357                |state, _cx| {
1358                    let state = state.unwrap_or_else(|| Rc::new(RefCell::new(None)));
1359                    (state.clone(), state)
1360                },
1361            );
1362            let flexes = self.flexes.lock().clone();
1363            let len = self.children.len();
1364            debug_assert!(flexes.len() == len);
1365            debug_assert!(flex_values_in_bounds(flexes.as_slice()));
1366
1367            let total_flex = len as f32;
1368
1369            let mut origin = bounds.origin;
1370            let space_per_flex = bounds.size.along(self.axis) / total_flex;
1371
1372            let mut bounding_boxes = self.bounding_boxes.lock();
1373            bounding_boxes.clear();
1374
1375            let mut layout = PaneAxisLayout {
1376                dragged_handle,
1377                children: Vec::new(),
1378            };
1379            for (ix, mut child) in mem::take(&mut self.children).into_iter().enumerate() {
1380                let child_flex = flexes[ix];
1381
1382                let child_size = bounds
1383                    .size
1384                    .apply_along(self.axis, |_| space_per_flex * child_flex)
1385                    .map(|d| d.round());
1386
1387                let child_bounds = Bounds {
1388                    origin,
1389                    size: child_size,
1390                };
1391
1392                bounding_boxes.push(Some(child_bounds));
1393                child.layout_as_root(child_size.into(), window, cx);
1394                child.prepaint_at(origin, window, cx);
1395
1396                origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
1397
1398                let is_leaf_pane = self.is_leaf_pane_mask.get(ix).copied().unwrap_or(true);
1399
1400                layout.children.push(PaneAxisChildLayout {
1401                    bounds: child_bounds,
1402                    element: child,
1403                    handle: None,
1404                    is_leaf_pane,
1405                })
1406            }
1407
1408            for (ix, child_layout) in layout.children.iter_mut().enumerate() {
1409                if ix < len - 1 {
1410                    child_layout.handle = Some(Self::layout_handle(
1411                        self.axis,
1412                        child_layout.bounds,
1413                        window,
1414                        cx,
1415                    ));
1416                }
1417            }
1418
1419            layout
1420        }
1421
1422        fn paint(
1423            &mut self,
1424            _id: Option<&GlobalElementId>,
1425            _inspector_id: Option<&gpui::InspectorElementId>,
1426            bounds: gpui::Bounds<ui::prelude::Pixels>,
1427            _: &mut Self::RequestLayoutState,
1428            layout: &mut Self::PrepaintState,
1429            window: &mut Window,
1430            cx: &mut App,
1431        ) {
1432            for child in &mut layout.children {
1433                child.element.paint(window, cx);
1434            }
1435
1436            let overlay_opacity = WorkspaceSettings::get(None, cx)
1437                .active_pane_modifiers
1438                .inactive_opacity
1439                .map(|val| val.0.clamp(0.0, 1.0))
1440                .and_then(|val| (val <= 1.).then_some(val));
1441
1442            let mut overlay_background = cx.theme().colors().editor_background;
1443            if let Some(opacity) = overlay_opacity {
1444                overlay_background.fade_out(opacity);
1445            }
1446
1447            let overlay_border = WorkspaceSettings::get(None, cx)
1448                .active_pane_modifiers
1449                .border_size
1450                .and_then(|val| (val >= 0.).then_some(val));
1451
1452            for (ix, child) in &mut layout.children.iter_mut().enumerate() {
1453                if overlay_opacity.is_some() || overlay_border.is_some() {
1454                    // the overlay has to be painted in origin+1px with size width-1px
1455                    // in order to accommodate the divider between panels
1456                    let overlay_bounds = Bounds {
1457                        origin: child
1458                            .bounds
1459                            .origin
1460                            .apply_along(Axis::Horizontal, |val| val + px(1.)),
1461                        size: child
1462                            .bounds
1463                            .size
1464                            .apply_along(Axis::Horizontal, |val| val - px(1.)),
1465                    };
1466
1467                    if overlay_opacity.is_some()
1468                        && child.is_leaf_pane
1469                        && self.active_pane_ix != Some(ix)
1470                    {
1471                        window.paint_quad(gpui::fill(overlay_bounds, overlay_background));
1472                    }
1473
1474                    if let Some(border) = overlay_border
1475                        && self.active_pane_ix == Some(ix)
1476                        && child.is_leaf_pane
1477                    {
1478                        window.paint_quad(gpui::quad(
1479                            overlay_bounds,
1480                            0.,
1481                            gpui::transparent_black(),
1482                            border,
1483                            cx.theme().colors().border_selected,
1484                            BorderStyle::Solid,
1485                        ));
1486                    }
1487                }
1488
1489                if let Some(handle) = child.handle.as_mut() {
1490                    let cursor_style = match self.axis {
1491                        Axis::Vertical => CursorStyle::ResizeRow,
1492                        Axis::Horizontal => CursorStyle::ResizeColumn,
1493                    };
1494
1495                    if layout
1496                        .dragged_handle
1497                        .borrow()
1498                        .is_some_and(|dragged_ix| dragged_ix == ix)
1499                    {
1500                        window.set_window_cursor_style(cursor_style);
1501                    } else {
1502                        window.set_cursor_style(cursor_style, &handle.hitbox);
1503                    }
1504
1505                    window.paint_quad(gpui::fill(
1506                        handle.divider_bounds,
1507                        cx.theme().colors().pane_group_border,
1508                    ));
1509
1510                    window.on_mouse_event({
1511                        let dragged_handle = layout.dragged_handle.clone();
1512                        let flexes = self.flexes.clone();
1513                        let workspace = self.workspace.clone();
1514                        let handle_hitbox = handle.hitbox.clone();
1515                        move |e: &MouseDownEvent, phase, window, cx| {
1516                            if phase.bubble() && handle_hitbox.is_hovered(window) {
1517                                dragged_handle.replace(Some(ix));
1518                                if e.click_count >= 2 {
1519                                    let mut borrow = flexes.lock();
1520                                    *borrow = vec![1.; borrow.len()];
1521                                    workspace
1522                                        .update(cx, |this, cx| this.serialize_workspace(window, cx))
1523                                        .log_err();
1524
1525                                    window.refresh();
1526                                }
1527                                cx.stop_propagation();
1528                            }
1529                        }
1530                    });
1531                    window.on_mouse_event({
1532                        let workspace = self.workspace.clone();
1533                        let dragged_handle = layout.dragged_handle.clone();
1534                        let flexes = self.flexes.clone();
1535                        let child_bounds = child.bounds;
1536                        let axis = self.axis;
1537                        move |e: &MouseMoveEvent, phase, window, cx| {
1538                            let dragged_handle = dragged_handle.borrow();
1539                            if phase.bubble() && *dragged_handle == Some(ix) {
1540                                Self::compute_resize(
1541                                    &flexes,
1542                                    e,
1543                                    ix,
1544                                    axis,
1545                                    child_bounds.origin,
1546                                    bounds.size,
1547                                    workspace.clone(),
1548                                    window,
1549                                    cx,
1550                                )
1551                            }
1552                        }
1553                    });
1554                }
1555            }
1556
1557            window.on_mouse_event({
1558                let dragged_handle = layout.dragged_handle.clone();
1559                move |_: &MouseUpEvent, phase, _window, _cx| {
1560                    if phase.bubble() {
1561                        dragged_handle.replace(None);
1562                    }
1563                }
1564            });
1565        }
1566    }
1567
1568    impl ParentElement for PaneAxisElement {
1569        fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
1570            self.children.extend(elements)
1571        }
1572    }
1573
1574    fn flex_values_in_bounds(flexes: &[f32]) -> bool {
1575        (flexes.iter().copied().sum::<f32>() - flexes.len() as f32).abs() < 0.001
1576    }
1577}