add `scroll` config option and default to entire screen

pull/33/head
chris west 1 year ago
parent 4029fca177
commit 6861848217

@ -2,6 +2,8 @@
This release adds a few new config options, for your convenience:
- `scroll` controls how many lines to jump by when paging up/down.
If set to 0 (the new default), you'll jump by an entire screen.
- `autoplay` controls whether you'll be prompted to play media files
or not. By default it's false, but one might find it handy to set
to `true` if hosting, say, a Gopher-powered music server.

@ -235,6 +235,9 @@ encoding utf8
# Wrap text at N columns. 0 = off (--wrap)
wrap 0
# How many lines to page up/down by? 0 = full screen
scroll 0
```
# MEDIA PLAYER SUPPORT

@ -55,6 +55,9 @@ encoding utf8
# Wrap text at N columns. 0 = off (--wrap)
wrap 0
# How many lines to page up/down by? 0 = full screen
scroll 0
";
/// Not all the config options are available in the phetch.conf. We
@ -82,6 +85,8 @@ pub struct Config {
pub mode: ui::Mode,
/// Column to wrap lines. 0 = off
pub wrap: usize,
/// Scroll by how many lines? 0 = full screen
pub scroll: usize,
}
impl Default for Config {
@ -97,6 +102,7 @@ impl Default for Config {
encoding: Encoding::default(),
mode: ui::Mode::default(),
wrap: 0,
scroll: 0,
}
}
}
@ -173,15 +179,23 @@ fn parse(text: &str) -> Result<Config> {
));
}
}
"scroll" => {
if let Ok(num) = val.parse() {
cfg.scroll = num;
} else {
return Err(error!(
"`scroll` expects a number value on line {}: {}",
linenum, val
));
}
}
"media" => {
cfg.media = match val.to_lowercase().as_ref() {
"false" | "none" => None,
_ => Some(val.into()),
}
}
"autoplay" => {
cfg.autoplay = to_bool(val)?
}
"autoplay" => cfg.autoplay = to_bool(val)?,
"encoding" => {
cfg.encoding = Encoding::from_str(val)
.map_err(|e| error!("{} on line {}: {:?}", e, linenum, line))?;

@ -10,7 +10,7 @@ use crate::{
config::SharedConfig as Config,
gopher::{self, Type},
terminal,
ui::{self, Action, Key, View, MAX_COLS, SCROLL_LINES},
ui::{self, Action, Key, View, MAX_COLS},
};
use std::fmt;
@ -38,8 +38,8 @@ pub struct Menu {
pub input: String,
/// UI mode. Interactive (Run), Printing, Raw mode...
pub mode: ui::Mode,
/// Scrolling offset, in rows.
pub scroll: usize,
/// Scrolling offset, in rows. 0 = full screen
pub offset: usize,
/// Incremental search mode?
pub searching: bool,
/// Was this menu retrieved via TLS?
@ -50,6 +50,8 @@ pub struct Menu {
pub size: (usize, usize),
/// Wide mode?
wide: bool,
/// Scroll by how many lines?
scroll: usize,
}
/// Represents a line in a Gopher menu. Provides the actual text of
@ -254,6 +256,7 @@ impl Menu {
tls,
tor: config.read().unwrap().tor,
wide: config.read().unwrap().wide,
scroll: config.read().unwrap().scroll,
mode: config.read().unwrap().mode,
..parse(url, response)
}
@ -287,6 +290,14 @@ impl Menu {
self.size.1
}
fn scroll_by(&self) -> usize {
if self.scroll == 0 {
self.rows() - 1
} else {
self.scroll
}
}
/// Calculated size of left margin.
fn indent(&self) -> usize {
if self.wide {
@ -318,9 +329,9 @@ impl Menu {
/// Where is the given link relative to the screen?
fn link_visibility(&self, i: usize) -> Option<LinkPos> {
let &pos = self.links.get(i)?;
Some(if pos < self.scroll {
Some(if pos < self.offset {
LinkPos::Above
} else if pos >= self.scroll + self.rows() - 1 {
} else if pos >= self.offset + self.rows() - 1 {
LinkPos::Below
} else {
LinkPos::Visible
@ -334,10 +345,10 @@ impl Menu {
}
let &pos = self.links.get(link)?;
let x = self.indent() + 1;
let y = if self.scroll > pos {
let y = if self.offset > pos {
pos + 1
} else {
pos + 1 - self.scroll
pos + 1 - self.offset
};
Some((x as u16, y as u16))
@ -352,7 +363,7 @@ impl Menu {
} else {
self.spans.len()
};
let iter = self.lines().skip(self.scroll).take(limit);
let iter = self.lines().skip(self.offset).take(limit);
let indent = self.indent();
let left_margin = " ".repeat(indent);
@ -475,7 +486,7 @@ impl Menu {
}
}
/// Scroll down by SCROLL_LINES, if possible.
/// Scroll down by a page, if possible.
fn action_page_down(&mut self) -> Action {
// If there are fewer menu items than screen lines, just
// select the final link and do nothing else.
@ -489,8 +500,8 @@ impl Menu {
// If we've already scrolled too far, select the final link
// and do nothing.
if self.scroll >= self.final_scroll() {
self.scroll = self.final_scroll();
if self.offset >= self.final_offset() {
self.offset = self.final_offset();
if !self.links.is_empty() {
self.link = self.links.len() - 1;
}
@ -498,11 +509,11 @@ impl Menu {
}
// Scroll...
self.scroll += SCROLL_LINES;
self.offset += self.scroll_by();
// ...but don't go past the final line.
if self.scroll > self.final_scroll() {
self.scroll = self.final_scroll();
if self.offset > self.final_offset() {
self.offset = self.final_offset();
}
// If the selected link isn't visible...
@ -512,7 +523,7 @@ impl Menu {
.links
.iter()
.skip(self.link + 1)
.find(|&&i| i >= self.scroll)
.find(|&&i| i >= self.offset)
{
if let Some(next_link_line) = self.line(next_link_pos) {
self.link = next_link_line.link;
@ -524,11 +535,11 @@ impl Menu {
}
fn action_page_up(&mut self) -> Action {
if self.scroll > 0 {
if self.scroll > SCROLL_LINES {
self.scroll -= SCROLL_LINES;
if self.offset > 0 {
if self.offset > self.scroll_by() {
self.offset -= self.scroll_by();
} else {
self.scroll = 0;
self.offset = 0;
}
if self.link == 0 {
return Action::Redraw;
@ -536,7 +547,7 @@ impl Menu {
if let Some(dir) = self.link_visibility(self.link) {
match dir {
LinkPos::Below => {
let scroll = self.scroll;
let scroll = self.offset;
if let Some(&pos) = self
.links
.iter()
@ -563,8 +574,8 @@ 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;
return if self.offset > 0 {
self.offset -= 1;
Action::Redraw
} else if !self.links.is_empty() {
self.link = self.links.len() - 1;
@ -589,8 +600,8 @@ impl Menu {
match dir {
LinkPos::Above => {
// scroll up by 1
if self.scroll > 0 {
self.scroll -= 1;
if self.offset > 0 {
self.offset -= 1;
}
// select it if it's visible now
if self.is_visible(new_link) {
@ -600,7 +611,7 @@ impl Menu {
LinkPos::Below => {
// jump to link....
if let Some(&pos) = self.links.get(new_link) {
self.scroll = pos;
self.offset = pos;
self.link = new_link;
}
}
@ -610,8 +621,8 @@ impl Menu {
self.link = new_link;
// scroll if we are within 5 lines of the top
if let Some(&pos) = self.links.get(self.link) {
if self.scroll > 0 && pos < self.scroll + 5 {
self.scroll -= 1;
if self.offset > 0 && pos < self.offset + 5 {
self.offset -= 1;
} else {
// otherwise redraw just the cursor
return self.reset_cursor(old_link);
@ -625,8 +636,8 @@ impl Menu {
}
}
/// Final `self.scroll` value.
fn final_scroll(&self) -> usize {
/// Final `self.offset` value.
fn final_offset(&self) -> usize {
let padding = (self.rows() as f64 * 0.9) as usize;
if self.spans.len() > padding {
self.spans.len() - padding
@ -666,13 +677,13 @@ impl Menu {
// no links or final link selected already
if self.links.is_empty() || new_link >= self.links.len() {
// if there are more rows, scroll down
if self.spans.len() >= self.rows() && self.scroll < self.final_scroll() {
self.scroll += 1;
if self.spans.len() >= self.rows() && self.offset < self.final_offset() {
self.offset += 1;
return Action::Redraw;
} else if !self.links.is_empty() {
// wrap around
self.link = 0;
self.scroll = 0;
self.offset = 0;
return Action::Redraw;
}
}
@ -692,13 +703,13 @@ impl Menu {
LinkPos::Above => {
// jump to link....
if let Some(&pos) = self.links.get(new_link) {
self.scroll = pos;
self.offset = pos;
self.link = new_link;
}
}
LinkPos::Below => {
// scroll down by 1
self.scroll += 1;
self.offset += 1;
// select it if it's visible now
if self.is_visible(new_link) {
self.link = new_link;
@ -712,9 +723,9 @@ impl Menu {
// scroll if we are within 5 lines of the end
if self.spans.len() >= self.rows() // dont scroll if content too small
&& pos >= self.scroll + self.rows() - 6
&& pos >= self.offset + self.rows() - 6
{
self.scroll += 1;
self.offset += 1;
} else {
// otherwise try to just re-draw the cursor
return self.reset_cursor(old_link);
@ -744,9 +755,9 @@ impl Menu {
}
} else {
if pos > 5 {
self.scroll = pos - 5;
self.offset = pos - 5;
} else {
self.scroll = 0;
self.offset = 0;
}
if !self.input.is_empty() {
Action::List(vec![self.redraw_input(), Action::Redraw])
@ -770,12 +781,12 @@ impl Menu {
if !self.is_visible(link) {
if let Some(&pos) = self.links.get(link) {
if pos > 5 {
self.scroll = pos - 5;
self.offset = pos - 5;
} else {
self.scroll = 0;
self.offset = 0;
}
if self.scroll > self.final_scroll() {
self.scroll = self.final_scroll();
if self.offset > self.final_offset() {
self.offset = self.final_offset();
}
return Action::Redraw;
}
@ -858,12 +869,12 @@ impl Menu {
Key::PageUp | Key::Ctrl('-') | Key::Char('-') => self.action_page_up(),
Key::PageDown | Key::Ctrl(' ') | Key::Char(' ') => self.action_page_down(),
Key::Home => {
self.scroll = 0;
self.offset = 0;
self.link = 0;
Action::Redraw
}
Key::End => {
self.scroll = self.final_scroll();
self.offset = self.final_offset();
if !self.links.is_empty() {
self.link = self.links.len() - 1;
}
@ -964,12 +975,13 @@ pub fn parse(url: &str, raw: String) -> Menu {
input: String::new(),
link: 0,
mode: Default::default(),
scroll: 0,
offset: 0,
searching: false,
size: (0, 0),
tls: false,
tor: false,
wide: false,
scroll: 0,
}
}
@ -1157,7 +1169,7 @@ i Err bitreich.org 70
assert_eq!(menu.link, 7);
assert_eq!(menu.link(menu.link).unwrap().link, 7);
assert_eq!(menu.scroll, 0);
assert_eq!(menu.offset, 0);
menu.action_page_up();
assert_eq!(menu.link, 0);
assert_eq!(menu.link(menu.link).unwrap().link, 0);

@ -6,7 +6,7 @@ use crate::{
config::SharedConfig as Config,
encoding::Encoding,
terminal,
ui::{self, Action, Key, View, MAX_COLS, SCROLL_LINES},
ui::{self, Action, Key, View, MAX_COLS},
};
use std::{borrow::Cow, fmt, str};
@ -22,7 +22,7 @@ pub struct Text {
/// Encoded response
encoded_response: String,
/// Current scroll offset, in rows
scroll: usize,
offset: usize,
/// Number of lines
lines: usize,
/// Size of longest line
@ -39,6 +39,8 @@ pub struct Text {
encoding: Encoding,
/// Currently in wide mode?
pub wide: bool,
/// How many lines to scroll by. 0 = full screen
scroll: usize,
}
impl fmt::Display for Text {
@ -83,36 +85,36 @@ impl View for Text {
fn respond(&mut self, c: Key) -> Action {
match c {
Key::Home => {
self.scroll = 0;
self.offset = 0;
Action::Redraw
}
Key::End => {
self.scroll = self.final_scroll();
self.offset = self.final_scroll();
Action::Redraw
}
Key::Ctrl('e') | Key::Char('e') => self.toggle_encoding(),
Key::Down | Key::Ctrl('n') | Key::Char('n') | Key::Ctrl('j') | Key::Char('j') => {
if self.scroll < self.final_scroll() {
self.scroll += 1;
if self.offset < self.final_scroll() {
self.offset += 1;
Action::Redraw
} else {
Action::None
}
}
Key::Up | Key::Ctrl('p') | Key::Char('p') | Key::Ctrl('k') | Key::Char('k') => {
if self.scroll > 0 {
self.scroll -= 1;
if self.offset > 0 {
self.offset -= 1;
Action::Redraw
} else {
Action::None
}
}
Key::PageUp | Key::Char('-') => {
if self.scroll > 0 {
if self.scroll >= SCROLL_LINES {
self.scroll -= SCROLL_LINES;
if self.offset > 0 {
if self.offset >= self.scroll_by() {
self.offset -= self.scroll_by();
} else {
self.scroll = 0;
self.offset = 0;
}
Action::Redraw
} else {
@ -120,9 +122,9 @@ impl View for Text {
}
}
Key::PageDown | Key::Char(' ') => {
self.scroll += SCROLL_LINES;
if self.scroll > self.final_scroll() {
self.scroll = self.final_scroll();
self.offset += self.scroll_by();
if self.offset > self.final_scroll() {
self.offset = self.final_scroll();
}
Action::Redraw
}
@ -143,7 +145,7 @@ impl View for Text {
let iter = wrap_text(&self.encoded_response, wrap)
.into_iter()
.skip(self.scroll)
.skip(self.offset)
.take(limit);
for line in iter {
@ -177,13 +179,14 @@ impl Text {
let tor = config.read().unwrap().tor;
let encoding = config.read().unwrap().encoding;
let wide = config.read().unwrap().wide;
let scroll = config.read().unwrap().scroll;
let mut new = Text {
config,
url: url.into(),
encoded_response: String::new(),
raw_response: response,
scroll: 0,
offset: 0,
lines: 0,
longest: 0,
size: (0, 0),
@ -192,6 +195,7 @@ impl Text {
tor,
encoding,
wide,
scroll,
};
new.encode_response();
new
@ -231,6 +235,15 @@ impl Text {
}
}
/// How many lines to scroll by when paging up or down.
fn scroll_by(&self) -> usize {
if self.scroll == 0 {
self.size.1 - 1
} else {
self.scroll
}
}
/// Determine the longest line, considering any line wrapping and
/// `MAX_COL`.
fn longest_line_with_wrap(&self, wrap: usize) -> usize {

@ -45,9 +45,6 @@ pub type Key = termion::event::Key;
/// Channel to receive Key events on.
pub type KeyReceiver = Arc<Mutex<Receiver<Key>>>;
/// How many lines to jump by when using page up/down.
pub const SCROLL_LINES: usize = 15;
/// How big the longest line can be, for the purposes of calculating
/// margin sizes. We often draw longer lines than this and allow
/// wrapping in text views.

Loading…
Cancel
Save