qutebrowser/qutebrowser/widgets/browser.py

221 lines
7.7 KiB
Python
Raw Normal View History

import logging
2014-01-21 08:37:21 +01:00
from PyQt5.QtCore import QUrl, pyqtSignal, Qt, QPoint, QEvent
from PyQt5.QtPrintSupport import QPrintPreviewDialog
2013-12-15 21:40:15 +01:00
from PyQt5.QtWebKitWidgets import QWebView
2013-12-15 21:40:15 +01:00
from qutebrowser.widgets.tabbar import TabWidget
class TabbedBrowser(TabWidget):
"""A TabWidget with QWebViews inside"""
cur_progress = pyqtSignal(int) # Progress of the current tab changed
cur_load_finished = pyqtSignal(bool) # Current tab finished loading
2014-01-21 08:37:21 +01:00
# Current tab changed scroll position
cur_scroll_perc_changed = pyqtSignal(int, int)
2014-01-19 19:41:34 +01:00
keypress = pyqtSignal('QKeyEvent')
_url_stack = [] # Stack of URLs of closed tabs
2013-12-15 21:40:15 +01:00
def __init__(self, parent):
super().__init__(parent)
self.currentChanged.connect(self._currentChanged_handler)
2013-12-15 21:40:15 +01:00
self.tabopen("http://ddg.gg/")
def tabopen(self, url):
"""Opens a new tab with a given url"""
2013-12-15 21:40:15 +01:00
tab = BrowserTab(self)
tab.openurl(url)
self.addTab(tab, url)
2013-12-16 22:07:11 +01:00
self.setCurrentWidget(tab)
self.cur_progress.emit(tab.progress)
tab.loadProgress.connect(
lambda *args: self._filter_signals(self.cur_progress, *args))
tab.loadFinished.connect(
lambda *args: self._filter_signals(self.cur_load_finished, *args))
2014-01-21 08:37:21 +01:00
tab.scroll_pos_changed.connect(self._scroll_pos_changed_handler)
# FIXME should we really bind this to loadStarted? Sometimes the URL
# isn't set correctly at this point, e.g. when doing
# setContent(..., baseUrl=QUrl('foo'))
tab.loadStarted.connect(self._loadStarted_handler)
tab.titleChanged.connect(self._titleChanged_handler)
2013-12-15 21:40:15 +01:00
2013-12-16 22:01:06 +01:00
def openurl(self, url):
"""Opens an url in the current tab"""
2014-01-17 23:22:49 +01:00
tab = self.currentWidget()
2013-12-16 22:01:06 +01:00
tab.openurl(url)
2014-01-17 23:17:24 +01:00
def undo_close(self):
"""Undos closing a tab"""
if self._url_stack:
self.tabopen(self._url_stack.pop())
2014-01-17 23:17:24 +01:00
def cur_close(self):
"""Closes the current tab"""
2014-01-17 23:22:49 +01:00
if self.count() > 1:
2014-01-17 08:28:04 +01:00
idx = self.currentIndex()
2014-01-17 23:22:49 +01:00
tab = self.currentWidget()
2014-01-17 23:17:24 +01:00
# FIXME maybe we should add the QUrl object here and deal with QUrls everywhere
# FIXME maybe we actually should store the webview objects here
self._url_stack.append(tab.url().url())
2014-01-17 08:28:04 +01:00
self.removeTab(idx)
else:
# FIXME
pass
2014-01-17 08:03:42 +01:00
def cur_reload(self):
"""Reloads the current tab"""
self.currentWidget().reload()
def cur_stop(self):
"""Stops loading in the current tab"""
self.currentWidget().stop()
def cur_print(self):
"""Prints the current tab"""
# FIXME that does not what I expect
preview = QPrintPreviewDialog()
preview.paintRequested.connect(self.currentWidget().print)
preview.exec_()
def cur_back(self):
"""Goes back in the history of the current tab"""
# FIXME display warning if beginning of history
self.currentWidget().back()
def cur_forward(self):
"""Goes forward in the history of the current tab"""
# FIXME display warning if end of history
self.currentWidget().forward()
def cur_scroll(self, dx, dy, count=None):
"""Scrolls the current tab by count * dx/dy"""
if count is None:
count = 1
dx = int(count) * int(dx)
dy = int(count) * int(dy)
self.currentWidget().page().mainFrame().scroll(dx, dy)
2014-01-19 17:45:03 +01:00
def cur_scroll_percent_x(self, perc=None, count=None):
"""Scrolls the current tab to a specific percent of the page.
Accepts percentage either as argument, or as count.
"""
self._cur_scroll_percent(perc, count, Qt.Horizontal)
def cur_scroll_percent_y(self, perc=None, count=None):
"""Scrolls the current tab to a specific percent of the page
Accepts percentage either as argument, or as count.
"""
self._cur_scroll_percent(perc, count, Qt.Vertical)
def _cur_scroll_percent(self, perc=None, count=None, orientation=None):
if perc is None and count is None:
perc = 100
elif perc is None:
perc = int(count)
else:
perc = int(perc)
frame = self.currentWidget().page().mainFrame()
m = frame.scrollBarMaximum(orientation)
if m == 0:
return
frame.setScrollBarValue(orientation, int(m * perc / 100))
2014-01-17 08:39:14 +01:00
def switch_prev(self):
"""Switches to the previous tab"""
2014-01-17 08:39:14 +01:00
idx = self.currentIndex()
if idx > 0:
self.setCurrentIndex(idx - 1)
else:
# FIXME
pass
def switch_next(self):
"""Switches to the next tab"""
2014-01-17 08:39:14 +01:00
idx = self.currentIndex()
if idx < self.count() - 1:
self.setCurrentIndex(idx + 1)
else:
# FIXME
pass
def keyPressEvent(self, e):
self.keypress.emit(e)
super().keyPressEvent(e)
def _titleChanged_handler(self, text):
2014-01-17 22:50:27 +01:00
if text:
self.setTabText(self.indexOf(self.sender()), text)
def _loadStarted_handler(self):
s = self.sender()
self.setTabText(self.indexOf(s), s.url().toString())
def _filter_signals(self, signal, *args):
"""Filters signals, and triggers TabbedBrowser signals if the signal
was sent from the _current_ tab and not from any other one.
"""
dbgstr = "{} ({})".format(
signal.signal, ','.join([str(e) for e in args]))
if self.currentWidget() == self.sender():
logging.debug('{} - emitting'.format(dbgstr))
signal.emit(*args)
2014-01-17 20:29:20 +01:00
else:
logging.debug('{} - ignoring'.format(dbgstr))
2014-01-17 08:39:14 +01:00
def _currentChanged_handler(self, idx):
2014-01-17 13:16:13 +01:00
tab = self.widget(idx)
self.cur_progress.emit(tab.progress)
def _scroll_pos_changed_handler(self, x, y):
"""Gets the new position from a BrowserTab. If it's the current tab, it
calculates the percentage and emits cur_scroll_perc_changed.
2014-01-21 08:37:21 +01:00
"""
sender = self.sender()
if sender != self.currentWidget():
return
frame = sender.page().mainFrame()
m = (frame.scrollBarMaximum(Qt.Horizontal),
frame.scrollBarMaximum(Qt.Vertical))
perc = (round(100 * x / m[0]) if m[0] != 0 else 0,
round(100 * y / m[1]) if m[1] != 0 else 0)
self.cur_scroll_perc_changed.emit(*perc)
2014-01-21 08:37:21 +01:00
2013-12-15 21:40:15 +01:00
class BrowserTab(QWebView):
"""One browser tab in TabbedBrowser"""
2014-01-17 13:16:13 +01:00
progress = 0
scroll_pos_changed = pyqtSignal(int, int)
_scroll_pos = (-1, -1)
2014-01-17 12:24:38 +01:00
2013-12-15 21:40:15 +01:00
def __init__(self, parent):
super().__init__(parent)
2014-01-17 13:16:13 +01:00
self.loadProgress.connect(self.set_progress)
2014-01-21 08:37:21 +01:00
self.installEventFilter(self)
# FIXME find some way to hide scrollbars without setScrollBarPolicy
2013-12-15 21:40:15 +01:00
self.show()
def openurl(self, url):
"""Opens an URL in the browser"""
2013-12-15 21:40:15 +01:00
if not url.startswith('http://'):
url = 'http://' + url
self.load(QUrl(url))
2014-01-17 13:16:13 +01:00
def set_progress(self, prog):
self.progress = prog
2014-01-21 08:37:21 +01:00
def eventFilter(self, watched, e):
"""Dirty hack to emit a signal if the scroll position changed.
We listen to repaint requests here, in the hope a repaint will always
be requested when scrolling, and if the scroll position actually
changed, we emit a signal.
"""
if watched == self and e.type() == QEvent.Paint:
frame = self.page().mainFrame()
new_pos = (frame.scrollBarValue(Qt.Horizontal),
frame.scrollBarValue(Qt.Vertical))
2014-01-21 08:37:21 +01:00
if self._scroll_pos != new_pos:
logging.debug("Updating scroll position")
self.scroll_pos_changed.emit(*new_pos)
2014-01-21 08:37:21 +01:00
self._scroll_pos = new_pos
return super().eventFilter(watched, e)