Python-FastUI-Widgets 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. fastuiwidgets/__init__.py +12 -0
  2. fastuiwidgets/_rc/__init__.py +0 -0
  3. fastuiwidgets/_rc/resource.py +98835 -0
  4. fastuiwidgets/common/__init__.py +12 -0
  5. fastuiwidgets/common/animation.py +530 -0
  6. fastuiwidgets/common/auto_wrap.py +164 -0
  7. fastuiwidgets/common/color.py +95 -0
  8. fastuiwidgets/common/config.py +423 -0
  9. fastuiwidgets/common/exception_handler.py +31 -0
  10. fastuiwidgets/common/font.py +38 -0
  11. fastuiwidgets/common/icon.py +703 -0
  12. fastuiwidgets/common/image_utils.py +198 -0
  13. fastuiwidgets/common/overload.py +47 -0
  14. fastuiwidgets/common/router.py +133 -0
  15. fastuiwidgets/common/screen.py +25 -0
  16. fastuiwidgets/common/smooth_scroll.py +141 -0
  17. fastuiwidgets/common/style_sheet.py +512 -0
  18. fastuiwidgets/common/theme_listener.py +27 -0
  19. fastuiwidgets/common/translator.py +14 -0
  20. fastuiwidgets/components/__init__.py +6 -0
  21. fastuiwidgets/components/date_time/__init__.py +4 -0
  22. fastuiwidgets/components/date_time/calendar_picker.py +121 -0
  23. fastuiwidgets/components/date_time/calendar_view.py +671 -0
  24. fastuiwidgets/components/date_time/date_picker.py +245 -0
  25. fastuiwidgets/components/date_time/fast_calendar_view.py +487 -0
  26. fastuiwidgets/components/date_time/picker_base.py +632 -0
  27. fastuiwidgets/components/date_time/time_picker.py +223 -0
  28. fastuiwidgets/components/dialog_box/__init__.py +6 -0
  29. fastuiwidgets/components/dialog_box/color_dialog.py +414 -0
  30. fastuiwidgets/components/dialog_box/dialog.py +167 -0
  31. fastuiwidgets/components/dialog_box/folder_list_dialog.py +307 -0
  32. fastuiwidgets/components/dialog_box/mask_dialog_base.py +120 -0
  33. fastuiwidgets/components/dialog_box/message_box_base.py +92 -0
  34. fastuiwidgets/components/dialog_box/message_dialog.py +65 -0
  35. fastuiwidgets/components/layout/__init__.py +3 -0
  36. fastuiwidgets/components/layout/expand_layout.py +96 -0
  37. fastuiwidgets/components/layout/flow_layout.py +236 -0
  38. fastuiwidgets/components/layout/v_box_layout.py +41 -0
  39. fastuiwidgets/components/material/__init__.py +6 -0
  40. fastuiwidgets/components/material/acrylic_combo_box.py +96 -0
  41. fastuiwidgets/components/material/acrylic_flyout.py +105 -0
  42. fastuiwidgets/components/material/acrylic_line_edit.py +27 -0
  43. fastuiwidgets/components/material/acrylic_menu.py +204 -0
  44. fastuiwidgets/components/material/acrylic_tool_tip.py +39 -0
  45. fastuiwidgets/components/material/acrylic_widget.py +42 -0
  46. fastuiwidgets/components/navigation/__init__.py +9 -0
  47. fastuiwidgets/components/navigation/breadcrumb.py +350 -0
  48. fastuiwidgets/components/navigation/navigation_bar.py +416 -0
  49. fastuiwidgets/components/navigation/navigation_interface.py +268 -0
  50. fastuiwidgets/components/navigation/navigation_panel.py +657 -0
  51. fastuiwidgets/components/navigation/navigation_widget.py +686 -0
  52. fastuiwidgets/components/navigation/pivot.py +272 -0
  53. fastuiwidgets/components/navigation/segmented_widget.py +174 -0
  54. fastuiwidgets/components/settings/__init__.py +8 -0
  55. fastuiwidgets/components/settings/custom_color_setting_card.py +139 -0
  56. fastuiwidgets/components/settings/expand_setting_card.py +390 -0
  57. fastuiwidgets/components/settings/folder_list_setting_card.py +134 -0
  58. fastuiwidgets/components/settings/options_setting_card.py +86 -0
  59. fastuiwidgets/components/settings/setting_card.py +449 -0
  60. fastuiwidgets/components/settings/setting_card_group.py +48 -0
  61. fastuiwidgets/components/widgets/__init__.py +41 -0
  62. fastuiwidgets/components/widgets/acrylic_label.py +261 -0
  63. fastuiwidgets/components/widgets/button.py +1059 -0
  64. fastuiwidgets/components/widgets/card_widget.py +369 -0
  65. fastuiwidgets/components/widgets/check_box.py +203 -0
  66. fastuiwidgets/components/widgets/combo_box.py +556 -0
  67. fastuiwidgets/components/widgets/command_bar.py +636 -0
  68. fastuiwidgets/components/widgets/cycle_list_widget.py +251 -0
  69. fastuiwidgets/components/widgets/flip_view.py +430 -0
  70. fastuiwidgets/components/widgets/flyout.py +521 -0
  71. fastuiwidgets/components/widgets/frameless_window.py +49 -0
  72. fastuiwidgets/components/widgets/icon_widget.py +53 -0
  73. fastuiwidgets/components/widgets/info_badge.py +483 -0
  74. fastuiwidgets/components/widgets/info_bar.py +596 -0
  75. fastuiwidgets/components/widgets/label.py +553 -0
  76. fastuiwidgets/components/widgets/line_edit.py +551 -0
  77. fastuiwidgets/components/widgets/list_view.py +158 -0
  78. fastuiwidgets/components/widgets/menu.py +1318 -0
  79. fastuiwidgets/components/widgets/pips_pager.py +331 -0
  80. fastuiwidgets/components/widgets/progress_bar.py +311 -0
  81. fastuiwidgets/components/widgets/progress_ring.py +212 -0
  82. fastuiwidgets/components/widgets/scroll_area.py +125 -0
  83. fastuiwidgets/components/widgets/scroll_bar.py +673 -0
  84. fastuiwidgets/components/widgets/separator.py +43 -0
  85. fastuiwidgets/components/widgets/slider.py +307 -0
  86. fastuiwidgets/components/widgets/spin_box.py +306 -0
  87. fastuiwidgets/components/widgets/stacked_widget.py +211 -0
  88. fastuiwidgets/components/widgets/state_tool_tip.py +188 -0
  89. fastuiwidgets/components/widgets/switch_button.py +312 -0
  90. fastuiwidgets/components/widgets/tab_view.py +804 -0
  91. fastuiwidgets/components/widgets/table_view.py +360 -0
  92. fastuiwidgets/components/widgets/teaching_tip.py +657 -0
  93. fastuiwidgets/components/widgets/tool_tip.py +460 -0
  94. fastuiwidgets/components/widgets/tree_view.py +216 -0
  95. fastuiwidgets/multimedia/__init__.py +3 -0
  96. fastuiwidgets/multimedia/media_play_bar.py +319 -0
  97. fastuiwidgets/multimedia/media_player.py +124 -0
  98. fastuiwidgets/multimedia/video_widget.py +93 -0
  99. fastuiwidgets/window/__init__.py +2 -0
  100. fastuiwidgets/window/fluent_window.py +413 -0
  101. fastuiwidgets/window/splash_screen.py +92 -0
  102. fastuiwidgets/window/stacked_widget.py +66 -0
  103. python_fastui_widgets-1.0.0.dist-info/METADATA +30 -0
  104. python_fastui_widgets-1.0.0.dist-info/RECORD +107 -0
  105. python_fastui_widgets-1.0.0.dist-info/WHEEL +5 -0
  106. python_fastui_widgets-1.0.0.dist-info/licenses/LICENSE +674 -0
  107. python_fastui_widgets-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,551 @@
