use crossterm::event::{self, KeyCode, KeyEvent};
use crate::script::ScriptMsg;
use crate::tdom::node::NodeArena;
use super::InputState;
use super::cmd::{RenderCmd, SelectionMode};
use super::field::{clear_text_selection, collect_input_ids};
use super::focus::focus_input;
use super::scroll_anim::{ScrollTarget, active_scroll_target};
use super::{ZOOM_MAX, ZOOM_MIN, ZOOM_STEP};
fn glide(state: &mut InputState, to: u32, max_scroll: u32, cmds: &mut Vec<RenderCmd>) -> bool {
if !state.smooth_scroll {
return false;
}
let base = active_scroll_target(state, ScrollTarget::Page).unwrap_or(state.scroll_y);
let to = to.min(max_scroll);
if to != base {
cmds.push(RenderCmd::ScrollAnimate {
target: ScrollTarget::Page,
to,
});
}
true
}
pub(super) fn page_level_builtins(
state: &mut InputState,
key: KeyEvent,
rows: u16,
max_scroll: u32,
arena: Option<&NodeArena>,
msgs: &mut Vec<ScriptMsg>,
) -> Vec<RenderCmd> {
let mut cmds = Vec::new();
if key.modifiers.contains(event::KeyModifiers::CONTROL) {
match key.code {
KeyCode::Char('=') | KeyCode::Char('+') => {
state.zoom = (state.zoom + ZOOM_STEP).min(ZOOM_MAX);
return vec![RenderCmd::Redraw {
scroll_y: state.scroll_y,
zoom: state.zoom,
}];
}
KeyCode::Char('-') | KeyCode::Char('_') => {
state.zoom = (state.zoom - ZOOM_STEP).max(ZOOM_MIN);
return vec![RenderCmd::Redraw {
scroll_y: state.scroll_y,
zoom: state.zoom,
}];
}
KeyCode::Char('0') => {
state.zoom = 1.0;
return vec![RenderCmd::Redraw {
scroll_y: state.scroll_y,
zoom: state.zoom,
}];
}
KeyCode::Char('c') | KeyCode::Char('x') => {
if state.focused_input.is_none()
&& state.text_sel_anchor.is_some()
&& match state.text_sel_mode {
SelectionMode::Flow => state.copy_flow,
SelectionMode::Block => state.copy_block,
}
&& let Ok(guard) = state.selection_text.lock()
{
state.input_clipboard = guard.clone();
}
}
KeyCode::Home => {
if glide(state, 0, u32::MAX, &mut cmds) || state.scroll_y == 0 {
return cmds;
}
state.scroll_y = 0;
return vec![RenderCmd::Rerender {
scroll_y: state.scroll_y,
}];
}
KeyCode::End => {
if glide(state, max_scroll, u32::MAX, &mut cmds) || state.scroll_y >= max_scroll {
return cmds;
}
state.scroll_y = max_scroll;
return vec![RenderCmd::Rerender {
scroll_y: state.scroll_y,
}];
}
_ => {}
}
} else if state.text_sel_anchor.is_some() {
clear_text_selection(state, &mut cmds);
}
match key.code {
KeyCode::Esc => cmds.push(RenderCmd::Quit),
KeyCode::Char('g') => {
if glide(state, 0, u32::MAX, &mut cmds) || state.scroll_y == 0 {
return cmds;
}
state.scroll_y = 0;
cmds.push(RenderCmd::Rerender {
scroll_y: state.scroll_y,
});
}
KeyCode::Char('G') => {
if glide(state, max_scroll, u32::MAX, &mut cmds) || state.scroll_y >= max_scroll {
return cmds;
}
state.scroll_y = max_scroll;
cmds.push(RenderCmd::Rerender {
scroll_y: state.scroll_y,
});
}
KeyCode::Tab => {
if let Some(arena) = arena {
let ids = collect_input_ids(arena);
if !ids.is_empty() {
let next = match state
.focused_input
.and_then(|cur| ids.iter().position(|&id| id == cur))
{
Some(p) => ids[(p + 1) % ids.len()],
None => ids[0],
};
focus_input(state, next, arena, msgs, &mut cmds);
}
}
}
KeyCode::BackTab => {
if let Some(arena) = arena {
let ids = collect_input_ids(arena);
if !ids.is_empty() {
let next = match state
.focused_input
.and_then(|cur| ids.iter().position(|&id| id == cur))
{
Some(p) => ids[(p + ids.len() - 1) % ids.len()],
None => ids[ids.len() - 1],
};
focus_input(state, next, arena, msgs, &mut cmds);
}
}
}
KeyCode::Up => {
state.scroll_anim = None;
if state.scroll_y == 0 {
return cmds;
}
state.scroll_y -= 1;
cmds.push(RenderCmd::Rerender {
scroll_y: state.scroll_y,
});
}
KeyCode::Down => {
state.scroll_anim = None;
if state.scroll_y >= max_scroll {
return cmds;
}
state.scroll_y += 1;
cmds.push(RenderCmd::Rerender {
scroll_y: state.scroll_y,
});
}
KeyCode::PageUp => {
let step = (rows as u32).saturating_sub(1).max(1);
let base = active_scroll_target(state, ScrollTarget::Page).unwrap_or(state.scroll_y);
if glide(state, base.saturating_sub(step), u32::MAX, &mut cmds) || state.scroll_y == 0 {
return cmds;
}
state.scroll_y = state.scroll_y.saturating_sub(step);
cmds.push(RenderCmd::Rerender {
scroll_y: state.scroll_y,
});
}
KeyCode::PageDown => {
let step = (rows as u32).saturating_sub(1).max(1);
let base = active_scroll_target(state, ScrollTarget::Page).unwrap_or(state.scroll_y);
if glide(state, base.saturating_add(step), max_scroll, &mut cmds)
|| state.scroll_y >= max_scroll
{
return cmds;
}
state.scroll_y = state.scroll_y.saturating_add(step);
cmds.push(RenderCmd::Rerender {
scroll_y: state.scroll_y,
});
}
KeyCode::Home => {
if glide(state, 0, u32::MAX, &mut cmds) || state.scroll_y == 0 {
return cmds;
}
state.scroll_y = 0;
cmds.push(RenderCmd::Rerender {
scroll_y: state.scroll_y,
});
}
KeyCode::End => {
if glide(state, max_scroll, u32::MAX, &mut cmds) || state.scroll_y >= max_scroll {
return cmds;
}
state.scroll_y = max_scroll;
cmds.push(RenderCmd::Rerender {
scroll_y: state.scroll_y,
});
}
_ => {}
}
cmds
}