Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
89 changes: 85 additions & 4 deletions src/sas/qtgui/MainWindow/GuiManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ def __init__(self, parent=None):
# Currently displayed perspective
self._current_perspective: Perspective | None = None
self.loadedPerspectives: dict[str, Perspective] = {}
self._connected_undo_stack = None
self._connected_tabbed_perspective = None

# Populate the main window with stuff
self.addWidgets()
Expand Down Expand Up @@ -403,6 +405,8 @@ def perspectiveChanged(self, new_perspective_name: str):
Respond to change of the perspective signal
"""

self._disconnect_undo_redo_hooks()

if new_perspective_name not in self.loadedPerspectives:
keylist = ', '.join(self.loadedPerspectives.keys())
raise KeyError(
Expand Down Expand Up @@ -493,6 +497,7 @@ def perspectiveChanged(self, new_perspective_name: str):
# Set the current perspective to new one and show
self._current_perspective = new_perspective
self._current_perspective.show()
self._connect_undo_redo_hooks()

def updatePerspective(self, data):
"""
Expand Down Expand Up @@ -683,15 +688,16 @@ def addCallbacks(self):
self.communicate.plotFromNameSignal.connect(self.showPlotFromName)
self.communicate.updateModelFromDataOperationPanelSignal.connect(self.updateModelFromDataOperationPanel)
self.communicate.activeGraphsSignal.connect(self.updatePlotItems)
self.communicate.undoRedoUpdateSignal.connect(self._update_undo_redo_actions)


def addTriggers(self):
"""
Trigger definitions for all menu/toolbar actions.
"""
# disable not yet fully implemented actions
self._workspace.actionUndo.setVisible(False)
self._workspace.actionRedo.setVisible(False)
self._workspace.actionUndo.setEnabled(False)
self._workspace.actionRedo.setEnabled(False)
self._workspace.actionReset.setVisible(False)
self._workspace.actionStartup_Settings.setVisible(False)
#self._workspace.actionImage_Viewer.setVisible(False)
Expand Down Expand Up @@ -889,12 +895,87 @@ def actionQuit(self):
def actionUndo(self):
"""
"""
print("actionUndo TRIGGERED")
stack = self._active_undo_stack()
if stack is not None:
stack.undo()

def actionRedo(self):
"""
"""
print("actionRedo TRIGGERED")
stack = self._active_undo_stack()
if stack is not None:
stack.redo()

def _active_undo_stack(self):
"""Return the undo stack for the active perspective, if available."""
if self._current_perspective is None:
return None
return getattr(self._current_perspective, "undo_stack", None)

def _disconnect_undo_redo_hooks(self):
"""Disconnect temporary undo/redo signal hooks."""
if self._connected_undo_stack is not None:
try:
self._connected_undo_stack.stackChanged.disconnect(
self._update_undo_redo_actions
)
except (RuntimeError, TypeError):
pass
self._connected_undo_stack = None

if self._connected_tabbed_perspective is not None:
try:
self._connected_tabbed_perspective.currentChanged.disconnect(
self._on_perspective_tab_changed
)
except (RuntimeError, TypeError):
pass
self._connected_tabbed_perspective = None

def _connect_undo_redo_hooks(self):
"""Connect action refresh hooks for active perspective and stack."""
perspective = self._current_perspective
if perspective is None:
self._update_undo_redo_actions()
return

if hasattr(perspective, "currentChanged"):
try:
perspective.currentChanged.connect(self._on_perspective_tab_changed)
self._connected_tabbed_perspective = perspective
except (RuntimeError, TypeError):
self._connected_tabbed_perspective = None

stack = self._active_undo_stack()
if stack is not None and hasattr(stack, "stackChanged"):
try:
stack.stackChanged.connect(self._update_undo_redo_actions)
self._connected_undo_stack = stack
except (RuntimeError, TypeError):
self._connected_undo_stack = None

self._update_undo_redo_actions()

def _on_perspective_tab_changed(self, *_):
"""Rewire undo hooks when active tab changes (e.g., fitting tabs)."""
self._disconnect_undo_redo_hooks()
self._connect_undo_redo_hooks()

def _update_undo_redo_actions(self):
"""Refresh undo/redo enabled state and action tooltips."""
stack = self._active_undo_stack()

if stack is None:
self._workspace.actionUndo.setEnabled(False)
self._workspace.actionRedo.setEnabled(False)
self._workspace.actionUndo.setToolTip("Undo")
self._workspace.actionRedo.setToolTip("Redo")
return

self._workspace.actionUndo.setEnabled(stack.can_undo())
self._workspace.actionRedo.setEnabled(stack.can_redo())
self._workspace.actionUndo.setToolTip(stack.undo_text())
self._workspace.actionRedo.setToolTip(stack.redo_text())

def actionCopy(self):
"""
Expand Down
9 changes: 9 additions & 0 deletions src/sas/qtgui/MainWindow/UI/MainWindowUI.ui
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,12 @@
<property name="text">
<string>Undo</string>
</property>
<property name="toolTip">
<string>Undo</string>
</property>
<property name="shortcut">
<string>Ctrl+Z</string>
</property>
</action>
<action name="actionRedo">
<property name="icon">
Expand All @@ -272,6 +278,9 @@
<property name="toolTip">
<string>Redo</string>
</property>
<property name="shortcut">
<string>Ctrl+Y</string>
</property>
</action>
<action name="actionCopy">
<property name="icon">
Expand Down
6 changes: 6 additions & 0 deletions src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,12 @@ def currentFittingWidget(self) -> FittingWidget | None:
else:
return None

@property
def undo_stack(self):
"""Return undo stack for the currently selected fitting tab."""
fitting_widget = self.currentFittingWidget
return None if fitting_widget is None else fitting_widget.undo_stack

def getFitTabs(self):
"""
Returns the list of fitting tabs
Expand Down
Loading
Loading