1253 lines · 42.9 KB · Rust
RawDownload
use crate::{    pane_group::element::pane_axis,    workspace_settings::{PaneSplitDirectionHorizontal, PaneSplitDirectionVertical},    AppState, FollowerState, Pane, Workspace, WorkspaceSettings,};use anyhow::{anyhow, Result};use call::{ActiveCall, ParticipantLocation};use client::proto::PeerId;use collections::HashMap;use gpui::{    point, size, Along, AnyView, AnyWeakView, Axis, Bounds, IntoElement, Model, MouseButton,    Pixels, Point, StyleRefinement, View, ViewContext,};use parking_lot::Mutex;use project::Project;use serde::Deserialize;use settings::Settings;use std::sync::Arc;use ui::prelude::*; pub const HANDLE_HITBOX_SIZE: f32 = 4.0;const HORIZONTAL_MIN_SIZE: f32 = 80.;const VERTICAL_MIN_SIZE: f32 = 100.; /// One or many panes, arranged in a horizontal or vertical axis due to a split./// Panes have all their tabs and capabilities preserved, and can be split again or resized./// Single-pane group is a regular pane.#[derive(Clone)]pub struct PaneGroup {    pub root: Member,} impl PaneGroup {    pub fn with_root(root: Member) -> Self {        Self { root }    }     pub fn new(pane: View<Pane>) -> Self {        Self {            root: Member::Pane(pane),        }    }     pub fn split(        &mut self,        old_pane: &View<Pane>,        new_pane: &View<Pane>,        direction: SplitDirection,    ) -> Result<()> {        match &mut self.root {            Member::Pane(pane) => {                if pane == old_pane {                    self.root = Member::new_axis(old_pane.clone(), new_pane.clone(), direction);                    Ok(())                } else {                    Err(anyhow!("Pane not found"))                }            }            Member::Axis(axis) => axis.split(old_pane, new_pane, direction),        }    }     pub fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {        match &self.root {            Member::Pane(_) => None,            Member::Axis(axis) => axis.bounding_box_for_pane(pane),        }    }     pub fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&View<Pane>> {        match &self.root {            Member::Pane(pane) => Some(pane),            Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),        }    }     /// Returns:    /// - Ok(true) if it found and removed a pane    /// - Ok(false) if it found but did not remove the pane    /// - Err(_) if it did not find the pane    pub fn remove(&mut self, pane: &View<Pane>) -> Result<bool> {        match &mut self.root {            Member::Pane(_) => Ok(false),            Member::Axis(axis) => {                if let Some(last_pane) = axis.remove(pane)? {                    self.root = last_pane;                }                Ok(true)            }        }    }     pub fn resize(        &mut self,        pane: &View<Pane>,        direction: Axis,        amount: Pixels,        bounds: &Bounds<Pixels>,    ) {        match &mut self.root {            Member::Pane(_) => {}            Member::Axis(axis) => {                let _ = axis.resize(pane, direction, amount, bounds);            }        };    }     pub fn reset_pane_sizes(&mut self) {        match &mut self.root {            Member::Pane(_) => {}            Member::Axis(axis) => {                let _ = axis.reset_pane_sizes();            }        };    }     pub fn swap(&mut self, from: &View<Pane>, to: &View<Pane>) {        match &mut self.root {            Member::Pane(_) => {}            Member::Axis(axis) => axis.swap(from, to),        };    }     #[allow(clippy::too_many_arguments)]    pub fn render(        &self,        project: &Model<Project>,        follower_states: &HashMap<PeerId, FollowerState>,        active_call: Option<&Model<ActiveCall>>,        active_pane: &View<Pane>,        zoomed: Option<&AnyWeakView>,        app_state: &Arc<AppState>,        cx: &mut ViewContext<Workspace>,    ) -> impl IntoElement {        self.root.render(            project,            0,            follower_states,            active_call,            active_pane,            zoomed,            app_state,            cx,        )    }     pub fn panes(&self) -> Vec<&View<Pane>> {        let mut panes = Vec::new();        self.root.collect_panes(&mut panes);        panes    }     pub fn first_pane(&self) -> View<Pane> {        self.root.first_pane()    }     pub fn find_pane_in_direction(        &mut self,        active_pane: &View<Pane>,        direction: SplitDirection,        cx: &WindowContext,    ) -> Option<&View<Pane>> {        let bounding_box = self.bounding_box_for_pane(active_pane)?;        let cursor = active_pane.read(cx).pixel_position_of_cursor(cx);        let center = match cursor {            Some(cursor) if bounding_box.contains(&cursor) => cursor,            _ => bounding_box.center(),        };         let distance_to_next = crate::HANDLE_HITBOX_SIZE;         let target = match direction {            SplitDirection::Left => {                Point::new(bounding_box.left() - distance_to_next.into(), center.y)            }            SplitDirection::Right => {                Point::new(bounding_box.right() + distance_to_next.into(), center.y)            }            SplitDirection::Up => {                Point::new(center.x, bounding_box.top() - distance_to_next.into())            }            SplitDirection::Down => {                Point::new(center.x, bounding_box.bottom() + distance_to_next.into())            }        };        self.pane_at_pixel_position(target)    }} #[derive(Debug, Clone)]pub enum Member {    Axis(PaneAxis),    Pane(View<Pane>),} impl Member {    fn new_axis(old_pane: View<Pane>, new_pane: View<Pane>, direction: SplitDirection) -> Self {        use Axis::*;        use SplitDirection::*;         let axis = match direction {            Up | Down => Vertical,            Left | Right => Horizontal,        };         let members = match direction {            Up | Left => vec![Member::Pane(new_pane), Member::Pane(old_pane)],            Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)],        };         Member::Axis(PaneAxis::new(axis, members))    }     fn contains(&self, needle: &View<Pane>) -> bool {        match self {            Member::Axis(axis) => axis.members.iter().any(|member| member.contains(needle)),            Member::Pane(pane) => pane == needle,        }    }     fn first_pane(&self) -> View<Pane> {        match self {            Member::Axis(axis) => axis.members[0].first_pane(),            Member::Pane(pane) => pane.clone(),        }    }     #[allow(clippy::too_many_arguments)]    pub fn render(        &self,        project: &Model<Project>,        basis: usize,        follower_states: &HashMap<PeerId, FollowerState>,        active_call: Option<&Model<ActiveCall>>,        active_pane: &View<Pane>,        zoomed: Option<&AnyWeakView>,        app_state: &Arc<AppState>,        cx: &mut ViewContext<Workspace>,    ) -> impl IntoElement {        match self {            Member::Pane(pane) => {                if zoomed == Some(&pane.downgrade().into()) {                    return div().into_any();                }                 let follower_state = follower_states.iter().find_map(|(leader_id, state)| {                    if state.center_pane == *pane {                        Some((*leader_id, state))                    } else {                        None                    }                });                 let leader = follower_state.as_ref().and_then(|(leader_id, _)| {                    let room = active_call?.read(cx).room()?.read(cx);                    room.remote_participant_for_peer_id(*leader_id)                });                 let is_in_unshared_view = follower_state.as_ref().map_or(false, |(_, state)| {                    state.active_view_id.is_some_and(|view_id| {                        !state.items_by_leader_view_id.contains_key(&view_id)                    })                });                 let is_in_panel = follower_state                    .as_ref()                    .map_or(false, |(_, state)| state.dock_pane.is_some());                 let mut leader_border = None;                let mut leader_status_box = None;                let mut leader_join_data = None;                if let Some(leader) = &leader {                    let mut leader_color = cx                        .theme()                        .players()                        .color_for_participant(leader.participant_index.0)                        .cursor;                    if is_in_panel {                        leader_color.fade_out(0.75);                    } else {                        leader_color.fade_out(0.3);                    }                    leader_border = Some(leader_color);                     leader_status_box = match leader.location {                        ParticipantLocation::SharedProject {                            project_id: leader_project_id,                        } => {                            if Some(leader_project_id) == project.read(cx).remote_id() {                                if is_in_unshared_view {                                    Some(Label::new(format!(                                        "{} is in an unshared pane",                                        leader.user.github_login                                    )))                                } else {                                    None                                }                            } else {                                leader_join_data = Some((leader_project_id, leader.user.id));                                Some(Label::new(format!(                                    "Follow {} to their active project",                                    leader.user.github_login,                                )))                            }                        }                        ParticipantLocation::UnsharedProject => Some(Label::new(format!(                            "{} is viewing an unshared Zed project",                            leader.user.github_login                        ))),                        ParticipantLocation::External => Some(Label::new(format!(                            "{} is viewing a window outside of Zed",                            leader.user.github_login                        ))),                    };                }                 div()                    .relative()                    .flex_1()                    .size_full()                    .child(                        AnyView::from(pane.clone())                            .cached(StyleRefinement::default().v_flex().size_full()),                    )                    .when_some(leader_border, |this, color| {                        this.child(                            div()                                .absolute()                                .size_full()                                .left_0()                                .top_0()                                .border_2()                                .border_color(color),                        )                    })                    .when_some(leader_status_box, |this, status_box| {                        this.child(                            div()                                .absolute()                                .w_96()                                .bottom_3()                                .right_3()                                .elevation_2(cx)                                .p_1()                                .child(status_box)                                .when_some(                                    leader_join_data,                                    |this, (leader_project_id, leader_user_id)| {                                        this.cursor_pointer().on_mouse_down(                                            MouseButton::Left,                                            cx.listener(move |this, _, cx| {                                                crate::join_in_room_project(                                                    leader_project_id,                                                    leader_user_id,                                                    this.app_state().clone(),                                                    cx,                                                )                                                .detach_and_log_err(cx);                                            }),                                        )                                    },                                ),                        )                    })                    .into_any()            }            Member::Axis(axis) => axis                .render(                    project,                    basis + 1,                    follower_states,                    active_call,                    active_pane,                    zoomed,                    app_state,                    cx,                )                .into_any(),        }    }     fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a View<Pane>>) {        match self {            Member::Axis(axis) => {                for member in &axis.members {                    member.collect_panes(panes);                }            }            Member::Pane(pane) => panes.push(pane),        }    }} #[derive(Debug, Clone)]pub struct PaneAxis {    pub axis: Axis,    pub members: Vec<Member>,    pub flexes: Arc<Mutex<Vec<f32>>>,    pub bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,} impl PaneAxis {    pub fn new(axis: Axis, members: Vec<Member>) -> Self {        let flexes = Arc::new(Mutex::new(vec![1.; members.len()]));        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));        Self {            axis,            members,            flexes,            bounding_boxes,        }    }     pub fn load(axis: Axis, members: Vec<Member>, flexes: Option<Vec<f32>>) -> Self {        let flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]);        // debug_assert!(members.len() == flexes.len());         let flexes = Arc::new(Mutex::new(flexes));        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));        Self {            axis,            members,            flexes,            bounding_boxes,        }    }     fn split(        &mut self,        old_pane: &View<Pane>,        new_pane: &View<Pane>,        direction: SplitDirection,    ) -> Result<()> {        for (mut idx, member) in self.members.iter_mut().enumerate() {            match member {                Member::Axis(axis) => {                    if axis.split(old_pane, new_pane, direction).is_ok() {                        return Ok(());                    }                }                Member::Pane(pane) => {                    if pane == old_pane {                        if direction.axis() == self.axis {                            if direction.increasing() {                                idx += 1;                            }                             self.members.insert(idx, Member::Pane(new_pane.clone()));                            *self.flexes.lock() = vec![1.; self.members.len()];                        } else {                            *member =                                Member::new_axis(old_pane.clone(), new_pane.clone(), direction);                        }                        return Ok(());                    }                }            }        }        Err(anyhow!("Pane not found"))    }     fn remove(&mut self, pane_to_remove: &View<Pane>) -> Result<Option<Member>> {        let mut found_pane = false;        let mut remove_member = None;        for (idx, member) in self.members.iter_mut().enumerate() {            match member {                Member::Axis(axis) => {                    if let Ok(last_pane) = axis.remove(pane_to_remove) {                        if let Some(last_pane) = last_pane {                            *member = last_pane;                        }                        found_pane = true;                        break;                    }                }                Member::Pane(pane) => {                    if pane == pane_to_remove {                        found_pane = true;                        remove_member = Some(idx);                        break;                    }                }            }        }         if found_pane {            if let Some(idx) = remove_member {                self.members.remove(idx);                *self.flexes.lock() = vec![1.; self.members.len()];            }             if self.members.len() == 1 {                let result = self.members.pop();                *self.flexes.lock() = vec![1.; self.members.len()];                Ok(result)            } else {                Ok(None)            }        } else {            Err(anyhow!("Pane not found"))

Lines 1–500 of 1253.