zed/crates/workspace/src/pane_group.rs

451 lines
14 KiB
Rust
Raw Normal View History

use crate::{FollowerStatesByLeader, Pane};
2021-03-18 21:52:46 +00:00
use anyhow::{anyhow, Result};
use client::PeerId;
use collections::HashMap;
use gpui::{elements::*, Axis, Border, ViewHandle};
use project::Collaborator;
use serde::Deserialize;
2021-10-05 09:14:30 +00:00
use theme::Theme;
2021-03-18 21:52:46 +00:00
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PaneGroup {
root: Member,
}
impl PaneGroup {
pub fn new(pane: ViewHandle<Pane>) -> Self {
2021-03-18 21:52:46 +00:00
Self {
root: Member::Pane(pane),
2021-03-18 21:52:46 +00:00
}
}
pub fn split(
&mut self,
old_pane: &ViewHandle<Pane>,
new_pane: &ViewHandle<Pane>,
2021-03-18 21:52:46 +00:00
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);
2021-03-18 21:52:46 +00:00
Ok(())
} else {
Err(anyhow!("Pane not found"))
}
}
Member::Axis(axis) => axis.split(old_pane, new_pane, direction),
2021-03-18 21:52:46 +00:00
}
}
pub fn remove(&mut self, pane: &ViewHandle<Pane>) -> Result<bool> {
2021-03-18 21:52:46 +00:00
match &mut self.root {
Member::Pane(_) => Ok(false),
Member::Axis(axis) => {
if let Some(last_pane) = axis.remove(pane)? {
2021-03-18 21:52:46 +00:00
self.root = last_pane;
}
Ok(true)
}
}
}
pub(crate) fn render<'a>(
&self,
theme: &Theme,
follower_states: &FollowerStatesByLeader,
collaborators: &HashMap<PeerId, Collaborator>,
) -> ElementBox {
self.root.render(theme, follower_states, collaborators)
2021-03-18 21:52:46 +00:00
}
pub(crate) fn panes(&self) -> Vec<&ViewHandle<Pane>> {
let mut panes = Vec::new();
self.root.collect_panes(&mut panes);
panes
}
2021-03-18 21:52:46 +00:00
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum Member {
Axis(PaneAxis),
Pane(ViewHandle<Pane>),
2021-03-18 21:52:46 +00:00
}
impl Member {
fn new_axis(
old_pane: ViewHandle<Pane>,
new_pane: ViewHandle<Pane>,
direction: SplitDirection,
) -> Self {
2021-03-18 21:52:46 +00:00
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)],
2021-03-18 21:52:46 +00:00
};
Member::Axis(PaneAxis { axis, members })
}
pub fn render(
&self,
theme: &Theme,
follower_states: &FollowerStatesByLeader,
collaborators: &HashMap<PeerId, Collaborator>,
) -> ElementBox {
2021-03-18 21:52:46 +00:00
match self {
Member::Pane(pane) => {
let mut border = Border::default();
let leader = follower_states
.iter()
.find_map(|(leader_id, follower_states)| {
if follower_states.contains_key(pane) {
Some(leader_id)
} else {
None
}
})
.and_then(|leader_id| collaborators.get(leader_id));
if let Some(leader) = leader {
let leader_color = theme
.editor
.replica_selection_style(leader.replica_id)
.cursor;
border = Border::all(theme.workspace.leader_border_width, leader_color);
border
.color
.fade_out(1. - theme.workspace.leader_border_opacity);
border.overlay = true;
}
ChildView::new(pane).contained().with_border(border).boxed()
}
Member::Axis(axis) => axis.render(theme, follower_states, collaborators),
2021-03-18 21:52:46 +00:00
}
}
fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a ViewHandle<Pane>>) {
match self {
Member::Axis(axis) => {
for member in &axis.members {
member.collect_panes(panes);
}
}
Member::Pane(pane) => panes.push(pane),
}
}
2021-03-18 21:52:46 +00:00
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct PaneAxis {
axis: Axis,
members: Vec<Member>,
}
impl PaneAxis {
fn split(
&mut self,
old_pane: &ViewHandle<Pane>,
new_pane: &ViewHandle<Pane>,
2021-03-18 21:52:46 +00:00
direction: SplitDirection,
) -> Result<()> {
use SplitDirection::*;
for (idx, member) in self.members.iter_mut().enumerate() {
match member {
Member::Axis(axis) => {
if axis.split(old_pane, new_pane, direction).is_ok() {
2021-03-18 21:52:46 +00:00
return Ok(());
}
}
Member::Pane(pane) => {
if pane == old_pane {
2021-03-18 21:52:46 +00:00
if direction.matches_axis(self.axis) {
match direction {
Up | Left => {
self.members.insert(idx, Member::Pane(new_pane.clone()));
2021-03-18 21:52:46 +00:00
}
Down | Right => {
self.members.insert(idx + 1, Member::Pane(new_pane.clone()));
2021-03-18 21:52:46 +00:00
}
}
} else {
*member =
Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
2021-03-18 21:52:46 +00:00
}
return Ok(());
}
}
}
}
Err(anyhow!("Pane not found"))
}
fn remove(&mut self, pane_to_remove: &ViewHandle<Pane>) -> Result<Option<Member>> {
2021-03-18 21:52:46 +00:00
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) {
2021-03-18 21:52:46 +00:00
if let Some(last_pane) = last_pane {
*member = last_pane;
}
found_pane = true;
break;
}
}
Member::Pane(pane) => {
if pane == pane_to_remove {
2021-03-18 21:52:46 +00:00
found_pane = true;
remove_member = Some(idx);
break;
}
}
}
}
if found_pane {
if let Some(idx) = remove_member {
self.members.remove(idx);
}
if self.members.len() == 1 {
Ok(self.members.pop())
} else {
Ok(None)
}
} else {
Err(anyhow!("Pane not found"))
}
}
fn render(
&self,
theme: &Theme,
follower_state: &FollowerStatesByLeader,
collaborators: &HashMap<PeerId, Collaborator>,
) -> ElementBox {
2021-03-18 21:52:46 +00:00
let last_member_ix = self.members.len() - 1;
Flex::new(self.axis)
.with_children(self.members.iter().enumerate().map(|(ix, member)| {
let mut member = member.render(theme, follower_state, collaborators);
2021-03-18 21:52:46 +00:00
if ix < last_member_ix {
let mut border = theme.workspace.pane_divider;
border.left = false;
border.right = false;
border.top = false;
border.bottom = false;
2021-03-18 21:52:46 +00:00
match self.axis {
Axis::Vertical => border.bottom = true,
Axis::Horizontal => border.right = true,
}
member = Container::new(member).with_border(border).boxed();
}
FlexItem::new(member).flex(1.0, true).boxed()
2021-03-18 21:52:46 +00:00
}))
.boxed()
}
}
#[derive(Clone, Copy, Debug, Deserialize)]
2021-03-18 21:52:46 +00:00
pub enum SplitDirection {
Up,
Down,
Left,
Right,
}
impl SplitDirection {
fn matches_axis(self, orientation: Axis) -> bool {
use Axis::*;
use SplitDirection::*;
match self {
Up | Down => match orientation {
Vertical => true,
Horizontal => false,
},
Left | Right => match orientation {
Vertical => false,
Horizontal => true,
},
}
}
}
#[cfg(test)]
mod tests {
// use super::*;
// use serde_json::json;
// #[test]
// fn test_split_and_remove() -> Result<()> {
// let mut group = PaneGroup::new(1);
// assert_eq!(
// serde_json::to_value(&group)?,
// json!({
// "type": "pane",
// "paneId": 1,
// })
// );
// group.split(1, 2, SplitDirection::Right)?;
// assert_eq!(
// serde_json::to_value(&group)?,
// json!({
// "type": "axis",
// "orientation": "horizontal",
// "members": [
// {"type": "pane", "paneId": 1},
// {"type": "pane", "paneId": 2},
// ]
// })
// );
// group.split(2, 3, SplitDirection::Up)?;
// assert_eq!(
// serde_json::to_value(&group)?,
// json!({
// "type": "axis",
// "orientation": "horizontal",
// "members": [
// {"type": "pane", "paneId": 1},
// {
// "type": "axis",
// "orientation": "vertical",
// "members": [
// {"type": "pane", "paneId": 3},
// {"type": "pane", "paneId": 2},
// ]
// },
// ]
// })
// );
// group.split(1, 4, SplitDirection::Right)?;
// assert_eq!(
// serde_json::to_value(&group)?,
// json!({
// "type": "axis",
// "orientation": "horizontal",
// "members": [
// {"type": "pane", "paneId": 1},
// {"type": "pane", "paneId": 4},
// {
// "type": "axis",
// "orientation": "vertical",
// "members": [
// {"type": "pane", "paneId": 3},
// {"type": "pane", "paneId": 2},
// ]
// },
// ]
// })
// );
// group.split(2, 5, SplitDirection::Up)?;
// assert_eq!(
// serde_json::to_value(&group)?,
// json!({
// "type": "axis",
// "orientation": "horizontal",
// "members": [
// {"type": "pane", "paneId": 1},
// {"type": "pane", "paneId": 4},
// {
// "type": "axis",
// "orientation": "vertical",
// "members": [
// {"type": "pane", "paneId": 3},
// {"type": "pane", "paneId": 5},
// {"type": "pane", "paneId": 2},
// ]
// },
// ]
// })
// );
// assert_eq!(true, group.remove(5)?);
// assert_eq!(
// serde_json::to_value(&group)?,
// json!({
// "type": "axis",
// "orientation": "horizontal",
// "members": [
// {"type": "pane", "paneId": 1},
// {"type": "pane", "paneId": 4},
// {
// "type": "axis",
// "orientation": "vertical",
// "members": [
// {"type": "pane", "paneId": 3},
// {"type": "pane", "paneId": 2},
// ]
// },
// ]
// })
// );
// assert_eq!(true, group.remove(4)?);
// assert_eq!(
// serde_json::to_value(&group)?,
// json!({
// "type": "axis",
// "orientation": "horizontal",
// "members": [
// {"type": "pane", "paneId": 1},
// {
// "type": "axis",
// "orientation": "vertical",
// "members": [
// {"type": "pane", "paneId": 3},
// {"type": "pane", "paneId": 2},
// ]
// },
// ]
// })
// );
// assert_eq!(true, group.remove(3)?);
// assert_eq!(
// serde_json::to_value(&group)?,
// json!({
// "type": "axis",
// "orientation": "horizontal",
// "members": [
// {"type": "pane", "paneId": 1},
// {"type": "pane", "paneId": 2},
// ]
// })
// );
// assert_eq!(true, group.remove(2)?);
// assert_eq!(
// serde_json::to_value(&group)?,
// json!({
// "type": "pane",
// "paneId": 1,
// })
// );
// assert_eq!(false, group.remove(1)?);
// assert_eq!(
// serde_json::to_value(&group)?,
// json!({
// "type": "pane",
// "paneId": 1,
// })
// );
// Ok(())
// }
}