From 215199dcce37a336a302195f57d40cb679530e78 Mon Sep 17 00:00:00 2001 From: Angela Remolina Date: Mon, 24 Apr 2023 20:29:48 -0500 Subject: [PATCH] PR: Define Switcher Plugin public API + fix switcher position bug (#20837) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Carlos Cordoba Co-authored-by: Daniel Althviz Moré --- spyder/app/tests/conftest.py | 4 +- spyder/app/tests/test_mainwindow.py | 57 ++++--- spyder/plugins/editor/plugin.py | 9 +- .../editor/tests/test_editor_config_dialog.py | 1 + .../utils/switcher_manager.py} | 6 +- spyder/plugins/editor/widgets/editor.py | 67 ++------- .../plugins/editor/widgets/tests/conftest.py | 2 +- .../editor/widgets/tests/test_editor.py | 2 +- .../widgets/tests/test_editor_and_outline.py | 4 +- .../widgets/tests/test_editorsplitter.py | 2 +- .../plugins/editor/widgets/tests/test_save.py | 2 +- .../editor/widgets/tests/test_shortcuts.py | 2 +- spyder/plugins/switcher/container.py | 48 +++--- spyder/plugins/switcher/plugin.py | 142 +++++++++++++++++- spyder/plugins/switcher/widgets/switcher.py | 58 +++++-- 15 files changed, 264 insertions(+), 142 deletions(-) rename spyder/plugins/{switcher/manager.py => editor/utils/switcher_manager.py} (98%) diff --git a/spyder/app/tests/conftest.py b/spyder/app/tests/conftest.py index 580a975cff2..5386c178138 100755 --- a/spyder/app/tests/conftest.py +++ b/spyder/app/tests/conftest.py @@ -477,8 +477,8 @@ def main_window(request, tmpdir, qtbot): for editorwindow in window.editor.editorwindows: editorwindow.close() editorstack = window.editor.get_current_editorstack() - if editorstack.switcher_dlg: - editorstack.switcher_dlg.close() + if editorstack.switcher_plugin: + editorstack.switcher_plugin.on_close() window.projects.close_project() diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 5cb3faba5f9..8365e31f66e 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -2392,7 +2392,8 @@ def test_tight_layout_option_for_inline_plot(main_window, qtbot, tmpdir): @pytest.mark.order(after="test_debug_unsaved_function") def test_switcher(main_window, qtbot, tmpdir): """Test the use of shorten paths when necessary in the switcher.""" - switcher = main_window.switcher.get_container().switcher + switcher = main_window.switcher + switcher_widget = switcher._switcher # Assert that the full path of a file is shown in the switcher file_a = tmpdir.join('test_file_a.py') @@ -2405,11 +2406,11 @@ def example_def_2(): ''') main_window.editor.load(str(file_a)) - main_window.switcher.get_container().open_switcher() - switcher_paths = [switcher.model.item(item_idx).get_description() - for item_idx in range(switcher.model.rowCount())] + switcher.open_switcher() + switcher_paths = [switcher_widget.model.item(item_idx).get_description() + for item_idx in range(switcher_widget.model.rowCount())] assert osp.dirname(str(file_a)) in switcher_paths or len(str(file_a)) > 75 - switcher.close() + switcher.on_close() # Assert that long paths are shortened in the switcher dir_b = tmpdir @@ -2419,21 +2420,21 @@ def example_def_2(): file_b.write('bar\n') main_window.editor.load(str(file_b)) - main_window.switcher.get_container().open_switcher() - file_b_text = switcher.model.item( - switcher.model.rowCount() - 1).get_description() + switcher.open_switcher() + file_b_text = switcher_widget.model.item( + switcher_widget.model.rowCount() - 1).get_description() assert '...' in file_b_text - switcher.close() + switcher.on_close() # Assert search works correctly search_texts = ['test_file_a', 'file_b', 'foo_spam'] expected_paths = [file_a, file_b, None] for search_text, expected_path in zip(search_texts, expected_paths): - main_window.switcher.get_container().open_switcher() - qtbot.keyClicks(switcher.edit, search_text) + switcher.open_switcher() + qtbot.keyClicks(switcher_widget.edit, search_text) qtbot.wait(200) - assert switcher.count() == bool(expected_path) - switcher.close() + assert switcher_widget.count() == bool(expected_path) + switcher.on_close() # Assert symbol switcher works main_window.editor.set_current_filename(str(file_a)) @@ -2449,15 +2450,15 @@ def example_def_2(): qtbot.wait(9000) - main_window.switcher.get_container().open_switcher() - qtbot.keyClicks(switcher.edit, '@') + switcher.open_switcher() + qtbot.keyClicks(switcher_widget.edit, '@') qtbot.wait(200) - assert switcher.count() == 2 - switcher.close() + assert switcher_widget.count() == 2 + switcher.on_close() @flaky(max_runs=3) -def test_edidorstack_open_switcher_dlg(main_window, tmpdir, qtbot): +def test_editorstack_open_switcher_dlg(main_window, tmpdir, qtbot): """ Test that the file switcher is working as expected when called from the editorstack. @@ -2472,16 +2473,15 @@ def test_edidorstack_open_switcher_dlg(main_window, tmpdir, qtbot): # Add a file to the editor. file = tmpdir.join('test_file_open_switcher_dlg.py') - file.write("a test file for test_edidorstack_open_switcher_dlg") + file.write("a test file for test_editorstack_open_switcher_dlg") main_window.editor.load(str(file)) # Test that the file switcher opens as expected from the editorstack. editorstack = main_window.editor.get_current_editorstack() - assert editorstack.switcher_dlg is None - editorstack.open_switcher_dlg() - assert editorstack.switcher_dlg - assert editorstack.switcher_dlg.isVisible() - assert (editorstack.switcher_dlg.count() == + editorstack.switcher_plugin.open_switcher() + assert editorstack.switcher_plugin + assert editorstack.switcher_plugin.is_visible() + assert (editorstack.switcher_plugin.count() == len(main_window.editor.get_filenames())) @@ -2527,11 +2527,10 @@ def example_def_2(): # Test that the symbol finder opens as expected from the editorstack. editorstack = main_window.editor.get_current_editorstack() - assert editorstack.switcher_dlg is None - editorstack.open_symbolfinder_dlg() - assert editorstack.switcher_dlg - assert editorstack.switcher_dlg.isVisible() - assert editorstack.switcher_dlg.count() == 2 + editorstack.switcher_plugin.open_symbolfinder() + assert editorstack.switcher_plugin + assert editorstack.switcher_plugin.is_visible() + assert editorstack.switcher_plugin.count() == 2 @flaky(max_runs=3) diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index f0d8961339f..05eadf7729c 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -51,7 +51,7 @@ SelectionContextModificator, ExtraAction) from spyder.plugins.editor.confpage import EditorConfigPage from spyder.plugins.editor.utils.autosave import AutosaveForPlugin -from spyder.plugins.switcher.manager import EditorSwitcherManager +from spyder.plugins.editor.utils.switcher_manager import EditorSwitcherManager from spyder.plugins.editor.widgets.codeeditor import CodeEditor from spyder.plugins.editor.widgets.editor import (EditorMainWindow, EditorSplitter, @@ -1362,14 +1362,9 @@ def register_plugin(self): self.add_dockwidget() - # Add modes to switcher - # TODO: 'Switcher' object has no attribute 'add_mode' - # it is needed to create a public API that contains the methods - # that handles the EditorSwitcherManager - self.switcher_manager = EditorSwitcherManager( self, - self.main.switcher.get_container().switcher, + self.main.switcher, self.get_current_editor, self.get_current_editorstack, section=self.get_plugin_title()) diff --git a/spyder/plugins/editor/tests/test_editor_config_dialog.py b/spyder/plugins/editor/tests/test_editor_config_dialog.py index 7de7d0d08e5..a9af216c538 100644 --- a/spyder/plugins/editor/tests/test_editor_config_dialog.py +++ b/spyder/plugins/editor/tests/test_editor_config_dialog.py @@ -28,6 +28,7 @@ class MainWindowMock(QMainWindow): ipyconsole = Mock() mainmenu = Mock() sig_setup_finished = Mock() + switcher = Mock() @pytest.mark.parametrize( diff --git a/spyder/plugins/switcher/manager.py b/spyder/plugins/editor/utils/switcher_manager.py similarity index 98% rename from spyder/plugins/switcher/manager.py rename to spyder/plugins/editor/utils/switcher_manager.py index 8b98fb60800..a2b93b685ac 100644 --- a/spyder/plugins/switcher/manager.py +++ b/spyder/plugins/editor/utils/switcher_manager.py @@ -32,7 +32,7 @@ class EditorSwitcherManager(object): LINE_MODE = ':' FILES_MODE = '' - def __init__(self, plugin, switcher_instance, get_codeeditor, + def __init__(self, plugin, switcher_plugin, get_codeeditor, get_editorstack, section=_("Editor")): """ 'get_codeeditor' and 'get_editorstack' params should be callables @@ -42,7 +42,7 @@ def __init__(self, plugin, switcher_instance, get_codeeditor, current_editorstack = get_editorstack() """ self._plugin = plugin - self._switcher = switcher_instance + self._switcher = switcher_plugin self._editor = get_codeeditor self._editorstack = get_editorstack self._section = section @@ -231,7 +231,7 @@ def line_switcher_handler(self, data, search_text, visible=False): try: line_number = int(line_number) editorstack.go_to_line(line_number) - self._switcher.setVisible(visible) + self._switcher.set_visible(visible) # Closing the switcher if not visible: self._current_line = None diff --git a/spyder/plugins/editor/widgets/editor.py b/spyder/plugins/editor/widgets/editor.py index 910d7ea5ae0..41e9308226f 100644 --- a/spyder/plugins/editor/widgets/editor.py +++ b/spyder/plugins/editor/widgets/editor.py @@ -38,7 +38,6 @@ get_filter, is_kde_desktop, is_anaconda) from spyder.plugins.editor.utils.autosave import AutosaveForStack from spyder.plugins.editor.utils.editor import get_file_language -from spyder.plugins.switcher.manager import EditorSwitcherManager from spyder.plugins.editor.widgets import codeeditor from spyder.plugins.editor.widgets.editorstack_helpers import ( ThreadManager, FileInfo, StackHistory) @@ -280,7 +279,7 @@ class EditorStack(QWidget, SpyderConfigurationAccessor): :py:meth:spyder.plugins.editor.widgets.editor.EditorStack.send_to_help """ - def __init__(self, parent, actions): + def __init__(self, parent, actions, use_switcher=True): QWidget.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose) @@ -297,10 +296,17 @@ def __init__(self, parent, actions): self.setLayout(layout) self.menu = None - self.switcher_dlg = None self.switcher_manager = None self.tabs = None self.tabs_switcher = None + self.switcher_plugin = None + + switcher_action = None + symbolfinder_action = None + if use_switcher: + self.switcher_plugin = self.get_plugin().main.switcher + switcher_action = self.switcher_plugin.get_action("file switcher") + symbolfinder_action = self.switcher_plugin.get_action("symbol finder") self.stack_history = StackHistory(self) @@ -313,16 +319,6 @@ def __init__(self, parent, actions): self.data = [] - switcher_action = create_action( - self, - _("File switcher..."), - icon=ima.icon('filelist'), - triggered=self.open_switcher_dlg) - symbolfinder_action = create_action( - self, - _("Find symbols in file..."), - icon=ima.icon('symbol_find'), - triggered=self.open_symbolfinder_dlg) copy_to_cb_action = create_action(self, _("Copy path to clipboard"), icon=ima.icon('editcopy'), triggered=lambda: @@ -792,41 +788,6 @@ def clone_from(self, other): self.clone_editor_from(other_finfo, set_current=True) self.set_stack_index(other.get_stack_index()) - @Slot() - @Slot(str) - def open_switcher_dlg(self, initial_text=''): - """Open file list management dialog box""" - if not self.tabs.count(): - return - if self.switcher_dlg is not None and self.switcher_dlg.isVisible(): - self.switcher_dlg.hide() - self.switcher_dlg.clear() - return - if self.switcher_dlg is None: - from spyder.plugins.switcher.widgets.switcher import Switcher - self.switcher_dlg = Switcher(self) - self.switcher_manager = EditorSwitcherManager( - self.get_plugin(), - self.switcher_dlg, - self.get_current_editor, - lambda: self, - section=self.get_plugin_title()) - - if isinstance(initial_text, bool): - initial_text = '' - - self.switcher_dlg.set_search_text(initial_text) - self.switcher_dlg.setup() - self.switcher_dlg.show() - # Note: the +1 pixel on the top makes it look better - delta_top = (self.tabs.tabBar().geometry().height() + - self.fname_label.geometry().height() + 1) - self.switcher_dlg.set_position(delta_top) - - @Slot() - def open_symbolfinder_dlg(self): - self.open_switcher_dlg(initial_text='@') - def get_plugin(self): """Get the plugin of the parent widget.""" # Needed for the editor stack to use its own switcher instance. @@ -3003,7 +2964,8 @@ class EditorSplitter(QSplitter): """QSplitter for editor windows.""" def __init__(self, parent, plugin, menu_actions, first=False, - register_editorstack_cb=None, unregister_editorstack_cb=None): + register_editorstack_cb=None, unregister_editorstack_cb=None, + use_switcher=True): """Create a splitter for dividing an editor window into panels. Adds a new EditorStack instance to this splitter. If it's not @@ -3039,7 +3001,7 @@ def __init__(self, parent, plugin, menu_actions, first=False, self.unregister_editorstack_cb = unregister_editorstack_cb self.menu_actions = menu_actions - self.editorstack = EditorStack(self, menu_actions) + self.editorstack = EditorStack(self, menu_actions, use_switcher) self.register_editorstack_cb(self.editorstack) if not first: self.plugin.clone_editorstack(editorstack=self.editorstack) @@ -3559,13 +3521,14 @@ def __init__(self): self.editorstacks = [] self.editorwindows = [] - self.last_focused_editorstack = {} # fake + self.last_focused_editorstack = {} # fake self.find_widget = FindReplace(self, enable_replace=True) self.outlineexplorer = OutlineExplorerWidget(None, self, self) self.outlineexplorer.edit_goto.connect(self.go_to_file) self.editor_splitter = EditorSplitter(self, self, menu_actions, - first=True) + first=True, + use_switcher=False) editor_widgets = QWidget(self) editor_layout = QVBoxLayout() diff --git a/spyder/plugins/editor/widgets/tests/conftest.py b/spyder/plugins/editor/widgets/tests/conftest.py index d030067df56..ba9f94b2a9f 100644 --- a/spyder/plugins/editor/widgets/tests/conftest.py +++ b/spyder/plugins/editor/widgets/tests/conftest.py @@ -50,7 +50,7 @@ def codeeditor_factory(): def editor_factory(new_file=True, text=None): - editorstack = EditorStack(None, []) + editorstack = EditorStack(None, [], False) editorstack.set_find_widget(FindReplace(editorstack)) editorstack.set_io_actions(Mock(), Mock(), Mock(), Mock()) if new_file: diff --git a/spyder/plugins/editor/widgets/tests/test_editor.py b/spyder/plugins/editor/widgets/tests/test_editor.py index f7c7c3e4a7d..f47ffef19e1 100644 --- a/spyder/plugins/editor/widgets/tests/test_editor.py +++ b/spyder/plugins/editor/widgets/tests/test_editor.py @@ -36,7 +36,7 @@ # ============================================================================= @pytest.fixture def base_editor_bot(qtbot): - editor_stack = EditorStack(None, []) + editor_stack = EditorStack(None, [], False) editor_stack.set_find_widget(Mock()) editor_stack.set_io_actions(Mock(), Mock(), Mock(), Mock()) return editor_stack diff --git a/spyder/plugins/editor/widgets/tests/test_editor_and_outline.py b/spyder/plugins/editor/widgets/tests/test_editor_and_outline.py index 1fb4d9f1e34..04afbf2c184 100644 --- a/spyder/plugins/editor/widgets/tests/test_editor_and_outline.py +++ b/spyder/plugins/editor/widgets/tests/test_editor_and_outline.py @@ -55,7 +55,7 @@ def get_tree_elements(treewidget): this_tree = {node.name: []} parent_tree.append(this_tree) this_stack = [(this_tree[node.name], child) - for child in node.children] + for child in node.children] stack = this_stack + stack return root_tree @@ -119,7 +119,7 @@ def completions_codeeditor_outline(completions_codeeditor, outlineexplorer): @pytest.fixture def editorstack(qtbot, outlineexplorer): def _create_editorstack(files): - editorstack = editor.EditorStack(None, []) + editorstack = editor.EditorStack(None, [], False) editorstack.set_find_widget(Mock()) editorstack.set_io_actions(Mock(), Mock(), Mock(), Mock()) editorstack.analysis_timer = Mock() diff --git a/spyder/plugins/editor/widgets/tests/test_editorsplitter.py b/spyder/plugins/editor/widgets/tests/test_editorsplitter.py index 0c876f5d8ec..bc06634ac8b 100644 --- a/spyder/plugins/editor/widgets/tests/test_editorsplitter.py +++ b/spyder/plugins/editor/widgets/tests/test_editorsplitter.py @@ -27,7 +27,7 @@ # ---- Qt Test Fixtures def editor_stack(): - editor_stack = EditorStack(None, []) + editor_stack = EditorStack(None, [], False) editor_stack.set_find_widget(Mock()) editor_stack.set_io_actions(Mock(), Mock(), Mock(), Mock()) return editor_stack diff --git a/spyder/plugins/editor/widgets/tests/test_save.py b/spyder/plugins/editor/widgets/tests/test_save.py index bdcf205c2c4..15d521efb2e 100644 --- a/spyder/plugins/editor/widgets/tests/test_save.py +++ b/spyder/plugins/editor/widgets/tests/test_save.py @@ -47,7 +47,7 @@ def add_files(editorstack): # ---- Qt Test Fixtures @pytest.fixture def base_editor_bot(qtbot): - editor_stack = editor.EditorStack(None, []) + editor_stack = editor.EditorStack(None, [], False) editor_stack.set_find_widget(Mock()) editor_stack.set_io_actions(Mock(), Mock(), Mock(), Mock()) return editor_stack, qtbot diff --git a/spyder/plugins/editor/widgets/tests/test_shortcuts.py b/spyder/plugins/editor/widgets/tests/test_shortcuts.py index cb63eee2d61..1d1c2062d0f 100644 --- a/spyder/plugins/editor/widgets/tests/test_shortcuts.py +++ b/spyder/plugins/editor/widgets/tests/test_shortcuts.py @@ -32,7 +32,7 @@ def editor_bot(qtbot): Set up EditorStack with CodeEditors containing some Python code. The cursor is at the empty line below the code. """ - editorstack = EditorStack(None, []) + editorstack = EditorStack(None, [], False) editorstack.set_find_widget(Mock()) editorstack.set_io_actions(Mock(), Mock(), Mock(), Mock()) editorstack.close_action.setEnabled(False) diff --git a/spyder/plugins/switcher/container.py b/spyder/plugins/switcher/container.py index 428c5440ba6..24f055a7219 100644 --- a/spyder/plugins/switcher/container.py +++ b/spyder/plugins/switcher/container.py @@ -9,10 +9,9 @@ # Third-party imports from qtpy.QtCore import Qt -from qtpy.QtCore import Signal +from qtpy.QtWidgets import QApplication # Spyder imports -from spyder.api.config.decorators import on_conf_change from spyder.api.translations import _ from spyder.api.widgets.main_container import PluginMainContainer from spyder.plugins.switcher.widgets.switcher import Switcher @@ -20,18 +19,6 @@ class SwitcherContainer(PluginMainContainer): - # Signals - # signals needed when the EditorSwitcherManager is migrated - # Dismissed switcher - sig_rejected = Signal() - # Search/Filter text changes - sig_text_changed = Signal() - # Current item changed - sig_item_changed = Signal() - # List item selected, mode and cleaned search text - sig_item_selected = Signal() - sig_mode_selected = Signal() - # --- PluginMainContainer API # ------------------------------------------------------------------------ def setup(self): @@ -47,7 +34,7 @@ def setup(self): register_shortcut=True, context=Qt.ApplicationShortcut ) - + self.create_action( 'symbol finder', _('Symbol finder...'), @@ -58,13 +45,6 @@ def setup(self): context=Qt.ApplicationShortcut ) - # Signals - self.switcher.sig_rejected.connect(self.sig_rejected) - self.switcher.sig_text_changed.connect(self.sig_text_changed) - self.switcher.sig_item_changed.connect(self.sig_item_changed) - self.switcher.sig_item_selected.connect(self.sig_item_selected) - self.switcher.sig_mode_selected.connect(self.sig_mode_selected) - def update_actions(self): pass @@ -84,18 +64,24 @@ def open_switcher(self, symbol=False): switcher.set_search_text('') switcher.setup() - switcher.show() + # Set position + mainwindow = self._plugin.get_main() # Note: The +8 pixel on the top makes it look better - # FIXME: Why is this using the toolbars menu? A: To not be on top of - # the toolbars. - # Probably toolbars should be taken into account for this 'delta' only - # when are visible - mainwindow = self._plugin.get_main() - delta_top = (mainwindow.toolbar.toolbars_menu.geometry().height() + - mainwindow.menuBar().geometry().height() + 8) + default_top = (mainwindow.toolbar.toolbars_menu.geometry().height() + + mainwindow.menuBar().geometry().height() + 8) + + current_window = QApplication.activeWindow() + if current_window == mainwindow: + if self.get_conf('toolbars_visible', section='toolbar'): + delta_top = default_top + else: + delta_top = mainwindow.menuBar().geometry().height() + 8 + else: + delta_top = default_top - switcher.set_position(delta_top) + switcher.set_position(delta_top, current_window) + switcher.show() def open_symbolfinder(self): """Open symbol list management dialog box.""" diff --git a/spyder/plugins/switcher/plugin.py b/spyder/plugins/switcher/plugin.py index 30ac15c415d..29a8e9599e1 100644 --- a/spyder/plugins/switcher/plugin.py +++ b/spyder/plugins/switcher/plugin.py @@ -12,10 +12,14 @@ # Standard library imports import sys +# Third-party imports +from qtpy.QtCore import Signal + # Local imports from spyder.api.translations import _ from spyder.api.plugins import Plugins, SpyderPluginV2 -from spyder.api.plugin_registration.decorators import on_plugin_available, on_plugin_teardown +from spyder.api.plugin_registration.decorators import (on_plugin_available, + on_plugin_teardown) from spyder.plugins.switcher.container import SwitcherContainer from spyder.plugins.mainmenu.api import ApplicationMenus, FileMenuSections @@ -41,6 +45,50 @@ class Switcher(SpyderPluginV2): CONF_FILE = False # --- Signals + sig_rejected = Signal() + """ + This signal is emitted when the plugin is dismissed. + """ + + sig_text_changed = Signal(str) + """ + This signal is emitted when the plugin search/filter text changes. + + Parameters + ---------- + search_text: str + The current search/filter text. + """ + + sig_item_changed = Signal(object) + """ + This signal is emitted when the plugin current item changes. + """ + + sig_item_selected = Signal(object, str, str) + """ + This signal is emitted when an item is selected from the switcher plugin + list. + + Parameters + ---------- + item: object + The current selected item from the switcher list (QStandardItem). + mode: str + The current selected mode (open files "", symbol "@" or line ":"). + search_text: str + Cleaned search/filter text. + """ + + sig_mode_selected = Signal(str) + """ + This signal is emitted when a mode is selected. + + Parameters + ---------- + mode: str + The selected mode (open files "", symbol "@" or line ":"). + """ # --- SpyderPluginV2 API # ------------------------------------------------------------------------ @@ -56,6 +104,17 @@ def get_icon(self): def on_initialize(self): container = self.get_container() + self._switcher = container.switcher + + self._switcher.sig_rejected.connect(self.sig_rejected) + self._switcher.sig_text_changed.connect(self.sig_text_changed) + self._switcher.sig_item_changed.connect(self.sig_item_changed) + self._switcher.sig_item_selected.connect(self.sig_item_selected) + self._switcher.sig_mode_selected.connect(self.sig_mode_selected) + + def on_close(self, cancellable=True): + """Close switcher widget.""" + self._switcher.close() @on_plugin_available(plugin=Plugins.MainMenu) def on_main_menu_available(self): @@ -89,3 +148,84 @@ def on_main_menu_teardown(self): # --- Public API # ------------------------------------------------------------------------ + + # Switcher methods + def set_placeholder_text(self, text): + """Set the text appearing on the empty line edit.""" + self._switcher.set_placeholder_text(text) + + def setup(self): + """Set-up list widget content based on the filtering.""" + self._switcher.setup() + + def open_switcher(self, symbol=False): + """Open switcher dialog.""" + self.get_container().open_switcher(symbol) + + def open_symbolfinder(self): + """Open symbol list management dialog.""" + self.get_container().open_symbolfinder() + + # QDialog methods + def show(self): + """Show switcher.""" + self._switcher.show() + + def hide(self): + """Hide switcher.""" + self._switcher.hide() + + def set_visible(self, visible): + """Show or hide switcher.""" + self._switcher.setVisible(visible) + + def is_visible(self): + """Return if the switcher is visible.""" + return self._switcher.isVisible() + + # Item methods + def current_item(self): + """Return the current selected item in the list widget.""" + return self._switcher.current_item() + + def add_item(self, icon=None, title=None, description=None, shortcut=None, + section=None, data=None, tool_tip=None, action_item=False, + last_item=True): + """Add a switcher list item.""" + self._switcher.add_item(icon, title, description, shortcut, + section, data, tool_tip, action_item, + last_item) + + def add_separator(self): + """Add a separator item.""" + self._switcher.add_separator() + + def clear(self): + """Remove all items from the list and clear the search text.""" + self._switcher.clear() + + def count(self): + """Get the item count in the list widget.""" + return self._switcher.count() + + # Mode methods + def add_mode(self, token, description): + """Add mode by token key and description.""" + self._switcher.add_mode(token, description) + + def get_mode(self): + """Get the current mode the switcher is in.""" + self._switcher.get_mode() + + def remove_mode(self, token): + """Remove mode by token key.""" + self._switcher.remove_mode(token) + + def clear_modes(self): + """Delete all modes spreviously defined.""" + self._switcher.clear_modes() + + # Lineedit methods + def set_search_text(self, string): + """Set the content of the search text.""" + self._switcher.set_search_text(string) diff --git a/spyder/plugins/switcher/widgets/switcher.py b/spyder/plugins/switcher/widgets/switcher.py index 64907b8217c..a42b8f0a2b5 100644 --- a/spyder/plugins/switcher/widgets/switcher.py +++ b/spyder/plugins/switcher/widgets/switcher.py @@ -18,7 +18,7 @@ from spyder.plugins.switcher.widgets.proxymodel import SwitcherProxyModel from spyder.plugins.switcher.widgets.item import ( SwitcherItem, SwitcherSeparatorItem) -from spyder.py3compat import TEXT_TYPES, to_text_string +from spyder.py3compat import to_text_string from spyder.utils.icon_manager import ima from spyder.widgets.helperwidgets import HTMLDelegate from spyder.utils.stringmatching import get_search_scores @@ -93,15 +93,51 @@ class Switcher(QDialog): SwitcherItem: [title description section] """ - # Dismissed switcher + # --- Signals sig_rejected = Signal() - # Search/Filter text changes - sig_text_changed = Signal(TEXT_TYPES[-1]) - # Current item changed + """ + This signal is emitted when the plugin is dismissed. + """ + + sig_text_changed = Signal(str) + """ + This signal is emitted when the plugin search/filter text changes. + + Parameters + ---------- + search_text: str + The current search/filter text. + """ + sig_item_changed = Signal(object) - # List item selected, mode and cleaned search text - sig_item_selected = Signal(object, TEXT_TYPES[-1], TEXT_TYPES[-1], ) - sig_mode_selected = Signal(TEXT_TYPES[-1]) + """ + This signal is emitted when the plugin current item changes. + """ + + sig_item_selected = Signal(object, str, str) + """ + This signal is emitted when an item is selected from the switcher plugin + list. + + Parameters + ---------- + item: object + The current selected item from the switcher list (QStandardItem). + mode: str + The current selected mode (open files "", symbol "@" or line ":"). + search_text: str + Cleaned search/filter text. + """ + + sig_mode_selected = Signal(str) + """ + This signal is emitted when a mode is selected. + + Parameters + ---------- + mode: str + The selected mode (open files "", symbol "@" or line ":"). + """ _MAX_NUM_ITEMS = 15 _MIN_WIDTH = 580 @@ -333,9 +369,11 @@ def set_height(self): switcher_height = self._MIN_HEIGHT self.setFixedHeight(int(switcher_height)) - def set_position(self, top): + def set_position(self, top, parent=None): """Set the position of the dialog.""" - parent = self.parent() + if parent is None: + parent = self.parent() + if parent is not None: geo = parent.geometry() width = self.list.width() # This has been set in setup