1use crate::{pane_group::element::pane_axis, AppState, FollowerState, Pane, Workspace};
2use anyhow::{anyhow, Result};
3use call::{ActiveCall, ParticipantLocation};
4use collections::HashMap;
5use gpui::{
6 point, size, AnyWeakView, Axis, Bounds, Entity as _, IntoElement, Model, Pixels, Point, View,
7 ViewContext,
8};
9use parking_lot::Mutex;
10use project::Project;
11use serde::Deserialize;
12use std::sync::Arc;
13use ui::{prelude::*, Button};
14
15const HANDLE_HITBOX_SIZE: f32 = 10.0; //todo!(change this back to 4)
16const HORIZONTAL_MIN_SIZE: f32 = 80.;
17const VERTICAL_MIN_SIZE: f32 = 100.;
18
19#[derive(Clone)]
20pub struct PaneGroup {
21 pub(crate) root: Member,
22}
23
24impl PaneGroup {
25 pub(crate) fn with_root(root: Member) -> Self {
26 Self { root }
27 }
28
29 pub fn new(pane: View<Pane>) -> Self {
30 Self {
31 root: Member::Pane(pane),
32 }
33 }
34
35 pub fn split(
36 &mut self,
37 old_pane: &View<Pane>,
38 new_pane: &View<Pane>,
39 direction: SplitDirection,
40 ) -> Result<()> {
41 match &mut self.root {
42 Member::Pane(pane) => {
43 if pane == old_pane {
44 self.root = Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
45 Ok(())
46 } else {
47 Err(anyhow!("Pane not found"))
48 }
49 }
50 Member::Axis(axis) => axis.split(old_pane, new_pane, direction),
51 }
52 }
53
54 pub fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
55 match &self.root {
56 Member::Pane(_) => None,
57 Member::Axis(axis) => axis.bounding_box_for_pane(pane),
58 }
59 }
60
61 pub fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&View<Pane>> {
62 match &self.root {
63 Member::Pane(pane) => Some(pane),
64 Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
65 }
66 }
67
68 /// Returns:
69 /// - Ok(true) if it found and removed a pane
70 /// - Ok(false) if it found but did not remove the pane
71 /// - Err(_) if it did not find the pane
72 pub fn remove(&mut self, pane: &View<Pane>) -> Result<bool> {
73 match &mut self.root {
74 Member::Pane(_) => Ok(false),
75 Member::Axis(axis) => {
76 if let Some(last_pane) = axis.remove(pane)? {
77 self.root = last_pane;
78 }
79 Ok(true)
80 }
81 }
82 }
83
84 pub fn swap(&mut self, from: &View<Pane>, to: &View<Pane>) {
85 match &mut self.root {
86 Member::Pane(_) => {}
87 Member::Axis(axis) => axis.swap(from, to),
88 };
89 }
90
91 pub(crate) fn render(
92 &self,
93 project: &Model<Project>,
94 follower_states: &HashMap<View<Pane>, FollowerState>,
95 active_call: Option<&Model<ActiveCall>>,
96 active_pane: &View<Pane>,
97 zoomed: Option<&AnyWeakView>,
98 app_state: &Arc<AppState>,
99 cx: &mut ViewContext<Workspace>,
100 ) -> impl IntoElement {
101 self.root.render(
102 project,
103 0,
104 follower_states,
105 active_call,
106 active_pane,
107 zoomed,
108 app_state,
109 cx,
110 )
111 }
112
113 pub(crate) fn panes(&self) -> Vec<&View<Pane>> {
114 let mut panes = Vec::new();
115 self.root.collect_panes(&mut panes);
116 panes
117 }
118
119 pub(crate) fn first_pane(&self) -> View<Pane> {
120 self.root.first_pane()
121 }
122}
123
124#[derive(Clone)]
125pub(crate) enum Member {
126 Axis(PaneAxis),
127 Pane(View<Pane>),
128}
129
130impl Member {
131 fn new_axis(old_pane: View<Pane>, new_pane: View<Pane>, direction: SplitDirection) -> Self {
132 use Axis::*;
133 use SplitDirection::*;
134
135 let axis = match direction {
136 Up | Down => Vertical,
137 Left | Right => Horizontal,
138 };
139
140 let members = match direction {
141 Up | Left => vec![Member::Pane(new_pane), Member::Pane(old_pane)],
142 Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)],
143 };
144
145 Member::Axis(PaneAxis::new(axis, members))
146 }
147
148 fn contains(&self, needle: &View<Pane>) -> bool {
149 match self {
150 Member::Axis(axis) => axis.members.iter().any(|member| member.contains(needle)),
151 Member::Pane(pane) => pane == needle,
152 }
153 }
154
155 fn first_pane(&self) -> View<Pane> {
156 match self {
157 Member::Axis(axis) => axis.members[0].first_pane(),
158 Member::Pane(pane) => pane.clone(),
159 }
160 }
161
162 pub fn render(
163 &self,
164 project: &Model<Project>,
165 basis: usize,
166 follower_states: &HashMap<View<Pane>, FollowerState>,
167 active_call: Option<&Model<ActiveCall>>,
168 active_pane: &View<Pane>,
169 zoomed: Option<&AnyWeakView>,
170 app_state: &Arc<AppState>,
171 cx: &mut ViewContext<Workspace>,
172 ) -> impl IntoElement {
173 match self {
174 Member::Pane(pane) => {
175 if zoomed == Some(&pane.downgrade().into()) {
176 return div().into_any();
177 }
178
179 let leader = follower_states.get(pane).and_then(|state| {
180 let room = active_call?.read(cx).room()?.read(cx);
181 room.remote_participant_for_peer_id(state.leader_id)
182 });
183
184 let mut leader_border = None;
185 let mut leader_status_box = None;
186 if let Some(leader) = &leader {
187 let mut leader_color = cx
188 .theme()
189 .players()
190 .color_for_participant(leader.participant_index.0)
191 .cursor;
192 leader_color.fade_out(0.3);
193 leader_border = Some(leader_color);
194
195 leader_status_box = match leader.location {
196 ParticipantLocation::SharedProject {
197 project_id: leader_project_id,
198 } => {
199 if Some(leader_project_id) == project.read(cx).remote_id() {
200 None
201 } else {
202 let leader_user = leader.user.clone();
203 let leader_user_id = leader.user.id;
204 Some(
205 Button::new(
206 ("leader-status", pane.entity_id()),
207 format!(
208 "Follow {} to their active project",
209 leader_user.github_login,
210 ),
211 )
212 .on_click(cx.listener(
213 move |this, _, cx| {
214 crate::join_remote_project(
215 leader_project_id,
216 leader_user_id,
217 this.app_state().clone(),
218 cx,
219 )
220 .detach_and_log_err(cx);
221 },
222 )),
223 )
224 }
225 }
226 ParticipantLocation::UnsharedProject => Some(Button::new(
227 ("leader-status", pane.entity_id()),
228 format!(
229 "{} is viewing an unshared Zed project",
230 leader.user.github_login
231 ),
232 )),
233 ParticipantLocation::External => Some(Button::new(
234 ("leader-status", pane.entity_id()),
235 format!(
236 "{} is viewing a window outside of Zed",
237 leader.user.github_login
238 ),
239 )),
240 };
241 }
242
243 div()
244 .relative()
245 .size_full()
246 .child(pane.clone())
247 .when_some(leader_border, |this, color| {
248 this.border_2().border_color(color)
249 })
250 .when_some(leader_status_box, |this, status_box| {
251 this.child(
252 div()
253 .absolute()
254 .w_96()
255 .bottom_3()
256 .right_3()
257 .z_index(1)
258 .child(status_box),
259 )
260 })
261 .into_any()
262
263 // let el = div()
264 // .flex()
265 // .flex_1()
266 // .gap_px()
267 // .w_full()
268 // .h_full()
269 // .bg(cx.theme().colors().editor)
270 // .children();
271 }
272 Member::Axis(axis) => axis
273 .render(
274 project,
275 basis + 1,
276 follower_states,
277 active_call,
278 active_pane,
279 zoomed,
280 app_state,
281 cx,
282 )
283 .into_any(),
284 }
285 }
286
287 fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a View<Pane>>) {
288 match self {
289 Member::Axis(axis) => {
290 for member in &axis.members {
291 member.collect_panes(panes);
292 }
293 }
294 Member::Pane(pane) => panes.push(pane),
295 }
296 }
297}
298
299#[derive(Clone)]
300pub(crate) struct PaneAxis {
301 pub axis: Axis,
302 pub members: Vec<Member>,
303 pub flexes: Arc<Mutex<Vec<f32>>>,
304 pub bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
305}
306
307impl PaneAxis {
308 pub fn new(axis: Axis, members: Vec<Member>) -> Self {
309 let flexes = Arc::new(Mutex::new(vec![1.; members.len()]));
310 let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
311 Self {
312 axis,
313 members,
314 flexes,
315 bounding_boxes,
316 }
317 }
318
319 pub fn load(axis: Axis, members: Vec<Member>, flexes: Option<Vec<f32>>) -> Self {
320 let flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]);
321 debug_assert!(members.len() == flexes.len());
322
323 let flexes = Arc::new(Mutex::new(flexes));
324 let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
325 Self {
326 axis,
327 members,
328 flexes,
329 bounding_boxes,
330 }
331 }
332
333 fn split(
334 &mut self,
335 old_pane: &View<Pane>,
336 new_pane: &View<Pane>,
337 direction: SplitDirection,
338 ) -> Result<()> {
339 for (mut idx, member) in self.members.iter_mut().enumerate() {
340 match member {
341 Member::Axis(axis) => {
342 if axis.split(old_pane, new_pane, direction).is_ok() {
343 return Ok(());
344 }
345 }
346 Member::Pane(pane) => {
347 if pane == old_pane {
348 if direction.axis() == self.axis {
349 if direction.increasing() {
350 idx += 1;
351 }
352
353 self.members.insert(idx, Member::Pane(new_pane.clone()));
354 *self.flexes.lock() = vec![1.; self.members.len()];
355 } else {
356 *member =
357 Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
358 }
359 return Ok(());
360 }
361 }
362 }
363 }
364 Err(anyhow!("Pane not found"))
365 }
366
367 fn remove(&mut self, pane_to_remove: &View<Pane>) -> Result<Option<Member>> {
368 let mut found_pane = false;
369 let mut remove_member = None;
370 for (idx, member) in self.members.iter_mut().enumerate() {
371 match member {
372 Member::Axis(axis) => {
373 if let Ok(last_pane) = axis.remove(pane_to_remove) {
374 if let Some(last_pane) = last_pane {
375 *member = last_pane;
376 }
377 found_pane = true;
378 break;
379 }
380 }
381 Member::Pane(pane) => {
382 if pane == pane_to_remove {
383 found_pane = true;
384 remove_member = Some(idx);
385 break;
386 }
387 }
388 }
389 }
390
391 if found_pane {
392 if let Some(idx) = remove_member {
393 self.members.remove(idx);
394 *self.flexes.lock() = vec![1.; self.members.len()];
395 }
396
397 if self.members.len() == 1 {
398 let result = self.members.pop();
399 *self.flexes.lock() = vec![1.; self.members.len()];
400 Ok(result)
401 } else {
402 Ok(None)
403 }
404 } else {
405 Err(anyhow!("Pane not found"))
406 }
407 }
408
409 fn swap(&mut self, from: &View<Pane>, to: &View<Pane>) {
410 for member in self.members.iter_mut() {
411 match member {
412 Member::Axis(axis) => axis.swap(from, to),
413 Member::Pane(pane) => {
414 if pane == from {
415 *member = Member::Pane(to.clone());
416 } else if pane == to {
417 *member = Member::Pane(from.clone())
418 }
419 }
420 }
421 }
422 }
423
424 fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
425 debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
426
427 for (idx, member) in self.members.iter().enumerate() {
428 match member {
429 Member::Pane(found) => {
430 if pane == found {
431 return self.bounding_boxes.lock()[idx];
432 }
433 }
434 Member::Axis(axis) => {
435 if let Some(rect) = axis.bounding_box_for_pane(pane) {
436 return Some(rect);
437 }
438 }
439 }
440 }
441 None
442 }
443
444 fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&View<Pane>> {
445 debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
446
447 let bounding_boxes = self.bounding_boxes.lock();
448
449 for (idx, member) in self.members.iter().enumerate() {
450 if let Some(coordinates) = bounding_boxes[idx] {
451 if coordinates.contains(&coordinate) {
452 return match member {
453 Member::Pane(found) => Some(found),
454 Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
455 };
456 }
457 }
458 }
459 None
460 }
461
462 fn render(
463 &self,
464 project: &Model<Project>,
465 basis: usize,
466 follower_states: &HashMap<View<Pane>, FollowerState>,
467 active_call: Option<&Model<ActiveCall>>,
468 active_pane: &View<Pane>,
469 zoomed: Option<&AnyWeakView>,
470 app_state: &Arc<AppState>,
471 cx: &mut ViewContext<Workspace>,
472 ) -> gpui::AnyElement {
473 debug_assert!(self.members.len() == self.flexes.lock().len());
474 let mut active_pane_ix = None;
475
476 pane_axis(
477 self.axis,
478 basis,
479 self.flexes.clone(),
480 self.bounding_boxes.clone(),
481 )
482 .children(self.members.iter().enumerate().map(|(ix, member)| {
483 if member.contains(active_pane) {
484 active_pane_ix = Some(ix);
485 }
486 member
487 .render(
488 project,
489 (basis + ix) * 10,
490 follower_states,
491 active_call,
492 active_pane,
493 zoomed,
494 app_state,
495 cx,
496 )
497 .into_any_element()
498 }))
499 .with_active_pane(active_pane_ix)
500 .into_any_element()
501 }
502}
503
504#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
505pub enum SplitDirection {
506 Up,
507 Down,
508 Left,
509 Right,
510}
511
512impl SplitDirection {
513 pub fn all() -> [Self; 4] {
514 [Self::Up, Self::Down, Self::Left, Self::Right]
515 }
516
517 pub fn edge(&self, rect: Bounds<Pixels>) -> Pixels {
518 match self {
519 Self::Up => rect.origin.y,
520 Self::Down => rect.lower_left().y,
521 Self::Left => rect.lower_left().x,
522 Self::Right => rect.lower_right().x,
523 }
524 }
525
526 pub fn along_edge(&self, bounds: Bounds<Pixels>, length: Pixels) -> Bounds<Pixels> {
527 match self {
528 Self::Up => Bounds {
529 origin: bounds.origin,
530 size: size(bounds.size.width, length),
531 },
532 Self::Down => Bounds {
533 origin: point(bounds.lower_left().x, bounds.lower_left().y - length),
534 size: size(bounds.size.width, length),
535 },
536 Self::Left => Bounds {
537 origin: bounds.origin,
538 size: size(length, bounds.size.height),
539 },
540 Self::Right => Bounds {
541 origin: point(bounds.lower_right().x - length, bounds.lower_left().y),
542 size: size(length, bounds.size.height),
543 },
544 }
545 }
546
547 pub fn axis(&self) -> Axis {
548 match self {
549 Self::Up | Self::Down => Axis::Vertical,
550 Self::Left | Self::Right => Axis::Horizontal,
551 }
552 }
553
554 pub fn increasing(&self) -> bool {
555 match self {
556 Self::Left | Self::Up => false,
557 Self::Down | Self::Right => true,
558 }
559 }
560}
561
562mod element {
563
564 use std::{cell::RefCell, iter, rc::Rc, sync::Arc};
565
566 use gpui::{
567 px, relative, Along, AnyElement, Axis, Bounds, CursorStyle, Element, IntoElement,
568 MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Size, Style,
569 WindowContext,
570 };
571 use parking_lot::Mutex;
572 use smallvec::SmallVec;
573
574 use super::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE};
575
576 pub fn pane_axis(
577 axis: Axis,
578 basis: usize,
579 flexes: Arc<Mutex<Vec<f32>>>,
580 bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
581 ) -> PaneAxisElement {
582 PaneAxisElement {
583 axis,
584 basis,
585 flexes,
586 bounding_boxes,
587 children: SmallVec::new(),
588 active_pane_ix: None,
589 }
590 }
591
592 pub struct PaneAxisElement {
593 axis: Axis,
594 basis: usize,
595 flexes: Arc<Mutex<Vec<f32>>>,
596 bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
597 children: SmallVec<[AnyElement; 2]>,
598 active_pane_ix: Option<usize>,
599 }
600
601 impl PaneAxisElement {
602 pub fn with_active_pane(mut self, active_pane_ix: Option<usize>) -> Self {
603 self.active_pane_ix = active_pane_ix;
604 self
605 }
606
607 fn compute_resize(
608 flexes: &Arc<Mutex<Vec<f32>>>,
609 e: &MouseMoveEvent,
610 ix: usize,
611 axis: Axis,
612 child_start: Point<Pixels>,
613 container_size: Size<Pixels>,
614 cx: &mut WindowContext,
615 ) {
616 let min_size = match axis {
617 Axis::Horizontal => px(HORIZONTAL_MIN_SIZE),
618 Axis::Vertical => px(VERTICAL_MIN_SIZE),
619 };
620 let mut flexes = flexes.lock();
621 debug_assert!(flex_values_in_bounds(flexes.as_slice()));
622
623 let size = move |ix, flexes: &[f32]| {
624 container_size.along(axis) * (flexes[ix] / flexes.len() as f32)
625 };
626
627 // Don't allow resizing to less than the minimum size, if elements are already too small
628 if min_size - px(1.) > size(ix, flexes.as_slice()) {
629 return;
630 }
631
632 let mut proposed_current_pixel_change =
633 (e.position - child_start).along(axis) - size(ix, flexes.as_slice());
634
635 let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
636 let flex_change = pixel_dx / container_size.along(axis);
637 let current_target_flex = flexes[target_ix] + flex_change;
638 let next_target_flex = flexes[(target_ix as isize + next) as usize] - flex_change;
639 (current_target_flex, next_target_flex)
640 };
641
642 let mut successors = iter::from_fn({
643 let forward = proposed_current_pixel_change > px(0.);
644 let mut ix_offset = 0;
645 let len = flexes.len();
646 move || {
647 let result = if forward {
648 (ix + 1 + ix_offset < len).then(|| ix + ix_offset)
649 } else {
650 (ix as isize - ix_offset as isize >= 0).then(|| ix - ix_offset)
651 };
652
653 ix_offset += 1;
654
655 result
656 }
657 });
658
659 while proposed_current_pixel_change.abs() > px(0.) {
660 let Some(current_ix) = successors.next() else {
661 break;
662 };
663
664 let next_target_size = Pixels::max(
665 size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change,
666 min_size,
667 );
668
669 let current_target_size = Pixels::max(
670 size(current_ix, flexes.as_slice()) + size(current_ix + 1, flexes.as_slice())
671 - next_target_size,
672 min_size,
673 );
674
675 let current_pixel_change =
676 current_target_size - size(current_ix, flexes.as_slice());
677
678 let (current_target_flex, next_target_flex) =
679 flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice());
680
681 flexes[current_ix] = current_target_flex;
682 flexes[current_ix + 1] = next_target_flex;
683
684 proposed_current_pixel_change -= current_pixel_change;
685 }
686
687 // todo!(schedule serialize)
688 // workspace.schedule_serialize(cx);
689 cx.notify();
690 }
691
692 fn push_handle(
693 flexes: Arc<Mutex<Vec<f32>>>,
694 dragged_handle: Rc<RefCell<Option<usize>>>,
695 axis: Axis,
696 ix: usize,
697 pane_bounds: Bounds<Pixels>,
698 axis_bounds: Bounds<Pixels>,
699 cx: &mut WindowContext,
700 ) {
701 let handle_bounds = Bounds {
702 origin: pane_bounds.origin.apply_along(axis, |o| {
703 o + pane_bounds.size.along(axis) - Pixels(HANDLE_HITBOX_SIZE / 2.)
704 }),
705 size: pane_bounds
706 .size
707 .apply_along(axis, |_| Pixels(HANDLE_HITBOX_SIZE)),
708 };
709
710 cx.with_z_index(3, |cx| {
711 if handle_bounds.contains(&cx.mouse_position()) {
712 cx.set_cursor_style(match axis {
713 Axis::Vertical => CursorStyle::ResizeUpDown,
714 Axis::Horizontal => CursorStyle::ResizeLeftRight,
715 })
716 }
717
718 cx.add_opaque_layer(handle_bounds);
719
720 cx.on_mouse_event({
721 let dragged_handle = dragged_handle.clone();
722 move |e: &MouseDownEvent, phase, _cx| {
723 if phase.bubble() && handle_bounds.contains(&e.position) {
724 dragged_handle.replace(Some(ix));
725 }
726 }
727 });
728 cx.on_mouse_event(move |e: &MouseMoveEvent, phase, cx| {
729 let dragged_handle = dragged_handle.borrow();
730 if phase.bubble() && *dragged_handle == Some(ix) {
731 Self::compute_resize(
732 &flexes,
733 e,
734 ix,
735 axis,
736 pane_bounds.origin,
737 axis_bounds.size,
738 cx,
739 )
740 }
741 });
742 });
743 }
744 }
745
746 impl IntoElement for PaneAxisElement {
747 type Element = Self;
748
749 fn element_id(&self) -> Option<ui::prelude::ElementId> {
750 Some(self.basis.into())
751 }
752
753 fn into_element(self) -> Self::Element {
754 self
755 }
756 }
757
758 impl Element for PaneAxisElement {
759 type State = Rc<RefCell<Option<usize>>>;
760
761 fn layout(
762 &mut self,
763 state: Option<Self::State>,
764 cx: &mut ui::prelude::WindowContext,
765 ) -> (gpui::LayoutId, Self::State) {
766 let mut style = Style::default();
767 style.size.width = relative(1.).into();
768 style.size.height = relative(1.).into();
769 let layout_id = cx.request_layout(&style, None);
770 let dragged_pane = state.unwrap_or_else(|| Rc::new(RefCell::new(None)));
771 (layout_id, dragged_pane)
772 }
773
774 fn paint(
775 &mut self,
776 bounds: gpui::Bounds<ui::prelude::Pixels>,
777 state: &mut Self::State,
778 cx: &mut ui::prelude::WindowContext,
779 ) {
780 let flexes = self.flexes.lock().clone();
781 let len = self.children.len();
782 debug_assert!(flexes.len() == len);
783 debug_assert!(flex_values_in_bounds(flexes.as_slice()));
784
785 let mut origin = bounds.origin;
786 let space_per_flex = bounds.size.along(self.axis) / len as f32;
787
788 let mut bounding_boxes = self.bounding_boxes.lock();
789 bounding_boxes.clear();
790
791 for (ix, child) in self.children.iter_mut().enumerate() {
792 //todo!(active_pane_magnification)
793 // If usign active pane magnification, need to switch to using
794 // 1 for all non-active panes, and then the magnification for the
795 // active pane.
796 let child_size = bounds
797 .size
798 .apply_along(self.axis, |_| space_per_flex * flexes[ix]);
799
800 let child_bounds = Bounds {
801 origin,
802 size: child_size,
803 };
804 bounding_boxes.push(Some(child_bounds));
805 cx.with_z_index(0, |cx| {
806 child.draw(origin, child_size.into(), cx);
807 });
808 cx.with_z_index(1, |cx| {
809 if ix < len - 1 {
810 Self::push_handle(
811 self.flexes.clone(),
812 state.clone(),
813 self.axis,
814 ix,
815 child_bounds,
816 bounds,
817 cx,
818 );
819 }
820 });
821
822 origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
823 }
824
825 cx.with_z_index(1, |cx| {
826 cx.on_mouse_event({
827 let state = state.clone();
828 move |_: &MouseUpEvent, phase, _cx| {
829 if phase.bubble() {
830 state.replace(None);
831 }
832 }
833 });
834 })
835 }
836 }
837
838 impl ParentElement for PaneAxisElement {
839 fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement; 2]> {
840 &mut self.children
841 }
842 }
843
844 fn flex_values_in_bounds(flexes: &[f32]) -> bool {
845 (flexes.iter().copied().sum::<f32>() - flexes.len() as f32).abs() < 0.001
846 }
847}