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 self.inner
759 }
760
761 fn navigable(&self) -> Navigation {
762 if self.is_focused() {
763 Navigation::Leave
764 } else {
765 Navigation::None
766 }
767 }
768}
769
770impl RelocatableState for SplitState {
771 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
772 if !self.relocate_split {
774 self.area = relocate_area(self.area, shift, clip);
775 self.inner = relocate_area(self.inner, shift, clip);
776 relocate_areas(self.widget_areas.as_mut_slice(), shift, clip);
777 relocate_areas(self.splitline_areas.as_mut_slice(), shift, clip);
778 relocate_positions(self.splitline_mark_position.as_mut_slice(), shift, clip);
779 }
780 }
781
782 fn relocate_popup(&mut self, shift: (i16, i16), clip: Rect) {
783 if self.relocate_split {
784 self.relocate_split = false;
785 self.area = relocate_area(self.area, shift, clip);
786 self.inner = relocate_area(self.inner, shift, clip);
787 relocate_areas(self.widget_areas.as_mut_slice(), shift, clip);
788 relocate_areas(self.splitline_areas.as_mut_slice(), shift, clip);
789 relocate_positions(self.splitline_mark_position.as_mut_slice(), shift, clip);
790 }
791 }
792}
793
794#[allow(clippy::len_without_is_empty)]
795impl SplitState {
796 pub fn new() -> Self {
798 Self::default()
799 }
800
801 pub fn named(name: &str) -> Self {
803 let mut z = Self::default();
804 z.focus = z.focus.with_name(name);
805 z
806 }
807
808 pub fn set_screen_split_pos(&mut self, n: usize, pos: (u16, u16)) -> bool {
815 if self.is_hidden(n) {
816 return false;
817 }
818 if self.direction == Direction::Horizontal {
819 let pos = if pos.0 < self.inner.left() {
820 0
821 } else if pos.0 < self.inner.right() {
822 pos.0 - self.inner.x
823 } else {
824 self.inner.width
825 };
826
827 let split_pos = self.split_pos(n);
828 self.set_split_pos(n, pos);
829
830 split_pos != self.split_pos(n)
831 } else {
832 let pos = if pos.1 < self.inner.top() {
833 0
834 } else if pos.1 < self.inner.bottom() {
835 pos.1 - self.inner.y
836 } else {
837 self.inner.height
838 };
839
840 let split_pos = self.split_pos(n);
841 self.set_split_pos(n, pos);
842
843 split_pos != self.split_pos(n)
844 }
845 }
846
847 pub fn move_split_left(&mut self, n: usize, delta: u16) -> bool {
851 let split_pos = self.split_pos(n);
852 self.set_split_pos(n, split_pos - delta);
853
854 split_pos != self.split_pos(n)
855 }
856
857 pub fn move_split_right(&mut self, n: usize, delta: u16) -> bool {
860 let split_pos = self.split_pos(n);
861 self.set_split_pos(n, split_pos + delta);
862
863 split_pos != self.split_pos(n)
864 }
865
866 pub fn move_split_up(&mut self, n: usize, delta: u16) -> bool {
869 self.move_split_left(n, delta)
870 }
871
872 pub fn move_split_down(&mut self, n: usize, delta: u16) -> bool {
875 self.move_split_right(n, delta)
876 }
877
878 pub fn select_next_split(&mut self) -> bool {
880 if self.is_focused() {
881 let n = self.focus_marker.unwrap_or_default();
882 if n + 1 >= self.area_length.len().saturating_sub(1) {
883 self.focus_marker = Some(0);
884 } else {
885 self.focus_marker = Some(n + 1);
886 }
887 true
888 } else {
889 false
890 }
891 }
892
893 pub fn select_prev_split(&mut self) -> bool {
895 if self.is_focused() {
896 let n = self.focus_marker.unwrap_or_default();
897 if n == 0 {
898 self.focus_marker =
899 Some(self.area_length.len().saturating_sub(1).saturating_sub(1));
900 } else {
901 self.focus_marker = Some(n - 1);
902 }
903 true
904 } else {
905 false
906 }
907 }
908
909 pub fn len(&self) -> usize {
911 self.area_length.len()
912 }
913
914 pub fn area_lengths(&self) -> &[u16] {
916 &self.area_length
917 }
918
919 pub fn set_area_lengths(&mut self, lengths: Vec<u16>) {
932 self.area_length = lengths;
933 self.area_constraint.clear();
934 while self.hidden_length.len() < self.area_length.len() {
935 self.hidden_length.push(0);
936 }
937 while self.hidden_length.len() > self.area_length.len() {
938 self.hidden_length.pop();
939 }
940 }
941
942 pub fn hidden_lengths(&self) -> &[u16] {
944 &self.hidden_length
945 }
946
947 pub fn set_hidden_lengths(&mut self, hidden: Vec<u16>) {
952 for i in 0..self.hidden_length.len() {
953 if let Some(v) = hidden.get(i) {
954 self.hidden_length[i] = *v;
955 } else {
956 self.hidden_length[i] = 0;
957 }
958 }
959 }
960
961 pub fn area_len(&self, n: usize) -> u16 {
971 if n >= self.area_length.len() {
972 return 0;
973 }
974 self.area_length[n]
975 }
976
977 pub fn total_area_len(&self) -> u16 {
979 self.area_length.iter().sum()
980 }
981
982 pub fn set_area_len(&mut self, n: usize, len: u16) {
1015 if n >= self.area_length.len() {
1016 return;
1017 }
1018 self.area_length[n] = len;
1019 self.area_constraint.clear();
1020 self.hidden_length[n] = 0;
1021 }
1022
1023 pub fn split_pos(&self, n: usize) -> u16 {
1035 if n + 1 >= self.area_length.len() {
1036 return self.area_length.iter().sum();
1037 }
1038 self.area_length[..n + 1].iter().sum()
1039 }
1040
1041 pub fn set_split_pos(&mut self, n: usize, pos: u16) {
1057 if n + 1 >= self.area_length.len() {
1058 return;
1059 }
1060
1061 match self.resize {
1062 SplitResize::Neighbours => {
1063 self.set_split_pos_neighbour(n, pos);
1064 }
1065 SplitResize::Full => {
1066 self.set_split_pos_full(n, pos);
1067 }
1068 }
1069 }
1070
1071 fn set_split_pos_neighbour(&mut self, n: usize, pos: u16) {
1074 assert!(n + 1 < self.area_length.len());
1075
1076 let mut pos_vec = Vec::new();
1078 let mut pp = 0;
1079 for len in &self.area_length {
1080 pp += *len;
1081 pos_vec.push(pp);
1082 }
1083 let pos_count = pos_vec.len();
1085
1086 let (min_pos, max_pos) = if n == 0 {
1087 if n + 2 >= pos_count {
1088 (SPLIT_WIDTH, pos_vec[n + 1])
1089 } else {
1090 (SPLIT_WIDTH, pos_vec[n + 1] - SPLIT_WIDTH)
1091 }
1092 } else if n + 2 < pos_count {
1093 (pos_vec[n - 1] + 1, pos_vec[n + 1] - SPLIT_WIDTH)
1094 } else {
1095 (pos_vec[n - 1] + 1, pos_vec[n + 1])
1096 };
1097
1098 pos_vec[n] = min(max(min_pos, pos), max_pos);
1099
1100 for i in 0..pos_vec.len() {
1102 if i > 0 {
1103 self.area_length[i] = pos_vec[i] - pos_vec[i - 1];
1104 } else {
1105 self.area_length[i] = pos_vec[i];
1106 }
1107 }
1108 self.area_constraint.clear();
1109 }
1110
1111 #[allow(clippy::needless_range_loop)]
1114 #[allow(clippy::comparison_chain)]
1115 fn set_split_pos_full(&mut self, n: usize, pos: u16) {
1116 assert!(n + 1 < self.area_length.len());
1117
1118 let total_len = self.total_area_len();
1119
1120 let mut pos_vec = Vec::new();
1122 let mut pp = 0;
1123 for len in &self.area_length {
1124 pp += *len;
1125 pos_vec.push(pp);
1126 }
1127 pos_vec.pop();
1129 let pos_count = pos_vec.len();
1130
1131 let mut min_pos = SPLIT_WIDTH;
1132 for i in 0..pos_vec.len() {
1133 if i < n {
1134 if self.area_length[i] == 0 {
1135 pos_vec[i] = min_pos;
1136 } else if self.hidden_length[i] != 0 {
1137 pos_vec[i] = min_pos;
1138 min_pos += SPLIT_WIDTH;
1139 } else {
1140 if pos_vec[i] >= pos {
1141 let rest_area_count = n - (i + 1);
1143 let rest_area_width = rest_area_count as u16 * SPLIT_WIDTH;
1144 pos_vec[i] = max(
1146 min_pos,
1147 pos.saturating_sub(SPLIT_WIDTH)
1148 .saturating_sub(rest_area_width),
1149 );
1150 min_pos += SPLIT_WIDTH;
1151 } else {
1152 }
1154 }
1155 } else if i == n {
1156 let rest_area_count = pos_count - (i + 1);
1158 let rest_area_width = rest_area_count as u16 * SPLIT_WIDTH;
1159 let rest_len = total_len - (min_pos + 1);
1160 let rest_len = rest_len - rest_area_width;
1162 let rest_len = rest_len + SPLIT_WIDTH;
1164
1165 let max_pos = min_pos + rest_len;
1166
1167 pos_vec[i] = min(max(min_pos, pos), max_pos);
1168
1169 min_pos = pos_vec[i] + SPLIT_WIDTH;
1170 } else {
1171 if self.area_length[i] == 0 {
1172 pos_vec[i] = min_pos;
1173 } else if self.hidden_length[i] != 0 {
1174 pos_vec[i] = min_pos;
1175 min_pos += SPLIT_WIDTH;
1176 } else {
1177 if pos_vec[i] <= pos {
1178 pos_vec[i] = min_pos;
1179 min_pos += SPLIT_WIDTH;
1180 } else {
1181 }
1183 }
1184 }
1185 }
1186
1187 for i in 0..pos_vec.len() {
1189 if i > 0 {
1190 self.area_length[i] = pos_vec[i] - pos_vec[i - 1];
1191 } else {
1192 self.area_length[i] = pos_vec[i];
1193 }
1194 }
1195 self.area_length[pos_count] = total_len - pos_vec[pos_count - 1];
1196 self.area_constraint.clear();
1197 }
1198
1199 pub fn is_hidden(&self, n: usize) -> bool {
1201 self.hidden_length[n] > 0
1202 }
1203
1204 pub fn hide_split(&mut self, n: usize) -> bool {
1208 if self.hidden_length[n] == 0 {
1209 self.area_constraint.clear();
1210
1211 let mut hide = if n + 1 == self.area_length.len() {
1212 self.area_length[n]
1213 } else {
1214 self.area_length[n].saturating_sub(SPLIT_WIDTH)
1215 };
1216 for idx in n + 1..self.area_length.len() {
1217 if self.hidden_length[idx] == 0 {
1218 self.area_length[idx] += hide;
1219 hide = 0;
1220 break;
1221 }
1222 }
1223 if hide > 0 {
1224 for idx in (0..n).rev() {
1225 if self.hidden_length[idx] == 0 {
1226 self.area_length[idx] += hide;
1227 hide = 0;
1228 break;
1229 }
1230 }
1231 }
1232
1233 if hide > 0 {
1234 self.hidden_length[n] = 0;
1236 false
1237 } else {
1238 if n + 1 == self.area_length.len() {
1239 self.hidden_length[n] = self.area_length[n];
1240 self.area_length[n] = 0;
1241 } else {
1242 self.hidden_length[n] = self.area_length[n].saturating_sub(SPLIT_WIDTH);
1243 self.area_length[n] = 1;
1244 };
1245 true
1246 }
1247 } else {
1248 false
1249 }
1250 }
1251
1252 pub fn show_split(&mut self, n: usize) -> bool {
1256 let mut show = self.hidden_length[n];
1257 if show > 0 {
1258 for idx in n + 1..self.area_length.len() {
1259 if self.hidden_length[idx] == 0 {
1260 if self.area_length[idx] > show + SPLIT_WIDTH {
1262 self.area_length[idx] -= show;
1263 show = 0;
1264 } else if self.area_length[idx] > SPLIT_WIDTH {
1265 show -= self.area_length[idx] - SPLIT_WIDTH;
1266 self.area_length[idx] = SPLIT_WIDTH;
1267 }
1268 if show == 0 {
1269 break;
1270 }
1271 }
1272 }
1273 if show > 0 {
1274 for idx in (0..n).rev() {
1275 if self.hidden_length[idx] == 0 {
1276 if self.area_length[idx] > show + SPLIT_WIDTH {
1277 self.area_length[idx] -= show;
1278 show = 0;
1279 } else if self.area_length[idx] > SPLIT_WIDTH {
1280 show -= self.area_length[idx] - SPLIT_WIDTH;
1281 self.area_length[idx] = SPLIT_WIDTH;
1282 }
1283 if show == 0 {
1284 break;
1285 }
1286 }
1287 }
1288 }
1289
1290 self.area_length[n] += self.hidden_length[n] - show;
1291 self.hidden_length[n] = 0;
1292 self.area_constraint.clear();
1293 true
1294 } else {
1295 false
1296 }
1297 }
1298}
1299
1300impl HandleEvent<Event, Regular, Outcome> for SplitState {
1301 fn handle(&mut self, event: &Event, _qualifier: Regular) -> Outcome {
1302 event_flow!(
1303 return if self.is_focused() {
1304 if let Some(n) = self.focus_marker {
1305 match event {
1306 ct_event!(keycode press Left) => self.select_prev_split().into(),
1307 ct_event!(keycode press Right) => self.select_next_split().into(),
1308 ct_event!(keycode press Up) => self.select_prev_split().into(),
1309 ct_event!(keycode press Down) => self.select_next_split().into(),
1310
1311 ct_event!(keycode press CONTROL-Left) => self.move_split_left(n, 1).into(),
1312 ct_event!(keycode press CONTROL-Right) => {
1313 self.move_split_right(n, 1).into()
1314 }
1315 ct_event!(keycode press CONTROL-Up) => self.move_split_up(n, 1).into(),
1316 ct_event!(keycode press CONTROL-Down) => self.move_split_down(n, 1).into(),
1317 _ => Outcome::Continue,
1318 }
1319 } else {
1320 Outcome::Continue
1321 }
1322 } else {
1323 Outcome::Continue
1324 }
1325 );
1326
1327 self.handle(event, MouseOnly)
1328 }
1329}
1330
1331impl HandleEvent<Event, MouseOnly, Outcome> for SplitState {
1332 fn handle(&mut self, event: &Event, _qualifier: MouseOnly) -> Outcome {
1333 if self.is_obscured() {
1334 return Outcome::Continue;
1335 }
1336
1337 match event {
1338 ct_event!(mouse any for m) if self.mouse.hover(&self.splitline_areas, m) => {
1339 Outcome::Changed
1340 }
1341 ct_event!(mouse any for m) => {
1342 let was_drag = self.mouse.drag.get();
1343 if self.mouse.drag(&self.splitline_areas, m) {
1344 if let Some(n) = self.mouse.drag.get() {
1345 self.set_screen_split_pos(n, self.mouse.pos_of(m)).into()
1346 } else {
1347 Outcome::Continue
1348 }
1349 } else {
1350 if was_drag.is_some() {
1352 Outcome::Changed
1353 } else {
1354 Outcome::Continue
1355 }
1356 }
1357 }
1358 _ => Outcome::Continue,
1359 }
1360 }
1361}
1362
1363pub fn handle_events(state: &mut SplitState, focus: bool, event: &Event) -> Outcome {
1367 state.focus.set(focus);
1368 HandleEvent::handle(state, event, Regular)
1369}
1370
1371pub fn handle_mouse_events(state: &mut SplitState, event: &Event) -> Outcome {
1373 HandleEvent::handle(state, event, MouseOnly)
1374}