Added page scrolling and cursor selection
This commit is contained in:
parent
d96fc47476
commit
81ab27cbbf
9 changed files with 251 additions and 104 deletions
|
|
@ -13,7 +13,7 @@ pub enum Dir {
|
|||
pub enum Action {
|
||||
Char(char), // Insert a character
|
||||
Backspace, // Backspace a character
|
||||
Move(Dir), // Move the cursor
|
||||
Move(Dir, bool, bool), // Move the cursor (dir, page, retain_base)
|
||||
PaneMove(Dir), // Move panes
|
||||
Cancel, // Cancels the current context
|
||||
Go, // Search, accept, or select the current option
|
||||
|
|
@ -92,22 +92,34 @@ impl RawEvent {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_move(&self) -> Option<Dir> {
|
||||
match &self.0 {
|
||||
TerminalEvent::Key(KeyEvent {
|
||||
code,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
kind: KeyEventKind::Press | KeyEventKind::Repeat,
|
||||
..
|
||||
}) => match code {
|
||||
KeyCode::Left => Some(Dir::Left),
|
||||
KeyCode::Right => Some(Dir::Right),
|
||||
KeyCode::Up => Some(Dir::Up),
|
||||
KeyCode::Down => Some(Dir::Down),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
pub fn to_move(&self) -> Option<Action> {
|
||||
let TerminalEvent::Key(KeyEvent {
|
||||
code,
|
||||
modifiers,
|
||||
kind: KeyEventKind::Press | KeyEventKind::Repeat,
|
||||
..
|
||||
}) = &self.0
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let retain_base = match *modifiers {
|
||||
KeyModifiers::NONE => false,
|
||||
KeyModifiers::SHIFT => true,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let (dir, page) = match code {
|
||||
KeyCode::PageUp => (Dir::Up, true),
|
||||
KeyCode::PageDown => (Dir::Down, true),
|
||||
KeyCode::Left => (Dir::Left, false),
|
||||
KeyCode::Right => (Dir::Right, false),
|
||||
KeyCode::Up => (Dir::Up, false),
|
||||
KeyCode::Down => (Dir::Down, false),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(Action::Move(dir, page, retain_base))
|
||||
}
|
||||
|
||||
pub fn to_open_prompt(&self) -> Option<Action> {
|
||||
|
|
|
|||
154
src/state.rs
154
src/state.rs
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
};
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
||||
use slotmap::{HopSlotMap, new_key_type};
|
||||
use std::{io, path::PathBuf};
|
||||
use std::{io, ops::Range, path::PathBuf};
|
||||
|
||||
new_key_type! {
|
||||
pub struct BufferId;
|
||||
|
|
@ -13,6 +13,7 @@ new_key_type! {
|
|||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct Cursor {
|
||||
pub base: usize,
|
||||
pub pos: usize,
|
||||
// Used to 'remember' the desired column when skipping over shorter lines
|
||||
desired_col: isize,
|
||||
|
|
@ -22,6 +23,14 @@ impl Cursor {
|
|||
fn reset_desired_col(&mut self, text: &Text) {
|
||||
self.desired_col = text.to_coord(self.pos)[0];
|
||||
}
|
||||
|
||||
pub fn selection(&self) -> Option<Range<usize>> {
|
||||
if self.base == self.pos {
|
||||
None
|
||||
} else {
|
||||
Some(self.base.min(self.pos)..self.base.max(self.pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Text {
|
||||
|
|
@ -31,16 +40,17 @@ pub struct Text {
|
|||
impl Text {
|
||||
pub fn to_coord(&self, pos: usize) -> [isize; 2] {
|
||||
let mut n = 0;
|
||||
let mut i = 0;
|
||||
let mut last_n = 0;
|
||||
let mut i: usize = 0;
|
||||
for line in self.lines() {
|
||||
if (n..n + line.len() + 1).contains(&pos) {
|
||||
return [(pos - n) as isize, i as isize];
|
||||
} else {
|
||||
n += line.len() + 1;
|
||||
i += 1;
|
||||
last_n = n;
|
||||
i += 1;
|
||||
if (n..n + line.len()).contains(&pos) {
|
||||
break;
|
||||
}
|
||||
n += line.len();
|
||||
}
|
||||
[0, i as isize]
|
||||
[(pos - last_n) as isize, i.saturating_sub(1) as isize]
|
||||
}
|
||||
|
||||
pub fn to_pos(&self, mut coord: [isize; 2]) -> usize {
|
||||
|
|
@ -50,16 +60,41 @@ impl Text {
|
|||
let mut pos = 0;
|
||||
for (i, line) in self.lines().enumerate() {
|
||||
if i as isize == coord[1] {
|
||||
return pos + coord[0].clamp(0, line.len() as isize) as usize;
|
||||
return pos + coord[0].clamp(0, line.len().saturating_sub(1) as isize) as usize;
|
||||
} else {
|
||||
pos += line.len() + 1;
|
||||
pos += line.len();
|
||||
}
|
||||
}
|
||||
pos.min(self.chars.len())
|
||||
}
|
||||
|
||||
/// Return an iterator over the lines of the text.
|
||||
///
|
||||
/// Guarantees:
|
||||
/// - If you sum the lengths of each line, it will be the same as the length (in characters) of the text
|
||||
pub fn lines(&self) -> impl Iterator<Item = &[char]> {
|
||||
self.chars.split(|c| *c == '\n')
|
||||
let mut start = 0;
|
||||
let mut i = 0;
|
||||
let mut finished = false;
|
||||
core::iter::from_fn(move || {
|
||||
loop {
|
||||
let Some(c) = self.chars.get(i) else {
|
||||
return if finished {
|
||||
None
|
||||
} else {
|
||||
let line = &self.chars[start..];
|
||||
finished = true;
|
||||
Some(line)
|
||||
};
|
||||
};
|
||||
i += 1;
|
||||
if *c == '\n' {
|
||||
let line = &self.chars[start..i];
|
||||
start = i;
|
||||
return Some(line);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,17 +119,23 @@ impl Buffer {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn move_cursor(&mut self, cursor_id: CursorId, dir: Dir) {
|
||||
pub fn move_cursor(
|
||||
&mut self,
|
||||
cursor_id: CursorId,
|
||||
dir: Dir,
|
||||
dist: [usize; 2],
|
||||
retain_base: bool,
|
||||
) {
|
||||
let Some(cursor) = self.cursors.get_mut(cursor_id) else {
|
||||
return;
|
||||
};
|
||||
match dir {
|
||||
Dir::Left => {
|
||||
cursor.pos = cursor.pos.saturating_sub(1);
|
||||
cursor.pos = cursor.pos.saturating_sub(dist[0]);
|
||||
cursor.reset_desired_col(&self.text);
|
||||
}
|
||||
Dir::Right => {
|
||||
cursor.pos = (cursor.pos + 1).min(self.text.chars.len());
|
||||
cursor.pos = (cursor.pos + dist[0]).min(self.text.chars.len());
|
||||
cursor.reset_desired_col(&self.text);
|
||||
}
|
||||
Dir::Up => {
|
||||
|
|
@ -104,19 +145,30 @@ impl Buffer {
|
|||
cursor.pos = 0;
|
||||
cursor.reset_desired_col(&self.text);
|
||||
} else {
|
||||
cursor.pos = self.text.to_pos([cursor.desired_col, coord[1] - 1]);
|
||||
cursor.pos = self
|
||||
.text
|
||||
.to_pos([cursor.desired_col, coord[1] - dist[1] as isize]);
|
||||
}
|
||||
}
|
||||
Dir::Down => {
|
||||
let mut coord = self.text.to_coord(cursor.pos);
|
||||
cursor.pos = self.text.to_pos([cursor.desired_col, coord[1] + 1]);
|
||||
cursor.pos = self
|
||||
.text
|
||||
.to_pos([cursor.desired_col, coord[1] + dist[1] as isize]);
|
||||
}
|
||||
};
|
||||
|
||||
if !retain_base {
|
||||
cursor.base = cursor.pos;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, pos: usize, c: char) {
|
||||
self.text.chars.insert(pos.min(self.text.chars.len()), c);
|
||||
self.cursors.values_mut().for_each(|cursor| {
|
||||
if cursor.base >= pos {
|
||||
cursor.base += 1;
|
||||
}
|
||||
if cursor.pos >= pos {
|
||||
cursor.pos += 1;
|
||||
cursor.reset_desired_col(&self.text);
|
||||
|
|
@ -124,35 +176,69 @@ impl Buffer {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, pos: usize) {
|
||||
pub fn enter(&mut self, cursor_id: CursorId, c: char) {
|
||||
let Some(cursor) = self.cursors.get(cursor_id) else {
|
||||
return;
|
||||
};
|
||||
if let Some(selection) = cursor.selection() {
|
||||
self.remove(selection);
|
||||
self.enter(cursor_id, c);
|
||||
} else {
|
||||
self.insert(cursor.pos, c);
|
||||
}
|
||||
}
|
||||
|
||||
// Assumes range is well-formed
|
||||
pub fn remove(&mut self, range: Range<usize>) {
|
||||
// TODO: Bell if false?
|
||||
if self.text.chars.len() > pos {
|
||||
self.text.chars.remove(pos);
|
||||
self.cursors.values_mut().for_each(|cursor| {
|
||||
if cursor.pos >= pos {
|
||||
cursor.pos = cursor.pos.saturating_sub(1);
|
||||
cursor.reset_desired_col(&self.text);
|
||||
}
|
||||
});
|
||||
self.text.chars.drain(range.clone());
|
||||
self.cursors.values_mut().for_each(|cursor| {
|
||||
if cursor.base >= range.start {
|
||||
cursor.base = cursor
|
||||
.base
|
||||
.saturating_sub(range.end - range.start)
|
||||
.max(range.start);
|
||||
}
|
||||
if cursor.pos >= range.start {
|
||||
cursor.pos = cursor
|
||||
.pos
|
||||
.saturating_sub(range.end - range.start)
|
||||
.max(range.start);
|
||||
cursor.reset_desired_col(&self.text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn backspace(&mut self, cursor_id: CursorId) {
|
||||
let Some(cursor) = self.cursors.get(cursor_id) else {
|
||||
return;
|
||||
};
|
||||
if let Some(selection) = cursor.selection() {
|
||||
self.remove(selection);
|
||||
} else {
|
||||
if let Some(pos) = cursor.pos.checked_sub(1) {
|
||||
self.remove(pos..pos + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn backspace(&mut self, pos: usize) {
|
||||
if let Some(pos) = pos.checked_sub(1) {
|
||||
self.remove(pos);
|
||||
pub fn delete(&mut self, cursor_id: CursorId) {
|
||||
let Some(cursor) = self.cursors.get(cursor_id) else {
|
||||
return;
|
||||
};
|
||||
if let Some(selection) = cursor.selection() {
|
||||
self.remove(selection);
|
||||
} else {
|
||||
self.remove(cursor.pos..cursor.pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, pos: usize) {
|
||||
self.remove(pos);
|
||||
}
|
||||
|
||||
pub fn start_session(&mut self) -> CursorId {
|
||||
self.cursors.insert(Cursor::default())
|
||||
}
|
||||
|
||||
pub fn end_session(&mut self, cursor: CursorId) {
|
||||
self.cursors.remove(cursor);
|
||||
pub fn end_session(&mut self, cursor_id: CursorId) {
|
||||
self.cursors.remove(cursor_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -187,19 +187,20 @@ impl<'a> Rect<'a> {
|
|||
|
||||
pub fn text<C: Borrow<char>>(
|
||||
&mut self,
|
||||
origin: [usize; 2],
|
||||
origin: [isize; 2],
|
||||
text: impl IntoIterator<Item = C>,
|
||||
) -> Rect {
|
||||
for (idx, c) in text.into_iter().enumerate() {
|
||||
if origin[0] + idx >= self.size()[0] {
|
||||
break;
|
||||
} else {
|
||||
if (0..self.size()[0] as isize).contains(&(origin[0] + idx as isize)) && origin[1] >= 0
|
||||
{
|
||||
let cell = Cell {
|
||||
c: *c.borrow(),
|
||||
fg: self.fg,
|
||||
bg: self.bg,
|
||||
};
|
||||
if let Some(c) = self.get_mut([origin[0] + idx, origin[1]]) {
|
||||
if let Some(c) =
|
||||
self.get_mut([(origin[0] + idx as isize) as usize, origin[1] as usize])
|
||||
{
|
||||
*c = cell;
|
||||
}
|
||||
}
|
||||
|
|
@ -349,7 +350,12 @@ impl<'a> Terminal<'a> {
|
|||
stdout.queue(style::SetBackgroundColor(bg)).unwrap();
|
||||
}
|
||||
|
||||
stdout.queue(style::Print(self.fb[0].cells[pos].c)).unwrap();
|
||||
// Convert non-printable chars
|
||||
let c = match self.fb[0].cells[pos].c {
|
||||
c if c.is_whitespace() => ' ',
|
||||
c => c,
|
||||
};
|
||||
stdout.queue(style::Print(c)).unwrap();
|
||||
|
||||
// Move cursor
|
||||
cursor_pos[0] += 1;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ pub struct Theme {
|
|||
pub margin_line_num: Color,
|
||||
pub border: BorderTheme,
|
||||
pub focus_border: BorderTheme,
|
||||
pub text: Color,
|
||||
pub whitespace: Color,
|
||||
}
|
||||
|
||||
impl Default for Theme {
|
||||
|
|
@ -53,6 +55,8 @@ impl Default for Theme {
|
|||
fg: Color::White,
|
||||
..BorderTheme::default()
|
||||
},
|
||||
text: Color::Reset,
|
||||
whitespace: Color::AnsiValue(245),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,7 @@ impl Input {
|
|||
|
||||
impl Element for Input {
|
||||
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp, Event> {
|
||||
match event.to_action(|e| {
|
||||
e.to_char()
|
||||
.map(Action::Char)
|
||||
.or_else(|| e.to_move().map(Action::Move))
|
||||
}) {
|
||||
match event.to_action(|e| e.to_char().map(Action::Char).or_else(|| e.to_move())) {
|
||||
Some(Action::Char('\x08')) => {
|
||||
self.cursor = self.cursor.saturating_sub(1);
|
||||
if self.text.len() > self.cursor {
|
||||
|
|
@ -33,11 +29,11 @@ impl Element for Input {
|
|||
self.cursor += 1;
|
||||
Ok(Resp::handled(None))
|
||||
}
|
||||
Some(Action::Move(Dir::Left)) => {
|
||||
Some(Action::Move(Dir::Left, _, _)) => {
|
||||
self.cursor = self.cursor.saturating_sub(1);
|
||||
Ok(Resp::handled(None))
|
||||
}
|
||||
Some(Action::Move(Dir::Right)) => {
|
||||
Some(Action::Move(Dir::Right, _, _)) => {
|
||||
self.cursor = (self.cursor + 1).min(self.text.len());
|
||||
Ok(Resp::handled(None))
|
||||
}
|
||||
|
|
@ -47,7 +43,7 @@ impl Element for Input {
|
|||
}
|
||||
|
||||
impl Visual for Input {
|
||||
fn render(&self, state: &State, frame: &mut Rect) {
|
||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||
frame.with(|frame| {
|
||||
frame.fill(' ');
|
||||
frame.text([0, 0], self.preamble.chars());
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ pub trait Element<CanEnd = CannotEnd> {
|
|||
}
|
||||
|
||||
pub trait Visual {
|
||||
fn render(&self, state: &State, frame: &mut Rect);
|
||||
fn render(&mut self, state: &State, frame: &mut Rect);
|
||||
}
|
||||
|
||||
pub struct Label(String);
|
||||
|
|
@ -76,10 +76,10 @@ impl std::ops::Deref for Label {
|
|||
}
|
||||
|
||||
impl Visual for Label {
|
||||
fn render(&self, state: &State, frame: &mut Rect) {
|
||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||
frame.with_bg(state.theme.ui_bg).fill(' ').with(|frame| {
|
||||
for (idx, line) in self.lines().enumerate() {
|
||||
frame.text([0, idx], line.chars());
|
||||
frame.text([0, idx as isize], line.chars());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ pub struct Doc {
|
|||
cursors: HashMap<BufferId, CursorId>,
|
||||
// x/y location in the buffer that the pane is trying to focus on
|
||||
focus: [isize; 2],
|
||||
// Remember the last known size for things like scrolling
|
||||
last_size: [usize; 2],
|
||||
}
|
||||
|
||||
impl Doc {
|
||||
|
|
@ -23,6 +25,7 @@ impl Doc {
|
|||
.into_iter()
|
||||
.collect(),
|
||||
focus: [0, 0],
|
||||
last_size: [1, 1],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,13 +37,20 @@ impl Doc {
|
|||
return;
|
||||
};
|
||||
let cursor_coord = buffer.text.to_coord(cursor.pos);
|
||||
self.focus[0] = self.focus[0].clamp(cursor_coord[0] - 20, cursor_coord[0] - 4);
|
||||
self.focus[1] = self.focus[1].clamp(cursor_coord[1] - 20, cursor_coord[1] - 4);
|
||||
for i in 0..2 {
|
||||
self.focus[i] = self.focus[i].clamp(
|
||||
cursor_coord[i] - self.last_size[i] as isize + 1,
|
||||
cursor_coord[i],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(self, state: &mut State) {
|
||||
for (buffer, cursor) in self.cursors {
|
||||
state.buffers[buffer].end_session(cursor);
|
||||
let Some(buffer) = state.buffers.get_mut(buffer) else {
|
||||
continue;
|
||||
};
|
||||
buffer.end_session(cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +64,7 @@ impl Element for Doc {
|
|||
match event.to_action(|e| {
|
||||
e.to_char()
|
||||
.map(Action::Char)
|
||||
.or_else(|| e.to_move().map(Action::Move))
|
||||
.or_else(|| e.to_move())
|
||||
.or_else(|| e.to_pane_move().map(Action::PaneMove))
|
||||
.or_else(|| e.to_open_switcher())
|
||||
}) {
|
||||
|
|
@ -72,21 +82,24 @@ impl Element for Doc {
|
|||
Ok(Resp::handled(None))
|
||||
}
|
||||
Some(Action::Char(c)) => {
|
||||
let Some(cursor) = buffer.cursors.get(self.cursors[&self.buffer]) else {
|
||||
return Err(event);
|
||||
};
|
||||
let cursor_id = self.cursors[&self.buffer];
|
||||
if c == '\x08' {
|
||||
buffer.backspace(cursor.pos);
|
||||
buffer.backspace(cursor_id);
|
||||
} else if c == '\x7F' {
|
||||
buffer.delete(cursor.pos);
|
||||
buffer.delete(cursor_id);
|
||||
} else {
|
||||
buffer.insert(cursor.pos, c);
|
||||
buffer.enter(cursor_id, c);
|
||||
}
|
||||
self.refocus(state);
|
||||
Ok(Resp::handled(None))
|
||||
}
|
||||
Some(Action::Move(dir)) => {
|
||||
buffer.move_cursor(self.cursors[&self.buffer], dir);
|
||||
Some(Action::Move(dir, page, retain_base)) => {
|
||||
let dist = if page {
|
||||
self.last_size.map(|s| s.saturating_sub(3).max(1))
|
||||
} else {
|
||||
[1, 1]
|
||||
};
|
||||
buffer.move_cursor(self.cursors[&self.buffer], dir, dist, retain_base);
|
||||
self.refocus(state);
|
||||
Ok(Resp::handled(None))
|
||||
}
|
||||
|
|
@ -96,7 +109,7 @@ impl Element for Doc {
|
|||
}
|
||||
|
||||
impl Visual for Doc {
|
||||
fn render(&self, state: &State, frame: &mut Rect) {
|
||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||
let Some(buffer) = state.buffers.get(self.buffer) else {
|
||||
return;
|
||||
};
|
||||
|
|
@ -105,11 +118,20 @@ impl Visual for Doc {
|
|||
};
|
||||
let cursor_coord = buffer.text.to_coord(cursor.pos);
|
||||
|
||||
let line_num_w = buffer.text.lines().count().ilog10() as usize + 1;
|
||||
let line_num_w = buffer.text.lines().count().max(1).ilog10() as usize + 1;
|
||||
let margin_w = line_num_w + 2;
|
||||
|
||||
for (i, (line_num, line)) in buffer
|
||||
self.last_size = [frame.size()[0] - margin_w, frame.size()[1]];
|
||||
|
||||
let mut pos = 0;
|
||||
for (i, (line_num, (line_pos, line))) in buffer
|
||||
.text
|
||||
.lines()
|
||||
.map(move |line| {
|
||||
let line_pos = pos;
|
||||
pos += line.len();
|
||||
(line_pos, line)
|
||||
})
|
||||
.enumerate()
|
||||
.skip(self.focus[1].max(0) as usize)
|
||||
.enumerate()
|
||||
|
|
@ -117,7 +139,7 @@ impl Visual for Doc {
|
|||
{
|
||||
// Margin
|
||||
frame
|
||||
.rect([0, i], [line_num_w + 2, 1])
|
||||
.rect([0, i], [margin_w, 1])
|
||||
.with_bg(state.theme.margin_bg)
|
||||
.with_fg(state.theme.margin_line_num)
|
||||
.fill(' ')
|
||||
|
|
@ -125,14 +147,37 @@ impl Visual for Doc {
|
|||
|
||||
// Line
|
||||
{
|
||||
let mut frame = frame.rect([line_num_w + 2, i], [!0, 1]);
|
||||
frame.text([0, 0], line);
|
||||
let mut frame = frame.rect([margin_w, i], [!0, 1]);
|
||||
for i in 0..frame.size()[0] {
|
||||
let coord = self.focus[0] + i as isize;
|
||||
if (0..line.len() as isize).contains(&coord) {
|
||||
let pos = line_pos + coord as usize;
|
||||
let selected = cursor.selection().map_or(false, |s| s.contains(&pos));
|
||||
let (fg, c) = match line[coord as usize] {
|
||||
'\n' if selected => (state.theme.whitespace, '⮠'),
|
||||
c => (state.theme.text, c),
|
||||
};
|
||||
frame
|
||||
.with_bg(if selected {
|
||||
state.theme.select_bg
|
||||
} else {
|
||||
Color::Reset
|
||||
})
|
||||
.with_fg(fg)
|
||||
.text([i as isize, 0], &[c]);
|
||||
}
|
||||
}
|
||||
|
||||
// Set cursor position
|
||||
if cursor_coord[1] == line_num as isize {
|
||||
frame.set_cursor([cursor_coord[0], 0], CursorStyle::BlinkingBar);
|
||||
frame.set_cursor(
|
||||
[cursor_coord[0] - self.focus[0], 0],
|
||||
CursorStyle::BlinkingBar,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pos += line.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -200,10 +245,12 @@ impl Element for Panes {
|
|||
}
|
||||
|
||||
impl Visual for Panes {
|
||||
fn render(&self, state: &State, frame: &mut Rect) {
|
||||
for (i, pane) in self.panes.iter().enumerate() {
|
||||
let boundary = |i| frame.size()[0] * i / self.panes.len();
|
||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||
let n = self.panes.len();
|
||||
let frame_w = frame.size()[0];
|
||||
let boundary = |i| frame_w * i / n;
|
||||
|
||||
for (i, pane) in self.panes.iter_mut().enumerate() {
|
||||
let (x0, x1) = (boundary(i), boundary(i + 1));
|
||||
|
||||
let is_selected = self.selected == i;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ impl Element<CanEnd> for Prompt {
|
|||
}
|
||||
|
||||
impl Visual for Prompt {
|
||||
fn render(&self, state: &State, frame: &mut Rect) {
|
||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||
frame.with(|f| self.input.render(state, f));
|
||||
}
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ impl Element<CanEnd> for Show {
|
|||
}
|
||||
|
||||
impl Visual for Show {
|
||||
fn render(&self, state: &State, frame: &mut Rect) {
|
||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||
let lines = self.label.lines().count();
|
||||
self.label.render(
|
||||
state,
|
||||
|
|
@ -99,7 +99,7 @@ impl Element<CanEnd> for Confirm {
|
|||
}
|
||||
|
||||
impl Visual for Confirm {
|
||||
fn render(&self, state: &State, frame: &mut Rect) {
|
||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||
let lines = self.label.lines().count();
|
||||
self.label.render(
|
||||
state,
|
||||
|
|
@ -118,16 +118,12 @@ pub struct Switcher {
|
|||
|
||||
impl Element<CanEnd> for Switcher {
|
||||
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp<CanEnd>, Event> {
|
||||
match event.to_action(|e| {
|
||||
e.to_cancel()
|
||||
.or_else(|| e.to_go())
|
||||
.or_else(|| e.to_move().map(Action::Move))
|
||||
}) {
|
||||
Some(Action::Move(Dir::Up)) => {
|
||||
match event.to_action(|e| e.to_cancel().or_else(|| e.to_go()).or_else(|| e.to_move())) {
|
||||
Some(Action::Move(Dir::Up, false, _)) => {
|
||||
self.selected = (self.selected + self.options.len() - 1) % self.options.len();
|
||||
Ok(Resp::handled(None))
|
||||
}
|
||||
Some(Action::Move(Dir::Down)) => {
|
||||
Some(Action::Move(Dir::Down, false, _)) => {
|
||||
self.selected = (self.selected + 1) % self.options.len();
|
||||
Ok(Resp::handled(None))
|
||||
}
|
||||
|
|
@ -146,7 +142,7 @@ impl Element<CanEnd> for Switcher {
|
|||
}
|
||||
|
||||
impl Visual for Switcher {
|
||||
fn render(&self, state: &State, frame: &mut Rect) {
|
||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||
for (i, buffer) in self.options.iter().enumerate() {
|
||||
let Some(buffer) = state.buffers.get(*buffer) else {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ impl Element<CanEnd> for Root {
|
|||
}
|
||||
|
||||
impl Visual for Root {
|
||||
fn render(&self, state: &State, frame: &mut Rect) {
|
||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||
frame.fill(' ');
|
||||
|
||||
let task_has_focus = matches!(self.tasks.last(), Some(Task::Prompt(_)));
|
||||
|
|
@ -117,7 +117,7 @@ impl Visual for Root {
|
|||
Some("Prompt (press alt + enter)"),
|
||||
)
|
||||
.with(|frame| {
|
||||
if let Some(Task::Prompt(p)) = self.tasks.last() {
|
||||
if let Some(Task::Prompt(p)) = self.tasks.last_mut() {
|
||||
p.render(state, frame);
|
||||
}
|
||||
});
|
||||
|
|
@ -129,7 +129,7 @@ impl Visual for Root {
|
|||
self.panes.render(state, frame);
|
||||
});
|
||||
|
||||
if let Some(task) = self.tasks.last() {
|
||||
if let Some(task) = self.tasks.last_mut() {
|
||||
match task {
|
||||
Task::Prompt(_) => {} // Prompt isn't rendered, it's always rendered above
|
||||
Task::Show(s) => s.render(state, frame),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue