use crate::_private::NonExhaustive;
use crate::splitter::split_impl::{get_fill_char, get_join_0, get_join_1, get_mark_0, get_mark_1};
use crate::splitter::split_layout::layout_split;
use crate::util::{fill_buf_area, revert_style};
use rat_event::util::MouseFlagsN;
use rat_event::{HandleEvent, MouseOnly, Outcome, Regular, ct_event, event_flow};
use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
use rat_reloc::{RelocatableState, relocate_area, relocate_areas, relocate_positions};
use ratatui_core::buffer::Buffer;
use ratatui_core::layout::{Constraint, Direction, Position, Rect};
use ratatui_core::style::Style;
use ratatui_core::widgets::{StatefulWidget, Widget};
use ratatui_crossterm::crossterm::event::Event;
use ratatui_widgets::block::Block;
use ratatui_widgets::borders::BorderType;
use std::cmp::{max, min};
use std::iter;
use std::rc::Rc;
use unicode_segmentation::UnicodeSegmentation;
mod split_impl;
mod split_layout;
#[derive(Debug, Default, Clone)]
pub struct Split<'a> {
direction: Direction,
constraints: Vec<Constraint>,
resize_constraints: Vec<ResizeConstraint>,
resize: SplitResize,
split_type: SplitType,
join_0: Option<BorderType>,
join_1: Option<BorderType>,
mark_offset: u16,
mark_0_char: Option<&'a str>,
mark_1_char: Option<&'a str>,
style: Style,
block: Option<Block<'a>>,
arrow_style: Option<Style>,
drag_style: Option<Style>,
}
#[derive(Debug, Clone)]
pub struct LayoutWidget<'a> {
split: Rc<Split<'a>>,
}
#[derive(Debug, Clone)]
pub struct SplitWidget<'a> {
split: Rc<Split<'a>>,
}
#[derive(Debug, Clone)]
pub struct SplitStyle {
pub style: Style,
pub block: Option<Block<'static>>,
pub border_style: Option<Style>,
pub title_style: Option<Style>,
pub arrow_style: Option<Style>,
pub drag_style: Option<Style>,
pub horizontal_mark: Option<&'static str>,
pub vertical_mark: Option<&'static str>,
pub non_exhaustive: NonExhaustive,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
pub enum SplitType {
#[default]
FullEmpty,
FullPlain,
FullDouble,
FullThick,
FullQuadrantInside,
FullQuadrantOutside,
Scroll,
Widget,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum SplitResize {
Neighbours,
#[default]
Full,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum ResizeConstraint {
Fixed,
#[default]
ScaleProportional,
ScaleEqual,
}
const SPLIT_WIDTH: u16 = 1;
#[derive(Debug)]
pub struct SplitState {
pub area: Rect,
pub inner: Rect,
pub widget_areas: Vec<Rect>,
pub splitline_areas: Vec<Rect>,
pub splitline_mark_position: Vec<Position>,
pub mark_offset: u16,
pub direction: Direction,
pub split_type: SplitType,
pub resize: SplitResize,
area_length: Vec<u16>,
area_constraint: Vec<Constraint>,
hidden_length: Vec<u16>,
pub focus: FocusFlag,
pub focus_marker: Option<usize>,
pub mouse: MouseFlagsN,
relocate_split: bool,
pub non_exhaustive: NonExhaustive,
}
impl SplitType {
pub fn is_full(&self) -> bool {
use SplitType::*;
match self {
FullEmpty => true,
FullPlain => true,
FullDouble => true,
FullThick => true,
FullQuadrantInside => true,
FullQuadrantOutside => true,
Scroll => false,
Widget => false,
}
}
}
impl Default for SplitStyle {
fn default() -> Self {
Self {
style: Default::default(),
block: Default::default(),
border_style: Default::default(),
title_style: Default::default(),
arrow_style: Default::default(),
drag_style: Default::default(),
horizontal_mark: Default::default(),
vertical_mark: Default::default(),
non_exhaustive: NonExhaustive,
}
}
}
impl<'a> Split<'a> {
pub fn new() -> Self {
Self {
direction: Direction::Horizontal,
..Default::default()
}
}
pub fn horizontal() -> Self {
Self {
direction: Direction::Horizontal,
..Default::default()
}
}
pub fn vertical() -> Self {
Self {
direction: Direction::Horizontal,
..Default::default()
}
}
pub fn constraints(mut self, constraints: impl IntoIterator<Item = Constraint>) -> Self {
self.constraints = constraints.into_iter().collect();
self.resize_constraints = iter::from_fn(|| Some(ResizeConstraint::ScaleProportional))
.take(self.constraints.len())
.collect();
self
}
pub fn resize_constraint(mut self, n: usize, constraint: ResizeConstraint) -> Self {
self.resize_constraints[n] = constraint;
self
}
pub fn direction(mut self, direction: Direction) -> Self {
self.direction = direction;
self
}
pub fn split_type(mut self, split_type: SplitType) -> Self {
self.split_type = split_type;
self
}
pub fn resize(mut self, resize: SplitResize) -> Self {
self.resize = resize;
self
}
pub fn join(mut self, border: BorderType) -> Self {
self.join_0 = Some(border);
self.join_1 = Some(border);
self
}
pub fn join_0(mut self, border: BorderType) -> Self {
self.join_0 = Some(border);
self
}
pub fn join_1(mut self, border: BorderType) -> Self {
self.join_1 = Some(border);
self
}
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);
self
}
pub fn styles(mut self, styles: SplitStyle) -> Self {
self.style = styles.style;
if styles.block.is_some() {
self.block = styles.block;
}
if let Some(border_style) = styles.border_style {
self.block = self.block.map(|v| v.border_style(border_style));
}
if let Some(title_style) = styles.title_style {
self.block = self.block.map(|v| v.title_style(title_style));
}
self.block = self.block.map(|v| v.style(self.style));
if styles.drag_style.is_some() {
self.drag_style = styles.drag_style;
}
if styles.arrow_style.is_some() {
self.arrow_style = styles.arrow_style;
}
match self.direction {
Direction::Horizontal => {
if let Some(mark) = styles.horizontal_mark {
let mut g = mark.graphemes(true);
if let Some(g0) = g.next() {
self.mark_0_char = Some(g0);
}
if let Some(g1) = g.next() {
self.mark_1_char = Some(g1);
}
}
}
Direction::Vertical => {
if let Some(mark) = styles.vertical_mark {
let mut g = mark.graphemes(true);
if let Some(g0) = g.next() {
self.mark_0_char = Some(g0);
}
if let Some(g1) = g.next() {
self.mark_1_char = Some(g1);
}
}
}
}
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self.block = self.block.map(|v| v.style(style));
self
}
pub fn arrow_style(mut self, style: Style) -> Self {
self.arrow_style = Some(style);
self
}
pub fn drag_style(mut self, style: Style) -> Self {
self.drag_style = Some(style);
self
}
pub fn mark_offset(mut self, offset: u16) -> Self {
self.mark_offset = offset;
self
}
pub fn mark_0(mut self, mark: &'a str) -> Self {
self.mark_0_char = Some(mark);
self
}
pub fn mark_1(mut self, mark: &'a str) -> Self {
self.mark_1_char = Some(mark);
self
}
#[deprecated(since = "2.4.0", note = "use into_widgets() instead")]
pub fn into_widget(self, area: Rect, state: &mut SplitState) -> SplitWidget<'a> {
layout_split(&self, area, state);
SplitWidget {
split: Rc::new(self),
}
}
#[deprecated(since = "2.4.0", note = "use into_widgets() instead")]
pub fn into_widget_layout(
self,
area: Rect,
state: &mut SplitState,
) -> (SplitWidget<'a>, Vec<Rect>) {
layout_split(&self, area, state);
(
SplitWidget {
split: Rc::new(self),
},
state.widget_areas.clone(),
)
}
pub fn into_widgets(self) -> (LayoutWidget<'a>, SplitWidget<'a>) {
let split = Rc::new(self);
(
LayoutWidget {
split: split.clone(), },
SplitWidget {
split, },
)
}
}
impl<'a> StatefulWidget for &Split<'a> {
type State = SplitState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
layout_split(self, area, state);
render_split(self, buf, state);
state.relocate_split = false;
}
}
impl<'a> StatefulWidget for Split<'a> {
type State = SplitState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
layout_split(&self, area, state);
render_split(&self, buf, state);
state.relocate_split = false;
}
}
impl<'a> StatefulWidget for &LayoutWidget<'a> {
type State = SplitState;
fn render(self, area: Rect, _buf: &mut Buffer, state: &mut Self::State) {
layout_split(&self.split, area, state);
}
}
impl<'a> StatefulWidget for LayoutWidget<'a> {
type State = SplitState;
fn render(self, area: Rect, _buf: &mut Buffer, state: &mut Self::State) {
layout_split(&self.split, area, state);
}
}
impl<'a> StatefulWidget for &SplitWidget<'a> {
type State = SplitState;
fn render(self, _area: Rect, buf: &mut Buffer, state: &mut Self::State) {
render_split(&self.split, buf, state);
}
}
impl StatefulWidget for SplitWidget<'_> {
type State = SplitState;
fn render(self, _area: Rect, buf: &mut Buffer, state: &mut Self::State) {
render_split(&self.split, buf, state);
}
}
fn render_split(split: &Split<'_>, buf: &mut Buffer, state: &mut SplitState) {
let area = state.area;
if state.is_focused() {
if state.focus_marker.is_none() {
state.focus_marker = Some(0);
}
} else {
state.focus_marker = None;
}
split.block.clone().render(area, buf);
for (n, split_area) in state.splitline_areas.iter().enumerate() {
if split.direction == Direction::Horizontal {
if split_area.width == 0 {
continue;
}
} else {
if split_area.height == 0 {
continue;
}
}
let (style, arrow_style) = if Some(n) == state.mouse.drag.get()
|| Some(n) == state.focus_marker
|| Some(n) == state.mouse.hover.get()
{
if let Some(drag) = split.drag_style {
(drag, drag)
} else {
(revert_style(split.style), revert_style(split.style))
}
} else {
if let Some(arrow) = split.arrow_style {
(split.style, arrow)
} else {
(split.style, split.style)
}
};
if let Some(fill) = get_fill_char(split) {
fill_buf_area(buf, *split_area, fill, style);
}
let mark = state.splitline_mark_position[n];
if split.direction == Direction::Horizontal {
if buf.area.contains((mark.x, mark.y).into()) {
if let Some(cell) = buf.cell_mut((mark.x, mark.y)) {
cell.set_style(arrow_style);
cell.set_symbol(get_mark_0(split));
}
}
if buf.area.contains((mark.x, mark.y + 1).into()) {
if let Some(cell) = buf.cell_mut((mark.x, mark.y + 1)) {
cell.set_style(arrow_style);
cell.set_symbol(get_mark_1(split));
}
}
} else {
if let Some(cell) = buf.cell_mut((mark.x, mark.y)) {
cell.set_style(arrow_style);
cell.set_symbol(get_mark_0(split));
}
if let Some(cell) = buf.cell_mut((mark.x + 1, mark.y)) {
cell.set_style(arrow_style);
cell.set_symbol(get_mark_1(split));
}
}
if let Some((pos_0, c_0)) = get_join_0(split, *split_area, state) {
if let Some(cell) = buf.cell_mut((pos_0.x, pos_0.y)) {
cell.set_symbol(c_0);
}
}
if let Some((pos_1, c_1)) = get_join_1(split, *split_area, state) {
if let Some(cell) = buf.cell_mut((pos_1.x, pos_1.y)) {
cell.set_symbol(c_1);
}
}
}
}
impl Default for SplitState {
fn default() -> Self {
Self {
area: Default::default(),
inner: Default::default(),
widget_areas: Default::default(),
splitline_areas: Default::default(),
splitline_mark_position: Default::default(),
mark_offset: Default::default(),
direction: Default::default(),
split_type: Default::default(),
resize: Default::default(),
area_length: Default::default(),
area_constraint: Default::default(),
hidden_length: Default::default(),
focus: Default::default(),
focus_marker: Default::default(),
mouse: Default::default(),
relocate_split: Default::default(),
non_exhaustive: NonExhaustive,
}
}
}
impl Clone for SplitState {
fn clone(&self) -> Self {
Self {
area: self.area,
inner: self.inner,
widget_areas: self.widget_areas.clone(),
splitline_areas: self.splitline_areas.clone(),
splitline_mark_position: self.splitline_mark_position.clone(),
mark_offset: self.mark_offset,
direction: self.direction,
split_type: self.split_type,
resize: self.resize,
area_length: self.area_length.clone(),
area_constraint: self.area_constraint.clone(),
hidden_length: self.hidden_length.clone(),
focus: self.focus.new_instance(),
focus_marker: self.focus_marker,
mouse: Default::default(),
relocate_split: self.relocate_split,
non_exhaustive: NonExhaustive,
}
}
}
impl HasFocus for SplitState {
fn build(&self, builder: &mut FocusBuilder) {
builder.leaf_widget(self);
}
fn focus(&self) -> FocusFlag {
self.focus.clone()
}
fn area(&self) -> Rect {
Rect::default()
}
fn navigable(&self) -> Navigation {
Navigation::Leave
}
}
impl RelocatableState for SplitState {
fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
if !self.relocate_split {
self.area = relocate_area(self.area, shift, clip);
self.inner = relocate_area(self.inner, shift, clip);
relocate_areas(self.widget_areas.as_mut_slice(), shift, clip);
relocate_areas(self.splitline_areas.as_mut_slice(), shift, clip);
relocate_positions(self.splitline_mark_position.as_mut_slice(), shift, clip);
}
}
fn relocate_popup(&mut self, shift: (i16, i16), clip: Rect) {
if self.relocate_split {
self.relocate_split = false;
self.area = relocate_area(self.area, shift, clip);
self.inner = relocate_area(self.inner, shift, clip);
relocate_areas(self.widget_areas.as_mut_slice(), shift, clip);
relocate_areas(self.splitline_areas.as_mut_slice(), shift, clip);
relocate_positions(self.splitline_mark_position.as_mut_slice(), shift, clip);
}
}
}
#[allow(clippy::len_without_is_empty)]
impl SplitState {
pub fn new() -> Self {
Self::default()
}
pub fn named(name: &str) -> Self {
let mut z = Self::default();
z.focus = z.focus.with_name(name);
z
}
pub fn set_screen_split_pos(&mut self, n: usize, pos: (u16, u16)) -> bool {
if self.is_hidden(n) {
return false;
}
if self.direction == Direction::Horizontal {
let pos = if pos.0 < self.inner.left() {
0
} else if pos.0 < self.inner.right() {
pos.0 - self.inner.x
} else {
self.inner.width
};
let split_pos = self.split_pos(n);
self.set_split_pos(n, pos);
split_pos != self.split_pos(n)
} else {
let pos = if pos.1 < self.inner.top() {
0
} else if pos.1 < self.inner.bottom() {
pos.1 - self.inner.y
} else {
self.inner.height
};
let split_pos = self.split_pos(n);
self.set_split_pos(n, pos);
split_pos != self.split_pos(n)
}
}
pub fn move_split_left(&mut self, n: usize, delta: u16) -> bool {
let split_pos = self.split_pos(n);
self.set_split_pos(n, split_pos - delta);
split_pos != self.split_pos(n)
}
pub fn move_split_right(&mut self, n: usize, delta: u16) -> bool {
let split_pos = self.split_pos(n);
self.set_split_pos(n, split_pos + delta);
split_pos != self.split_pos(n)
}
pub fn move_split_up(&mut self, n: usize, delta: u16) -> bool {
self.move_split_left(n, delta)
}
pub fn move_split_down(&mut self, n: usize, delta: u16) -> bool {
self.move_split_right(n, delta)
}
pub fn select_next_split(&mut self) -> bool {
if self.is_focused() {
let n = self.focus_marker.unwrap_or_default();
if n + 1 >= self.area_length.len().saturating_sub(1) {
self.focus_marker = Some(0);
} else {
self.focus_marker = Some(n + 1);
}
true
} else {
false
}
}
pub fn select_prev_split(&mut self) -> bool {
if self.is_focused() {
let n = self.focus_marker.unwrap_or_default();
if n == 0 {
self.focus_marker =
Some(self.area_length.len().saturating_sub(1).saturating_sub(1));
} else {
self.focus_marker = Some(n - 1);
}
true
} else {
false
}
}
pub fn len(&self) -> usize {
self.area_length.len()
}
pub fn area_lengths(&self) -> &[u16] {
&self.area_length
}
pub fn set_area_lengths(&mut self, lengths: Vec<u16>) {
self.area_length = lengths;
self.area_constraint.clear();
while self.hidden_length.len() < self.area_length.len() {
self.hidden_length.push(0);
}
while self.hidden_length.len() > self.area_length.len() {
self.hidden_length.pop();
}
}
pub fn hidden_lengths(&self) -> &[u16] {
&self.hidden_length
}
pub fn set_hidden_lengths(&mut self, hidden: Vec<u16>) {
for i in 0..self.hidden_length.len() {
if let Some(v) = hidden.get(i) {
self.hidden_length[i] = *v;
} else {
self.hidden_length[i] = 0;
}
}
}
pub fn area_len(&self, n: usize) -> u16 {
if n >= self.area_length.len() {
return 0;
}
self.area_length[n]
}
pub fn total_area_len(&self) -> u16 {
self.area_length.iter().sum()
}
pub fn set_area_len(&mut self, n: usize, len: u16) {
if n >= self.area_length.len() {
return;
}
self.area_length[n] = len;
self.area_constraint.clear();
self.hidden_length[n] = 0;
}
pub fn split_pos(&self, n: usize) -> u16 {
if n + 1 >= self.area_length.len() {
return self.area_length.iter().sum();
}
self.area_length[..n + 1].iter().sum()
}
pub fn set_split_pos(&mut self, n: usize, pos: u16) {
if n + 1 >= self.area_length.len() {
return;
}
match self.resize {
SplitResize::Neighbours => {
self.set_split_pos_neighbour(n, pos);
}
SplitResize::Full => {
self.set_split_pos_full(n, pos);
}
}
}
fn set_split_pos_neighbour(&mut self, n: usize, pos: u16) {
assert!(n + 1 < self.area_length.len());
let mut pos_vec = Vec::new();
let mut pp = 0;
for len in &self.area_length {
pp += *len;
pos_vec.push(pp);
}
let pos_count = pos_vec.len();
let (min_pos, max_pos) = if n == 0 {
if n + 2 >= pos_count {
(SPLIT_WIDTH, pos_vec[n + 1])
} else {
(SPLIT_WIDTH, pos_vec[n + 1] - SPLIT_WIDTH)
}
} else if n + 2 < pos_count {
(pos_vec[n - 1] + 1, pos_vec[n + 1] - SPLIT_WIDTH)
} else {
(pos_vec[n - 1] + 1, pos_vec[n + 1])
};
pos_vec[n] = min(max(min_pos, pos), max_pos);
for i in 0..pos_vec.len() {
if i > 0 {
self.area_length[i] = pos_vec[i] - pos_vec[i - 1];
} else {
self.area_length[i] = pos_vec[i];
}
}
self.area_constraint.clear();
}
#[allow(clippy::needless_range_loop)]
#[allow(clippy::comparison_chain)]
fn set_split_pos_full(&mut self, n: usize, pos: u16) {
assert!(n + 1 < self.area_length.len());
let total_len = self.total_area_len();
let mut pos_vec = Vec::new();
let mut pp = 0;
for len in &self.area_length {
pp += *len;
pos_vec.push(pp);
}
pos_vec.pop();
let pos_count = pos_vec.len();
let mut min_pos = SPLIT_WIDTH;
for i in 0..pos_vec.len() {
if i < n {
if self.area_length[i] == 0 {
pos_vec[i] = min_pos;
} else if self.hidden_length[i] != 0 {
pos_vec[i] = min_pos;
min_pos += SPLIT_WIDTH;
} else {
if pos_vec[i] >= pos {
let rest_area_count = n - (i + 1);
let rest_area_width = rest_area_count as u16 * SPLIT_WIDTH;
pos_vec[i] = max(
min_pos,
pos.saturating_sub(SPLIT_WIDTH)
.saturating_sub(rest_area_width),
);
min_pos += SPLIT_WIDTH;
} else {
}
}
} else if i == n {
let rest_area_count = pos_count - (i + 1);
let rest_area_width = rest_area_count as u16 * SPLIT_WIDTH;
let rest_len = total_len - (min_pos + 1);
let rest_len = rest_len - rest_area_width;
let rest_len = rest_len + SPLIT_WIDTH;
let max_pos = min_pos + rest_len;
pos_vec[i] = min(max(min_pos, pos), max_pos);
min_pos = pos_vec[i] + SPLIT_WIDTH;
} else {
if self.area_length[i] == 0 {
pos_vec[i] = min_pos;
} else if self.hidden_length[i] != 0 {
pos_vec[i] = min_pos;
min_pos += SPLIT_WIDTH;
} else {
if pos_vec[i] <= pos {
pos_vec[i] = min_pos;
min_pos += SPLIT_WIDTH;
} else {
}
}
}
}
for i in 0..pos_vec.len() {
if i > 0 {
self.area_length[i] = pos_vec[i] - pos_vec[i - 1];
} else {
self.area_length[i] = pos_vec[i];
}
}
self.area_length[pos_count] = total_len - pos_vec[pos_count - 1];
self.area_constraint.clear();
}
pub fn is_hidden(&self, n: usize) -> bool {
self.hidden_length[n] > 0
}
pub fn hide_split(&mut self, n: usize) -> bool {
if self.hidden_length[n] == 0 {
self.area_constraint.clear();
let mut hide = if n + 1 == self.area_length.len() {
self.area_length[n]
} else {
self.area_length[n].saturating_sub(SPLIT_WIDTH)
};
for idx in n + 1..self.area_length.len() {
if self.hidden_length[idx] == 0 {
self.area_length[idx] += hide;
hide = 0;
break;
}
}
if hide > 0 {
for idx in (0..n).rev() {
if self.hidden_length[idx] == 0 {
self.area_length[idx] += hide;
hide = 0;
break;
}
}
}
if hide > 0 {
self.hidden_length[n] = 0;
false
} else {
if n + 1 == self.area_length.len() {
self.hidden_length[n] = self.area_length[n];
self.area_length[n] = 0;
} else {
self.hidden_length[n] = self.area_length[n].saturating_sub(SPLIT_WIDTH);
self.area_length[n] = 1;
};
true
}
} else {
false
}
}
pub fn show_split(&mut self, n: usize) -> bool {
let mut show = self.hidden_length[n];
if show > 0 {
for idx in n + 1..self.area_length.len() {
if self.hidden_length[idx] == 0 {
if self.area_length[idx] > show + SPLIT_WIDTH {
self.area_length[idx] -= show;
show = 0;
} else if self.area_length[idx] > SPLIT_WIDTH {
show -= self.area_length[idx] - SPLIT_WIDTH;
self.area_length[idx] = SPLIT_WIDTH;
}
if show == 0 {
break;
}
}
}
if show > 0 {
for idx in (0..n).rev() {
if self.hidden_length[idx] == 0 {
if self.area_length[idx] > show + SPLIT_WIDTH {
self.area_length[idx] -= show;
show = 0;
} else if self.area_length[idx] > SPLIT_WIDTH {
show -= self.area_length[idx] - SPLIT_WIDTH;
self.area_length[idx] = SPLIT_WIDTH;
}
if show == 0 {
break;
}
}
}
}
self.area_length[n] += self.hidden_length[n] - show;
self.hidden_length[n] = 0;
self.area_constraint.clear();
true
} else {
false
}
}
}
impl HandleEvent<Event, Regular, Outcome> for SplitState {
fn handle(&mut self, event: &Event, _qualifier: Regular) -> Outcome {
event_flow!(
return if self.is_focused() {
if let Some(n) = self.focus_marker {
match event {
ct_event!(keycode press Left) => self.select_prev_split().into(),
ct_event!(keycode press Right) => self.select_next_split().into(),
ct_event!(keycode press Up) => self.select_prev_split().into(),
ct_event!(keycode press Down) => self.select_next_split().into(),
ct_event!(keycode press CONTROL-Left) => self.move_split_left(n, 1).into(),
ct_event!(keycode press CONTROL-Right) => {
self.move_split_right(n, 1).into()
}
ct_event!(keycode press CONTROL-Up) => self.move_split_up(n, 1).into(),
ct_event!(keycode press CONTROL-Down) => self.move_split_down(n, 1).into(),
_ => Outcome::Continue,
}
} else {
Outcome::Continue
}
} else {
Outcome::Continue
}
);
self.handle(event, MouseOnly)
}
}
impl HandleEvent<Event, MouseOnly, Outcome> for SplitState {
fn handle(&mut self, event: &Event, _qualifier: MouseOnly) -> Outcome {
if !self.has_mouse_focus() {
return Outcome::Continue;
}
match event {
ct_event!(mouse any for m) if self.mouse.hover(&self.splitline_areas, m) => {
Outcome::Changed
}
ct_event!(mouse any for m) => {
let was_drag = self.mouse.drag.get();
if self.mouse.drag(&self.splitline_areas, m) {
if let Some(n) = self.mouse.drag.get() {
self.set_screen_split_pos(n, self.mouse.pos_of(m)).into()
} else {
Outcome::Continue
}
} else {
if was_drag.is_some() {
Outcome::Changed
} else {
Outcome::Continue
}
}
}
_ => Outcome::Continue,
}
}
}
pub fn handle_events(state: &mut SplitState, focus: bool, event: &Event) -> Outcome {
state.focus.set(focus);
HandleEvent::handle(state, event, Regular)
}
pub fn handle_mouse_events(state: &mut SplitState, event: &Event) -> Outcome {
HandleEvent::handle(state, event, MouseOnly)
}