Make hints work

This commit is contained in:
Florian Bruhin 2014-05-01 15:27:32 +02:00
parent 12b36de5b5
commit fd678ff864
5 changed files with 92 additions and 13 deletions

View File

@ -222,6 +222,26 @@ class CurCommandDispatcher(QObject):
"""Fire a completed hint."""
self._tabs.currentWidget().hintmanager.fire(keystr)
@cmdutils.register(instance='mainwindow.tabs.cur')
def prev_page(self):
"""Click on a "previous" link."""
widget = self._tabs.currentWidget()
frame = widget.page_.currentFrame()
if frame is None:
message.error("No frame focused!")
return
widget.hintmanager.click_prevnext(frame, prev=True)
@cmdutils.register(instance='mainwindow.tabs.cur')
def next_page(self):
"""Click on a "next" link."""
widget = self._tabs.currentWidget()
frame = widget.page_.currentFrame()
if frame is None:
message.error("No frame focused!")
return
widget.hintmanager.click_prevnext(frame, prev=False)
@pyqtSlot(str, int)
def search(self, text, flags):
"""Search for text in the current page.

View File

@ -207,19 +207,24 @@ class HintManager(QObject):
css, string))
return doc.lastChild()
def _click(self, elem):
def _click(self, elem, target=None, frame=None):
"""Click an element.
Args:
elem: The QWebElement to click.
target_override: Target which overrides self._target
"""
if self._target == 'rapid':
if target is not None:
pass
elif self._target == 'rapid':
target = 'bgtab'
else:
target = self._target
if frame is None:
frame = self._frame
self.set_open_target.emit(target)
point = elem.geometry().topLeft()
scrollpos = self._frame.scrollPosition()
scrollpos = frame.scrollPosition()
logging.debug("Clicking on \"{}\" at {}/{} - {}/{}".format(
elem.toPlainText(), point.x(), point.y(), scrollpos.x(),
scrollpos.y()))
@ -281,22 +286,23 @@ class HintManager(QObject):
def click_prevnext(self, frame, prev=False):
"""Click a "previous"/"next" element on the page."""
# First check for <link rel="prev(ious)|next">
self.target='normal'
elems = frame.findAllElements(webelem.SELECTORS['prevnext_rel'])
rel_values = ['prev', 'previous'] if prev else ['next']
for e in elems:
if e.attribute('rel') in rel_values:
self.click(e)
self._click(e, 'normal', frame)
return
# Then check for regular links
elems = frame.findAllElements(webelem.SELECTORS['prevnext'])
option = 'prev-regexes' if prev else 'next-regexes'
for regex in config.get('hints', option):
for e in elems:
if regex.match(e.toPlainText()):
self.click(e)
return
message.error("No prev/forward links found!")
if elems:
for regex in config.get('hints', option):
for e in elems:
if regex.match(e.toPlainText()):
self._click(e, 'normal', frame)
return
message.error("No {} links found!".format("prev" if prev
else "forward"))
def start(self, frame, baseurl, mode='all', target='normal'):

View File

@ -17,7 +17,9 @@
"""Setting options used for qutebrowser."""
import re
import shlex
from sre_constants import error as RegexError
from PyQt5.QtGui import QColor
@ -511,6 +513,42 @@ class KeyBindingName(BaseType):
pass
class Regex(BaseType):
"""A regular expression."""
def __init__(self, flags=0):
self.flags = flags
def validate(self, value):
try:
re.compile(value, self.flags)
except RegexError as e:
raise ValidationError(value, "must be a valid regex - " + str(e))
def transform(self, value):
return re.compile(value, self.flags)
class RegexList(List):
"""A list of regexes."""
def __init__(self, flags=0):
self.flags=flags
def transform(self, value):
vals = super().transform(value)
return [re.compile(pattern, self.flags) for pattern in vals]
def validate(self, value):
try:
self.transform(value)
except RegexError as e:
raise ValidationError(value, "must be a list valid regexes - " +
str(e))
class AutoSearch(BaseType):
"""Whether to start a search when something else than an URL is entered."""

View File

@ -24,6 +24,7 @@ SECTION_DESC: A dictionary with descriptions for sections.
DATA: The config defaults, an OrderedDict of sections.
"""
import re
from collections import OrderedDict
from qutebrowser.config._value import SettingValue
@ -397,6 +398,18 @@ DATA = OrderedDict([
('auto-follow',
SettingValue(types.Bool(), 'true'),
"Whether to auto-follow a hint if there's only one left."),
('next-regexes',
SettingValue(types.RegexList(flags=re.IGNORECASE),
r'\bnext\b,\bmore\b,\bnewer\b,^>$$,^(>>|»|→|≫)$$,'
r'^(>|»|→|≫),(>|»|→|≫)$$'),
"A comma-separated list of regexes to use for 'next' links."),
('prev-regexes',
SettingValue(types.RegexList(flags=re.IGNORECASE),
r'\bprev(ious)\b,\bback\b,\bolder\b,^<$$,^(<<|«|←|≪)$$,'
r'^(<|«|←|≪),(<|«|←|≪)$$'),
"A comma-separated list of regexes to use for 'prev' links."),
)),
('searchengines', sect.ValueList(
@ -456,6 +469,8 @@ DATA = OrderedDict([
('PP', 'tabpaste sel'),
('-', 'zoomout'),
('+', 'zoomin'),
('[[', 'prev_page'),
(']]', 'next_page'),
('<Ctrl-V>', 'enter_mode passthrough'),
('<Ctrl-Q>', 'quit'),
('<Ctrl-Shift-T>', 'undo'),

View File

@ -36,8 +36,8 @@ SELECTORS = {
'input[type=tel], input[type=number], '
'input[type=password], input[type=search], textarea'),
'url': '[src], [href]',
'prevnext_rel': 'link [role=link]',
'prevnext': 'a button [role=button]',
'prevnext_rel': 'link, [role=link]',
'prevnext': 'a, button, [role=button]',
}
SELECTORS['editable_focused'] = ', '.join(