#!/usr/bin/env python import sys from PySide2 import QtCore, QtWidgets import gemini class GViewport(QtWidgets.QTextBrowser): hoverUrlChanged = QtCore.Signal(str) def __init__(self, address_bar): self._current_url = None self._last_redirect = (QtCore.QUrl(), {}) self.address_bar = address_bar self._hover_url = None QtWidgets.QTextBrowser.__init__(self) def mouseMoveEvent(self, event): cur = self.cursorForPosition(event.localPos().toPoint()) hover_url = cur.charFormat().anchorHref() hover_url = gemini.urljoin(self._current_url, hover_url) if hover_url != self._hover_url: self._hover_url = hover_url self.hoverUrlChanged.emit(self._hover_url) return super().mouseMoveEvent(event) def loadResource(self, type_, url): current_url = QtCore.QUrl(self._current_url) if self._last_redirect[0].toString() == url.toString(): gem = self._last_redirect[1] else: if not url.scheme(): url.setScheme(current_url.scheme()) if not url.host(): url.setHost(current_url.host()) if url.port() == -1: url.setPort(1965) if not url.path().startswith('/'): url.setPath(current_url.path().rsplit('/', 1)[0] + '/' + url.path()) gem = gemini.get(gemini.urljoin(current_url.toString(), url.toString())) if 'body' in gem: html = gemini.gem2html(gem['body']) else: html = '

{} {}

'.format(gem['status'], gem['meta']) self._current_url = url.toString() return html def setSource(self, url): if url.scheme() != 'gemini': return gem = gemini.get(gemini.urljoin(self._current_url, url.toString())) while gem['status'][0] == '3': url = QtCore.QUrl(gem['meta']) if url.port() == 1965: url.setPort(-1) print('redirect: {}'.format(url)) gem = gemini.get(gemini.urljoin(self._current_url, url.toString())) self._last_redirect = (url, gem) if url.port() == 1965: url.setPort(-1) print('setSource: {}'.format(url)) return super().setSource(url) def setRawSource(self): return self.setSource(QtCore.QUrl(self.address_bar.text())) class GUrlBar(QtWidgets.QLineEdit): def __init__(self): QtWidgets.QLineEdit.__init__(self) def setUrl(self, url): return self.setText(url.toDisplayString()) class GBrowser(QtWidgets.QMainWindow): def __init__(self): QtWidgets.QMainWindow.__init__(self) self.init_ui() def init_ui(self): self.statusBar().showMessage('Ready') back = QtWidgets.QPushButton("back") forward = QtWidgets.QPushButton("forward") address = GUrlBar() toolbar = QtWidgets.QToolBar() toolbar.setObjectName('navigationToolbar') toolbar.addWidget(back) toolbar.addWidget(forward) toolbar.addWidget(address) browser = GViewport(address) back.clicked.connect(browser.backward) forward.clicked.connect(browser.forward) browser.sourceChanged.connect(address.setUrl) address.returnPressed.connect(browser.setRawSource) browser.hoverUrlChanged.connect(self.statusBar().showMessage) self.addToolBar(toolbar) self.setCentralWidget(browser) browser.setSource(QtCore.QUrl('gemini://gemini.circumlunar.space/')) settings = QtCore.QSettings("xcolour.net", "GeminiBrowser") self.restoreGeometry(settings.value("geometry")) self.restoreState(settings.value("windowState")) self.setWindowTitle('Gemini Browser') self.show() def closeEvent(self, event): settings = QtCore.QSettings("xcolour.net", "GeminiBrowser") settings.setValue("geometry", self.saveGeometry()) settings.setValue("windowState", self.saveState()) super().closeEvent(event) app = QtWidgets.QApplication(sys.argv) ex = GBrowser() sys.exit(app.exec_())