Added the new elements and wired through the pointers to update the pane axis ratios

This commit is contained in:
Mikayla Maki 2023-07-12 17:53:01 -07:00
parent 488b41826b
commit 5385ca411b
No known key found for this signature in database
4 changed files with 375 additions and 109 deletions

View File

@ -1,83 +0,0 @@
use gpui::{Element, View, Axis, AnyElement};
// Model for the center group: AdjustableGroup of AdjustableGroups
// Implementation notes
// - These have two representations: Exact pixel widths and ratios of elements compared to whole space
// - We have a constraint of minimum sizes for things.
// - If The space is smaller than allowed, things run off the edge
// - When doing Drag resize, we update the pixel width representation, causing a recalc of the ratios
// - If dragging past minimum, take space from next item, until out of space
// - When doing a reflow (e.g. layout) we read off the ratios and calculate pixels from that
// - When adding / removing items in an Adjustable flex, reset to default ratios (1:1)
// - By default, every item takes up as much space as possible
//
struct AdjustableFlex<V: View> {
axis: Axis,
handle_size: f32,
items: Vec<(AnyElement<V>, f32)>
}
impl<V: View> AdjustableFlex<V> {
fn new(axis: Axis) -> Self {
AdjustableFlex {
axis,
handle_size: 2.,
items: Vec::new(),
}
}
fn add_item()
}
impl<V: View> Element<V> for AdjustableFlex<V> {
type LayoutState = ();
type PaintState = ();
fn layout(
&mut self,
constraint: gpui::SizeConstraint,
view: &mut V,
cx: &mut gpui::LayoutContext<V>,
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
todo!()
}
fn paint(
&mut self,
scene: &mut gpui::SceneBuilder,
bounds: gpui::geometry::rect::RectF,
visible_bounds: gpui::geometry::rect::RectF,
layout: &mut Self::LayoutState,
view: &mut V,
cx: &mut gpui::ViewContext<V>,
) -> Self::PaintState {
todo!()
}
fn rect_for_text_range(
&self,
range_utf16: std::ops::Range<usize>,
bounds: gpui::geometry::rect::RectF,
visible_bounds: gpui::geometry::rect::RectF,
layout: &Self::LayoutState,
paint: &Self::PaintState,
view: &V,
cx: &gpui::ViewContext<V>,
) -> Option<gpui::geometry::rect::RectF> {
todo!()
}
fn debug(
&self,
bounds: gpui::geometry::rect::RectF,
layout: &Self::LayoutState,
paint: &Self::PaintState,
view: &V,
cx: &gpui::ViewContext<V>,
) -> serde_json::Value {
todo!()
}
}

View File

