@@ -1,13 +1,9 @@
-use crate::{AppState, FollowerState, Pane, Workspace};
-use anyhow::{anyhow, bail, Result};
+use crate::{pane_group::element::pane_axis, AppState, FollowerState, Pane, Workspace};
+use anyhow::{anyhow, Result};
use call::{ActiveCall, ParticipantLocation};
use collections::HashMap;
-use db::sqlez::{
- bindable::{Bind, Column, StaticColumnCount},
- statement::Statement,
-};
use gpui::{
- point, size, AnyWeakView, Bounds, Div, Entity as _, IntoElement, Model, Pixels, Point, View,
+ point, size, AnyWeakView, Axis, Bounds, Entity as _, IntoElement, Model, Pixels, Point, View,
ViewContext,
};
use parking_lot::Mutex;
@@ -16,42 +12,10 @@ use serde::Deserialize;
use std::sync::Arc;
use ui::{prelude::*, Button};
-const HANDLE_HITBOX_SIZE: f32 = 4.0;
+const HANDLE_HITBOX_SIZE: f32 = 10.0; //todo!(change this back to 4)
const HORIZONTAL_MIN_SIZE: f32 = 80.;
const VERTICAL_MIN_SIZE: f32 = 100.;
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum Axis {
- Vertical,
- Horizontal,
-}
-
-impl StaticColumnCount for Axis {}
-impl Bind for Axis {
- fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result<i32> {
- match self {
- Axis::Horizontal => "Horizontal",
- Axis::Vertical => "Vertical",
- }
- .bind(statement, start_index)
- }
-}
-
-impl Column for Axis {
- fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> {
- String::column(statement, start_index).and_then(|(axis_text, next_index)| {
- Ok((
- match axis_text.as_str() {
- "Horizontal" => Axis::Horizontal,
- "Vertical" => Axis::Vertical,
- _ => bail!("Stored serialized item kind is incorrect"),
- },
- next_index,
- ))
- })
- }
-}
-
#[derive(Clone, PartialEq)]
pub struct PaneGroup {
pub(crate) root: Member,
@@ -612,7 +576,7 @@ impl PaneAxis {
for (idx, member) in self.members.iter().enumerate() {
if let Some(coordinates) = bounding_boxes[idx] {
- if coordinates.contains_point(&coordinate) {
+ if coordinates.contains(&coordinate) {
return match member {
Member::Pane(found) => Some(found),
Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
@@ -632,80 +596,42 @@ impl PaneAxis {
zoomed: Option<&AnyWeakView>,
app_state: &Arc<AppState>,
cx: &mut ViewContext<Workspace>,
- ) -> Div {
+ ) -> gpui::AnyElement {
debug_assert!(self.members.len() == self.flexes.lock().len());
+ let mut active_pane_ix = None;
- div()
- .flex()
- .flex_auto()
- .map(|s| match self.axis {
- Axis::Vertical => s.flex_col(),
- Axis::Horizontal => s.flex_row(),
- })
- .children(self.members.iter().enumerate().map(|(ix, member)| {
- match member {
- Member::Axis(axis) => axis
- .render(
- project,
- basis,
- follower_states,
- active_pane,
- zoomed,
- app_state,
- cx,
- )
- .into_any_element(),
- Member::Pane(pane) => pane.clone().into_any_element(),
- }
- }))
-
- // let mut pane_axis = PaneAxisElement::new(
- // self.axis,
- // basis,
- // self.flexes.clone(),
- // self.bounding_boxes.clone(),
- // );
- // 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) {
- // active_pane_ix = Some(ix);
- // }
-
- // let mut member = member.render(
- // project,
- // (basis + ix) * 10,
- // theme,
- // follower_states,
- // active_call,
- // active_pane,
- // zoomed,
- // app_state,
- // cx,
- // );
-
- // if !last {
- // let mut border = theme.workspace.pane_divider;
- // border.left = false;
- // border.right = false;
- // border.top = false;
- // border.bottom = false;
-
- // match self.axis {
- // Axis::Vertical => border.bottom = true,
- // Axis::Horizontal => border.right = true,
- // }
-
- // member = member.contained().with_border(border).into_any();
- // }
+ pane_axis(
+ self.axis,
+ basis,
+ self.flexes.clone(),
+ self.bounding_boxes.clone(),
+ )
+ .children(self.members.iter().enumerate().map(|(ix, member)| {
+ if member.contains(active_pane) {
+ active_pane_ix = Some(ix);
+ }
- // pane_axis = pane_axis.with_child(member.into_any());
- // }
- // pane_axis.set_active_pane(active_pane_ix);
- // pane_axis.into_any()
+ match member {
+ Member::Axis(axis) => axis
+ .render(
+ project,
+ (basis + ix) * 10,
+ follower_states,
+ active_pane,
+ zoomed,
+ app_state,
+ cx,
+ )
+ .into_any_element(),
+ Member::Pane(pane) => div()
+ .size_full()
+ .border()
+ .child(pane.clone())
+ .into_any_element(),
+ }
+ }))
+ .with_active_pane(active_pane_ix)
+ .into_any_element()
}
}
@@ -767,403 +693,677 @@ impl SplitDirection {
}
}
-// mod element {
-// // use std::{cell::RefCell, iter::from_fn, ops::Range, rc::Rc};
-
-// // use gpui::{
-// // geometry::{
-// // rect::Bounds<Pixels>,
-// // vector::{vec2f, Vector2F},
-// // },
-// // json::{self, ToJson},
-// // platform::{CursorStyle, MouseButton},
-// // scene::MouseDrag,
-// // AnyElement, Axis, CursorRegion, Element, EventContext, MouseRegion, Bounds<Pixels>Ext,
-// // 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>>,
-// bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
-// }
-
-// impl PaneAxisElement {
-// pub fn new(
-// axis: Axis,
-// basis: usize,
-// flexes: Rc<RefCell<Vec<f32>>>,
-// bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
-// ) -> Self {
-// Self {
-// axis,
-// basis,
-// flexes,
-// bounding_boxes,
-// 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 ViewContext<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));
-// }
-// }
-
-// fn handle_resize(
-// flexes: Rc<RefCell<Vec<f32>>>,
-// axis: Axis,
-// preceding_ix: usize,
-// child_start: Vector2F,
-// drag_bounds: Bounds<Pixels>,
-// ) -> impl Fn(MouseDrag, &mut Workspace, &mut EventContext<Workspace>) {
-// let size = move |ix, flexes: &[f32]| {
-// drag_bounds.length_along(axis) * (flexes[ix] / flexes.len() as f32)
-// };
-
-// move |drag, workspace: &mut Workspace, cx| {
-// if drag.end {
-// // TODO: Clear cascading resize state
-// return;
-// }
-// let min_size = match axis {
-// Axis::Horizontal => HORIZONTAL_MIN_SIZE,
-// Axis::Vertical => VERTICAL_MIN_SIZE,
-// };
-// let mut flexes = flexes.borrow_mut();
-
-// // Don't allow resizing to less than the minimum size, if elements are already too small
-// if min_size - 1. > size(preceding_ix, flexes.as_slice()) {
-// return;
-// }
-
-// let mut proposed_current_pixel_change = (drag.position - child_start).along(axis)
-// - size(preceding_ix, flexes.as_slice());
-
-// let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
-// let flex_change = pixel_dx / drag_bounds.length_along(axis);
-// let current_target_flex = flexes[target_ix] + flex_change;
-// let next_target_flex =
-// flexes[(target_ix as isize + next) as usize] - flex_change;
-// (current_target_flex, next_target_flex)
-// };
-
-// let mut successors = from_fn({
-// let forward = proposed_current_pixel_change > 0.;
-// let mut ix_offset = 0;
-// let len = flexes.len();
-// move || {
-// let result = if forward {
-// (preceding_ix + 1 + ix_offset < len).then(|| preceding_ix + ix_offset)
-// } else {
-// (preceding_ix as isize - ix_offset as isize >= 0)
-// .then(|| preceding_ix - ix_offset)
-// };
-
-// ix_offset += 1;
-
-// result
-// }
-// });
-
-// while proposed_current_pixel_change.abs() > 0. {
-// let Some(current_ix) = successors.next() else {
-// break;
-// };
-
-// let next_target_size = f32::max(
-// size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change,
-// min_size,
-// );
-
-// let current_target_size = f32::max(
-// size(current_ix, flexes.as_slice())
-// + size(current_ix + 1, flexes.as_slice())
-// - next_target_size,
-// min_size,
-// );
-
-// let current_pixel_change =
-// current_target_size - size(current_ix, flexes.as_slice());
-
-// let (current_target_flex, next_target_flex) =
-// flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice());
-
-// flexes[current_ix] = current_target_flex;
-// flexes[current_ix + 1] = next_target_flex;
-
-// proposed_current_pixel_change -= current_pixel_change;
-// }
-
-// workspace.schedule_serialize(cx);
-// cx.notify();
-// }
-// }
-// }
-
-// 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 ViewContext<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,
-// bounds: Bounds<Pixels>,
-// visible_bounds: Bounds<Pixels>,
-// 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 {
-// cx.scene().push_layer(Some(visible_bounds));
-// }
-
-// let mut child_origin = bounds.origin();
-
-// let mut bounding_boxes = self.bounding_boxes.borrow_mut();
-// bounding_boxes.clear();
-
-// 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(child_origin, visible_bounds, view, cx);
-
-// bounding_boxes.push(Some(Bounds<Pixels>::new(child_origin, child.size())));
-
-// match self.axis {
-// Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
-// Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
-// }
-
-// if can_resize && children_iter.peek().is_some() {
-// cx.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 => Bounds<Pixels>::new(
-// handle_origin,
-// vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()),
-// ),
-// Axis::Vertical => Bounds<Pixels>::new(
-// handle_origin,
-// vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE),
-// ),
-// };
-
-// let style = match self.axis {
-// Axis::Horizontal => CursorStyle::ResizeLeftRight,
-// Axis::Vertical => CursorStyle::ResizeUpDown,
-// };
-
-// cx.scene().push_cursor_region(CursorRegion {
-// bounds: handle_bounds,
-// style,
-// });
-
-// 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,
-// Self::handle_resize(
-// self.flexes.clone(),
-// self.axis,
-// ix,
-// child_start,
-// visible_bounds.clone(),
-// ),
-// )
-// .on_click(MouseButton::Left, {
-// let flexes = self.flexes.clone();
-// move |e, v: &mut Workspace, cx| {
-// if e.click_count >= 2 {
-// let mut borrow = flexes.borrow_mut();
-// *borrow = vec![1.; borrow.len()];
-// v.schedule_serialize(cx);
-// cx.notify();
-// }
-// }
-// });
-// cx.scene().push_mouse_region(mouse_region);
-
-// cx.scene().pop_stacking_context();
-// }
-// }
-
-// if overflowing {
-// cx.scene().pop_layer();
-// }
-// }
-
-// fn rect_for_text_range(
-// &self,
-// range_utf16: Range<usize>,
-// _: Bounds<Pixels>,
-// _: Bounds<Pixels>,
-// _: &Self::LayoutState,
-// _: &Self::PaintState,
-// view: &Workspace,
-// cx: &ViewContext<Workspace>,
-// ) -> Option<Bounds<Pixels>> {
-// self.children
-// .iter()
-// .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
-// }
-
-// fn debug(
-// &self,
-// bounds: Bounds<Pixels>,
-// _: &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>>()
-// })
-// }
-// }
-// }
+mod element {
+
+ use std::{cell::RefCell, iter, rc::Rc, sync::Arc};
+
+ use gpui::{
+ px, relative, Along, AnyElement, Axis, Bounds, CursorStyle, Element, IntoElement,
+ MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Style, WindowContext,
+ };
+ use parking_lot::Mutex;
+ use smallvec::SmallVec;
+
+ use super::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE};
+
+ pub fn pane_axis(
+ axis: Axis,
+ basis: usize,
+ flexes: Arc<Mutex<Vec<f32>>>,
+ bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
+ ) -> PaneAxisElement {
+ PaneAxisElement {
+ axis,
+ basis,
+ flexes,
+ bounding_boxes,
+ children: SmallVec::new(),
+ active_pane_ix: None,
+ }
+ }
+
+ pub struct PaneAxisElement {
+ axis: Axis,
+ basis: usize,
+ flexes: Arc<Mutex<Vec<f32>>>,
+ bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
+ children: SmallVec<[AnyElement; 2]>,
+ active_pane_ix: Option<usize>,
+ }
+
+ impl PaneAxisElement {
+ pub fn with_active_pane(mut self, active_pane_ix: Option<usize>) -> Self {
+ self.active_pane_ix = active_pane_ix;
+ self
+ }
+
+ fn compute_resize(
+ flexes: &Arc<Mutex<Vec<f32>>>,
+ e: &MouseMoveEvent,
+ ix: usize,
+ axis: Axis,
+ axis_bounds: Bounds<Pixels>,
+ cx: &mut WindowContext,
+ ) {
+ let min_size = match axis {
+ Axis::Horizontal => px(HORIZONTAL_MIN_SIZE),
+ Axis::Vertical => px(VERTICAL_MIN_SIZE),
+ };
+ let mut flexes = flexes.lock();
+ debug_assert!(flex_values_in_bounds(flexes.as_slice()));
+
+ let size = move |ix, flexes: &[f32]| {
+ axis_bounds.size.along(axis) * (flexes[ix] / flexes.len() as f32)
+ };
+
+ // Don't allow resizing to less than the minimum size, if elements are already too small
+ if min_size - px(1.) > size(ix, flexes.as_slice()) {
+ return;
+ }
+
+ let mut proposed_current_pixel_change =
+ (e.position - axis_bounds.origin).along(axis) - size(ix, flexes.as_slice());
+
+ let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
+ let flex_change = pixel_dx / axis_bounds.size.along(axis);
+ let current_target_flex = flexes[target_ix] + flex_change;
+ let next_target_flex = flexes[(target_ix as isize + next) as usize] - flex_change;
+ (current_target_flex, next_target_flex)
+ };
+
+ let mut successors = iter::from_fn({
+ let forward = proposed_current_pixel_change > px(0.);
+ let mut ix_offset = 0;
+ let len = flexes.len();
+ move || {
+ let result = if forward {
+ (ix + 1 + ix_offset < len).then(|| ix + ix_offset)
+ } else {
+ (ix as isize - ix_offset as isize >= 0).then(|| ix - ix_offset)
+ };
+
+ ix_offset += 1;
+
+ result
+ }
+ });
+
+ while proposed_current_pixel_change.abs() > px(0.) {
+ let Some(current_ix) = successors.next() else {
+ break;
+ };
+
+ let next_target_size = Pixels::max(
+ size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change,
+ min_size,
+ );
+
+ let current_target_size = Pixels::max(
+ size(current_ix, flexes.as_slice()) + size(current_ix + 1, flexes.as_slice())
+ - next_target_size,
+ min_size,
+ );
+
+ let current_pixel_change =
+ current_target_size - size(current_ix, flexes.as_slice());
+
+ let (current_target_flex, next_target_flex) =
+ flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice());
+
+ flexes[current_ix] = current_target_flex;
+ flexes[current_ix + 1] = next_target_flex;
+
+ proposed_current_pixel_change -= current_pixel_change;
+ }
+
+ // todo!(reserialize workspace)
+ // workspace.schedule_serialize(cx);
+ cx.notify();
+ }
+
+ fn push_handle(
+ flexes: Arc<Mutex<Vec<f32>>>,
+ dragged_handle: Rc<RefCell<Option<usize>>>,
+ axis: Axis,
+ ix: usize,
+ pane_bounds: Bounds<Pixels>,
+ axis_bounds: Bounds<Pixels>,
+ cx: &mut WindowContext,
+ ) {
+ let handle_bounds = Bounds {
+ origin: pane_bounds.origin.apply_along(axis, |o| {
+ o + pane_bounds.size.along(axis) - Pixels(HANDLE_HITBOX_SIZE / 2.)
+ }),
+ size: pane_bounds
+ .size
+ .apply_along(axis, |_| Pixels(HANDLE_HITBOX_SIZE)),
+ };
+
+ cx.with_z_index(3, |cx| {
+ if handle_bounds.contains(&cx.mouse_position()) {
+ cx.set_cursor_style(match axis {
+ Axis::Vertical => CursorStyle::ResizeUpDown,
+ Axis::Horizontal => CursorStyle::ResizeLeftRight,
+ })
+ }
+
+ cx.add_opaque_layer(handle_bounds);
+
+ cx.on_mouse_event({
+ let dragged_handle = dragged_handle.clone();
+ move |e: &MouseDownEvent, phase, cx| {
+ if phase.bubble() && handle_bounds.contains(&e.position) {
+ dragged_handle.replace(Some(ix));
+ }
+ }
+ });
+ cx.on_mouse_event(move |e: &MouseMoveEvent, phase, cx| {
+ let dragged_handle = dragged_handle.borrow();
+ if *dragged_handle == Some(ix) {
+ Self::compute_resize(&flexes, e, ix, axis, axis_bounds, cx)
+ }
+ });
+ });
+ }
+ }
+
+ impl IntoElement for PaneAxisElement {
+ type Element = Self;
+
+ fn element_id(&self) -> Option<ui::prelude::ElementId> {
+ Some(self.basis.into())
+ }
+
+ fn into_element(self) -> Self::Element {
+ self
+ }
+ }
+
+ impl Element for PaneAxisElement {
+ type State = Rc<RefCell<Option<usize>>>;
+
+ fn layout(
+ &mut self,
+ state: Option<Self::State>,
+ cx: &mut ui::prelude::WindowContext,
+ ) -> (gpui::LayoutId, Self::State) {
+ let mut style = Style::default();
+ style.size.width = relative(1.).into();
+ style.size.height = relative(1.).into();
+ let layout_id = cx.request_layout(&style, None);
+ let dragged_pane = state.unwrap_or_else(|| Rc::new(RefCell::new(None)));
+ (layout_id, dragged_pane)
+ }
+
+ fn paint(
+ self,
+ bounds: gpui::Bounds<ui::prelude::Pixels>,
+ state: &mut Self::State,
+ cx: &mut ui::prelude::WindowContext,
+ ) {
+ let flexes = self.flexes.lock().clone();
+ let len = self.children.len();
+ debug_assert!(flexes.len() == len);
+ debug_assert!(flex_values_in_bounds(flexes.as_slice()));
+
+ let mut origin = bounds.origin;
+ let space_per_flex = bounds.size.along(self.axis) / len as f32;
+
+ let mut bounding_boxes = self.bounding_boxes.lock();
+ bounding_boxes.clear();
+
+ for (ix, child) in self.children.into_iter().enumerate() {
+ //todo!(active_pane_magnification)
+ // If usign active pane magnification, need to switch to using
+ // 1 for all non-active panes, and then the magnification for the
+ // active pane.
+ let child_size = bounds
+ .size
+ .apply_along(self.axis, |_| space_per_flex * flexes[ix]);
+
+ let child_bounds = Bounds {
+ origin,
+ size: child_size,
+ };
+ bounding_boxes.push(Some(child_bounds));
+ cx.with_z_index(0, |cx| {
+ child.draw(origin, child_size.into(), cx);
+ });
+ cx.with_z_index(1, |cx| {
+ if ix < len - 1 {
+ Self::push_handle(
+ self.flexes.clone(),
+ state.clone(),
+ self.axis,
+ ix,
+ child_bounds,
+ bounds,
+ cx,
+ );
+ }
+ });
+
+ origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
+ }
+
+ cx.with_z_index(1, |cx| {
+ cx.on_mouse_event({
+ let state = state.clone();
+ move |e: &MouseUpEvent, phase, cx| {
+ if phase.bubble() {
+ state.replace(None);
+ }
+ }
+ });
+ })
+ }
+ }
+
+ impl ParentElement for PaneAxisElement {
+ fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement; 2]> {
+ &mut self.children
+ }
+ }
+
+ fn flex_values_in_bounds(flexes: &[f32]) -> bool {
+ (flexes.iter().copied().sum::<f32>() - flexes.len() as f32).abs() < 0.001
+ }
+ // // use std::{cell::RefCell, iter::from_fn, ops::Range, rc::Rc};
+
+ // // use gpui::{
+ // // geometry::{
+ // // rect::Bounds<Pixels>,
+ // // vector::{vec2f, Vector2F},
+ // // },
+ // // json::{self, ToJson},
+ // // platform::{CursorStyle, MouseButton},
+ // // scene::MouseDrag,
+ // // AnyElement, Axis, CursorRegion, Element, EventContext, MouseRegion, Bounds<Pixels>Ext,
+ // // 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>>,
+ // bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
+ // }
+
+ // impl PaneAxisElement {
+ // pub fn new(
+ // axis: Axis,
+ // basis: usize,
+ // flexes: Rc<RefCell<Vec<f32>>>,
+ // bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
+ // ) -> Self {
+ // Self {
+ // axis,
+ // basis,
+ // flexes,
+ // bounding_boxes,
+ // 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 ViewContext<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));
+ // }
+ // }
+
+ // fn handle_resize(
+ // flexes: Rc<RefCell<Vec<f32>>>,
+ // axis: Axis,
+ // preceding_ix: usize,
+ // child_start: Vector2F,
+ // drag_bounds: Bounds<Pixels>,
+ // ) -> impl Fn(MouseDrag, &mut Workspace, &mut EventContext<Workspace>) {
+ // let size = move |ix, flexes: &[f32]| {
+ // drag_bounds.length_along(axis) * (flexes[ix] / flexes.len() as f32)
+ // };
+
+ // move |drag, workspace: &mut Workspace, cx| {
+ // if drag.end {
+ // // TODO: Clear cascading resize state
+ // return;
+ // }
+ // let min_size = match axis {
+ // Axis::Horizontal => HORIZONTAL_MIN_SIZE,
+ // Axis::Vertical => VERTICAL_MIN_SIZE,
+ // };
+ // let mut flexes = flexes.borrow_mut();
+
+ // // Don't allow resizing to less than the minimum size, if elements are already too small
+ // if min_size - 1. > size(preceding_ix, flexes.as_slice()) {
+ // return;
+ // }
+
+ // let mut proposed_current_pixel_change = (drag.position - child_start).along(axis)
+ // - size(preceding_ix, flexes.as_slice());
+
+ // let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
+ // let flex_change = pixel_dx / drag_bounds.length_along(axis);
+ // let current_target_flex = flexes[target_ix] + flex_change;
+ // let next_target_flex =
+ // flexes[(target_ix as isize + next) as usize] - flex_change;
+ // (current_target_flex, next_target_flex)
+ // };
+
+ // let mut successors = from_fn({
+ // let forward = proposed_current_pixel_change > 0.;
+ // let mut ix_offset = 0;
+ // let len = flexes.len();
+ // move || {
+ // let result = if forward {
+ // (preceding_ix + 1 + ix_offset < len).then(|| preceding_ix + ix_offset)
+ // } else {
+ // (preceding_ix as isize - ix_offset as isize >= 0)
+ // .then(|| preceding_ix - ix_offset)
+ // };
+
+ // ix_offset += 1;
+
+ // result
+ // }
+ // });
+
+ // while proposed_current_pixel_change.abs() > 0. {
+ // let Some(current_ix) = successors.next() else {
+ // break;
+ // };
+
+ // let next_target_size = f32::max(
+ // size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change,
+ // min_size,
+ // );
+
+ // let current_target_size = f32::max(
+ // size(current_ix, flexes.as_slice())
+ // + size(current_ix + 1, flexes.as_slice())
+ // - next_target_size,
+ // min_size,
+ // );
+
+ // let current_pixel_change =
+ // current_target_size - size(current_ix, flexes.as_slice());
+
+ // let (current_target_flex, next_target_flex) =
+ // flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice());
+
+ // flexes[current_ix] = current_target_flex;
+ // flexes[current_ix + 1] = next_target_flex;
+
+ // proposed_current_pixel_change -= current_pixel_change;
+ // }
+
+ // workspace.schedule_serialize(cx);
+ // cx.notify();
+ // }
+ // }
+ // }
+
+ // 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 ViewContext<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,
+ // bounds: Bounds<Pixels>,
+ // visible_bounds: Bounds<Pixels>,
+ // 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 {
+ // cx.scene().push_layer(Some(visible_bounds));
+ // }
+
+ // let mut child_origin = bounds.origin();
+
+ // let mut bounding_boxes = self.bounding_boxes.borrow_mut();
+ // bounding_boxes.clear();
+
+ // 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(child_origin, visible_bounds, view, cx);
+
+ // bounding_boxes.push(Some(Bounds<Pixels>::new(child_origin, child.size())));
+
+ // match self.axis {
+ // Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
+ // Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
+ // }
+
+ // if can_resize && children_iter.peek().is_some() {
+ // cx.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 => Bounds<Pixels>::new(
+ // handle_origin,
+ // vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()),
+ // ),
+ // Axis::Vertical => Bounds<Pixels>::new(
+ // handle_origin,
+ // vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE),
+ // ),
+ // };
+
+ // let style = match self.axis {
+ // Axis::Horizontal => CursorStyle::ResizeLeftRight,
+ // Axis::Vertical => CursorStyle::ResizeUpDown,
+ // };
+
+ // cx.scene().push_cursor_region(CursorRegion {
+ // bounds: handle_bounds,
+ // style,
+ // });
+
+ // 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,
+ // Self::handle_resize(
+ // self.flexes.clone(),
+ // self.axis,
+ // ix,
+ // child_start,
+ // visible_bounds.clone(),
+ // ),
+ // )
+ // .on_click(MouseButton::Left, {
+ // let flexes = self.flexes.clone();
+ // move |e, v: &mut Workspace, cx| {
+ // if e.click_count >= 2 {
+ // let mut borrow = flexes.borrow_mut();
+ // *borrow = vec![1.; borrow.len()];
+ // v.schedule_serialize(cx);
+ // cx.notify();
+ // }
+ // }
+ // });
+ // cx.scene().push_mouse_region(mouse_region);
+
+ // cx.scene().pop_stacking_context();
+ // }
+ // }
+
+ // if overflowing {
+ // cx.scene().pop_layer();
+ // }
+ // }
+
+ // fn rect_for_text_range(
+ // &self,
+ // range_utf16: Range<usize>,
+ // _: Bounds<Pixels>,
+ // _: Bounds<Pixels>,
+ // _: &Self::LayoutState,
+ // _: &Self::PaintState,
+ // view: &Workspace,
+ // cx: &ViewContext<Workspace>,
+ // ) -> Option<Bounds<Pixels>> {
+ // self.children
+ // .iter()
+ // .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
+ // }
+
+ // fn debug(
+ // &self,
+ // bounds: Bounds<Pixels>,
+ // _: &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>>()
+ // })
+ // }
+ // }
+}
@@ -29,12 +29,12 @@ use futures::{
Future, FutureExt, StreamExt,
};
use gpui::{
- actions, div, impl_actions, point, size, Action, AnyModel, AnyView, AnyWeakView,
+ actions, canvas, div, impl_actions, point, size, Action, AnyModel, AnyView, AnyWeakView,
AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, Entity,
EntityId, EventEmitter, FocusHandle, FocusableView, GlobalPixels, InteractiveElement,
- KeyContext, ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Point,
- PromptLevel, Render, Size, Styled, Subscription, Task, View, ViewContext, VisualContext,
- WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
+ KeyContext, ManagedView, Model, ModelContext, MouseMoveEvent, ParentElement, PathPromptOptions,
+ Pixels, Point, PromptLevel, Render, Size, Styled, Subscription, Task, View, ViewContext,
+ VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
};
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
use itertools::Itertools;
@@ -227,6 +227,9 @@ pub fn init_settings(cx: &mut AppContext) {
}
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
+ cx.default_global::<DockDragState>();
+ cx.default_global::<DockClickReset>();
+
init_settings(cx);
notifications::init(cx);
@@ -480,8 +483,6 @@ struct FollowerState {
items_by_leader_view_id: HashMap<ViewId, Box<dyn FollowableItemHandle>>,
}
-enum WorkspaceBounds {}
-
impl Workspace {
pub fn new(
workspace_id: WorkspaceId,
@@ -2032,7 +2033,7 @@ impl Workspace {
};
let cursor = self.active_pane.read(cx).pixel_position_of_cursor(cx);
let center = match cursor {
- Some(cursor) if bounding_box.contains_point(&cursor) => cursor,
+ Some(cursor) if bounding_box.contains(&cursor) => cursor,
_ => bounding_box.center(),
};
@@ -3571,6 +3572,16 @@ impl FocusableView for Workspace {
}
}
+struct WorkspaceBounds(Bounds<Pixels>);
+
+//todo!("remove this when better drag APIs are in GPUI2")
+#[derive(Default)]
+struct DockDragState(Option<DockPosition>);
+
+//todo!("remove this when better double APIs are in GPUI2")
+#[derive(Default)]
+struct DockClickReset(Option<Task<()>>);
+
impl Render for Workspace {
type Element = Div;
@@ -3614,6 +3625,37 @@ impl Render for Workspace {
.border_t()
.border_b()
.border_color(cx.theme().colors().border)
+ .on_mouse_up(gpui::MouseButton::Left, |_, cx| {
+ cx.update_global(|drag: &mut DockDragState, cx| {
+ drag.0 = None;
+ })
+ })
+ .on_mouse_move(cx.listener(|workspace, e: &MouseMoveEvent, cx| {
+ if let Some(types) = &cx.global::<DockDragState>().0 {
+ let workspace_bounds = cx.global::<WorkspaceBounds>().0;
+ match types {
+ DockPosition::Left => {
+ let size = e.position.x;
+ workspace.left_dock.update(cx, |left_dock, cx| {
+ left_dock.resize_active_panel(Some(size.0), cx);
+ });
+ }
+ DockPosition::Right => {
+ let size = workspace_bounds.size.width - e.position.x;
+ workspace.right_dock.update(cx, |right_dock, cx| {
+ right_dock.resize_active_panel(Some(size.0), cx);
+ });
+ }
+ DockPosition::Bottom => {
+ let size = workspace_bounds.size.height - e.position.y;
+ workspace.bottom_dock.update(cx, |bottom_dock, cx| {
+ bottom_dock.resize_active_panel(Some(size.0), cx);
+ });
+ }
+ }
+ }
+ }))
+ .child(canvas(|bounds, cx| cx.set_global(WorkspaceBounds(bounds))))
.child(self.modal_layer.clone())
.child(
div()