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_core::buffer::Buffer;
46use ratatui_core::layout::{Constraint, Direction, Position, Rect};
47use ratatui_core::style::Style;
48use ratatui_core::widgets::{StatefulWidget, Widget};
49use ratatui_crossterm::crossterm::event::Event;
50use ratatui_widgets::block::Block;
51use ratatui_widgets::borders::BorderType;
52use std::cmp::{max, min};
53use std::iter;
54use std::rc::Rc;
55use unicode_segmentation::UnicodeSegmentation;
56
57mod split_impl;
58mod split_layout;
59
60#[derive(Debug, Default, Clone)]
61pub struct Split<'a> {
83 direction: Direction,
85 constraints: Vec<Constraint>,
88 resize_constraints: Vec<ResizeConstraint>,
90 resize: SplitResize,
92
93 split_type: SplitType,
95 join_0: Option<BorderType>,
97 join_1: Option<BorderType>,
99 mark_offset: u16,
101 mark_0_char: Option<&'a str>,
103 mark_1_char: Option<&'a str>,
105
106 style: Style,
108 block: Option<Block<'a>>,
109 arrow_style: Option<Style>,
110 drag_style: Option<Style>,
111}
112
113#[derive(Debug, Clone)]
115pub struct LayoutWidget<'a> {
116 split: Rc<Split<'a>>,
117}
118
119#[derive(Debug, Clone)]
121pub struct SplitWidget<'a> {
122 split: Rc<Split<'a>>,
123}
124
125#[derive(Debug, Clone)]
129pub struct SplitStyle {
130 pub style: Style,
132 pub block: Option<Block<'static>>,
134 pub border_style: Option<Style>,
135 pub title_style: Option<Style>,
136 pub arrow_style: Option<Style>,
138 pub drag_style: Option<Style>,
140
141 pub horizontal_mark: Option<&'static str>,
144 pub vertical_mark: Option<&'static str>,
147
148 pub non_exhaustive: NonExhaustive,
149}
150
151#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
153pub enum SplitType {
154 #[default]
157 FullEmpty,
158 FullPlain,
161 FullDouble,
164 FullThick,
167 FullQuadrantInside,
171 FullQuadrantOutside,
175 Scroll,
186 Widget,
193}
194
195#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
197pub enum SplitResize {
198 Neighbours,
201 #[default]
205 Full,
206}
207
208#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
210pub enum ResizeConstraint {
211 Fixed,
213 #[default]
216 ScaleProportional,
217 ScaleEqual,
223}
224
225const SPLIT_WIDTH: u16 = 1;
226
227#[derive(Debug)]
229pub struct SplitState {
230 pub area: Rect,
233 pub inner: Rect,
236 pub widget_areas: Vec<Rect>,
240 pub splitline_areas: Vec<Rect>,
244 pub splitline_mark_position: Vec<Position>,
247 pub mark_offset: u16,
250
251 pub direction: Direction,
254 pub split_type: SplitType,
256 pub resize: SplitResize,
258
259 area_length: Vec<u16>,
264 area_constraint: Vec<Constraint>,
265 hidden_length: Vec<u16>,
267
268 pub focus: FocusFlag,
271 pub focus_marker: Option<usize>,
275
276 pub mouse: MouseFlagsN,
279
280 relocate_split: bool,
283
284 pub non_exhaustive: NonExhaustive,
285}
286
287impl SplitType {
288 pub fn is_full(&self) -> bool {
289 use SplitType::*;
290 match self {
291 FullEmpty => true,
292 FullPlain => true,
293 FullDouble => true,
294 FullThick => true,
295 FullQuadrantInside => true,
296 FullQuadrantOutside => true,
297 Scroll => false,
298 Widget => false,
299 }
300 }
301}
302
303impl Default for SplitStyle {
304 fn default() -> Self {
305 Self {
306 style: Default::default(),
307 block: Default::default(),
308 border_style: Default::default(),
309 title_style: Default::default(),
310 arrow_style: Default::default(),
311 drag_style: Default::default(),
312 horizontal_mark: Default::default(),
313 vertical_mark: Default::default(),
314 non_exhaustive: NonExhaustive,
315 }
316 }
317}
318
319impl<'a> Split<'a> {
320 pub fn new() -> Self {
325 Self {
326 direction: Direction::Horizontal,
327 ..Default::default()
328 }
329 }
330
331 pub fn horizontal() -> Self {
333 Self {
334 direction: Direction::Horizontal,
335 ..Default::default()
336 }
337 }
338
339 pub fn vertical() -> Self {
341 Self {
342 direction: Direction::Horizontal,
343 ..Default::default()
344 }
345 }
346
347 pub fn constraints(mut self, constraints: impl IntoIterator<Item = Constraint>) -> Self {
353 self.constraints = constraints.into_iter().collect();
354 self.resize_constraints = iter::from_fn(|| Some(ResizeConstraint::ScaleProportional))
355 .take(self.constraints.len())
356 .collect();
357 self
358 }
359
360 pub fn resize_constraint(mut self, n: usize, constraint: ResizeConstraint) -> Self {
362 self.resize_constraints[n] = constraint;
363 self
364 }
365
366 pub fn direction(mut self, direction: Direction) -> Self {
370 self.direction = direction;
371 self
372 }
373
374 pub fn split_type(mut self, split_type: SplitType) -> Self {
376 self.split_type = split_type;
377 self
378 }
379
380 pub fn resize(mut self, resize: SplitResize) -> Self {
382 self.resize = resize;
383 self
384 }
385
386 pub fn join(mut self, border: BorderType) -> Self {
390 self.join_0 = Some(border);
391 self.join_1 = Some(border);
392 self
393 }
394
395 pub fn join_0(mut self, border: BorderType) -> Self {
399 self.join_0 = Some(border);
400 self
401 }
402
403 pub fn join_1(mut self, border: BorderType) -> Self {
407 self.join_1 = Some(border);
408 self
409 }
410
411 pub fn block(mut self, block: Block<'a>) -> Self {
413 self.block = Some(block);
414 self
415 }
416
417 pub fn styles(mut self, styles: SplitStyle) -> Self {
419 self.style = styles.style;
420 if styles.block.is_some() {
421 self.block = styles.block;
422 }
423 if let Some(border_style) = styles.border_style {
424 self.block = self.block.map(|v| v.border_style(border_style));
425 }
426 if let Some(title_style) = styles.title_style {
427 self.block = self.block.map(|v| v.title_style(title_style));
428 }
429 self.block = self.block.map(|v| v.style(self.style));
430
431 if styles.drag_style.is_some() {
432 self.drag_style = styles.drag_style;
433 }
434 if styles.arrow_style.is_some() {
435 self.arrow_style = styles.arrow_style;
436 }
437 match self.direction {
438 Direction::Horizontal => {
439 if let Some(mark) = styles.horizontal_mark {
440 let mut g = mark.graphemes(true);
441 if let Some(g0) = g.next() {
442 self.mark_0_char = Some(g0);
443 }
444 if let Some(g1) = g.next() {
445 self.mark_1_char = Some(g1);
446 }
447 }
448 }
449 Direction::Vertical => {
450 if let Some(mark) = styles.vertical_mark {
451 let mut g = mark.graphemes(true);
452 if let Some(g0) = g.next() {
453 self.mark_0_char = Some(g0);
454 }
455 if let Some(g1) = g.next() {
456 self.mark_1_char = Some(g1);
457 }
458 }
459 }
460 }
461 self
462 }
463
464 pub fn style(mut self, style: Style) -> Self {
466 self.style = style;
467 self.block = self.block.map(|v| v.style(style));
468 self
469 }
470
471 pub fn arrow_style(mut self, style: Style) -> Self {
473 self.arrow_style = Some(style);
474 self
475 }
476
477 pub fn drag_style(mut self, style: Style) -> Self {
479 self.drag_style = Some(style);
480 self
481 }
482
483 pub fn mark_offset(mut self, offset: u16) -> Self {
485 self.mark_offset = offset;
486 self
487 }
488
489 pub fn mark_0(mut self, mark: &'a str) -> Self {
491 self.mark_0_char = Some(mark);
492 self
493 }
494
495 pub fn mark_1(mut self, mark: &'a str) -> Self {
497 self.mark_1_char = Some(mark);
498 self
499 }
500
501 #[deprecated(since = "2.4.0", note = "use into_widgets() instead")]
509 pub fn into_widget(self, area: Rect, state: &mut SplitState) -> SplitWidget<'a> {
510 layout_split(&self, area, state);
511 SplitWidget {
512 split: Rc::new(self),
513 }
514 }
515
516 #[deprecated(since = "2.4.0", note = "use into_widgets() instead")]
525 pub fn into_widget_layout(
526 self,
527 area: Rect,
528 state: &mut SplitState,
529 ) -> (SplitWidget<'a>, Vec<Rect>) {
530 layout_split(&self, area, state);
531 (
532 SplitWidget {
533 split: Rc::new(self),
534 },
535 state.widget_areas.clone(),
536 )
537 }
538
539 pub fn into_widgets(self) -> (LayoutWidget<'a>, SplitWidget<'a>) {
551 let split = Rc::new(self);
552 (
553 LayoutWidget {
554 split: split.clone(), },
556 SplitWidget {
557 split, },
559 )
560 }
561}
562
563impl<'a> StatefulWidget for &Split<'a> {
564 type State = SplitState;
565
566 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
567 layout_split(self, area, state);
568 render_split(self, buf, state);
569 state.relocate_split = false;
570 }
571}
572
573impl<'a> StatefulWidget for Split<'a> {
574 type State = SplitState;
575
576 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
577 layout_split(&self, area, state);
578 render_split(&self, buf, state);
579 state.relocate_split = false;
580 }
581}
582
583impl<'a> StatefulWidget for &LayoutWidget<'a> {
584 type State = SplitState;
585
586 fn render(self, area: Rect, _buf: &mut Buffer, state: &mut Self::State) {
587 layout_split(&self.split, area, state);
589 }
590}
591
592impl<'a> StatefulWidget for LayoutWidget<'a> {
593 type State = SplitState;
594
595 fn render(self, area: Rect, _buf: &mut Buffer, state: &mut Self::State) {
596 layout_split(&self.split, area, state);
598 }
599}
600
601impl<'a> StatefulWidget for &SplitWidget<'a> {
602 type State = SplitState;
603
604 fn render(self, _area: Rect, buf: &mut Buffer, state: &mut Self::State) {
605 render_split(&self.split, buf, state);
606 }
607}
608
609impl StatefulWidget for SplitWidget<'_> {
610 type State = SplitState;
611
612 fn render(self, _area: Rect, buf: &mut Buffer, state: &mut Self::State) {
613 render_split(&self.split, buf, state);
614 }
615}
616
617fn render_split(split: &Split<'_>, buf: &mut Buffer, state: &mut SplitState) {
618 let area = state.area;
619 if state.is_focused() {
620 if state.focus_marker.is_none() {
621 state.focus_marker = Some(0);
622 }
623 } else {
624 state.focus_marker = None;
625 }
626
627 split.block.clone().render(area, buf);
628
629 for (n, split_area) in state.splitline_areas.iter().enumerate() {
630 if split.direction == Direction::Horizontal {
632 if split_area.width == 0 {
633 continue;
634 }
635 } else {
636 if split_area.height == 0 {
637 continue;
638 }
639 }
640
641 let (style, arrow_style) = if Some(n) == state.mouse.drag.get()
642 || Some(n) == state.focus_marker
643 || Some(n) == state.mouse.hover.get()
644 {
645 if let Some(drag) = split.drag_style {
646 (drag, drag)
647 } else {
648 (revert_style(split.style), revert_style(split.style))
649 }
650 } else {
651 if let Some(arrow) = split.arrow_style {
652 (split.style, arrow)
653 } else {
654 (split.style, split.style)
655 }
656 };
657
658 if let Some(fill) = get_fill_char(split) {
659 fill_buf_area(buf, *split_area, fill, style);
660 }
661
662 let mark = state.splitline_mark_position[n];
663 if split.direction == Direction::Horizontal {
664 if buf.area.contains((mark.x, mark.y).into()) {
665 if let Some(cell) = buf.cell_mut((mark.x, mark.y)) {
666 cell.set_style(arrow_style);
667 cell.set_symbol(get_mark_0(split));
668 }
669 }
670 if buf.area.contains((mark.x, mark.y + 1).into()) {
671 if let Some(cell) = buf.cell_mut((mark.x, mark.y + 1)) {
672 cell.set_style(arrow_style);
673 cell.set_symbol(get_mark_1(split));
674 }
675 }
676 } else {
677 if let Some(cell) = buf.cell_mut((mark.x, mark.y)) {
678 cell.set_style(arrow_style);
679 cell.set_symbol(get_mark_0(split));
680 }
681 if let Some(cell) = buf.cell_mut((mark.x + 1, mark.y)) {
682 cell.set_style(arrow_style);
683 cell.set_symbol(get_mark_1(split));
684 }
685 }
686
687 if let Some((pos_0, c_0)) = get_join_0(split, *split_area, state) {
688 if let Some(cell) = buf.cell_mut((pos_0.x, pos_0.y)) {
689 cell.set_symbol(c_0);
690 }
691 }
692 if let Some((pos_1, c_1)) = get_join_1(split, *split_area, state) {
693 if let Some(cell) = buf.cell_mut((pos_1.x, pos_1.y)) {
694 cell.set_symbol(c_1);
695 }
696 }
697 }
698}
699
700impl Default for SplitState {
701 fn default() -> Self {
702 Self {
703 area: Default::default(),
704 inner: Default::default(),
705 widget_areas: Default::default(),
706 splitline_areas: Default::default(),
707 splitline_mark_position: Default::default(),
708 mark_offset: Default::default(),
709 direction: Default::default(),
710 split_type: Default::default(),
711 resize: Default::default(),
712 area_length: Default::default(),
713 area_constraint: Default::default(),
714 hidden_length: Default::default(),
715 focus: Default::default(),
716 focus_marker: Default::default(),
717 mouse: Default::default(),
718 relocate_split: Default::default(),
719 non_exhaustive: NonExhaustive,
720 }
721 }
722}
723
724impl Clone for SplitState {
725 fn clone(&self) -> Self {
726 Self {
727 area: self.area,
728 inner: self.inner,
729 widget_areas: self.widget_areas.clone(),
730 splitline_areas: self.splitline_areas.clone(),
731 splitline_mark_position: self.splitline_mark_position.clone(),
732 mark_offset: self.mark_offset,
733 direction: self.direction,
734 split_type: self.split_type,
735 resize: self.resize,
736 area_length: self.area_length.clone(),
737 area_constraint: self.area_constraint.clone(),
738 hidden_length: self.hidden_length.clone(),
739 focus: self.focus.new_instance(),
740 focus_marker: self.focus_marker,
741 mouse: Default::default(),
742 relocate_split: self.relocate_split,
743 non_exhaustive: NonExhaustive,
744 }
745 }
746}
747
748impl HasFocus for SplitState {
749 fn build(&self, builder: &mut FocusBuilder) {
750 builder.leaf_widget(self);
751 }
752
753 fn focus(&self) -> FocusFlag {
754 self.focus.clone()
755 }
756
757 fn area(&self) -> Rect {
758 Rect::default()
760 }
761
762 fn navigable(&self) -> Navigation {
763 Navigation::Leave
764 }
765}
766
767impl RelocatableState for SplitState {
768 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
769 if !self.relocate_split {
771 self.area = relocate_area(self.area, shift, clip);
772 self.inner = relocate_area(self.inner, shift, clip);
773 relocate_areas(self.widget_areas.as_mut_slice(), shift, clip);
774 relocate_areas(self.splitline_areas.as_mut_slice(), shift, clip);
775 relocate_positions(self.splitline_mark_position.as_mut_slice(), shift, clip);
776 }
777 }
778
779 fn relocate_popup(&mut self, shift: (i16, i16), clip: Rect) {
780 if self.relocate_split {
781 self.relocate_split = false;
782 self.area = relocate_area(self.area, shift, clip);
783 self.inner = relocate_area(self.inner, shift, clip);
784 relocate_areas(self.widget_areas.as_mut_slice(), shift, clip);
785 relocate_areas(self.splitline_areas.as_mut_slice(), shift, clip);
786 relocate_positions(self.splitline_mark_position.as_mut_slice(), shift, clip);
787 }
788 }
789}
790
791#[allow(clippy::len_without_is_empty)]
792impl SplitState {
793 pub fn new() -> Self {
795 Self::default()
796 }
797
798 pub fn named(name: &str) -> Self {
800 let mut z = Self::default();
801 z.focus = z.focus.with_name(name);
802 z
803 }
804
805 pub fn set_screen_split_pos(&mut self, n: usize, pos: (u16, u16)) -> bool {
812 if self.is_hidden(n) {
813 return false;
814 }
815 if self.direction == Direction::Horizontal {
816 let pos = if pos.0 < self.inner.left() {
817 0
818 } else if pos.0 < self.inner.right() {
819 pos.0 - self.inner.x
820 } else {
821 self.inner.width
822 };
823
824 let split_pos = self.split_pos(n);
825 self.set_split_pos(n, pos);
826
827 split_pos != self.split_pos(n)
828 } else {
829 let pos = if pos.1 < self.inner.top() {
830 0
831 } else if pos.1 < self.inner.bottom() {
832 pos.1 - self.inner.y
833 } else {
834 self.inner.height
835 };
836
837 let split_pos = self.split_pos(n);
838 self.set_split_pos(n, pos);
839
840 split_pos != self.split_pos(n)
841 }
842 }
843
844 pub fn move_split_left(&mut self, n: usize, delta: u16) -> bool {
848 let split_pos = self.split_pos(n);
849 self.set_split_pos(n, split_pos - delta);
850
851 split_pos != self.split_pos(n)
852 }
853
854 pub fn move_split_right(&mut self, n: usize, delta: u16) -> bool {
857 let split_pos = self.split_pos(n);
858 self.set_split_pos(n, split_pos + delta);
859
860 split_pos != self.split_pos(n)
861 }
862
863 pub fn move_split_up(&mut self, n: usize, delta: u16) -> bool {
866 self.move_split_left(n, delta)
867 }
868
869 pub fn move_split_down(&mut self, n: usize, delta: u16) -> bool {
872 self.move_split_right(n, delta)
873 }
874
875 pub fn select_next_split(&mut self) -> bool {
877 if self.is_focused() {
878 let n = self.focus_marker.unwrap_or_default();
879 if n + 1 >= self.area_length.len().saturating_sub(1) {
880 self.focus_marker = Some(0);
881 } else {
882 self.focus_marker = Some(n + 1);
883 }
884 true
885 } else {
886 false
887 }
888 }
889
890 pub fn select_prev_split(&mut self) -> bool {
892 if self.is_focused() {
893 let n = self.focus_marker.unwrap_or_default();
894 if n == 0 {
895 self.focus_marker =
896 Some(self.area_length.len().saturating_sub(1).saturating_sub(1));
897 } else {
898 self.focus_marker = Some(n - 1);
899 }
900 true
901 } else {
902 false
903 }
904 }
905
906 pub fn len(&self) -> usize {
908 self.area_length.len()
909 }
910
911 pub fn area_lengths(&self) -> &[u16] {
913 &self.area_length
914 }
915
916 pub fn set_area_lengths(&mut self, lengths: Vec<u16>) {
929 self.area_length = lengths;
930 self.area_constraint.clear();
931 while self.hidden_length.len() < self.area_length.len() {
932 self.hidden_length.push(0);
933 }
934 while self.hidden_length.len() > self.area_length.len() {
935 self.hidden_length.pop();
936 }
937 }
938
939 pub fn hidden_lengths(&self) -> &[u16] {
941 &self.hidden_length
942 }
943
944 pub fn set_hidden_lengths(&mut self, hidden: Vec<u16>) {
949 for i in 0..self.hidden_length.len() {
950 if let Some(v) = hidden.get(i) {
951 self.hidden_length[i] = *v;
952 } else {
953 self.hidden_length[i] = 0;
954 }
955 }
956 }
957
958 pub fn area_len(&self, n: usize) -> u16 {
968 if n >= self.area_length.len() {
969 return 0;
970 }
971 self.area_length[n]
972 }
973
974 pub fn total_area_len(&self) -> u16 {
976 self.area_length.iter().sum()
977 }
978
979 pub fn set_area_len(&mut self, n: usize, len: u16) {
1012 if n >= self.area_length.len() {
1013 return;
1014 }
1015 self.area_length[n] = len;
1016 self.area_constraint.clear();
1017 self.hidden_length[n] = 0;
1018 }
1019
1020 pub fn split_pos(&self, n: usize) -> u16 {
1032 if n + 1 >= self.area_length.len() {
1033 return self.area_length.iter().sum();
1034 }
1035 self.area_length[..n + 1].iter().sum()
1036 }
1037
1038 pub fn set_split_pos(&mut self, n: usize, pos: u16) {
1054 if n + 1 >= self.area_length.len() {
1055 return;
1056 }
1057
1058 match self.resize {
1059 SplitResize::Neighbours => {
1060 self.set_split_pos_neighbour(n, pos);
1061 }
1062 SplitResize::Full => {
1063 self.set_split_pos_full(n, pos);
1064 }
1065 }
1066 }
1067
1068 fn set_split_pos_neighbour(&mut self, n: usize, pos: u16) {
1071 assert!(n + 1 < self.area_length.len());
1072
1073 let mut pos_vec = Vec::new();
1075 let mut pp = 0;
1076 for len in &self.area_length {
1077 pp += *len;
1078 pos_vec.push(pp);
1079 }
1080 let pos_count = pos_vec.len();
1082
1083 let (min_pos, max_pos) = if n == 0 {
1084 if n + 2 >= pos_count {
1085 (SPLIT_WIDTH, pos_vec[n + 1])
1086 } else {
1087 (SPLIT_WIDTH, pos_vec[n + 1] - SPLIT_WIDTH)
1088 }
1089 } else if n + 2 < pos_count {
1090 (pos_vec[n - 1] + 1, pos_vec[n + 1] - SPLIT_WIDTH)
1091 } else {
1092 (pos_vec[n - 1] + 1, pos_vec[n + 1])
1093 };
1094
1095 pos_vec[n] = min(max(min_pos, pos), max_pos);
1096
1097 for i in 0..pos_vec.len() {
1099 if i > 0 {
1100 self.area_length[i] = pos_vec[i] - pos_vec[i - 1];
1101 } else {
1102 self.area_length[i] = pos_vec[i];
1103 }
1104 }
1105 self.area_constraint.clear();
1106 }
1107
1108 #[allow(clippy::needless_range_loop)]
1111 #[allow(clippy::comparison_chain)]
1112 fn set_split_pos_full(&mut self, n: usize, pos: u16) {
1113 assert!(n + 1 < self.area_length.len());
1114
1115 let total_len = self.total_area_len();
1116
1117 let mut pos_vec = Vec::new();
1119 let mut pp = 0;
1120 for len in &self.area_length {
1121 pp += *len;
1122 pos_vec.push(pp);
1123 }
1124 pos_vec.pop();
1126 let pos_count = pos_vec.len();
1127
1128 let mut min_pos = SPLIT_WIDTH;
1129 for i in 0..pos_vec.len() {
1130 if i < n {
1131 if self.area_length[i] == 0 {
1132 pos_vec[i] = min_pos;
1133 } else if self.hidden_length[i] != 0 {
1134 pos_vec[i] = min_pos;
1135 min_pos += SPLIT_WIDTH;
1136 } else {
1137 if pos_vec[i] >= pos {
1138 let rest_area_count = n - (i + 1);
1140 let rest_area_width = rest_area_count as u16 * SPLIT_WIDTH;
1141 pos_vec[i] = max(
1143 min_pos,
1144 pos.saturating_sub(SPLIT_WIDTH)
1145 .saturating_sub(rest_area_width),
1146 );
1147 min_pos += SPLIT_WIDTH;
1148 } else {
1149 }
1151 }
1152 } else if i == n {
1153 let rest_area_count = pos_count - (i + 1);
1155 let rest_area_width = rest_area_count as u16 * SPLIT_WIDTH;
1156 let rest_len = total_len - (min_pos + 1);
1157 let rest_len = rest_len - rest_area_width;
1159 let rest_len = rest_len + SPLIT_WIDTH;
1161
1162 let max_pos = min_pos + rest_len;
1163
1164 pos_vec[i] = min(max(min_pos, pos), max_pos);
1165
1166 min_pos = pos_vec[i] + SPLIT_WIDTH;
1167 } else {
1168 if self.area_length[i] == 0 {
1169 pos_vec[i] = min_pos;
1170 } else if self.hidden_length[i] != 0 {
1171 pos_vec[i] = min_pos;
1172 min_pos += SPLIT_WIDTH;
1173 } else {
1174 if pos_vec[i] <= pos {
1175 pos_vec[i] = min_pos;
1176 min_pos += SPLIT_WIDTH;
1177 } else {
1178 }
1180 }
1181 }
1182 }
1183
1184 for i in 0..pos_vec.len() {
1186 if i > 0 {
1187 self.area_length[i] = pos_vec[i] - pos_vec[i - 1];
1188 } else {
1189 self.area_length[i] = pos_vec[i];
1190 }
1191 }
1192 self.area_length[pos_count] = total_len - pos_vec[pos_count - 1];
1193 self.area_constraint.clear();
1194 }
1195
1196 pub fn is_hidden(&self, n: usize) -> bool {
1198 self.hidden_length[n] > 0
1199 }
1200
1201 pub fn hide_split(&mut self, n: usize) -> bool {
1205 if self.hidden_length[n] == 0 {
1206 self.area_constraint.clear();
1207
1208 let mut hide = if n + 1 == self.area_length.len() {
1209 self.area_length[n]
1210 } else {
1211 self.area_length[n].saturating_sub(SPLIT_WIDTH)
1212 };
1213 for idx in n + 1..self.area_length.len() {
1214 if self.hidden_length[idx] == 0 {
1215 self.area_length[idx] += hide;
1216 hide = 0;
1217 break;
1218 }
1219 }
1220 if hide > 0 {
1221 for idx in (0..n).rev() {
1222 if self.hidden_length[idx] == 0 {
1223 self.area_length[idx] += hide;
1224 hide = 0;
1225 break;
1226 }
1227 }
1228 }
1229
1230 if hide > 0 {
1231 self.hidden_length[n] = 0;
1233 false
1234 } else {
1235 if n + 1 == self.area_length.len() {
1236 self.hidden_length[n] = self.area_length[n];
1237 self.area_length[n] = 0;
1238 } else {
1239 self.hidden_length[n] = self.area_length[n].saturating_sub(SPLIT_WIDTH);
1240 self.area_length[n] = 1;
1241 };
1242 true
1243 }
1244 } else {
1245 false
1246 }
1247 }
1248
1249 pub fn show_split(&mut self, n: usize) -> bool {
1253 let mut show = self.hidden_length[n];
1254 if show > 0 {
1255 for idx in n + 1..self.area_length.len() {
1256 if self.hidden_length[idx] == 0 {
1257 if self.area_length[idx] > show + SPLIT_WIDTH {
1259 self.area_length[idx] -= show;
1260 show = 0;
1261 } else if self.area_length[idx] > SPLIT_WIDTH {
1262 show -= self.area_length[idx] - SPLIT_WIDTH;
1263 self.area_length[idx] = SPLIT_WIDTH;
1264 }
1265 if show == 0 {
1266 break;
1267 }
1268 }
1269 }
1270 if show > 0 {
1271 for idx in (0..n).rev() {
1272 if self.hidden_length[idx] == 0 {
1273 if self.area_length[idx] > show + SPLIT_WIDTH {
1274 self.area_length[idx] -= show;
1275 show = 0;
1276 } else if self.area_length[idx] > SPLIT_WIDTH {
1277 show -= self.area_length[idx] - SPLIT_WIDTH;
1278 self.area_length[idx] = SPLIT_WIDTH;
1279 }
1280 if show == 0 {
1281 break;
1282 }
1283 }
1284 }
1285 }
1286
1287 self.area_length[n] += self.hidden_length[n] - show;
1288 self.hidden_length[n] = 0;
1289 self.area_constraint.clear();
1290 true
1291 } else {
1292 false
1293 }
1294 }
1295}
1296
1297impl HandleEvent<Event, Regular, Outcome> for SplitState {
1298 fn handle(&mut self, event: &Event, _qualifier: Regular) -> Outcome {
1299 event_flow!(
1300 return if self.is_focused() {
1301 if let Some(n) = self.focus_marker {
1302 match event {
1303 ct_event!(keycode press Left) => self.select_prev_split().into(),
1304 ct_event!(keycode press Right) => self.select_next_split().into(),
1305 ct_event!(keycode press Up) => self.select_prev_split().into(),
1306 ct_event!(keycode press Down) => self.select_next_split().into(),
1307
1308 ct_event!(keycode press CONTROL-Left) => self.move_split_left(n, 1).into(),
1309 ct_event!(keycode press CONTROL-Right) => {
1310 self.move_split_right(n, 1).into()
1311 }
1312 ct_event!(keycode press CONTROL-Up) => self.move_split_up(n, 1).into(),
1313 ct_event!(keycode press CONTROL-Down) => self.move_split_down(n, 1).into(),
1314 _ => Outcome::Continue,
1315 }
1316 } else {
1317 Outcome::Continue
1318 }
1319 } else {
1320 Outcome::Continue
1321 }
1322 );
1323
1324 self.handle(event, MouseOnly)
1325 }
1326}
1327
1328impl HandleEvent<Event, MouseOnly, Outcome> for SplitState {
1329 fn handle(&mut self, event: &Event, _qualifier: MouseOnly) -> Outcome {
1330 match event {
1331 ct_event!(mouse any for m) if self.mouse.hover(&self.splitline_areas, m) => {
1332 Outcome::Changed
1333 }
1334 ct_event!(mouse any for m) => {
1335 let was_drag = self.mouse.drag.get();
1336 if self.mouse.drag(&self.splitline_areas, m) {
1337 if let Some(n) = self.mouse.drag.get() {
1338 self.set_screen_split_pos(n, self.mouse.pos_of(m)).into()
1339 } else {
1340 Outcome::Continue
1341 }
1342 } else {
1343 if was_drag.is_some() {
1345 Outcome::Changed
1346 } else {
1347 Outcome::Continue
1348 }
1349 }
1350 }
1351 _ => Outcome::Continue,
1352 }
1353 }
1354}
1355
1356pub fn handle_events(state: &mut SplitState, focus: bool, event: &Event) -> Outcome {
1360 state.focus.set(focus);
1361 HandleEvent::handle(state, event, Regular)
1362}
1363
1364pub fn handle_mouse_events(state: &mut SplitState, event: &Event) -> Outcome {
1366 HandleEvent::handle(state, event, MouseOnly)
1367}