Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Improve Maintain focus in the editor option and unmaximize plugins when running/debugging code #18928

Merged
merged 19 commits into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6e7e27d
IPython console: Don't give it focus when undocked
ccordoba12 Apr 7, 2022
773f93e
Editor: Use 'Maintain focus in editor' option when running files too
ccordoba12 Apr 17, 2022
2d5a906
Editor/IPython console: Use 'Maintain focus in editor' when running d…
ccordoba12 Apr 17, 2022
3e9197e
Editor: Update text of an option to better reflect how it works
ccordoba12 Apr 17, 2022
b5a1d90
API: Fix calls to switch_to_plugin when plugins are docked/undocked
ccordoba12 Apr 18, 2022
6cc42d3
Main Window: Use layouts.maximize_action to maximize/unmaximize plugins
ccordoba12 Apr 18, 2022
1965292
Layout: Set maximized state when plugins are maximized
ccordoba12 Apr 18, 2022
ef8ef78
Main window: Remove maximize_dockwidget method
ccordoba12 Apr 18, 2022
b699690
API: Add method to call create_window directly from plugins
ccordoba12 Apr 18, 2022
72f5819
Testing: Expand test_maximize_minimize_plugins to cover much more cases
ccordoba12 Apr 18, 2022
f1cef1f
Testing: Expand test_focus_to_editor option to cover more cases
ccordoba12 Apr 27, 2022
3862d93
IPython console: Unmaximize plugins before any run or debug operation
ccordoba12 Aug 3, 2022
8ea1fba
IPython console: Add default values to most args of run_script
ccordoba12 Aug 4, 2022
52090d0
Testing: Fix an error on Windows
ccordoba12 Aug 4, 2022
da2a8d2
Projects: Move logic to unmaximize any other plugin to the Layout plugin
ccordoba12 Aug 6, 2022
f458f22
Main Window: Move logic to switch to plugins to the Layout plugin
ccordoba12 Aug 6, 2022
a4206fb
Testing: Simplify test_maximize_minimize_plugins
ccordoba12 Aug 6, 2022
7dc3231
IPython console: Remove missing word in docstring
ccordoba12 Aug 8, 2022
fcbf425
Use a signal to request unmaximizing plugins
ccordoba12 Aug 8, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion spyder/api/plugins/new_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,17 @@ class SpyderPluginV2(QObject, SpyderActionMixin, SpyderConfigurationObserver,
To be used by plugins tracking main window position changes.
"""

sig_unmaximize_plugin_requested = Signal((), (object,))
"""
This signal is emitted to inform the main window that it needs to
unmaximize the currently maximized plugin, if any.

Parameters
----------
plugin_instance: SpyderDockablePlugin
Unmaximize plugin only if it is not `plugin_instance`.
"""

# --- Private attributes -------------------------------------------------
# ------------------------------------------------------------------------
# Define configuration name map for plugin to split configuration
Expand Down Expand Up @@ -1043,7 +1054,8 @@ def switch_to_plugin(self, force_focus=False):
"""
Switch to plugin and define if focus should be given or not.
"""
self.sig_switch_to_plugin_requested.emit(self, force_focus)
if self.get_widget().windowwidget is None:
self.sig_switch_to_plugin_requested.emit(self, force_focus)

def set_ancestor(self, ancestor_widget):
"""
Expand All @@ -1068,6 +1080,9 @@ def toggle_view_action(self):
def create_dockwidget(self, mainwindow):
return self.get_widget().create_dockwidget(mainwindow)

def create_window(self):
self.get_widget().create_window()

def close_window(self, save_undocked=False):
self.get_widget().close_window(save_undocked=save_undocked)

Expand Down
46 changes: 33 additions & 13 deletions spyder/api/widgets/main_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ class PluginMainWidget(QWidget, SpyderWidgetMixin, SpyderToolbarMixin):
stacked vertically and cannot be placed horizontally next to each other.
"""

# --- Attributes
# ------------------------------------------------------------------------
# ---- Attributes
# -------------------------------------------------------------------------
ENABLE_SPINNER = False
"""
This attribute enables/disables showing a spinner on the top right to the
Expand Down Expand Up @@ -114,8 +114,8 @@ class PluginMainWidget(QWidget, SpyderWidgetMixin, SpyderToolbarMixin):
the plugin, then this attribute should have a `None` value.
"""

# --- Signals
# ------------------------------------------------------------------------
# ---- Signals
# -------------------------------------------------------------------------
sig_free_memory_requested = Signal()
"""
This signal can be emitted to request the main application to garbage
Expand Down Expand Up @@ -206,12 +206,12 @@ def __init__(self, name, plugin, parent=None):
self._plugin = plugin
self._parent = parent
self._default_margins = None
self.is_maximized = None
self.is_visible = None
self.dock_action = None
self.undock_action = None
self.close_action = None
self._toolbars_already_rendered = False
self._is_maximized = False

# Attribute used to access the action, toolbar, toolbutton and menu
# registries
Expand Down Expand Up @@ -291,8 +291,8 @@ def __init__(self, name, plugin, parent=None):
self._toolbars_layout.addLayout(self._main_toolbar_layout)
self._main_layout.addLayout(self._toolbars_layout, stretch=1)

# --- Private Methods
# ------------------------------------------------------------------------
# ---- Private Methods
# -------------------------------------------------------------------------
def _setup(self):
"""
Setup default actions, create options menu, and connect signals.
Expand Down Expand Up @@ -452,8 +452,8 @@ def _on_title_bar_shown(self, visible):
method = getattr(self.lock_unlock_action, method_name)
method(_("Unlock to move pane to another position"))

# --- Public Qt overriden methods
# ------------------------------------------------------------------------
# ---- Public Qt overriden methods
# -------------------------------------------------------------------------
def setLayout(self, layout):
"""
Set layout of the main widget of this plugin.
Expand All @@ -467,8 +467,8 @@ def closeEvent(self, event):
self.on_close()
super().closeEvent(event)

# --- Public methods to use
# ------------------------------------------------------------------------
# ---- Public methods to use
# -------------------------------------------------------------------------
def get_plugin(self):
"""
Return the parent plugin.
Expand Down Expand Up @@ -735,8 +735,8 @@ def render_toolbars(self):

# self._toolbars_already_rendered = True

# --- SpyderDockwidget handling ------------------------------------------
# ------------------------------------------------------------------------
# ---- SpyderDockwidget handling ------------------------------------------
# -------------------------------------------------------------------------
@Slot()
def create_window(self):
"""
Expand Down Expand Up @@ -814,6 +814,7 @@ def close_window(self, save_undocked=False):

if self.dockwidget is not None:
self.sig_update_ancestor_requested.emit()
self.get_plugin().switch_to_plugin()
self.dockwidget.setWidget(self)
self.dockwidget.setVisible(True)
self.dockwidget.raise_()
Expand Down Expand Up @@ -924,6 +925,24 @@ def lock_unlock_position(self):
else:
self.dockwidget.set_title_bar()

def get_maximized_state(self):
"""Get dockwidget's maximized state."""
return self._is_maximized

def set_maximized_state(self, state):
"""
Set internal attribute that holds dockwidget's maximized state.

Parameters
----------
state: bool
True if the plugin is maximized, False otherwise.
"""
self._is_maximized = state

# This is necessary for old API plugins interacting with new ones.
self._plugin._ismaximized = state

# --- API: methods to define or override
# ------------------------------------------------------------------------
def get_title(self):
Expand Down Expand Up @@ -963,6 +982,7 @@ def on_close(self):
"""
pass


def run_test():
# Third party imports
from qtpy.QtWidgets import QHBoxLayout, QTableWidget, QMainWindow
Expand Down
73 changes: 20 additions & 53 deletions spyder/app/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,13 +213,16 @@ def register_plugin(self, plugin_name, external=False, omit_conf=False):
self.show_plugin_compatibility_message(message)
return

# Connect Plugin Signals to main window methods
# Connect plugin signals to main window methods
plugin.sig_exception_occurred.connect(self.handle_exception)
plugin.sig_free_memory_requested.connect(self.free_memory)
plugin.sig_quit_requested.connect(self.close)
plugin.sig_redirect_stdio_requested.connect(
self.redirect_internalshell_stdio)
plugin.sig_status_message_requested.connect(self.show_status_message)
plugin.sig_unmaximize_plugin_requested.connect(self.unmaximize_plugin)
plugin.sig_unmaximize_plugin_requested[object].connect(
self.unmaximize_plugin)

if isinstance(plugin, SpyderDockablePlugin):
plugin.sig_focus_changed.connect(self.plugin_focus_changed)
Expand Down Expand Up @@ -345,68 +348,32 @@ def create_plugin_conf_widget(self, plugin):
conf_widget.initialize()
return conf_widget

@property
def last_plugin(self):
"""
Get last plugin with focus if it is a dockable widget.

If a non-dockable plugin has the focus this will return by default
the Editor plugin.
"""
# Needed to prevent errors with the old API at
# spyder/plugins/base::_switch_to_plugin
return self.layouts.get_last_plugin()

def maximize_dockwidget(self, restore=False):
"""
This is needed to prevent errors with the old API at
spyder/plugins/base::_switch_to_plugin.

See spyder-ide/spyder#15164

Parameters
----------
restore : bool, optional
If the current dockwidget needs to be restored to its unmaximized
state. The default is False.
"""
self.layouts.maximize_dockwidget(restore=restore)

def switch_to_plugin(self, plugin, force_focus=None):
"""
Switch to this plugin.
Switch to `plugin`.

Notes
-----
This operation unmaximizes the current plugin (if any), raises
this plugin to view (if it's hidden) and gives it focus (if
possible).
"""
last_plugin = self.last_plugin
try:
# New API
if (last_plugin is not None
and last_plugin.get_widget().is_maximized
and last_plugin is not plugin):
self.layouts.maximize_dockwidget()
except AttributeError:
# Old API
if (last_plugin is not None and self.last_plugin._ismaximized
and last_plugin is not plugin):
self.layouts.maximize_dockwidget()
self.layouts.switch_to_plugin(plugin, force_focus=force_focus)

try:
# New API
if not plugin.toggle_view_action.isChecked():
plugin.toggle_view_action.setChecked(True)
plugin.get_widget().is_visible = False
except AttributeError:
# Old API
if not plugin._toggle_view_action.isChecked():
plugin._toggle_view_action.setChecked(True)
plugin._widget._is_visible = False
def unmaximize_plugin(self, not_this_plugin=None):
"""
Unmaximize currently maximized plugin, if any.

plugin.change_visibility(True, force_focus=force_focus)
Parameters
----------
not_this_plugin: SpyderDockablePlugin, optional
Unmaximize plugin if the maximized one is `not_this_plugin`.
"""
if not_this_plugin is None:
self.layouts.unmaximize_dockwidget()
else:
self.layouts.unmaximize_other_dockwidget(
plugin_instance=not_this_plugin)

def remove_dockwidget(self, plugin):
"""
Expand Down Expand Up @@ -1219,7 +1186,7 @@ def restore_undocked_plugins(self):
plugin = PLUGIN_REGISTRY.get_plugin(plugin_name)
if isinstance(plugin, SpyderDockablePlugin):
if plugin.get_conf('undocked_on_window_close', default=False):
plugin.get_widget().create_window()
plugin.create_window()
elif isinstance(plugin, SpyderPluginWidget):
if plugin.get_option('undocked_on_window_close',
default=False):
Expand Down
Loading