dece
0b79bd9e9e
This absolutely un-atomic commit also introduces the Links class to hold methods regarding link lookups on top of the standard dict class.
99 lines
3.3 KiB
Python
99 lines
3.3 KiB
Python
"""Single Gemini page curses management."""
|
|
|
|
import curses
|
|
|
|
from bebop.gemtext import parse_gemtext
|
|
from bebop.links import Links
|
|
from bebop.rendering import format_elements, render_lines
|
|
|
|
|
|
class Page:
|
|
"""Window containing page content."""
|
|
|
|
MAX_COLS = 1000
|
|
|
|
def __init__(self, initial_num_lines):
|
|
self.dim = (initial_num_lines, Page.MAX_COLS)
|
|
self.pad = curses.newpad(*self.dim)
|
|
self.pad.scrollok(True)
|
|
self.pad.idlok(True)
|
|
self.metalines = []
|
|
self.current_line = 0
|
|
self.current_column = 0
|
|
self.links = Links()
|
|
|
|
def show_gemtext(self, gemtext: bytes):
|
|
"""Render Gemtext data in the content pad."""
|
|
# Parse and format Gemtext.
|
|
elements = parse_gemtext(gemtext)
|
|
self.metalines = format_elements(elements, 80)
|
|
# Render metalines.
|
|
self.pad.clear()
|
|
self.dim = render_lines(self.metalines, self.pad, Page.MAX_COLS)
|
|
self.current_line = 0
|
|
self.current_column = 0
|
|
# Aggregate links for navigation.
|
|
self.links = Links()
|
|
for meta, _ in self.metalines:
|
|
if "link_id" in meta and "url" in meta:
|
|
self.links[meta["link_id"]] = meta["url"]
|
|
|
|
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
|
|
self.pad.refresh(*content_position, 0, 0, x, y)
|
|
|
|
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):
|
|
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):
|
|
if self.current_line:
|
|
self.current_line = 0
|
|
return True
|
|
return False
|
|
|
|
def go_to_end(self, window_height):
|
|
max_line = self.dim[0] - window_height
|
|
if self.current_line != max_line:
|
|
self.current_line = max_line
|
|
return True
|
|
return False
|