SWB: Links, Listen einrücken, Adresszeile
SimpleWebBrowser unterstützt nun Links, die Listenelemente werden eingerückt und es gibt eine Adresszeile.
Links
Links sind einfach implementiert. Sie werden in blau hervorgehoben und bei einem Mausklick wird die neue Seite geladen. Mouseover, Linkhistory u.ä. werden nicht unterstützt.
Gesammelt werden die Links in SimpleWebBrowser.link[]
mit ihrem href
-Wert
und den zugehörigen Widgets. link.py:
class Link: def __init__(self, href=None): self.href = href self.widgets = [] def is_point_in(self, x, y): for widget in self.widgets: if widget.is_point_in(x, y): return True return False
class SimpleWebbrowser: ... self.links = [] self.link_current = None self.link_active = '' .... def parse_html(self, element): ... if element.tag == 'a': self.link_current = Link(href=element.get('href')) self.links.append(self.link_current) self.attr_stack.append(self.attr_current) self.attr_current = AttributeWidget(fg=(0,0,255,255), bg=(255,255,255,255)) self.text_box.widgets.append(self.attr_current) if element.text != None: for word in element.text.split(): text_widget = TextWidget(word) self.text_box.widgets.append(text_widget) if self.link_current != None: self.link_current.widgets.append(text_widget) for child in element: self.parse_html(child) ... if element.tag == 'a': self.link_current = None self.attr_current = self.attr_stack.pop() self.text_box.widgets.append(self.attr_current) if element.tail != None: for word in element.tail.split(): text_widget = TextWidget(word) self.text_box.widgets.append(text_widget) if self.link_current != None: self.link_current.widgets.append(text_widget)
Bei einem Mausklick wird überprüft, ob der Mauszeiger in einem der Widgets liegt und der zugehörige Link aufgerufen. simplewebbrowser.py:
def on_mouse_up(self, event): for link in self.links: if link.is_point_in(event.pos[0] - self.text_box.left + self.text_box.scroll_left, event.pos[1] - self.text_box.top + self.text_box.scroll_top): self.load(link.href) break
Dabei müssen relative Links wie href="../path/to/page.html
zu absoluten konvertiert werden,
in dem sie mit der URL der gerade angezeigten Seite verknüpft werden. Genau dies erledigt
urljoin()
von der Python-urllib. url_join.py:
from urllib.parse import urljoin print(urljoin('http://www.example.com/a/b/c/page.html', '../new_path/to/new_page.html'))
Optimierung: Kein Zeichnen bei Mausbewegungen
Außerdem wird zur Optimierung, bei Mausbewegungen und unbekannten Tastendrücken, die Darstellung nun nicht mehr neu gezeichnet. Hätte ich mal gleich als erstes machen sollen - durch diese Optimierung reagiert SimpleWebBrowser wesentlich schneller und angenehmer. simplewebbrowser.py:
def run(self): do_redraw = True while True: if do_redraw: self.draw(self.g) pygame.display.flip() do_redraw = True event = pygame.event.wait() ... if event.type == pygame.MOUSEMOTION: do_redraw = False
Download: Revision 138
svn checkout --revision 138 https://svn.sven-drieling.de/repos/trunk/playground/webbrowser/ yd-playground-webbrowser-138
Listen einrücken
Das Einrücken von Listenelementen ist mit Hilfe des Indent
-Attributs implementiert.
indent_widget.py:
class IndentWidget(Widget): def __init__(self, indent=0): super().__init__(0, 0, 0, 0) self.indent = indent def draw(self, g, left, top): pass
Beim Anfang einer Liste wird Indent(32)
eingefügt und am Ende diese Einrückung mit
Indent(-32)
wieder rückgängig gemacht.
simplewebbrowser.py:
def parse_html(self, element): ... if element.tag in ['ul', 'ol']: self.text_box.widgets.append(IndentWidget(32)) if element.text != None: for word in element.text.split(): ... if element.tag in ['ul', 'ol']: self.text_box.widgets.append(IndentWidget(-32)) ...
layout()
berücksichtigt diesen Wert bei der Positionsberechnung der
Widgets. Diese ist durch die if isinstance(widget, IndentWidget)
-Abfragen nicht schön aber
einfach zu implementieren und funktioniert erst einmal.
textbox.py:
def layout(self, g): ... try: widget = next(widgetsIter) ... if isinstance(widget, IndentWidget): self.indent += widget.indent while True: # First widget in line widget.left = self.indent widget.top = self.text_height line_width = self.indent + widget.width line_height = widget.height # Fill line with following widgets widget = next(widgetsIter) ... if isinstance(widget, IndentWidget): self.indent += widget.indent line_width += widget.indent ... while line_width + self.WORD_GAP + widget.width < self.width: ... if isinstance(widget, IndentWidget): self.indent += widget.indent line_width += widget.indent widget.left = self.left + line_width widget.top = self.text_height ...
Mit den funktionierenden Links und eingerückten Listen hat das schon etwas von einem HTML 2.0-Webbrowser :-)
Download: Revision 140
svn checkout --revision 140 https://svn.sven-drieling.de/repos/trunk/playground/webbrowser/ yd-playground-webbrowser-140
Adresszeile
Die Adresszeile zur Anzeige und Eingabe des aktuellen Links ist das text_field_widget.py. Weil es innerhalb des Fensters nun zwei Widgets gibt, die Eingaben verarbeiten, hat sich einiges am Aufbau von simplewebbrowser.py verändert.
Wichtigste Änderung ist die Verwaltung des gerade aktiven Widgets, das die Eingabe zugeschickt
bekommt (on_mouse_up(), on_key_down()
, ...).
if event.type == pygame.MOUSEBUTTONDOWN: if self.address_bar.is_point_in(event.pos[0], event.pos[1]): new_active_widget = self.address_bar else: new_active_widget = self if new_active_widget != self.active_widget: self.active_widget.on_blur() self.active_widget.draw(self.g) self.active_widget = new_active_widget self.active_widget.on_focus() self.active_widget.draw(self.g) pygame.display.flip() self.active_widget.on_mouse_down(event) if event.type == pygame.MOUSEBUTTONUP: self.active_widget.on_mouse_up(event)
Weil die TextBox
derzeit kein Widget ist und z.B. das Sammeln der Links derzeit noch
in SimpleWebbrowser
und nicht on der TextBox
erfolgt, ist das alles im
Moment noch etwas durcheinandergewirbelt.
Download: Revision 145
svn checkout --revision 145 https://svn.sven-drieling.de/repos/trunk/playground/webbrowser/ yd-playground-webbrowser-145