|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2021 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a widget showing the list of open pages. |
|
8 """ |
|
9 |
|
10 from PyQt6.QtCore import pyqtSlot, pyqtSignal, Qt, QPoint |
|
11 from PyQt6.QtGui import QGuiApplication, QClipboard |
|
12 from PyQt6.QtWidgets import QListWidget, QAbstractItemView, QMenu |
|
13 |
|
14 import UI.PixmapCache |
|
15 |
|
16 |
|
17 class OpenPagesWidget(QListWidget): |
|
18 """ |
|
19 Class implementing a widget showing the list of open pages. |
|
20 |
|
21 @signal currentPageChanged(index) emitted to signal a change of the current |
|
22 page index |
|
23 """ |
|
24 currentPageChanged = pyqtSignal(int) |
|
25 |
|
26 def __init__(self, stack, parent=None): |
|
27 """ |
|
28 Constructor |
|
29 |
|
30 @param stack reference to the stack widget containing the open |
|
31 help pages |
|
32 @type QStackedWidget |
|
33 @param parent reference to the parent widget (defaults to None) |
|
34 @type QWidget (optional) |
|
35 """ |
|
36 super().__init__(parent) |
|
37 self.setObjectName("OpenPagesWidget") |
|
38 |
|
39 self.__helpViewer = parent |
|
40 |
|
41 self.setAlternatingRowColors(True) |
|
42 self.setSelectionMode( |
|
43 QAbstractItemView.SelectionMode.SingleSelection) |
|
44 self.setContextMenuPolicy( |
|
45 Qt.ContextMenuPolicy.CustomContextMenu) |
|
46 self.currentRowChanged.connect( |
|
47 self.__currentRowChanged) |
|
48 self.customContextMenuRequested.connect( |
|
49 self.__showContextMenu) |
|
50 |
|
51 self.__stack = stack |
|
52 self.__stack.currentChanged.connect(self.__currentPageChanged) |
|
53 |
|
54 self.__initContextMenu() |
|
55 |
|
56 self.__defaultFont = self.font() |
|
57 self.__boldFont = self.font() |
|
58 self.__boldFont.setBold(True) |
|
59 |
|
60 def __initContextMenu(self): |
|
61 """ |
|
62 Private method to initialize the context menu. |
|
63 """ |
|
64 self.__menu = QMenu(self) |
|
65 self.__menu.addAction( |
|
66 UI.PixmapCache.getIcon("tabClose"), |
|
67 self.tr('Close'), self.__contextMenuClose) |
|
68 self.closeOthersMenuAct = self.__menu.addAction( |
|
69 UI.PixmapCache.getIcon("tabCloseOther"), |
|
70 self.tr("Close Others"), |
|
71 self.__contextMenuCloseOthers) |
|
72 self.__menu.addAction( |
|
73 self.tr('Close All'), self.__contextMenuCloseAll) |
|
74 self.__menu.addSeparator() |
|
75 self.__copyUrlAct = self.__menu.addAction( |
|
76 self.tr("Copy URL to Clipboard"), |
|
77 self.__contextMenuCopyUrlToClipboard) |
|
78 |
|
79 @pyqtSlot(QPoint) |
|
80 def __showContextMenu(self, point): |
|
81 """ |
|
82 Private slot to handle the customContextMenuRequested signal of |
|
83 the viewlist. |
|
84 |
|
85 @param point position to open the menu at |
|
86 @type QPoint |
|
87 """ |
|
88 itm = self.itemAt(point) |
|
89 self.__copyUrlAct.setEnabled(bool(itm) and itm.text() != "about:blank") |
|
90 self.closeOthersMenuAct.setEnabled(self.count() > 1) |
|
91 self.__menu.popup(self.mapToGlobal(point)) |
|
92 |
|
93 @pyqtSlot(int) |
|
94 def __currentPageChanged(self, index): |
|
95 """ |
|
96 Private slot to handle a change of the shown page. |
|
97 |
|
98 @param index index of the current page |
|
99 @type int |
|
100 """ |
|
101 for row in range(self.count()): |
|
102 itm = self.item(row) |
|
103 itm.setFont( |
|
104 self.__boldFont if row == index else self.__defaultFont |
|
105 ) |
|
106 |
|
107 @pyqtSlot(int) |
|
108 def __currentRowChanged(self, row): |
|
109 """ |
|
110 Private slot handling a change of the current row. |
|
111 |
|
112 @param row current row |
|
113 @type int |
|
114 """ |
|
115 self.__stack.setCurrentIndex(row) |
|
116 self.currentPageChanged.emit(row) |
|
117 |
|
118 def addPage(self, viewer, background=False): |
|
119 """ |
|
120 Public method to add a viewer page to our list. |
|
121 |
|
122 @param viewer reference to the viewer object |
|
123 @type HelpViewerImpl |
|
124 @param background flag indicating to not change the current page |
|
125 (defaults to False) |
|
126 @type bool (optional) |
|
127 """ |
|
128 self.addItem(viewer.pageTitle()) |
|
129 viewer.titleChanged.connect( |
|
130 lambda: self.__viewerTitleChanged(viewer)) |
|
131 |
|
132 if not background: |
|
133 self.setCurrentRow( |
|
134 self.count() - 1) |
|
135 if self.count() == 1: |
|
136 self.__currentPageChanged(0) |
|
137 |
|
138 def insertPage(self, index, viewer, background=False): |
|
139 """ |
|
140 Public method to insert a viewer page into our list. |
|
141 |
|
142 @param index index to insert at |
|
143 @type int |
|
144 @param viewer reference to the viewer object |
|
145 @type HelpViewerImpl |
|
146 @param background flag indicating to not change the current page |
|
147 (defaults to False) |
|
148 @type bool (optional) |
|
149 """ |
|
150 currentRow = self.currentRow() |
|
151 self.insertItem(index, viewer.pageTitle()) |
|
152 viewer.titleChanged.connect( |
|
153 lambda: self.__viewerTitleChanged(viewer)) |
|
154 |
|
155 if not background: |
|
156 self.setCurrentRow(index) |
|
157 else: |
|
158 self.setCurrentRow(currentRow) |
|
159 |
|
160 def __viewerTitleChanged(self, viewer): |
|
161 """ |
|
162 Private method to handle the change of a viewer title. |
|
163 |
|
164 @param viewer reference to the viewer that change title |
|
165 @type HelpViewerImpl |
|
166 """ |
|
167 index = self.__stack.indexOf(viewer) |
|
168 itm = self.item(index) |
|
169 itm.setText(viewer.pageTitle()) |
|
170 self.currentPageChanged.emit(index) |
|
171 |
|
172 ####################################################################### |
|
173 ## Context menu action methods |
|
174 ####################################################################### |
|
175 |
|
176 @pyqtSlot() |
|
177 def __contextMenuClose(self): |
|
178 """ |
|
179 Private slot to close a page via the context menu. |
|
180 """ |
|
181 self.closeCurrentPage() |
|
182 |
|
183 @pyqtSlot() |
|
184 def __contextMenuCloseOthers(self): |
|
185 """ |
|
186 Private slot to close all other pages via the context menu. |
|
187 """ |
|
188 self.closeOtherPages() |
|
189 |
|
190 @pyqtSlot() |
|
191 def __contextMenuCloseAll(self): |
|
192 """ |
|
193 Private slot to close all pages via the context menu. |
|
194 """ |
|
195 self.closeAllPages() |
|
196 |
|
197 @pyqtSlot() |
|
198 def __contextMenuCopyUrlToClipboard(self): |
|
199 """ |
|
200 Private slot to copy the URL to the clipboard. |
|
201 """ |
|
202 row = self.currentRow() |
|
203 viewer = self.__stack.widget(row) |
|
204 url = viewer.link() |
|
205 if url.isValid(): |
|
206 urlStr = url.toString() |
|
207 |
|
208 # copy the URL to both clipboard areas |
|
209 QGuiApplication.clipboard().setText( |
|
210 urlStr, QClipboard.Mode.Clipboard) |
|
211 QGuiApplication.clipboard().setText( |
|
212 urlStr, QClipboard.Mode.Selection) |
|
213 |
|
214 def __removeViewer(self, row): |
|
215 """ |
|
216 Private method to remove a viewer page. |
|
217 |
|
218 @param row row associated with the viewer |
|
219 @type int |
|
220 """ |
|
221 viewer = self.__stack.widget(row) |
|
222 self.__stack.removeWidget(viewer) |
|
223 viewer.deleteLater() |
|
224 |
|
225 itm = self.takeItem(row) |
|
226 del itm |
|
227 |
|
228 ####################################################################### |
|
229 ## Slots for external access below |
|
230 ####################################################################### |
|
231 |
|
232 @pyqtSlot() |
|
233 def closeCurrentPage(self): |
|
234 """ |
|
235 Public slot to close the current page. |
|
236 """ |
|
237 row = self.currentRow() |
|
238 self.__removeViewer(row) |
|
239 |
|
240 if self.count() == 0: |
|
241 self.__helpViewer.addPage() |
|
242 |
|
243 @pyqtSlot() |
|
244 def closeOtherPages(self): |
|
245 """ |
|
246 Public slot to close all other pages. |
|
247 """ |
|
248 currentRow = self.currentRow() |
|
249 for row in range(self.count() - 1, -1, -1): |
|
250 if row != currentRow: |
|
251 self.__removeViewer(row) |
|
252 |
|
253 @pyqtSlot() |
|
254 def closeAllPages(self): |
|
255 """ |
|
256 Public slot to close all pages. |
|
257 """ |
|
258 while self.count() != 0: |
|
259 self.__removeViewer(0) |
|
260 self.__helpViewer.addPage() |