@ -1,6 +1,6 @@
use std::sync::Arc;
use std::{cell::RefCell, rc::Rc, sync::Arc};
use crate::{AppState, FollowerStatesByLeader, Pane, Workspace, WorkspaceSettings};
use crate::{AppState, FollowerStatesByLeader, Pane, Workspace};
use anyhow::{anyhow, Result};
use call::{ActiveCall, ParticipantLocation};
use gpui::{
@ -9,12 +9,13 @@ use gpui::{
platform::{CursorStyle, MouseButton},
AnyViewHandle, Axis, Border, ModelHandle, ViewContext, ViewHandle,
};
use itertools::Itertools;
use project::Project;
use serde::Deserialize;
use theme::Theme;
#[derive(Clone, Debug, Eq, PartialEq)]
use self::adjustable_group::{AdjustableGroupElement, AdjustableGroupItem};
#[derive(Clone, Debug, PartialEq)]
pub struct PaneGroup {
pub(crate) root: Member,
}
@ -78,6 +79,7 @@ impl PaneGroup {
) -> AnyElement<Workspace> {
self.root.render(
project,
0,
theme,
follower_states,
active_call,
@ -95,7 +97,7 @@ impl PaneGroup {
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum Member {
Axis(PaneAxis),
Pane(ViewHandle<Pane>),
@ -120,7 +122,11 @@ impl Member {
Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)],
};
Member::Axis(PaneAxis { axis, members })
Member::Axis(PaneAxis {
axis,
members,
ratios: Default::default(),
})
}
fn contains(&self, needle: &ViewHandle<Pane>) -> bool {
@ -133,6 +139,7 @@ impl Member {
pub fn render(
&self,
project: &ModelHandle<Project>,
basis: usize,
theme: &Theme,
follower_states: &FollowerStatesByLeader,
active_call: Option<&ModelHandle<ActiveCall>>,
@ -273,6 +280,7 @@ impl Member {
}
Member::Axis(axis) => axis.render(
project,
basis + 1,
theme,
follower_states,
active_call,
@ -296,10 +304,11 @@ impl Member {
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct PaneAxis {
pub axis: Axis,
pub members: Vec<Member>,
pub ratios: Rc<RefCell<Vec<f32>>>,
}
impl PaneAxis {
@ -378,6 +387,7 @@ impl PaneAxis {
fn render(
&self,
project: &ModelHandle<Project>,
basis: usize,
theme: &Theme,
follower_state: &FollowerStatesByLeader,
active_call: Option<&ModelHandle<ActiveCall>>,
@ -386,19 +396,29 @@ impl PaneAxis {
app_state: &Arc<AppState>,
cx: &mut ViewContext<Workspace>,
) -> AnyElement<Workspace> {
let mut flex_container = Flex::new(self.axis);
let ratios = self.ratios.clone();
let mut flex_container = AdjustableGroupElement::new(self.axis, 2., basis, move |new_flexes| {
let mut borrow = ratios.borrow_mut();
borrow.extend(new_flexes);
borrow.truncate(10);
dbg!(borrow);
});
let next_basis = basis + self.members.len();
let mut members = self.members.iter().enumerate().peekable();
while let Some((ix, member)) = members.next() {
while let Some((_ix, member)) = members.next() {
let last = members.peek().is_none();
let mut flex = 1.0;
if member.contains(active_pane) {
flex = settings::get::<WorkspaceSettings>(cx).active_pane_magnification;
}
// TODO: Include minimum sizes
// TODO: Restore this
// if member.contains(active_pane) {
// flex = settings::get::<WorkspaceSettings>(cx).active_pane_magnification;
// }
let mut member = member.render(
project,
next_basis,
theme,
follower_state,
active_call,
@ -424,20 +444,11 @@ impl PaneAxis {
Axis::Vertical => HandleSide::Bottom,
};
member = member.contained().with_border(border)
.resizable(side, 1., |workspace, size, cx| {
dbg!("resize", size);
})
.into_any();
member = member.contained().with_border(border).into_any();
}
flex_container = flex_container.with_child(
FlexItem::new(member)
.flex(flex, true)
.into_any()
);
flex_container =
flex_container.with_child(AdjustableGroupItem::new(member, flex).into_any());
}
flex_container.into_any()
@ -496,3 +507,341 @@ impl SplitDirection {
}
}
}
mod adjustable_group {
use std::{any::Any, ops::Range, rc::Rc};
use gpui::{
color::Color,
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
json::{self, ToJson},
platform::{CursorStyle, MouseButton},
AnyElement, Axis, CursorRegion, Element, LayoutContext, MouseRegion, Quad, SceneBuilder,
SizeConstraint, Vector2FExt, View, ViewContext,
};
use serde_json::Value;
struct AdjustableFlexData {
flex: f32,
}
pub struct AdjustableGroupElement<V: View> {
axis: Axis,
handle_size: f32,
basis: usize,
callback: Rc<dyn Fn(Vec<f32>)>,
children: Vec<AnyElement<V>>,
}
impl<V: View> AdjustableGroupElement<V> {
pub fn new(
axis: Axis,
handle_size: f32,
basis: usize,
callback: impl Fn(Vec<f32>) + 'static,
) -> Self {
Self {
axis,
handle_size,
basis,
callback: Rc::new(callback),
children: Default::default(),
}
}
fn layout_flex_children(
&mut self,
constraint: SizeConstraint,
remaining_space: &mut f32,
remaining_flex: &mut f32,
cross_axis_max: &mut f32,
view: &mut V,
cx: &mut LayoutContext<V>,
) {
let cross_axis = self.axis.invert();
let last_ix = self.children.len() - 1;
for (ix, child) in self.children.iter_mut().enumerate() {
let flex = child.metadata::<AdjustableFlexData>().unwrap().flex;
let handle_size = if ix == last_ix { 0. } else { self.handle_size };
let child_size = if *remaining_flex == 0.0 {
*remaining_space
} else {
let space_per_flex = *remaining_space / *remaining_flex;
space_per_flex * flex
} - handle_size;
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) + handle_size;
*remaining_flex -= flex;
*cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
}
}
}
impl<V: View> Extend<AnyElement<V>> for AdjustableGroupElement<V> {
fn extend<T: IntoIterator<Item = AnyElement<V>>>(&mut self, children: T) {
self.children.extend(children);
}
}
impl<V: View> Element<V> for AdjustableGroupElement<V> {
type LayoutState = f32;
type PaintState = ();
fn layout(
&mut self,
constraint: SizeConstraint,
view: &mut V,
cx: &mut LayoutContext<V>,
) -> (Vector2F, Self::LayoutState) {
let mut remaining_flex = 0.;
let mut cross_axis_max: f32 = 0.0;
for child in &mut self.children {
let metadata = child.metadata::<AdjustableFlexData>();
let flex = metadata
.map(|metadata| metadata.flex)
.expect("All children of an adjustable flex must be AdjustableFlexItems");
remaining_flex += flex;
}
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_flex_children(
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 V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
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 last_ix = self.children.len() - 1;
for (ix, child) in self.children.iter_mut().enumerate() {
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 ix != last_ix {
let bounds = match self.axis {
Axis::Horizontal => RectF::new(
child_origin,
vec2f(self.handle_size, visible_bounds.height()),
),
Axis::Vertical => RectF::new(
child_origin,
vec2f(visible_bounds.width(), self.handle_size),
),
};
scene.push_quad(Quad {
bounds,
background: Some(Color::red()),
..Default::default()
});
let style = match self.axis {
Axis::Horizontal => CursorStyle::ResizeLeftRight,
Axis::Vertical => CursorStyle::ResizeUpDown,
};
scene.push_cursor_region(CursorRegion { bounds, style });
enum ResizeHandle {}
let callback = self.callback.clone();
let axis = self.axis;
let mut mouse_region =
MouseRegion::new::<ResizeHandle>(cx.view_id(), self.basis + ix, bounds);
mouse_region =
mouse_region.on_drag(MouseButton::Left, move |drag, v: &mut V, cx| {
dbg!(drag);
callback({
match axis {
Axis::Horizontal => vec![0., 1., 2.],
Axis::Vertical => vec![3., 2., 1.],
}
})
});
scene.push_mouse_region(mouse_region);
match self.axis {
Axis::Horizontal => child_origin += vec2f(self.handle_size, 0.0),
Axis::Vertical => child_origin += vec2f(0.0, self.handle_size),
}
}
}
if overflowing {
scene.pop_layer();
}
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
view: &V,
cx: &ViewContext<V>,
) -> 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: &V,
cx: &ViewContext<V>,
) -> json::Value {
serde_json::json!({
"type": "Flex",
"bounds": bounds.to_json(),
"axis": self.axis.to_json(),
"children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
})
}
}
pub struct AdjustableGroupItem<V: View> {
metadata: AdjustableFlexData,
child: AnyElement<V>,
}
impl<V: View> AdjustableGroupItem<V> {
pub fn new(child: impl Element<V>, flex: f32) -> Self {
Self {
metadata: AdjustableFlexData { flex },
child: child.into_any(),
}
}
}
impl<V: View> Element<V> for AdjustableGroupItem<V> {
type LayoutState = ();
type PaintState = ();
fn layout(
&mut self,
constraint: SizeConstraint,
view: &mut V,
cx: &mut LayoutContext<V>,
) -> (Vector2F, Self::LayoutState) {
let size = self.child.layout(constraint, view, cx);
(size, ())
}
fn paint(
&mut self,
scene: &mut SceneBuilder,
bounds: RectF,
visible_bounds: RectF,
_: &mut Self::LayoutState,
view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
self.child
.paint(scene, bounds.origin(), visible_bounds, view, cx)
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, view, cx)
}
fn metadata(&self) -> Option<&dyn Any> {
Some(&self.metadata)
}
fn debug(
&self,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
view: &V,
cx: &ViewContext<V>,
) -> Value {
serde_json::json!({
"type": "Flexible",
"flex": self.metadata.flex,
"child": self.child.debug(view, cx)
})
}
}
}

View File

@ -187,6 +187,7 @@ impl SerializedPaneGroup {
Member::Axis(PaneAxis {
axis: *axis,
members,
ratios: Default::default()
}),
current_active_pane,
items,

View File

@ -4,7 +4,6 @@ pub mod notifications;
pub mod pane;
pub mod pane_group;
mod persistence;
mod adjustable_flex;
pub mod searchable;
pub mod shared_screen;
mod status_bar;
@ -2924,7 +2923,7 @@ impl Workspace {
cx: &AppContext,
) -> SerializedPaneGroup {
match pane_group {
Member::Axis(PaneAxis { axis, members }) => SerializedPaneGroup::Group {
Member::Axis(PaneAxis { axis, members, .. }) => SerializedPaneGroup::Group {
axis: *axis,
children: members
.iter()