Merge remote-tracking branch 'origin/pr/3348'

This commit is contained in:
Florian Bruhin 2018-01-20 18:28:14 +01:00
commit 1f5cbf21a3
9 changed files with 1461 additions and 47 deletions

2
.github/CODEOWNERS vendored
View File

@ -8,3 +8,5 @@ tests/unit/completion/* @rcorre
tests/unit/misc/test_sql.py @rcorre tests/unit/misc/test_sql.py @rcorre
qutebrowser/config/configdata.yml @mschilli87 qutebrowser/config/configdata.yml @mschilli87
qutebrowser/javascript/caret.js @artur-shaik

View File

@ -393,7 +393,7 @@ class AbstractCaret(QObject):
def has_selection(self): def has_selection(self):
raise NotImplementedError raise NotImplementedError
def selection(self, html=False): def selection(self, html=False, callback=None):
raise NotImplementedError raise NotImplementedError
def follow_selected(self, *, tab=False): def follow_selected(self, *, tab=False):

View File

@ -849,14 +849,21 @@ class CommandDispatcher:
s = self._yank_url(what) s = self._yank_url(what)
what = 'URL' # For printing what = 'URL' # For printing
elif what == 'selection': elif what == 'selection':
caret = self._current_widget().caret def _selection_callback(s):
s = caret.selection() if not self._current_widget().caret.has_selection() or not s:
if not caret.has_selection() or not s:
message.info("Nothing to yank") message.info("Nothing to yank")
return return
self._yank_to_target(s, sel, what, keep)
caret = self._current_widget().caret
caret.selection(callback=_selection_callback)
return
else: # pragma: no cover else: # pragma: no cover
raise ValueError("Invalid value {!r} for `what'.".format(what)) raise ValueError("Invalid value {!r} for `what'.".format(what))
self._yank_to_target(s, sel, what, keep)
def _yank_to_target(self, s, sel, what, keep):
if sel and utils.supports_selection(): if sel and utils.supports_selection():
target = "primary selection" target = "primary selection"
else: else:
@ -1208,8 +1215,19 @@ class CommandDispatcher:
log.procs.debug("Executing {} with args {}, userscript={}".format( log.procs.debug("Executing {} with args {}, userscript={}".format(
cmd, args, userscript)) cmd, args, userscript))
if userscript: if userscript:
def _selection_callback(s):
try:
self._run_userscript(s, cmd, args, verbose)
except cmdexc.CommandError as e:
message.error(str(e))
# ~ expansion is handled by the userscript module. # ~ expansion is handled by the userscript module.
self._run_userscript(cmd, *args, verbose=verbose) # dirty hack for async call because of:
# https://bugreports.qt.io/browse/QTBUG-53134
# until it fixed or blocked async call implemented:
# https://github.com/qutebrowser/qutebrowser/issues/3327
caret = self._current_widget().caret
caret.selection(callback=_selection_callback)
else: else:
cmd = os.path.expanduser(cmd) cmd = os.path.expanduser(cmd)
proc = guiprocess.GUIProcess(what='command', verbose=verbose, proc = guiprocess.GUIProcess(what='command', verbose=verbose,
@ -1229,7 +1247,7 @@ class CommandDispatcher:
"""Open main startpage in current tab.""" """Open main startpage in current tab."""
self.openurl(config.val.url.start_pages[0]) self.openurl(config.val.url.start_pages[0])
def _run_userscript(self, cmd, *args, verbose=False): def _run_userscript(self, selection, cmd, args, verbose):
"""Run a userscript given as argument. """Run a userscript given as argument.
Args: Args:
@ -1247,7 +1265,7 @@ class CommandDispatcher:
tab = self._tabbed_browser.currentWidget() tab = self._tabbed_browser.currentWidget()
if tab is not None and tab.caret.has_selection(): if tab is not None and tab.caret.has_selection():
env['QUTE_SELECTED_TEXT'] = tab.caret.selection() env['QUTE_SELECTED_TEXT'] = selection
try: try:
env['QUTE_SELECTED_HTML'] = tab.caret.selection(html=True) env['QUTE_SELECTED_HTML'] = tab.caret.selection(html=True)
except browsertab.UnsupportedOperationError: except browsertab.UnsupportedOperationError:

View File

@ -21,6 +21,7 @@
import math import math
import functools import functools
import sys
import html as html_utils import html as html_utils
import sip import sip
@ -201,70 +202,98 @@ class WebEngineCaret(browsertab.AbstractCaret):
@pyqtSlot(usertypes.KeyMode) @pyqtSlot(usertypes.KeyMode)
def _on_mode_entered(self, mode): def _on_mode_entered(self, mode):
pass if mode != usertypes.KeyMode.caret:
return
self._tab.run_js_async(
javascript.assemble('caret', 'setPlatform', sys.platform))
self._js_call('setInitialCursor')
@pyqtSlot(usertypes.KeyMode) @pyqtSlot(usertypes.KeyMode)
def _on_mode_left(self): def _on_mode_left(self):
pass self.drop_selection()
self._js_call('disableCaret')
def move_to_next_line(self, count=1): def move_to_next_line(self, count=1):
log.stub() for _ in range(count):
self._js_call('moveDown')
def move_to_prev_line(self, count=1): def move_to_prev_line(self, count=1):
log.stub() for _ in range(count):
self._js_call('moveUp')
def move_to_next_char(self, count=1): def move_to_next_char(self, count=1):
log.stub() for _ in range(count):
self._js_call('moveRight')
def move_to_prev_char(self, count=1): def move_to_prev_char(self, count=1):
log.stub() for _ in range(count):
self._js_call('moveLeft')
def move_to_end_of_word(self, count=1): def move_to_end_of_word(self, count=1):
log.stub() for _ in range(count):
self._js_call('moveToEndOfWord')
def move_to_next_word(self, count=1): def move_to_next_word(self, count=1):
log.stub() for _ in range(count):
self._js_call('moveToNextWord')
def move_to_prev_word(self, count=1): def move_to_prev_word(self, count=1):
log.stub() for _ in range(count):
self._js_call('moveToPreviousWord')
def move_to_start_of_line(self): def move_to_start_of_line(self):
log.stub() self._js_call('moveToStartOfLine')
def move_to_end_of_line(self): def move_to_end_of_line(self):
log.stub() self._js_call('moveToEndOfLine')
def move_to_start_of_next_block(self, count=1): def move_to_start_of_next_block(self, count=1):
log.stub() for _ in range(count):
self._js_call('moveToStartOfNextBlock')
def move_to_start_of_prev_block(self, count=1): def move_to_start_of_prev_block(self, count=1):
log.stub() for _ in range(count):
self._js_call('moveToStartOfPrevBlock')
def move_to_end_of_next_block(self, count=1): def move_to_end_of_next_block(self, count=1):
log.stub() for _ in range(count):
self._js_call('moveToEndOfNextBlock')
def move_to_end_of_prev_block(self, count=1): def move_to_end_of_prev_block(self, count=1):
log.stub() for _ in range(count):
self._js_call('moveToEndOfPrevBlock')
def move_to_start_of_document(self): def move_to_start_of_document(self):
log.stub() self._js_call('moveToStartOfDocument')
def move_to_end_of_document(self): def move_to_end_of_document(self):
log.stub() self._js_call('moveToEndOfDocument')
def toggle_selection(self): def toggle_selection(self):
log.stub() self._js_call('toggleSelection')
def drop_selection(self): def drop_selection(self):
log.stub() self._js_call('dropSelection')
def has_selection(self): def has_selection(self):
if qtutils.version_check('5.10'):
return self._widget.hasSelection() return self._widget.hasSelection()
else:
# WORKAROUND for
# https://bugreports.qt.io/browse/QTBUG-53134
return True
def selection(self, html=False): def selection(self, html=False, callback=None):
if html: if html:
raise browsertab.UnsupportedOperationError raise browsertab.UnsupportedOperationError
return self._widget.selectedText() if qtutils.version_check('5.10'):
callback(self._widget.selectedText())
else:
# WORKAROUND for
# https://bugreports.qt.io/browse/QTBUG-53134
self._tab.run_js_async(
javascript.assemble('caret', 'getSelection'), callback)
def _follow_selected_cb(self, js_elem, tab=False): def _follow_selected_cb(self, js_elem, tab=False):
"""Callback for javascript which clicks the selected element. """Callback for javascript which clicks the selected element.
@ -308,6 +337,10 @@ class WebEngineCaret(browsertab.AbstractCaret):
self._tab.run_js_async(js_code, lambda jsret: self._tab.run_js_async(js_code, lambda jsret:
self._follow_selected_cb(jsret, tab)) self._follow_selected_cb(jsret, tab))
def _js_call(self, command):
self._tab.run_js_async(
javascript.assemble('caret', command))
class WebEngineScroller(browsertab.AbstractScroller): class WebEngineScroller(browsertab.AbstractScroller):
@ -577,6 +610,7 @@ class WebEngineTab(browsertab.AbstractTab):
'window._qutebrowser = window._qutebrowser || {};', 'window._qutebrowser = window._qutebrowser || {};',
utils.read_file('javascript/scroll.js'), utils.read_file('javascript/scroll.js'),
utils.read_file('javascript/webelem.js'), utils.read_file('javascript/webelem.js'),
utils.read_file('javascript/caret.js'),
]) ])
script = QWebEngineScript() script = QWebEngineScript()
script.setInjectionPoint(QWebEngineScript.DocumentCreation) script.setInjectionPoint(QWebEngineScript.DocumentCreation)

View File

@ -161,7 +161,7 @@ class WebKitCaret(browsertab.AbstractCaret):
settings = self._widget.settings() settings = self._widget.settings()
settings.setAttribute(QWebSettings.CaretBrowsingEnabled, True) settings.setAttribute(QWebSettings.CaretBrowsingEnabled, True)
self.selection_enabled = bool(self.selection()) self.selection_enabled = bool(self._selection())
if self._widget.isVisible(): if self._widget.isVisible():
# Sometimes the caret isn't immediately visible, but unfocusing # Sometimes the caret isn't immediately visible, but unfocusing
@ -174,7 +174,7 @@ class WebKitCaret(browsertab.AbstractCaret):
# #
# Note: We can't use hasSelection() here, as that's always # Note: We can't use hasSelection() here, as that's always
# true in caret mode. # true in caret mode.
if not self.selection(): if not self.selection_enabled:
self._widget.page().currentFrame().evaluateJavaScript( self._widget.page().currentFrame().evaluateJavaScript(
utils.read_file('javascript/position_caret.js')) utils.read_file('javascript/position_caret.js'))
@ -336,9 +336,13 @@ class WebKitCaret(browsertab.AbstractCaret):
def has_selection(self): def has_selection(self):
return self._widget.hasSelection() return self._widget.hasSelection()
def selection(self, html=False): def selection(self, html=False, callback=False):
callback(self._selection(html))
def _selection(self, html=False):
if html: if html:
return self._widget.selectedHtml() return self._widget.selectedHtml()
else:
return self._widget.selectedText() return self._widget.selectedText()
def follow_selected(self, *, tab=False): def follow_selected(self, *, tab=False):
@ -351,7 +355,7 @@ class WebKitCaret(browsertab.AbstractCaret):
self._tab.run_js_async( self._tab.run_js_async(
'window.getSelection().anchorNode.parentNode.click()') 'window.getSelection().anchorNode.parentNode.click()')
else: else:
selection = self.selection(html=True) selection = self._selection(html=True)
try: try:
selected_element = xml.etree.ElementTree.fromstring( selected_element = xml.etree.ElementTree.fromstring(
'<html>{}</html>'.format(selection)).find('a') '<html>{}</html>'.format(selection)).find('a')

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,6 @@ from qutebrowser.keyinput import modeparsers, keyparser
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.commands import cmdexc, cmdutils from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.utils import usertypes, log, objreg, utils from qutebrowser.utils import usertypes, log, objreg, utils
from qutebrowser.misc import objects
@attr.s(frozen=True) @attr.s(frozen=True)
@ -267,10 +266,6 @@ class ModeManager(QObject):
usertypes.KeyMode.yesno, usertypes.KeyMode.prompt]: usertypes.KeyMode.yesno, usertypes.KeyMode.prompt]:
raise cmdexc.CommandError( raise cmdexc.CommandError(
"Mode {} can't be entered manually!".format(mode)) "Mode {} can't be entered manually!".format(mode))
elif (m == usertypes.KeyMode.caret and
objects.backend == usertypes.Backend.QtWebEngine):
raise cmdexc.CommandError("Caret mode is not supported with "
"QtWebEngine yet.")
self.enter(m, 'command') self.enter(m, 'command')

View File

@ -129,7 +129,6 @@ Feature: Opening external editors
And I kill the waiting editor And I kill the waiting editor
Then the error "Edited element vanished" should be shown Then the error "Edited element vanished" should be shown
@qtwebengine_todo: Caret mode is not implemented yet
Scenario: Spawning an editor in caret mode Scenario: Spawning an editor in caret mode
When I set up a fake editor returning "foobar" When I set up a fake editor returning "foobar"
And I open data/editor.html And I open data/editor.html

View File

@ -17,15 +17,9 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
import pytest
import pytest_bdd as bdd import pytest_bdd as bdd
# pylint: disable=unused-import # pylint: disable=unused-import
from end2end.features.test_yankpaste_bdd import init_fake_clipboard from end2end.features.test_yankpaste_bdd import init_fake_clipboard
pytestmark = pytest.mark.qtwebengine_todo("Caret mode is not implemented",
run=False)
bdd.scenarios('caret.feature') bdd.scenarios('caret.feature')