pane_group.rs

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