Start getting :open-editor to work with WebEngine

It doesn't actually work yet (as it claims the field is not editable),
but at least does not crash when the backend limitation for the command
is removed.
This commit is contained in:
Florian Bruhin 2016-08-08 16:24:34 +02:00
parent 58fb41ab9d
commit 9a17591fb7
6 changed files with 76 additions and 19 deletions

View File

@ -613,6 +613,15 @@ class AbstractTab(QWidget):
"""
raise NotImplementedError
def find_focus_element(self, callback):
"""Find the focused element on the page async.
Args:
callback: The callback to be called when the search finished.
Called with a WebEngineElement or None.
"""
raise NotImplementedError
def __repr__(self):
try:
url = utils.elide(self.url().toDisplayString(QUrl.EncodeUnicode),

View File

@ -1408,6 +1408,21 @@ class CommandDispatcher:
url = QUrl('qute://log?level={}'.format(level))
self._open(url, tab, bg, window)
def _open_editor_cb(self, elem):
"""Open editor after the focus elem was found in open_editor."""
if elem is None:
message.error(self._win_id, "No element focused!")
return
if not elem.is_editable(strict=True):
message.error(self._win_id, "Focused element is not editable!")
return
text = elem.text(use_js=True)
ed = editor.ExternalEditor(self._win_id, self._tabbed_browser)
ed.editing_finished.connect(functools.partial(
self.on_editing_finished, elem))
ed.edit(text)
@cmdutils.register(instance='command-dispatcher',
modes=[KeyMode.insert], hide=True, scope='window',
backend=usertypes.Backend.QtWebKit)
@ -1417,20 +1432,8 @@ class CommandDispatcher:
The editor which should be launched can be configured via the
`general -> editor` config option.
"""
# FIXME:qtwebengine have a proper API for this
tab = self._current_widget()
page = tab._widget.page() # pylint: disable=protected-access
try:
elem = webkitelem.focus_elem(page.currentFrame())
except webkitelem.IsNullError:
raise cmdexc.CommandError("No element focused!")
if not elem.is_editable(strict=True):
raise cmdexc.CommandError("Focused element is not editable!")
text = elem.text(use_js=True)
ed = editor.ExternalEditor(self._win_id, self._tabbed_browser)
ed.editing_finished.connect(functools.partial(
self.on_editing_finished, elem))
ed.edit(text)
tab.find_focus_element(self._open_editor_cb)
def on_editing_finished(self, elem, text):
"""Write the editor text into the form field and clean up tempfile.

View File

@ -431,6 +431,25 @@ class WebEngineTab(browsertab.AbstractTab):
js_cb = functools.partial(self._find_all_elements_js_cb, callback)
self.run_js_async(js_code, js_cb)
def _find_focus_element_js_cb(self, callback, js_elem):
"""Handle a found focus elem coming from JS and call the real callback.
Args:
callback: The callback originally passed to find_focus_element.
Called with a WebEngineElement or None.
js_elem: The element serialized from javascript.
"""
log.webview.debug("Got focus element from JS: {!r}".format(js_elem))
if js_elem is None:
callback(None)
else:
callback(webengineelem.WebEngineElement(js_elem))
def find_focus_element(self, callback):
js_code = javascript.assemble('webelem', 'focus_element')
js_cb = functools.partial(self._find_focus_element_js_cb, callback)
self.run_js_async(js_code, js_cb)
def _connect_signals(self):
view = self._widget
page = view.page()

View File

@ -574,6 +574,18 @@ class WebKitTab(browsertab.AbstractTab):
callback(elems)
def find_focus_element(self, callback):
frame = self._widget.page().currentFrame()
if frame is None:
callback(None)
return
elem = frame.findFirstElement('*:focus')
if elem.isNull():
callback(None)
else:
callback(webkitelem.WebKitElement(elem))
@pyqtSlot()
def _on_frame_load_finished(self):
"""Make sure we emit an appropriate status when loading finished.

View File

@ -223,6 +223,7 @@ class WebView(QWebView):
def mouserelease_insertmode(self):
"""If we have an insertmode check scheduled, handle it."""
# FIXME:qtwebengine Use tab.find_focus_element here
if not self._check_insertmode:
return
self._check_insertmode = False

View File

@ -22,7 +22,12 @@ document._qutebrowser_elements = [];
function _qutebrowser_serialize_elem(elem, id) {
var out = {};
var out = {
"id": id,
"text": elem.text,
"tag_name": elem.tagName,
"outer_xml": elem.outerHTML
};
var attributes = {};
for (var i = 0; i < elem.attributes.length; ++i) {
@ -31,11 +36,6 @@ function _qutebrowser_serialize_elem(elem, id) {
}
out["attributes"] = attributes;
out["text"] = elem.text;
out["tag_name"] = elem.tagName;
out["outer_xml"] = elem.outerHTML;
out["id"] = id;
// console.log(JSON.stringify(out));
return out;
@ -58,6 +58,19 @@ function _qutebrowser_find_all_elements(selector) {
}
function _qutebrowser_focus_element() {
var elem = document.activeElement;
if (!elem || elem === document.body) {
// "When there is no selection, the active element is the page's <body>
// or null."
return null;
}
var id = document._qutebrowser_elements.length;
return _qutebrowser_serialize_elem(elem, id);
}
function _qutebrowser_get_element(id) {
return document._qutebrowser_elements[id];
}