summaryrefslogtreecommitdiff
path: root/browser.py
blob: 8a542f9f0dbfb332c58858891a3b429467af653a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/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):
        print(request.requestUrl().toString())
        gem = gemini.get(request.requestUrl().toString())
        print(gem['status'], gem['meta'])
        buf = QtCore.QBuffer(parent=request)
        buf.open(QtCore.QIODevice.WriteOnly)
        buf.write(gemini.gem2html(gem['body']).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):
        return self.setText(url.toDisplayString())


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("back")
        forward = QtWidgets.QPushButton("forward")
        address = GUrlBar()
        toolbar = QtWidgets.QToolBar()
        toolbar.setObjectName('navigationToolbar')
        toolbar.addWidget(back)
        toolbar.addWidget(forward)
        toolbar.addWidget(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(address.setUrl)
        address.returnPressed.connect(self._browser.load)
        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))
        back_shortcut = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_H), self, self._browser.back)
        forward_shortcut = QtGui.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_L), self, self._browser.forward)
        reload_shortcut = QtGui.QShortcut(QtGui.QKeySequence("Shift+R"), self, self._browser.reload)

    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)


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())