summaryrefslogtreecommitdiff
path: root/browser/browser.py
diff options
context:
space:
mode:
Diffstat (limited to 'browser/browser.py')
-rwxr-xr-xbrowser/browser.py133
1 files changed, 133 insertions, 0 deletions
diff --git a/browser/browser.py b/browser/browser.py
new file mode 100755
index 0000000..f5475ab
--- /dev/null
+++ b/browser/browser.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+
+import sys
+
+from PySide6 import QtCore, QtWidgets, QtGui, QtWebEngineWidgets, QtWebEngineCore
+
+import gemini
+
+
+class GeminiPage(QtWebEngineCore.QWebEnginePage):
+ def acceptNavigationRequest(self, url, navtype, mainframe):
+ """
+ Block non-gemini page navigation and
+ send the url to the os instead
+ """
+ if url.scheme() == 'gemini':
+ return True
+ else:
+ QtGui.QDesktopServices().openUrl(url)
+ return False
+
+
+class GeminiSchemeHandler(QtWebEngineCore.QWebEngineUrlSchemeHandler):
+ def requestStarted(self, request):
+ request_url = gemini.hack_url(request.requestUrl().toString())
+ print(request_url)
+ gem = gemini.get(request_url)
+ print(gem['status'], gem['meta'])
+ buf = QtCore.QBuffer(parent=request)
+ buf.open(QtCore.QIODevice.WriteOnly)
+ buf.write(gemini.gem2html(gem).encode('utf8'))
+ buf.seek(0)
+ buf.close()
+ request.reply(b'text/html', buf)
+
+
+class GUrlBar(QtWidgets.QLineEdit):
+
+ def __init__(self):
+ QtWidgets.QLineEdit.__init__(self)
+
+ def setUrl(self, url):
+ url = gemini.hack_url(url.toDisplayString())
+ return self.setText(url)
+
+
+class GBrowser(QtWidgets.QMainWindow):
+
+ def __init__(self, initial_url=None, profile=None):
+ if initial_url is None:
+ initial_url = 'gemini://gemini.circumlunar.space/'
+ if profile is None:
+ profile = QtWebEngineCore.QWebEngineProfile.defaultProfile()
+ QtWidgets.QMainWindow.__init__(self)
+
+ # Main Viewport
+ self._browser = QtWebEngineWidgets.QWebEngineView()
+ page = GeminiPage(profile, self._browser)
+ self._browser.setPage(page)
+ self.setCentralWidget(self._browser)
+
+ # Navigation Toolbar
+ back = QtWidgets.QPushButton('←', self)
+ forward = QtWidgets.QPushButton('→', self)
+ self._address = GUrlBar()
+ toolbar = QtWidgets.QToolBar()
+ toolbar.setObjectName('navigationToolbar')
+ toolbar.addWidget(back)
+ toolbar.addWidget(forward)
+ toolbar.addWidget(self._address)
+ self.addToolBar(toolbar)
+
+ # Status Bar
+ request_status = QtWidgets.QLabel()
+ self.statusBar().addWidget(request_status)
+
+ # Connect signals
+ back.clicked.connect(self._browser.back)
+ forward.clicked.connect(self._browser.forward)
+ self._browser.urlChanged.connect(self._address.setUrl)
+ self._address.returnPressed.connect(self.loadAddress)
+ self._browser.page().linkHovered.connect(self.set_status_url)
+
+ self._browser.load(QtCore.QUrl(initial_url))
+
+ settings = QtCore.QSettings("xcolour.net", "GeminiBrowser")
+ self.restoreGeometry(settings.value("geometry"))
+ self.restoreState(settings.value("windowState"))
+ self.setWindowTitle('Gemini Browser')
+ self.show()
+
+ # Shortcuts
+ down_shortcut = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_J), self)
+ down_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Down, QtCore.Qt.NoModifier)
+ down_shortcut.activated.connect(lambda: QtWidgets.QApplication.sendEvent(self._browser.focusProxy(), down_event))
+ up_shortcut = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_K), self)
+ up_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Up, QtCore.Qt.NoModifier)
+ up_shortcut.activated.connect(lambda: QtWidgets.QApplication.sendEvent(self._browser.focusProxy(), up_event))
+ QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_H), self, self._browser.back)
+ QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_L), self, self._browser.forward)
+ QtGui.QShortcut(QtGui.QKeySequence("Shift+R"), self, self._browser.reload)
+ QtGui.QShortcut(QtGui.QKeySequence("Esc"), self._address, lambda: self.setFocus())
+ QtGui.QShortcut(QtGui.QKeySequence("Shift+L"), self, lambda: self._address.setFocus())
+
+ def set_status_url(self, url):
+ if url:
+ self.statusBar().showMessage(url)
+ else:
+ self.statusBar().clearMessage()
+
+ def closeEvent(self, event):
+ settings = QtCore.QSettings("xcolour.net", "GeminiBrowser")
+ settings.setValue("geometry", self.saveGeometry())
+ settings.setValue("windowState", self.saveState())
+ self._browser.page().deleteLater()
+ super().closeEvent(event)
+
+ def loadAddress(self):
+ self._browser.load(self._address.text())
+ self._browser.setFocus()
+
+
+scheme = QtWebEngineCore.QWebEngineUrlScheme(b'gemini')
+scheme.setDefaultPort(1965)
+scheme.setFlags(QtWebEngineCore.QWebEngineUrlScheme.SecureScheme)
+QtWebEngineCore.QWebEngineUrlScheme.registerScheme(scheme)
+app = QtWidgets.QApplication(sys.argv)
+gem_handler = GeminiSchemeHandler()
+profile = QtWebEngineCore.QWebEngineProfile()
+profile.removeAllUrlSchemeHandlers()
+profile.installUrlSchemeHandler(b'gemini', gem_handler)
+ex = GBrowser(sys.argv[1] if len(sys.argv) > 1 else None, profile)
+sys.exit(app.exec())