1use std::{cell::RefCell, rc::Rc, sync::Arc};
2
3use crate::{
4 pane_group::element::PaneAxisElement, AppState, FollowerStatesByLeader, Pane, Workspace,
5};
6use anyhow::{anyhow, Result};
7use call::{ActiveCall, ParticipantLocation};
8use gpui::{
9 elements::*,
10 geometry::{rect::RectF, vector::Vector2F},
11 platform::{CursorStyle, MouseButton},
12 AnyViewHandle, Axis, Border, ModelHandle, ViewContext, ViewHandle,
13};
14use project::Project;
15use serde::Deserialize;
16use theme::Theme;
17
18const HANDLE_HITBOX_SIZE: f32 = 4.0;
19const HORIZONTAL_MIN_SIZE: f32 = 80.;
20const VERTICAL_MIN_SIZE: f32 = 100.;
21
22#[derive(Clone, Debug, PartialEq)]
23pub struct PaneGroup {
24 pub(crate) root: Member,
25}
26
27impl PaneGroup {
28 pub(crate) fn with_root(root: Member) -> Self {
29 Self { root }
30 }
31
32 pub fn new(pane: ViewHandle<Pane>) -> Self {
33 Self {
34 root: Member::Pane(pane),
35 }
36 }
37
38 pub fn split(
39 &mut self,
40 old_pane: &ViewHandle<Pane>,
41 new_pane: &ViewHandle<Pane>,
42 direction: SplitDirection,
43 ) -> Result<()> {
44 match &mut self.root {
45 Member::Pane(pane) => {
46 if pane == old_pane {
47 self.root = Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
48 Ok(())
49 } else {
50 Err(anyhow!("Pane not found"))
51 }
52 }
53 Member::Axis(axis) => axis.split(old_pane, new_pane, direction),
54 }
55 }
56
57 /// Returns:
58 /// - Ok(true) if it found and removed a pane
59 /// - Ok(false) if it found but did not remove the pane
60 /// - Err(_) if it did not find the pane
61 pub fn remove(&mut self, pane: &ViewHandle<Pane>) -> Result<bool> {
62 match &mut self.root {
63 Member::Pane(_) => Ok(false),
64 Member::Axis(axis) => {
65 if let Some(last_pane) = axis.remove(pane)? {
66 self.root = last_pane;
67 }
68 Ok(true)
69 }
70 }
71 }
72
73 pub(crate) fn render(
74 &self,
75 project: &ModelHandle<Project>,
76 theme: &Theme,
77 follower_states: &FollowerStatesByLeader,
78 active_call: Option<&ModelHandle<ActiveCall>>,
79 active_pane: &ViewHandle<Pane>,
80 zoomed: Option<&AnyViewHandle>,
81 app_state: &Arc<AppState>,
82 cx: &mut ViewContext<Workspace>,
83 ) -> AnyElement<Workspace> {
84 self.root.render(
85 project,
86 0,
87 theme,
88 follower_states,
89 active_call,
90 active_pane,
91 zoomed,
92 app_state,
93 cx,
94 )
95 }
96
97 pub(crate) fn panes(&self) -> Vec<&ViewHandle<Pane>> {
98 let mut panes = Vec::new();
99 self.root.collect_panes(&mut panes);
100 panes
101 }
102}
103
104#[derive(Clone, Debug, PartialEq)]
105pub(crate) enum Member {
106 Axis(PaneAxis),
107 Pane(ViewHandle<Pane>),
108}
109
110impl Member {
111 fn new_axis(
112 old_pane: ViewHandle<Pane>,
113 new_pane: ViewHandle<Pane>,
114 direction: SplitDirection,
115 ) -> Self {
116 use Axis::*;
117 use SplitDirection::*;
118
119 let axis = match direction {
120 Up | Down => Vertical,
121 Left | Right => Horizontal,
122 };
123
124 let members = match direction {
125 Up | Left => vec![Member::Pane(new_pane), Member::Pane(old_pane)],
126 Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)],
127 };
128
129 Member::Axis(PaneAxis::new(axis, members))
130 }
131
132 fn contains(&self, needle: &ViewHandle<Pane>) -> bool {
133 match self {
134 Member::Axis(axis) => axis.members.iter().any(|member| member.contains(needle)),
135 Member::Pane(pane) => pane == needle,
136 }
137 }
138
139 pub fn render(
140 &self,
141 project: &ModelHandle<Project>,
142 basis: usize,
143 theme: &Theme,
144 follower_states: &FollowerStatesByLeader,
145 active_call: Option<&ModelHandle<ActiveCall>>,
146 active_pane: &ViewHandle<Pane>,
147 zoomed: Option<&AnyViewHandle>,
148 app_state: &Arc<AppState>,
149 cx: &mut ViewContext<Workspace>,
150 ) -> AnyElement<Workspace> {
151 enum FollowIntoExternalProject {}
152
153 match self {
154 Member::Pane(pane) => {
155 let pane_element = if Some(&**pane) == zoomed {
156 Empty::new().into_any()
157 } else {
158 ChildView::new(pane, cx).into_any()
159 };
160
161 let leader = follower_states
162 .iter()
163 .find_map(|(leader_id, follower_states)| {
164 if follower_states.contains_key(pane) {
165 Some(leader_id)
166 } else {
167 None
168 }
169 })
170 .and_then(|leader_id| {
171 let room = active_call?.read(cx).room()?.read(cx);
172 let collaborator = project.read(cx).collaborators().get(leader_id)?;
173 let participant = room.remote_participant_for_peer_id(*leader_id)?;
174 Some((collaborator.replica_id, participant))
175 });
176
177 let border = if let Some((replica_id, _)) = leader.as_ref() {
178 let leader_color = theme.editor.replica_selection_style(*replica_id).cursor;
179 let mut border = Border::all(theme.workspace.leader_border_width, leader_color);
180 border
181 .color
182 .fade_out(1. - theme.workspace.leader_border_opacity);
183 border.overlay = true;
184 border
185 } else {
186 Border::default()
187 };
188
189 let leader_status_box = if let Some((_, leader)) = leader {
190 match leader.location {
191 ParticipantLocation::SharedProject {
192 project_id: leader_project_id,
193 } => {
194 if Some(leader_project_id) == project.read(cx).remote_id() {
195 None
196 } else {
197 let leader_user = leader.user.clone();
198 let leader_user_id = leader.user.id;
199 let app_state = Arc::downgrade(app_state);
200 Some(
201 MouseEventHandler::<FollowIntoExternalProject, _>::new(
202 pane.id(),
203 cx,
204 |_, _| {
205 Label::new(
206 format!(
207 "Follow {} on their active project",
208 leader_user.github_login,
209 ),
210 theme
211 .workspace
212 .external_location_message
213 .text
214 .clone(),
215 )
216 .contained()
217 .with_style(
218 theme.workspace.external_location_message.container,
219 )
220 },
221 )
222 .with_cursor_style(CursorStyle::PointingHand)
223 .on_click(MouseButton::Left, move |_, _, cx| {
224 if let Some(app_state) = app_state.upgrade() {
225 crate::join_remote_project(
226 leader_project_id,
227 leader_user_id,
228 app_state,
229 cx,
230 )
231 .detach_and_log_err(cx);
232 }
233 })
234 .aligned()
235 .bottom()
236 .right()
237 .into_any(),
238 )
239 }
240 }
241 ParticipantLocation::UnsharedProject => Some(
242 Label::new(
243 format!(
244 "{} is viewing an unshared Zed project",
245 leader.user.github_login
246 ),
247 theme.workspace.external_location_message.text.clone(),
248 )
249 .contained()
250 .with_style(theme.workspace.external_location_message.container)
251 .aligned()
252 .bottom()
253 .right()
254 .into_any(),
255 ),
256 ParticipantLocation::External => Some(
257 Label::new(
258 format!(
259 "{} is viewing a window outside of Zed",
260 leader.user.github_login
261 ),
262 theme.workspace.external_location_message.text.clone(),
263 )
264 .contained()
265 .with_style(theme.workspace.external_location_message.container)
266 .aligned()
267 .bottom()
268 .right()
269 .into_any(),
270 ),
271 }
272 } else {
273 None
274 };
275
276 Stack::new()
277 .with_child(pane_element.contained().with_border(border))
278 .with_children(leader_status_box)
279 .into_any()
280 }
281 Member::Axis(axis) => axis.render(
282 project,
283 basis + 1,
284 theme,
285 follower_states,
286 active_call,
287 active_pane,
288 zoomed,
289 app_state,
290 cx,
291 ),
292 }
293 }
294
295 fn collect_panes<'a>(&'a self, panes: &mut Vec<&'a ViewHandle<Pane>>) {
296 match self {
297 Member::Axis(axis) => {
298 for member in &axis.members {
299 member.collect_panes(panes);
300 }
301 }
302 Member::Pane(pane) => panes.push(pane),
303 }
304 }
305}
306
307#[derive(Clone, Debug, PartialEq)]
308pub(crate) struct PaneAxis {
309 pub axis: Axis,
310 pub members: Vec<Member>,
311 pub flexes: Rc<RefCell<Vec<f32>>>,
312}
313
314impl PaneAxis {
315 pub fn new(axis: Axis, members: Vec<Member>) -> Self {
316 let flexes = Rc::new(RefCell::new(vec![1.; members.len()]));
317 Self {
318 axis,
319 members,
320 flexes,
321 }
322 }
323
324 pub fn load(axis: Axis, members: Vec<Member>, flexes: Option<Vec<f32>>) -> Self {
325 let flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]);
326 debug_assert!(members.len() == flexes.len());
327
328 let flexes = Rc::new(RefCell::new(flexes));
329 Self {
330 axis,
331 members,
332 flexes,
333 }
334 }
335
336 fn split(
337 &mut self,
338 old_pane: &ViewHandle<Pane>,
339 new_pane: &ViewHandle<Pane>,
340 direction: SplitDirection,
341 ) -> Result<()> {
342 for (mut idx, member) in self.members.iter_mut().enumerate() {
343 match member {
344 Member::Axis(axis) => {
345 if axis.split(old_pane, new_pane, direction).is_ok() {
346 return Ok(());
347 }
348 }
349 Member::Pane(pane) => {
350 if pane == old_pane {
351 if direction.axis() == self.axis {
352 if direction.increasing() {
353 idx += 1;
354 }
355
356 self.members.insert(idx, Member::Pane(new_pane.clone()));
357 *self.flexes.borrow_mut() = vec![1.; self.members.len()];
358 } else {
359 *member =
360 Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
361 }
362 return Ok(());
363 }
364 }
365 }
366 }
367 Err(anyhow!("Pane not found"))
368 }
369
370 fn remove(&mut self, pane_to_remove: &ViewHandle<Pane>) -> Result<Option<Member>> {
371 let mut found_pane = false;
372 let mut remove_member = None;
373 for (idx, member) in self.members.iter_mut().enumerate() {
374 match member {
375 Member::Axis(axis) => {
376 if let Ok(last_pane) = axis.remove(pane_to_remove) {
377 if let Some(last_pane) = last_pane {
378 *member = last_pane;
379 }
380 found_pane = true;
381 break;
382 }
383 }
384 Member::Pane(pane) => {
385 if pane == pane_to_remove {
386 found_pane = true;
387 remove_member = Some(idx);
388 break;
389 }
390 }
391 }
392 }
393
394 if found_pane {
395 if let Some(idx) = remove_member {
396 self.members.remove(idx);
397 *self.flexes.borrow_mut() = vec![1.; self.members.len()];
398 }
399
400 if self.members.len() == 1 {
401 let result = self.members.pop();
402 *self.flexes.borrow_mut() = vec![1.; self.members.len()];
403 Ok(result)
404 } else {
405 Ok(None)
406 }
407 } else {
408 Err(anyhow!("Pane not found"))
409 }
410 }
411
412 fn render(
413 &self,
414 project: &ModelHandle<Project>,
415 basis: usize,
416 theme: &Theme,
417 follower_state: &FollowerStatesByLeader,
418 active_call: Option<&ModelHandle<ActiveCall>>,
419 active_pane: &ViewHandle<Pane>,
420 zoomed: Option<&AnyViewHandle>,
421 app_state: &Arc<AppState>,
422 cx: &mut ViewContext<Workspace>,
423 ) -> AnyElement<Workspace> {
424 debug_assert!(self.members.len() == self.flexes.borrow().len());
425
426 let mut pane_axis = PaneAxisElement::new(self.axis, basis, self.flexes.clone());
427 let mut active_pane_ix = None;
428
429 let mut members = self.members.iter().enumerate().peekable();
430 while let Some((ix, member)) = members.next() {
431 let last = members.peek().is_none();
432
433 if member.contains(active_pane) {
434 active_pane_ix = Some(ix);
435 }
436
437 let mut member = member.render(
438 project,
439 (basis + ix) * 10,
440 theme,
441 follower_state,
442 active_call,
443 active_pane,
444 zoomed,
445 app_state,
446 cx,
447 );
448
449 if !last {
450 let mut border = theme.workspace.pane_divider;
451 border.left = false;
452 border.right = false;
453 border.top = false;
454 border.bottom = false;
455
456 match self.axis {
457 Axis::Vertical => border.bottom = true,
458 Axis::Horizontal => border.right = true,
459 }
460
461 member = member.contained().with_border(border).into_any();
462 }
463
464 pane_axis = pane_axis.with_child(member.into_any());
465 }
466 pane_axis.set_active_pane(active_pane_ix);
467 pane_axis.into_any()
468 }
469}
470
471#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
472pub enum SplitDirection {
473 Up,
474 Down,
475 Left,
476 Right,
477}
478
479impl SplitDirection {
480 pub fn all() -> [Self; 4] {
481 [Self::Up, Self::Down, Self::Left, Self::Right]
482 }
483
484 pub fn edge(&self, rect: RectF) -> f32 {
485 match self {
486 Self::Up => rect.min_y(),
487 Self::Down => rect.max_y(),
488 Self::Left => rect.min_x(),
489 Self::Right => rect.max_x(),
490 }
491 }
492
493 // Returns a new rectangle which shares an edge in SplitDirection and has `size` along SplitDirection
494 pub fn along_edge(&self, rect: RectF, size: f32) -> RectF {
495 match self {
496 Self::Up => RectF::new(rect.origin(), Vector2F::new(rect.width(), size)),
497 Self::Down => RectF::new(
498 rect.lower_left() - Vector2F::new(0., size),
499 Vector2F::new(rect.width(), size),
500 ),
501 Self::Left => RectF::new(rect.origin(), Vector2F::new(size, rect.height())),
502 Self::Right => RectF::new(
503 rect.upper_right() - Vector2F::new(size, 0.),
504 Vector2F::new(size, rect.height()),
505 ),
506 }
507 }
508
509 pub fn axis(&self) -> Axis {
510 match self {
511 Self::Up | Self::Down => Axis::Vertical,
512 Self::Left | Self::Right => Axis::Horizontal,
513 }
514 }
515
516 pub fn increasing(&self) -> bool {
517 match self {
518 Self::Left | Self::Up => false,
519 Self::Down | Self::Right => true,
520 }
521 }
522}
523
524mod element {
525 use std::{cell::RefCell, ops::Range, rc::Rc};
526
527 use gpui::{
528 geometry::{
529 rect::RectF,
530 vector::{vec2f, Vector2F},
531 },
532 json::{self, ToJson},
533 platform::{CursorStyle, MouseButton},
534 AnyElement, Axis, CursorRegion, Element, LayoutContext, MouseRegion, RectFExt,
535 SceneBuilder, SizeConstraint, Vector2FExt, ViewContext,
536 };
537
538 use crate::{
539 pane_group::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE},
540 Workspace, WorkspaceSettings,
541 };
542
543 pub struct PaneAxisElement {
544 axis: Axis,
545 basis: usize,
546 active_pane_ix: Option<usize>,
547 flexes: Rc<RefCell<Vec<f32>>>,
548 children: Vec<AnyElement<Workspace>>,
549 }
550
551 impl PaneAxisElement {
552 pub fn new(axis: Axis, basis: usize, flexes: Rc<RefCell<Vec<f32>>>) -> Self {
553 Self {
554 axis,
555 basis,
556 flexes,
557 active_pane_ix: None,
558 children: Default::default(),
559 }
560 }
561
562 pub fn set_active_pane(&mut self, active_pane_ix: Option<usize>) {
563 self.active_pane_ix = active_pane_ix;
564 }
565
566 fn layout_children(
567 &mut self,
568 active_pane_magnification: f32,
569 constraint: SizeConstraint,
570 remaining_space: &mut f32,
571 remaining_flex: &mut f32,
572 cross_axis_max: &mut f32,
573 view: &mut Workspace,
574 cx: &mut LayoutContext<Workspace>,
575 ) {
576 let flexes = self.flexes.borrow();
577 let cross_axis = self.axis.invert();
578 for (ix, child) in self.children.iter_mut().enumerate() {
579 let flex = if active_pane_magnification != 1. {
580 if let Some(active_pane_ix) = self.active_pane_ix {
581 if ix == active_pane_ix {
582 active_pane_magnification
583 } else {
584 1.
585 }
586 } else {
587 1.
588 }
589 } else {
590 flexes[ix]
591 };
592
593 let child_size = if *remaining_flex == 0.0 {
594 *remaining_space
595 } else {
596 let space_per_flex = *remaining_space / *remaining_flex;
597 space_per_flex * flex
598 };
599
600 let child_constraint = match self.axis {
601 Axis::Horizontal => SizeConstraint::new(
602 vec2f(child_size, constraint.min.y()),
603 vec2f(child_size, constraint.max.y()),
604 ),
605 Axis::Vertical => SizeConstraint::new(
606 vec2f(constraint.min.x(), child_size),
607 vec2f(constraint.max.x(), child_size),
608 ),
609 };
610 let child_size = child.layout(child_constraint, view, cx);
611 *remaining_space -= child_size.along(self.axis);
612 *remaining_flex -= flex;
613 *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
614 }
615 }
616 }
617
618 impl Extend<AnyElement<Workspace>> for PaneAxisElement {
619 fn extend<T: IntoIterator<Item = AnyElement<Workspace>>>(&mut self, children: T) {
620 self.children.extend(children);
621 }
622 }
623
624 impl Element<Workspace> for PaneAxisElement {
625 type LayoutState = f32;
626 type PaintState = ();
627
628 fn layout(
629 &mut self,
630 constraint: SizeConstraint,
631 view: &mut Workspace,
632 cx: &mut LayoutContext<Workspace>,
633 ) -> (Vector2F, Self::LayoutState) {
634 debug_assert!(self.children.len() == self.flexes.borrow().len());
635
636 let active_pane_magnification =
637 settings::get::<WorkspaceSettings>(cx).active_pane_magnification;
638
639 let mut remaining_flex = 0.;
640
641 if active_pane_magnification != 1. {
642 let active_pane_flex = self
643 .active_pane_ix
644 .map(|_| active_pane_magnification)
645 .unwrap_or(1.);
646 remaining_flex += self.children.len() as f32 - 1. + active_pane_flex;
647 } else {
648 for flex in self.flexes.borrow().iter() {
649 remaining_flex += flex;
650 }
651 }
652
653 let mut cross_axis_max: f32 = 0.0;
654 let mut remaining_space = constraint.max_along(self.axis);
655
656 if remaining_space.is_infinite() {
657 panic!("flex contains flexible children but has an infinite constraint along the flex axis");
658 }
659
660 self.layout_children(
661 active_pane_magnification,
662 constraint,
663 &mut remaining_space,
664 &mut remaining_flex,
665 &mut cross_axis_max,
666 view,
667 cx,
668 );
669
670 let mut size = match self.axis {
671 Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
672 Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
673 };
674
675 if constraint.min.x().is_finite() {
676 size.set_x(size.x().max(constraint.min.x()));
677 }
678 if constraint.min.y().is_finite() {
679 size.set_y(size.y().max(constraint.min.y()));
680 }
681
682 if size.x() > constraint.max.x() {
683 size.set_x(constraint.max.x());
684 }
685 if size.y() > constraint.max.y() {
686 size.set_y(constraint.max.y());
687 }
688
689 (size, remaining_space)
690 }
691
692 fn paint(
693 &mut self,
694 scene: &mut SceneBuilder,
695 bounds: RectF,
696 visible_bounds: RectF,
697 remaining_space: &mut Self::LayoutState,
698 view: &mut Workspace,
699 cx: &mut ViewContext<Workspace>,
700 ) -> Self::PaintState {
701 let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.;
702 let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
703
704 let overflowing = *remaining_space < 0.;
705 if overflowing {
706 scene.push_layer(Some(visible_bounds));
707 }
708
709 let mut child_origin = bounds.origin();
710
711 let mut children_iter = self.children.iter_mut().enumerate().peekable();
712 while let Some((ix, child)) = children_iter.next() {
713 let child_start = child_origin.clone();
714 child.paint(scene, child_origin, visible_bounds, view, cx);
715
716 match self.axis {
717 Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
718 Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
719 }
720
721 if let Some(Some((next_ix, next_child))) = can_resize.then(|| children_iter.peek())
722 {
723 scene.push_stacking_context(None, None);
724
725 let handle_origin = match self.axis {
726 Axis::Horizontal => child_origin - vec2f(HANDLE_HITBOX_SIZE / 2., 0.0),
727 Axis::Vertical => child_origin - vec2f(0.0, HANDLE_HITBOX_SIZE / 2.),
728 };
729
730 let handle_bounds = match self.axis {
731 Axis::Horizontal => RectF::new(
732 handle_origin,
733 vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()),
734 ),
735 Axis::Vertical => RectF::new(
736 handle_origin,
737 vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE),
738 ),
739 };
740
741 let style = match self.axis {
742 Axis::Horizontal => CursorStyle::ResizeLeftRight,
743 Axis::Vertical => CursorStyle::ResizeUpDown,
744 };
745
746 scene.push_cursor_region(CursorRegion {
747 bounds: handle_bounds,
748 style,
749 });
750
751 let axis = self.axis;
752 let child_size = child.size();
753 let next_child_size = next_child.size();
754 let drag_bounds = visible_bounds.clone();
755 let flexes = self.flexes.clone();
756 let current_flex = flexes.borrow()[ix];
757 let next_ix = *next_ix;
758 let next_flex = flexes.borrow()[next_ix];
759 enum ResizeHandle {}
760 let mut mouse_region = MouseRegion::new::<ResizeHandle>(
761 cx.view_id(),
762 self.basis + ix,
763 handle_bounds,
764 );
765 mouse_region = mouse_region.on_drag(
766 MouseButton::Left,
767 move |drag, workspace: &mut Workspace, cx| {
768 let min_size = match axis {
769 Axis::Horizontal => HORIZONTAL_MIN_SIZE,
770 Axis::Vertical => VERTICAL_MIN_SIZE,
771 };
772 // Don't allow resizing to less than the minimum size, if elements are already too small
773 if min_size - 1. > child_size.along(axis)
774 || min_size - 1. > next_child_size.along(axis)
775 {
776 return;
777 }
778
779 let mut current_target_size = (drag.position - child_start).along(axis);
780
781 let proposed_current_pixel_change =
782 current_target_size - child_size.along(axis);
783
784 if proposed_current_pixel_change < 0. {
785 current_target_size = f32::max(current_target_size, min_size);
786 } else if proposed_current_pixel_change > 0. {
787 // TODO: cascade this change to other children if current item is at min size
788 let next_target_size = f32::max(
789 next_child_size.along(axis) - proposed_current_pixel_change,
790 min_size,
791 );
792 current_target_size = f32::min(
793 current_target_size,
794 child_size.along(axis) + next_child_size.along(axis)
795 - next_target_size,
796 );
797 }
798
799 let current_pixel_change = current_target_size - child_size.along(axis);
800 let flex_change = current_pixel_change / drag_bounds.length_along(axis);
801 let current_target_flex = current_flex + flex_change;
802 let next_target_flex = next_flex - flex_change;
803
804 let mut borrow = flexes.borrow_mut();
805 *borrow.get_mut(ix).unwrap() = current_target_flex;
806 *borrow.get_mut(next_ix).unwrap() = next_target_flex;
807
808 workspace.schedule_serialize(cx);
809 cx.notify();
810 },
811 );
812 scene.push_mouse_region(mouse_region);
813
814 scene.pop_stacking_context();
815 }
816 }
817
818 if overflowing {
819 scene.pop_layer();
820 }
821 }
822
823 fn rect_for_text_range(
824 &self,
825 range_utf16: Range<usize>,
826 _: RectF,
827 _: RectF,
828 _: &Self::LayoutState,
829 _: &Self::PaintState,
830 view: &Workspace,
831 cx: &ViewContext<Workspace>,
832 ) -> Option<RectF> {
833 self.children
834 .iter()
835 .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
836 }
837
838 fn debug(
839 &self,
840 bounds: RectF,
841 _: &Self::LayoutState,
842 _: &Self::PaintState,
843 view: &Workspace,
844 cx: &ViewContext<Workspace>,
845 ) -> json::Value {
846 serde_json::json!({
847 "type": "PaneAxis",
848 "bounds": bounds.to_json(),
849 "axis": self.axis.to_json(),
850 "flexes": *self.flexes.borrow(),
851 "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
852 })
853 }
854 }
855}