From 4115dd8b06b47090cd49fe6f1989f05881522745 Mon Sep 17 00:00:00 2001 From: Jorge Moraleda Date: Wed, 29 Nov 2023 18:37:07 -0500 Subject: [PATCH] Improve support when specifying a pre-existing toolbar as the target for the restore icon when minimizing a pane in agw.aui --- wx/lib/agw/aui/auibar.py | 40 ++++++++++++------ wx/lib/agw/aui/framemanager.py | 76 ++++++++++++++++++++++++++++------ 2 files changed, 91 insertions(+), 25 deletions(-) diff --git a/wx/lib/agw/aui/auibar.py b/wx/lib/agw/aui/auibar.py index ac2ac9167..27eecde4c 100644 --- a/wx/lib/agw/aui/auibar.py +++ b/wx/lib/agw/aui/auibar.py @@ -271,8 +271,9 @@ class AuiToolBarItem(object): """ AuiToolBarItem is a toolbar element. - It has a unique id (except for the separators which always have id = -1), the - style (telling whether it is a normal button, separator or a control), the + It has a unique id (except for the separators which always have id = -1 and the + automatically added restore-from-minimize which always have id = ID_RESTORE_FRAME), + the style (telling whether it is a normal button, separator or a control), the state (toggled or not, enabled or not) and short and long help strings. The default implementations use the short help string for the tooltip text which is popped up when the mouse pointer enters the tool and the long help string @@ -2035,26 +2036,26 @@ def ClearTools(self): self.Clear() - def DeleteTool(self, tool_id): + def DeleteTool(self, tool): """ Removes the specified tool from the toolbar and deletes it. - :param integer `tool_id`: the :class:`AuiToolBarItem` identifier. + :param `tool`: the :class:`AuiToolBarItem` or its integer identifier. :returns: ``True`` if the tool was deleted, ``False`` otherwise. :note: Note that it is unnecessary to call :meth:`Realize` for the change to take place, it will happen immediately. """ - - idx = self.GetToolIndex(tool_id) - - if idx >= 0 and idx < len(self._items): - self._items.pop(idx) - self.Realize() - return True - - return False + if isinstance(tool, AuiToolBarItem): + try: + self._items.remove(tool) + self.Realize() + return True + except ValueError: + return False + # Assume tool is the id of the tool to be removed + return self.DeleteToolByPos(self.GetToolIndex(tool)) def DeleteToolByPos(self, pos): @@ -2114,6 +2115,19 @@ def FindToolByLabel(self, label): return None + def FindToolByUserData(self, userData): + """ + Finds a tool for the given client id. + + :param PyObject `userData`: the user data object. + """ + + for item in self._items: + if item.user_data == userData: + return item + + return None + def FindToolForPosition(self, x, y): """ Finds a tool for the given mouse position. diff --git a/wx/lib/agw/aui/framemanager.py b/wx/lib/agw/aui/framemanager.py index 7cd5846ec..8788f8d8b 100644 --- a/wx/lib/agw/aui/framemanager.py +++ b/wx/lib/agw/aui/framemanager.py @@ -4778,6 +4778,11 @@ def DetachPane(self, window): id = notebook.GetPageIndex(p.window) notebook.RemovePage(id) p.window.Reparent(self._frame) + + elif p.IsMinimized(): + # if we were minimized, restore so any restore icons in toolbars are removed + self.RestoreMinimizedPane(p) + # make sure there are no references to this pane in our uiparts, # just in case the caller doesn't call Update() immediately after @@ -4807,6 +4812,11 @@ def ClosePane(self, pane_info): # if we were maximized, restore if pane_info.IsMaximized(): self.RestorePane(pane_info) + + # if we were minimized, restore so any restore icons in toolbars are removed + if pane_info.IsMinimized(): + self.RestoreMinimizedPane(pane_info) + if pane_info.frame: if self._agwFlags & AUI_MGR_ANIMATE_FRAMES: @@ -5072,6 +5082,9 @@ def SavePaneInfo(self, pane): result = "name=" + EscapeDelimiters(pane.name) + ";" result += "caption=" + EscapeDelimiters(pane.caption) + ";" + if pane.minimize_target: + result += "minitarget=" + EscapeDelimiters(pane.minimize_target) + ";" + result += "minimode=%u;" % pane.minimize_mode result += "state=%u;" % pane.state result += "dir=%d;" % pane.dock_direction @@ -5119,6 +5132,10 @@ def LoadPaneInfo(self, pane_part, pane): pane.name = value elif val_name == "caption": pane.caption = value + elif val_name == "minitarget": + pane.minimize_target = value + elif val_name == "minimode": + pane.minimize_mode = int(value) elif val_name == "state": pane.state = int(value) elif val_name == "dir": @@ -5195,7 +5212,8 @@ def SavePerspective(self): dock.size) return result - def LoadPerspective(self, layout, update=True, restorecaption=False): + def LoadPerspective(self, layout, update=True, restorecaption=False, + restoreminimize=False): """ Loads a layout which was saved with :meth:`SavePerspective`. @@ -5206,6 +5224,8 @@ def LoadPerspective(self, layout, update=True, restorecaption=False): :param bool `update`: whether to update immediately the window or not; :param bool `restorecaption`: ``False``, restore from persist storage, otherwise use the caption defined in code. + :param bool `restoreminimize`: ``False``, restore from persist storage, + otherwise use the minimize_toolbar and minimize_mode defined in code. """ input = layout @@ -5223,6 +5243,7 @@ def LoadPerspective(self, layout, update=True, restorecaption=False): # mark all panes currently managed as docked and hidden saveCapt = {} # see restorecaption param saveIcon = {} # icons are not preserved by perspectives, so preserve them + saveMinimize = {} # see restoreminimize param for pane in self._panes: # dock the notebook pages @@ -5238,6 +5259,7 @@ def LoadPerspective(self, layout, update=True, restorecaption=False): pane.Dock().Hide() saveCapt[pane.name] = pane.caption saveIcon[pane.name] = pane.icon + saveMinimize[pane.name] = pane.minimize_target, pane.minimize_mode # clear out the dock array; this will be reconstructed self._docks = [] @@ -5298,6 +5320,10 @@ def LoadPerspective(self, layout, update=True, restorecaption=False): # restore icons from code. This is always done. Since icons are not saved in perspectives if pane.name in saveIcon: pane.icon = saveIcon[pane.name] + # restore minimize parameters from code + if restoreminimize: + if pane.name in saveMinimize: + pane.minimize_target, pane.minimize_mode = saveMinimize[pane.name] if not p.IsOk(): if pane.IsNotebookControl(): @@ -5319,10 +5345,29 @@ def LoadPerspective(self, layout, update=True, restorecaption=False): if isinstance(pane.window, auibar.AuiToolBar) and (pane.IsFloatable() or pane.IsDockable()): pane.window.SetGripperVisible(True) - for p in self._panes: - if p.IsMinimized(): - self.MinimizePane(p, False) - + inexistentPaneIndexes = [] + for paneIndex, p in enumerate(self._panes): + if p.window: + if p.IsMinimized(): + self.MinimizePane(p, False) + elif p.minimize_mode & AUI_MINIMIZE_POS_MASK == AUI_MINIMIZE_POS_TOOLBAR: + # If the pane specifies a minimize toolbar, which would be preserved by the loading + # operation rather than using an automatic toolbar which would not be preserved) then + # if pane was minimized before we loaded perspective but the new perspective unminimized + # it then its minimize-from-restore icon is still in the toolbar and must be removed + minimizeToolbar = self.GetPane(p.minimize_target).window + item = minimizeToolbar.FindToolByUserData((ID_RESTORE_FRAME, p.window)) + if item: # Delete the icon if it was there + minimizeToolbar.DeleteTool(item) + else: + # The perspective is referencing a non-existing pane that was added nonetheless. This will + # happen with a notebook that ends up containing no pages + inexistentPaneIndexes.append(paneIndex) + + for paneIndex in reversed(inexistentPaneIndexes): # reverse is required when deleting elements by index + del self._panes[index] + + if update: self.Update() @@ -9863,13 +9908,20 @@ def MinimizePane(self, paneInfo, mgrUpdate=True): restore_bitmap = img.ConvertToBitmap() target = None + restoreToolAlreadyInToolbar = False if posMask == AUI_MINIMIZE_POS_TOOLBAR: target = paneInfo.name - - minimize_toolbar.AddSimpleTool(ID_RESTORE_FRAME, paneInfo.caption, restore_bitmap, - _(six.u("Restore %s")) % paneInfo.caption, target=target) - minimize_toolbar.SetAuiManager(self) - minimize_toolbar.Realize() + if paneInfo.IsMinimized() and minimize_toolbar.FindToolByUserData((ID_RESTORE_FRAME, paneInfo.window)): + # We are loading a perspective. If the perspective contains the minimize_toolbar, + # then the restore tool for this pane is already in it and we should not add it again + restoreToolAlreadyInToolbar = True + + if not restoreToolAlreadyInToolbar: + tool = minimize_toolbar.AddSimpleTool(ID_RESTORE_FRAME, paneInfo.caption, restore_bitmap, + _(six.u("Restore %s")) % paneInfo.caption, target=target) + tool.SetUserData((ID_RESTORE_FRAME, paneInfo.window)) + minimize_toolbar.SetAuiManager(self) + minimize_toolbar.Realize() toolpanelname = paneInfo.name + "_min" if paneInfo.IsMaximized(): @@ -10031,8 +10083,8 @@ def RestoreMinimizedPane(self, paneInfo): targetName = pane.minimize_target toolbarPane = self.GetPane(targetName) toolbar = toolbarPane.window - item = toolbar.FindToolByLabel(pane.caption) - toolbar.DeleteTool(item.id) + item = toolbar.FindToolByUserData((ID_RESTORE_FRAME, pane.window)) + toolbar.DeleteTool(item) else: paneInfo.window.Show(False) self.DetachPane(paneInfo.window)