page: split page/page_pad modules
Also add title to Page but really this needs a big cleanup.
This commit is contained in:
parent
40133a1e6b
commit
d6bcd1f706
|
@ -13,7 +13,8 @@ from bebop.history import History
|
|||
from bebop.links import Links
|
||||
from bebop.mouse import ButtonState
|
||||
from bebop.navigation import *
|
||||
from bebop.page import Page, PagePad
|
||||
from bebop.page import Page
|
||||
from bebop.page_pad import PagePad
|
||||
from bebop.protocol import Request, Response
|
||||
|
||||
|
||||
|
@ -208,7 +209,8 @@ class Browser:
|
|||
if command in ("o", "open"):
|
||||
self.open_url(words[1], assume_absolute=True)
|
||||
|
||||
def open_url(self, url, base_url=None, redirects=0, assume_absolute=False):
|
||||
def open_url(self, url, base_url=None, redirects=0, assume_absolute=False,
|
||||
history=True, use_cache=True):
|
||||
"""Try to open an URL.
|
||||
|
||||
This function assumes that the URL can be from an user and thus tries a
|
||||
|
@ -223,6 +225,8 @@ class Browser:
|
|||
- base_url: an URL string to use as base in case `url` is relative.
|
||||
- redirections: number of redirections we did yet for the same request.
|
||||
- assume_absolute: assume we intended to use an absolute URL if True.
|
||||
- history: whether the URL should be pushed to history on success.
|
||||
- use_cache: whether we should look for an already cached document.
|
||||
"""
|
||||
if redirects > 5:
|
||||
self.set_status_error(f"Too many redirections ({url}).")
|
||||
|
|
110
bebop/page.py
110
bebop/page.py
|
@ -1,11 +1,8 @@
|
|||
"""Single Gemini page curses management."""
|
||||
|
||||
import curses
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from bebop.gemtext import parse_gemtext
|
||||
from bebop.gemtext import parse_gemtext, Title
|
||||
from bebop.rendering import generate_metalines
|
||||
from bebop.links import Links
|
||||
from bebop.rendering import generate_metalines, render_lines
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -13,6 +10,7 @@ class Page:
|
|||
"""Page-related data."""
|
||||
metalines: list = field(default_factory=list)
|
||||
links: Links = field(default_factory=Links)
|
||||
title: str = ""
|
||||
|
||||
@staticmethod
|
||||
def from_gemtext(gemtext: str):
|
||||
|
@ -20,98 +18,10 @@ class Page:
|
|||
elements = parse_gemtext(gemtext)
|
||||
metalines = generate_metalines(elements, 80)
|
||||
links = Links.from_metalines(metalines)
|
||||
return Page(metalines, links)
|
||||
|
||||
|
||||
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
|
||||
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):
|
||||
"""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."""
|
||||
max_line = self.dim[0] - window_height
|
||||
if self.current_line != max_line:
|
||||
self.current_line = max_line
|
||||
return True
|
||||
return False
|
||||
# TODO this is horrible; merge parsing with page generation directly
|
||||
title = ""
|
||||
for element in elements:
|
||||
if isinstance(element, Title) and element.level == 1:
|
||||
title = element.text
|
||||
break
|
||||
return Page(metalines, links, title)
|
||||
|
|
100
bebop/page_pad.py
Normal file
100
bebop/page_pad.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
"""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
|
||||
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):
|
||||
"""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."""
|
||||
max_line = self.dim[0] - window_height
|
||||
if self.current_line != max_line:
|
||||
self.current_line = max_line
|
||||
return True
|
||||
return False
|
Reference in a new issue