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