First try at hinting with frames

This commit is contained in:
Florian Bruhin 2014-05-12 07:49:44 +02:00
parent 6584618078
commit 4eebe2dc57
4 changed files with 77 additions and 38 deletions

5
TODO
View File

@ -75,8 +75,9 @@ Bugs
- Pasting highlighted text (to mrxvt) does not work (iggy).
- Hinting problems on https://bugreports.qt-project.org/secure/Dashboard.jspa
http://wiki.gentoo.org/wiki/Lenovo_ThinkPad_T440s (top navbar)
- Hinting problems on http://wiki.gentoo.org/wiki/Lenovo_ThinkPad_T440s (top navbar, e.g. lists, etc.)
- 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
e.g. http://www.mtb-news.de/forum/t/welcher-schuh-five-ten-vs-oneal.529148/

View File

@ -226,7 +226,7 @@ class CurCommandDispatcher(QObject):
targetstr: Where to open the links.
"""
widget = self._tabs.currentWidget()
frame = widget.page_.currentFrame()
frame = widget.page_.mainFrame()
if frame is None:
message.error("No frame focused!")
return

View File

@ -48,7 +48,7 @@ class HintManager(QObject):
HINT_CSS: The CSS template to use for hints.
Attributes:
_frame: The QWebFrame to use.
_frames: The QWebFrames to use.
_elems: A mapping from keystrings to (elem, label) namedtuples.
_baseurl: The URL of the current page.
_target: What to do with the opened links.
@ -96,10 +96,10 @@ class HintManager(QObject):
"""
super().__init__(parent)
self._elems = {}
self._frame = None
self._target = None
self._baseurl = None
self._to_follow = None
self._frames = []
modeman.instance().left.connect(self.on_mode_left)
def _hint_strings(self, elems):
@ -223,7 +223,7 @@ class HintManager(QObject):
The newly created label elment
"""
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()
# is false so we can work with it.
# As a workaround, we use appendInside() with markup as argument, and
@ -244,21 +244,15 @@ class HintManager(QObject):
else:
target = self._target
self.set_open_target.emit(Target[target])
# 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.
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
pos = webelem.pos_on_screen(elem)
logging.debug("Clicking on \"{}\" at {}/{}".format(
elem.toPlainText(), pos.x(), pos.y()))
events = [
QMouseEvent(QEvent.MouseMove, point, Qt.NoButton, Qt.NoButton,
QMouseEvent(QEvent.MouseMove, pos, Qt.NoButton, Qt.NoButton,
Qt.NoModifier),
QMouseEvent(QEvent.MouseButtonPress, point, Qt.LeftButton,
QMouseEvent(QEvent.MouseButtonPress, pos, Qt.LeftButton,
Qt.NoButton, Qt.NoModifier),
QMouseEvent(QEvent.MouseButtonRelease, point, Qt.LeftButton,
QMouseEvent(QEvent.MouseButtonRelease, pos, Qt.LeftButton,
Qt.NoButton, Qt.NoModifier),
]
for evt in events:
@ -365,7 +359,6 @@ class HintManager(QObject):
Emit:
hint_strings_updated: Emitted to update keypraser.
"""
elems = frame.findAllElements(webelem.SELECTORS[group])
self._target = target
self._baseurl = baseurl
if frame is None:
@ -373,11 +366,14 @@ class HintManager(QObject):
# start. But since we had a bug where frame is None in
# on_mode_left, we are extra careful here.
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)
visible_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)
if not visible_elems:
message.error("No elements found.")
@ -398,7 +394,8 @@ class HintManager(QObject):
for e, string in zip(visible_elems, strings):
label = self._draw_label(e, string)
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)
modeman.enter('hint', 'HintManager.start')
@ -508,14 +505,16 @@ class HintManager(QObject):
for elem in self._elems.values():
if not elem.label.isNull():
elem.label.removeFromDocument()
if self._frame is not None:
# The frame which was focused in start() might not be available
# anymore, since Qt might already have deleted it (e.g. when a new
# page is loaded).
self._frame.contentsSizeChanged.disconnect(
self.on_contents_size_changed)
if self._frames is not None:
for frame in self._frames:
if frame is not None:
# The frame which was focused in start() might not be
# 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._elems = {}
self._to_follow = None
self._target = None
self._frame = None
self._frames = []
message.clear()

View File

@ -25,6 +25,8 @@ Module attributes:
without "href".
"""
import logging
import qutebrowser.utils.url as urlutils
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.
Args:
e: The QWebElement to check.
frame: The QWebFrame in which the element should be visible in.
If None, the element's frame is used.
elem: The QWebElement to check.
Return:
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!")
if frame is None:
frame = e.webFrame()
rect = e.geometry()
frame = elem.webFrame()
rect = elem.geometry()
if (not rect.isValid()) and rect.x() == 0:
# Most likely an invisible link
return False
framegeom = frame.geometry()
framegeom.moveTo(0, 0)
framegeom.translate(frame.scrollPosition())
if not framegeom.contains(rect.topLeft()):
if not framegeom.intersects(rect):
# out of screen
return False
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):
"""Escape values special to javascript in strings.
@ -102,3 +119,25 @@ def javascript_escape(text):
for orig, repl in replacements:
text = text.replace(orig, repl)
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