use crate::{ AppState, CollaboratorId, FollowerState, Pane, Workspace, WorkspaceSettings, pane_group::element::pane_axis, workspace_settings::{PaneSplitDirectionHorizontal, PaneSplitDirectionVertical},};use anyhow::Result;use call::{ActiveCall, ParticipantLocation};use collections::HashMap;use gpui::{ Along, AnyView, AnyWeakView, Axis, Bounds, Entity, Hsla, IntoElement, MouseButton, Pixels, Point, StyleRefinement, WeakEntity, Window, point, size,};use parking_lot::Mutex;use project::Project;use schemars::JsonSchema;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,} pub struct PaneRenderResult { pub element: gpui::AnyElement, pub contains_active_pane: bool,} impl PaneGroup { pub fn with_root(root: Member) -> Self { Self { root } } pub fn new(pane: Entity<Pane>) -> Self { Self { root: Member::Pane(pane), } } pub fn split( &mut self, old_pane: &Entity<Pane>, new_pane: &Entity<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 { anyhow::bail!("Pane not found"); } } Member::Axis(axis) => axis.split(old_pane, new_pane, direction), } } pub fn bounding_box_for_pane(&self, pane: &Entity<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<&Entity<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: &Entity<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: &Entity<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: &Entity<Pane>, to: &Entity<Pane>) { match &mut self.root { Member::Pane(_) => {} Member::Axis(axis) => axis.swap(from, to), }; } pub fn render( &self, zoomed: Option<&AnyWeakView>, render_cx: &dyn PaneLeaderDecorator, window: &mut Window, cx: &mut App, ) -> impl IntoElement { self.root.render(0, zoomed, render_cx, window, cx).element } pub fn panes(&self) -> Vec<&Entity<Pane>> { let mut panes = Vec::new(); self.root.collect_panes(&mut panes); panes } pub fn first_pane(&self) -> Entity<Pane> { self.root.first_pane() } pub fn last_pane(&self) -> Entity<Pane> { self.root.last_pane() } pub fn find_pane_in_direction( &mut self, active_pane: &Entity<Pane>, direction: SplitDirection, cx: &App, ) -> Option<&Entity<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) } pub fn invert_axies(&mut self) { self.root.invert_pane_axies(); }} #[derive(Debug, Clone)]pub enum Member { Axis(PaneAxis), Pane(Entity<Pane>),} #[derive(Clone, Copy)]pub struct PaneRenderContext<'a> { pub project: &'a Entity<Project>, pub follower_states: &'a HashMap<CollaboratorId, FollowerState>, pub active_call: Option<&'a Entity<ActiveCall>>, pub active_pane: &'a Entity<Pane>, pub app_state: &'a Arc<AppState>, pub workspace: &'a WeakEntity<Workspace>,} #[derive(Default)]pub struct LeaderDecoration { border: Option<Hsla>, status_box: Option<AnyElement>,} pub trait PaneLeaderDecorator { fn decorate(&self, pane: &Entity<Pane>, cx: &App) -> LeaderDecoration; fn active_pane(&self) -> &Entity<Pane>; fn workspace(&self) -> &WeakEntity<Workspace>;} pub struct ActivePaneDecorator<'a> { active_pane: &'a Entity<Pane>, workspace: &'a WeakEntity<Workspace>,} impl<'a> ActivePaneDecorator<'a> { pub fn new(active_pane: &'a Entity<Pane>, workspace: &'a WeakEntity<Workspace>) -> Self { Self { active_pane, workspace, } }} impl PaneLeaderDecorator for ActivePaneDecorator<'_> { fn decorate(&self, _: &Entity<Pane>, _: &App) -> LeaderDecoration { LeaderDecoration::default() } fn active_pane(&self) -> &Entity<Pane> { self.active_pane } fn workspace(&self) -> &WeakEntity<Workspace> { self.workspace }} impl PaneLeaderDecorator for PaneRenderContext<'_> { fn decorate(&self, pane: &Entity<Pane>, cx: &App) -> LeaderDecoration { let follower_state = self.follower_states.iter().find_map(|(leader_id, state)| { if state.center_pane == *pane { Some((*leader_id, state)) } else { None } }); let Some((leader_id, follower_state)) = follower_state else { return LeaderDecoration::default(); }; let mut leader_color; let status_box; match leader_id { CollaboratorId::PeerId(peer_id) => { let Some(leader) = self.active_call.as_ref().and_then(|call| { let room = call.read(cx).room()?.read(cx); room.remote_participant_for_peer_id(peer_id) }) else { return LeaderDecoration::default(); }; let is_in_unshared_view = follower_state.active_view_id.is_some_and(|view_id| { !follower_state .items_by_leader_view_id .contains_key(&view_id) }); let mut leader_join_data = None; let leader_status_box = match leader.location { ParticipantLocation::SharedProject { project_id: leader_project_id, } => { if Some(leader_project_id) == self.project.read(cx).remote_id() { is_in_unshared_view.then(|| { Label::new(format!( "{} is in an unshared pane", leader.user.github_login )) }) } 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 ))), }; status_box = leader_status_box.map(|status| { div() .absolute() .w_96() .bottom_3() .right_3() .elevation_2(cx) .p_1() .child(status) .when_some( leader_join_data, |this, (leader_project_id, leader_user_id)| { let app_state = self.app_state.clone(); this.cursor_pointer().on_mouse_down( MouseButton::Left, move |_, _, cx| { crate::join_in_room_project( leader_project_id, leader_user_id, app_state.clone(), cx, ) .detach_and_log_err(cx); }, ) }, ) .into_any_element() }); leader_color = cx .theme() .players() .color_for_participant(leader.participant_index.0) .cursor; } CollaboratorId::Agent => { status_box = None; leader_color = cx.theme().players().agent().cursor; } } let is_in_panel = follower_state.dock_pane.is_some(); if is_in_panel { leader_color.fade_out(0.75); } else { leader_color.fade_out(0.3); } LeaderDecoration { status_box, border: Some(leader_color), } } fn active_pane(&self) -> &Entity<Pane> { self.active_pane } fn workspace(&self) -> &WeakEntity<Workspace> { self.workspace }} impl Member { fn new_axis(old_pane: Entity<Pane>, new_pane: Entity<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 first_pane(&self) -> Entity<Pane> { match self { Member::Axis(axis) => axis.members[0].first_pane(), Member::Pane(pane) => pane.clone(), } } fn last_pane(&self) -> Entity<Pane> { match self { Member::Axis(axis) => axis.members.last().unwrap().last_pane(), Member::Pane(pane) => pane.clone(), } } pub fn render( &self, basis: usize, zoomed: Option<&AnyWeakView>, render_cx: &dyn PaneLeaderDecorator, window: &mut Window, cx: &mut App, ) -> PaneRenderResult { match self { Member::Pane(pane) => { if zoomed == Some(&pane.downgrade().into()) { return PaneRenderResult { element: div().into_any(), contains_active_pane: false, }; } let decoration = render_cx.decorate(pane, cx); let is_active = pane == render_cx.active_pane(); PaneRenderResult { element: div() .relative() .flex_1() .size_full() .child( AnyView::from(pane.clone()) .cached(StyleRefinement::default().v_flex().size_full()), ) .when_some(decoration.border, |this, color| { this.child( div() .absolute() .size_full() .left_0() .top_0() .border_2() .border_color(color), ) }) .children(decoration.status_box) .into_any(), contains_active_pane: is_active, } } Member::Axis(axis) => axis.render(basis + 1, zoomed, render_cx, window, cx), } } fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a Entity<Pane>>) { match self { Member::Axis(axis) => { for member in &axis.members { member.collect_panes(panes); } } Member::Pane(pane) => panes.push(pane), } } fn invert_pane_axies(&mut self) { match self { Self::Axis(axis) => { axis.axis = axis.axis.invert(); for member in axis.members.iter_mut() { member.invert_pane_axies(); } } Self::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 mut flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]); if flexes.len() != members.len() || (flexes.iter().copied().sum::<f32>() - flexes.len() as f32).abs() >= 0.001 { flexes = vec![1.; members.len()]; } let flexes = Arc::new(Mutex::new(flexes));Lines 1–500 of 1357. Load more
use crate::{ AppState, CollaboratorId, FollowerState, Pane, Workspace, WorkspaceSettings, pane_group::element::pane_axis, workspace_settings::{PaneSplitDirectionHorizontal, PaneSplitDirectionVertical}, }; use anyhow::Result; use call::{ActiveCall, ParticipantLocation}; use collections::HashMap; use gpui::{ Along, AnyView, AnyWeakView, Axis, Bounds, Entity, Hsla, IntoElement, MouseButton, Pixels, Point, StyleRefinement, WeakEntity, Window, point, size, }; use parking_lot::Mutex; use project::Project; use schemars::JsonSchema; 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, } pub struct PaneRenderResult { pub element: gpui::AnyElement, pub contains_active_pane: bool, } impl PaneGroup { pub fn with_root(root: Member) -> Self { Self { root } } pub fn new(pane: Entity<Pane>) -> Self { Self { root: Member::Pane(pane), } } pub fn split( &mut self, old_pane: &Entity<Pane>, new_pane: &Entity<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 { anyhow::bail!("Pane not found"); } } Member::Axis(axis) => axis.split(old_pane, new_pane, direction), } } pub fn bounding_box_for_pane(&self, pane: &Entity<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<&Entity<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: &Entity<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: &Entity<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: &Entity<Pane>, to: &Entity<Pane>) { match &mut self.root { Member::Pane(_) => {} Member::Axis(axis) => axis.swap(from, to), }; } pub fn render( &self, zoomed: Option<&AnyWeakView>, render_cx: &dyn PaneLeaderDecorator, window: &mut Window, cx: &mut App, ) -> impl IntoElement { self.root.render(0, zoomed, render_cx, window, cx).element } pub fn panes(&self) -> Vec<&Entity<Pane>> { let mut panes = Vec::new(); self.root.collect_panes(&mut panes); panes } pub fn first_pane(&self) -> Entity<Pane> { self.root.first_pane() } pub fn last_pane(&self) -> Entity<Pane> { self.root.last_pane() } pub fn find_pane_in_direction( &mut self, active_pane: &Entity<Pane>, direction: SplitDirection, cx: &App, ) -> Option<&Entity<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) } pub fn invert_axies(&mut self) { self.root.invert_pane_axies(); } } #[derive(Debug, Clone)] pub enum Member { Axis(PaneAxis), Pane(Entity<Pane>), } #[derive(Clone, Copy)] pub struct PaneRenderContext<'a> { pub project: &'a Entity<Project>, pub follower_states: &'a HashMap<CollaboratorId, FollowerState>, pub active_call: Option<&'a Entity<ActiveCall>>, pub active_pane: &'a Entity<Pane>, pub app_state: &'a Arc<AppState>, pub workspace: &'a WeakEntity<Workspace>, } #[derive(Default)] pub struct LeaderDecoration { border: Option<Hsla>, status_box: Option<AnyElement>, } pub trait PaneLeaderDecorator { fn decorate(&self, pane: &Entity<Pane>, cx: &App) -> LeaderDecoration; fn active_pane(&self) -> &Entity<Pane>; fn workspace(&self) -> &WeakEntity<Workspace>; } pub struct ActivePaneDecorator<'a> { active_pane: &'a Entity<Pane>, workspace: &'a WeakEntity<Workspace>, } impl<'a> ActivePaneDecorator<'a> { pub fn new(active_pane: &'a Entity<Pane>, workspace: &'a WeakEntity<Workspace>) -> Self { Self { active_pane, workspace, } } } impl PaneLeaderDecorator for ActivePaneDecorator<'_> { fn decorate(&self, _: &Entity<Pane>, _: &App) -> LeaderDecoration { LeaderDecoration::default() } fn active_pane(&self) -> &Entity<Pane> { self.active_pane } fn workspace(&self) -> &WeakEntity<Workspace> { self.workspace } } impl PaneLeaderDecorator for PaneRenderContext<'_> { fn decorate(&self, pane: &Entity<Pane>, cx: &App) -> LeaderDecoration { let follower_state = self.follower_states.iter().find_map(|(leader_id, state)| { if state.center_pane == *pane { Some((*leader_id, state)) } else { None } }); let Some((leader_id, follower_state)) = follower_state else { return LeaderDecoration::default(); }; let mut leader_color; let status_box; match leader_id { CollaboratorId::PeerId(peer_id) => { let Some(leader) = self.active_call.as_ref().and_then(|call| { let room = call.read(cx).room()?.read(cx); room.remote_participant_for_peer_id(peer_id) }) else { return LeaderDecoration::default(); }; let is_in_unshared_view = follower_state.active_view_id.is_some_and(|view_id| { !follower_state .items_by_leader_view_id .contains_key(&view_id) }); let mut leader_join_data = None; let leader_status_box = match leader.location { ParticipantLocation::SharedProject { project_id: leader_project_id, } => { if Some(leader_project_id) == self.project.read(cx).remote_id() { is_in_unshared_view.then(|| { Label::new(format!( "{} is in an unshared pane", leader.user.github_login )) }) } 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 ))), }; status_box = leader_status_box.map(|status| { div() .absolute() .w_96() .bottom_3() .right_3() .elevation_2(cx) .p_1() .child(status) .when_some( leader_join_data, |this, (leader_project_id, leader_user_id)| { let app_state = self.app_state.clone(); this.cursor_pointer().on_mouse_down( MouseButton::Left, move |_, _, cx| { crate::join_in_room_project( leader_project_id, leader_user_id, app_state.clone(), cx, ) .detach_and_log_err(cx); }, ) }, ) .into_any_element() }); leader_color = cx .theme() .players() .color_for_participant(leader.participant_index.0) .cursor; } CollaboratorId::Agent => { status_box = None; leader_color = cx.theme().players().agent().cursor; } } let is_in_panel = follower_state.dock_pane.is_some(); if is_in_panel { leader_color.fade_out(0.75); } else { leader_color.fade_out(0.3); } LeaderDecoration { status_box, border: Some(leader_color), } } fn active_pane(&self) -> &Entity<Pane> { self.active_pane } fn workspace(&self) -> &WeakEntity<Workspace> { self.workspace } } impl Member { fn new_axis(old_pane: Entity<Pane>, new_pane: Entity<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 first_pane(&self) -> Entity<Pane> { match self { Member::Axis(axis) => axis.members[0].first_pane(), Member::Pane(pane) => pane.clone(), } } fn last_pane(&self) -> Entity<Pane> { match self { Member::Axis(axis) => axis.members.last().unwrap().last_pane(), Member::Pane(pane) => pane.clone(), } } pub fn render( &self, basis: usize, zoomed: Option<&AnyWeakView>, render_cx: &dyn PaneLeaderDecorator, window: &mut Window, cx: &mut App, ) -> PaneRenderResult { match self { Member::Pane(pane) => { if zoomed == Some(&pane.downgrade().into()) { return PaneRenderResult { element: div().into_any(), contains_active_pane: false, }; } let decoration = render_cx.decorate(pane, cx); let is_active = pane == render_cx.active_pane(); PaneRenderResult { element: div() .relative() .flex_1() .size_full() .child( AnyView::from(pane.clone()) .cached(StyleRefinement::default().v_flex().size_full()), ) .when_some(decoration.border, |this, color| { this.child( div() .absolute() .size_full() .left_0() .top_0() .border_2() .border_color(color), ) }) .children(decoration.status_box) .into_any(), contains_active_pane: is_active, } } Member::Axis(axis) => axis.render(basis + 1, zoomed, render_cx, window, cx), } } fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a Entity<Pane>>) { match self { Member::Axis(axis) => { for member in &axis.members { member.collect_panes(panes); } } Member::Pane(pane) => panes.push(pane), } } fn invert_pane_axies(&mut self) { match self { Self::Axis(axis) => { axis.axis = axis.axis.invert(); for member in axis.members.iter_mut() { member.invert_pane_axies(); } } Self::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 mut flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]); if flexes.len() != members.len() || (flexes.iter().copied().sum::<f32>() - flexes.len() as f32).abs() >= 0.001 { flexes = vec![1.; members.len()]; } let flexes = Arc::new(Mutex::new(flexes));