mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-30 23:12:53 +03:00
Added the new elements and wired through the pointers to update the pane axis ratios
This commit is contained in:
parent
488b41826b
commit
5385ca411b
@ -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!()
|
||||
}
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +187,7 @@ impl SerializedPaneGroup {
|
||||
Member::Axis(PaneAxis {
|
||||
axis: *axis,
|
||||
members,
|
||||
ratios: Default::default()
|
||||
}),
|
||||
current_active_pane,
|
||||
items,
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user