pane_group.rs

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