Reordering in widget submodules

This commit is contained in:
Florian Bruhin 2014-02-18 18:32:07 +01:00
parent 133d720de5
commit 96747c9d58
3 changed files with 267 additions and 267 deletions

View File

@ -102,6 +102,118 @@ class TabbedBrowser(TabWidget):
self._space.setContext(Qt.WidgetWithChildrenShortcut)
self._space.activated.connect(lambda: self.cur_scroll_page(0, 1))
def _cb_tab_shutdown(self, tab):
"""Called after a tab has been shut down completely."""
try:
self._tabs.remove(tab)
except ValueError:
logging.exception("tab {} could not be removed".format(tab))
logging.debug("Tabs after removing: {}".format(self._tabs))
if not self._tabs: # all tabs shut down
logging.debug("Tab shutdown complete.")
self.shutdown_complete.emit()
def _cur_scroll_percent(self, perc=None, count=None, orientation=None):
"""Inner logic for cur_scroll_percent_(x|y)."""
if perc is None and count is None:
perc = 100
elif perc is None:
perc = int(count)
else:
perc = float(perc)
frame = self.currentWidget().page_.mainFrame()
m = frame.scrollBarMaximum(orientation)
if m == 0:
return
frame.setScrollBarValue(orientation, int(m * perc / 100))
def _widget(self, count=None):
"""Return a widget based on a count/idx.
If count is None, return the current widget.
"""
if count is None:
return self.currentWidget()
elif 1 <= count <= self.count():
return self.widget(count - 1)
else:
return None
def _titleChanged_handler(self, text):
"""Set the title of a tab.
Slot for the titleChanged signal of any tab.
"""
logging.debug('title changed to "{}"'.format(text))
if text:
self.setTabText(self.indexOf(self.sender()), text)
else:
logging.debug('ignoring title change')
def _filter_factory(self, signal):
"""Return a partial functon calling _filter_signals with a signal."""
return functools.partial(self._filter_signals, signal)
def _filter_signals(self, signal, *args):
"""Filter signals and trigger TabbedBrowser signals if needed.
Triggers signal if the original signal was sent from the _current_ tab
and not from any other one.
The original signal does not matter, since we get the new signal and
all args.
The current value of the signal is also stored in tab.signal_cache so
it can be emitted later when the tab changes to the current tab.
signal -- The signal to emit if the sender was the current widget.
*args -- The args to pass to the signal.
"""
# FIXME BUG the signal cache ordering seems to be weird sometimes.
# How to reproduce:
# - Open tab
# - While loading, open another tab
# - Switch back to #1 when loading finished
# - It seems loadingStarted is before loadingFinished
sender = self.sender()
log_signal = not signal.signal.startswith('2cur_progress')
if log_signal:
logging.debug('signal {} (tab {})'.format(dbg_signal(signal, args),
self.indexOf(sender)))
if not isinstance(sender, BrowserTab):
# FIXME why does this happen?
logging.warn('Got signal {} by {} which is no tab!'.format(
dbg_signal(signal, args), sender))
return
sender.signal_cache.add(signal, args)
if self.currentWidget() == sender:
if log_signal:
logging.debug(' emitting')
return signal.emit(*args)
else:
if log_signal:
logging.debug(' ignoring')
def shutdown(self):
"""Try to shut down all tabs cleanly."""
try:
self.currentChanged.disconnect()
except TypeError:
pass
tabcount = self.count()
if tabcount == 0:
logging.debug("No tabs -> shutdown complete")
self.shutdown_complete.emit()
return
for tabidx in range(tabcount):
logging.debug("Shutting down tab {}/{}".format(tabidx, tabcount))
tab = self.widget(tabidx)
tab.shutdown(callback=functools.partial(self._cb_tab_shutdown,
tab))
def tabopen(self, url):
"""Open a new tab with a given url.
@ -136,17 +248,6 @@ class TabbedBrowser(TabWidget):
url = urlutils.urlstring(self.currentWidget().url())
self.set_cmd_text.emit(':tabopen ' + url)
def openurl(self, url, count=None):
"""Open an url in the current/[count]th tab.
Command handler for :open.
url -- The URL to open.
"""
tab = self._widget(count)
if tab is not None:
tab.openurl(url)
def opencur(self):
"""Set the statusbar to :open and the current URL."""
url = urlutils.urlstring(self.currentWidget().url())
@ -183,16 +284,16 @@ class TabbedBrowser(TabWidget):
elif last_close == 'blank':
tab.openurl('about:blank')
def _cb_tab_shutdown(self, tab):
"""Called after a tab has been shut down completely."""
try:
self._tabs.remove(tab)
except ValueError:
logging.exception("tab {} could not be removed".format(tab))
logging.debug("Tabs after removing: {}".format(self._tabs))
if not self._tabs: # all tabs shut down
logging.debug("Tab shutdown complete.")
self.shutdown_complete.emit()
def openurl(self, url, count=None):
"""Open an url in the current/[count]th tab.
Command handler for :open.
url -- The URL to open.
"""
tab = self._widget(count)
if tab is not None:
tab.openurl(url)
def cur_reload(self, count=None):
"""Reload the current/[count]th tab.
@ -289,20 +390,6 @@ class TabbedBrowser(TabWidget):
"""
self._cur_scroll_percent(perc, count, Qt.Vertical)
def _cur_scroll_percent(self, perc=None, count=None, orientation=None):
"""Inner logic for cur_scroll_percent_(x|y)."""
if perc is None and count is None:
perc = 100
elif perc is None:
perc = int(count)
else:
perc = float(perc)
frame = self.currentWidget().page_.mainFrame()
m = frame.scrollBarMaximum(orientation)
if m == 0:
return
frame.setScrollBarValue(orientation, int(m * perc / 100))
def cur_scroll_page(self, mx, my, count=1):
"""Scroll the frame mx pages to the right and my pages down."""
# FIXME this might not work with HTML frames
@ -311,6 +398,30 @@ class TabbedBrowser(TabWidget):
page.mainFrame().scroll(int(count) * float(mx) * size.width(),
int(count) * float(my) * size.height())
def cur_yank(self, sel=False):
"""Yank the current url to the clipboard or primary selection.
Command handler for :yank.
"""
clip = QApplication.clipboard()
url = urlutils.urlstring(self.currentWidget().url())
mode = QClipboard.Selection if sel else QClipboard.Clipboard
clip.setText(url, mode)
# FIXME provide visual feedback
def cur_yank_title(self, sel=False):
"""Yank the current title to the clipboard or primary selection.
Command handler for :yanktitle.
"""
clip = QApplication.clipboard()
title = self.tabText(self.currentIndex())
mode = QClipboard.Selection if sel else QClipboard.Clipboard
clip.setText(title, mode)
# FIXME provide visual feedbac
def switch_prev(self, count=1):
"""Switch to the ([count]th) previous tab.
@ -337,30 +448,6 @@ class TabbedBrowser(TabWidget):
# FIXME
pass
def cur_yank(self, sel=False):
"""Yank the current url to the clipboard or primary selection.
Command handler for :yank.
"""
clip = QApplication.clipboard()
url = urlutils.urlstring(self.currentWidget().url())
mode = QClipboard.Selection if sel else QClipboard.Clipboard
clip.setText(url, mode)
# FIXME provide visual feedback
def cur_yank_title(self, sel=False):
"""Yank the current title to the clipboard or primary selection.
Command handler for :yanktitle.
"""
clip = QApplication.clipboard()
title = self.tabText(self.currentIndex())
mode = QClipboard.Selection if sel else QClipboard.Clipboard
clip.setText(title, mode)
# FIXME provide visual feedbac
def paste(self, sel=False):
"""Open a page from the clipboard.
@ -392,93 +479,6 @@ class TabbedBrowser(TabWidget):
self.keypress.emit(e)
super().keyPressEvent(e)
def _widget(self, count=None):
"""Return a widget based on a count/idx.
If count is None, return the current widget.
"""
if count is None:
return self.currentWidget()
elif 1 <= count <= self.count():
return self.widget(count - 1)
else:
return None
def _titleChanged_handler(self, text):
"""Set the title of a tab.
Slot for the titleChanged signal of any tab.
"""
logging.debug('title changed to "{}"'.format(text))
if text:
self.setTabText(self.indexOf(self.sender()), text)
else:
logging.debug('ignoring title change')
def _filter_factory(self, signal):
"""Return a partial functon calling _filter_signals with a signal."""
return functools.partial(self._filter_signals, signal)
def _filter_signals(self, signal, *args):
"""Filter signals and trigger TabbedBrowser signals if needed.
Triggers signal if the original signal was sent from the _current_ tab
and not from any other one.
The original signal does not matter, since we get the new signal and
all args.
The current value of the signal is also stored in tab.signal_cache so
it can be emitted later when the tab changes to the current tab.
signal -- The signal to emit if the sender was the current widget.
*args -- The args to pass to the signal.
"""
# FIXME BUG the signal cache ordering seems to be weird sometimes.
# How to reproduce:
# - Open tab
# - While loading, open another tab
# - Switch back to #1 when loading finished
# - It seems loadingStarted is before loadingFinished
sender = self.sender()
log_signal = not signal.signal.startswith('2cur_progress')
if log_signal:
logging.debug('signal {} (tab {})'.format(dbg_signal(signal, args),
self.indexOf(sender)))
if not isinstance(sender, BrowserTab):
# FIXME why does this happen?
logging.warn('Got signal {} by {} which is no tab!'.format(
dbg_signal(signal, args), sender))
return
sender.signal_cache.add(signal, args)
if self.currentWidget() == sender:
if log_signal:
logging.debug(' emitting')
return signal.emit(*args)
else:
if log_signal:
logging.debug(' ignoring')
def shutdown(self):
"""Try to shut down all tabs cleanly."""
try:
self.currentChanged.disconnect()
except TypeError:
pass
tabcount = self.count()
if tabcount == 0:
logging.debug("No tabs -> shutdown complete")
self.shutdown_complete.emit()
return
for tabidx in range(tabcount):
logging.debug("Shutting down tab {}/{}".format(tabidx, tabcount))
tab = self.widget(tabidx)
tab.shutdown(callback=functools.partial(self._cb_tab_shutdown,
tab))
class BrowserTab(QWebView):
@ -664,18 +664,6 @@ class BrowserPage(QWebPage):
self.network_access_manager = NetworkManager(self)
self.setNetworkAccessManager(self.network_access_manager)
def supportsExtension(self, ext):
"""Override QWebPage::supportsExtension to provide error pages."""
return ext in self._extension_handlers
def extension(self, ext, opt, out):
"""Override QWebPage::extension to provide error pages."""
try:
handler = self._extension_handlers[ext]
except KeyError:
return super().extension(ext, opt, out)
return handler(opt, out)
def _handle_errorpage(self, opt, out):
"""Display an error page if needed.
@ -695,6 +683,18 @@ class BrowserPage(QWebPage):
title=title, url=urlstr, error=info.errorString, icon='')
return True
def supportsExtension(self, ext):
"""Override QWebPage::supportsExtension to provide error pages."""
return ext in self._extension_handlers
def extension(self, ext, opt, out):
"""Override QWebPage::extension to provide error pages."""
try:
handler = self._extension_handlers[ext]
except KeyError:
return super().extension(ext, opt, out)
return handler(opt, out)
class NetworkManager(QNetworkAccessManager):

