sniper rifle cursor drawing

pull/6/head
dvkt 5 years ago
parent e7132a0bc8
commit 22c0025766

@ -2,7 +2,7 @@ use crate::gopher::{self, Type};
use crate::ui::{Action, Key, View, MAX_COLS, SCROLL_LINES};
use std::{
fmt,
io::{stdout, Write},
io::{self, stdout, Write},
};
pub struct Menu {
@ -67,7 +67,7 @@ impl Menu {
Self::parse(url, response)
}
fn _cols(&self) -> usize {
fn cols(&self) -> usize {
self.size.0
}
@ -75,6 +75,26 @@ impl Menu {
self.size.1
}
/// Calculated size of left margin.
fn indent(&self) -> usize {
let cols = self.cols();
let longest = if self.longest > MAX_COLS {
MAX_COLS
} else {
self.longest
};
if longest > cols {
0
} else {
let left = (cols - longest) / 2;
if left > 6 {
left - 6
} else {
0
}
}
}
fn link(&self, i: usize) -> Option<&Line> {
if let Some(line) = self.links.get(i) {
self.lines.get(*line)
@ -118,29 +138,16 @@ impl Menu {
}
let iter = self.lines.iter().skip(self.scroll).take(rows - 1);
let longest = if self.longest > MAX_COLS {
MAX_COLS
} else {
self.longest
};
let indent = if longest > cols {
String::from("")
} else {
let left = (cols - longest) / 2;
if left > 6 {
" ".repeat(left - 6)
} else {
String::from("")
}
};
let indent = self.indent();
let left_margin = " ".repeat(indent);
let mut line_count = 0;
for line in iter {
line_count += 1;
let mut line_size = 0;
if !self.wide {
out.push_str(&indent);
line_size += indent.len();
out.push_str(&left_margin);
line_size += indent;
}
if line.typ == Type::Info {
out.push_str(" ");
@ -199,13 +206,56 @@ impl Menu {
out
}
/// Clear and re-draw the cursor.
fn reset_cursor(&mut self) -> io::Result<()> {
if self.links.is_empty() {
return Ok(());
}
let old_link = if self.link > 0 { self.link - 1 } else { 0 };
let mut out = stdout();
if let Some(clear) = self.clear_cursor(old_link) {
out.write_all(clear.as_ref())?;
}
if let Some(cursor) = self.draw_cursor() {
out.write_all(cursor.as_ref())?;
}
out.flush()?;
Ok(())
}
/// Clear the cursor, if it's on screen.
fn clear_cursor(&self, link: usize) -> Option<String> {
if self.links.is_empty() || !self.is_visible(link) {
return None;
}
let &pos = self.links.get(link)?;
Some(format!(
"{} ",
termion::cursor::Goto(self.indent() as u16, (pos + 1) as u16)
))
}
/// Print this string to draw the cursor on screen.
/// Returns None if no is link selected.
fn draw_cursor(&self) -> Option<String> {
if self.links.is_empty() {
return None;
}
let &pos = self.links.get(self.link)?;
Some(format!(
"{}\x1b[0;1m*\x1b[0m",
termion::cursor::Goto(self.indent() as u16, (pos + 1) as u16)
))
}
/// User input field.
fn render_input(&self) -> String {
format!(
"{}Find:\x1b[0m {}{}{}",
termion::cursor::Goto(1, self.rows() as u16),
self.input,
termion::cursor::Show,
termion::clear::AfterCursor,
termion::clear::UntilNewline,
)
}
@ -303,6 +353,7 @@ impl Menu {
}
fn action_up(&mut self) -> Action {
// no links, just scroll up
if self.link == 0 {
return if self.scroll > 0 {
self.scroll -= 1;
@ -317,7 +368,8 @@ impl Menu {
}
// if text is entered, find previous match
if !self.input.is_empty() {
// TODO fix number input like this
if self.searching && !self.input.is_empty() {
if let Some(pos) = self.rlink_matching(self.link, &self.input) {
return self.action_select_link(pos);
} else {
@ -353,6 +405,9 @@ impl Menu {
if self.scroll > 0 && pos < self.scroll + 5 {
self.scroll -= 1;
}
} else if self.reset_cursor().is_ok() {
// otherwise redraw just the cursor
return Action::None;
}
}
}
@ -442,14 +497,18 @@ impl Menu {
}
}
LinkPos::Visible => {
// select next link down
self.link = new_link;
// scroll if we are within 5 lines of the end
// link is visible, so select it
if let Some(&pos) = self.links.get(self.link) {
self.link = new_link;
// scroll if we are within 5 lines of the end
if self.lines.len() >= self.rows() // dont scroll if content too small
&& pos >= self.scroll + self.rows() - 6
{
self.scroll += 1;
} else if self.reset_cursor().is_ok() {
// otherwise try to just re-draw the cursor
return Action::None;
}
}
}

Loading…
Cancel
Save