WIP: Resizing splits (#2715)
We're finally doing the thing. TODO: - [x] Choose an approach - Decided to add a new element just for the pane axis, containing a slimmed down copy of the flex code. - [x] Wire through callbacks and pointers so that data goes where it needs to - [x] Do the flex juggling math on resize - [x] Update the flexes when updating the split tree - [x] Restore the active_pane_magnification setting - [x] Serialize an axis' flexes Release Notes: - Made the center pane group splits resizable. Note that resizing is disabled if the `active_pane_magnification` setting is changed from default.
This commit is contained in:
commit
b7ed467690
7 changed files with 558 additions and 106 deletions
|
@ -1182,8 +1182,10 @@ impl EditorElement {
|
||||||
});
|
});
|
||||||
scene.push_mouse_region(
|
scene.push_mouse_region(
|
||||||
MouseRegion::new::<ScrollbarMouseHandlers>(cx.view_id(), cx.view_id(), track_bounds)
|
MouseRegion::new::<ScrollbarMouseHandlers>(cx.view_id(), cx.view_id(), track_bounds)
|
||||||
.on_move(move |_, editor: &mut Editor, cx| {
|
.on_move(move |event, editor: &mut Editor, cx| {
|
||||||
|
if event.pressed_button.is_none() {
|
||||||
editor.scroll_manager.show_scrollbar(cx);
|
editor.scroll_manager.show_scrollbar(cx);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.on_down(MouseButton::Left, {
|
.on_down(MouseButton::Left, {
|
||||||
let row_range = row_range.clone();
|
let row_range = row_range.clone();
|
||||||
|
|
|
@ -1268,6 +1268,19 @@ impl Vector2FExt for Vector2F {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait RectFExt {
|
||||||
|
fn length_along(self, axis: Axis) -> f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RectFExt for RectF {
|
||||||
|
fn length_along(self, axis: Axis) -> f32 {
|
||||||
|
match axis {
|
||||||
|
Axis::Horizontal => self.width(),
|
||||||
|
Axis::Vertical => self.height(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct SizeConstraint {
|
pub struct SizeConstraint {
|
||||||
pub min: Vector2F,
|
pub min: Vector2F,
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub mod json;
|
||||||
pub mod keymap_matcher;
|
pub mod keymap_matcher;
|
||||||
pub mod platform;
|
pub mod platform;
|
||||||
pub use gpui_macros::{test, Element};
|
pub use gpui_macros::{test, Element};
|
||||||
pub use window::{Axis, SizeConstraint, Vector2FExt, WindowContext};
|
pub use window::{Axis, RectFExt, SizeConstraint, Vector2FExt, WindowContext};
|
||||||
|
|
||||||
pub use anyhow;
|
pub use anyhow;
|
||||||
pub use serde_json;
|
pub use serde_json;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use std::sync::Arc;
|
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use crate::{AppState, FollowerStatesByLeader, Pane, Workspace, WorkspaceSettings};
|
use crate::{
|
||||||
|
pane_group::element::PaneAxisElement, AppState, FollowerStatesByLeader, Pane, Workspace,
|
||||||
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use call::{ActiveCall, ParticipantLocation};
|
use call::{ActiveCall, ParticipantLocation};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -13,7 +15,11 @@ use project::Project;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use theme::Theme;
|
use theme::Theme;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
const HANDLE_HITBOX_SIZE: f32 = 4.0;
|
||||||
|
const HORIZONTAL_MIN_SIZE: f32 = 80.;
|
||||||
|
const VERTICAL_MIN_SIZE: f32 = 100.;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PaneGroup {
|
pub struct PaneGroup {
|
||||||
pub(crate) root: Member,
|
pub(crate) root: Member,
|
||||||
}
|
}
|
||||||
|
@ -77,6 +83,7 @@ impl PaneGroup {
|
||||||
) -> AnyElement<Workspace> {
|
) -> AnyElement<Workspace> {
|
||||||
self.root.render(
|
self.root.render(
|
||||||
project,
|
project,
|
||||||
|
0,
|
||||||
theme,
|
theme,
|
||||||
follower_states,
|
follower_states,
|
||||||
active_call,
|
active_call,
|
||||||
|
@ -94,7 +101,7 @@ impl PaneGroup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub(crate) enum Member {
|
pub(crate) enum Member {
|
||||||
Axis(PaneAxis),
|
Axis(PaneAxis),
|
||||||
Pane(ViewHandle<Pane>),
|
Pane(ViewHandle<Pane>),
|
||||||
|
@ -119,7 +126,7 @@ impl Member {
|
||||||
Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)],
|
Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)],
|
||||||
};
|
};
|
||||||
|
|
||||||
Member::Axis(PaneAxis { axis, members })
|
Member::Axis(PaneAxis::new(axis, members))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contains(&self, needle: &ViewHandle<Pane>) -> bool {
|
fn contains(&self, needle: &ViewHandle<Pane>) -> bool {
|
||||||
|
@ -132,6 +139,7 @@ impl Member {
|
||||||
pub fn render(
|
pub fn render(
|
||||||
&self,
|
&self,
|
||||||
project: &ModelHandle<Project>,
|
project: &ModelHandle<Project>,
|
||||||
|
basis: usize,
|
||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
follower_states: &FollowerStatesByLeader,
|
follower_states: &FollowerStatesByLeader,
|
||||||
active_call: Option<&ModelHandle<ActiveCall>>,
|
active_call: Option<&ModelHandle<ActiveCall>>,
|
||||||
|
@ -272,6 +280,7 @@ impl Member {
|
||||||
}
|
}
|
||||||
Member::Axis(axis) => axis.render(
|
Member::Axis(axis) => axis.render(
|
||||||
project,
|
project,
|
||||||
|
basis + 1,
|
||||||
theme,
|
theme,
|
||||||
follower_states,
|
follower_states,
|
||||||
active_call,
|
active_call,
|
||||||
|
@ -295,13 +304,35 @@ impl Member {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub(crate) struct PaneAxis {
|
pub(crate) struct PaneAxis {
|
||||||
pub axis: Axis,
|
pub axis: Axis,
|
||||||
pub members: Vec<Member>,
|
pub members: Vec<Member>,
|
||||||
|
pub flexes: Rc<RefCell<Vec<f32>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaneAxis {
|
impl PaneAxis {
|
||||||
|
pub fn new(axis: Axis, members: Vec<Member>) -> Self {
|
||||||
|
let flexes = Rc::new(RefCell::new(vec![1.; members.len()]));
|
||||||
|
Self {
|
||||||
|
axis,
|
||||||
|
members,
|
||||||
|
flexes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = Rc::new(RefCell::new(flexes));
|
||||||
|
Self {
|
||||||
|
axis,
|
||||||
|
members,
|
||||||
|
flexes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn split(
|
fn split(
|
||||||
&mut self,
|
&mut self,
|
||||||
old_pane: &ViewHandle<Pane>,
|
old_pane: &ViewHandle<Pane>,
|
||||||
|
@ -323,6 +354,7 @@ impl PaneAxis {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.members.insert(idx, Member::Pane(new_pane.clone()));
|
self.members.insert(idx, Member::Pane(new_pane.clone()));
|
||||||
|
*self.flexes.borrow_mut() = vec![1.; self.members.len()];
|
||||||
} else {
|
} else {
|
||||||
*member =
|
*member =
|
||||||
Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
|
Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
|
||||||
|
@ -362,6 +394,7 @@ impl PaneAxis {
|
||||||
if found_pane {
|
if found_pane {
|
||||||
if let Some(idx) = remove_member {
|
if let Some(idx) = remove_member {
|
||||||
self.members.remove(idx);
|
self.members.remove(idx);
|
||||||
|
*self.flexes.borrow_mut() = vec![1.; self.members.len()];
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.members.len() == 1 {
|
if self.members.len() == 1 {
|
||||||
|
@ -377,6 +410,7 @@ impl PaneAxis {
|
||||||
fn render(
|
fn render(
|
||||||
&self,
|
&self,
|
||||||
project: &ModelHandle<Project>,
|
project: &ModelHandle<Project>,
|
||||||
|
basis: usize,
|
||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
follower_state: &FollowerStatesByLeader,
|
follower_state: &FollowerStatesByLeader,
|
||||||
active_call: Option<&ModelHandle<ActiveCall>>,
|
active_call: Option<&ModelHandle<ActiveCall>>,
|
||||||
|
@ -385,16 +419,22 @@ impl PaneAxis {
|
||||||
app_state: &Arc<AppState>,
|
app_state: &Arc<AppState>,
|
||||||
cx: &mut ViewContext<Workspace>,
|
cx: &mut ViewContext<Workspace>,
|
||||||
) -> AnyElement<Workspace> {
|
) -> AnyElement<Workspace> {
|
||||||
let last_member_ix = self.members.len() - 1;
|
debug_assert!(self.members.len() == self.flexes.borrow().len());
|
||||||
Flex::new(self.axis)
|
|
||||||
.with_children(self.members.iter().enumerate().map(|(ix, member)| {
|
let mut pane_axis = PaneAxisElement::new(self.axis, basis, self.flexes.clone());
|
||||||
let mut flex = 1.0;
|
let mut active_pane_ix = None;
|
||||||
|
|
||||||
|
let mut members = self.members.iter().enumerate().peekable();
|
||||||
|
while let Some((ix, member)) = members.next() {
|
||||||
|
let last = members.peek().is_none();
|
||||||
|
|
||||||
if member.contains(active_pane) {
|
if member.contains(active_pane) {
|
||||||
flex = settings::get::<WorkspaceSettings>(cx).active_pane_magnification;
|
active_pane_ix = Some(ix);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut member = member.render(
|
let mut member = member.render(
|
||||||
project,
|
project,
|
||||||
|
(basis + ix) * 10,
|
||||||
theme,
|
theme,
|
||||||
follower_state,
|
follower_state,
|
||||||
active_call,
|
active_call,
|
||||||
|
@ -403,22 +443,26 @@ impl PaneAxis {
|
||||||
app_state,
|
app_state,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
if ix < last_member_ix {
|
|
||||||
|
if !last {
|
||||||
let mut border = theme.workspace.pane_divider;
|
let mut border = theme.workspace.pane_divider;
|
||||||
border.left = false;
|
border.left = false;
|
||||||
border.right = false;
|
border.right = false;
|
||||||
border.top = false;
|
border.top = false;
|
||||||
border.bottom = false;
|
border.bottom = false;
|
||||||
|
|
||||||
match self.axis {
|
match self.axis {
|
||||||
Axis::Vertical => border.bottom = true,
|
Axis::Vertical => border.bottom = true,
|
||||||
Axis::Horizontal => border.right = true,
|
Axis::Horizontal => border.right = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
member = member.contained().with_border(border).into_any();
|
member = member.contained().with_border(border).into_any();
|
||||||
}
|
}
|
||||||
|
|
||||||
FlexItem::new(member).flex(flex, true)
|
pane_axis = pane_axis.with_child(member.into_any());
|
||||||
}))
|
}
|
||||||
.into_any()
|
pane_axis.set_active_pane(active_pane_ix);
|
||||||
|
pane_axis.into_any()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,3 +518,336 @@ impl SplitDirection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod element {
|
||||||
|
use std::{cell::RefCell, ops::Range, rc::Rc};
|
||||||
|
|
||||||
|
use gpui::{
|
||||||
|
geometry::{
|
||||||
|
rect::RectF,
|
||||||
|
vector::{vec2f, Vector2F},
|
||||||
|
},
|
||||||
|
json::{self, ToJson},
|
||||||
|
platform::{CursorStyle, MouseButton},
|
||||||
|
AnyElement, Axis, CursorRegion, Element, LayoutContext, MouseRegion, RectFExt,
|
||||||
|
SceneBuilder, SizeConstraint, Vector2FExt, ViewContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
pane_group::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE},
|
||||||
|
Workspace, WorkspaceSettings,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct PaneAxisElement {
|
||||||
|
axis: Axis,
|
||||||
|
basis: usize,
|
||||||
|
active_pane_ix: Option<usize>,
|
||||||
|
flexes: Rc<RefCell<Vec<f32>>>,
|
||||||
|
children: Vec<AnyElement<Workspace>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaneAxisElement {
|
||||||
|
pub fn new(axis: Axis, basis: usize, flexes: Rc<RefCell<Vec<f32>>>) -> Self {
|
||||||
|
Self {
|
||||||
|
axis,
|
||||||
|
basis,
|
||||||
|
flexes,
|
||||||
|
active_pane_ix: None,
|
||||||
|
children: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_active_pane(&mut self, active_pane_ix: Option<usize>) {
|
||||||
|
self.active_pane_ix = active_pane_ix;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout_children(
|
||||||
|
&mut self,
|
||||||
|
active_pane_magnification: f32,
|
||||||
|
constraint: SizeConstraint,
|
||||||
|
remaining_space: &mut f32,
|
||||||
|
remaining_flex: &mut f32,
|
||||||
|
cross_axis_max: &mut f32,
|
||||||
|
view: &mut Workspace,
|
||||||
|
cx: &mut LayoutContext<Workspace>,
|
||||||
|
) {
|
||||||
|
let flexes = self.flexes.borrow();
|
||||||
|
let cross_axis = self.axis.invert();
|
||||||
|
for (ix, child) in self.children.iter_mut().enumerate() {
|
||||||
|
let flex = if active_pane_magnification != 1. {
|
||||||
|
if let Some(active_pane_ix) = self.active_pane_ix {
|
||||||
|
if ix == active_pane_ix {
|
||||||
|
active_pane_magnification
|
||||||
|
} else {
|
||||||
|
1.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
1.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
flexes[ix]
|
||||||
|
};
|
||||||
|
|
||||||
|
let child_size = if *remaining_flex == 0.0 {
|
||||||
|
*remaining_space
|
||||||
|
} else {
|
||||||
|
let space_per_flex = *remaining_space / *remaining_flex;
|
||||||
|
space_per_flex * flex
|
||||||
|
};
|
||||||
|
|
||||||
|
let child_constraint = match self.axis {
|
||||||
|
Axis::Horizontal => SizeConstraint::new(
|
||||||
|
vec2f(child_size, constraint.min.y()),
|
||||||
|
vec2f(child_size, constraint.max.y()),
|
||||||
|
),
|
||||||
|
Axis::Vertical => SizeConstraint::new(
|
||||||
|
vec2f(constraint.min.x(), child_size),
|
||||||
|
vec2f(constraint.max.x(), child_size),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let child_size = child.layout(child_constraint, view, cx);
|
||||||
|
*remaining_space -= child_size.along(self.axis);
|
||||||
|
*remaining_flex -= flex;
|
||||||
|
*cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Extend<AnyElement<Workspace>> for PaneAxisElement {
|
||||||
|
fn extend<T: IntoIterator<Item = AnyElement<Workspace>>>(&mut self, children: T) {
|
||||||
|
self.children.extend(children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Element<Workspace> for PaneAxisElement {
|
||||||
|
type LayoutState = f32;
|
||||||
|
type PaintState = ();
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&mut self,
|
||||||
|
constraint: SizeConstraint,
|
||||||
|
view: &mut Workspace,
|
||||||
|
cx: &mut LayoutContext<Workspace>,
|
||||||
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
|
debug_assert!(self.children.len() == self.flexes.borrow().len());
|
||||||
|
|
||||||
|
let active_pane_magnification =
|
||||||
|
settings::get::<WorkspaceSettings>(cx).active_pane_magnification;
|
||||||
|
|
||||||
|
let mut remaining_flex = 0.;
|
||||||
|
|
||||||
|
if active_pane_magnification != 1. {
|
||||||
|
let active_pane_flex = self
|
||||||
|
.active_pane_ix
|
||||||
|
.map(|_| active_pane_magnification)
|
||||||
|
.unwrap_or(1.);
|
||||||
|
remaining_flex += self.children.len() as f32 - 1. + active_pane_flex;
|
||||||
|
} else {
|
||||||
|
for flex in self.flexes.borrow().iter() {
|
||||||
|
remaining_flex += flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cross_axis_max: f32 = 0.0;
|
||||||
|
let mut remaining_space = constraint.max_along(self.axis);
|
||||||
|
|
||||||
|
if remaining_space.is_infinite() {
|
||||||
|
panic!("flex contains flexible children but has an infinite constraint along the flex axis");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.layout_children(
|
||||||
|
active_pane_magnification,
|
||||||
|
constraint,
|
||||||
|
&mut remaining_space,
|
||||||
|
&mut remaining_flex,
|
||||||
|
&mut cross_axis_max,
|
||||||
|
view,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut size = match self.axis {
|
||||||
|
Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
|
||||||
|
Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
|
||||||
|
};
|
||||||
|
|
||||||
|
if constraint.min.x().is_finite() {
|
||||||
|
size.set_x(size.x().max(constraint.min.x()));
|
||||||
|
}
|
||||||
|
if constraint.min.y().is_finite() {
|
||||||
|
size.set_y(size.y().max(constraint.min.y()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if size.x() > constraint.max.x() {
|
||||||
|
size.set_x(constraint.max.x());
|
||||||
|
}
|
||||||
|
if size.y() > constraint.max.y() {
|
||||||
|
size.set_y(constraint.max.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
(size, remaining_space)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(
|
||||||
|
&mut self,
|
||||||
|
scene: &mut SceneBuilder,
|
||||||
|
bounds: RectF,
|
||||||
|
visible_bounds: RectF,
|
||||||
|
remaining_space: &mut Self::LayoutState,
|
||||||
|
view: &mut Workspace,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Self::PaintState {
|
||||||
|
let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.;
|
||||||
|
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||||
|
|
||||||
|
let overflowing = *remaining_space < 0.;
|
||||||
|
if overflowing {
|
||||||
|
scene.push_layer(Some(visible_bounds));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut child_origin = bounds.origin();
|
||||||
|
|
||||||
|
let mut children_iter = self.children.iter_mut().enumerate().peekable();
|
||||||
|
while let Some((ix, child)) = children_iter.next() {
|
||||||
|
let child_start = child_origin.clone();
|
||||||
|
child.paint(scene, child_origin, visible_bounds, view, cx);
|
||||||
|
|
||||||
|
match self.axis {
|
||||||
|
Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
|
||||||
|
Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Some((next_ix, next_child))) = can_resize.then(|| children_iter.peek())
|
||||||
|
{
|
||||||
|
scene.push_stacking_context(None, None);
|
||||||
|
|
||||||
|
let handle_origin = match self.axis {
|
||||||
|
Axis::Horizontal => child_origin - vec2f(HANDLE_HITBOX_SIZE / 2., 0.0),
|
||||||
|
Axis::Vertical => child_origin - vec2f(0.0, HANDLE_HITBOX_SIZE / 2.),
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle_bounds = match self.axis {
|
||||||
|
Axis::Horizontal => RectF::new(
|
||||||
|
handle_origin,
|
||||||
|
vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()),
|
||||||
|
),
|
||||||
|
Axis::Vertical => RectF::new(
|
||||||
|
handle_origin,
|
||||||
|
vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let style = match self.axis {
|
||||||
|
Axis::Horizontal => CursorStyle::ResizeLeftRight,
|
||||||
|
Axis::Vertical => CursorStyle::ResizeUpDown,
|
||||||
|
};
|
||||||
|
|
||||||
|
scene.push_cursor_region(CursorRegion {
|
||||||
|
bounds: handle_bounds,
|
||||||
|
style,
|
||||||
|
});
|
||||||
|
|
||||||
|
let axis = self.axis;
|
||||||
|
let child_size = child.size();
|
||||||
|
let next_child_size = next_child.size();
|
||||||
|
let drag_bounds = visible_bounds.clone();
|
||||||
|
let flexes = self.flexes.clone();
|
||||||
|
let current_flex = flexes.borrow()[ix];
|
||||||
|
let next_ix = *next_ix;
|
||||||
|
let next_flex = flexes.borrow()[next_ix];
|
||||||
|
enum ResizeHandle {}
|
||||||
|
let mut mouse_region = MouseRegion::new::<ResizeHandle>(
|
||||||
|
cx.view_id(),
|
||||||
|
self.basis + ix,
|
||||||
|
handle_bounds,
|
||||||
|
);
|
||||||
|
mouse_region = mouse_region.on_drag(
|
||||||
|
MouseButton::Left,
|
||||||
|
move |drag, workspace: &mut Workspace, cx| {
|
||||||
|
let min_size = match axis {
|
||||||
|
Axis::Horizontal => HORIZONTAL_MIN_SIZE,
|
||||||
|
Axis::Vertical => VERTICAL_MIN_SIZE,
|
||||||
|
};
|
||||||
|
// Don't allow resizing to less than the minimum size, if elements are already too small
|
||||||
|
if min_size - 1. > child_size.along(axis)
|
||||||
|
|| min_size - 1. > next_child_size.along(axis)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current_target_size = (drag.position - child_start).along(axis);
|
||||||
|
|
||||||
|
let proposed_current_pixel_change =
|
||||||
|
current_target_size - child_size.along(axis);
|
||||||
|
|
||||||
|
if proposed_current_pixel_change < 0. {
|
||||||
|
current_target_size = f32::max(current_target_size, min_size);
|
||||||
|
} else if proposed_current_pixel_change > 0. {
|
||||||
|
// TODO: cascade this change to other children if current item is at min size
|
||||||
|
let next_target_size = f32::max(
|
||||||
|
next_child_size.along(axis) - proposed_current_pixel_change,
|
||||||
|
min_size,
|
||||||
|
);
|
||||||
|
current_target_size = f32::min(
|
||||||
|
current_target_size,
|
||||||
|
child_size.along(axis) + next_child_size.along(axis)
|
||||||
|
- next_target_size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_pixel_change = current_target_size - child_size.along(axis);
|
||||||
|
let flex_change = current_pixel_change / drag_bounds.length_along(axis);
|
||||||
|
let current_target_flex = current_flex + flex_change;
|
||||||
|
let next_target_flex = next_flex - flex_change;
|
||||||
|
|
||||||
|
let mut borrow = flexes.borrow_mut();
|
||||||
|
*borrow.get_mut(ix).unwrap() = current_target_flex;
|
||||||
|
*borrow.get_mut(next_ix).unwrap() = next_target_flex;
|
||||||
|
|
||||||
|
workspace.schedule_serialize(cx);
|
||||||
|
cx.notify();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
scene.push_mouse_region(mouse_region);
|
||||||
|
|
||||||
|
scene.pop_stacking_context();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if overflowing {
|
||||||
|
scene.pop_layer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rect_for_text_range(
|
||||||
|
&self,
|
||||||
|
range_utf16: Range<usize>,
|
||||||
|
_: RectF,
|
||||||
|
_: RectF,
|
||||||
|
_: &Self::LayoutState,
|
||||||
|
_: &Self::PaintState,
|
||||||
|
view: &Workspace,
|
||||||
|
cx: &ViewContext<Workspace>,
|
||||||
|
) -> Option<RectF> {
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug(
|
||||||
|
&self,
|
||||||
|
bounds: RectF,
|
||||||
|
_: &Self::LayoutState,
|
||||||
|
_: &Self::PaintState,
|
||||||
|
view: &Workspace,
|
||||||
|
cx: &ViewContext<Workspace>,
|
||||||
|
) -> json::Value {
|
||||||
|
serde_json::json!({
|
||||||
|
"type": "PaneAxis",
|
||||||
|
"bounds": bounds.to_json(),
|
||||||
|
"axis": self.axis.to_json(),
|
||||||
|
"flexes": *self.flexes.borrow(),
|
||||||
|
"children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ define_connection! {
|
||||||
// parent_group_id: Option<usize>, // None indicates that this is the root node
|
// parent_group_id: Option<usize>, // None indicates that this is the root node
|
||||||
// position: Optiopn<usize>, // None indicates that this is the root node
|
// position: Optiopn<usize>, // None indicates that this is the root node
|
||||||
// axis: Option<Axis>, // 'Vertical', 'Horizontal'
|
// axis: Option<Axis>, // 'Vertical', 'Horizontal'
|
||||||
|
// flexes: Option<Vec<f32>>, // A JSON array of floats
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// panes(
|
// panes(
|
||||||
|
@ -168,7 +169,12 @@ define_connection! {
|
||||||
ALTER TABLE workspaces ADD COLUMN left_dock_zoom INTEGER; //bool
|
ALTER TABLE workspaces ADD COLUMN left_dock_zoom INTEGER; //bool
|
||||||
ALTER TABLE workspaces ADD COLUMN right_dock_zoom INTEGER; //bool
|
ALTER TABLE workspaces ADD COLUMN right_dock_zoom INTEGER; //bool
|
||||||
ALTER TABLE workspaces ADD COLUMN bottom_dock_zoom INTEGER; //bool
|
ALTER TABLE workspaces ADD COLUMN bottom_dock_zoom INTEGER; //bool
|
||||||
)];
|
),
|
||||||
|
// Add pane group flex data
|
||||||
|
sql!(
|
||||||
|
ALTER TABLE pane_groups ADD COLUMN flexes TEXT;
|
||||||
|
)
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkspaceDb {
|
impl WorkspaceDb {
|
||||||
|
@ -359,9 +365,15 @@ impl WorkspaceDb {
|
||||||
group_id: Option<GroupId>,
|
group_id: Option<GroupId>,
|
||||||
) -> Result<Vec<SerializedPaneGroup>> {
|
) -> Result<Vec<SerializedPaneGroup>> {
|
||||||
type GroupKey = (Option<GroupId>, WorkspaceId);
|
type GroupKey = (Option<GroupId>, WorkspaceId);
|
||||||
type GroupOrPane = (Option<GroupId>, Option<Axis>, Option<PaneId>, Option<bool>);
|
type GroupOrPane = (
|
||||||
|
Option<GroupId>,
|
||||||
|
Option<Axis>,
|
||||||
|
Option<PaneId>,
|
||||||
|
Option<bool>,
|
||||||
|
Option<String>,
|
||||||
|
);
|
||||||
self.select_bound::<GroupKey, GroupOrPane>(sql!(
|
self.select_bound::<GroupKey, GroupOrPane>(sql!(
|
||||||
SELECT group_id, axis, pane_id, active
|
SELECT group_id, axis, pane_id, active, flexes
|
||||||
FROM (SELECT
|
FROM (SELECT
|
||||||
group_id,
|
group_id,
|
||||||
axis,
|
axis,
|
||||||
|
@ -369,7 +381,8 @@ impl WorkspaceDb {
|
||||||
NULL as active,
|
NULL as active,
|
||||||
position,
|
position,
|
||||||
parent_group_id,
|
parent_group_id,
|
||||||
workspace_id
|
workspace_id,
|
||||||
|
flexes
|
||||||
FROM pane_groups
|
FROM pane_groups
|
||||||
UNION
|
UNION
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -379,18 +392,24 @@ impl WorkspaceDb {
|
||||||
panes.active as active,
|
panes.active as active,
|
||||||
position,
|
position,
|
||||||
parent_group_id,
|
parent_group_id,
|
||||||
panes.workspace_id as workspace_id
|
panes.workspace_id as workspace_id,
|
||||||
|
NULL
|
||||||
FROM center_panes
|
FROM center_panes
|
||||||
JOIN panes ON center_panes.pane_id = panes.pane_id)
|
JOIN panes ON center_panes.pane_id = panes.pane_id)
|
||||||
WHERE parent_group_id IS ? AND workspace_id = ?
|
WHERE parent_group_id IS ? AND workspace_id = ?
|
||||||
ORDER BY position
|
ORDER BY position
|
||||||
))?((group_id, workspace_id))?
|
))?((group_id, workspace_id))?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(group_id, axis, pane_id, active)| {
|
.map(|(group_id, axis, pane_id, active, flexes)| {
|
||||||
if let Some((group_id, axis)) = group_id.zip(axis) {
|
if let Some((group_id, axis)) = group_id.zip(axis) {
|
||||||
|
let flexes = flexes
|
||||||
|
.map(|flexes| serde_json::from_str::<Vec<f32>>(&flexes))
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
Ok(SerializedPaneGroup::Group {
|
Ok(SerializedPaneGroup::Group {
|
||||||
axis,
|
axis,
|
||||||
children: self.get_pane_group(workspace_id, Some(group_id))?,
|
children: self.get_pane_group(workspace_id, Some(group_id))?,
|
||||||
|
flexes,
|
||||||
})
|
})
|
||||||
} else if let Some((pane_id, active)) = pane_id.zip(active) {
|
} else if let Some((pane_id, active)) = pane_id.zip(active) {
|
||||||
Ok(SerializedPaneGroup::Pane(SerializedPane::new(
|
Ok(SerializedPaneGroup::Pane(SerializedPane::new(
|
||||||
|
@ -417,14 +436,34 @@ impl WorkspaceDb {
|
||||||
parent: Option<(GroupId, usize)>,
|
parent: Option<(GroupId, usize)>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match pane_group {
|
match pane_group {
|
||||||
SerializedPaneGroup::Group { axis, children } => {
|
SerializedPaneGroup::Group {
|
||||||
|
axis,
|
||||||
|
children,
|
||||||
|
flexes,
|
||||||
|
} => {
|
||||||
let (parent_id, position) = unzip_option(parent);
|
let (parent_id, position) = unzip_option(parent);
|
||||||
|
|
||||||
|
let flex_string = flexes
|
||||||
|
.as_ref()
|
||||||
|
.map(|flexes| serde_json::json!(flexes).to_string());
|
||||||
|
|
||||||
let group_id = conn.select_row_bound::<_, i64>(sql!(
|
let group_id = conn.select_row_bound::<_, i64>(sql!(
|
||||||
INSERT INTO pane_groups(workspace_id, parent_group_id, position, axis)
|
INSERT INTO pane_groups(
|
||||||
VALUES (?, ?, ?, ?)
|
workspace_id,
|
||||||
|
parent_group_id,
|
||||||
|
position,
|
||||||
|
axis,
|
||||||
|
flexes
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?)
|
||||||
RETURNING group_id
|
RETURNING group_id
|
||||||
))?((workspace_id, parent_id, position, *axis))?
|
))?((
|
||||||
|
workspace_id,
|
||||||
|
parent_id,
|
||||||
|
position,
|
||||||
|
*axis,
|
||||||
|
flex_string,
|
||||||
|
))?
|
||||||
.ok_or_else(|| anyhow!("Couldn't retrieve group_id from inserted pane_group"))?;
|
.ok_or_else(|| anyhow!("Couldn't retrieve group_id from inserted pane_group"))?;
|
||||||
|
|
||||||
for (position, group) in children.iter().enumerate() {
|
for (position, group) in children.iter().enumerate() {
|
||||||
|
@ -641,6 +680,14 @@ mod tests {
|
||||||
assert_eq!(test_text_1, "test-text-1");
|
assert_eq!(test_text_1, "test-text-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn group(axis: gpui::Axis, children: Vec<SerializedPaneGroup>) -> SerializedPaneGroup {
|
||||||
|
SerializedPaneGroup::Group {
|
||||||
|
axis,
|
||||||
|
flexes: None,
|
||||||
|
children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_full_workspace_serialization() {
|
async fn test_full_workspace_serialization() {
|
||||||
env_logger::try_init().ok();
|
env_logger::try_init().ok();
|
||||||
|
@ -652,12 +699,12 @@ mod tests {
|
||||||
// | - - - | |
|
// | - - - | |
|
||||||
// | 3,4 | |
|
// | 3,4 | |
|
||||||
// -----------------
|
// -----------------
|
||||||
let center_group = SerializedPaneGroup::Group {
|
let center_group = group(
|
||||||
axis: gpui::Axis::Horizontal,
|
gpui::Axis::Horizontal,
|
||||||
children: vec![
|
vec![
|
||||||
SerializedPaneGroup::Group {
|
group(
|
||||||
axis: gpui::Axis::Vertical,
|
gpui::Axis::Vertical,
|
||||||
children: vec![
|
vec![
|
||||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||||
vec![
|
vec![
|
||||||
SerializedItem::new("Terminal", 5, false),
|
SerializedItem::new("Terminal", 5, false),
|
||||||
|
@ -673,7 +720,7 @@ mod tests {
|
||||||
false,
|
false,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
},
|
),
|
||||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||||
vec![
|
vec![
|
||||||
SerializedItem::new("Terminal", 9, false),
|
SerializedItem::new("Terminal", 9, false),
|
||||||
|
@ -682,7 +729,7 @@ mod tests {
|
||||||
false,
|
false,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
};
|
);
|
||||||
|
|
||||||
let workspace = SerializedWorkspace {
|
let workspace = SerializedWorkspace {
|
||||||
id: 5,
|
id: 5,
|
||||||
|
@ -811,12 +858,12 @@ mod tests {
|
||||||
// | - - - | |
|
// | - - - | |
|
||||||
// | 3,4 | |
|
// | 3,4 | |
|
||||||
// -----------------
|
// -----------------
|
||||||
let center_pane = SerializedPaneGroup::Group {
|
let center_pane = group(
|
||||||
axis: gpui::Axis::Horizontal,
|
gpui::Axis::Horizontal,
|
||||||
children: vec![
|
vec![
|
||||||
SerializedPaneGroup::Group {
|
group(
|
||||||
axis: gpui::Axis::Vertical,
|
gpui::Axis::Vertical,
|
||||||
children: vec![
|
vec![
|
||||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||||
vec![
|
vec![
|
||||||
SerializedItem::new("Terminal", 1, false),
|
SerializedItem::new("Terminal", 1, false),
|
||||||
|
@ -832,7 +879,7 @@ mod tests {
|
||||||
true,
|
true,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
},
|
),
|
||||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||||
vec![
|
vec![
|
||||||
SerializedItem::new("Terminal", 5, true),
|
SerializedItem::new("Terminal", 5, true),
|
||||||
|
@ -841,7 +888,7 @@ mod tests {
|
||||||
false,
|
false,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
};
|
);
|
||||||
|
|
||||||
let workspace = default_workspace(&["/tmp"], ¢er_pane);
|
let workspace = default_workspace(&["/tmp"], ¢er_pane);
|
||||||
|
|
||||||
|
@ -858,12 +905,12 @@ mod tests {
|
||||||
|
|
||||||
let db = WorkspaceDb(open_test_db("test_cleanup_panes").await);
|
let db = WorkspaceDb(open_test_db("test_cleanup_panes").await);
|
||||||
|
|
||||||
let center_pane = SerializedPaneGroup::Group {
|
let center_pane = group(
|
||||||
axis: gpui::Axis::Horizontal,
|
gpui::Axis::Horizontal,
|
||||||
children: vec![
|
vec![
|
||||||
SerializedPaneGroup::Group {
|
group(
|
||||||
axis: gpui::Axis::Vertical,
|
gpui::Axis::Vertical,
|
||||||
children: vec![
|
vec![
|
||||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||||
vec![
|
vec![
|
||||||
SerializedItem::new("Terminal", 1, false),
|
SerializedItem::new("Terminal", 1, false),
|
||||||
|
@ -879,7 +926,7 @@ mod tests {
|
||||||
true,
|
true,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
},
|
),
|
||||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||||
vec![
|
vec![
|
||||||
SerializedItem::new("Terminal", 5, false),
|
SerializedItem::new("Terminal", 5, false),
|
||||||
|
@ -888,7 +935,7 @@ mod tests {
|
||||||
false,
|
false,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
};
|
);
|
||||||
|
|
||||||
let id = &["/tmp"];
|
let id = &["/tmp"];
|
||||||
|
|
||||||
|
@ -896,9 +943,9 @@ mod tests {
|
||||||
|
|
||||||
db.save_workspace(workspace.clone()).await;
|
db.save_workspace(workspace.clone()).await;
|
||||||
|
|
||||||
workspace.center_group = SerializedPaneGroup::Group {
|
workspace.center_group = group(
|
||||||
axis: gpui::Axis::Vertical,
|
gpui::Axis::Vertical,
|
||||||
children: vec![
|
vec![
|
||||||
SerializedPaneGroup::Pane(SerializedPane::new(
|
SerializedPaneGroup::Pane(SerializedPane::new(
|
||||||
vec![
|
vec![
|
||||||
SerializedItem::new("Terminal", 1, false),
|
SerializedItem::new("Terminal", 1, false),
|
||||||
|
@ -914,7 +961,7 @@ mod tests {
|
||||||
true,
|
true,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
};
|
);
|
||||||
|
|
||||||
db.save_workspace(workspace.clone()).await;
|
db.save_workspace(workspace.clone()).await;
|
||||||
|
|
||||||
|
|
|
@ -127,10 +127,11 @@ impl Bind for DockData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum SerializedPaneGroup {
|
pub enum SerializedPaneGroup {
|
||||||
Group {
|
Group {
|
||||||
axis: Axis,
|
axis: Axis,
|
||||||
|
flexes: Option<Vec<f32>>,
|
||||||
children: Vec<SerializedPaneGroup>,
|
children: Vec<SerializedPaneGroup>,
|
||||||
},
|
},
|
||||||
Pane(SerializedPane),
|
Pane(SerializedPane),
|
||||||
|
@ -149,7 +150,7 @@ impl Default for SerializedPaneGroup {
|
||||||
impl SerializedPaneGroup {
|
impl SerializedPaneGroup {
|
||||||
#[async_recursion(?Send)]
|
#[async_recursion(?Send)]
|
||||||
pub(crate) async fn deserialize(
|
pub(crate) async fn deserialize(
|
||||||
&self,
|
self,
|
||||||
project: &ModelHandle<Project>,
|
project: &ModelHandle<Project>,
|
||||||
workspace_id: WorkspaceId,
|
workspace_id: WorkspaceId,
|
||||||
workspace: &WeakViewHandle<Workspace>,
|
workspace: &WeakViewHandle<Workspace>,
|
||||||
|
@ -160,7 +161,11 @@ impl SerializedPaneGroup {
|
||||||
Vec<Option<Box<dyn ItemHandle>>>,
|
Vec<Option<Box<dyn ItemHandle>>>,
|
||||||
)> {
|
)> {
|
||||||
match self {
|
match self {
|
||||||
SerializedPaneGroup::Group { axis, children } => {
|
SerializedPaneGroup::Group {
|
||||||
|
axis,
|
||||||
|
children,
|
||||||
|
flexes,
|
||||||
|
} => {
|
||||||
let mut current_active_pane = None;
|
let mut current_active_pane = None;
|
||||||
let mut members = Vec::new();
|
let mut members = Vec::new();
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
|
@ -184,10 +189,7 @@ impl SerializedPaneGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
Member::Axis(PaneAxis {
|
Member::Axis(PaneAxis::load(axis, members, flexes)),
|
||||||
axis: *axis,
|
|
||||||
members,
|
|
||||||
}),
|
|
||||||
current_active_pane,
|
current_active_pane,
|
||||||
items,
|
items,
|
||||||
))
|
))
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
pub mod dock;
|
pub mod dock;
|
||||||
/// NOTE: Focus only 'takes' after an update has flushed_effects.
|
|
||||||
///
|
|
||||||
/// This may cause issues when you're trying to write tests that use workspace focus to add items at
|
|
||||||
/// specific locations.
|
|
||||||
pub mod item;
|
pub mod item;
|
||||||
pub mod notifications;
|
pub mod notifications;
|
||||||
pub mod pane;
|
pub mod pane;
|
||||||
|
@ -508,6 +504,7 @@ pub struct Workspace {
|
||||||
subscriptions: Vec<Subscription>,
|
subscriptions: Vec<Subscription>,
|
||||||
_apply_leader_updates: Task<Result<()>>,
|
_apply_leader_updates: Task<Result<()>>,
|
||||||
_observe_current_user: Task<Result<()>>,
|
_observe_current_user: Task<Result<()>>,
|
||||||
|
_schedule_serialize: Option<Task<()>>,
|
||||||
pane_history_timestamp: Arc<AtomicUsize>,
|
pane_history_timestamp: Arc<AtomicUsize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,6 +719,7 @@ impl Workspace {
|
||||||
app_state,
|
app_state,
|
||||||
_observe_current_user,
|
_observe_current_user,
|
||||||
_apply_leader_updates,
|
_apply_leader_updates,
|
||||||
|
_schedule_serialize: None,
|
||||||
leader_updates_tx,
|
leader_updates_tx,
|
||||||
subscriptions,
|
subscriptions,
|
||||||
pane_history_timestamp,
|
pane_history_timestamp,
|
||||||
|
@ -2897,6 +2895,14 @@ impl Workspace {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn schedule_serialize(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
self._schedule_serialize = Some(cx.spawn(|this, cx| async move {
|
||||||
|
cx.background().timer(Duration::from_millis(100)).await;
|
||||||
|
this.read_with(&cx, |this, cx| this.serialize_workspace(cx))
|
||||||
|
.ok();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
fn serialize_workspace(&self, cx: &ViewContext<Self>) {
|
fn serialize_workspace(&self, cx: &ViewContext<Self>) {
|
||||||
fn serialize_pane_handle(
|
fn serialize_pane_handle(
|
||||||
pane_handle: &ViewHandle<Pane>,
|
pane_handle: &ViewHandle<Pane>,
|
||||||
|
@ -2927,12 +2933,17 @@ impl Workspace {
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> SerializedPaneGroup {
|
) -> SerializedPaneGroup {
|
||||||
match pane_group {
|
match pane_group {
|
||||||
Member::Axis(PaneAxis { axis, members }) => SerializedPaneGroup::Group {
|
Member::Axis(PaneAxis {
|
||||||
|
axis,
|
||||||
|
members,
|
||||||
|
flexes,
|
||||||
|
}) => SerializedPaneGroup::Group {
|
||||||
axis: *axis,
|
axis: *axis,
|
||||||
children: members
|
children: members
|
||||||
.iter()
|
.iter()
|
||||||
.map(|member| build_serialized_pane_group(member, cx))
|
.map(|member| build_serialized_pane_group(member, cx))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
|
flexes: Some(flexes.borrow().clone()),
|
||||||
},
|
},
|
||||||
Member::Pane(pane_handle) => {
|
Member::Pane(pane_handle) => {
|
||||||
SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx))
|
SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue