diff --git a/nxt_editor/dockwidgets/dock_widget_base.py b/nxt_editor/dockwidgets/dock_widget_base.py index 7940da7..6b35a55 100644 --- a/nxt_editor/dockwidgets/dock_widget_base.py +++ b/nxt_editor/dockwidgets/dock_widget_base.py @@ -56,7 +56,6 @@ def __init__(self, title, parent=None, graph_model=None, # set graph model self.stage_model = graph_model self.model_signal_connections = [] - self.topLevelChanged.connect(self.on_window_status_changed) def set_stage_model(self, stage_model): """Sets the stage model for docwidgets to use, also calls the @@ -69,17 +68,6 @@ def set_stage_model(self, stage_model): if self.stage_model: self.set_stage_model_connections(self.stage_model, True) - def on_window_status_changed(self, is_window): - if is_window: - flags = ( - QtCore.Qt.Window | - QtCore.Qt.CustomizeWindowHint | - QtCore.Qt.WindowMinMaxButtonsHint | - QtCore.Qt.WindowCloseButtonHint - ) - self.setWindowFlags(flags) - self.show() - def set_stage_model_connections(self, model, connect): """Connect and disconnect signals from the docwidget. Called by `set_graph_model`. diff --git a/nxt_editor/main_window.py b/nxt_editor/main_window.py index d3aead7..240c2c2 100644 --- a/nxt_editor/main_window.py +++ b/nxt_editor/main_window.py @@ -218,6 +218,14 @@ def __init__(self, filepath=None, parent=None, start_rpc=False): QtCore.Qt.LeftDockWidgetArea) self.setTabPosition(QtCore.Qt.AllDockWidgetAreas, QtWidgets.QTabWidget.North) + # Snapshot the factory dock/toolbar layout before any saved state is + # restored, so reset_layout can always return to it + self.default_layout_state = self.saveState() + # Qt creates dock tab bars lazily when docks get tabified, so watch + # for them to support double clicking a tab to float its dock + for dock in self.findChildren(DockWidgetBase): + dock.dockLocationChanged.connect(self.install_dock_tab_filters) + dock.topLevelChanged.connect(self.install_dock_tab_filters) # status bar self.status_bar = QtWidgets.QStatusBar() @@ -849,16 +857,53 @@ def eventFilter(self, widget, event): if isinstance(widget.parent(), NxtCodeEditor): self.code_editor.update_code_is_local() self.code_editor.enter_editing() + elif isinstance(widget, QtWidgets.QTabBar): + return self.float_dock_from_tab(widget, event.pos()) return False + def float_dock_from_tab(self, tab_bar, pos): + """Float the dock widget whose tab sits at `pos` in `tab_bar`. + :param tab_bar: QTabBar of a dock area owned by this window + :param pos: QPoint click position local to the tab bar + :return: True if a dock was floated + """ + index = tab_bar.tabAt(pos) + if index == -1: + return False + title = tab_bar.tabText(index) + for dock in self.findChildren(DockWidgetBase): + if dock.windowTitle() == title: + dock.setFloating(True) + dock.raise_() + return True + return False + + def install_dock_tab_filters(self, *args): + """Defer a tick so Qt has (re)built its internal dock tab bars.""" + QtCore.QTimer.singleShot(0, self.scan_dock_tab_bars) + + def scan_dock_tab_bars(self): + """Filter the dock area tab bars so tab double clicks reach + eventFilter. Qt creates them lazily when docks get tabified. + """ + for tab_bar in self.findChildren(QtWidgets.QTabBar): + if tab_bar.parent() is self: + tab_bar.installEventFilter(self) + def show(self): """Centering after the window is shown because the center is based on the window's size.""" # Todo: add previous rect to the bookmarks data for the layer - use this instead of center if it exists super(MainWindow, self).show() self.center_view() + def reset_layout(self): + """Restore all dock widgets and toolbars to the default layout.""" + self.restoreState(self.default_layout_state) + self.state_last_hidden = None + def showEvent(self, event): + self.install_dock_tab_filters() if self.state_last_hidden: self.restoreState(self.state_last_hidden) super(MainWindow, self).showEvent(event) @@ -1173,6 +1218,10 @@ def __init__(self, parent=None): self.view_menu.addSeparator() self.view_menu.addAction(self.view_actions.implicit_action) self.view_menu.addAction(self.view_actions.grid_action) + self.view_menu.addSeparator() + self.reset_layout_action = self.view_menu.addAction('Reset Layout') + self.reset_layout_action.triggered.connect( + self.main_window.reset_layout) self.view_opt_menu = self.view_menu.addMenu('Options') self.view_opt_menu.setTearOffEnabled(True) self.view_opt_menu.addAction(self.view_actions.tooltip_action)