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.""" """Fire a completed hint."""
self._tabs.currentWidget().hintmanager.fire(keystr) 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) @pyqtSlot(str, int)
def search(self, text, flags): def search(self, text, flags):
"""Search for text in the current page. """Search for text in the current page.

View File

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

View File

@ -17,7 +17,9 @@
"""Setting options used for qutebrowser.""" """Setting options used for qutebrowser."""
import re
import shlex import shlex
from sre_constants import error as RegexError
from PyQt5.QtGui import QColor from PyQt5.QtGui import QColor
@ -511,6 +513,42 @@ class KeyBindingName(BaseType):
pass 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): class AutoSearch(BaseType):
"""Whether to start a search when something else than an URL is entered.""" """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. DATA: The config defaults, an OrderedDict of sections.
""" """
import re
from collections import OrderedDict from collections import OrderedDict
from qutebrowser.config._value import SettingValue from qutebrowser.config._value import SettingValue
@ -397,6 +398,18 @@ DATA = OrderedDict([
('auto-follow', ('auto-follow',
SettingValue(types.Bool(), 'true'), SettingValue(types.Bool(), 'true'),
"Whether to auto-follow a hint if there's only one left."), "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( ('searchengines', sect.ValueList(
@ -456,6 +469,8 @@ DATA = OrderedDict([
('PP', 'tabpaste sel'), ('PP', 'tabpaste sel'),
('-', 'zoomout'), ('-', 'zoomout'),
('+', 'zoomin'), ('+', 'zoomin'),
('[[', 'prev_page'),
(']]', 'next_page'),
('<Ctrl-V>', 'enter_mode passthrough'), ('<Ctrl-V>', 'enter_mode passthrough'),
('<Ctrl-Q>', 'quit'), ('<Ctrl-Q>', 'quit'),
('<Ctrl-Shift-T>', 'undo'), ('<Ctrl-Shift-T>', 'undo'),

View File

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