8 """ |
8 """ |
9 |
9 |
10 import contextlib |
10 import contextlib |
11 |
11 |
12 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QCoreApplication |
12 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QCoreApplication |
13 from PyQt6.QtGui import QColor, QIcon |
13 from PyQt6.QtGui import QColor, QIcon, QCursor, QPalette |
14 from PyQt6.QtWidgets import QWidget, QBoxLayout, QWIDGETSIZE_MAX |
14 from PyQt6.QtWidgets import QWidget, QBoxLayout, QWIDGETSIZE_MAX, QMenu |
|
15 |
|
16 from EricWidgets.EricApplication import ericApp |
15 |
17 |
16 from .EricClickableLabel import EricClickableLabel |
18 from .EricClickableLabel import EricClickableLabel |
|
19 |
|
20 import UI.PixmapCache |
17 |
21 |
18 |
22 |
19 class EricIconBar(QWidget): |
23 class EricIconBar(QWidget): |
20 """ |
24 """ |
21 Class implementing a bar widget showing just icons. |
25 Class implementing a bar widget showing just icons. |
52 QCoreApplication.translate("EricIconBar", "very large") |
56 QCoreApplication.translate("EricIconBar", "very large") |
53 ), |
57 ), |
54 } |
58 } |
55 DefaultBarSize = "md" |
59 DefaultBarSize = "md" |
56 |
60 |
|
61 MoreLabelAspect = 36 / 96 |
|
62 |
|
63 MenuStyleSheetTemplate = ( |
|
64 "QMenu {{ background-color: {0}; " |
|
65 "selection-background-color: {1}; " |
|
66 "border: 1px solid; }}" |
|
67 ) |
57 WidgetStyleSheetTemplate = "QWidget {{ background-color: {0}; }}" |
68 WidgetStyleSheetTemplate = "QWidget {{ background-color: {0}; }}" |
58 LabelStyleSheetTemplate = "QLabel {{ background-color: {0}; }}" |
69 LabelStyleSheetTemplate = "QLabel {{ background-color: {0}; }}" |
59 |
70 |
60 currentChanged = pyqtSignal(int) |
71 currentChanged = pyqtSignal(int) |
61 currentClicked = pyqtSignal(int) |
72 currentClicked = pyqtSignal(int) |
83 self.__barSize, self.__borderSize = ( |
94 self.__barSize, self.__borderSize = ( |
84 EricIconBar.BarSizes[EricIconBar.DefaultBarSize][:2]) |
95 EricIconBar.BarSizes[EricIconBar.DefaultBarSize][:2]) |
85 self.__fixedHeightWidth = ( |
96 self.__fixedHeightWidth = ( |
86 self.__barSize + 2 * self.__borderSize |
97 self.__barSize + 2 * self.__borderSize |
87 ) |
98 ) |
|
99 self.__minimumHeightWidth = int( |
|
100 self.__barSize * self.MoreLabelAspect) + 2 * self.__borderSize |
88 |
101 |
89 # set initial values |
102 # set initial values |
90 self.__color = QColor("#008800") |
103 self.__color = QColor("#008800") |
91 self.__orientation = Qt.Orientation.Horizontal |
104 self.__orientation = Qt.Orientation.Horizontal |
92 self.__currentIndex = -1 |
105 self.__currentIndex = -1 |
93 self.__icons = [] |
106 self.__icons = [] |
94 |
107 |
95 # initialize with horizontal layout and change later if needed |
108 # initialize with horizontal layout and change later if needed |
96 self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) |
109 self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) |
97 self.setFixedHeight(self.__fixedHeightWidth) |
110 self.setFixedHeight(self.__fixedHeightWidth) |
|
111 self.setMinimumWidth(self.__minimumHeightWidth) |
98 |
112 |
99 self.__layout = QBoxLayout(QBoxLayout.Direction.LeftToRight) |
113 self.__layout = QBoxLayout(QBoxLayout.Direction.LeftToRight) |
100 self.__layout.setContentsMargins( |
114 self.__layout.setContentsMargins( |
101 self.__borderSize, self.__borderSize, |
115 self.__borderSize, self.__borderSize, |
102 self.__borderSize, self.__borderSize) |
116 self.__borderSize, self.__borderSize) |
107 |
121 |
108 if orientation != self.__orientation: |
122 if orientation != self.__orientation: |
109 self.setOrientation(orientation) |
123 self.setOrientation(orientation) |
110 |
124 |
111 self.setColor(self.__color) |
125 self.setColor(self.__color) |
|
126 |
|
127 self.__createMoreLabel() |
|
128 self.__layout.insertWidget(0, self.__moreLabel) |
|
129 |
|
130 self.__adjustIconLabels() |
112 |
131 |
113 def setOrientation(self, orientation): |
132 def setOrientation(self, orientation): |
114 """ |
133 """ |
115 Public method to set the widget orientation. |
134 Public method to set the widget orientation. |
116 |
135 |
118 @type Qt.Orientation |
137 @type Qt.Orientation |
119 """ |
138 """ |
120 # reset list widget size constraints |
139 # reset list widget size constraints |
121 self.setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX) |
140 self.setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX) |
122 |
141 |
|
142 # remove the 'More' icon |
|
143 itm = self.__layout.takeAt(self.__layout.count() - 2) |
|
144 itm.widget().deleteLater() |
|
145 del itm |
|
146 |
123 if orientation == Qt.Orientation.Horizontal: |
147 if orientation == Qt.Orientation.Horizontal: |
124 self.setFixedHeight(self.__fixedHeightWidth) |
148 self.setFixedHeight(self.__fixedHeightWidth) |
|
149 self.setMinimumWidth(self.__minimumHeightWidth) |
125 self.__layout.setDirection(QBoxLayout.Direction.LeftToRight) |
150 self.__layout.setDirection(QBoxLayout.Direction.LeftToRight) |
126 elif orientation == Qt.Orientation.Vertical: |
151 elif orientation == Qt.Orientation.Vertical: |
127 self.setFixedWidth(self.__fixedHeightWidth) |
152 self.setFixedWidth(self.__fixedHeightWidth) |
|
153 self.setMinimumHeight(self.__minimumHeightWidth) |
128 self.__layout.setDirection(QBoxLayout.Direction.TopToBottom) |
154 self.__layout.setDirection(QBoxLayout.Direction.TopToBottom) |
129 |
155 |
130 self.__orientation = orientation |
156 self.__orientation = orientation |
|
157 |
|
158 self.__createMoreLabel() |
|
159 self.__layout.insertWidget(self.__layout.count() - 1, self.__moreLabel) |
|
160 |
|
161 self.__adjustIconLabels() |
131 |
162 |
132 def orientation(self): |
163 def orientation(self): |
133 """ |
164 """ |
134 Public method to get the orientation of the widget. |
165 Public method to get the orientation of the widget. |
135 |
166 |
144 |
175 |
145 @param barSize size category for the bar (one of 'xs', 'sm', 'md', |
176 @param barSize size category for the bar (one of 'xs', 'sm', 'md', |
146 'lg', 'xl', 'xxl') |
177 'lg', 'xl', 'xxl') |
147 @type str |
178 @type str |
148 """ |
179 """ |
|
180 # remove the 'More' icon |
|
181 itm = self.__layout.takeAt(self.__layout.count() - 2) |
|
182 itm.widget().deleteLater() |
|
183 del itm |
|
184 |
149 self.__barSize, self.__borderSize = ( |
185 self.__barSize, self.__borderSize = ( |
150 EricIconBar.BarSizes[barSize][:2]) |
186 EricIconBar.BarSizes[barSize][:2]) |
151 self.__barSizeKey = barSize |
187 self.__barSizeKey = barSize |
152 self.__fixedHeightWidth = ( |
188 self.__fixedHeightWidth = ( |
153 self.__barSize + 2 * self.__borderSize |
189 self.__barSize + 2 * self.__borderSize |
154 ) |
190 ) |
|
191 self.__minimumHeightWidth = int( |
|
192 self.__barSize * self.MoreLabelAspect) + 2 * self.__borderSize |
155 |
193 |
156 if self.__orientation == Qt.Orientation.Horizontal: |
194 if self.__orientation == Qt.Orientation.Horizontal: |
157 self.setFixedHeight(self.__fixedHeightWidth) |
195 self.setFixedHeight(self.__fixedHeightWidth) |
|
196 self.setMinimumWidth(self.__minimumHeightWidth) |
158 elif self.__orientation == Qt.Orientation.Vertical: |
197 elif self.__orientation == Qt.Orientation.Vertical: |
159 self.setFixedWidth(self.__fixedHeightWidth) |
198 self.setFixedWidth(self.__fixedHeightWidth) |
|
199 self.setMinimumHeight(self.__minimumHeightWidth) |
160 |
200 |
161 self.__layout.setContentsMargins( |
201 self.__layout.setContentsMargins( |
162 self.__borderSize, self.__borderSize, |
202 self.__borderSize, self.__borderSize, |
163 self.__borderSize, self.__borderSize) |
203 self.__borderSize, self.__borderSize) |
164 |
204 |
167 if iconLabel: |
207 if iconLabel: |
168 widget = iconLabel.widget() |
208 widget = iconLabel.widget() |
169 widget.setFixedSize(self.__barSize, self.__barSize) |
209 widget.setFixedSize(self.__barSize, self.__barSize) |
170 widget.setPixmap( |
210 widget.setPixmap( |
171 icon.pixmap(self.__barSize, self.__barSize)) |
211 icon.pixmap(self.__barSize, self.__barSize)) |
|
212 |
|
213 self.__createMoreLabel() |
|
214 self.__layout.insertWidget(self.__layout.count() - 1, self.__moreLabel) |
|
215 |
|
216 self.__adjustIconLabels() |
172 |
217 |
173 def barSize(self): |
218 def barSize(self): |
174 """ |
219 """ |
175 Public method to get the icon bar size. |
220 Public method to get the icon bar size. |
176 |
221 |
229 |
274 |
230 iconLabel.clicked.connect(lambda: self.__iconClicked(iconLabel)) |
275 iconLabel.clicked.connect(lambda: self.__iconClicked(iconLabel)) |
231 |
276 |
232 return iconLabel |
277 return iconLabel |
233 |
278 |
|
279 def __createMoreLabel(self): |
|
280 """ |
|
281 Private method to create the label to be shown for too many icons. |
|
282 """ |
|
283 self.__moreLabel = EricClickableLabel(self) |
|
284 self.__moreLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) |
|
285 if self.__orientation == Qt.Orientation.Horizontal: |
|
286 self.__moreLabel.setFixedSize( |
|
287 int(self.__barSize * self.MoreLabelAspect), self.__barSize) |
|
288 self.__moreLabel.setPixmap( |
|
289 UI.PixmapCache.getIcon("sbDotsH96").pixmap( |
|
290 int(self.__barSize * self.MoreLabelAspect), self.__barSize |
|
291 ) |
|
292 ) |
|
293 else: |
|
294 self.__moreLabel.setFixedSize( |
|
295 self.__barSize, int(self.__barSize * self.MoreLabelAspect)) |
|
296 self.__moreLabel.setPixmap( |
|
297 UI.PixmapCache.getIcon("sbDotsV96").pixmap( |
|
298 self.__barSize, int(self.__barSize * self.MoreLabelAspect) |
|
299 ) |
|
300 ) |
|
301 |
|
302 self.__moreLabel.clicked.connect(self.__moreLabelClicked) |
|
303 |
234 def addIcon(self, icon, label=""): |
304 def addIcon(self, icon, label=""): |
235 """ |
305 """ |
236 Public method to add an icon to the bar. |
306 Public method to add an icon to the bar. |
237 |
307 |
238 @param icon reference to the icon |
308 @param icon reference to the icon |
260 |
330 |
261 if self.__currentIndex < 0: |
331 if self.__currentIndex < 0: |
262 self.setCurrentIndex(index) |
332 self.setCurrentIndex(index) |
263 elif index <= self.__currentIndex: |
333 elif index <= self.__currentIndex: |
264 self.setCurrentIndex(self.__currentIndex + 1) |
334 self.setCurrentIndex(self.__currentIndex + 1) |
|
335 |
|
336 self.__adjustIconLabels() |
265 |
337 |
266 def removeIcon(self, index): |
338 def removeIcon(self, index): |
267 """ |
339 """ |
268 Public method to remove an icon from the bar. |
340 Public method to remove an icon from the bar. |
269 |
341 |
280 |
352 |
281 if index == self.__currentIndex: |
353 if index == self.__currentIndex: |
282 self.setCurrentIndex(index) |
354 self.setCurrentIndex(index) |
283 elif index < self.__currentIndex: |
355 elif index < self.__currentIndex: |
284 self.setCurrentIndex(self.__currentIndex - 1) |
356 self.setCurrentIndex(self.__currentIndex - 1) |
|
357 |
|
358 self.__adjustIconLabels() |
285 |
359 |
286 @pyqtSlot() |
360 @pyqtSlot() |
287 def __iconClicked(self, label): |
361 def __iconClicked(self, label): |
288 """ |
362 """ |
289 Private slot to handle an icon been clicked. |
363 Private slot to handle an icon been clicked. |
341 Public method to get the number of icon labels. |
415 Public method to get the number of icon labels. |
342 |
416 |
343 @return number of icon labels |
417 @return number of icon labels |
344 @rtype int |
418 @rtype int |
345 """ |
419 """ |
346 return self.__layout.count() - 1 |
420 return len(self.__icons) |
347 |
421 |
348 def wheelEvent(self, evt): |
422 def wheelEvent(self, evt): |
349 """ |
423 """ |
350 Protected method to handle a wheel event. |
424 Protected method to handle a wheel event. |
351 |
425 |
379 if index == self.count(): |
453 if index == self.count(): |
380 # wrap around |
454 # wrap around |
381 index = 0 |
455 index = 0 |
382 |
456 |
383 self.setCurrentIndex(index) |
457 self.setCurrentIndex(index) |
|
458 |
|
459 @pyqtSlot() |
|
460 def __moreLabelClicked(self): |
|
461 """ |
|
462 Private slot to handle a click onto the 'More' label. |
|
463 """ |
|
464 menu = QMenu(self) |
|
465 baseColor = ericApp().palette().color( |
|
466 QPalette.ColorRole.Base) |
|
467 highlightColor = ericApp().palette().color( |
|
468 QPalette.ColorRole.Highlight) |
|
469 menu.setStyleSheet( |
|
470 EricIconBar.MenuStyleSheetTemplate.format( |
|
471 baseColor.name(), highlightColor.name())) |
|
472 |
|
473 for index in range(self.__layout.count() - 2): |
|
474 iconLabel = self.__layout.itemAt(index) |
|
475 if iconLabel: |
|
476 widget = iconLabel.widget() |
|
477 if not widget.isVisible(): |
|
478 act = menu.addAction(widget.toolTip()) |
|
479 act.setData(index) |
|
480 |
|
481 selectedAction = menu.exec(QCursor.pos()) |
|
482 if selectedAction is not None: |
|
483 index = selectedAction.data() |
|
484 if index >= 0: |
|
485 if index == self.__currentIndex: |
|
486 self.currentClicked.emit(self.__currentIndex) |
|
487 else: |
|
488 self.setCurrentIndex(index) |
|
489 |
|
490 def resizeEvent(self, evt): |
|
491 """ |
|
492 Protected method to handle resizing of the icon bar. |
|
493 |
|
494 @param evt reference to the event object |
|
495 @type QResizeEvent |
|
496 """ |
|
497 self.__adjustIconLabels() |
|
498 |
|
499 def __adjustIconLabels(self): |
|
500 """ |
|
501 Private method to adjust the visibility of the icon labels. |
|
502 """ |
|
503 size = ( |
|
504 self.width() |
|
505 if self.orientation() == Qt.Orientation.Horizontal else |
|
506 self.height() |
|
507 ) - 2 * self.__borderSize |
|
508 |
|
509 iconsSize = ( |
|
510 self.count() * self.__barSize + |
|
511 (self.count() - 1) * self.__layout.spacing() |
|
512 ) |
|
513 |
|
514 if size < iconsSize: |
|
515 self.__moreLabel.show() |
|
516 iconsSize += int(self.__barSize * self.MoreLabelAspect) |
|
517 for index in range(self.count() - 1, -1, -1): |
|
518 iconLabel = self.__layout.itemAt(index) |
|
519 if iconLabel: |
|
520 if size < iconsSize: |
|
521 iconLabel.widget().hide() |
|
522 iconsSize -= self.__barSize - self.__layout.spacing() |
|
523 else: |
|
524 iconLabel.widget().show() |
|
525 else: |
|
526 self.__moreLabel.hide() |
|
527 for index in range(self.__layout.count() - 2): |
|
528 iconLabel = self.__layout.itemAt(index) |
|
529 if iconLabel: |
|
530 iconLabel.widget().show() |