1use crate::{AppState, FollowerState, Pane, Workspace};
2use anyhow::{anyhow, bail, Result};
3use call::{ActiveCall, ParticipantLocation};
4use collections::HashMap;
5use db::sqlez::{
6 bindable::{Bind, Column, StaticColumnCount},
7 statement::Statement,
8};
9use gpui::{
10 point, size, AnyWeakView, Bounds, Div, Entity as _, IntoElement, Model, Pixels, Point, View,
11 ViewContext,
12};
13use parking_lot::Mutex;
14use project::Project;
15use serde::Deserialize;
16use std::sync::Arc;
17use ui::{prelude::*, Button};
18
19const HANDLE_HITBOX_SIZE: f32 = 4.0;
20const HORIZONTAL_MIN_SIZE: f32 = 80.;
21const VERTICAL_MIN_SIZE: f32 = 100.;
22
23#[derive(Copy, Clone, PartialEq, Eq, Debug)]
24pub enum Axis {
25 Vertical,
26 Horizontal,
27}
28
29impl StaticColumnCount for Axis {}
30impl Bind for Axis {
31 fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result<i32> {
32 match self {
33 Axis::Horizontal => "Horizontal",
34 Axis::Vertical => "Vertical",
35 }
36 .bind(statement, start_index)
37 }
38}
39
40impl Column for Axis {
41 fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> {
42 String::column(statement, start_index).and_then(|(axis_text, next_index)| {
43 Ok((
44 match axis_text.as_str() {
45 "Horizontal" => Axis::Horizontal,
46 "Vertical" => Axis::Vertical,
47 _ => bail!("Stored serialized item kind is incorrect"),
48 },
49 next_index,
50 ))
51 })
52 }
53}
54
55#[derive(Clone, PartialEq)]
56pub struct PaneGroup {
57 pub(crate) root: Member,
58}
59
60impl PaneGroup {
61 pub(crate) fn with_root(root: Member) -> Self {
62 Self { root }
63 }
64
65 pub fn new(pane: View<Pane>) -> Self {
66 Self {
67 root: Member::Pane(pane),
68 }
69 }
70
71 pub fn split(
72 &mut self,
73 old_pane: &View<Pane>,
74 new_pane: &View<Pane>,
75 direction: SplitDirection,
76 ) -> Result<()> {
77 match &mut self.root {
78 Member::Pane(pane) => {
79 if pane == old_pane {
80 self.root = Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
81 Ok(())
82 } else {
83 Err(anyhow!("Pane not found"))
84 }
85 }
86 Member::Axis(axis) => axis.split(old_pane, new_pane, direction),
87 }
88 }
89
90 pub fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
91 match &self.root {
92 Member::Pane(_) => None,
93 Member::Axis(axis) => axis.bounding_box_for_pane(pane),
94 }
95 }
96
97 pub fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&View<Pane>> {
98 match &self.root {
99 Member::Pane(pane) => Some(pane),
100 Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
101 }
102 }
103
104 /// Returns:
105 /// - Ok(true) if it found and removed a pane
106 /// - Ok(false) if it found but did not remove the pane
107 /// - Err(_) if it did not find the pane
108 pub fn remove(&mut self, pane: &View<Pane>) -> Result<bool> {
109 match &mut self.root {
110 Member::Pane(_) => Ok(false),
111 Member::Axis(axis) => {
112 if let Some(last_pane) = axis.remove(pane)? {
113 self.root = last_pane;
114 }
115 Ok(true)
116 }
117 }
118 }
119
120 pub fn swap(&mut self, from: &View<Pane>, to: &View<Pane>) {
121 match &mut self.root {
122 Member::Pane(_) => {}
123 Member::Axis(axis) => axis.swap(from, to),
124 };
125 }
126
127 pub(crate) fn render(
128 &self,
129 project: &Model<Project>,
130 follower_states: &HashMap<View<Pane>, FollowerState>,
131 active_call: Option<&Model<ActiveCall>>,
132 active_pane: &View<Pane>,
133 zoomed: Option<&AnyWeakView>,
134 app_state: &Arc<AppState>,
135 cx: &mut ViewContext<Workspace>,
136 ) -> impl IntoElement {
137 self.root.render(
138 project,
139 0,
140 follower_states,
141 active_call,
142 active_pane,
143 zoomed,
144 app_state,
145 cx,
146 )
147 }
148
149 pub(crate) fn panes(&self) -> Vec<&View<Pane>> {
150 let mut panes = Vec::new();
151 self.root.collect_panes(&mut panes);
152 panes
153 }
154
155 pub(crate) fn first_pane(&self) -> View<Pane> {
156 self.root.first_pane()
157 }
158}
159
160#[derive(Clone, PartialEq)]
161pub(crate) enum Member {
162 Axis(PaneAxis),
163 Pane(View<Pane>),
164}
165
166impl Member {
167 fn new_axis(old_pane: View<Pane>, new_pane: View<Pane>, direction: SplitDirection) -> Self {
168 use Axis::*;
169 use SplitDirection::*;
170
171 let axis = match direction {
172 Up | Down => Vertical,
173 Left | Right => Horizontal,
174 };
175
176 let members = match direction {
177 Up | Left => vec![Member::Pane(new_pane), Member::Pane(old_pane)],
178 Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)],
179 };
180
181 Member::Axis(PaneAxis::new(axis, members))
182 }
183
184 fn contains(&self, needle: &View<Pane>) -> bool {
185 match self {
186 Member::Axis(axis) => axis.members.iter().any(|member| member.contains(needle)),
187 Member::Pane(pane) => pane == needle,
188 }
189 }
190
191 fn first_pane(&self) -> View<Pane> {
192 match self {
193 Member::Axis(axis) => axis.members[0].first_pane(),
194 Member::Pane(pane) => pane.clone(),
195 }
196 }
197
198 pub fn render(
199 &self,
200 project: &Model<Project>,
201 basis: usize,
202 follower_states: &HashMap<View<Pane>, FollowerState>,
203 active_call: Option<&Model<ActiveCall>>,
204 active_pane: &View<Pane>,
205 zoomed: Option<&AnyWeakView>,
206 app_state: &Arc<AppState>,
207 cx: &mut ViewContext<Workspace>,
208 ) -> impl IntoElement {
209 match self {
210 Member::Pane(pane) => {
211 let leader = follower_states.get(pane).and_then(|state| {
212 let room = active_call?.read(cx).room()?.read(cx);
213 room.remote_participant_for_peer_id(state.leader_id)
214 });
215
216 let mut leader_border = None;
217 let mut leader_status_box = None;
218 if let Some(leader) = &leader {
219 let mut leader_color = cx
220 .theme()
221 .players()
222 .color_for_participant(leader.participant_index.0)
223 .cursor;
224 leader_color.fade_out(0.3);
225 leader_border = Some(leader_color);
226
227 leader_status_box = match leader.location {
228 ParticipantLocation::SharedProject {
229 project_id: leader_project_id,
230 } => {
231 if Some(leader_project_id) == project.read(cx).remote_id() {
232 None
233 } else {
234 let leader_user = leader.user.clone();
235 let leader_user_id = leader.user.id;
236 Some(
237 Button::new(
238 ("leader-status", pane.entity_id()),
239 format!(
240 "Follow {} to their active project",
241 leader_user.github_login,
242 ),
243 )
244 .on_click(cx.listener(
245 move |this, _, cx| {
246 crate::join_remote_project(
247 leader_project_id,
248 leader_user_id,
249 this.app_state().clone(),
250 cx,
251 )
252 .detach_and_log_err(cx);
253 },
254 )),
255 )
256 }
257 }
258 ParticipantLocation::UnsharedProject => Some(Button::new(
259 ("leader-status", pane.entity_id()),
260 format!(
261 "{} is viewing an unshared Zed project",
262 leader.user.github_login
263 ),
264 )),
265 ParticipantLocation::External => Some(Button::new(
266 ("leader-status", pane.entity_id()),
267 format!(
268 "{} is viewing a window outside of Zed",
269 leader.user.github_login
270 ),
271 )),
272 };
273 }
274
275 div()
276 .relative()
277 .size_full()
278 .child(pane.clone())
279 .when_some(leader_border, |this, color| {
280 this.border_2().border_color(color)
281 })
282 .when_some(leader_status_box, |this, status_box| {
283 this.child(
284 div()
285 .absolute()
286 .w_96()
287 .bottom_3()
288 .right_3()
289 .z_index(1)
290 .child(status_box),
291 )
292 })
293 .into_any()
294
295 // let el = div()
296 // .flex()
297 // .flex_1()
298 // .gap_px()
299 // .w_full()
300 // .h_full()
301 // .bg(cx.theme().colors().editor)
302 // .children();
303 }
304 Member::Axis(axis) => axis
305 .render(
306 project,
307 basis + 1,
308 follower_states,
309 active_pane,
310 zoomed,
311 app_state,
312 cx,
313 )
314 .into_any(),
315 }
316
317 // enum FollowIntoExternalProject {}
318
319 // match self {
320 // Member::Pane(pane) => {
321 // let pane_element = if Some(&**pane) == zoomed {
322 // Empty::new().into_any()
323 // } else {
324 // ChildView::new(pane, cx).into_any()
325 // };
326
327 // let leader = follower_states.get(pane).and_then(|state| {
328 // let room = active_call?.read(cx).room()?.read(cx);
329 // room.remote_participant_for_peer_id(state.leader_id)
330 // });
331
332 // let mut leader_border = Border::default();
333 // let mut leader_status_box = None;
334 // if let Some(leader) = &leader {
335 // let leader_color = theme
336 // .editor
337 // .selection_style_for_room_participant(leader.participant_index.0)
338 // .cursor;
339 // leader_border = Border::all(theme.workspace.leader_border_width, leader_color);
340 // leader_border
341 // .color
342 // .fade_out(1. - theme.workspace.leader_border_opacity);
343 // leader_border.overlay = true;
344
345 // leader_status_box = match leader.location {
346 // ParticipantLocation::SharedProject {
347 // project_id: leader_project_id,
348 // } => {
349 // if Some(leader_project_id) == project.read(cx).remote_id() {
350 // None
351 // } else {
352 // let leader_user = leader.user.clone();
353 // let leader_user_id = leader.user.id;
354 // Some(
355 // MouseEventHandler::new::<FollowIntoExternalProject, _>(
356 // pane.id(),
357 // cx,
358 // |_, _| {
359 // Label::new(
360 // format!(
361 // "Follow {} to their active project",
362 // leader_user.github_login,
363 // ),
364 // theme
365 // .workspace
366 // .external_location_message
367 // .text
368 // .clone(),
369 // )
370 // .contained()
371 // .with_style(
372 // theme.workspace.external_location_message.container,
373 // )
374 // },
375 // )
376 // .with_cursor_style(CursorStyle::PointingHand)
377 // .on_click(MouseButton::Left, move |_, this, cx| {
378 // crate::join_remote_project(
379 // leader_project_id,
380 // leader_user_id,
381 // this.app_state().clone(),
382 // cx,
383 // )
384 // .detach_and_log_err(cx);
385 // })
386 // .aligned()
387 // .bottom()
388 // .right()
389 // .into_any(),
390 // )
391 // }
392 // }
393 // ParticipantLocation::UnsharedProject => Some(
394 // Label::new(
395 // format!(
396 // "{} is viewing an unshared Zed project",
397 // leader.user.github_login
398 // ),
399 // theme.workspace.external_location_message.text.clone(),
400 // )
401 // .contained()
402 // .with_style(theme.workspace.external_location_message.container)
403 // .aligned()
404 // .bottom()
405 // .right()
406 // .into_any(),
407 // ),
408 // ParticipantLocation::External => Some(
409 // Label::new(
410 // format!(
411 // "{} is viewing a window outside of Zed",
412 // leader.user.github_login
413 // ),
414 // theme.workspace.external_location_message.text.clone(),
415 // )
416 // .contained()
417 // .with_style(theme.workspace.external_location_message.container)
418 // .aligned()
419 // .bottom()
420 // .right()
421 // .into_any(),
422 // ),
423 // };
424 // }
425
426 // Stack::new()
427 // .with_child(pane_element.contained().with_border(leader_border))
428 // .with_children(leader_status_box)
429 // .into_any()
430 // }
431 // Member::Axis(axis) => axis.render(
432 // project,
433 // basis + 1,
434 // theme,
435 // follower_states,
436 // active_call,
437 // active_pane,
438 // zoomed,
439 // app_state,
440 // cx,
441 // ),
442 // }
443 }
444
445 fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a View<Pane>>) {
446 match self {
447 Member::Axis(axis) => {
448 for member in &axis.members {
449 member.collect_panes(panes);
450 }
451 }
452 Member::Pane(pane) => panes.push(pane),
453 }
454 }
455}
456
457#[derive(Clone)]
458pub(crate) struct PaneAxis {
459 pub axis: Axis,
460 pub members: Vec<Member>,
461 pub flexes: Arc<Mutex<Vec<f32>>>,
462 pub bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
463}
464
465impl PartialEq for PaneAxis {
466 fn eq(&self, other: &Self) -> bool {
467 todo!()
468 }
469}
470
471impl PaneAxis {
472 pub fn new(axis: Axis, members: Vec<Member>) -> Self {
473 let flexes = Arc::new(Mutex::new(vec![1.; members.len()]));
474 let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
475 Self {
476 axis,
477 members,
478 flexes,
479 bounding_boxes,
480 }
481 }
482
483 pub fn load(axis: Axis, members: Vec<Member>, flexes: Option<Vec<f32>>) -> Self {
484 let flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]);
485 debug_assert!(members.len() == flexes.len());
486
487 let flexes = Arc::new(Mutex::new(flexes));
488 let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
489 Self {
490 axis,
491 members,
492 flexes,
493 bounding_boxes,
494 }
495 }
496
497 fn split(
498 &mut self,
499 old_pane: &View<Pane>,
500 new_pane: &View<Pane>,
501 direction: SplitDirection,
502 ) -> Result<()> {
503 for (mut idx, member) in self.members.iter_mut().enumerate() {
504 match member {
505 Member::Axis(axis) => {
506 if axis.split(old_pane, new_pane, direction).is_ok() {
507 return Ok(());
508 }
509 }
510 Member::Pane(pane) => {
511 if pane == old_pane {
512 if direction.axis() == self.axis {
513 if direction.increasing() {
514 idx += 1;
515 }
516
517 self.members.insert(idx, Member::Pane(new_pane.clone()));
518 *self.flexes.lock() = vec![1.; self.members.len()];
519 } else {
520 *member =
521 Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
522 }
523 return Ok(());
524 }
525 }
526 }
527 }
528 Err(anyhow!("Pane not found"))
529 }
530
531 fn remove(&mut self, pane_to_remove: &View<Pane>) -> Result<Option<Member>> {
532 let mut found_pane = false;
533 let mut remove_member = None;
534 for (idx, member) in self.members.iter_mut().enumerate() {
535 match member {
536 Member::Axis(axis) => {
537 if let Ok(last_pane) = axis.remove(pane_to_remove) {
538 if let Some(last_pane) = last_pane {
539 *member = last_pane;
540 }
541 found_pane = true;
542 break;
543 }
544 }
545 Member::Pane(pane) => {
546 if pane == pane_to_remove {
547 found_pane = true;
548 remove_member = Some(idx);
549 break;
550 }
551 }
552 }
553 }
554
555 if found_pane {
556 if let Some(idx) = remove_member {
557 self.members.remove(idx);
558 *self.flexes.lock() = vec![1.; self.members.len()];
559 }
560
561 if self.members.len() == 1 {
562 let result = self.members.pop();
563 *self.flexes.lock() = vec![1.; self.members.len()];
564 Ok(result)
565 } else {
566 Ok(None)
567 }
568 } else {
569 Err(anyhow!("Pane not found"))
570 }
571 }
572
573 fn swap(&mut self, from: &View<Pane>, to: &View<Pane>) {
574 for member in self.members.iter_mut() {
575 match member {
576 Member::Axis(axis) => axis.swap(from, to),
577 Member::Pane(pane) => {
578 if pane == from {
579 *member = Member::Pane(to.clone());
580 } else if pane == to {
581 *member = Member::Pane(from.clone())
582 }
583 }
584 }
585 }
586 }
587
588 fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
589 debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
590
591 for (idx, member) in self.members.iter().enumerate() {
592 match member {
593 Member::Pane(found) => {
594 if pane == found {
595 return self.bounding_boxes.lock()[idx];
596 }
597 }
598 Member::Axis(axis) => {
599 if let Some(rect) = axis.bounding_box_for_pane(pane) {
600 return Some(rect);
601 }
602 }
603 }
604 }
605 None
606 }
607
608 fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&View<Pane>> {
609 debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
610
611 let bounding_boxes = self.bounding_boxes.lock();
612
613 for (idx, member) in self.members.iter().enumerate() {
614 if let Some(coordinates) = bounding_boxes[idx] {
615 if coordinates.contains_point(&coordinate) {
616 return match member {
617 Member::Pane(found) => Some(found),
618 Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
619 };
620 }
621 }
622 }
623 None
624 }
625
626 fn render(
627 &self,
628 project: &Model<Project>,
629 basis: usize,
630 follower_states: &HashMap<View<Pane>, FollowerState>,
631 active_pane: &View<Pane>,
632 zoomed: Option<&AnyWeakView>,
633 app_state: &Arc<AppState>,
634 cx: &mut ViewContext<Workspace>,
635 ) -> Div {
636 debug_assert!(self.members.len() == self.flexes.lock().len());
637
638 div()
639 .flex()
640 .flex_auto()
641 .map(|s| match self.axis {
642 Axis::Vertical => s.flex_col(),
643 Axis::Horizontal => s.flex_row(),
644 })
645 .children(self.members.iter().enumerate().map(|(ix, member)| {
646 match member {
647 Member::Axis(axis) => axis
648 .render(
649 project,
650 basis,
651 follower_states,
652 active_pane,
653 zoomed,
654 app_state,
655 cx,
656 )
657 .into_any_element(),
658 Member::Pane(pane) => pane.clone().into_any_element(),
659 }
660 }))
661
662 // let mut pane_axis = PaneAxisElement::new(
663 // self.axis,
664 // basis,
665 // self.flexes.clone(),
666 // self.bounding_boxes.clone(),
667 // );
668 // let mut active_pane_ix = None;
669
670 // let mut members = self.members.iter().enumerate().peekable();
671 // while let Some((ix, member)) = members.next() {
672 // let last = members.peek().is_none();
673
674 // if member.contains(active_pane) {
675 // active_pane_ix = Some(ix);
676 // }
677
678 // let mut member = member.render(
679 // project,
680 // (basis + ix) * 10,
681 // theme,
682 // follower_states,
683 // active_call,
684 // active_pane,
685 // zoomed,
686 // app_state,
687 // cx,
688 // );
689
690 // if !last {
691 // let mut border = theme.workspace.pane_divider;
692 // border.left = false;
693 // border.right = false;
694 // border.top = false;
695 // border.bottom = false;
696
697 // match self.axis {
698 // Axis::Vertical => border.bottom = true,
699 // Axis::Horizontal => border.right = true,
700 // }
701
702 // member = member.contained().with_border(border).into_any();
703 // }
704
705 // pane_axis = pane_axis.with_child(member.into_any());
706 // }
707 // pane_axis.set_active_pane(active_pane_ix);
708 // pane_axis.into_any()
709 }
710}
711
712#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
713pub enum SplitDirection {
714 Up,
715 Down,
716 Left,
717 Right,
718}
719
720impl SplitDirection {
721 pub fn all() -> [Self; 4] {
722 [Self::Up, Self::Down, Self::Left, Self::Right]
723 }
724
725 pub fn edge(&self, rect: Bounds<Pixels>) -> Pixels {
726 match self {
727 Self::Up => rect.origin.y,
728 Self::Down => rect.lower_left().y,
729 Self::Left => rect.lower_left().x,
730 Self::Right => rect.lower_right().x,
731 }
732 }
733
734 pub fn along_edge(&self, bounds: Bounds<Pixels>, length: Pixels) -> Bounds<Pixels> {
735 match self {
736 Self::Up => Bounds {
737 origin: bounds.origin,
738 size: size(bounds.size.width, length),
739 },
740 Self::Down => Bounds {
741 origin: point(bounds.lower_left().x, bounds.lower_left().y - length),
742 size: size(bounds.size.width, length),
743 },
744 Self::Left => Bounds {
745 origin: bounds.origin,
746 size: size(length, bounds.size.height),
747 },
748 Self::Right => Bounds {
749 origin: point(bounds.lower_right().x - length, bounds.lower_left().y),
750 size: size(length, bounds.size.height),
751 },
752 }
753 }
754
755 pub fn axis(&self) -> Axis {
756 match self {
757 Self::Up | Self::Down => Axis::Vertical,
758 Self::Left | Self::Right => Axis::Horizontal,
759 }
760 }
761
762 pub fn increasing(&self) -> bool {
763 match self {
764 Self::Left | Self::Up => false,
765 Self::Down | Self::Right => true,
766 }
767 }
768}
769
770// mod element {
771// // use std::{cell::RefCell, iter::from_fn, ops::Range, rc::Rc};
772
773// // use gpui::{
774// // geometry::{
775// // rect::Bounds<Pixels>,
776// // vector::{vec2f, Vector2F},
777// // },
778// // json::{self, ToJson},
779// // platform::{CursorStyle, MouseButton},
780// // scene::MouseDrag,
781// // AnyElement, Axis, CursorRegion, Element, EventContext, MouseRegion, Bounds<Pixels>Ext,
782// // SizeConstraint, Vector2FExt, ViewContext,
783// // };
784
785// use crate::{
786// pane_group::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE},
787// Workspace, WorkspaceSettings,
788// };
789
790// pub struct PaneAxisElement {
791// axis: Axis,
792// basis: usize,
793// active_pane_ix: Option<usize>,
794// flexes: Rc<RefCell<Vec<f32>>>,
795// children: Vec<AnyElement<Workspace>>,
796// bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
797// }
798
799// impl PaneAxisElement {
800// pub fn new(
801// axis: Axis,
802// basis: usize,
803// flexes: Rc<RefCell<Vec<f32>>>,
804// bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
805// ) -> Self {
806// Self {
807// axis,
808// basis,
809// flexes,
810// bounding_boxes,
811// active_pane_ix: None,
812// children: Default::default(),
813// }
814// }
815
816// pub fn set_active_pane(&mut self, active_pane_ix: Option<usize>) {
817// self.active_pane_ix = active_pane_ix;
818// }
819
820// fn layout_children(
821// &mut self,
822// active_pane_magnification: f32,
823// constraint: SizeConstraint,
824// remaining_space: &mut f32,
825// remaining_flex: &mut f32,
826// cross_axis_max: &mut f32,
827// view: &mut Workspace,
828// cx: &mut ViewContext<Workspace>,
829// ) {
830// let flexes = self.flexes.borrow();
831// let cross_axis = self.axis.invert();
832// for (ix, child) in self.children.iter_mut().enumerate() {
833// let flex = if active_pane_magnification != 1. {
834// if let Some(active_pane_ix) = self.active_pane_ix {
835// if ix == active_pane_ix {
836// active_pane_magnification
837// } else {
838// 1.
839// }
840// } else {
841// 1.
842// }
843// } else {
844// flexes[ix]
845// };
846
847// let child_size = if *remaining_flex == 0.0 {
848// *remaining_space
849// } else {
850// let space_per_flex = *remaining_space / *remaining_flex;
851// space_per_flex * flex
852// };
853
854// let child_constraint = match self.axis {
855// Axis::Horizontal => SizeConstraint::new(
856// vec2f(child_size, constraint.min.y()),
857// vec2f(child_size, constraint.max.y()),
858// ),
859// Axis::Vertical => SizeConstraint::new(
860// vec2f(constraint.min.x(), child_size),
861// vec2f(constraint.max.x(), child_size),
862// ),
863// };
864// let child_size = child.layout(child_constraint, view, cx);
865// *remaining_space -= child_size.along(self.axis);
866// *remaining_flex -= flex;
867// *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
868// }
869// }
870
871// fn handle_resize(
872// flexes: Rc<RefCell<Vec<f32>>>,
873// axis: Axis,
874// preceding_ix: usize,
875// child_start: Vector2F,
876// drag_bounds: Bounds<Pixels>,
877// ) -> impl Fn(MouseDrag, &mut Workspace, &mut EventContext<Workspace>) {
878// let size = move |ix, flexes: &[f32]| {
879// drag_bounds.length_along(axis) * (flexes[ix] / flexes.len() as f32)
880// };
881
882// move |drag, workspace: &mut Workspace, cx| {
883// if drag.end {
884// // TODO: Clear cascading resize state
885// return;
886// }
887// let min_size = match axis {
888// Axis::Horizontal => HORIZONTAL_MIN_SIZE,
889// Axis::Vertical => VERTICAL_MIN_SIZE,
890// };
891// let mut flexes = flexes.borrow_mut();
892
893// // Don't allow resizing to less than the minimum size, if elements are already too small
894// if min_size - 1. > size(preceding_ix, flexes.as_slice()) {
895// return;
896// }
897
898// let mut proposed_current_pixel_change = (drag.position - child_start).along(axis)
899// - size(preceding_ix, flexes.as_slice());
900
901// let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
902// let flex_change = pixel_dx / drag_bounds.length_along(axis);
903// let current_target_flex = flexes[target_ix] + flex_change;
904// let next_target_flex =
905// flexes[(target_ix as isize + next) as usize] - flex_change;
906// (current_target_flex, next_target_flex)
907// };
908
909// let mut successors = from_fn({
910// let forward = proposed_current_pixel_change > 0.;
911// let mut ix_offset = 0;
912// let len = flexes.len();
913// move || {
914// let result = if forward {
915// (preceding_ix + 1 + ix_offset < len).then(|| preceding_ix + ix_offset)
916// } else {
917// (preceding_ix as isize - ix_offset as isize >= 0)
918// .then(|| preceding_ix - ix_offset)
919// };
920
921// ix_offset += 1;
922
923// result
924// }
925// });
926
927// while proposed_current_pixel_change.abs() > 0. {
928// let Some(current_ix) = successors.next() else {
929// break;
930// };
931
932// let next_target_size = f32::max(
933// size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change,
934// min_size,
935// );
936
937// let current_target_size = f32::max(
938// size(current_ix, flexes.as_slice())
939// + size(current_ix + 1, flexes.as_slice())
940// - next_target_size,
941// min_size,
942// );
943
944// let current_pixel_change =
945// current_target_size - size(current_ix, flexes.as_slice());
946
947// let (current_target_flex, next_target_flex) =
948// flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice());
949
950// flexes[current_ix] = current_target_flex;
951// flexes[current_ix + 1] = next_target_flex;
952
953// proposed_current_pixel_change -= current_pixel_change;
954// }
955
956// workspace.schedule_serialize(cx);
957// cx.notify();
958// }
959// }
960// }
961
962// impl Extend<AnyElement<Workspace>> for PaneAxisElement {
963// fn extend<T: IntoIterator<Item = AnyElement<Workspace>>>(&mut self, children: T) {
964// self.children.extend(children);
965// }
966// }
967
968// impl Element<Workspace> for PaneAxisElement {
969// type LayoutState = f32;
970// type PaintState = ();
971
972// fn layout(
973// &mut self,
974// constraint: SizeConstraint,
975// view: &mut Workspace,
976// cx: &mut ViewContext<Workspace>,
977// ) -> (Vector2F, Self::LayoutState) {
978// debug_assert!(self.children.len() == self.flexes.borrow().len());
979
980// let active_pane_magnification =
981// settings::get::<WorkspaceSettings>(cx).active_pane_magnification;
982
983// let mut remaining_flex = 0.;
984
985// if active_pane_magnification != 1. {
986// let active_pane_flex = self
987// .active_pane_ix
988// .map(|_| active_pane_magnification)
989// .unwrap_or(1.);
990// remaining_flex += self.children.len() as f32 - 1. + active_pane_flex;
991// } else {
992// for flex in self.flexes.borrow().iter() {
993// remaining_flex += flex;
994// }
995// }
996
997// let mut cross_axis_max: f32 = 0.0;
998// let mut remaining_space = constraint.max_along(self.axis);
999
1000// if remaining_space.is_infinite() {
1001// panic!("flex contains flexible children but has an infinite constraint along the flex axis");
1002// }
1003
1004// self.layout_children(
1005// active_pane_magnification,
1006// constraint,
1007// &mut remaining_space,
1008// &mut remaining_flex,
1009// &mut cross_axis_max,
1010// view,
1011// cx,
1012// );
1013
1014// let mut size = match self.axis {
1015// Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
1016// Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
1017// };
1018
1019// if constraint.min.x().is_finite() {
1020// size.set_x(size.x().max(constraint.min.x()));
1021// }
1022// if constraint.min.y().is_finite() {
1023// size.set_y(size.y().max(constraint.min.y()));
1024// }
1025
1026// if size.x() > constraint.max.x() {
1027// size.set_x(constraint.max.x());
1028// }
1029// if size.y() > constraint.max.y() {
1030// size.set_y(constraint.max.y());
1031// }
1032
1033// (size, remaining_space)
1034// }
1035
1036// fn paint(
1037// &mut self,
1038// bounds: Bounds<Pixels>,
1039// visible_bounds: Bounds<Pixels>,
1040// remaining_space: &mut Self::LayoutState,
1041// view: &mut Workspace,
1042// cx: &mut ViewContext<Workspace>,
1043// ) -> Self::PaintState {
1044// let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.;
1045// let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
1046
1047// let overflowing = *remaining_space < 0.;
1048// if overflowing {
1049// cx.scene().push_layer(Some(visible_bounds));
1050// }
1051
1052// let mut child_origin = bounds.origin();
1053
1054// let mut bounding_boxes = self.bounding_boxes.borrow_mut();
1055// bounding_boxes.clear();
1056
1057// let mut children_iter = self.children.iter_mut().enumerate().peekable();
1058// while let Some((ix, child)) = children_iter.next() {
1059// let child_start = child_origin.clone();
1060// child.paint(child_origin, visible_bounds, view, cx);
1061
1062// bounding_boxes.push(Some(Bounds<Pixels>::new(child_origin, child.size())));
1063
1064// match self.axis {
1065// Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
1066// Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
1067// }
1068
1069// if can_resize && children_iter.peek().is_some() {
1070// cx.scene().push_stacking_context(None, None);
1071
1072// let handle_origin = match self.axis {
1073// Axis::Horizontal => child_origin - vec2f(HANDLE_HITBOX_SIZE / 2., 0.0),
1074// Axis::Vertical => child_origin - vec2f(0.0, HANDLE_HITBOX_SIZE / 2.),
1075// };
1076
1077// let handle_bounds = match self.axis {
1078// Axis::Horizontal => Bounds<Pixels>::new(
1079// handle_origin,
1080// vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()),
1081// ),
1082// Axis::Vertical => Bounds<Pixels>::new(
1083// handle_origin,
1084// vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE),
1085// ),
1086// };
1087
1088// let style = match self.axis {
1089// Axis::Horizontal => CursorStyle::ResizeLeftRight,
1090// Axis::Vertical => CursorStyle::ResizeUpDown,
1091// };
1092
1093// cx.scene().push_cursor_region(CursorRegion {
1094// bounds: handle_bounds,
1095// style,
1096// });
1097
1098// enum ResizeHandle {}
1099// let mut mouse_region = MouseRegion::new::<ResizeHandle>(
1100// cx.view_id(),
1101// self.basis + ix,
1102// handle_bounds,
1103// );
1104// mouse_region = mouse_region
1105// .on_drag(
1106// MouseButton::Left,
1107// Self::handle_resize(
1108// self.flexes.clone(),
1109// self.axis,
1110// ix,
1111// child_start,
1112// visible_bounds.clone(),
1113// ),
1114// )
1115// .on_click(MouseButton::Left, {
1116// let flexes = self.flexes.clone();
1117// move |e, v: &mut Workspace, cx| {
1118// if e.click_count >= 2 {
1119// let mut borrow = flexes.borrow_mut();
1120// *borrow = vec![1.; borrow.len()];
1121// v.schedule_serialize(cx);
1122// cx.notify();
1123// }
1124// }
1125// });
1126// cx.scene().push_mouse_region(mouse_region);
1127
1128// cx.scene().pop_stacking_context();
1129// }
1130// }
1131
1132// if overflowing {
1133// cx.scene().pop_layer();
1134// }
1135// }
1136
1137// fn rect_for_text_range(
1138// &self,
1139// range_utf16: Range<usize>,
1140// _: Bounds<Pixels>,
1141// _: Bounds<Pixels>,
1142// _: &Self::LayoutState,
1143// _: &Self::PaintState,
1144// view: &Workspace,
1145// cx: &ViewContext<Workspace>,
1146// ) -> Option<Bounds<Pixels>> {
1147// self.children
1148// .iter()
1149// .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
1150// }
1151
1152// fn debug(
1153// &self,
1154// bounds: Bounds<Pixels>,
1155// _: &Self::LayoutState,
1156// _: &Self::PaintState,
1157// view: &Workspace,
1158// cx: &ViewContext<Workspace>,
1159// ) -> json::Value {
1160// serde_json::json!({
1161// "type": "PaneAxis",
1162// "bounds": bounds.to_json(),
1163// "axis": self.axis.to_json(),
1164// "flexes": *self.flexes.borrow(),
1165// "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
1166// })
1167// }
1168// }
1169// }