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