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 {
326 Self {
327 direction: Direction::Horizontal,
328 ..Default::default()
329 }
330 }
331
332 pub fn horizontal() -> Self {
334 Self {
335 direction: Direction::Horizontal,
336 ..Default::default()
337 }
338 }
339
340 pub fn vertical() -> Self {
342 Self {
343 direction: Direction::Horizontal,
344 ..Default::default()
345 }
346 }
347
348 pub fn constraints(mut self, constraints: impl IntoIterator<Item = Constraint>) -> Self {
354 self.constraints = constraints.into_iter().collect();
355 self.resize_constraints = iter::from_fn(|| Some(ResizeConstraint::ScaleProportional))
356 .take(self.constraints.len())
357 .collect();
358 self
359 }
360
361 pub fn resize_constraint(mut self, n: usize, constraint: ResizeConstraint) -> Self {
363 self.resize_constraints[n] = constraint;
364 self
365 }
366
367 pub fn direction(mut self, direction: Direction) -> Self {
371 self.direction = direction;
372 self
373 }
374
375 pub fn split_type(mut self, split_type: SplitType) -> Self {
377 self.split_type = split_type;
378 self
379 }
380
381 pub fn resize(mut self, resize: SplitResize) -> Self {
383 self.resize = resize;
384 self
385 }
386
387 pub fn join(mut self, border: BorderType) -> Self {
391 self.join_0 = Some(border);
392 self.join_1 = Some(border);
393 self
394 }
395
396 pub fn join_0(mut self, border: BorderType) -> Self {
400 self.join_0 = Some(border);
401 self
402 }
403
404 pub fn join_1(mut self, border: BorderType) -> Self {
408 self.join_1 = Some(border);
409 self
410 }
411
412 pub fn block(mut self, block: Block<'a>) -> Self {
414 self.block = Some(block);
415 self
416 }
417
418 pub fn styles(mut self, styles: SplitStyle) -> Self {
420 self.style = styles.style;
421 if styles.block.is_some() {
422 self.block = styles.block;
423 }
424 if let Some(border_style) = styles.border_style {
425 self.block = self.block.map(|v| v.border_style(border_style));
426 }
427 if let Some(title_style) = styles.title_style {
428 self.block = self.block.map(|v| v.title_style(title_style));
429 }
430 self.block = self.block.map(|v| v.style(self.style));
431
432 if styles.drag_style.is_some() {
433 self.drag_style = styles.drag_style;
434 }
435 if styles.arrow_style.is_some() {
436 self.arrow_style = styles.arrow_style;
437 }
438 match self.direction {
439 Direction::Horizontal => {
440 if let Some(mark) = styles.horizontal_mark {
441 let mut g = mark.graphemes(true);
442 if let Some(g0) = g.next() {
443 self.mark_0_char = Some(g0);
444 }
445 if let Some(g1) = g.next() {
446 self.mark_1_char = Some(g1);
447 }
448 }
449 }
450 Direction::Vertical => {
451 if let Some(mark) = styles.vertical_mark {
452 let mut g = mark.graphemes(true);
453 if let Some(g0) = g.next() {
454 self.mark_0_char = Some(g0);
455 }
456 if let Some(g1) = g.next() {
457 self.mark_1_char = Some(g1);
458 }
459 }
460 }
461 }
462 self
463 }
464
465 pub fn style(mut self, style: Style) -> Self {
467 self.style = style;
468 self.block = self.block.map(|v| v.style(style));
469 self
470 }
471
472 pub fn arrow_style(mut self, style: Style) -> Self {
474 self.arrow_style = Some(style);
475 self
476 }
477
478 pub fn drag_style(mut self, style: Style) -> Self {
480 self.drag_style = Some(style);
481 self
482 }
483
484 pub fn mark_offset(mut self, offset: u16) -> Self {
486 self.mark_offset = offset;
487 self
488 }
489
490 pub fn mark_0(mut self, mark: &'a str) -> Self {
492 self.mark_0_char = Some(mark);
493 self
494 }
495
496 pub fn mark_1(mut self, mark: &'a str) -> Self {
498 self.mark_1_char = Some(mark);
499 self
500 }
501
502 #[deprecated(since = "2.4.0", note = "use into_widgets() instead")]
510 pub fn into_widget(self, area: Rect, state: &mut SplitState) -> SplitWidget<'a> {
511 layout_split(&self, area, state);
512 SplitWidget {
513 split: Rc::new(self),
514 }
515 }
516
517 #[deprecated(since = "2.4.0", note = "use into_widgets() instead")]
526 pub fn into_widget_layout(
527 self,
528 area: Rect,
529 state: &mut SplitState,
530 ) -> (SplitWidget<'a>, Vec<Rect>) {
531 layout_split(&self, area, state);
532 (
533 SplitWidget {
534 split: Rc::new(self),
535 },
536 state.widget_areas.clone(),
537 )
538 }
539
540 pub fn into_widgets(self) -> (LayoutWidget<'a>, SplitWidget<'a>) {
552 let split = Rc::new(self);
553 (
554 LayoutWidget {
555 split: split.clone(), },
557 SplitWidget {
558 split, },
560 )
561 }
562}
563
564impl<'a> StatefulWidget for &Split<'a> {
565 type State = SplitState;
566
567 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
568 layout_split(self, area, state);
569 render_split(self, buf, state);
570 state.relocate_split = false;
571 }
572}
573
574impl<'a> StatefulWidget for Split<'a> {
575 type State = SplitState;
576
577 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
578 layout_split(&self, area, state);
579 render_split(&self, buf, state);
580 state.relocate_split = false;
581 }
582}
583
584impl<'a> StatefulWidget for &LayoutWidget<'a> {
585 type State = SplitState;
586
587 fn render(self, area: Rect, _buf: &mut Buffer, state: &mut Self::State) {
588 layout_split(&self.split, area, state);
590 }
591}
592
593impl<'a> StatefulWidget for LayoutWidget<'a> {
594 type State = SplitState;
595
596 fn render(self, area: Rect, _buf: &mut Buffer, state: &mut Self::State) {
597 layout_split(&self.split, area, state);
599 }
600}
601
602impl<'a> StatefulWidget for &SplitWidget<'a> {
603 type State = SplitState;
604
605 fn render(self, _area: Rect, buf: &mut Buffer, state: &mut Self::State) {
606 render_split(&self.split, buf, state);
607 }
608}
609
610impl StatefulWidget for SplitWidget<'_> {
611 type State = SplitState;
612
613 fn render(self, _area: Rect, buf: &mut Buffer, state: &mut Self::State) {
614 render_split(&self.split, buf, state);
615 }
616}
617
618fn render_split(split: &Split<'_>, buf: &mut Buffer, state: &mut SplitState) {
619 let area = state.area;
620 if state.is_focused() {
621 if state.focus_marker.is_none() {
622 state.focus_marker = Some(0);
623 }
624 } else {
625 state.focus_marker = None;
626 }
627
628 split.block.clone().render(area, buf);
629
630 for (n, split_area) in state.splitline_areas.iter().enumerate() {
631 if split.direction == Direction::Horizontal {
633 if split_area.width == 0 {
634 continue;
635 }
636 } else {
637 if split_area.height == 0 {
638 continue;
639 }
640 }
641
642 let (style, arrow_style) = if Some(n) == state.mouse.drag.get()
643 || Some(n) == state.focus_marker
644 || Some(n) == state.mouse.hover.get()
645 {
646 if let Some(drag) = split.drag_style {
647 (drag, drag)
648 } else {
649 (revert_style(split.style), revert_style(split.style))
650 }
651 } else {
652 if let Some(arrow) = split.arrow_style {
653 (split.style, arrow)
654 } else {
655 (split.style, split.style)
656 }
657 };
658
659 if let Some(fill) = get_fill_char(split) {
660 fill_buf_area(buf, *split_area, fill, style);
661 }
662
663 let mark = state.splitline_mark_position[n];
664 if split.direction == Direction::Horizontal {
665 if buf.area.contains((mark.x, mark.y).into()) {
666 if let Some(cell) = buf.cell_mut((mark.x, mark.y)) {
667 cell.set_style(arrow_style);
668 cell.set_symbol(get_mark_0(split));
669 }
670 }
671 if buf.area.contains((mark.x, mark.y + 1).into()) {
672 if let Some(cell) = buf.cell_mut((mark.x, mark.y + 1)) {
673 cell.set_style(arrow_style);
674 cell.set_symbol(get_mark_1(split));
675 }
676 }
677 } else {
678 if let Some(cell) = buf.cell_mut((mark.x, mark.y)) {
679 cell.set_style(arrow_style);
680 cell.set_symbol(get_mark_0(split));
681 }
682 if let Some(cell) = buf.cell_mut((mark.x + 1, mark.y)) {
683 cell.set_style(arrow_style);
684 cell.set_symbol(get_mark_1(split));
685 }
686 }
687
688 if let Some((pos_0, c_0)) = get_join_0(split, *split_area, state) {
689 if let Some(cell) = buf.cell_mut((pos_0.x, pos_0.y)) {
690 cell.set_symbol(c_0);
691 }
692 }
693 if let Some((pos_1, c_1)) = get_join_1(split, *split_area, state) {
694 if let Some(cell) = buf.cell_mut((pos_1.x, pos_1.y)) {
695 cell.set_symbol(c_1);
696 }
697 }
698 }
699}
700
701impl Default for SplitState {
702 fn default() -> Self {
703 Self {
704 area: Default::default(),
705 inner: Default::default(),
706 widget_areas: Default::default(),
707 splitline_areas: Default::default(),
708 splitline_mark_position: Default::default(),
709 mark_offset: Default::default(),
710 direction: Default::default(),
711 split_type: Default::default(),
712 resize: Default::default(),
713 area_length: Default::default(),
714 area_constraint: Default::default(),
715 hidden_length: Default::default(),
716 focus: Default::default(),
717 focus_marker: Default::default(),
718 mouse: Default::default(),
719 relocate_split: Default::default(),
720 non_exhaustive: NonExhaustive,
721 }
722 }
723}
724
725impl Clone for SplitState {
726 fn clone(&self) -> Self {
727 Self {
728 area: self.area,
729 inner: self.inner,
730 widget_areas: self.widget_areas.clone(),
731 splitline_areas: self.splitline_areas.clone(),
732 splitline_mark_position: self.splitline_mark_position.clone(),
733 mark_offset: self.mark_offset,
734 direction: self.direction,
735 split_type: self.split_type,
736 resize: self.resize,
737 area_length: self.area_length.clone(),
738 area_constraint: self.area_constraint.clone(),
739 hidden_length: self.hidden_length.clone(),
740 focus: self.focus.new_instance(),
741 focus_marker: self.focus_marker,
742 mouse: Default::default(),
743 relocate_split: self.relocate_split,
744 non_exhaustive: NonExhaustive,
745 }
746 }
747}
748
749impl HasFocus for SplitState {
750 fn build(&self, builder: &mut FocusBuilder) {
751 builder.leaf_widget(self);
752 }
753
754 fn focus(&self) -> FocusFlag {
755 self.focus.clone()
756 }
757
758 fn area(&self) -> Rect {
759 self.inner
760 }
761
762 fn navigable(&self) -> Navigation {
763 if self.is_focused() {
764 Navigation::Leave
765 } else {
766 Navigation::None
767 }
768 }
769}
770
771impl RelocatableState for SplitState {
772 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
773 if !self.relocate_split {
775 self.area = relocate_area(self.area, shift, clip);
776 self.inner = relocate_area(self.inner, shift, clip);
777 relocate_areas(self.widget_areas.as_mut_slice(), shift, clip);
778 relocate_areas(self.splitline_areas.as_mut_slice(), shift, clip);
779 relocate_positions(self.splitline_mark_position.as_mut_slice(), shift, clip);
780 }
781 }
782
783 fn relocate_popup(&mut self, shift: (i16, i16), clip: Rect) {
784 if self.relocate_split {
785 self.relocate_split = false;
786 self.area = relocate_area(self.area, shift, clip);
787 self.inner = relocate_area(self.inner, shift, clip);
788 relocate_areas(self.widget_areas.as_mut_slice(), shift, clip);
789 relocate_areas(self.splitline_areas.as_mut_slice(), shift, clip);
790 relocate_positions(self.splitline_mark_position.as_mut_slice(), shift, clip);
791 }
792 }
793}
794
795#[allow(clippy::len_without_is_empty)]
796impl SplitState {
797 pub fn new() -> Self {
799 Self::default()
800 }
801
802 pub fn named(name: &str) -> Self {
804 let mut z = Self::default();
805 z.focus = z.focus.with_name(name);
806 z
807 }
808
809 pub fn set_screen_split_pos(&mut self, n: usize, pos: (u16, u16)) -> bool {
816 if self.is_hidden(n) {
817 return false;
818 }
819 if self.direction == Direction::Horizontal {
820 let pos = if pos.0 < self.inner.left() {
821 0
822 } else if pos.0 < self.inner.right() {
823 pos.0 - self.inner.x
824 } else {
825 self.inner.width
826 };
827
828 let split_pos = self.split_pos(n);
829 self.set_split_pos(n, pos);
830
831 split_pos != self.split_pos(n)
832 } else {
833 let pos = if pos.1 < self.inner.top() {
834 0
835 } else if pos.1 < self.inner.bottom() {
836 pos.1 - self.inner.y
837 } else {
838 self.inner.height
839 };
840
841 let split_pos = self.split_pos(n);
842 self.set_split_pos(n, pos);
843
844 split_pos != self.split_pos(n)
845 }
846 }
847
848 pub fn move_split_left(&mut self, n: usize, delta: u16) -> bool {
852 let split_pos = self.split_pos(n);
853 self.set_split_pos(n, split_pos - delta);
854
855 split_pos != self.split_pos(n)
856 }
857
858 pub fn move_split_right(&mut self, n: usize, delta: u16) -> bool {
861 let split_pos = self.split_pos(n);
862 self.set_split_pos(n, split_pos + delta);
863
864 split_pos != self.split_pos(n)
865 }
866
867 pub fn move_split_up(&mut self, n: usize, delta: u16) -> bool {
870 self.move_split_left(n, delta)
871 }
872
873 pub fn move_split_down(&mut self, n: usize, delta: u16) -> bool {
876 self.move_split_right(n, delta)
877 }
878
879 pub fn select_next_split(&mut self) -> bool {
881 if self.is_focused() {
882 let n = self.focus_marker.unwrap_or_default();
883 if n + 1 >= self.area_length.len().saturating_sub(1) {
884 self.focus_marker = Some(0);
885 } else {
886 self.focus_marker = Some(n + 1);
887 }
888 true
889 } else {
890 false
891 }
892 }
893
894 pub fn select_prev_split(&mut self) -> bool {
896 if self.is_focused() {
897 let n = self.focus_marker.unwrap_or_default();
898 if n == 0 {
899 self.focus_marker =
900 Some(self.area_length.len().saturating_sub(1).saturating_sub(1));
901 } else {
902 self.focus_marker = Some(n - 1);
903 }
904 true
905 } else {
906 false
907 }
908 }
909
910 pub fn len(&self) -> usize {
912 self.area_length.len()
913 }
914
915 pub fn area_lengths(&self) -> &[u16] {
917 &self.area_length
918 }
919
920 pub fn set_area_lengths(&mut self, lengths: Vec<u16>) {
933 self.area_length = lengths;
934 self.area_constraint.clear();
935 while self.hidden_length.len() < self.area_length.len() {
936 self.hidden_length.push(0);
937 }
938 while self.hidden_length.len() > self.area_length.len() {
939 self.hidden_length.pop();
940 }
941 }
942
943 pub fn hidden_lengths(&self) -> &[u16] {
945 &self.hidden_length
946 }
947
948 pub fn set_hidden_lengths(&mut self, hidden: Vec<u16>) {
953 for i in 0..self.hidden_length.len() {
954 if let Some(v) = hidden.get(i) {
955 self.hidden_length[i] = *v;
956 } else {
957 self.hidden_length[i] = 0;
958 }
959 }
960 }
961
962 pub fn area_len(&self, n: usize) -> u16 {
972 if n >= self.area_length.len() {
973 return 0;
974 }
975 self.area_length[n]
976 }
977
978 pub fn total_area_len(&self) -> u16 {
980 self.area_length.iter().sum()
981 }
982
983 pub fn set_area_len(&mut self, n: usize, len: u16) {
1016 if n >= self.area_length.len() {
1017 return;
1018 }
1019 self.area_length[n] = len;
1020 self.area_constraint.clear();
1021 self.hidden_length[n] = 0;
1022 }
1023
1024 pub fn split_pos(&self, n: usize) -> u16 {
1036 if n + 1 >= self.area_length.len() {
1037 return self.area_length.iter().sum();
1038 }
1039 self.area_length[..n + 1].iter().sum()
1040 }
1041
1042 pub fn set_split_pos(&mut self, n: usize, pos: u16) {
1058 if n + 1 >= self.area_length.len() {
1059 return;
1060 }
1061
1062 match self.resize {
1063 SplitResize::Neighbours => {
1064 self.set_split_pos_neighbour(n, pos);
1065 }
1066 SplitResize::Full => {
1067 self.set_split_pos_full(n, pos);
1068 }
1069 }
1070 }
1071
1072 fn set_split_pos_neighbour(&mut self, n: usize, pos: u16) {
1075 assert!(n + 1 < self.area_length.len());
1076
1077 let mut pos_vec = Vec::new();
1079 let mut pp = 0;
1080 for len in &self.area_length {
1081 pp += *len;
1082 pos_vec.push(pp);
1083 }
1084 let pos_count = pos_vec.len();
1086
1087 let (min_pos, max_pos) = if n == 0 {
1088 if n + 2 >= pos_count {
1089 (SPLIT_WIDTH, pos_vec[n + 1])
1090 } else {
1091 (SPLIT_WIDTH, pos_vec[n + 1] - SPLIT_WIDTH)
1092 }
1093 } else if n + 2 < pos_count {
1094 (pos_vec[n - 1] + 1, pos_vec[n + 1] - SPLIT_WIDTH)
1095 } else {
1096 (pos_vec[n - 1] + 1, pos_vec[n + 1])
1097 };
1098
1099 pos_vec[n] = min(max(min_pos, pos), max_pos);
1100
1101 for i in 0..pos_vec.len() {
1103 if i > 0 {
1104 self.area_length[i] = pos_vec[i] - pos_vec[i - 1];
1105 } else {
1106 self.area_length[i] = pos_vec[i];
1107 }
1108 }
1109 self.area_constraint.clear();
1110 }
1111
1112 #[allow(clippy::needless_range_loop)]
1115 #[allow(clippy::comparison_chain)]
1116 fn set_split_pos_full(&mut self, n: usize, pos: u16) {
1117 assert!(n + 1 < self.area_length.len());
1118
1119 let total_len = self.total_area_len();
1120
1121 let mut pos_vec = Vec::new();
1123 let mut pp = 0;
1124 for len in &self.area_length {
1125 pp += *len;
1126 pos_vec.push(pp);
1127 }
1128 pos_vec.pop();
1130 let pos_count = pos_vec.len();
1131
1132 let mut min_pos = SPLIT_WIDTH;
1133 for i in 0..pos_vec.len() {
1134 if i < n {
1135 if self.area_length[i] == 0 {
1136 pos_vec[i] = min_pos;
1137 } else if self.hidden_length[i] != 0 {
1138 pos_vec[i] = min_pos;
1139 min_pos += SPLIT_WIDTH;
1140 } else {
1141 if pos_vec[i] >= pos {
1142 let rest_area_count = n - (i + 1);
1144 let rest_area_width = rest_area_count as u16 * SPLIT_WIDTH;
1145 pos_vec[i] = max(
1147 min_pos,
1148 pos.saturating_sub(SPLIT_WIDTH)
1149 .saturating_sub(rest_area_width),
1150 );
1151 min_pos += SPLIT_WIDTH;
1152 } else {
1153 }
1155 }
1156 } else if i == n {
1157 let rest_area_count = pos_count - (i + 1);
1159 let rest_area_width = rest_area_count as u16 * SPLIT_WIDTH;
1160 let rest_len = total_len - (min_pos + 1);
1161 let rest_len = rest_len - rest_area_width;
1163 let rest_len = rest_len + SPLIT_WIDTH;
1165
1166 let max_pos = min_pos + rest_len;
1167
1168 pos_vec[i] = min(max(min_pos, pos), max_pos);
1169
1170 min_pos = pos_vec[i] + SPLIT_WIDTH;
1171 } else {
1172 if self.area_length[i] == 0 {
1173 pos_vec[i] = min_pos;
1174 } else if self.hidden_length[i] != 0 {
1175 pos_vec[i] = min_pos;
1176 min_pos += SPLIT_WIDTH;
1177 } else {
1178 if pos_vec[i] <= pos {
1179 pos_vec[i] = min_pos;
1180 min_pos += SPLIT_WIDTH;
1181 } else {
1182 }
1184 }
1185 }
1186 }
1187
1188 for i in 0..pos_vec.len() {
1190 if i > 0 {
1191 self.area_length[i] = pos_vec[i] - pos_vec[i - 1];
1192 } else {
1193 self.area_length[i] = pos_vec[i];
1194 }
1195 }
1196 self.area_length[pos_count] = total_len - pos_vec[pos_count - 1];
1197 self.area_constraint.clear();
1198 }
1199
1200 pub fn is_hidden(&self, n: usize) -> bool {
1202 self.hidden_length[n] > 0
1203 }
1204
1205 pub fn hide_split(&mut self, n: usize) -> bool {
1209 if self.hidden_length[n] == 0 {
1210 self.area_constraint.clear();
1211
1212 let mut hide = if n + 1 == self.area_length.len() {
1213 self.area_length[n]
1214 } else {
1215 self.area_length[n].saturating_sub(SPLIT_WIDTH)
1216 };
1217 for idx in n + 1..self.area_length.len() {
1218 if self.hidden_length[idx] == 0 {
1219 self.area_length[idx] += hide;
1220 hide = 0;
1221 break;
1222 }
1223 }
1224 if hide > 0 {
1225 for idx in (0..n).rev() {
1226 if self.hidden_length[idx] == 0 {
1227 self.area_length[idx] += hide;
1228 hide = 0;
1229 break;
1230 }
1231 }
1232 }
1233
1234 if hide > 0 {
1235 self.hidden_length[n] = 0;
1237 false
1238 } else {
1239 if n + 1 == self.area_length.len() {
1240 self.hidden_length[n] = self.area_length[n];
1241 self.area_length[n] = 0;
1242 } else {
1243 self.hidden_length[n] = self.area_length[n].saturating_sub(SPLIT_WIDTH);
1244 self.area_length[n] = 1;
1245 };
1246 true
1247 }
1248 } else {
1249 false
1250 }
1251 }
1252
1253 pub fn show_split(&mut self, n: usize) -> bool {
1257 let mut show = self.hidden_length[n];
1258 if show > 0 {
1259 for idx in n + 1..self.area_length.len() {
1260 if self.hidden_length[idx] == 0 {
1261 if self.area_length[idx] > show + SPLIT_WIDTH {
1263 self.area_length[idx] -= show;
1264 show = 0;
1265 } else if self.area_length[idx] > SPLIT_WIDTH {
1266 show -= self.area_length[idx] - SPLIT_WIDTH;
1267 self.area_length[idx] = SPLIT_WIDTH;
1268 }
1269 if show == 0 {
1270 break;
1271 }
1272 }
1273 }
1274 if show > 0 {
1275 for idx in (0..n).rev() {
1276 if self.hidden_length[idx] == 0 {
1277 if self.area_length[idx] > show + SPLIT_WIDTH {
1278 self.area_length[idx] -= show;
1279 show = 0;
1280 } else if self.area_length[idx] > SPLIT_WIDTH {
1281 show -= self.area_length[idx] - SPLIT_WIDTH;
1282 self.area_length[idx] = SPLIT_WIDTH;
1283 }
1284 if show == 0 {
1285 break;
1286 }
1287 }
1288 }
1289 }
1290
1291 self.area_length[n] += self.hidden_length[n] - show;
1292 self.hidden_length[n] = 0;
1293 self.area_constraint.clear();
1294 true
1295 } else {
1296 false
1297 }
1298 }
1299}
1300
1301impl HandleEvent<Event, Regular, Outcome> for SplitState {
1302 fn handle(&mut self, event: &Event, _qualifier: Regular) -> Outcome {
1303 event_flow!(
1304 return if self.is_focused() {
1305 if let Some(n) = self.focus_marker {
1306 match event {
1307 ct_event!(keycode press Left) => self.select_prev_split().into(),
1308 ct_event!(keycode press Right) => self.select_next_split().into(),
1309 ct_event!(keycode press Up) => self.select_prev_split().into(),
1310 ct_event!(keycode press Down) => self.select_next_split().into(),
1311
1312 ct_event!(keycode press CONTROL-Left) => self.move_split_left(n, 1).into(),
1313 ct_event!(keycode press CONTROL-Right) => {
1314 self.move_split_right(n, 1).into()
1315 }
1316 ct_event!(keycode press CONTROL-Up) => self.move_split_up(n, 1).into(),
1317 ct_event!(keycode press CONTROL-Down) => self.move_split_down(n, 1).into(),
1318 _ => Outcome::Continue,
1319 }
1320 } else {
1321 Outcome::Continue
1322 }
1323 } else {
1324 Outcome::Continue
1325 }
1326 );
1327
1328 self.handle(event, MouseOnly)
1329 }
1330}
1331
1332impl HandleEvent<Event, MouseOnly, Outcome> for SplitState {
1333 fn handle(&mut self, event: &Event, _qualifier: MouseOnly) -> Outcome {
1334 if self.is_obscured() {
1335 return Outcome::Continue;
1336 }
1337
1338 match event {
1339 ct_event!(mouse any for m) if self.mouse.hover(&self.splitline_areas, m) => {
1340 Outcome::Changed
1341 }
1342 ct_event!(mouse any for m) => {
1343 let was_drag = self.mouse.drag.get();
1344 if self.mouse.drag(&self.splitline_areas, m) {
1345 if let Some(n) = self.mouse.drag.get() {
1346 self.set_screen_split_pos(n, self.mouse.pos_of(m)).into()
1347 } else {
1348 Outcome::Continue
1349 }
1350 } else {
1351 if was_drag.is_some() {
1353 Outcome::Changed
1354 } else {
1355 Outcome::Continue
1356 }
1357 }
1358 }
1359 _ => Outcome::Continue,
1360 }
1361 }
1362}
1363
1364pub fn handle_events(state: &mut SplitState, focus: bool, event: &Event) -> Outcome {
1368 state.focus.set(focus);
1369 HandleEvent::handle(state, event, Regular)
1370}
1371
1372pub fn handle_mouse_events(state: &mut SplitState, event: &Event) -> Outcome {
1374 HandleEvent::handle(state, event, MouseOnly)
1375}