First try at hinting with frames
This commit is contained in:
parent
6584618078
commit
4eebe2dc57
5
TODO
5
TODO
@ -75,8 +75,9 @@ Bugs
|
|||||||
|
|
||||||
- Pasting highlighted text (to mrxvt) does not work (iggy).
|
- Pasting highlighted text (to mrxvt) does not work (iggy).
|
||||||
|
|
||||||
- Hinting problems on https://bugreports.qt-project.org/secure/Dashboard.jspa
|
- Hinting problems on http://wiki.gentoo.org/wiki/Lenovo_ThinkPad_T440s (top navbar, e.g. lists, etc.)
|
||||||
http://wiki.gentoo.org/wiki/Lenovo_ThinkPad_T440s (top navbar)
|
|
||||||
|
- clicking on text fields at https://bugreports.qt-project.org/secure/Dashboard.jspa doesn't go into insert mode. (because of iframes?)
|
||||||
|
|
||||||
- scroll_page doesn't care about always visible bars, so content gets hidden
|
- scroll_page doesn't care about always visible bars, so content gets hidden
|
||||||
e.g. http://www.mtb-news.de/forum/t/welcher-schuh-five-ten-vs-oneal.529148/
|
e.g. http://www.mtb-news.de/forum/t/welcher-schuh-five-ten-vs-oneal.529148/
|
||||||
|
@ -226,7 +226,7 @@ class CurCommandDispatcher(QObject):
|
|||||||
targetstr: Where to open the links.
|
targetstr: Where to open the links.
|
||||||
"""
|
"""
|
||||||
widget = self._tabs.currentWidget()
|
widget = self._tabs.currentWidget()
|
||||||
frame = widget.page_.currentFrame()
|
frame = widget.page_.mainFrame()
|
||||||
if frame is None:
|
if frame is None:
|
||||||
message.error("No frame focused!")
|
message.error("No frame focused!")
|
||||||
return
|
return
|
||||||
|
@ -48,7 +48,7 @@ class HintManager(QObject):
|
|||||||
HINT_CSS: The CSS template to use for hints.
|
HINT_CSS: The CSS template to use for hints.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
_frame: The QWebFrame to use.
|
_frames: The QWebFrames to use.
|
||||||
_elems: A mapping from keystrings to (elem, label) namedtuples.
|
_elems: A mapping from keystrings to (elem, label) namedtuples.
|
||||||
_baseurl: The URL of the current page.
|
_baseurl: The URL of the current page.
|
||||||
_target: What to do with the opened links.
|
_target: What to do with the opened links.
|
||||||
@ -96,10 +96,10 @@ class HintManager(QObject):
|
|||||||
"""
|
"""
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._elems = {}
|
self._elems = {}
|
||||||
self._frame = None
|
|
||||||
self._target = None
|
self._target = None
|
||||||
self._baseurl = None
|
self._baseurl = None
|
||||||
self._to_follow = None
|
self._to_follow = None
|
||||||
|
self._frames = []
|
||||||
modeman.instance().left.connect(self.on_mode_left)
|
modeman.instance().left.connect(self.on_mode_left)
|
||||||
|
|
||||||
def _hint_strings(self, elems):
|
def _hint_strings(self, elems):
|
||||||
@ -223,7 +223,7 @@ class HintManager(QObject):
|
|||||||
The newly created label elment
|
The newly created label elment
|
||||||
"""
|
"""
|
||||||
css = self._get_hint_css(elem)
|
css = self._get_hint_css(elem)
|
||||||
doc = self._frame.documentElement()
|
doc = elem.webFrame().documentElement()
|
||||||
# It seems impossible to create an empty QWebElement for which isNull()
|
# It seems impossible to create an empty QWebElement for which isNull()
|
||||||
# is false so we can work with it.
|
# is false so we can work with it.
|
||||||
# As a workaround, we use appendInside() with markup as argument, and
|
# As a workaround, we use appendInside() with markup as argument, and
|
||||||
@ -244,21 +244,15 @@ class HintManager(QObject):
|
|||||||
else:
|
else:
|
||||||
target = self._target
|
target = self._target
|
||||||
self.set_open_target.emit(Target[target])
|
self.set_open_target.emit(Target[target])
|
||||||
# FIXME Instead of clicking the center, we could have nicer heuristics.
|
pos = webelem.pos_on_screen(elem)
|
||||||
# e.g. parse (-webkit-)border-radius correctly and click text fields at
|
logging.debug("Clicking on \"{}\" at {}/{}".format(
|
||||||
# the bottom right, and everything else on the top left or so.
|
elem.toPlainText(), pos.x(), pos.y()))
|
||||||
point = elem.geometry().center()
|
|
||||||
scrollpos = self._frame.scrollPosition()
|
|
||||||
logging.debug("Clicking on \"{}\" at {}/{} - {}/{}".format(
|
|
||||||
elem.toPlainText(), point.x(), point.y(), scrollpos.x(),
|
|
||||||
scrollpos.y()))
|
|
||||||
point -= scrollpos
|
|
||||||
events = [
|
events = [
|
||||||
QMouseEvent(QEvent.MouseMove, point, Qt.NoButton, Qt.NoButton,
|
QMouseEvent(QEvent.MouseMove, pos, Qt.NoButton, Qt.NoButton,
|
||||||
Qt.NoModifier),
|
Qt.NoModifier),
|
||||||
QMouseEvent(QEvent.MouseButtonPress, point, Qt.LeftButton,
|
QMouseEvent(QEvent.MouseButtonPress, pos, Qt.LeftButton,
|
||||||
Qt.NoButton, Qt.NoModifier),
|
Qt.NoButton, Qt.NoModifier),
|
||||||
QMouseEvent(QEvent.MouseButtonRelease, point, Qt.LeftButton,
|
QMouseEvent(QEvent.MouseButtonRelease, pos, Qt.LeftButton,
|
||||||
Qt.NoButton, Qt.NoModifier),
|
Qt.NoButton, Qt.NoModifier),
|
||||||
]
|
]
|
||||||
for evt in events:
|
for evt in events:
|
||||||
@ -365,7 +359,6 @@ class HintManager(QObject):
|
|||||||
Emit:
|
Emit:
|
||||||
hint_strings_updated: Emitted to update keypraser.
|
hint_strings_updated: Emitted to update keypraser.
|
||||||
"""
|
"""
|
||||||
elems = frame.findAllElements(webelem.SELECTORS[group])
|
|
||||||
self._target = target
|
self._target = target
|
||||||
self._baseurl = baseurl
|
self._baseurl = baseurl
|
||||||
if frame is None:
|
if frame is None:
|
||||||
@ -373,11 +366,14 @@ class HintManager(QObject):
|
|||||||
# start. But since we had a bug where frame is None in
|
# start. But since we had a bug where frame is None in
|
||||||
# on_mode_left, we are extra careful here.
|
# on_mode_left, we are extra careful here.
|
||||||
raise ValueError("start() was called with frame=None")
|
raise ValueError("start() was called with frame=None")
|
||||||
self._frame = frame
|
self._frames = webelem.get_child_frames(frame)
|
||||||
|
elems = []
|
||||||
|
for f in self._frames:
|
||||||
|
elems += f.findAllElements(webelem.SELECTORS[group])
|
||||||
filterfunc = webelem.FILTERS.get(group, lambda e: True)
|
filterfunc = webelem.FILTERS.get(group, lambda e: True)
|
||||||
visible_elems = []
|
visible_elems = []
|
||||||
for e in elems:
|
for e in elems:
|
||||||
if filterfunc(e) and webelem.is_visible(e, self._frame):
|
if filterfunc(e) and webelem.is_visible(e):
|
||||||
visible_elems.append(e)
|
visible_elems.append(e)
|
||||||
if not visible_elems:
|
if not visible_elems:
|
||||||
message.error("No elements found.")
|
message.error("No elements found.")
|
||||||
@ -398,7 +394,8 @@ class HintManager(QObject):
|
|||||||
for e, string in zip(visible_elems, strings):
|
for e, string in zip(visible_elems, strings):
|
||||||
label = self._draw_label(e, string)
|
label = self._draw_label(e, string)
|
||||||
self._elems[string] = ElemTuple(e, label)
|
self._elems[string] = ElemTuple(e, label)
|
||||||
frame.contentsSizeChanged.connect(self.on_contents_size_changed)
|
for f in self._frames:
|
||||||
|
f.contentsSizeChanged.connect(self.on_contents_size_changed)
|
||||||
self.hint_strings_updated.emit(strings)
|
self.hint_strings_updated.emit(strings)
|
||||||
modeman.enter('hint', 'HintManager.start')
|
modeman.enter('hint', 'HintManager.start')
|
||||||
|
|
||||||
@ -508,14 +505,16 @@ class HintManager(QObject):
|
|||||||
for elem in self._elems.values():
|
for elem in self._elems.values():
|
||||||
if not elem.label.isNull():
|
if not elem.label.isNull():
|
||||||
elem.label.removeFromDocument()
|
elem.label.removeFromDocument()
|
||||||
if self._frame is not None:
|
if self._frames is not None:
|
||||||
# The frame which was focused in start() might not be available
|
for frame in self._frames:
|
||||||
# anymore, since Qt might already have deleted it (e.g. when a new
|
if frame is not None:
|
||||||
# page is loaded).
|
# The frame which was focused in start() might not be
|
||||||
self._frame.contentsSizeChanged.disconnect(
|
# available anymore, since Qt might already have deleted it
|
||||||
|
# (e.g. when a new page is loaded).
|
||||||
|
frame.contentsSizeChanged.disconnect(
|
||||||
self.on_contents_size_changed)
|
self.on_contents_size_changed)
|
||||||
self._elems = {}
|
self._elems = {}
|
||||||
self._to_follow = None
|
self._to_follow = None
|
||||||
self._target = None
|
self._target = None
|
||||||
self._frame = None
|
self._frames = []
|
||||||
message.clear()
|
message.clear()
|
||||||
|
@ -25,6 +25,8 @@ Module attributes:
|
|||||||
without "href".
|
without "href".
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
import qutebrowser.utils.url as urlutils
|
import qutebrowser.utils.url as urlutils
|
||||||
from qutebrowser.utils.usertypes import enum
|
from qutebrowser.utils.usertypes import enum
|
||||||
|
|
||||||
@ -56,33 +58,48 @@ FILTERS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def is_visible(e, frame=None):
|
def is_visible(elem):
|
||||||
"""Check whether the element is currently visible in its frame.
|
"""Check whether the element is currently visible in its frame.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
e: The QWebElement to check.
|
elem: The QWebElement to check.
|
||||||
frame: The QWebFrame in which the element should be visible in.
|
|
||||||
If None, the element's frame is used.
|
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
True if the element is visible, False otherwise.
|
True if the element is visible, False otherwise.
|
||||||
"""
|
"""
|
||||||
if e.isNull():
|
# FIXME we should also check if the frame is visible
|
||||||
|
if elem.isNull():
|
||||||
raise ValueError("Element is a null-element!")
|
raise ValueError("Element is a null-element!")
|
||||||
if frame is None:
|
frame = elem.webFrame()
|
||||||
frame = e.webFrame()
|
rect = elem.geometry()
|
||||||
rect = e.geometry()
|
|
||||||
if (not rect.isValid()) and rect.x() == 0:
|
if (not rect.isValid()) and rect.x() == 0:
|
||||||
# Most likely an invisible link
|
# Most likely an invisible link
|
||||||
return False
|
return False
|
||||||
framegeom = frame.geometry()
|
framegeom = frame.geometry()
|
||||||
|
framegeom.moveTo(0, 0)
|
||||||
framegeom.translate(frame.scrollPosition())
|
framegeom.translate(frame.scrollPosition())
|
||||||
if not framegeom.contains(rect.topLeft()):
|
if not framegeom.intersects(rect):
|
||||||
# out of screen
|
# out of screen
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def pos_on_screen(elem):
|
||||||
|
"""Get the position of the element on the screen."""
|
||||||
|
# FIXME Instead of clicking the center, we could have nicer heuristics.
|
||||||
|
# e.g. parse (-webkit-)border-radius correctly and click text fields at
|
||||||
|
# the bottom right, and everything else on the top left or so.
|
||||||
|
frame = elem.webFrame()
|
||||||
|
pos = elem.geometry().center()
|
||||||
|
while frame is not None:
|
||||||
|
pos += frame.geometry().topLeft()
|
||||||
|
logging.debug("After adding frame pos: {}".format(pos))
|
||||||
|
pos -= frame.scrollPosition()
|
||||||
|
logging.debug("After removing frame scrollpos: {}".format(pos))
|
||||||
|
frame = frame.parentFrame()
|
||||||
|
return pos
|
||||||
|
|
||||||
|
|
||||||
def javascript_escape(text):
|
def javascript_escape(text):
|
||||||
"""Escape values special to javascript in strings.
|
"""Escape values special to javascript in strings.
|
||||||
|
|
||||||
@ -102,3 +119,25 @@ def javascript_escape(text):
|
|||||||
for orig, repl in replacements:
|
for orig, repl in replacements:
|
||||||
text = text.replace(orig, repl)
|
text = text.replace(orig, repl)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def get_child_frames(startframe):
|
||||||
|
"""Get all children recursively of a given QWebFrame.
|
||||||
|
|
||||||
|
Loosly based on http://blog.nextgenetics.net/?e=64
|
||||||
|
|
||||||
|
Args:
|
||||||
|
startframe: The QWebFrame to start with.
|
||||||
|
|
||||||
|
Return:
|
||||||
|
A list of children QWebFrame, or an empty list.
|
||||||
|
"""
|
||||||
|
results = []
|
||||||
|
frames = [startframe]
|
||||||
|
while frames:
|
||||||
|
new_frames = []
|
||||||
|
for frame in frames:
|
||||||
|
results.append(frame)
|
||||||
|
new_frames += frame.childFrames()
|
||||||
|
frames = new_frames
|
||||||
|
return results
|
||||||
|
Loading…
Reference in New Issue
Block a user