diff --git a/spyder/app/tests/spyder-boilerplate/setup.py b/spyder/app/tests/spyder-boilerplate/setup.py index 8e420f28359..8d9c1ce9639 100644 --- a/spyder/app/tests/spyder-boilerplate/setup.py +++ b/spyder/app/tests/spyder-boilerplate/setup.py @@ -23,7 +23,7 @@ install_requires=[ "qtpy", "qtawesome", - "spyder>=5.1.1", + "spyder>=6", ], packages=find_packages(), entry_points={ diff --git a/spyder/app/tests/spyder-boilerplate/spyder_boilerplate/spyder/plugin.py b/spyder/app/tests/spyder-boilerplate/spyder_boilerplate/spyder/plugin.py index a08de26c308..27b2c21dc04 100644 --- a/spyder/app/tests/spyder-boilerplate/spyder_boilerplate/spyder/plugin.py +++ b/spyder/app/tests/spyder-boilerplate/spyder_boilerplate/spyder/plugin.py @@ -10,7 +10,7 @@ # Third party imports import qtawesome as qta -from qtpy.QtWidgets import QHBoxLayout, QLabel +from qtpy.QtWidgets import QHBoxLayout, QTextEdit # Spyder imports from spyder.api.config.decorators import on_conf_change @@ -50,12 +50,13 @@ class SpyderBoilerplateWidget(PluginMainWidget): def __init__(self, name=None, plugin=None, parent=None): super().__init__(name, plugin, parent) - # Create an example label - self._example_label = QLabel("Example Label") + # Create example widgets + self._example_widget = QTextEdit(self) + self._example_widget.setText("Example text") # Add example label to layout layout = QHBoxLayout() - layout.addWidget(self._example_label) + layout.addWidget(self._example_widget) self.setLayout(layout) # --- PluginMainWidget API @@ -72,7 +73,7 @@ def setup(self): name=SpyderBoilerplateActions.ExampleAction, text="Example action", tip="Example hover hint", - icon=self.create_icon("spyder"), + icon=self.create_icon("python"), triggered=lambda: print("Example action triggered!"), ) @@ -92,6 +93,19 @@ def setup(self): SpyderBoilerplateOptionsMenuSections.ExampleSection, ) + # Shortcuts + self.register_shortcut_for_widget( + "Change text", + self.change_text, + ) + + self.register_shortcut_for_widget( + "new text", + self.new_text, + context="editor", + plugin_name=self._plugin.NAME, + ) + def update_actions(self): pass @@ -101,6 +115,15 @@ def on_section_conf_change(self, section): # --- Public API # ------------------------------------------------------------------------ + def change_text(self): + if self._example_widget.toPlainText() == "": + self._example_widget.setText("Example text") + else: + self._example_widget.setText("") + + def new_text(self): + if self._example_widget.toPlainText() != "Another text": + self._example_widget.setText("Another text") class SpyderBoilerplate(SpyderDockablePlugin): @@ -115,6 +138,15 @@ class SpyderBoilerplate(SpyderDockablePlugin): CONF_SECTION = NAME CONF_WIDGET_CLASS = SpyderBoilerplateConfigPage CUSTOM_LAYOUTS = [VerticalSplitLayout2] + CONF_DEFAULTS = [ + (CONF_SECTION, {}), + ( + "shortcuts", + # Note: These shortcut names are capitalized to check we can + # set/get/reset them correctly. + {f"{NAME}/Change text": "Ctrl+B", "editor/New text": "Ctrl+H"}, + ), + ] # --- Signals diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 5756dcf580b..cb4b77f2f22 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -83,6 +83,7 @@ from spyder.plugins.run.api import ( RunExecutionParameters, ExtendedRunExecutionParameters, WorkingDirOpts, WorkingDirSource, RunContext) +from spyder.plugins.shortcuts.widgets.table import SEQUENCE from spyder.py3compat import qbytearray_to_str, to_text_string from spyder.utils.environ import set_user_env from spyder.utils.conda import get_list_conda_envs @@ -5434,6 +5435,91 @@ def test_add_external_plugins_to_dependencies(main_window, qtbot): assert 'spyder-boilerplate' in external_names +@pytest.mark.skipif( + sys.platform.startswith("linux") and running_in_ci(), + reason="Fails on Linux and CI" +) +@pytest.mark.skipif(not running_in_ci(), reason="Only works in CIs") +def test_shortcuts_in_external_plugins(main_window, qtbot): + """Test that keyboard shortcuts for widgets work in external plugins.""" + # Wait until the window is fully up + shell = main_window.ipyconsole.get_current_shellwidget() + qtbot.waitUntil( + lambda: shell.spyder_kernel_ready and shell._prompt_html is not None, + timeout=SHELL_TIMEOUT) + + # Show plugin + main_widget = main_window.get_plugin('spyder_boilerplate').get_widget() + main_widget.toggle_view_action.setChecked(True) + + # Give focus to text edit area + example_widget = main_widget._example_widget + example_widget.setFocus() + + # Check first shortcut is working + qtbot.keyClick(example_widget, Qt.Key_B, modifier=Qt.ControlModifier) + assert example_widget.toPlainText() == "" + qtbot.keyClick(example_widget, Qt.Key_B, modifier=Qt.ControlModifier) + assert example_widget.toPlainText() == "Example text" + + # Check second shortcut is working + qtbot.keyClick(example_widget, Qt.Key_H, modifier=Qt.ControlModifier) + assert example_widget.toPlainText() == "Another text" + qtbot.keyClick(example_widget, Qt.Key_H, modifier=Qt.ControlModifier) + assert example_widget.toPlainText() == "Another text" + + # Open Preferences and select shortcuts table + dlg, index, page = preferences_dialog_helper( + qtbot, main_window, 'shortcuts' + ) + table = page.table + + # Change shortcuts in table + new_shortcuts = [("change text", "Ctrl+J"), ("new text", "Alt+K")] + for name, sequence in new_shortcuts: + table.finder.setFocus() + table.finder.clear() + qtbot.keyClicks(table.finder, name) + index = table.proxy_model.mapToSource(table.currentIndex()) + row = index.row() + sequence_index = table.source_model.index(row, SEQUENCE) + table.source_model.setData(sequence_index, sequence) + + # Save new shortcuts + dlg.ok_btn.animateClick() + qtbot.wait(1000) + + # Check new shortcuts are working + example_widget.setFocus() + qtbot.keyClick(example_widget, Qt.Key_J, modifier=Qt.ControlModifier) + assert example_widget.toPlainText() == "" + qtbot.keyClick(example_widget, Qt.Key_J, modifier=Qt.ControlModifier) + assert example_widget.toPlainText() == "Example text" + + qtbot.keyClick(example_widget, Qt.Key_K, modifier=Qt.AltModifier) + assert example_widget.toPlainText() == "Another text" + + # Open Preferences again and reset shortcuts + dlg, index, page = preferences_dialog_helper( + qtbot, main_window, 'shortcuts' + ) + page.reset_to_default(force=True) + + # Close preferences + dlg.ok_btn.animateClick() + qtbot.wait(1000) + + # Check default shortcuts are working again + example_widget.setFocus() + qtbot.keyClick(example_widget, Qt.Key_B, modifier=Qt.ControlModifier) + assert example_widget.toPlainText() == "" + qtbot.keyClick(example_widget, Qt.Key_B, modifier=Qt.ControlModifier) + assert example_widget.toPlainText() == "Example text" + + qtbot.keyClick(example_widget, Qt.Key_H, modifier=Qt.ControlModifier) + assert example_widget.toPlainText() == "Another text" + + def test_locals_globals_var_debug(main_window, qtbot, tmpdir): """Test that the debugger can handle variables named globals and locals.""" ipyconsole = main_window.ipyconsole