use crate::term::Screen;
pub struct CopyState {
snapshot: Screen,
start: Pos,
end: Option<Pos>,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Pos {
pub y: i32,
pub x: i32,
}
#[derive(Clone, Copy, Debug)]
pub enum CopyMove {
Up,
Down,
Left,
Right,
}
impl CopyState {
pub fn new(screen: Screen) -> Self {
let y = (screen.size().height as i32 - 1).max(0);
CopyState {
snapshot: screen,
start: Pos { y, x: 0 },
end: None,
}
}
pub fn snapshot(&self) -> &Screen {
&self.snapshot
}
pub fn start(&self) -> Pos {
self.start
}
pub fn end(&self) -> Option<Pos> {
self.end
}
pub fn cursor(&self) -> Pos {
self.end.unwrap_or(self.start)
}
fn cursor_mut(&mut self) -> &mut Pos {
self.end.as_mut().unwrap_or(&mut self.start)
}
pub fn move_cursor(&mut self, dir: CopyMove) {
let width = self.snapshot.size().width as i32;
let height = self.snapshot.size().height as i32;
let scrollback_len = self.snapshot.scrollback_len() as i32;
let pos = self.cursor_mut();
match dir {
CopyMove::Up => {
if pos.y > -scrollback_len {
pos.y -= 1;
}
}
CopyMove::Down => {
if pos.y + 1 < height {
pos.y += 1;
}
}
CopyMove::Left => {
if pos.x > 0 {
pos.x -= 1;
}
}
CopyMove::Right => {
if pos.x + 1 < width {
pos.x += 1;
}
}
}
}
pub fn begin_selection(&mut self) {
if self.end.is_none() {
self.end = Some(self.start);
}
}
pub fn pos_at(&self, row: u16, col: u16) -> Pos {
Pos {
y: row as i32 - self.snapshot.scrollback() as i32,
x: col as i32,
}
}
pub fn set_anchor(&mut self, pos: Pos) {
self.start = pos;
self.end = None;
}
pub fn set_extent(&mut self, pos: Pos) {
self.end = Some(pos);
}
pub fn scroll_up(&mut self, n: usize) {
self.snapshot.scroll_screen_up(n);
}
pub fn scroll_down(&mut self, n: usize) {
self.snapshot.scroll_screen_down(n);
}
pub fn scrollback(&self) -> usize {
self.snapshot.scrollback()
}
pub fn selected_text(&self) -> Option<String> {
let end = self.end?;
let (low, high) = Pos::to_low_high(self.start, end);
Some(
self
.snapshot
.get_selected_text(low.x, low.y, high.x, high.y),
)
}
}
impl Pos {
pub fn to_low_high(a: Pos, b: Pos) -> (Pos, Pos) {
if a.y < b.y || (a.y == b.y && a.x < b.x) {
(a, b)
} else {
(b, a)
}
}
pub fn within(start: Pos, end: Pos, target: Pos) -> bool {
let (low, high) = Pos::to_low_high(start, end);
let Pos { y, x } = target;
if y > low.y {
y < high.y || (y == high.y && x <= high.x)
} else if y == low.y {
if y < high.y {
x >= low.x
} else if y == high.y {
x >= low.x && x <= high.x
} else {
false
}
} else {
false
}
}
}