diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 547e276db..827d11761 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -392,7 +392,7 @@ class AbstractCaret(QObject): def has_selection(self): raise NotImplementedError - def selection(self, html=False): + def selection(self, html=False, selection=None): raise NotImplementedError def follow_selected(self, *, tab=False): diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index e007de76d..e92291816 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -838,6 +838,11 @@ class CommandDispatcher: sel: Use the primary selection instead of the clipboard. keep: Stay in visual mode after yanking the selection. """ + self.yank_object = { + 'sel': sel, + 'what': what, + 'keep': keep + } if what == 'title': s = self._tabbed_browser.page_title(self._current_index()) elif what == 'domain': @@ -850,28 +855,36 @@ class CommandDispatcher: what = 'URL' # For printing elif what == 'selection': caret = self._current_widget().caret - s = caret.selection() - if not caret.has_selection() or not s: - message.info("Nothing to yank") - return + caret.selection(False, self._yank_callback) + return else: # pragma: no cover raise ValueError("Invalid value {!r} for `what'.".format(what)) - if sel and utils.supports_selection(): + self._yank_to_target(s) + + def _yank_callback(self, s): + if not self._current_widget().caret.has_selection() or not s: + message.info("Nothing to yank") + return + self._yank_to_target(s) + + def _yank_to_target(self, s): + if self.yank_object['sel'] and utils.supports_selection(): target = "primary selection" else: - sel = False + self.yank_object['sel'] = False target = "clipboard" - utils.set_clipboard(s, selection=sel) - if what != 'selection': - message.info("Yanked {} to {}: {}".format(what, target, s)) + utils.set_clipboard(s, selection=self.yank_object['sel']) + if self.yank_object['what'] != 'selection': + message.info("Yanked {} to {}: {}".format(self.yank_object['what'], target, s)) else: message.info("{} {} yanked to {}".format( len(s), "char" if len(s) == 1 else "chars", target)) - if not keep: + if not self.yank_object['keep']: modeman.leave(self._win_id, KeyMode.caret, "yank selected", maybe=True) + self.yank_object = None @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('count', count=True) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 03cf267ea..764db3024 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -231,20 +231,24 @@ class WebEngineCaret(browsertab.AbstractCaret): javascript.assemble('caret', 'moveToEndOfLine')) def move_to_start_of_next_block(self, count=1): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToStartOfNextBlock')) + for _ in range(count): + self._tab.run_js_async( + javascript.assemble('caret', 'moveToStartOfNextBlock')) def move_to_start_of_prev_block(self, count=1): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToStartOfPrevBlock')) + for _ in range(count): + self._tab.run_js_async( + javascript.assemble('caret', 'moveToStartOfPrevBlock')) def move_to_end_of_next_block(self, count=1): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToEndOfNextBlock')) + for _ in range(count): + self._tab.run_js_async( + javascript.assemble('caret', 'moveToEndOfNextBlock')) def move_to_end_of_prev_block(self, count=1): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToEndOfPrevBlock')) + for _ in range(count): + self._tab.run_js_async( + javascript.assemble('caret', 'moveToEndOfPrevBlock')) def move_to_start_of_document(self): self._tab.run_js_async( @@ -259,15 +263,28 @@ class WebEngineCaret(browsertab.AbstractCaret): javascript.assemble('caret', 'toggleSelection')) def drop_selection(self): - log.stub() + self._tab.run_js_async( + javascript.assemble('caret', 'dropSelection')) def has_selection(self): - return self._widget.hasSelection() + if qtutils.version_check('5.10'): + 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: 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( + 'window.getSelection().toString()', callback) + self.drop_selection() def _follow_selected_cb(self, js_elem, tab=False): """Callback for javascript which clicks the selected element. diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index 43bec3456..a1aeee698 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -335,10 +335,10 @@ class WebKitCaret(browsertab.AbstractCaret): def has_selection(self): return self._widget.hasSelection() - def selection(self, html=False): + def selection(self, html=False, callback=False): if html: - return self._widget.selectedHtml() - return self._widget.selectedText() + callback(self._widget.selectedHtml()) + callback(self._widget.selectedText()) def follow_selected(self, *, tab=False): if not self.has_selection(): diff --git a/qutebrowser/javascript/webengine_caret.js b/qutebrowser/javascript/webengine_caret.js index 9074e4e33..b13cfb171 100644 --- a/qutebrowser/javascript/webengine_caret.js +++ b/qutebrowser/javascript/webengine_caret.js @@ -1690,6 +1690,14 @@ window._qutebrowser.caret = (function() { CaretBrowsing.move('forward', 'documentboundary'); } + funcs.dropSelection = () => { + window.getSelection().removeAllRanges(); + } + + funcs.getSelection = () => { + return window.getSelection().toString(); + } + funcs.toggleSelection = () => { CaretBrowsing.selectionEnabled = !CaretBrowsing.selectionEnabled; }