Use simple enums for constants
This commit is contained in:
parent
91e7565d1e
commit
4ebe643ea6
1
THANKS
1
THANKS
@ -27,6 +27,7 @@ valuable ones:
|
||||
- Bleeding Fingers
|
||||
- artyom.stv
|
||||
- hank
|
||||
- Alec Thomas
|
||||
|
||||
Thanks to these people for helpful bits and pieces in the Qt bugtracker and IRC
|
||||
channels:
|
||||
|
1
TODO
1
TODO
@ -7,7 +7,6 @@ Style
|
||||
=====
|
||||
|
||||
_foo = QApplication.instance().obj.foo for global singletons?
|
||||
Use py3.4 Enums with a backport to qutebrowser.utils?
|
||||
initialize completion models at some nicer place (not in widget)
|
||||
|
||||
Major features
|
||||
|
@ -33,6 +33,7 @@ import qutebrowser.utils.message as message
|
||||
import qutebrowser.commands.utils as cmdutils
|
||||
import qutebrowser.utils.webelem as webelem
|
||||
import qutebrowser.config.config as config
|
||||
import qutebrowser.browser.hints as hints
|
||||
from qutebrowser.utils.misc import shell_escape
|
||||
|
||||
|
||||
@ -216,21 +217,31 @@ class CurCommandDispatcher(QObject):
|
||||
break
|
||||
|
||||
@cmdutils.register(instance='mainwindow.tabs.cur')
|
||||
def hint(self, mode='all', target='normal'):
|
||||
def hint(self, groupstr='all', targetstr='normal'):
|
||||
"""Start hinting.
|
||||
|
||||
Command handler for :hint.
|
||||
|
||||
Args:
|
||||
mode: The hinting mode to use.
|
||||
target: Where to open the links.
|
||||
groupstr: The hinting mode to use.
|
||||
targetstr: Where to open the links.
|
||||
"""
|
||||
widget = self._tabs.currentWidget()
|
||||
frame = widget.page_.currentFrame()
|
||||
if frame is None:
|
||||
message.error("No frame focused!")
|
||||
else:
|
||||
widget.hintmanager.start(frame, widget.url(), mode, target)
|
||||
return
|
||||
try:
|
||||
group = getattr(webelem.Group, groupstr)
|
||||
except AttributeError:
|
||||
message.error("Unknown hinting group {}!".format(groupstr))
|
||||
return
|
||||
try:
|
||||
target = getattr(hints.Target, targetstr)
|
||||
except AttributeError:
|
||||
message.error("Unknown hinting target {}!".format(targetstr))
|
||||
return
|
||||
widget.hintmanager.start(frame, widget.url(), group, target)
|
||||
|
||||
@cmdutils.register(instance='mainwindow.tabs.cur', hide=True)
|
||||
def follow_hint(self):
|
||||
|
@ -30,11 +30,16 @@ import qutebrowser.keyinput.modeman as modeman
|
||||
import qutebrowser.utils.message as message
|
||||
import qutebrowser.utils.url as urlutils
|
||||
import qutebrowser.utils.webelem as webelem
|
||||
from qutebrowser.utils.usertypes import enum
|
||||
|
||||
|
||||
ElemTuple = namedtuple('ElemTuple', 'elem, label')
|
||||
|
||||
|
||||
Target = enum('normal', 'tab', 'bgtab', 'yank', 'yank_primary', 'cmd',
|
||||
'cmd_tab', 'cmd_bgtab', 'rapid')
|
||||
|
||||
|
||||
class HintManager(QObject):
|
||||
|
||||
"""Manage drawing hints over links or other elements.
|
||||
@ -47,10 +52,10 @@ class HintManager(QObject):
|
||||
_elems: A mapping from keystrings to (elem, label) namedtuples.
|
||||
_baseurl: The URL of the current page.
|
||||
_target: What to do with the opened links.
|
||||
'normal'/'tab'/'bgtab': Get passed to BrowserTab.
|
||||
'yank'/'yank_primary': Yank to clipboard/primary selection
|
||||
'cmd'/'cmd_tab'/'cmd_bgtab': Enter link to commandline
|
||||
'rapid': Rapid mode with background tabs
|
||||
normal/tab/bgtab: Get passed to BrowserTab.
|
||||
yank/yank_primary: Yank to clipboard/primary selection
|
||||
cmd/cmd_tab/cmd_bgtab: Enter link to commandline
|
||||
rapid: Rapid mode with background tabs
|
||||
_to_follow: The link to follow when enter is pressed.
|
||||
|
||||
Signals:
|
||||
@ -217,11 +222,11 @@ class HintManager(QObject):
|
||||
Args:
|
||||
elem: The QWebElement to click.
|
||||
"""
|
||||
if self._target == 'rapid':
|
||||
target = 'bgtab'
|
||||
if self._target == Target.rapid:
|
||||
target = Target.bgtab
|
||||
else:
|
||||
target = self._target
|
||||
self.set_open_target.emit(target)
|
||||
self.set_open_target.emit(Target.reverse_mapping[target])
|
||||
point = elem.geometry().topLeft()
|
||||
scrollpos = self._frame.scrollPosition()
|
||||
logging.debug("Clicking on \"{}\" at {}/{} - {}/{}".format(
|
||||
@ -245,7 +250,7 @@ class HintManager(QObject):
|
||||
Args:
|
||||
link: The URL to open.
|
||||
"""
|
||||
sel = self._target == 'yank_primary'
|
||||
sel = self._target == Target.yank_primary
|
||||
mode = QClipboard.Selection if sel else QClipboard.Clipboard
|
||||
QApplication.clipboard().setText(urlutils.urlstring(link), mode)
|
||||
message.info("URL yanked to {}".format("primary selection" if sel
|
||||
@ -258,9 +263,9 @@ class HintManager(QObject):
|
||||
link: The link to open.
|
||||
"""
|
||||
commands = {
|
||||
'cmd': 'open',
|
||||
'cmd_tab': 'tabopen',
|
||||
'cmd_bgtab': 'backtabopen',
|
||||
Target.cmd: 'open',
|
||||
Target.cmd_tab: 'tabopen',
|
||||
Target.cmd_bgtab: 'backtabopen',
|
||||
}
|
||||
message.set_cmd_text(':{} {}'.format(commands[self._target],
|
||||
urlutils.urlstring(link)))
|
||||
@ -325,23 +330,20 @@ class HintManager(QObject):
|
||||
return
|
||||
self.openurl.emit(link, newtab)
|
||||
|
||||
def start(self, frame, baseurl, mode='all', target='normal'):
|
||||
def start(self, frame, baseurl, group=webelem.Group.all,
|
||||
target=Target.normal):
|
||||
"""Start hinting.
|
||||
|
||||
Args:
|
||||
frame: The QWebFrame to place hints in.
|
||||
baseurl: URL of the current page.
|
||||
mode: The mode to be used.
|
||||
group: Which group of elements to hint.
|
||||
target: What to do with the link. See attribute docstring.
|
||||
|
||||
Emit:
|
||||
hint_strings_updated: Emitted to update keypraser.
|
||||
"""
|
||||
try:
|
||||
elems = frame.findAllElements(webelem.SELECTORS[mode])
|
||||
except KeyError:
|
||||
message.error("Hinting mode '{}' does not exist!".format(mode))
|
||||
return
|
||||
elems = frame.findAllElements(webelem.SELECTORS[group])
|
||||
self._target = target
|
||||
self._baseurl = baseurl
|
||||
if frame is None:
|
||||
@ -350,7 +352,7 @@ class HintManager(QObject):
|
||||
# on_mode_left, we are extra careful here.
|
||||
raise ValueError("start() was called with frame=None")
|
||||
self._frame = frame
|
||||
filterfunc = webelem.FILTERS.get(mode, lambda e: True)
|
||||
filterfunc = webelem.FILTERS.get(group, lambda e: True)
|
||||
visible_elems = []
|
||||
for e in elems:
|
||||
if filterfunc(e) and webelem.is_visible(e, self._frame):
|
||||
@ -359,21 +361,17 @@ class HintManager(QObject):
|
||||
message.error("No elements found.")
|
||||
return
|
||||
texts = {
|
||||
'normal': "Follow hint...",
|
||||
'tab': "Follow hint in new tab...",
|
||||
'bgtab': "Follow hint in background tab...",
|
||||
'yank': "Yank hint to clipboard...",
|
||||
'yank_primary': "Yank hint to primary selection...",
|
||||
'cmd': "Set hint in commandline...",
|
||||
'cmd_tab': "Set hint in commandline as new tab...",
|
||||
'cmd_bgtab': "Set hint in commandline as background tab...",
|
||||
'rapid': "Follow hint (rapid mode)...",
|
||||
Target.normal: "Follow hint...",
|
||||
Target.tab: "Follow hint in new tab...",
|
||||
Target.bgtab: "Follow hint in background tab...",
|
||||
Target.yank: "Yank hint to clipboard...",
|
||||
Target.yank_primary: "Yank hint to primary selection...",
|
||||
Target.cmd: "Set hint in commandline...",
|
||||
Target.cmd_tab: "Set hint in commandline as new tab...",
|
||||
Target.cmd_bgtab: "Set hint in commandline as background tab...",
|
||||
Target.rapid: "Follow hint (rapid mode)...",
|
||||
}
|
||||
try:
|
||||
message.text(texts[target])
|
||||
except KeyError:
|
||||
message.error("Hinting target '{}' does not exist!".format(target))
|
||||
return
|
||||
message.text(texts[target])
|
||||
strings = self._hint_strings(visible_elems)
|
||||
for e, string in zip(visible_elems, strings):
|
||||
label = self._draw_label(e, string)
|
||||
@ -426,18 +424,18 @@ class HintManager(QObject):
|
||||
return
|
||||
# Handlers which take a QWebElement
|
||||
elem_handlers = {
|
||||
'normal': self._click,
|
||||
'tab': self._click,
|
||||
'bgtab': self._click,
|
||||
'rapid': self._click,
|
||||
Target.normal: self._click,
|
||||
Target.tab: self._click,
|
||||
Target.bgtab: self._click,
|
||||
Target.rapid: self._click,
|
||||
}
|
||||
# Handlers which take a link string
|
||||
link_handlers = {
|
||||
'yank': self._yank,
|
||||
'yank_primary': self._yank,
|
||||
'cmd': self._preset_cmd_text,
|
||||
'cmd_tab': self._preset_cmd_text,
|
||||
'cmd_bgtab': self._preset_cmd_text,
|
||||
Target.yank: self._yank,
|
||||
Target.yank_primary: self._yank,
|
||||
Target.cmd: self._preset_cmd_text,
|
||||
Target.cmd_tab: self._preset_cmd_text,
|
||||
Target.cmd_bgtab: self._preset_cmd_text,
|
||||
}
|
||||
elem = self._elems[keystr].elem
|
||||
if self._target in elem_handlers:
|
||||
@ -448,7 +446,9 @@ class HintManager(QObject):
|
||||
message.error("No suitable link found for this element.")
|
||||
return
|
||||
link_handlers[self._target](link)
|
||||
if self._target != 'rapid':
|
||||
else:
|
||||
raise ValueError("No suitable handler found!")
|
||||
if self._target != Target.rapid:
|
||||
modeman.leave('hint')
|
||||
|
||||
def follow_hint(self):
|
||||
|
@ -30,95 +30,96 @@ from PyQt5.QtCore import pyqtSlot
|
||||
from PyQt5.QtWebKit import QWebSettings
|
||||
|
||||
import qutebrowser.config.config as config
|
||||
from qutebrowser.utils.usertypes import enum
|
||||
|
||||
ATTRIBUTE = 0
|
||||
SETTER = 1
|
||||
STATIC_SETTER = 2
|
||||
MapType = enum('attribute', 'setter', 'static_setter')
|
||||
|
||||
|
||||
MAPPINGS = {
|
||||
# noqa
|
||||
'auto-load-images':
|
||||
(ATTRIBUTE, QWebSettings.AutoLoadImages),
|
||||
(MapType.attribute, QWebSettings.AutoLoadImages),
|
||||
'dns-prefetch-enabled':
|
||||
(ATTRIBUTE, QWebSettings.DnsPrefetchEnabled),
|
||||
(MapType.attribute, QWebSettings.DnsPrefetchEnabled),
|
||||
'javascript-enabled':
|
||||
(ATTRIBUTE, QWebSettings.JavascriptEnabled),
|
||||
(MapType.attribute, QWebSettings.JavascriptEnabled),
|
||||
#'java-enabled':
|
||||
# (ATTRIBUTE, QWebSettings.JavaEnabled),
|
||||
# (MapType.attribute, QWebSettings.JavaEnabled),
|
||||
'plugins-enabled':
|
||||
(ATTRIBUTE, QWebSettings.PluginsEnabled),
|
||||
(MapType.attribute, QWebSettings.PluginsEnabled),
|
||||
'private-browsing-enabled':
|
||||
(ATTRIBUTE, QWebSettings.PrivateBrowsingEnabled),
|
||||
(MapType.attribute, QWebSettings.PrivateBrowsingEnabled),
|
||||
'javascript-can-open-windows':
|
||||
(ATTRIBUTE, QWebSettings.JavascriptCanOpenWindows),
|
||||
(MapType.attribute, QWebSettings.JavascriptCanOpenWindows),
|
||||
'javascript-can-close-windows':
|
||||
(ATTRIBUTE, QWebSettings.JavascriptCanCloseWindows),
|
||||
(MapType.attribute, QWebSettings.JavascriptCanCloseWindows),
|
||||
'javascript-can-access-clipboard':
|
||||
(ATTRIBUTE, QWebSettings.JavascriptCanAccessClipboard),
|
||||
(MapType.attribute, QWebSettings.JavascriptCanAccessClipboard),
|
||||
'developer-extras-enabled':
|
||||
(ATTRIBUTE, QWebSettings.DeveloperExtrasEnabled),
|
||||
(MapType.attribute, QWebSettings.DeveloperExtrasEnabled),
|
||||
'spatial-navigation-enabled':
|
||||
(ATTRIBUTE, QWebSettings.SpatialNavigationEnabled),
|
||||
(MapType.attribute, QWebSettings.SpatialNavigationEnabled),
|
||||
'links-included-in-focus-chain':
|
||||
(ATTRIBUTE, QWebSettings.LinksIncludedInFocusChain),
|
||||
(MapType.attribute, QWebSettings.LinksIncludedInFocusChain),
|
||||
'zoom-text-only':
|
||||
(ATTRIBUTE, QWebSettings.ZoomTextOnly),
|
||||
(MapType.attribute, QWebSettings.ZoomTextOnly),
|
||||
'print-element-backgrounds':
|
||||
(ATTRIBUTE, QWebSettings.PrintElementBackgrounds),
|
||||
(MapType.attribute, QWebSettings.PrintElementBackgrounds),
|
||||
'offline-storage-database-enabled':
|
||||
(ATTRIBUTE, QWebSettings.OfflineStorageDatabaseEnabled),
|
||||
(MapType.attribute, QWebSettings.OfflineStorageDatabaseEnabled),
|
||||
'offline-web-application-storage-enabled':
|
||||
(ATTRIBUTE, QWebSettings.OfflineWebApplicationCacheEnabled),
|
||||
(MapType.attribute, QWebSettings.OfflineWebApplicationCacheEnabled),
|
||||
'local-storage-enabled':
|
||||
(ATTRIBUTE, QWebSettings.LocalStorageEnabled),
|
||||
(MapType.attribute, QWebSettings.LocalStorageEnabled),
|
||||
'local-content-can-access-remote-urls':
|
||||
(ATTRIBUTE, QWebSettings.LocalContentCanAccessRemoteUrls),
|
||||
(MapType.attribute, QWebSettings.LocalContentCanAccessRemoteUrls),
|
||||
'local-content-can-access-file-urls':
|
||||
(ATTRIBUTE, QWebSettings.LocalContentCanAccessFileUrls),
|
||||
(MapType.attribute, QWebSettings.LocalContentCanAccessFileUrls),
|
||||
'xss-auditing-enabled':
|
||||
(ATTRIBUTE, QWebSettings.XSSAuditingEnabled),
|
||||
(MapType.attribute, QWebSettings.XSSAuditingEnabled),
|
||||
#'accelerated-compositing-enabled':
|
||||
# (ATTRIBUTE, QWebSettings.AcceleratedCompositingEnabled),
|
||||
# (MapType.attribute, QWebSettings.AcceleratedCompositingEnabled),
|
||||
#'tiled-backing-store-enabled':
|
||||
# (ATTRIBUTE, QWebSettings.TiledBackingStoreEnabled),
|
||||
# (MapType.attribute, QWebSettings.TiledBackingStoreEnabled),
|
||||
'frame-flattening-enabled':
|
||||
(ATTRIBUTE, QWebSettings.FrameFlatteningEnabled),
|
||||
(MapType.attribute, QWebSettings.FrameFlatteningEnabled),
|
||||
'site-specific-quirks-enabled':
|
||||
(ATTRIBUTE, QWebSettings.SiteSpecificQuirksEnabled),
|
||||
(MapType.attribute, QWebSettings.SiteSpecificQuirksEnabled),
|
||||
'user-stylesheet':
|
||||
(SETTER, lambda qws, v: qws.setUserStyleSheetUrl(v)),
|
||||
(MapType.setter, lambda qws, v: qws.setUserStyleSheetUrl(v)),
|
||||
'css-media-type':
|
||||
(SETTER, lambda qws, v: qws.setCSSMediaType(v)),
|
||||
(MapType.setter, lambda qws, v: qws.setCSSMediaType(v)),
|
||||
'default-encoding':
|
||||
(SETTER, lambda qws, v: qws.setDefaultTextEncoding(v)),
|
||||
(MapType.setter, lambda qws, v: qws.setDefaultTextEncoding(v)),
|
||||
'font-family-standard':
|
||||
(SETTER, lambda qws, v:
|
||||
(MapType.setter, lambda qws, v:
|
||||
qws.setFontFamily(QWebSettings.StandardFont, v)),
|
||||
'font-family-fixed':
|
||||
(SETTER, lambda qws, v:
|
||||
(MapType.setter, lambda qws, v:
|
||||
qws.setFontFamily(QWebSettings.FixedFont, v)),
|
||||
'font-family-serif':
|
||||
(SETTER, lambda qws, v:
|
||||
(MapType.setter, lambda qws, v:
|
||||
qws.setFontFamily(QWebSettings.SerifFont, v)),
|
||||
'font-family-sans-serif':
|
||||
(SETTER, lambda qws, v:
|
||||
(MapType.setter, lambda qws, v:
|
||||
qws.setFontFamily(QWebSettings.SansSerifFont, v)),
|
||||
'font-family-cursive':
|
||||
(SETTER, lambda qws, v:
|
||||
(MapType.setter, lambda qws, v:
|
||||
qws.setFontFamily(QWebSettings.CursiveFont, v)),
|
||||
'font-family-fantasy':
|
||||
(SETTER, lambda qws, v:
|
||||
(MapType.setter, lambda qws, v:
|
||||
qws.setFontFamily(QWebSettings.FantasyFont, v)),
|
||||
'maximum-pages-in-cache':
|
||||
(STATIC_SETTER, lambda v: QWebSettings.setMaximumPagesInCache(v)),
|
||||
(MapType.static_setter, lambda v:
|
||||
QWebSettings.setMaximumPagesInCache(v)),
|
||||
'object-cache-capacities':
|
||||
(STATIC_SETTER, lambda v: QWebSettings.setObjectCacheCapacities(*v)),
|
||||
(MapType.static_setter, lambda v:
|
||||
QWebSettings.setObjectCacheCapacities(*v)),
|
||||
'offline-storage-default-quota':
|
||||
(STATIC_SETTER, lambda v:
|
||||
(MapType.static_setter, lambda v:
|
||||
QWebSettings.setOfflineStorageDefaultQuota(v)),
|
||||
'offline-web-application-cache-quota':
|
||||
(STATIC_SETTER, lambda v:
|
||||
(MapType.static_setter, lambda v:
|
||||
QWebSettings.setOfflineWebApplicationCacheQuota(v)),
|
||||
}
|
||||
|
||||
@ -130,15 +131,16 @@ def _set_setting(typ, arg, value):
|
||||
"""Set a QWebSettings setting.
|
||||
|
||||
Args:
|
||||
typ: The type of the item (ATTRIBUTE/SETTER/STATIC_SETTER)
|
||||
typ: The type of the item
|
||||
(MapType.attribute/MapType.setter/MapType.static_setter)
|
||||
arg: The argument (attribute/handler)
|
||||
value: The value to set.
|
||||
"""
|
||||
if typ == ATTRIBUTE:
|
||||
if typ == MapType.attribute:
|
||||
settings.setAttribute(arg, value)
|
||||
elif typ == SETTER and value is not None:
|
||||
elif typ == MapType.setter and value is not None:
|
||||
arg(settings, value)
|
||||
elif typ == STATIC_SETTER and value is not None:
|
||||
elif typ == MapType.static_setter and value is not None:
|
||||
arg(value)
|
||||
|
||||
|
||||
|
@ -25,6 +25,7 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject, QTimer
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
|
||||
import qutebrowser.config.config as config
|
||||
from qutebrowser.utils.usertypes import enum
|
||||
|
||||
|
||||
class BaseKeyParser(QObject):
|
||||
@ -35,14 +36,16 @@ class BaseKeyParser(QObject):
|
||||
execute() to do whatever they want to.
|
||||
|
||||
Class Attributes:
|
||||
MATCH_PARTIAL: Constant for a partial match (no keychain matched yet,
|
||||
but it's still possible in the future.
|
||||
MATCH_DEFINITIVE: Constant for a full match (keychain matches exactly).
|
||||
MATCH_AMBIGUOUS: There are both a partial and a definitive match.
|
||||
MATCH_NONE: Constant for no match (no more matches possible).
|
||||
Match: types of a match between a binding and the keystring.
|
||||
partial: No keychain matched yet, but it's still possible in the
|
||||
future.
|
||||
definitive: Keychain matches exactly.
|
||||
ambiguous: There are both a partial and a definitive match.
|
||||
none: No more matches possible.
|
||||
|
||||
TYPE_CHAIN: execute() was called via a chain-like keybinding
|
||||
TYPE_SPECIAL: execute() was called via a special keybinding
|
||||
Types: type of a keybinding.
|
||||
chain: execute() was called via a chain-like keybinding
|
||||
special: execute() was called via a special keybinding
|
||||
|
||||
Attributes:
|
||||
bindings: Bound keybindings
|
||||
@ -60,13 +63,8 @@ class BaseKeyParser(QObject):
|
||||
|
||||
keystring_updated = pyqtSignal(str)
|
||||
|
||||
MATCH_PARTIAL = 0
|
||||
MATCH_DEFINITIVE = 1
|
||||
MATCH_AMBIGUOUS = 2
|
||||
MATCH_NONE = 3
|
||||
|
||||
TYPE_CHAIN = 0
|
||||
TYPE_SPECIAL = 1
|
||||
Match = enum('partial', 'definitive', 'ambiguous', 'none')
|
||||
Type = enum('chain', 'special')
|
||||
|
||||
def __init__(self, parent=None, supports_count=None,
|
||||
supports_chains=False):
|
||||
@ -134,7 +132,7 @@ class BaseKeyParser(QObject):
|
||||
except KeyError:
|
||||
logging.debug("No binding found for {}.".format(modstr + keystr))
|
||||
return False
|
||||
self.execute(cmdstr, self.TYPE_SPECIAL)
|
||||
self.execute(cmdstr, self.Type.special)
|
||||
return True
|
||||
|
||||
def _handle_single_key(self, e):
|
||||
@ -173,15 +171,15 @@ class BaseKeyParser(QObject):
|
||||
|
||||
(match, binding) = self._match_key(cmd_input)
|
||||
|
||||
if match == self.MATCH_DEFINITIVE:
|
||||
if match == self.Match.definitive:
|
||||
self._keystring = ''
|
||||
self.execute(binding, self.TYPE_CHAIN, count)
|
||||
elif match == self.MATCH_AMBIGUOUS:
|
||||
self.execute(binding, self.Type.chain, count)
|
||||
elif match == self.Match.ambiguous:
|
||||
self._handle_ambiguous_match(binding, count)
|
||||
elif match == self.MATCH_PARTIAL:
|
||||
elif match == self.Match.partial:
|
||||
logging.debug("No match for \"{}\" (added {})".format(
|
||||
self._keystring, txt))
|
||||
elif match == self.MATCH_NONE:
|
||||
elif match == self.Match.none:
|
||||
logging.debug("Giving up with \"{}\", no matches".format(
|
||||
self._keystring))
|
||||
self._keystring = ''
|
||||
@ -196,11 +194,11 @@ class BaseKeyParser(QObject):
|
||||
|
||||
Return:
|
||||
A tuple (matchtype, binding).
|
||||
matchtype: MATCH_DEFINITIVE, MATCH_AMBIGUOUS, MATCH_PARTIAL or
|
||||
MATCH_NONE
|
||||
binding: - None with MATCH_PARTIAL/MATCH_NONE
|
||||
- The found binding with MATCH_DEFINITIVE/
|
||||
MATCH_AMBIGUOUS
|
||||
matchtype: Match.definitive, Match.ambiguous, Match.partial or
|
||||
Match.none
|
||||
binding: - None with Match.partial/Match.none
|
||||
- The found binding with Match.definitive/
|
||||
Match.ambiguous
|
||||
"""
|
||||
# A (cmd_input, binding) tuple (k, v of bindings) or None.
|
||||
definitive_match = None
|
||||
@ -219,13 +217,13 @@ class BaseKeyParser(QObject):
|
||||
partial_match = True
|
||||
break
|
||||
if definitive_match is not None and partial_match:
|
||||
return (self.MATCH_AMBIGUOUS, definitive_match[1])
|
||||
return (self.Match.ambiguous, definitive_match[1])
|
||||
elif definitive_match is not None:
|
||||
return (self.MATCH_DEFINITIVE, definitive_match[1])
|
||||
return (self.Match.definitive, definitive_match[1])
|
||||
elif partial_match:
|
||||
return (self.MATCH_PARTIAL, None)
|
||||
return (self.Match.partial, None)
|
||||
else:
|
||||
return (self.MATCH_NONE, None)
|
||||
return (self.Match.none, None)
|
||||
|
||||
def _stop_delayed_exec(self):
|
||||
"""Stop a delayed execution if any is running."""
|
||||
@ -246,7 +244,7 @@ class BaseKeyParser(QObject):
|
||||
if time == 0:
|
||||
# execute immediately
|
||||
self._keystring = ''
|
||||
self.execute(binding, self.TYPE_CHAIN, count)
|
||||
self.execute(binding, self.Type.chain, count)
|
||||
else:
|
||||
# execute in `time' ms
|
||||
logging.debug("Scheduling execution of {} in {}ms".format(binding,
|
||||
@ -271,7 +269,7 @@ class BaseKeyParser(QObject):
|
||||
self._timer = None
|
||||
self._keystring = ''
|
||||
self.keystring_updated.emit(self._keystring)
|
||||
self.execute(command, self.TYPE_CHAIN, count)
|
||||
self.execute(command, self.Type.chain, count)
|
||||
|
||||
def handle(self, e):
|
||||
"""Handle a new keypress and call the respective handlers.
|
||||
@ -329,7 +327,7 @@ class BaseKeyParser(QObject):
|
||||
|
||||
Args:
|
||||
cmdstr: The command to execute as a string.
|
||||
keytype: TYPE_CHAIN or TYPE_SPECIAL
|
||||
keytype: Type.chain or Type.special
|
||||
count: The count if given.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
@ -127,9 +127,9 @@ class HintKeyParser(CommandKeyParser):
|
||||
"""Handle a completed keychain.
|
||||
|
||||
Emit:
|
||||
fire_hint: Emitted if keytype is TYPE_CHAIN
|
||||
fire_hint: Emitted if keytype is chain
|
||||
"""
|
||||
if keytype == self.TYPE_CHAIN:
|
||||
if keytype == self.Type.chain:
|
||||
self.fire_hint.emit(cmdstr)
|
||||
else:
|
||||
# execute as command
|
||||
|
@ -133,21 +133,21 @@ class OneTests(TestCase):
|
||||
self.nl = NeighborList([1], default=1)
|
||||
|
||||
def test_first_wrap(self):
|
||||
self.nl._mode = NeighborList.WRAP
|
||||
self.nl._mode = NeighborList.Modes.wrap
|
||||
self.nl.firstitem()
|
||||
self.assertEqual(self.nl.idx, 0)
|
||||
self.assertEqual(self.nl.previtem(), 1)
|
||||
self.assertEqual(self.nl.idx, 0)
|
||||
|
||||
def test_first_block(self):
|
||||
self.nl._mode = NeighborList.BLOCK
|
||||
self.nl._mode = NeighborList.Modes.block
|
||||
self.nl.firstitem()
|
||||
self.assertEqual(self.nl.idx, 0)
|
||||
self.assertEqual(self.nl.previtem(), 1)
|
||||
self.assertEqual(self.nl.idx, 0)
|
||||
|
||||
def test_first_raise(self):
|
||||
self.nl._mode = NeighborList.RAISE
|
||||
self.nl._mode = NeighborList.Modes.exception
|
||||
self.nl.firstitem()
|
||||
self.assertEqual(self.nl.idx, 0)
|
||||
with self.assertRaises(IndexError):
|
||||
@ -155,21 +155,21 @@ class OneTests(TestCase):
|
||||
self.assertEqual(self.nl.idx, 0)
|
||||
|
||||
def test_last_wrap(self):
|
||||
self.nl._mode = NeighborList.WRAP
|
||||
self.nl._mode = NeighborList.Modes.wrap
|
||||
self.nl.lastitem()
|
||||
self.assertEqual(self.nl.idx, 0)
|
||||
self.assertEqual(self.nl.nextitem(), 1)
|
||||
self.assertEqual(self.nl.idx, 0)
|
||||
|
||||
def test_last_block(self):
|
||||
self.nl._mode = NeighborList.BLOCK
|
||||
self.nl._mode = NeighborList.Modes.block
|
||||
self.nl.lastitem()
|
||||
self.assertEqual(self.nl.idx, 0)
|
||||
self.assertEqual(self.nl.nextitem(), 1)
|
||||
self.assertEqual(self.nl.idx, 0)
|
||||
|
||||
def test_last_raise(self):
|
||||
self.nl._mode = NeighborList.RAISE
|
||||
self.nl._mode = NeighborList.Modes.exception
|
||||
self.nl.lastitem()
|
||||
self.assertEqual(self.nl.idx, 0)
|
||||
with self.assertRaises(IndexError):
|
||||
@ -179,11 +179,11 @@ class OneTests(TestCase):
|
||||
|
||||
class BlockTests(TestCase):
|
||||
|
||||
"""Tests with mode=BLOCK."""
|
||||
"""Tests with mode=block."""
|
||||
|
||||
def setUp(self):
|
||||
self.nl = NeighborList([1, 2, 3, 4, 5], default=3,
|
||||
mode=NeighborList.BLOCK)
|
||||
mode=NeighborList.Modes.block)
|
||||
|
||||
def test_first(self):
|
||||
self.nl.firstitem()
|
||||
@ -200,11 +200,11 @@ class BlockTests(TestCase):
|
||||
|
||||
class WrapTests(TestCase):
|
||||
|
||||
"""Tests with mode=WRAP."""
|
||||
"""Tests with mode=wrap."""
|
||||
|
||||
def setUp(self):
|
||||
self.nl = NeighborList([1, 2, 3, 4, 5], default=3,
|
||||
mode=NeighborList.WRAP)
|
||||
mode=NeighborList.Modes.wrap)
|
||||
|
||||
def test_first(self):
|
||||
self.nl.firstitem()
|
||||
@ -221,11 +221,11 @@ class WrapTests(TestCase):
|
||||
|
||||
class RaiseTests(TestCase):
|
||||
|
||||
"""Tests with mode=RAISE."""
|
||||
"""Tests with mode=exception."""
|
||||
|
||||
def setUp(self):
|
||||
self.nl = NeighborList([1, 2, 3, 4, 5], default=3,
|
||||
mode=NeighborList.RAISE)
|
||||
mode=NeighborList.Modes.exception)
|
||||
|
||||
def test_first(self):
|
||||
self.nl.firstitem()
|
||||
|
@ -26,12 +26,31 @@ import logging
|
||||
_UNSET = object()
|
||||
|
||||
|
||||
def enum(*items, **named):
|
||||
|
||||
"""Factory for simple enumerations.
|
||||
|
||||
We really don't need more complex things here, so we don't use python3.4's
|
||||
enum, because we'd have to backport things to 3.3 and maybe even 3.2.
|
||||
|
||||
Based on: http://stackoverflow.com/a/1695250/2085149
|
||||
|
||||
Args:
|
||||
*items: Items to be sequentally enumerated.
|
||||
**named: Items to have a given position/number.
|
||||
"""
|
||||
enums = dict(zip(items, range(len(items))), **named)
|
||||
reverse = dict((v, k) for k, v in enums.items())
|
||||
enums['reverse_mapping'] = reverse
|
||||
return type('Enum', (), enums)
|
||||
|
||||
|
||||
class NeighborList:
|
||||
|
||||
"""A list of items which saves it current position.
|
||||
|
||||
Class attributes:
|
||||
BLOCK/WRAP/RAISE: Modes, see constructor documentation.
|
||||
Modes: Different modes, see constructor documentation.
|
||||
|
||||
Attributes:
|
||||
idx: The current position in the list.
|
||||
@ -39,20 +58,18 @@ class NeighborList:
|
||||
_mode: The current mode.
|
||||
"""
|
||||
|
||||
BLOCK = 0
|
||||
WRAP = 1
|
||||
RAISE = 2
|
||||
Modes = enum('block', 'wrap', 'exception')
|
||||
|
||||
def __init__(self, items=None, default=_UNSET, mode=RAISE):
|
||||
def __init__(self, items=None, default=_UNSET, mode=Modes.exception):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
items: The list of items to iterate in.
|
||||
_default: The initially selected value.
|
||||
_mode: Behaviour when the first/last item is reached.
|
||||
BLOCK: Stay on the selected item
|
||||
WRAP: Wrap around to the other end
|
||||
RAISE: Raise an IndexError.
|
||||
Modes.block: Stay on the selected item
|
||||
Modes.wrap: Wrap around to the other end
|
||||
Modes.exception: Raise an IndexError.
|
||||
"""
|
||||
if items is None:
|
||||
self._items = []
|
||||
@ -80,7 +97,8 @@ class NeighborList:
|
||||
The new item.
|
||||
|
||||
Raise:
|
||||
IndexError if the border of the list is reached and mode is RAISE.
|
||||
IndexError if the border of the list is reached and mode is
|
||||
exception.
|
||||
"""
|
||||
logging.debug("{} items, idx {}, offset {}".format(len(self._items),
|
||||
self.idx, offset))
|
||||
@ -92,13 +110,13 @@ class NeighborList:
|
||||
else:
|
||||
raise IndexError
|
||||
except IndexError:
|
||||
if self._mode == self.BLOCK:
|
||||
if self._mode == self.Modes.block:
|
||||
new = self.curitem()
|
||||
elif self._mode == self.WRAP:
|
||||
elif self._mode == self.Modes.wrap:
|
||||
self.idx += offset
|
||||
self.idx %= len(self.items)
|
||||
new = self.curitem()
|
||||
elif self._mode == self.RAISE:
|
||||
elif self._mode == self.Modes.exception:
|
||||
raise
|
||||
else:
|
||||
self.idx += offset
|
||||
|
@ -18,6 +18,7 @@
|
||||
"""Utilities related to QWebElements.
|
||||
|
||||
Module attributes:
|
||||
Group: Enum for different kinds of groups.
|
||||
SELECTORS: CSS selectors for different groups of elements.
|
||||
FILTERS: A dictionary of filter functions for the modes.
|
||||
The filter for "links" filters javascript:-links and a-tags
|
||||
@ -25,27 +26,33 @@ Module attributes:
|
||||
"""
|
||||
|
||||
import qutebrowser.utils.url as urlutils
|
||||
from qutebrowser.utils.usertypes import enum
|
||||
|
||||
|
||||
Group = enum('all', 'links', 'images', 'editable', 'url', 'prevnext_rel',
|
||||
'prevnext', 'editable_focused')
|
||||
|
||||
|
||||
SELECTORS = {
|
||||
'all': ('a, textarea, select, input:not([type=hidden]), button, '
|
||||
'frame, iframe, [onclick], [onmousedown], [role=link], '
|
||||
'[role=option], [role=button], img'),
|
||||
'links': 'a',
|
||||
'images': 'img',
|
||||
'editable': ('input[type=text], input[type=email], input[type=url], '
|
||||
'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]',
|
||||
Group.all: ('a, textarea, select, input:not([type=hidden]), button, '
|
||||
'frame, iframe, [onclick], [onmousedown], [role=link], '
|
||||
'[role=option], [role=button], img'),
|
||||
Group.links: 'a',
|
||||
Group.images: 'img',
|
||||
Group.editable: ('input[type=text], input[type=email], input[type=url], '
|
||||
'input[type=tel], input[type=number], '
|
||||
'input[type=password], input[type=search], textarea'),
|
||||
Group.url: '[src], [href]',
|
||||
Group.prevnext_rel: 'link, [role=link]',
|
||||
Group.prevnext: 'a, button, [role=button]',
|
||||
}
|
||||
|
||||
SELECTORS['editable_focused'] = ', '.join(
|
||||
[sel.strip() + ':focus' for sel in SELECTORS['editable'].split(',')])
|
||||
SELECTORS[Group.editable_focused] = ', '.join(
|
||||
[sel.strip() + ':focus' for sel in SELECTORS[Group.editable].split(',')])
|
||||
|
||||
FILTERS = {
|
||||
'links': (lambda e: e.hasAttribute('href') and
|
||||
urlutils.qurl(e.attribute('href')).scheme() != 'javascript'),
|
||||
Group.links: (lambda e: e.hasAttribute('href') and
|
||||
urlutils.qurl(e.attribute('href')).scheme() != 'javascript'),
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,7 +33,10 @@ import qutebrowser.utils.webelem as webelem
|
||||
from qutebrowser.browser.webpage import BrowserPage
|
||||
from qutebrowser.browser.hints import HintManager
|
||||
from qutebrowser.utils.signals import SignalCache
|
||||
from qutebrowser.utils.usertypes import NeighborList
|
||||
from qutebrowser.utils.usertypes import NeighborList, enum
|
||||
|
||||
|
||||
Target = enum('normal', 'tab', 'bgtab')
|
||||
|
||||
|
||||
class WebView(QWebView):
|
||||
@ -72,7 +75,7 @@ class WebView(QWebView):
|
||||
super().__init__(parent)
|
||||
self._scroll_pos = (-1, -1)
|
||||
self._shutdown_callback = None
|
||||
self._open_target = 'normal'
|
||||
self._open_target = Target.normal
|
||||
self._force_open_target = None
|
||||
self._destroyed = {}
|
||||
self._zoom = None
|
||||
@ -95,7 +98,7 @@ class WebView(QWebView):
|
||||
self._zoom = NeighborList(
|
||||
config.get('general', 'zoom-levels'),
|
||||
default=config.get('general', 'default-zoom'),
|
||||
mode=NeighborList.BLOCK)
|
||||
mode=NeighborList.Modes.block)
|
||||
|
||||
def _on_destroyed(self, sender):
|
||||
"""Called when a subsystem has been destroyed during shutdown.
|
||||
@ -226,9 +229,9 @@ class WebView(QWebView):
|
||||
Emit:
|
||||
open_tab: Emitted if window should be opened in a new tab.
|
||||
"""
|
||||
if self._open_target == 'tab':
|
||||
if self._open_target == Target.tab:
|
||||
self.open_tab.emit(url, False)
|
||||
elif self._open_target == 'bgtab':
|
||||
elif self._open_target == Target.bgtab:
|
||||
self.open_tab.emit(url, True)
|
||||
else:
|
||||
self.openurl(url)
|
||||
@ -252,7 +255,7 @@ class WebView(QWebView):
|
||||
return
|
||||
frame = self.page_.currentFrame()
|
||||
elem = frame.findFirstElement(
|
||||
webelem.SELECTORS['editable_focused'])
|
||||
webelem.SELECTORS[webelem.Group.editable_focused])
|
||||
logging.debug("focus element: {}".format(not elem.isNull()))
|
||||
if elem.isNull():
|
||||
modeman.maybe_leave("insert")
|
||||
@ -266,7 +269,7 @@ class WebView(QWebView):
|
||||
Args:
|
||||
target: A string to set self._force_open_target to.
|
||||
"""
|
||||
self._force_open_target = target
|
||||
self._force_open_target = getattr(Target, target)
|
||||
|
||||
def paintEvent(self, e):
|
||||
"""Extend paintEvent to emit a signal if the scroll position changed.
|
||||
@ -339,10 +342,10 @@ class WebView(QWebView):
|
||||
elif (e.button() == Qt.MidButton or
|
||||
e.modifiers() & Qt.ControlModifier):
|
||||
if config.get('general', 'background-tabs'):
|
||||
self._open_target = "bgtab"
|
||||
self._open_target = Target.bgtab
|
||||
else:
|
||||
self._open_target = "tab"
|
||||
self._open_target = Target.tab
|
||||
logging.debug("Setting target: {}".format(self._open_target))
|
||||
else:
|
||||
self._open_target = "normal"
|
||||
self._open_target = Target.normal
|
||||
return super().mousePressEvent(e)
|
||||
|
Loading…
Reference in New Issue
Block a user