View File

@ -121,6 +121,29 @@ class CompletionView(QTreeView):
self.hide()
# FIXME set elidemode
def _next_idx(self, upwards):
"""Get the previous/next QModelIndex displayed in the view.
Used by tab_handler.
upwards -- Get previous item, not next.
"""
idx = self.selectionModel().currentIndex()
if not idx.isValid():
# No item selected yet
return self.model.first_item()
while True:
idx = self.indexAbove(idx) if upwards else self.indexBelow(idx)
# wrap around if we arrived at beginning/end
if not idx.isValid() and upwards:
return self.model.last_item()
elif not idx.isValid() and not upwards:
return self.model.first_item()
elif idx.parent().isValid():
# Item is a real item, not a category header -> success
return idx
def setmodel(self, model):
"""Switch completion to a new model.
@ -205,29 +228,6 @@ class CompletionView(QTreeView):
self._ignore_next = True
self.append_cmd_text.emit(self.model.data(idx) + ' ')
def _next_idx(self, upwards):
"""Get the previous/next QModelIndex displayed in the view.
Used by tab_handler.
upwards -- Get previous item, not next.
"""
idx = self.selectionModel().currentIndex()
if not idx.isValid():
# No item selected yet
return self.model.first_item()
while True:
idx = self.indexAbove(idx) if upwards else self.indexBelow(idx)
# wrap around if we arrived at beginning/end
if not idx.isValid() and upwards:
return self.model.last_item()
elif not idx.isValid() and not upwards:
return self.model.first_item()
elif idx.parent().isValid():
# Item is a real item, not a category header -> success
return idx
class _CompletionItemDelegate(QStyledItemDelegate):
@ -254,40 +254,6 @@ class _CompletionItemDelegate(QStyledItemDelegate):
self._style = None
super().__init__(parent)
def sizeHint(self, option, index):
"""Override sizeHint of QStyledItemDelegate.
Return the cell size based on the QTextDocument size, but might not
work correctly yet.
"""
value = index.data(Qt.SizeHintRole)
if value is not None:
return value
self._opt = QStyleOptionViewItem(option)
self.initStyleOption(self._opt, index)
self._style = self._opt.widget.style()
self._get_textdoc(index)
docsize = self._doc.size().toSize()
size = self._style.sizeFromContents(QStyle.CT_ItemViewItem, self._opt,
docsize, self._opt.widget)
return size + QSize(10, 1)
def paint(self, painter, option, index):
"""Override the QStyledItemDelegate paint function."""
self._painter = painter
self._painter.save()
self._opt = QStyleOptionViewItem(option)
self.initStyleOption(self._opt, index)
self._style = self._opt.widget.style()
self._draw_background()
self._draw_icon()
self._draw_text(index)
self._draw_focus_rect()
self._painter.restore()
def _draw_background(self):
"""Draw the background of an ItemViewItem."""
self._style.drawPrimitive(self._style.PE_PanelItemViewItem, self._opt,
@ -428,3 +394,37 @@ class _CompletionItemDelegate(QStyledItemDelegate):
o.backgroundColor = self._opt.palette.color(cg, role)
self._style.drawPrimitive(QStyle.PE_FrameFocusRect, o, self._painter,
self._opt.widget)
def sizeHint(self, option, index):
"""Override sizeHint of QStyledItemDelegate.
Return the cell size based on the QTextDocument size, but might not
work correctly yet.
"""
value = index.data(Qt.SizeHintRole)
if value is not None:
return value
self._opt = QStyleOptionViewItem(option)
self.initStyleOption(self._opt, index)
self._style = self._opt.widget.style()
self._get_textdoc(index)
docsize = self._doc.size().toSize()
size = self._style.sizeFromContents(QStyle.CT_ItemViewItem, self._opt,
docsize, self._opt.widget)
return size + QSize(10, 1)
def paint(self, painter, option, index):
"""Override the QStyledItemDelegate paint function."""
self._painter = painter
self._painter.save()
self._opt = QStyleOptionViewItem(option)
self.initStyleOption(self._opt, index)
self._style = self._opt.widget.style()
self._draw_background()
self._draw_icon()
self._draw_text(index)
self._draw_focus_rect()
self._painter.restore()

View File

@ -226,49 +226,6 @@ class _Command(QLineEdit):
sc.activated.connect(handler)
self._shortcuts.append(sc)
@pyqtSlot()
def _on_return_pressed(self):
"""Handle the command in the status bar."""
signals = {
':': self.got_cmd,
'/': self.got_search,
'?': self.got_search_rev,
}
self._histbrowse_stop()
text = self.text()
if not self.history or text != self.history[-1]:
self.history.append(text)
self.setText('')
if text[0] in signals:
signals[text[0]].emit(text.lstrip(text[0]))
@pyqtSlot(str)
def set_cmd_text(self, text):
"""Preset the statusbar to some text."""
self.setText(text)
self.setFocus()
@pyqtSlot(str)
def on_append_cmd_text(self, text):
"""Append text to the commandline."""
# FIXME do the right thing here
self.setText(':' + text)
self.setFocus()
def focusOutEvent(self, e):
"""Clear the statusbar text if it's explicitely unfocused."""
if e.reason() in [Qt.MouseFocusReason, Qt.TabFocusReason,
Qt.BacktabFocusReason, Qt.OtherFocusReason]:
self.setText('')
self._histbrowse_stop()
self.hide_completion.emit()
super().focusOutEvent(e)
def focusInEvent(self, e):
"""Clear error message when the statusbar is focused."""
self._statusbar.clear_error()
super().focusInEvent(e)
def _histbrowse_start(self):
"""Start browsing to the history.
@ -319,6 +276,49 @@ class _Command(QLineEdit):
self._tmphist, len(self._tmphist), self._histpos))
self.set_cmd_text(self._tmphist[self._histpos])
@pyqtSlot()
def _on_return_pressed(self):
"""Handle the command in the status bar."""
signals = {
':': self.got_cmd,
'/': self.got_search,
'?': self.got_search_rev,
}
self._histbrowse_stop()
text = self.text()
if not self.history or text != self.history[-1]:
self.history.append(text)
self.setText('')
if text[0] in signals:
signals[text[0]].emit(text.lstrip(text[0]))
@pyqtSlot(str)
def set_cmd_text(self, text):
"""Preset the statusbar to some text."""
self.setText(text)
self.setFocus()
@pyqtSlot(str)
def on_append_cmd_text(self, text):
"""Append text to the commandline."""
# FIXME do the right thing here
self.setText(':' + text)
self.setFocus()
def focusOutEvent(self, e):
"""Clear the statusbar text if it's explicitely unfocused."""
if e.reason() in [Qt.MouseFocusReason, Qt.TabFocusReason,
Qt.BacktabFocusReason, Qt.OtherFocusReason]:
self.setText('')
self._histbrowse_stop()
self.hide_completion.emit()
super().focusOutEvent(e)
def focusInEvent(self, e):
"""Clear error message when the statusbar is focused."""
self._statusbar.clear_error()
super().focusInEvent(e)
class _CommandValidator(QValidator):
@ -397,6 +397,15 @@ class TextBase(QLabel):
self._elidemode = elidemode
self._elided_text = ''
def _update_elided_text(self, width):
"""Update the elided text when necessary.
width -- The maximal width the text should take.
"""
self._elided_text = self.fontMetrics().elidedText(
self.text(), self._elidemode, width, Qt.TextShowMnemonic)
def setText(self, txt):
"""Extend QLabel::setText to update the elided text afterwards."""
super().setText(txt)
@ -407,15 +416,6 @@ class TextBase(QLabel):
super().resizeEvent(e)
self._update_elided_text(e.size().width())
def _update_elided_text(self, width):
"""Update the elided text when necessary.
width -- The maximal width the text should take.
"""
self._elided_text = self.fontMetrics().elidedText(
self.text(), self._elidemode, width, Qt.TextShowMnemonic)
def paintEvent(self, e):
"""Override QLabel::paintEvent to draw elided text."""
if self._elidemode == Qt.ElideNone: