2021-03-18 01:55:31 +01:00
|
|
|
"""Single Gemini page curses management."""
|
|
|
|
|
|
|
|
import curses
|
|
|
|
|
|
|
|
from bebop.page import Page
|
|
|
|
from bebop.rendering import render_lines
|
|
|
|
|
|
|
|
|
|
|
|
class PagePad:
|
|
|
|
"""Window containing page content."""
|
|
|
|
|
|
|
|
MAX_COLS = 1000
|
|
|
|
|
|
|
|
def __init__(self, initial_num_lines):
|
|
|
|
self.dim = (initial_num_lines, PagePad.MAX_COLS)
|
|
|
|
self.pad = curses.newpad(*self.dim)
|
|
|
|
self.pad.scrollok(True)
|
|
|
|
self.pad.idlok(True)
|
|
|
|
self.current_line = 0
|
|
|
|
self.current_column = 0
|
|
|
|
self.current_page = None
|
|
|
|
|
|
|
|
def show_page(self, page: Page):
|
|
|
|
"""Render Gemtext data in the content pad."""
|
|
|
|
self.current_page = page
|
|
|
|
self.pad.clear()
|
|
|
|
self.dim = render_lines(page.metalines, self.pad, PagePad.MAX_COLS)
|
|
|
|
self.current_line = 0
|
|
|
|
self.current_column = 0
|
|
|
|
|
|
|
|
def refresh_content(self, x, y):
|
|
|
|
"""Refresh content pad's view using the current line/column."""
|
|
|
|
if x <= 0 or y <= 0:
|
|
|
|
return
|
|
|
|
content_position = self.current_line, self.current_column
|
2021-05-09 15:54:56 +02:00
|
|
|
try:
|
|
|
|
self.pad.refresh(*content_position, 0, 0, x, y)
|
|
|
|
except curses.error:
|
|
|
|
pass
|
2021-03-18 01:55:31 +01:00
|
|
|
|
|
|
|
def scroll_v(self, num_lines: int, window_height: int =0):
|
|
|
|
"""Make the content pad scroll up and down by num_lines.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
- num_lines: amount of lines to scroll, can be negative to scroll up.
|
|
|
|
- window_height: total window height, used to limit scrolling down.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
True if scrolling occured and the pad has to be refreshed.
|
|
|
|
"""
|
|
|
|
if num_lines < 0:
|
|
|
|
num_lines = -num_lines
|
|
|
|
min_line = 0
|
|
|
|
if self.current_line > min_line:
|
|
|
|
self.current_line = max(self.current_line - num_lines, min_line)
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
max_line = self.dim[0] - window_height
|
|
|
|
if self.current_line < max_line:
|
|
|
|
self.current_line = min(self.current_line + num_lines, max_line)
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def scroll_h(self, num_columns: int, window_width: int =0):
|
|
|
|
"""Make the content pad scroll left and right by num_columns.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
- num_columns: amount of columns to scroll, can be negative to scroll
|
|
|
|
left.
|
|
|
|
- window_width: total window width, used to limit scrolling right.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
True if scrolling occured and the pad has to be refreshed.
|
|
|
|
"""
|
|
|
|
if num_columns < 0:
|
|
|
|
num_columns = -num_columns
|
|
|
|
min_column = 0
|
|
|
|
if self.current_column > min_column:
|
|
|
|
new_column = self.current_column - num_columns
|
|
|
|
self.current_column = max(new_column, min_column)
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
max_column = self.dim[1] - window_width
|
|
|
|
if self.current_column < max_column:
|
|
|
|
new_column = self.current_column + num_columns
|
|
|
|
self.current_column = min(new_column, max_column)
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def go_to_beginning(self):
|
|
|
|
"""Make the pad show its start; return True if a refresh is needed."""
|
|
|
|
if self.current_line:
|
|
|
|
self.current_line = 0
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def go_to_end(self, window_height):
|
|
|
|
"""Make the pad show its bottom; return True if a refresh is needed."""
|
2021-06-04 19:00:13 +02:00
|
|
|
max_line = self.get_max_line(window_height)
|
2021-03-18 01:55:31 +01:00
|
|
|
if self.current_line != max_line:
|
|
|
|
self.current_line = max_line
|
|
|
|
return True
|
|
|
|
return False
|
2021-04-17 22:02:06 +02:00
|
|
|
|
|
|
|
def go_to_first_column(self):
|
|
|
|
"""Move to the first column; return True if a refresh is needed."""
|
|
|
|
if self.current_column:
|
|
|
|
self.current_column = 0
|
|
|
|
return True
|
|
|
|
return False
|
2021-06-04 19:00:13 +02:00
|
|
|
|
|
|
|
def get_max_line(self, window_height):
|
|
|
|
"""Return the max line that can be used as current_line."""
|
|
|
|
return self.dim[0] - window_height
|