Fix and clean up insertmode checks
This commit is contained in:
parent
fdb4b24148
commit
cf17af147e
@ -30,6 +30,7 @@ Module attributes:
|
|||||||
from PyQt5.QtCore import QRect, QUrl
|
from PyQt5.QtCore import QRect, QUrl
|
||||||
from PyQt5.QtWebKit import QWebElement
|
from PyQt5.QtWebKit import QWebElement
|
||||||
|
|
||||||
|
import qutebrowser.utils.log as log
|
||||||
from qutebrowser.utils.usertypes import enum
|
from qutebrowser.utils.usertypes import enum
|
||||||
|
|
||||||
|
|
||||||
@ -43,17 +44,21 @@ SELECTORS = {
|
|||||||
'[role=option], [role=button], img'),
|
'[role=option], [role=button], img'),
|
||||||
Group.links: 'a',
|
Group.links: 'a',
|
||||||
Group.images: 'img',
|
Group.images: 'img',
|
||||||
Group.editable: ('input[type=text], input[type=email], input[type=url], '
|
# This doesn't contain all the groups where we should switch to insert mode
|
||||||
'input[type=tel], input[type=number], '
|
# - it is just used when opening the external editor.
|
||||||
'input[type=password], input[type=search], textarea'),
|
Group.editable_focused: ('input[type=text]:focus, '
|
||||||
|
'input[type=email]:focus, '
|
||||||
|
'input[type=url]:focus, '
|
||||||
|
'input[type=tel]:focus, '
|
||||||
|
'input[type=number]:focus, '
|
||||||
|
'input[type=password]:focus, '
|
||||||
|
'input[type=search]:focus, '
|
||||||
|
'textarea:focus'),
|
||||||
Group.url: '[src], [href]',
|
Group.url: '[src], [href]',
|
||||||
Group.prevnext_rel: 'link, [role=link]',
|
Group.prevnext_rel: 'link, [role=link]',
|
||||||
Group.prevnext: 'a, button, [role=button]',
|
Group.prevnext: 'a, button, [role=button]',
|
||||||
}
|
}
|
||||||
|
|
||||||
SELECTORS[Group.editable_focused] = ', '.join(
|
|
||||||
[sel.strip() + ':focus' for sel in SELECTORS[Group.editable].split(',')])
|
|
||||||
|
|
||||||
FILTERS = {
|
FILTERS = {
|
||||||
Group.links: (lambda e: e.hasAttribute('href') and
|
Group.links: (lambda e: e.hasAttribute('href') and
|
||||||
QUrl(e.attribute('href')).scheme() != 'javascript'),
|
QUrl(e.attribute('href')).scheme() != 'javascript'),
|
||||||
@ -171,3 +176,53 @@ def get_child_frames(startframe):
|
|||||||
new_frames += frame.childFrames()
|
new_frames += frame.childFrames()
|
||||||
frames = new_frames
|
frames = new_frames
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def is_editable(elem):
|
||||||
|
"""Check whether we should switch to insert mode for this element.
|
||||||
|
|
||||||
|
FIXME: add tests
|
||||||
|
|
||||||
|
Args:
|
||||||
|
elem: The QWebElement to check.
|
||||||
|
|
||||||
|
Return:
|
||||||
|
True if we should switch to insert mode, False otherwise.
|
||||||
|
"""
|
||||||
|
# Beginnings of div-classes which are actually some kind of editor.
|
||||||
|
div_classes = ['CodeMirror', # Javascript editor over a textarea
|
||||||
|
'kix-'] # Google Docs editor
|
||||||
|
tag = elem.tagName().lower()
|
||||||
|
if tag == 'input':
|
||||||
|
objtype = elem.attribute('type')
|
||||||
|
if objtype in ['text', 'email', 'url', 'tel', 'number', 'password',
|
||||||
|
'search', '']:
|
||||||
|
return is_writable(elem)
|
||||||
|
elif tag == 'textarea':
|
||||||
|
return is_writable(elem)
|
||||||
|
elif tag in ('embed', 'applet', 'select'):
|
||||||
|
# Flash/Java/...
|
||||||
|
return config.get('input', 'insert-mode-on-plugins')
|
||||||
|
elif tag == 'object':
|
||||||
|
# Could be Flash/Java/..., could be image/audio/...
|
||||||
|
if not elem.hasAttribute('type'):
|
||||||
|
log.webview.debug("<object> without type clicked...")
|
||||||
|
return False
|
||||||
|
objtype = elem.attribute('type')
|
||||||
|
if (objtype.startswith('application/') or
|
||||||
|
elem.hasAttribute('classid')):
|
||||||
|
# Let's hope flash/java stuff has an application/* mimetype OR
|
||||||
|
# at least a classid attribute. Oh, and let's hope images/...
|
||||||
|
# DON'T have a classid attribute. HTML sucks.
|
||||||
|
log.webview.debug("<object type='{}'> clicked.".format(objtype))
|
||||||
|
return config.get('input', 'insert-mode-on-plugins')
|
||||||
|
elif tag == 'div':
|
||||||
|
log.webview.debug("div with classes {} clicked!".format(
|
||||||
|
elem.classes()))
|
||||||
|
for klass in elem.classes():
|
||||||
|
if any([klass.startswith(e) for e in div_classes]):
|
||||||
|
return True
|
||||||
|
elif tag == 'span':
|
||||||
|
log.webview.debug("span with classes {} clicked!".format(
|
||||||
|
elem.classes()))
|
||||||
|
return False
|
||||||
|
@ -179,47 +179,6 @@ class WebView(QWebView):
|
|||||||
log.destroy.debug("Everything destroyed, calling callback")
|
log.destroy.debug("Everything destroyed, calling callback")
|
||||||
self._shutdown_callback()
|
self._shutdown_callback()
|
||||||
|
|
||||||
def _is_editable(self, hitresult):
|
|
||||||
"""Check if a hit result needs keyboard focus.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
hitresult: A QWebHitTestResult
|
|
||||||
"""
|
|
||||||
# Beginnings of div-classes which are actually some kind of editor.
|
|
||||||
div_classes = ['CodeMirror', # Javascript editor over a textarea
|
|
||||||
'kix-'] # Google Docs editor
|
|
||||||
elem = hitresult.element()
|
|
||||||
tag = elem.tagName().lower()
|
|
||||||
if hitresult.isContentEditable() and webelem.is_writable(elem):
|
|
||||||
# text fields and the like
|
|
||||||
return True
|
|
||||||
elif tag in ('embed', 'applet', 'select'):
|
|
||||||
# Flash/Java/...
|
|
||||||
return config.get('input', 'insert-mode-on-plugins')
|
|
||||||
elif tag == 'object':
|
|
||||||
# Could be Flash/Java/..., could be image/audio/...
|
|
||||||
if not elem.hasAttribute('type'):
|
|
||||||
log.mouse.debug("<object> without type clicked...")
|
|
||||||
return False
|
|
||||||
objtype = elem.attribute('type')
|
|
||||||
if (objtype.startswith('application/') or
|
|
||||||
elem.hasAttribute('classid')):
|
|
||||||
# Let's hope flash/java stuff has an application/* mimetype OR
|
|
||||||
# at least a classid attribute. Oh, and let's hope images/...
|
|
||||||
# DON'T have a classid attribute. HTML sucks.
|
|
||||||
log.mouse.debug("<object type='{}'> clicked.".format(objtype))
|
|
||||||
return config.get('input', 'insert-mode-on-plugins')
|
|
||||||
elif tag == 'div':
|
|
||||||
log.webview.debug("div with classes {} clicked!".format(
|
|
||||||
elem.classes()))
|
|
||||||
for klass in elem.classes():
|
|
||||||
if any([klass.startswith(e) for e in div_classes]):
|
|
||||||
return True
|
|
||||||
elif tag == 'span':
|
|
||||||
log.webview.debug("span with classes {} clicked!".format(
|
|
||||||
elem.classes()))
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _mousepress_backforward(self, e):
|
def _mousepress_backforward(self, e):
|
||||||
"""Handle back/forward mouse button presses.
|
"""Handle back/forward mouse button presses.
|
||||||
|
|
||||||
@ -258,9 +217,11 @@ class WebView(QWebView):
|
|||||||
# relative to the QWebView, not to the frame. This makes no sense to
|
# relative to the QWebView, not to the frame. This makes no sense to
|
||||||
# me, but it works this way.
|
# me, but it works this way.
|
||||||
hitresult = frame.hitTestContent(pos)
|
hitresult = frame.hitTestContent(pos)
|
||||||
|
elem = hitresult.element()
|
||||||
if hitresult.isNull():
|
if hitresult.isNull():
|
||||||
log.mouse.debug("Hitresult is null!")
|
log.mouse.debug("Hitresult is null!")
|
||||||
elif self._is_editable(hitresult):
|
elif ((hitresult.isContentEditable() and webelem.is_writable(elem)) or
|
||||||
|
webelem.is_editable(elem)):
|
||||||
log.mouse.debug("Clicked editable element!")
|
log.mouse.debug("Clicked editable element!")
|
||||||
modeman.enter('insert', 'click')
|
modeman.enter('insert', 'click')
|
||||||
else:
|
else:
|
||||||
@ -453,10 +414,11 @@ class WebView(QWebView):
|
|||||||
if modeman.instance().mode == 'insert' or not ok:
|
if modeman.instance().mode == 'insert' or not ok:
|
||||||
return
|
return
|
||||||
frame = self.page().currentFrame()
|
frame = self.page().currentFrame()
|
||||||
elem = frame.findFirstElement(
|
elem = frame.findFirstElement(':focus')
|
||||||
webelem.SELECTORS[webelem.Group.editable_focused])
|
log.modes.debug("focus element: {}".format(elem.toOuterXml()))
|
||||||
log.modes.debug("focus element: {}".format(not elem.isNull()))
|
if elem.isNull():
|
||||||
if not elem.isNull():
|
log.webview.debug("Focused element is null!")
|
||||||
|
elif webelem.is_editable(elem):
|
||||||
modeman.enter('insert', 'load finished')
|
modeman.enter('insert', 'load finished')
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
|
Loading…
Reference in New Issue
Block a user