We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
The close button on dock widgets is gone, only hide is there
## 🐛 Bug
Not sure if it was intentional (maybe a miscommunication) or an oversight, but the x at the top left of dock widgets is gone.  I think we should go back to having an x icon (close the dock widget and destroy the instance).  a hide (eye) icon (hide the dock widget but don't destroy it).  and the icon that pops it out of the dock

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `napari/_qt/widgets/qt_viewer_dock_widget.py`
Content:
```
1 import warnings
2 from functools import reduce
3 from itertools import count
4 from operator import ior
5 from typing import TYPE_CHECKING, List, Optional
6 from weakref import ReferenceType, ref
7 
8 from qtpy.QtCore import Qt
9 from qtpy.QtWidgets import (
10     QDockWidget,
11     QFrame,
12     QHBoxLayout,
13     QLabel,
14     QPushButton,
15     QSizePolicy,
16     QVBoxLayout,
17     QWidget,
18 )
19 
20 from ...utils.translations import trans
21 from ..utils import combine_widgets, qt_signals_blocked
22 
23 if TYPE_CHECKING:
24     from ..qt_viewer import QtViewer
25 
26 counter = count()
27 _sentinel = object()
28 
29 _SHORTCUT_DEPRECATION_STRING = trans._(
30     'The shortcut parameter is deprecated since version 0.4.8, please use the action and shortcut manager APIs. The new action manager and shortcut API allow user configuration and localisation. (got {shortcut})',
31     shortcut="{shortcut}",
32 )
33 
34 
35 class QtViewerDockWidget(QDockWidget):
36     """Wrap a QWidget in a QDockWidget and forward viewer events
37 
38     Parameters
39     ----------
40     qt_viewer : QtViewer
41         The QtViewer instance that this dock widget will belong to.
42     widget : QWidget
43         `widget` that will be added as QDockWidget's main widget.
44     name : str
45         Name of dock widget.
46     area : str
47         Side of the main window to which the new dock widget will be added.
48         Must be in {'left', 'right', 'top', 'bottom'}
49     allowed_areas : list[str], optional
50         Areas, relative to main window, that the widget is allowed dock.
51         Each item in list must be in {'left', 'right', 'top', 'bottom'}
52         By default, all areas are allowed.
53     shortcut : str, optional
54         Keyboard shortcut to appear in dropdown menu.
55         .. deprecated:: 0.4.8
56 
57             The shortcut parameter is deprecated since version 0.4.8, please use
58             the action and shortcut manager APIs. The new action manager and
59             shortcut API allow user configuration and localisation.
60     add_vertical_stretch : bool, optional
61         Whether to add stretch to the bottom of vertical widgets (pushing
62         widgets up towards the top of the allotted area, instead of letting
63         them distribute across the vertical space).  By default, True.
64     """
65 
66     def __init__(
67         self,
68         qt_viewer,
69         widget: QWidget,
70         *,
71         name: str = '',
72         area: str = 'right',
73         allowed_areas: Optional[List[str]] = None,
74         shortcut=_sentinel,
75         object_name: str = '',
76         add_vertical_stretch=True,
77     ):
78         self._ref_qt_viewer: 'ReferenceType[QtViewer]' = ref(qt_viewer)
79         super().__init__(name)
80         self._parent = qt_viewer
81         self.name = name
82 
83         areas = {
84             'left': Qt.LeftDockWidgetArea,
85             'right': Qt.RightDockWidgetArea,
86             'top': Qt.TopDockWidgetArea,
87             'bottom': Qt.BottomDockWidgetArea,
88         }
89         if area not in areas:
90             raise ValueError(
91                 trans._(
92                     'area argument must be in {areas}',
93                     deferred=True,
94                     areas=list(areas.keys()),
95                 )
96             )
97         self.area = area
98         self.qt_area = areas[area]
99         if shortcut is not _sentinel:
100             warnings.warn(
101                 _SHORTCUT_DEPRECATION_STRING.format(shortcut=shortcut),
102                 FutureWarning,
103                 stacklevel=2,
104             )
105         else:
106             shortcut = None
107         self._shortcut = shortcut
108 
109         if allowed_areas:
110             if not isinstance(allowed_areas, (list, tuple)):
111                 raise TypeError(
112                     trans._(
113                         '`allowed_areas` must be a list or tuple',
114                         deferred=True,
115                     )
116                 )
117 
118             if any(area not in areas for area in allowed_areas):
119                 raise ValueError(
120                     trans._(
121                         'all allowed_areas argument must be in {areas}',
122                         deferred=True,
123                         areas=list(areas.keys()),
124                     )
125                 )
126             allowed_areas = reduce(ior, [areas[a] for a in allowed_areas])
127         else:
128             allowed_areas = Qt.AllDockWidgetAreas
129         self.setAllowedAreas(allowed_areas)
130         self.setMinimumHeight(50)
131         self.setMinimumWidth(50)
132         # FIXME:
133         self.setObjectName(object_name or name)
134 
135         is_vertical = area in {'left', 'right'}
136         widget = combine_widgets(widget, vertical=is_vertical)
137         self.setWidget(widget)
138         if is_vertical and add_vertical_stretch:
139             self._maybe_add_vertical_stretch(widget)
140 
141         self._features = self.features()
142         self.dockLocationChanged.connect(self._set_title_orientation)
143 
144         # custom title bar
145         self.title = QtCustomTitleBar(self, title=self.name)
146         self.setTitleBarWidget(self.title)
147         self.visibilityChanged.connect(self._on_visibility_changed)
148 
149     @property
150     def _parent(self):
151         """
152         Let's make sure parent always a weakref:
153 
154             1) parent is likely to always exists after child
155             2) even if not strictly necessary it make it easier to view reference cycles.
156         """
157         return self._ref_parent()
158 
159     @_parent.setter
160     def _parent(self, obj):
161         self._ref_parent = ref(obj)
162 
163     def destroyOnClose(self):
164         """Destroys dock plugin dock widget when 'x' is clicked."""
165         self._ref_qt_viewer().viewer.window.remove_dock_widget(self)
166 
167     def _maybe_add_vertical_stretch(self, widget):
168         """Add vertical stretch to the bottom of a vertical layout only
169 
170         ...if there is not already a widget that wants vertical space
171         (like a textedit or listwidget or something).
172         """
173         exempt_policies = {
174             QSizePolicy.Expanding,
175             QSizePolicy.MinimumExpanding,
176             QSizePolicy.Ignored,
177         }
178         if widget.sizePolicy().verticalPolicy() in exempt_policies:
179             return
180 
181         # not uncommon to see people shadow the builtin layout() method
182         # which breaks our ability to add vertical stretch...
183         try:
184             wlayout = widget.layout()
185             if wlayout is None:
186                 return
187         except TypeError:
188             return
189 
190         for i in range(wlayout.count()):
191             wdg = wlayout.itemAt(i).widget()
192             if (
193                 wdg is not None
194                 and wdg.sizePolicy().verticalPolicy() in exempt_policies
195             ):
196                 return
197 
198         # not all widgets have addStretch...
199         if hasattr(wlayout, 'addStretch'):
200             wlayout.addStretch(next(counter))
201 
202     @property
203     def shortcut(self):
204         warnings.warn(
205             _SHORTCUT_DEPRECATION_STRING,
206             FutureWarning,
207             stacklevel=2,
208         )
209         return self._shortcut
210 
211     def setFeatures(self, features):
212         super().setFeatures(features)
213         self._features = self.features()
214 
215     def keyPressEvent(self, event):
216         # if you subclass QtViewerDockWidget and override the keyPressEvent
217         # method, be sure to call super().keyPressEvent(event) at the end of
218         # your method to pass uncaught key-combinations to the viewer.
219         return self._ref_qt_viewer().keyPressEvent(event)
220 
221     def _set_title_orientation(self, area):
222         if area in (Qt.LeftDockWidgetArea, Qt.RightDockWidgetArea):
223             features = self._features
224             if features & self.DockWidgetVerticalTitleBar:
225                 features = features ^ self.DockWidgetVerticalTitleBar
226         else:
227             features = self._features | self.DockWidgetVerticalTitleBar
228         self.setFeatures(features)
229 
230     @property
231     def is_vertical(self):
232         if not self.isFloating():
233             par = self.parent()
234             if par and hasattr(par, 'dockWidgetArea'):
235                 return par.dockWidgetArea(self) in (
236                     Qt.LeftDockWidgetArea,
237                     Qt.RightDockWidgetArea,
238                 )
239         return self.size().height() > self.size().width()
240 
241     def _on_visibility_changed(self, visible):
242         try:
243             actions = [
244                 action.text()
245                 for action in self._ref_qt_viewer().viewer.window.plugins_menu.actions()
246             ]
247             idx = actions.index(self.name)
248 
249             current_action = (
250                 self._ref_qt_viewer().viewer.window.plugins_menu.actions()[idx]
251             )
252             current_action.setChecked(visible)
253             self.setVisible(visible)
254 
255         except (AttributeError, ValueError):
256             # AttributeError: This error happens when the plugins menu is not yet built.
257             # ValueError: This error is when the action is from the windows menu.
258             pass
259         if not visible:
260             return
261         with qt_signals_blocked(self):
262             self.setTitleBarWidget(None)
263             if not self.isFloating():
264                 self.title = QtCustomTitleBar(
265                     self, title=self.name, vertical=not self.is_vertical
266                 )
267                 self.setTitleBarWidget(self.title)
268 
269     def setWidget(self, widget):
270         widget._parent = self
271         super().setWidget(widget)
272 
273 
274 class QtCustomTitleBar(QLabel):
275     """A widget to be used as the titleBar in the QtViewerDockWidget.
276 
277     Keeps vertical size minimal, has a hand cursor and styles (in stylesheet)
278     for hover. Close and float buttons.
279 
280     Parameters
281     ----------
282     parent : QDockWidget
283         The QtViewerDockWidget to which this titlebar belongs
284     title : str
285         A string to put in the titlebar.
286     vertical : bool
287         Whether this titlebar is oriented vertically or not.
288     """
289 
290     def __init__(self, parent, title: str = '', vertical=False):
291         super().__init__(parent)
292         self.setObjectName("QtCustomTitleBar")
293         self.setProperty('vertical', str(vertical))
294         self.vertical = vertical
295         self.setToolTip(trans._('drag to move. double-click to float'))
296 
297         line = QFrame(self)
298         line.setObjectName("QtCustomTitleBarLine")
299 
300         add_close = False
301         try:
302             # if the plugins menu is already created, check to see if this is a plugin
303             # dock widget.  If it is, then add the close button option to the title bar.
304             actions = [
305                 action.text()
306                 for action in self.parent()._qt_viewer.viewer.window.plugins_menu.actions()
307             ]
308             if self.parent().name in actions:
309                 add_close = True
310                 self.close_button = QPushButton(self)
311                 self.close_button.setToolTip(trans._('close this panel'))
312                 self.close_button.setObjectName("QTitleBarCloseButton")
313                 self.close_button.setCursor(Qt.ArrowCursor)
314                 self.close_button.clicked.connect(
315                     lambda: self.parent().destroyOnClose()
316                 )
317             else:
318                 add_close = False
319         except AttributeError:
320             pass
321         self.hide_button = QPushButton(self)
322         self.hide_button.setToolTip(trans._('hide this panel'))
323         self.hide_button.setObjectName("QTitleBarHideButton")
324         self.hide_button.setCursor(Qt.ArrowCursor)
325         self.hide_button.clicked.connect(lambda: self.parent().close())
326 
327         self.float_button = QPushButton(self)
328         self.float_button.setToolTip(trans._('float this panel'))
329         self.float_button.setObjectName("QTitleBarFloatButton")
330         self.float_button.setCursor(Qt.ArrowCursor)
331         self.float_button.clicked.connect(
332             lambda: self.parent().setFloating(not self.parent().isFloating())
333         )
334         self.title = QLabel(title, self)
335         self.title.setSizePolicy(
336             QSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Maximum)
337         )
338 
339         if vertical:
340             layout = QVBoxLayout()
341             layout.setSpacing(4)
342             layout.setContentsMargins(0, 8, 0, 8)
343             line.setFixedWidth(1)
344             if add_close:
345                 layout.addWidget(self.close_button, 0, Qt.AlignHCenter)
346             layout.addWidget(self.hide_button, 0, Qt.AlignHCenter)
347             layout.addWidget(self.float_button, 0, Qt.AlignHCenter)
348             layout.addWidget(line, 0, Qt.AlignHCenter)
349             self.title.hide()
350 
351         else:
352             layout = QHBoxLayout()
353             layout.setSpacing(4)
354             layout.setContentsMargins(8, 1, 8, 0)
355             line.setFixedHeight(1)
356             if add_close:
357                 layout.addWidget(self.close_button)
358 
359             layout.addWidget(self.hide_button)
360             layout.addWidget(self.float_button)
361             layout.addWidget(line)
362             layout.addWidget(self.title)
363 
364         self.setLayout(layout)
365         self.setCursor(Qt.OpenHandCursor)
366 
367     def sizeHint(self):
368         # this seems to be the correct way to set the height of the titlebar
369         szh = super().sizeHint()
370         if self.vertical:
371             szh.setWidth(20)
372         else:
373             szh.setHeight(20)
374         return szh
375
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/napari/_qt/widgets/qt_viewer_dock_widget.py b/napari/_qt/widgets/qt_viewer_dock_widget.py
--- a/napari/_qt/widgets/qt_viewer_dock_widget.py
+++ b/napari/_qt/widgets/qt_viewer_dock_widget.py
@@ -301,10 +301,9 @@
         try:
             # if the plugins menu is already created, check to see if this is a plugin
             # dock widget.  If it is, then add the close button option to the title bar.
-            actions = [
-                action.text()
-                for action in self.parent()._qt_viewer.viewer.window.plugins_menu.actions()
-            ]
+            win = self.parent().parent()._qt_viewer.viewer.window
+            actions = [action.text() for action in win.plugins_menu.actions()]
+
             if self.parent().name in actions:
                 add_close = True
                 self.close_button = QPushButton(self)

{"golden_diff": "diff --git a/napari/_qt/widgets/qt_viewer_dock_widget.py b/napari/_qt/widgets/qt_viewer_dock_widget.py\n--- a/napari/_qt/widgets/qt_viewer_dock_widget.py\n+++ b/napari/_qt/widgets/qt_viewer_dock_widget.py\n@@ -301,10 +301,9 @@\n         try:\n             # if the plugins menu is already created, check to see if this is a plugin\n             # dock widget.  If it is, then add the close button option to the title bar.\n-            actions = [\n-                action.text()\n-                for action in self.parent()._qt_viewer.viewer.window.plugins_menu.actions()\n-            ]\n+            win = self.parent().parent()._qt_viewer.viewer.window\n+            actions = [action.text() for action in win.plugins_menu.actions()]\n+\n             if self.parent().name in actions:\n                 add_close = True\n                 self.close_button = QPushButton(self)\n", "issue": "The close button on dock widgets is gone, only hide is there\n## \ud83d\udc1b Bug\r\nNot sure if it was intentional (maybe a miscommunication) or an oversight, but the x at the top left of dock widgets is gone.  I think we should go back to having an x icon (close the dock widget and destroy the instance).  a hide (eye) icon (hide the dock widget but don't destroy it).  and the icon that pops it out of the dock\n", "before_files": [{"content": "import warnings\nfrom functools import reduce\nfrom itertools import count\nfrom operator import ior\nfrom typing import TYPE_CHECKING, List, Optional\nfrom weakref import ReferenceType, ref\n\nfrom qtpy.QtCore import Qt\nfrom qtpy.QtWidgets import (\n    QDockWidget,\n    QFrame,\n    QHBoxLayout,\n    QLabel,\n    QPushButton,\n    QSizePolicy,\n    QVBoxLayout,\n    QWidget,\n)\n\nfrom ...utils.translations import trans\nfrom ..utils import combine_widgets, qt_signals_blocked\n\nif TYPE_CHECKING:\n    from ..qt_viewer import QtViewer\n\ncounter = count()\n_sentinel = object()\n\n_SHORTCUT_DEPRECATION_STRING = trans._(\n    'The shortcut parameter is deprecated since version 0.4.8, please use the action and shortcut manager APIs. The new action manager and shortcut API allow user configuration and localisation. (got {shortcut})',\n    shortcut=\"{shortcut}\",\n)\n\n\nclass QtViewerDockWidget(QDockWidget):\n    \"\"\"Wrap a QWidget in a QDockWidget and forward viewer events\n\n    Parameters\n    ----------\n    qt_viewer : QtViewer\n        The QtViewer instance that this dock widget will belong to.\n    widget : QWidget\n        `widget` that will be added as QDockWidget's main widget.\n    name : str\n        Name of dock widget.\n    area : str\n        Side of the main window to which the new dock widget will be added.\n        Must be in {'left', 'right', 'top', 'bottom'}\n    allowed_areas : list[str], optional\n        Areas, relative to main window, that the widget is allowed dock.\n        Each item in list must be in {'left', 'right', 'top', 'bottom'}\n        By default, all areas are allowed.\n    shortcut : str, optional\n        Keyboard shortcut to appear in dropdown menu.\n        .. deprecated:: 0.4.8\n\n            The shortcut parameter is deprecated since version 0.4.8, please use\n            the action and shortcut manager APIs. The new action manager and\n            shortcut API allow user configuration and localisation.\n    add_vertical_stretch : bool, optional\n        Whether to add stretch to the bottom of vertical widgets (pushing\n        widgets up towards the top of the allotted area, instead of letting\n        them distribute across the vertical space).  By default, True.\n    \"\"\"\n\n    def __init__(\n        self,\n        qt_viewer,\n        widget: QWidget,\n        *,\n        name: str = '',\n        area: str = 'right',\n        allowed_areas: Optional[List[str]] = None,\n        shortcut=_sentinel,\n        object_name: str = '',\n        add_vertical_stretch=True,\n    ):\n        self._ref_qt_viewer: 'ReferenceType[QtViewer]' = ref(qt_viewer)\n        super().__init__(name)\n        self._parent = qt_viewer\n        self.name = name\n\n        areas = {\n            'left': Qt.LeftDockWidgetArea,\n            'right': Qt.RightDockWidgetArea,\n            'top': Qt.TopDockWidgetArea,\n            'bottom': Qt.BottomDockWidgetArea,\n        }\n        if area not in areas:\n            raise ValueError(\n                trans._(\n                    'area argument must be in {areas}',\n                    deferred=True,\n                    areas=list(areas.keys()),\n                )\n            )\n        self.area = area\n        self.qt_area = areas[area]\n        if shortcut is not _sentinel:\n            warnings.warn(\n                _SHORTCUT_DEPRECATION_STRING.format(shortcut=shortcut),\n                FutureWarning,\n                stacklevel=2,\n            )\n        else:\n            shortcut = None\n        self._shortcut = shortcut\n\n        if allowed_areas:\n            if not isinstance(allowed_areas, (list, tuple)):\n                raise TypeError(\n                    trans._(\n                        '`allowed_areas` must be a list or tuple',\n                        deferred=True,\n                    )\n                )\n\n            if any(area not in areas for area in allowed_areas):\n                raise ValueError(\n                    trans._(\n                        'all allowed_areas argument must be in {areas}',\n                        deferred=True,\n                        areas=list(areas.keys()),\n                    )\n                )\n            allowed_areas = reduce(ior, [areas[a] for a in allowed_areas])\n        else:\n            allowed_areas = Qt.AllDockWidgetAreas\n        self.setAllowedAreas(allowed_areas)\n        self.setMinimumHeight(50)\n        self.setMinimumWidth(50)\n        # FIXME:\n        self.setObjectName(object_name or name)\n\n        is_vertical = area in {'left', 'right'}\n        widget = combine_widgets(widget, vertical=is_vertical)\n        self.setWidget(widget)\n        if is_vertical and add_vertical_stretch:\n            self._maybe_add_vertical_stretch(widget)\n\n        self._features = self.features()\n        self.dockLocationChanged.connect(self._set_title_orientation)\n\n        # custom title bar\n        self.title = QtCustomTitleBar(self, title=self.name)\n        self.setTitleBarWidget(self.title)\n        self.visibilityChanged.connect(self._on_visibility_changed)\n\n    @property\n    def _parent(self):\n        \"\"\"\n        Let's make sure parent always a weakref:\n\n            1) parent is likely to always exists after child\n            2) even if not strictly necessary it make it easier to view reference cycles.\n        \"\"\"\n        return self._ref_parent()\n\n    @_parent.setter\n    def _parent(self, obj):\n        self._ref_parent = ref(obj)\n\n    def destroyOnClose(self):\n        \"\"\"Destroys dock plugin dock widget when 'x' is clicked.\"\"\"\n        self._ref_qt_viewer().viewer.window.remove_dock_widget(self)\n\n    def _maybe_add_vertical_stretch(self, widget):\n        \"\"\"Add vertical stretch to the bottom of a vertical layout only\n\n        ...if there is not already a widget that wants vertical space\n        (like a textedit or listwidget or something).\n        \"\"\"\n        exempt_policies = {\n            QSizePolicy.Expanding,\n            QSizePolicy.MinimumExpanding,\n            QSizePolicy.Ignored,\n        }\n        if widget.sizePolicy().verticalPolicy() in exempt_policies:\n            return\n\n        # not uncommon to see people shadow the builtin layout() method\n        # which breaks our ability to add vertical stretch...\n        try:\n            wlayout = widget.layout()\n            if wlayout is None:\n                return\n        except TypeError:\n            return\n\n        for i in range(wlayout.count()):\n            wdg = wlayout.itemAt(i).widget()\n            if (\n                wdg is not None\n                and wdg.sizePolicy().verticalPolicy() in exempt_policies\n            ):\n                return\n\n        # not all widgets have addStretch...\n        if hasattr(wlayout, 'addStretch'):\n            wlayout.addStretch(next(counter))\n\n    @property\n    def shortcut(self):\n        warnings.warn(\n            _SHORTCUT_DEPRECATION_STRING,\n            FutureWarning,\n            stacklevel=2,\n        )\n        return self._shortcut\n\n    def setFeatures(self, features):\n        super().setFeatures(features)\n        self._features = self.features()\n\n    def keyPressEvent(self, event):\n        # if you subclass QtViewerDockWidget and override the keyPressEvent\n        # method, be sure to call super().keyPressEvent(event) at the end of\n        # your method to pass uncaught key-combinations to the viewer.\n        return self._ref_qt_viewer().keyPressEvent(event)\n\n    def _set_title_orientation(self, area):\n        if area in (Qt.LeftDockWidgetArea, Qt.RightDockWidgetArea):\n            features = self._features\n            if features & self.DockWidgetVerticalTitleBar:\n                features = features ^ self.DockWidgetVerticalTitleBar\n        else:\n            features = self._features | self.DockWidgetVerticalTitleBar\n        self.setFeatures(features)\n\n    @property\n    def is_vertical(self):\n        if not self.isFloating():\n            par = self.parent()\n            if par and hasattr(par, 'dockWidgetArea'):\n                return par.dockWidgetArea(self) in (\n                    Qt.LeftDockWidgetArea,\n                    Qt.RightDockWidgetArea,\n                )\n        return self.size().height() > self.size().width()\n\n    def _on_visibility_changed(self, visible):\n        try:\n            actions = [\n                action.text()\n                for action in self._ref_qt_viewer().viewer.window.plugins_menu.actions()\n            ]\n            idx = actions.index(self.name)\n\n            current_action = (\n                self._ref_qt_viewer().viewer.window.plugins_menu.actions()[idx]\n            )\n            current_action.setChecked(visible)\n            self.setVisible(visible)\n\n        except (AttributeError, ValueError):\n            # AttributeError: This error happens when the plugins menu is not yet built.\n            # ValueError: This error is when the action is from the windows menu.\n            pass\n        if not visible:\n            return\n        with qt_signals_blocked(self):\n            self.setTitleBarWidget(None)\n            if not self.isFloating():\n                self.title = QtCustomTitleBar(\n                    self, title=self.name, vertical=not self.is_vertical\n                )\n                self.setTitleBarWidget(self.title)\n\n    def setWidget(self, widget):\n        widget._parent = self\n        super().setWidget(widget)\n\n\nclass QtCustomTitleBar(QLabel):\n    \"\"\"A widget to be used as the titleBar in the QtViewerDockWidget.\n\n    Keeps vertical size minimal, has a hand cursor and styles (in stylesheet)\n    for hover. Close and float buttons.\n\n    Parameters\n    ----------\n    parent : QDockWidget\n        The QtViewerDockWidget to which this titlebar belongs\n    title : str\n        A string to put in the titlebar.\n    vertical : bool\n        Whether this titlebar is oriented vertically or not.\n    \"\"\"\n\n    def __init__(self, parent, title: str = '', vertical=False):\n        super().__init__(parent)\n        self.setObjectName(\"QtCustomTitleBar\")\n        self.setProperty('vertical', str(vertical))\n        self.vertical = vertical\n        self.setToolTip(trans._('drag to move. double-click to float'))\n\n        line = QFrame(self)\n        line.setObjectName(\"QtCustomTitleBarLine\")\n\n        add_close = False\n        try:\n            # if the plugins menu is already created, check to see if this is a plugin\n            # dock widget.  If it is, then add the close button option to the title bar.\n            actions = [\n                action.text()\n                for action in self.parent()._qt_viewer.viewer.window.plugins_menu.actions()\n            ]\n            if self.parent().name in actions:\n                add_close = True\n                self.close_button = QPushButton(self)\n                self.close_button.setToolTip(trans._('close this panel'))\n                self.close_button.setObjectName(\"QTitleBarCloseButton\")\n                self.close_button.setCursor(Qt.ArrowCursor)\n                self.close_button.clicked.connect(\n                    lambda: self.parent().destroyOnClose()\n                )\n            else:\n                add_close = False\n        except AttributeError:\n            pass\n        self.hide_button = QPushButton(self)\n        self.hide_button.setToolTip(trans._('hide this panel'))\n        self.hide_button.setObjectName(\"QTitleBarHideButton\")\n        self.hide_button.setCursor(Qt.ArrowCursor)\n        self.hide_button.clicked.connect(lambda: self.parent().close())\n\n        self.float_button = QPushButton(self)\n        self.float_button.setToolTip(trans._('float this panel'))\n        self.float_button.setObjectName(\"QTitleBarFloatButton\")\n        self.float_button.setCursor(Qt.ArrowCursor)\n        self.float_button.clicked.connect(\n            lambda: self.parent().setFloating(not self.parent().isFloating())\n        )\n        self.title = QLabel(title, self)\n        self.title.setSizePolicy(\n            QSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Maximum)\n        )\n\n        if vertical:\n            layout = QVBoxLayout()\n            layout.setSpacing(4)\n            layout.setContentsMargins(0, 8, 0, 8)\n            line.setFixedWidth(1)\n            if add_close:\n                layout.addWidget(self.close_button, 0, Qt.AlignHCenter)\n            layout.addWidget(self.hide_button, 0, Qt.AlignHCenter)\n            layout.addWidget(self.float_button, 0, Qt.AlignHCenter)\n            layout.addWidget(line, 0, Qt.AlignHCenter)\n            self.title.hide()\n\n        else:\n            layout = QHBoxLayout()\n            layout.setSpacing(4)\n            layout.setContentsMargins(8, 1, 8, 0)\n            line.setFixedHeight(1)\n            if add_close:\n                layout.addWidget(self.close_button)\n\n            layout.addWidget(self.hide_button)\n            layout.addWidget(self.float_button)\n            layout.addWidget(line)\n            layout.addWidget(self.title)\n\n        self.setLayout(layout)\n        self.setCursor(Qt.OpenHandCursor)\n\n    def sizeHint(self):\n        # this seems to be the correct way to set the height of the titlebar\n        szh = super().sizeHint()\n        if self.vertical:\n            szh.setWidth(20)\n        else:\n            szh.setHeight(20)\n        return szh\n", "path": "napari/_qt/widgets/qt_viewer_dock_widget.py"}], "after_files": [{"content": "import warnings\nfrom functools import reduce\nfrom itertools import count\nfrom operator import ior\nfrom typing import TYPE_CHECKING, List, Optional\nfrom weakref import ReferenceType, ref\n\nfrom qtpy.QtCore import Qt\nfrom qtpy.QtWidgets import (\n    QDockWidget,\n    QFrame,\n    QHBoxLayout,\n    QLabel,\n    QPushButton,\n    QSizePolicy,\n    QVBoxLayout,\n    QWidget,\n)\n\nfrom ...utils.translations import trans\nfrom ..utils import combine_widgets, qt_signals_blocked\n\nif TYPE_CHECKING:\n    from ..qt_viewer import QtViewer\n\ncounter = count()\n_sentinel = object()\n\n_SHORTCUT_DEPRECATION_STRING = trans._(\n    'The shortcut parameter is deprecated since version 0.4.8, please use the action and shortcut manager APIs. The new action manager and shortcut API allow user configuration and localisation. (got {shortcut})',\n    shortcut=\"{shortcut}\",\n)\n\n\nclass QtViewerDockWidget(QDockWidget):\n    \"\"\"Wrap a QWidget in a QDockWidget and forward viewer events\n\n    Parameters\n    ----------\n    qt_viewer : QtViewer\n        The QtViewer instance that this dock widget will belong to.\n    widget : QWidget\n        `widget` that will be added as QDockWidget's main widget.\n    name : str\n        Name of dock widget.\n    area : str\n        Side of the main window to which the new dock widget will be added.\n        Must be in {'left', 'right', 'top', 'bottom'}\n    allowed_areas : list[str], optional\n        Areas, relative to main window, that the widget is allowed dock.\n        Each item in list must be in {'left', 'right', 'top', 'bottom'}\n        By default, all areas are allowed.\n    shortcut : str, optional\n        Keyboard shortcut to appear in dropdown menu.\n        .. deprecated:: 0.4.8\n\n            The shortcut parameter is deprecated since version 0.4.8, please use\n            the action and shortcut manager APIs. The new action manager and\n            shortcut API allow user configuration and localisation.\n    add_vertical_stretch : bool, optional\n        Whether to add stretch to the bottom of vertical widgets (pushing\n        widgets up towards the top of the allotted area, instead of letting\n        them distribute across the vertical space).  By default, True.\n    \"\"\"\n\n    def __init__(\n        self,\n        qt_viewer,\n        widget: QWidget,\n        *,\n        name: str = '',\n        area: str = 'right',\n        allowed_areas: Optional[List[str]] = None,\n        shortcut=_sentinel,\n        object_name: str = '',\n        add_vertical_stretch=True,\n    ):\n        self._ref_qt_viewer: 'ReferenceType[QtViewer]' = ref(qt_viewer)\n        super().__init__(name)\n        self._parent = qt_viewer\n        self.name = name\n\n        areas = {\n            'left': Qt.LeftDockWidgetArea,\n            'right': Qt.RightDockWidgetArea,\n            'top': Qt.TopDockWidgetArea,\n            'bottom': Qt.BottomDockWidgetArea,\n        }\n        if area not in areas:\n            raise ValueError(\n                trans._(\n                    'area argument must be in {areas}',\n                    deferred=True,\n                    areas=list(areas.keys()),\n                )\n            )\n        self.area = area\n        self.qt_area = areas[area]\n        if shortcut is not _sentinel:\n            warnings.warn(\n                _SHORTCUT_DEPRECATION_STRING.format(shortcut=shortcut),\n                FutureWarning,\n                stacklevel=2,\n            )\n        else:\n            shortcut = None\n        self._shortcut = shortcut\n\n        if allowed_areas:\n            if not isinstance(allowed_areas, (list, tuple)):\n                raise TypeError(\n                    trans._(\n                        '`allowed_areas` must be a list or tuple',\n                        deferred=True,\n                    )\n                )\n\n            if any(area not in areas for area in allowed_areas):\n                raise ValueError(\n                    trans._(\n                        'all allowed_areas argument must be in {areas}',\n                        deferred=True,\n                        areas=list(areas.keys()),\n                    )\n                )\n            allowed_areas = reduce(ior, [areas[a] for a in allowed_areas])\n        else:\n            allowed_areas = Qt.AllDockWidgetAreas\n        self.setAllowedAreas(allowed_areas)\n        self.setMinimumHeight(50)\n        self.setMinimumWidth(50)\n        # FIXME:\n        self.setObjectName(object_name or name)\n\n        is_vertical = area in {'left', 'right'}\n        widget = combine_widgets(widget, vertical=is_vertical)\n        self.setWidget(widget)\n        if is_vertical and add_vertical_stretch:\n            self._maybe_add_vertical_stretch(widget)\n\n        self._features = self.features()\n        self.dockLocationChanged.connect(self._set_title_orientation)\n\n        # custom title bar\n        self.title = QtCustomTitleBar(self, title=self.name)\n        self.setTitleBarWidget(self.title)\n        self.visibilityChanged.connect(self._on_visibility_changed)\n\n    @property\n    def _parent(self):\n        \"\"\"\n        Let's make sure parent always a weakref:\n\n            1) parent is likely to always exists after child\n            2) even if not strictly necessary it make it easier to view reference cycles.\n        \"\"\"\n        return self._ref_parent()\n\n    @_parent.setter\n    def _parent(self, obj):\n        self._ref_parent = ref(obj)\n\n    def destroyOnClose(self):\n        \"\"\"Destroys dock plugin dock widget when 'x' is clicked.\"\"\"\n        self._ref_qt_viewer().viewer.window.remove_dock_widget(self)\n\n    def _maybe_add_vertical_stretch(self, widget):\n        \"\"\"Add vertical stretch to the bottom of a vertical layout only\n\n        ...if there is not already a widget that wants vertical space\n        (like a textedit or listwidget or something).\n        \"\"\"\n        exempt_policies = {\n            QSizePolicy.Expanding,\n            QSizePolicy.MinimumExpanding,\n            QSizePolicy.Ignored,\n        }\n        if widget.sizePolicy().verticalPolicy() in exempt_policies:\n            return\n\n        # not uncommon to see people shadow the builtin layout() method\n        # which breaks our ability to add vertical stretch...\n        try:\n            wlayout = widget.layout()\n            if wlayout is None:\n                return\n        except TypeError:\n            return\n\n        for i in range(wlayout.count()):\n            wdg = wlayout.itemAt(i).widget()\n            if (\n                wdg is not None\n                and wdg.sizePolicy().verticalPolicy() in exempt_policies\n            ):\n                return\n\n        # not all widgets have addStretch...\n        if hasattr(wlayout, 'addStretch'):\n            wlayout.addStretch(next(counter))\n\n    @property\n    def shortcut(self):\n        warnings.warn(\n            _SHORTCUT_DEPRECATION_STRING,\n            FutureWarning,\n            stacklevel=2,\n        )\n        return self._shortcut\n\n    def setFeatures(self, features):\n        super().setFeatures(features)\n        self._features = self.features()\n\n    def keyPressEvent(self, event):\n        # if you subclass QtViewerDockWidget and override the keyPressEvent\n        # method, be sure to call super().keyPressEvent(event) at the end of\n        # your method to pass uncaught key-combinations to the viewer.\n        return self._ref_qt_viewer().keyPressEvent(event)\n\n    def _set_title_orientation(self, area):\n        if area in (Qt.LeftDockWidgetArea, Qt.RightDockWidgetArea):\n            features = self._features\n            if features & self.DockWidgetVerticalTitleBar:\n                features = features ^ self.DockWidgetVerticalTitleBar\n        else:\n            features = self._features | self.DockWidgetVerticalTitleBar\n        self.setFeatures(features)\n\n    @property\n    def is_vertical(self):\n        if not self.isFloating():\n            par = self.parent()\n            if par and hasattr(par, 'dockWidgetArea'):\n                return par.dockWidgetArea(self) in (\n                    Qt.LeftDockWidgetArea,\n                    Qt.RightDockWidgetArea,\n                )\n        return self.size().height() > self.size().width()\n\n    def _on_visibility_changed(self, visible):\n        try:\n            actions = [\n                action.text()\n                for action in self._ref_qt_viewer().viewer.window.plugins_menu.actions()\n            ]\n            idx = actions.index(self.name)\n\n            current_action = (\n                self._ref_qt_viewer().viewer.window.plugins_menu.actions()[idx]\n            )\n            current_action.setChecked(visible)\n            self.setVisible(visible)\n\n        except (AttributeError, ValueError):\n            # AttributeError: This error happens when the plugins menu is not yet built.\n            # ValueError: This error is when the action is from the windows menu.\n            pass\n        if not visible:\n            return\n        with qt_signals_blocked(self):\n            self.setTitleBarWidget(None)\n            if not self.isFloating():\n                self.title = QtCustomTitleBar(\n                    self, title=self.name, vertical=not self.is_vertical\n                )\n                self.setTitleBarWidget(self.title)\n\n    def setWidget(self, widget):\n        widget._parent = self\n        super().setWidget(widget)\n\n\nclass QtCustomTitleBar(QLabel):\n    \"\"\"A widget to be used as the titleBar in the QtViewerDockWidget.\n\n    Keeps vertical size minimal, has a hand cursor and styles (in stylesheet)\n    for hover. Close and float buttons.\n\n    Parameters\n    ----------\n    parent : QDockWidget\n        The QtViewerDockWidget to which this titlebar belongs\n    title : str\n        A string to put in the titlebar.\n    vertical : bool\n        Whether this titlebar is oriented vertically or not.\n    \"\"\"\n\n    def __init__(self, parent, title: str = '', vertical=False):\n        super().__init__(parent)\n        self.setObjectName(\"QtCustomTitleBar\")\n        self.setProperty('vertical', str(vertical))\n        self.vertical = vertical\n        self.setToolTip(trans._('drag to move. double-click to float'))\n\n        line = QFrame(self)\n        line.setObjectName(\"QtCustomTitleBarLine\")\n\n        add_close = False\n        try:\n            # if the plugins menu is already created, check to see if this is a plugin\n            # dock widget.  If it is, then add the close button option to the title bar.\n            win = self.parent().parent()._qt_viewer.viewer.window\n            actions = [action.text() for action in win.plugins_menu.actions()]\n\n            if self.parent().name in actions:\n                add_close = True\n                self.close_button = QPushButton(self)\n                self.close_button.setToolTip(trans._('close this panel'))\n                self.close_button.setObjectName(\"QTitleBarCloseButton\")\n                self.close_button.setCursor(Qt.ArrowCursor)\n                self.close_button.clicked.connect(\n                    lambda: self.parent().destroyOnClose()\n                )\n            else:\n                add_close = False\n        except AttributeError:\n            pass\n        self.hide_button = QPushButton(self)\n        self.hide_button.setToolTip(trans._('hide this panel'))\n        self.hide_button.setObjectName(\"QTitleBarHideButton\")\n        self.hide_button.setCursor(Qt.ArrowCursor)\n        self.hide_button.clicked.connect(lambda: self.parent().close())\n\n        self.float_button = QPushButton(self)\n        self.float_button.setToolTip(trans._('float this panel'))\n        self.float_button.setObjectName(\"QTitleBarFloatButton\")\n        self.float_button.setCursor(Qt.ArrowCursor)\n        self.float_button.clicked.connect(\n            lambda: self.parent().setFloating(not self.parent().isFloating())\n        )\n        self.title = QLabel(title, self)\n        self.title.setSizePolicy(\n            QSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Maximum)\n        )\n\n        if vertical:\n            layout = QVBoxLayout()\n            layout.setSpacing(4)\n            layout.setContentsMargins(0, 8, 0, 8)\n            line.setFixedWidth(1)\n            if add_close:\n                layout.addWidget(self.close_button, 0, Qt.AlignHCenter)\n            layout.addWidget(self.hide_button, 0, Qt.AlignHCenter)\n            layout.addWidget(self.float_button, 0, Qt.AlignHCenter)\n            layout.addWidget(line, 0, Qt.AlignHCenter)\n            self.title.hide()\n\n        else:\n            layout = QHBoxLayout()\n            layout.setSpacing(4)\n            layout.setContentsMargins(8, 1, 8, 0)\n            line.setFixedHeight(1)\n            if add_close:\n                layout.addWidget(self.close_button)\n\n            layout.addWidget(self.hide_button)\n            layout.addWidget(self.float_button)\n            layout.addWidget(line)\n            layout.addWidget(self.title)\n\n        self.setLayout(layout)\n        self.setCursor(Qt.OpenHandCursor)\n\n    def sizeHint(self):\n        # this seems to be the correct way to set the height of the titlebar\n        szh = super().sizeHint()\n        if self.vertical:\n            szh.setWidth(20)\n        else:\n            szh.setHeight(20)\n        return szh\n", "path": "napari/_qt/widgets/qt_viewer_dock_widget.py"}]}