1
+ # coding: utf-8
2
+ from typing import List, Union
3
+ from PySide6.QtCore import QSize, Qt, QRectF, Signal, QPoint, QTimer, QEvent, QAbstractItemModel, Property, QModelIndex
4
+ from PySide6.QtGui import QPainter, QPainterPath, QIcon, QColor, QAction
5
+ from PySide6.QtWidgets import (QApplication, QHBoxLayout, QLineEdit, QToolButton, QTextEdit,
6
+ QPlainTextEdit, QCompleter, QStyle, QWidget, QTextBrowser)
7
+
8
+
9
+ from ...common.style_sheet import FluentStyleSheet, themeColor
10
+ from ...common.icon import isDarkTheme, FluentIconBase, drawIcon
11
+ from ...common.icon import FluentIcon as FIF
12
+ from ...common.font import setFont
13
+ from ...common.color import FluentSystemColor, autoFallbackThemeColor
14
+ from .tool_tip import ToolTipFilter
15
+ from .menu import LineEditMenu, TextEditMenu, RoundMenu, MenuAnimationType, IndicatorMenuItemDelegate
16
+ from .scroll_bar import SmoothScrollDelegate
17
+
18
+
19
+ class LineEditButton(QToolButton):
20
+ """ Line edit button """
21
+
22
+ def __init__(self, icon: Union[str, QIcon, FluentIconBase], parent=None):
23
+ super().__init__(parent=parent)
24
+ self._icon = icon
25
+ self._action = None
26
+ self.isPressed = False
27
+ self.setFixedSize(31, 23)
28
+ self.setIconSize(QSize(10, 10))
29
+ self.setCursor(Qt.CursorShape.PointingHandCursor)
30
+ self.setObjectName('lineEditButton')
31
+ FluentStyleSheet.LINE_EDIT.apply(self)
32
+
33
+ def setAction(self, action: QAction):
34
+ self._action = action
35
+ self._onActionChanged()
36
+
37
+ self.clicked.connect(action.trigger)
38
+ action.toggled.connect(self.setChecked)
39
+ action.changed.connect(self._onActionChanged)
40
+
41
+ self.installEventFilter(ToolTipFilter(self, 700))
42
+
43
+ def _onActionChanged(self):
44
+ action = self.action()
45
+ self.setIcon(action.icon())
46
+ self.setToolTip(action.toolTip())
47
+ self.setEnabled(action.isEnabled())
48
+ self.setCheckable(action.isCheckable())
49
+ self.setChecked(action.isChecked())
50
+
51
+ def action(self):
52
+ return self._action
53
+
54
+ def setIcon(self, icon: Union[str, FluentIconBase, QIcon]):
55
+ self._icon = icon
56
+ self.update()
57
+
58
+ def mousePressEvent(self, e):
59
+ self.isPressed = True
60
+ super().mousePressEvent(e)
61
+
62
+ def mouseReleaseEvent(self, e):
63
+ self.isPressed = False
64
+ super().mouseReleaseEvent(e)
65
+
66
+ def paintEvent(self, e):
67
+ super().paintEvent(e)
68
+ painter = QPainter(self)
69
+ painter.setRenderHints(QPainter.Antialiasing |
70
+ QPainter.SmoothPixmapTransform)
71
+
72
+ iw, ih = self.iconSize().width(), self.iconSize().height()
73
+ w, h = self.width(), self.height()
74
+ rect = QRectF((w - iw)/2, (h - ih)/2, iw, ih)
75
+
76
+ if self.isPressed:
77
+ painter.setOpacity(0.7)
78
+
79
+ if isDarkTheme():
80
+ drawIcon(self._icon, painter, rect)
81
+ else:
82
+ drawIcon(self._icon, painter, rect, fill='#656565')
83
+
84
+
85
+ class LineEdit(QLineEdit):
86
+ """ Line edit """
87
+
88
+ def __init__(self, parent=None):
89
+ super().__init__(parent=parent)
90
+ self._isClearButtonEnabled = False
91
+ self._completer = None # type: QCompleter
92
+ self._completerMenu = None # type: CompleterMenu
93
+ self._isError = False
94
+ self.lightFocusedBorderColor = QColor()
95
+ self.darkFocusedBorderColor = QColor()
96
+
97
+ self.leftButtons = [] # type: List[LineEditButton]
98
+ self.rightButtons = [] # type: List[LineEditButton]
99
+
100
+ self.setProperty("transparent", True)
101
+ FluentStyleSheet.LINE_EDIT.apply(self)
102
+ self.setFixedHeight(33)
103
+ self.setAttribute(Qt.WA_MacShowFocusRect, False)
104
+ setFont(self)
105
+
106
+ self.hBoxLayout = QHBoxLayout(self)
107
+ self.clearButton = LineEditButton(FIF.CLOSE, self)
108
+
109
+ self.clearButton.setFixedSize(29, 25)
110
+ self.clearButton.hide()
111
+
112
+ self.hBoxLayout.setSpacing(3)
113
+ self.hBoxLayout.setContentsMargins(4, 4, 4, 4)
114
+ self.hBoxLayout.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
115
+ self.hBoxLayout.addWidget(self.clearButton, 0, Qt.AlignRight)
116
+
117
+ self.clearButton.clicked.connect(self.clear)
118
+ self.textChanged.connect(self.__onTextChanged)
119
+ self.textEdited.connect(self.__onTextEdited)
120
+
121
+ def isError(self):
122
+ return self._isError
123
+
124
+ def setError(self, isError: bool):
125
+ """ set the error status """
126
+ if isError == self.isError():
127
+ return
128
+
129
+ self._isError = isError
130
+ self.update()
131
+
132
+ def setCustomFocusedBorderColor(self, light, dark):
133
+ """ set the border color in focused status
134
+
135
+ Parameters
136
+ ----------
137
+ light, dark: str | QColor | Qt.GlobalColor
138
+ border color in light/dark theme mode
139
+ """
140
+ self.lightFocusedBorderColor = QColor(light)
141
+ self.darkFocusedBorderColor = QColor(dark)
142
+ self.update()
143
+
144
+ def focusedBorderColor(self):
145
+ if self.isError():
146
+ return FluentSystemColor.CRITICAL_FOREGROUND.color()
147
+
148
+ return autoFallbackThemeColor(self.lightFocusedBorderColor, self.darkFocusedBorderColor)
149
+
150
+ def setClearButtonEnabled(self, enable: bool):
151
+ self._isClearButtonEnabled = enable
152
+ self._adjustTextMargins()
153
+
154
+ def isClearButtonEnabled(self) -> bool:
155
+ return self._isClearButtonEnabled
156
+
157
+ def setCompleter(self, completer: QCompleter):
158
+ self._completer = completer
159
+
160
+ def completer(self):
161
+ return self._completer
162
+
163
+ def addAction(self, action: QAction, position=QLineEdit.ActionPosition.TrailingPosition):
164
+ QWidget.addAction(self, action)
165
+
166
+ button = LineEditButton(action.icon())
167
+ button.setAction(action)
168
+ button.setFixedWidth(29)
169
+
170
+ if position == QLineEdit.ActionPosition.LeadingPosition:
171
+ self.hBoxLayout.insertWidget(len(self.leftButtons), button, 0, Qt.AlignLeading)
172
+ if not self.leftButtons:
173
+ self.hBoxLayout.insertStretch(1, 1)
174
+
175
+ self.leftButtons.append(button)
176
+ else:
177
+ self.rightButtons.append(button)
178
+ self.hBoxLayout.addWidget(button, 0, Qt.AlignRight)
179
+
180
+ self._adjustTextMargins()
181
+
182
+ def addActions(self, actions, position=QLineEdit.ActionPosition.TrailingPosition):
183
+ for action in actions:
184
+ self.addAction(action, position)
185
+
186
+ def _adjustTextMargins(self):
187
+ left = len(self.leftButtons) * 30
188
+ right = len(self.rightButtons) * 30 + 28 * self.isClearButtonEnabled()
189
+ m = self.textMargins()
190
+ self.setTextMargins(left, m.top(), right, m.bottom())
191
+
192
+ def focusOutEvent(self, e):
193
+ super().focusOutEvent(e)
194
+ self.clearButton.hide()
195
+
196
+ def focusInEvent(self, e):
197
+ super().focusInEvent(e)
198
+ if self.isClearButtonEnabled():
199
+ self.clearButton.setVisible(bool(self.text()))
200
+
201
+ def __onTextChanged(self, text):
202
+ """ text changed slot """
203
+ if self.isClearButtonEnabled():
204
+ self.clearButton.setVisible(bool(text) and self.hasFocus())
205
+
206
+ def __onTextEdited(self, text):
207
+ if not self.completer():
208
+ return
209
+
210
+ if self.text():
211
+ QTimer.singleShot(50, self._showCompleterMenu)
212
+ elif self._completerMenu:
213
+ self._completerMenu.close()
214
+
215
+ def setCompleterMenu(self, menu):
216
+ """ set completer menu
217
+
218
+ Parameters
219
+ ----------
220
+ menu: CompleterMenu
221
+ completer menu
222
+ """
223
+ menu.activated.connect(self._completer.activated)
224
+ menu.indexActivated.connect(lambda idx: self._completer.activated[QModelIndex].emit(idx))
225
+ self._completerMenu = menu
226
+
227
+ def _showCompleterMenu(self):
228
+ if not self.completer() or not self.text():
229
+ return
230
+
231
+ # create menu
232
+ if not self._completerMenu:
233
+ self.setCompleterMenu(CompleterMenu(self))
234
+
235
+ # add menu items
236
+ self.completer().setCompletionPrefix(self.text())
237
+ changed = self._completerMenu.setCompletion(self.completer().completionModel(), self.completer().completionColumn())
238
+ self._completerMenu.setMaxVisibleItems(self.completer().maxVisibleItems())
239
+
240
+ # show menu
241
+ if changed:
242
+ self._completerMenu.popup()
243
+
244
+ def contextMenuEvent(self, e):
245
+ menu = LineEditMenu(self)
246
+ menu.exec(e.globalPos(), ani=True)
247
+
248
+ def paintEvent(self, e):
249
+ super().paintEvent(e)
250
+ if not self.hasFocus():
251
+ return
252
+
253
+ painter = QPainter(self)
254
+ painter.setRenderHints(QPainter.Antialiasing)
255
+ painter.setPen(Qt.NoPen)
256
+
257
+ m = self.contentsMargins()
258
+ path = QPainterPath()
259
+ w, h = self.width()-m.left()-m.right(), self.height()
260
+ path.addRoundedRect(QRectF(m.left(), h-10, w, 10), 5, 5)
261
+
262
+ rectPath = QPainterPath()
263
+ rectPath.addRect(m.left(), h-10, w, 8)
264
+ path = path.subtracted(rectPath)
265
+
266
+ painter.fillPath(path, self.focusedBorderColor())
267
+
268
+
269
+ class CompleterMenu(RoundMenu):
270
+ """ Completer menu """
271
+
272
+ activated = Signal(str)
273
+ indexActivated = Signal(QModelIndex)
274
+
275
+ def __init__(self, lineEdit: LineEdit):
276
+ super().__init__()
277
+ self.items = []
278
+ self.indexes = []
279
+ self.lineEdit = lineEdit
280
+
281
+ self.view.setViewportMargins(0, 2, 0, 6)
282
+ self.view.setObjectName('completerListWidget')
283
+ self.view.setItemDelegate(IndicatorMenuItemDelegate())
284
+ self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
285
+
286
+ self.installEventFilter(self)
287
+ self.setItemHeight(33)
288
+
289
+ def setCompletion(self, model: QAbstractItemModel, column=0):
290
+ """ set the completion model """
291
+ items = []
292
+ self.indexes.clear()
293
+ for i in range(model.rowCount()):
294
+ items.append(model.data(model.index(i, column)))
295
+ self.indexes.append(model.index(i, column))
296
+
297
+ if self.items == items and self.isVisible():
298
+ return False
299
+
300
+ self.setItems(items)
301
+ return True
302
+
303
+ def setItems(self, items: List[str]):
304
+ """ set completion items """
305
+ self.view.clear()
306
+
307
+ self.items = items
308
+ self.view.addItems(items)
309
+
310
+ for i in range(self.view.count()):
311
+ item = self.view.item(i)
312
+ item.setSizeHint(QSize(1, self.itemHeight))
313
+
314
+ def _onItemClicked(self, item):
315
+ self._hideMenu(False)
316
+ self._onCompletionItemSelected(item.text(), self.view.row(item))
317
+
318
+ def eventFilter(self, obj, e: QEvent):
319
+ if e.type() != QEvent.KeyPress:
320
+ return super().eventFilter(obj, e)
321
+
322
+ # redirect input to line edit
323
+ self.lineEdit.event(e)
324
+ self.view.event(e)
325
+
326
+ if e.key() == Qt.Key_Escape:
327
+ self.close()
328
+ if e.key() in [Qt.Key_Enter, Qt.Key_Return] and self.view.currentRow() >= 0:
329
+ self._onCompletionItemSelected(self.view.currentItem().text(), self.view.currentRow())
330
+ self.close()
331
+
332
+ return super().eventFilter(obj, e)
333
+
334
+ def _onCompletionItemSelected(self, text, row):
335
+ self.lineEdit.setText(text)
336
+ self.activated.emit(text)
337
+
338
+ if 0 <= row < len(self.indexes):
339
+ self.indexActivated.emit(self.indexes[row])
340
+
341
+ def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN):
342
+ return super().exec(pos, ani, aniType)
343
+
344
+ def popup(self):
345
+ """ show menu """
346
+ if not self.items:
347
+ return self.close()
348
+
349
+ # adjust menu size
350
+ p = self.lineEdit
351
+ if self.view.width() < p.width():
352
+ self.view.setMinimumWidth(p.width())
353
+ self.adjustSize()
354
+
355
+ # determine the animation type by choosing the maximum height of view
356
+ x = -self.width()//2 + self.layout().contentsMargins().left() + p.width()//2
357
+ y = p.height() - self.layout().contentsMargins().top() + 2
358
+ pd = p.mapToGlobal(QPoint(x, y))
359
+ hd = self.view.heightForAnimation(pd, MenuAnimationType.FADE_IN_DROP_DOWN)
360
+
361
+ pu = p.mapToGlobal(QPoint(x, 7))
362
+ hu = self.view.heightForAnimation(pu, MenuAnimationType.FADE_IN_PULL_UP)
363
+
364
+ if hd >= hu:
365
+ pos = pd
366
+ aniType = MenuAnimationType.FADE_IN_DROP_DOWN
367
+ else:
368
+ pos = pu
369
+ aniType = MenuAnimationType.FADE_IN_PULL_UP
370
+
371
+ self.view.adjustSize(pos, aniType)
372
+
373
+ # update border style
374
+ self.view.setProperty('dropDown', aniType == MenuAnimationType.FADE_IN_DROP_DOWN)
375
+ self.view.setStyle(QApplication.style())
376
+
377
+ self.adjustSize()
378
+ self.exec(pos, aniType=aniType)
379
+
380
+ # remove the focus of menu
381
+ self.view.setFocusPolicy(Qt.NoFocus)
382
+ self.setFocusPolicy(Qt.NoFocus)
383
+ p.setFocus()
384
+
385
+
386
+ class SearchLineEdit(LineEdit):
387
+ """ Search line edit """
388
+
389
+ searchSignal = Signal(str)
390
+ clearSignal = Signal()
391
+
392
+ def __init__(self, parent=None):
393
+ super().__init__(parent)
394
+ self.searchButton = LineEditButton(FIF.SEARCH, self)
395
+
396
+ self.hBoxLayout.addWidget(self.searchButton, 0, Qt.AlignRight)
397
+ self.setClearButtonEnabled(True)
398
+ self.setTextMargins(0, 0, 59, 0)
399
+
400
+ self.searchButton.clicked.connect(self.search)
401
+ self.clearButton.clicked.connect(self.clearSignal)
402
+
403
+ def search(self):
404
+ """ emit search signal """
405
+ text = self.text().strip()
406
+ if text:
407
+ self.searchSignal.emit(text)
408
+ else:
409
+ self.clearSignal.emit()
410
+
411
+ def setClearButtonEnabled(self, enable: bool):
412
+ self._isClearButtonEnabled = enable
413
+ self.setTextMargins(0, 0, 28*enable+30, 0)
414
+
415
+
416
+ class EditLayer(QWidget):
417
+ """ Edit layer """
418
+
419
+ def __init__(self, parent):
420
+ super().__init__(parent=parent)
421
+ self.setAttribute(Qt.WA_TransparentForMouseEvents)
422
+ parent.installEventFilter(self)
423
+
424
+ def eventFilter(self, obj, e):
425
+ if obj is self.parent() and e.type() == QEvent.Resize:
426
+ self.resize(e.size())
427
+
428
+ return super().eventFilter(obj, e)
429
+
430
+ def paintEvent(self, e):
431
+ if not self.parent().hasFocus():
432
+ return
433
+
434
+ painter = QPainter(self)
435
+ painter.setRenderHints(QPainter.Antialiasing)
436
+ painter.setPen(Qt.NoPen)
437
+
438
+ m = self.contentsMargins()
439
+ path = QPainterPath()
440
+ w, h = self.width()-m.left()-m.right(), self.height()
441
+ path.addRoundedRect(QRectF(m.left(), h-10, w, 10), 5, 5)
442
+
443
+ rectPath = QPainterPath()
444
+ rectPath.addRect(m.left(), h-10, w, 7.5)
445
+ path = path.subtracted(rectPath)
446
+
447
+ painter.fillPath(path, themeColor())
448
+
449
+
450
+ class TextEdit(QTextEdit):
451
+ """ Text edit """
452
+
453
+ def __init__(self, parent=None):
454
+ super().__init__(parent=parent)
455
+ self.layer = EditLayer(self)
456
+ self.scrollDelegate = SmoothScrollDelegate(self)
457
+ FluentStyleSheet.LINE_EDIT.apply(self)
458
+ setFont(self)
459
+
460
+ def contextMenuEvent(self, e):
461
+ menu = TextEditMenu(self)
462
+ menu.exec(e.globalPos(), ani=True)
463
+
464
+
465
+ class PlainTextEdit(QPlainTextEdit):
466
+ """ Plain text edit """
467
+
468
+ def __init__(self, parent=None):
469
+ super().__init__(parent=parent)
470
+ self.layer = EditLayer(self)
471
+ self.scrollDelegate = SmoothScrollDelegate(self)
472
+ FluentStyleSheet.LINE_EDIT.apply(self)
473
+ setFont(self)
474
+
475
+ def contextMenuEvent(self, e):
476
+ menu = TextEditMenu(self)
477
+ menu.exec(e.globalPos())
478
+
479
+
480
+ class TextBrowser(QTextBrowser):
481
+ """ Text browser """
482
+
483
+ def __init__(self, parent=None):
484
+ super().__init__(parent)
485
+ self.layer = EditLayer(self)
486
+ self.scrollDelegate = SmoothScrollDelegate(self)
487
+ FluentStyleSheet.LINE_EDIT.apply(self)
488
+ setFont(self)
489
+
490
+ def contextMenuEvent(self, e):
491
+ menu = TextEditMenu(self)
492
+ menu.exec(e.globalPos())
493
+
494
+
495
+ class PasswordLineEdit(LineEdit):
496
+ """ Password line edit """
497
+
498
+ def __init__(self, parent=None):
499
+ super().__init__(parent)
500
+ self.viewButton = LineEditButton(FIF.VIEW, self)
501
+
502
+ self.setEchoMode(QLineEdit.Password)
503
+ self.setContextMenuPolicy(Qt.NoContextMenu)
504
+ self.hBoxLayout.addWidget(self.viewButton, 0, Qt.AlignRight)
505
+ self.setClearButtonEnabled(False)
506
+
507
+ self.viewButton.installEventFilter(self)
508
+ self.viewButton.setIconSize(QSize(13, 13))
509
+ self.viewButton.setFixedSize(29, 25)
510
+
511
+ def setPasswordVisible(self, isVisible: bool):
512
+ """ set the visibility of password """
513
+ if isVisible:
514
+ self.setEchoMode(QLineEdit.Normal)
515
+ else:
516
+ self.setEchoMode(QLineEdit.Password)
517
+
518
+ def isPasswordVisible(self):
519
+ return self.echoMode() == QLineEdit.Normal
520
+
521
+ def setClearButtonEnabled(self, enable: bool):
522
+ self._isClearButtonEnabled = enable
523
+
524
+ if self.viewButton.isHidden():
525
+ self.setTextMargins(0, 0, 28*enable, 0)
526
+ else:
527
+ self.setTextMargins(0, 0, 28*enable + 30, 0)
528
+
529
+ def setViewPasswordButtonVisible(self, isVisible: bool):
530
+ """ set the visibility of view password button """
531
+ self.viewButton.setVisible(isVisible)
532
+
533
+ def eventFilter(self, obj, e):
534
+ if obj is not self.viewButton or not self.isEnabled():
535
+ return super().eventFilter(obj, e)
536
+
537
+ if e.type() == QEvent.MouseButtonPress:
538
+ self.setPasswordVisible(True)
539
+ elif e.type() == QEvent.MouseButtonRelease:
540
+ self.setPasswordVisible(False)
541
+
542
+ return super().eventFilter(obj, e)
543
+
544
+ def inputMethodQuery(self, query: Qt.InputMethodQuery):
545
+ # Disable IME for PasswordLineEdit
546
+ if query == Qt.InputMethodQuery.ImEnabled:
547
+ return False
548
+ else:
549
+ return super().inputMethodQuery(query)
550
+
551
+ passwordVisible = Property(bool, isPasswordVisible, setPasswordVisible)
@@ -0,0 +1,158 @@
1
+ # coding:utf-8
2
+ from typing import List, Union
3
+
4
+ from PySide6.QtCore import Qt, QModelIndex, Property
5
+ from PySide6.QtGui import QPainter
6
+ from PySide6.QtWidgets import QStyleOptionViewItem, QListView, QListView, QListWidget, QWidget
7
+
8
+ from .scroll_bar import SmoothScrollDelegate
9
+ from .table_view import TableItemDelegate
10
+ from ...common.style_sheet import FluentStyleSheet, themeColor
11
+ from ...common.color import autoFallbackThemeColor
12
+
13
+
14
+ class ListItemDelegate(TableItemDelegate):
15
+ """ List item delegate """
16
+
17
+ def __init__(self, parent: QListView):
18
+ super().__init__(parent)
19
+
20
+ def _drawBackground(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex):
21
+ painter.drawRoundedRect(option.rect, 5, 5)
22
+
23
+ def _drawIndicator(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex):
24
+ y, h = option.rect.y(), option.rect.height()
25
+ ph = round(0.35*h if self.pressedRow == index.row() else 0.257*h)
26
+ painter.setBrush(autoFallbackThemeColor(self.lightCheckedColor, self.darkCheckedColor))
27
+ painter.drawRoundedRect(0, ph + y, 3, h - 2*ph, 1.5, 1.5)
28
+
29
+
30
+ class ListBase:
31
+
32
+ def __init__(self, *args, **kwargs):
33
+ super().__init__(*args, **kwargs)
34
+ self.delegate = ListItemDelegate(self)
35
+ self.scrollDelegate = SmoothScrollDelegate(self)
36
+ self._isSelectRightClickedRow = False
37
+
38
+ FluentStyleSheet.LIST_VIEW.apply(self)
39
+ self.setItemDelegate(self.delegate)
40
+ self.setMouseTracking(True)
41
+
42
+ self.entered.connect(lambda i: self._setHoverRow(i.row()))
43
+ self.pressed.connect(lambda i: self._setPressedRow(i.row()))
44
+
45
+ def _setHoverRow(self, row: int):
46
+ """ set hovered row """
47
+ self.delegate.setHoverRow(row)
48
+ self.viewport().update()
49
+
50
+ def _setPressedRow(self, row: int):
51
+ """ set pressed row """
52
+ if self.selectionMode() == QListView.SelectionMode.NoSelection:
53
+ return
54
+
55
+ self.delegate.setPressedRow(row)
56
+ self.viewport().update()
57
+
58
+ def _setSelectedRows(self, indexes: List[QModelIndex]):
59
+ if self.selectionMode() == QListView.SelectionMode.NoSelection:
60
+ return
61
+
62
+ self.delegate.setSelectedRows(indexes)
63
+ self.viewport().update()
64
+
65
+ def leaveEvent(self, e):
66
+ QListView.leaveEvent(self, e)
67
+ self._setHoverRow(-1)
68
+
69
+ def resizeEvent(self, e):
70
+ QListView.resizeEvent(self, e)
71
+ self.viewport().update()
72
+
73
+ def keyPressEvent(self, e):
74
+ QListView.keyPressEvent(self, e)
75
+ self.updateSelectedRows()
76
+
77
+ def mousePressEvent(self, e):
78
+ if e.button() == Qt.LeftButton or self._isSelectRightClickedRow:
79
+ return QListView.mousePressEvent(self, e)
80
+
81
+ index = self.indexAt(e.pos())
82
+ if index.isValid():
83
+ self._setPressedRow(index.row())
84
+
85
+ QWidget.mousePressEvent(self, e)
86
+
87
+ def mouseReleaseEvent(self, e):
88
+ QListView.mouseReleaseEvent(self, e)
89
+ self.updateSelectedRows()
90
+
91
+ if self.indexAt(e.pos()).row() < 0 or e.button() == Qt.RightButton:
92
+ self._setPressedRow(-1)
93
+
94
+ def setItemDelegate(self, delegate: ListItemDelegate):
95
+ self.delegate = delegate
96
+ super().setItemDelegate(delegate)
97
+
98
+ def clearSelection(self):
99
+ QListView.clearSelection(self)
100
+ self.updateSelectedRows()
101
+
102
+ def setCurrentIndex(self, index: QModelIndex):
103
+ QListView.setCurrentIndex(self, index)
104
+ self.updateSelectedRows()
105
+
106
+ def updateSelectedRows(self):
107
+ self._setSelectedRows(self.selectedIndexes())
108
+
109
+ def setCheckedColor(self, light, dark):
110
+ """ set the color in checked status
111
+
112
+ Parameters
113
+ ----------
114
+ light, dark: str | QColor | Qt.GlobalColor
115
+ color in light/dark theme mode
116
+ """
117
+ self.delegate.setCheckedColor(light, dark)
118
+
119
+
120
+ class ListWidget(ListBase, QListWidget):
121
+ """ List widget """
122
+
123
+ def __init__(self, parent=None):
124
+ super().__init__(parent)
125
+
126
+ def setCurrentItem(self, item, command=None):
127
+ self.setCurrentRow(self.row(item), command)
128
+
129
+ def setCurrentRow(self, row: int, command=None):
130
+ if not command:
131
+ super().setCurrentRow(row)
132
+ else:
133
+ super().setCurrentRow(row, command)
134
+
135
+ self.updateSelectedRows()
136
+
137
+ def isSelectRightClickedRow(self):
138
+ return self._isSelectRightClickedRow
139
+
140
+ def setSelectRightClickedRow(self, isSelect: bool):
141
+ self._isSelectRightClickedRow = isSelect
142
+
143
+ selectRightClickedRow = Property(bool, isSelectRightClickedRow, setSelectRightClickedRow)
144
+
145
+
146
+ class ListView(ListBase, QListView):
147
+ """ List view """
148
+
149
+ def __init__(self, parent=None):
150
+ super().__init__(parent)
151
+
152
+ def isSelectRightClickedRow(self):
153
+ return self._isSelectRightClickedRow
154
+
155
+ def setSelectRightClickedRow(self, isSelect: bool):
156
+ self._isSelectRightClickedRow = isSelect
157
+
158
+ selectRightClickedRow = Property(bool, isSelectRightClickedRow, setSelectRightClickedRow)