1use crate::_private::NonExhaustive;
38use crate::splitter::split_impl::{get_fill_char, get_join_0, get_join_1, get_mark_0, get_mark_1};
39use crate::splitter::split_layout::layout_split;
40use crate::util::{fill_buf_area, revert_style};
41use rat_event::util::MouseFlagsN;
42use rat_event::{HandleEvent, MouseOnly, Outcome, Regular, ct_event, event_flow};
43use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
44use rat_reloc::{RelocatableState, relocate_area, relocate_areas, relocate_positions};
45use ratatui::buffer::Buffer;
46use ratatui::layout::{Constraint, Direction, Position, Rect};
47use ratatui::style::Style;
48use ratatui::widgets::{Block, BorderType, StatefulWidget, Widget};
49use std::cmp::{max, min};
50use std::iter;
51use std::rc::Rc;
52use unicode_segmentation::UnicodeSegmentation;
53
54mod split_impl;
55mod split_layout;
56
57#[derive(Debug, Default, Clone)]
58pub struct Split<'a> {
80 direction: Direction,
82 constraints: Vec<Constraint>,
85 resize_constraints: Vec<ResizeConstraint>,
87 resize: SplitResize,
89
90 split_type: SplitType,
92 join_0: Option<BorderType>,
94 join_1: Option<BorderType>,
96 mark_offset: u16,
98 mark_0_char: Option<&'a str>,
100 mark_1_char: Option<&'a str>,
102
103 style: Style,
105 block: Option<Block<'a>>,
106 arrow_style: Option<Style>,
107 drag_style: Option<Style>,
108}
109
110#[derive(Debug, Clone)]
112pub struct LayoutWidget<'a> {
113 split: Rc<Split<'a>>,
114}
115
116#[derive(Debug, Clone)]
118pub struct SplitWidget<'a> {
119 split: Rc<Split<'a>>,
120}
121
122#[derive(Debug, Clone)]
126pub struct SplitStyle {
127 pub style: Style,
129 pub block: Option<Block<'static>>,
131 pub border_style: Option<Style>,
132 pub title_style: Option<Style>,
133 pub arrow_style: Option<Style>,
135 pub drag_style: Option<Style>,
137
138 pub horizontal_mark: Option<&'static str>,
141 pub vertical_mark: Option<&'static str>,
144
145 pub non_exhaustive: NonExhaustive,
146}
147
148#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
150pub enum SplitType {
151 #[default]
154 FullEmpty,
155 FullPlain,
158 FullDouble,
161 FullThick,
164 FullQuadrantInside,
168 FullQuadrantOutside,
172 Scroll,
183 Widget,
190}
191
192#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
194pub enum SplitResize {
195 Neighbours,
198 #[default]
202 Full,
203}
204
205#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
207pub enum ResizeConstraint {
208 Fixed,
210 #[default]
213 ScaleProportional,
214 ScaleEqual,
220}
221
222const SPLIT_WIDTH: u16 = 1;
223
224#[derive(Debug)]
226pub struct SplitState {
227 pub area: Rect,
230 pub inner: Rect,
233 pub widget_areas: Vec<Rect>,
237 pub splitline_areas: Vec<Rect>,
241 pub splitline_mark_position: Vec<Position>,
244 pub mark_offset: u16,
247
248 pub direction: Direction,
251 pub split_type: SplitType,
253 pub resize: SplitResize,
255
256 area_length: Vec<u16>,
261 area_constraint: Vec<Constraint>,
262 hidden_length: Vec<u16>,
264
265 pub focus: FocusFlag,
268 pub focus_marker: Option<usize>,
272
273 pub mouse: MouseFlagsN,
276
277 relocate_split: bool,
280
281 pub non_exhaustive: NonExhaustive,
282}
283
284impl SplitType {
285 pub fn is_full(&self) -> bool {
286 use SplitType::*;
287 match self {
288 FullEmpty => true,
289 FullPlain => true,
290 FullDouble => true,
291 FullThick => true,
292 FullQuadrantInside => true,
293 FullQuadrantOutside => true,
294 Scroll => false,
295 Widget => false,
296 }
297 }
298}
299
300impl Default for SplitStyle {
301 fn default() -> Self {
302 Self {
303 style: Default::default(),
304 block: Default::default(),
305 border_style: Default::default(),
306 title_style: Default::default(),
307 arrow_style: Default::default(),
308 drag_style: Default::default(),
309 horizontal_mark: Default::default(),
310 vertical_mark: Default::default(),
311 non_exhaustive: NonExhaustive,
312 }
313 }
314}
315
316impl<'a> Split<'a> {
317 pub fn new() -> Self {
322 Self {
323 direction: Direction::Horizontal,
324 ..Default::default()
325 }
326 }
327
328 pub fn horizontal() -> Self {
330 Self {
331 direction: Direction::Horizontal,
332 ..Default::default()
333 }
334 }
335
336 pub fn vertical() -> Self {
338 Self {
339 direction: Direction::Horizontal,
340 ..Default::default()
341 }
342 }
343
344 pub fn constraints(mut self, constraints: impl IntoIterator<Item = Constraint>) -> Self {
350 self.constraints = constraints.into_iter().collect();
351 self.resize_constraints = iter::from_fn(|| Some(ResizeConstraint::ScaleProportional))
352 .take(self.constraints.len())
353 .collect();
354 self
355 }
356
357 pub fn resize_constraint(mut self, n: usize, constraint: ResizeConstraint) -> Self {
359 self.resize_constraints[n] = constraint;
360 self
361 }
362
363 pub fn direction(mut self, direction: Direction) -> Self {
367 self.direction = direction;
368 self
369 }
370
371 pub fn split_type(mut self, split_type: SplitType) -> Self {
373 self.split_type = split_type;
374 self
375 }
376
377 pub fn resize(mut self, resize: SplitResize) -> Self {
379 self.resize = resize;
380 self
381 }
382
383 pub fn join(mut self, border: BorderType) -> Self {
387 self.join_0 = Some(border);
388 self.join_1 = Some(border);
389 self
390 }
391
392 pub fn join_0(mut self, border: BorderType) -> Self {
396 self.join_0 = Some(border);
397 self
398 }
399
400 pub fn join_1(mut self, border: BorderType) -> Self {
404 self.join_1 = Some(border);
405 self
406 }
407
408 pub fn block(mut self, block: Block<'a>) -> Self {
410 self.block = Some(block);
411 self
412 }
413
414 pub fn styles(mut self, styles: SplitStyle) -> Self {
416 self.style = styles.style;
417 if styles.block.is_some() {
418 self.block = styles.block;
419 }
420 if let Some(border_style) = styles.border_style {
421 self.block = self.block.map(|v| v.border_style(border_style));
422 }
423 if let Some(title_style) = styles.title_style {
424 self.block = self.block.map(|v| v.title_style(title_style));
425 }
426 self.block = self.block.map(|v| v.style(self.style));
427
428 if styles.drag_style.is_some() {
429 self.drag_style = styles.drag_style;
430 }
431 if styles.arrow_style.is_some() {
432 self.arrow_style = styles.arrow_style;
433 }
434 match self.direction {
435 Direction::Horizontal => {
436 if let Some(mark) = styles.horizontal_mark {
437 let mut g = mark.graphemes(true);
438 if let Some(g0) = g.next() {
439 self.mark_0_char = Some(g0);
440 }
441 if let Some(g1) = g.next() {
442 self.mark_1_char = Some(g1);
443 }
444 }
445 }
446 Direction::Vertical => {
447 if let Some(mark) = styles.vertical_mark {
448 let mut g = mark.graphemes(true);
449 if let Some(g0) = g.next() {
450 self.mark_0_char = Some(g0);
451 }
452 if let Some(g1) = g.next() {
453 self.mark_1_char = Some(g1);
454 }
455 }
456 }
457 }
458 self
459 }
460
461 pub fn style(mut self, style: Style) -> Self {
463 self.style = style;
464 self.block = self.block.map(|v| v.style(style));
465 self
466 }
467
468 pub fn arrow_style(mut self, style: Style) -> Self {
470 self.arrow_style = Some(style);
471 self
472 }
473
474 pub fn drag_style(mut self, style: Style) -> Self {
476 self.drag_style = Some(style);
477 self
478 }
479
480 pub fn mark_offset(mut self, offset: u16) -> Self {
482 self.mark_offset = offset;
483 self
484 }
485
486 pub fn mark_0(mut self, mark: &'a str) -> Self {
488 self.mark_0_char = Some(mark);
489 self
490 }
491
492 pub fn mark_1(mut self, mark: &'a str) -> Self {
494 self.mark_1_char = Some(mark);
495 self
496 }
497
498 #[deprecated(since = "2.4.0", note = "use into_widgets() instead")]
506 pub fn into_widget(self, area: Rect, state: &mut SplitState) -> SplitWidget<'a> {
507 layout_split(&self, area, state);
508 SplitWidget {
509 split: Rc::new(self),
510 }
511 }
512
513 #[deprecated(since = "2.4.0", note = "use into_widgets() instead")]
522 pub fn into_widget_layout(
523 self,
524 area: Rect,
525 state: &mut SplitState,
526 ) -> (SplitWidget<'a>, Vec<Rect>) {
527 layout_split(&self, area, state);
528 (
529 SplitWidget {
530 split: Rc::new(self),
531 },
532 state.widget_areas.clone(),
533 )
534 }
535
536 pub fn into_widgets(self) -> (LayoutWidget<'a>, SplitWidget<'a>) {
548 let split = Rc::new(self);
549 (
550 LayoutWidget {
551 split: split.clone(), },
553 SplitWidget {
554 split, },
556 )
557 }
558}
559
560impl<'a> StatefulWidget for &Split<'a> {
561 type State = SplitState;
562
563 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
564 layout_split(self, area, state);
565 render_split(self, buf, state);
566 state.relocate_split = false;
567 }
568}
569
570impl<'a> StatefulWidget for Split<'a> {
571 type State = SplitState;
572
573 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
574 layout_split(&self, area, state);
575 render_split(&self, buf, state);
576 state.relocate_split = false;
577 }
578}
579
580impl<'a> StatefulWidget for &LayoutWidget<'a> {
581 type State = SplitState;
582
583 fn render(self, area: Rect, _buf: &mut Buffer, state: &mut Self::State) {
584 layout_split(&self.split, area, state);
586 }
587}
588
589impl<'a> StatefulWidget for LayoutWidget<'a> {
590 type State = SplitState;
591
592 fn render(self, area: Rect, _buf: &mut Buffer, state: &mut Self::State) {
593 layout_split(&self.split, area, state);
595 }
596}
597
598impl<'a> StatefulWidget for &SplitWidget<'a> {
599 type State = SplitState;
600
601 fn render(self, _area: Rect, buf: &mut Buffer, state: &mut Self::State) {
602 render_split(&self.split, buf, state);
603 }
604}
605
606impl StatefulWidget for SplitWidget<'_> {
607 type State = SplitState;
608
609 fn render(self, _area: Rect, buf: &mut Buffer, state: &mut Self::State) {
610 render_split(&self.split, buf, state);
611 }
612}
613
614fn render_split(split: &Split<'_>, buf: &mut Buffer, state: &mut SplitState) {
615 let area = state.area;
616 if state.is_focused() {
617 if state.focus_marker.is_none() {
618 state.focus_marker = Some(0);
619 }
620 } else {
621 state.focus_marker = None;
622 }
623
624 split.block.render(area, buf);
625
626 for (n, split_area) in state.splitline_areas.iter().enumerate() {
627 if split.direction == Direction::Horizontal {
629 if split_area.width == 0 {
630 continue;
631 }
632 } else {
633 if split_area.height == 0 {
634 continue;
635 }
636 }
637
638 let (style, arrow_style) = if Some(n) == state.mouse.drag.get()
639 || Some(n) == state.focus_marker
640 || Some(n) == state.mouse.hover.get()
641 {
642 if let Some(drag) = split.drag_style {
643 (drag, drag)
644 } else {
645 (revert_style(split.style), revert_style(split.style))
646 }
647 } else {
648 if let Some(arrow) = split.arrow_style {
649 (split.style, arrow)
650 } else {
651 (split.style, split.style)
652 }
653 };
654
655 if let Some(fill) = get_fill_char(split) {
656 fill_buf_area(buf, *split_area, fill, style);
657 }
658
659 let mark = state.splitline_mark_position[n];
660 if split.direction == Direction::Horizontal {
661 if buf.area.contains((mark.x, mark.y).into()) {
662 if let Some(cell) = buf.cell_mut((mark.x, mark.y)) {
663 cell.set_style(arrow_style);
664 cell.set_symbol(get_mark_0(split));
665 }
666 }
667 if buf.area.contains((mark.x, mark.y + 1).into()) {
668 if let Some(cell) = buf.cell_mut((mark.x, mark.y + 1)) {
669 cell.set_style(arrow_style);
670 cell.set_symbol(get_mark_1(split));
671 }
672 }
673 } else {
674 if let Some(cell) = buf.cell_mut((mark.x, mark.y)) {
675 cell.set_style(arrow_style);
676 cell.set_symbol(get_mark_0(split));
677 }
678 if let Some(cell) = buf.cell_mut((mark.x + 1, mark.y)) {
679 cell.set_style(arrow_style);
680 cell.set_symbol(get_mark_1(split));
681 }
682 }
683
684 if let Some((pos_0, c_0)) = get_join_0(split, *split_area, state) {
685 if let Some(cell) = buf.cell_mut((pos_0.x, pos_0.y)) {
686 cell.set_symbol(c_0);
687 }
688 }
689 if let Some((pos_1, c_1)) = get_join_1(split, *split_area, state) {
690 if let Some(cell) = buf.cell_mut((pos_1.x, pos_1.y)) {
691 cell.set_symbol(c_1);
692 }
693 }
694 }
695}
696
697impl Default for SplitState {
698 fn default() -> Self {
699 Self {
700 area: Default::default(),
701 inner: Default::default(),
702 widget_areas: Default::default(),
703 splitline_areas: Default::default(),
704 splitline_mark_position: Default::default(),
705 mark_offset: Default::default(),
706 direction: Default::default(),
707 split_type: Default::default(),
708 resize: Default::default(),
709 area_length: Default::default(),
710 area_constraint: Default::default(),
711 hidden_length: Default::default(),
712 focus: Default::default(),
713 focus_marker: Default::default(),
714 mouse: Default::default(),
715 relocate_split: Default::default(),
716 non_exhaustive: NonExhaustive,
717 }
718 }
719}
720
721impl Clone for SplitState {
722 fn clone(&self) -> Self {
723 Self {
724 area: self.area,
725 inner: self.inner,
726 widget_areas: self.widget_areas.clone(),
727 splitline_areas: self.splitline_areas.clone(),
728 splitline_mark_position: self.splitline_mark_position.clone(),
729 mark_offset: self.mark_offset,
730 direction: self.direction,
731 split_type: self.split_type,
732 resize: self.resize,
733 area_length: self.area_length.clone(),
734 area_constraint: self.area_constraint.clone(),
735 hidden_length: self.hidden_length.clone(),
736 focus: self.focus.new_instance(),
737 focus_marker: self.focus_marker,
738 mouse: Default::default(),
739 relocate_split: self.relocate_split,
740 non_exhaustive: NonExhaustive,
741 }
742 }
743}
744
745impl HasFocus for SplitState {
746 fn build(&self, builder: &mut FocusBuilder) {
747 builder.leaf_widget(self);
748 }
749
750 fn focus(&self) -> FocusFlag {
751 self.focus.clone()
752 }
753
754 fn area(&self) -> Rect {
755 Rect::default()
757 }
758
759 fn navigable(&self) -> Navigation {
760 Navigation::Leave
761 }
762}
763
764impl RelocatableState for SplitState {
765 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
766 if !self.relocate_split {
768 self.area = relocate_area(self.area, shift, clip);
769 self.inner = relocate_area(self.inner, shift, clip);
770 relocate_areas(self.widget_areas.as_mut_slice(), shift, clip);
771 relocate_areas(self.splitline_areas.as_mut_slice(), shift, clip);
772 relocate_positions(self.splitline_mark_position.as_mut_slice(), shift, clip);
773 }
774 }
775
776 fn relocate_popup(&mut self, shift: (i16, i16), clip: Rect) {
777 if self.relocate_split {
778 self.relocate_split = false;
779 self.area = relocate_area(self.area, shift, clip);
780 self.inner = relocate_area(self.inner, shift, clip);
781 relocate_areas(self.widget_areas.as_mut_slice(), shift, clip);
782 relocate_areas(self.splitline_areas.as_mut_slice(), shift, clip);
783 relocate_positions(self.splitline_mark_position.as_mut_slice(), shift, clip);
784 }
785 }
786}
787
788#[allow(clippy::len_without_is_empty)]
789impl SplitState {
790 pub fn new() -> Self {
792 Self::default()
793 }
794
795 pub fn named(name: &str) -> Self {
797 let mut z = Self::default();
798 z.focus = z.focus.with_name(name);
799 z
800 }
801
802 pub fn set_screen_split_pos(&mut self, n: usize, pos: (u16, u16)) -> bool {
809 if self.is_hidden(n) {
810 return false;
811 }
812 if self.direction == Direction::Horizontal {
813 let pos = if pos.0 < self.inner.left() {
814 0
815 } else if pos.0 < self.inner.right() {
816 pos.0 - self.inner.x
817 } else {
818 self.inner.width
819 };
820
821 let split_pos = self.split_pos(n);
822 self.set_split_pos(n, pos);
823
824 split_pos != self.split_pos(n)
825 } else {
826 let pos = if pos.1 < self.inner.top() {
827 0
828 } else if pos.1 < self.inner.bottom() {
829 pos.1 - self.inner.y
830 } else {
831 self.inner.height
832 };
833
834 let split_pos = self.split_pos(n);
835 self.set_split_pos(n, pos);
836
837 split_pos != self.split_pos(n)
838 }
839 }
840
841 pub fn move_split_left(&mut self, n: usize, delta: u16) -> bool {
845 let split_pos = self.split_pos(n);
846 self.set_split_pos(n, split_pos - delta);
847
848 split_pos != self.split_pos(n)
849 }
850
851 pub fn move_split_right(&mut self, n: usize, delta: u16) -> bool {
854 let split_pos = self.split_pos(n);
855 self.set_split_pos(n, split_pos + delta);
856
857 split_pos != self.split_pos(n)
858 }
859
860 pub fn move_split_up(&mut self, n: usize, delta: u16) -> bool {
863 self.move_split_left(n, delta)
864 }
865
866 pub fn move_split_down(&mut self, n: usize, delta: u16) -> bool {
869 self.move_split_right(n, delta)
870 }
871
872 pub fn select_next_split(&mut self) -> bool {
874 if self.is_focused() {
875 let n = self.focus_marker.unwrap_or_default();
876 if n + 1 >= self.area_length.len().saturating_sub(1) {
877 self.focus_marker = Some(0);
878 } else {
879 self.focus_marker = Some(n + 1);
880 }
881 true
882 } else {
883 false
884 }
885 }
886
887 pub fn select_prev_split(&mut self) -> bool {
889 if self.is_focused() {
890 let n = self.focus_marker.unwrap_or_default();
891 if n == 0 {
892 self.focus_marker =
893 Some(self.area_length.len().saturating_sub(1).saturating_sub(1));
894 } else {
895 self.focus_marker = Some(n - 1);
896 }
897 true
898 } else {
899 false
900 }
901 }
902
903 pub fn len(&self) -> usize {
905 self.area_length.len()
906 }
907
908 pub fn area_lengths(&self) -> &[u16] {
910 &self.area_length
911 }
912
913 pub fn set_area_lengths(&mut self, lengths: Vec<u16>) {
926 self.area_length = lengths;
927 self.area_constraint.clear();
928 while self.hidden_length.len() < self.area_length.len() {
929 self.hidden_length.push(0);
930 }
931 while self.hidden_length.len() > self.area_length.len() {
932 self.hidden_length.pop();
933 }
934 }
935
936 pub fn hidden_lengths(&self) -> &[u16] {
938 &self.hidden_length
939 }
940
941 pub fn set_hidden_lengths(&mut self, hidden: Vec<u16>) {
946 for i in 0..self.hidden_length.len() {
947 if let Some(v) = hidden.get(i) {
948 self.hidden_length[i] = *v;
949 } else {
950 self.hidden_length[i] = 0;
951 }
952 }
953 }
954
955 pub fn area_len(&self, n: usize) -> u16 {
965 if n >= self.area_length.len() {
966 return 0;
967 }
968 self.area_length[n]
969 }
970
971 pub fn total_area_len(&self) -> u16 {
973 self.area_length.iter().sum()
974 }
975
976 pub fn set_area_len(&mut self, n: usize, len: u16) {
1009 if n >= self.area_length.len() {
1010 return;
1011 }
1012 self.area_length[n] = len;
1013 self.area_constraint.clear();
1014 self.hidden_length[n] = 0;
1015 }
1016
1017 pub fn split_pos(&self, n: usize) -> u16 {
1029 if n + 1 >= self.area_length.len() {
1030 return self.area_length.iter().sum();
1031 }
1032 self.area_length[..n + 1].iter().sum()
1033 }
1034
1035 pub fn set_split_pos(&mut self, n: usize, pos: u16) {
1051 if n + 1 >= self.area_length.len() {
1052 return;
1053 }
1054
1055 match self.resize {
1056 SplitResize::Neighbours => {
1057 self.set_split_pos_neighbour(n, pos);
1058 }
1059 SplitResize::Full => {
1060 self.set_split_pos_full(n, pos);
1061 }
1062 }
1063 }
1064
1065 fn set_split_pos_neighbour(&mut self, n: usize, pos: u16) {
1068 assert!(n + 1 < self.area_length.len());
1069
1070 let mut pos_vec = Vec::new();
1072 let mut pp = 0;
1073 for len in &self.area_length {
1074 pp += *len;
1075 pos_vec.push(pp);
1076 }
1077 let pos_count = pos_vec.len();
1079
1080 let (min_pos, max_pos) = if n == 0 {
1081 if n + 2 >= pos_count {
1082 (SPLIT_WIDTH, pos_vec[n + 1])
1083 } else {
1084 (SPLIT_WIDTH, pos_vec[n + 1] - SPLIT_WIDTH)
1085 }
1086 } else if n + 2 < pos_count {
1087 (pos_vec[n - 1] + 1, pos_vec[n + 1] - SPLIT_WIDTH)
1088 } else {
1089 (pos_vec[n - 1] + 1, pos_vec[n + 1])
1090 };
1091
1092 pos_vec[n] = min(max(min_pos, pos), max_pos);
1093
1094 for i in 0..pos_vec.len() {
1096 if i > 0 {
1097 self.area_length[i] = pos_vec[i] - pos_vec[i - 1];
1098 } else {
1099 self.area_length[i] = pos_vec[i];
1100 }
1101 }
1102 self.area_constraint.clear();
1103 }
1104
1105 #[allow(clippy::needless_range_loop)]
1108 #[allow(clippy::comparison_chain)]
1109 fn set_split_pos_full(&mut self, n: usize, pos: u16) {
1110 assert!(n + 1 < self.area_length.len());
1111
1112 let total_len = self.total_area_len();
1113
1114 let mut pos_vec = Vec::new();
1116 let mut pp = 0;
1117 for len in &self.area_length {
1118 pp += *len;
1119 pos_vec.push(pp);
1120 }
1121 pos_vec.pop();
1123 let pos_count = pos_vec.len();
1124
1125 let mut min_pos = SPLIT_WIDTH;
1126 for i in 0..pos_vec.len() {
1127 if i < n {
1128 if self.area_length[i] == 0 {
1129 pos_vec[i] = min_pos;
1130 } else if self.hidden_length[i] != 0 {
1131 pos_vec[i] = min_pos;
1132 min_pos += SPLIT_WIDTH;
1133 } else {
1134 if pos_vec[i] >= pos {
1135 let rest_area_count = n - (i + 1);
1137 let rest_area_width = rest_area_count as u16 * SPLIT_WIDTH;
1138 pos_vec[i] = max(
1140 min_pos,
1141 pos.saturating_sub(SPLIT_WIDTH)
1142 .saturating_sub(rest_area_width),
1143 );
1144 min_pos += SPLIT_WIDTH;
1145 } else {
1146 }
1148 }
1149 } else if i == n {
1150 let rest_area_count = pos_count - (i + 1);
1152 let rest_area_width = rest_area_count as u16 * SPLIT_WIDTH;
1153 let rest_len = total_len - (min_pos + 1);
1154 let rest_len = rest_len - rest_area_width;
1156 let rest_len = rest_len + SPLIT_WIDTH;
1158
1159 let max_pos = min_pos + rest_len;
1160
1161 pos_vec[i] = min(max(min_pos, pos), max_pos);
1162
1163 min_pos = pos_vec[i] + SPLIT_WIDTH;
1164 } else {
1165 if self.area_length[i] == 0 {
1166 pos_vec[i] = min_pos;
1167 } else if self.hidden_length[i] != 0 {
1168 pos_vec[i] = min_pos;
1169 min_pos += SPLIT_WIDTH;
1170 } else {
1171 if pos_vec[i] <= pos {
1172 pos_vec[i] = min_pos;
1173 min_pos += SPLIT_WIDTH;
1174 } else {
1175 }
1177 }
1178 }
1179 }
1180
1181 for i in 0..pos_vec.len() {
1183 if i > 0 {
1184 self.area_length[i] = pos_vec[i] - pos_vec[i - 1];
1185 } else {
1186 self.area_length[i] = pos_vec[i];
1187 }
1188 }
1189 self.area_length[pos_count] = total_len - pos_vec[pos_count - 1];
1190 self.area_constraint.clear();
1191 }
1192
1193 pub fn is_hidden(&self, n: usize) -> bool {
1195 self.hidden_length[n] > 0
1196 }
1197
1198 pub fn hide_split(&mut self, n: usize) -> bool {
1202 if self.hidden_length[n] == 0 {
1203 self.area_constraint.clear();
1204
1205 let mut hide = if n + 1 == self.area_length.len() {
1206 self.area_length[n]
1207 } else {
1208 self.area_length[n].saturating_sub(SPLIT_WIDTH)
1209 };
1210 for idx in n + 1..self.area_length.len() {
1211 if self.hidden_length[idx] == 0 {
1212 self.area_length[idx] += hide;
1213 hide = 0;
1214 break;
1215 }
1216 }
1217 if hide > 0 {
1218 for idx in (0..n).rev() {
1219 if self.hidden_length[idx] == 0 {
1220 self.area_length[idx] += hide;
1221 hide = 0;
1222 break;
1223 }
1224 }
1225 }
1226
1227 if hide > 0 {
1228 self.hidden_length[n] = 0;
1230 false
1231 } else {
1232 if n + 1 == self.area_length.len() {
1233 self.hidden_length[n] = self.area_length[n];
1234 self.area_length[n] = 0;
1235 } else {
1236 self.hidden_length[n] = self.area_length[n].saturating_sub(SPLIT_WIDTH);
1237 self.area_length[n] = 1;
1238 };
1239 true
1240 }
1241 } else {
1242 false
1243 }
1244 }
1245
1246 pub fn show_split(&mut self, n: usize) -> bool {
1250 let mut show = self.hidden_length[n];
1251 if show > 0 {
1252 for idx in n + 1..self.area_length.len() {
1253 if self.hidden_length[idx] == 0 {
1254 if self.area_length[idx] > show + SPLIT_WIDTH {
1256 self.area_length[idx] -= show;
1257 show = 0;
1258 } else if self.area_length[idx] > SPLIT_WIDTH {
1259 show -= self.area_length[idx] - SPLIT_WIDTH;
1260 self.area_length[idx] = SPLIT_WIDTH;
1261 }
1262 if show == 0 {
1263 break;
1264 }
1265 }
1266 }
1267 if show > 0 {
1268 for idx in (0..n).rev() {
1269 if self.hidden_length[idx] == 0 {
1270 if self.area_length[idx] > show + SPLIT_WIDTH {
1271 self.area_length[idx] -= show;
1272 show = 0;
1273 } else if self.area_length[idx] > SPLIT_WIDTH {
1274 show -= self.area_length[idx] - SPLIT_WIDTH;
1275 self.area_length[idx] = SPLIT_WIDTH;
1276 }
1277 if show == 0 {
1278 break;
1279 }
1280 }
1281 }
1282 }
1283
1284 self.area_length[n] += self.hidden_length[n] - show;
1285 self.hidden_length[n] = 0;
1286 self.area_constraint.clear();
1287 true
1288 } else {
1289 false
1290 }
1291 }
1292}
1293
1294impl HandleEvent<crossterm::event::Event, Regular, Outcome> for SplitState {
1295 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Regular) -> Outcome {
1296 event_flow!(
1297 return if self.is_focused() {
1298 if let Some(n) = self.focus_marker {
1299 match event {
1300 ct_event!(keycode press Left) => self.select_prev_split().into(),
1301 ct_event!(keycode press Right) => self.select_next_split().into(),
1302 ct_event!(keycode press Up) => self.select_prev_split().into(),
1303 ct_event!(keycode press Down) => self.select_next_split().into(),
1304
1305 ct_event!(keycode press CONTROL-Left) => self.move_split_left(n, 1).into(),
1306 ct_event!(keycode press CONTROL-Right) => {
1307 self.move_split_right(n, 1).into()
1308 }
1309 ct_event!(keycode press CONTROL-Up) => self.move_split_up(n, 1).into(),
1310 ct_event!(keycode press CONTROL-Down) => self.move_split_down(n, 1).into(),
1311 _ => Outcome::Continue,
1312 }
1313 } else {
1314 Outcome::Continue
1315 }
1316 } else {
1317 Outcome::Continue
1318 }
1319 );
1320
1321 self.handle(event, MouseOnly)
1322 }
1323}
1324
1325impl HandleEvent<crossterm::event::Event, MouseOnly, Outcome> for SplitState {
1326 fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> Outcome {
1327 match event {
1328 ct_event!(mouse any for m) if self.mouse.hover(&self.splitline_areas, m) => {
1329 Outcome::Changed
1330 }
1331 ct_event!(mouse any for m) => {
1332 let was_drag = self.mouse.drag.get();
1333 if self.mouse.drag(&self.splitline_areas, m) {
1334 if let Some(n) = self.mouse.drag.get() {
1335 self.set_screen_split_pos(n, self.mouse.pos_of(m)).into()
1336 } else {
1337 Outcome::Continue
1338 }
1339 } else {
1340 if was_drag.is_some() {
1342 Outcome::Changed
1343 } else {
1344 Outcome::Continue
1345 }
1346 }
1347 }
1348 _ => Outcome::Continue,
1349 }
1350 }
1351}
1352
1353pub fn handle_events(
1357 state: &mut SplitState,
1358 focus: bool,
1359 event: &crossterm::event::Event,
1360) -> Outcome {
1361 state.focus.set(focus);
1362 HandleEvent::handle(state, event, Regular)
1363}
1364
1365pub fn handle_mouse_events(state: &mut SplitState, event: &crossterm::event::Event) -> Outcome {
1367 HandleEvent::handle(state, event, MouseOnly)
1368}