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,521 @@
1
+ # coding:utf-8
2
+ from enum import Enum
3
+ import sys
4
+ from typing import Union
5
+
6
+ from PySide6.QtCore import (Qt, QPropertyAnimation, QPoint, QParallelAnimationGroup, QEasingCurve, QMargins,
7
+ QRectF, QObject, QSize, Signal, QEvent)
8
+ from PySide6.QtGui import QPixmap, QPainter, QColor, QCursor, QIcon, QImage, QPainterPath, QBrush, QMovie, QImageReader
9
+ from PySide6.QtWidgets import QWidget, QGraphicsDropShadowEffect, QLabel, QHBoxLayout, QVBoxLayout, QApplication
10
+
11
+ from ...common.auto_wrap import TextWrap
12
+ from ...common.style_sheet import isDarkTheme, FluentStyleSheet
13
+ from ...common.icon import FluentIconBase, drawIcon, FluentIcon
14
+ from ...common.screen import getCurrentScreenGeometry
15
+ from .button import TransparentToolButton
16
+ from .label import ImageLabel
17
+
18
+
19
+ class FlyoutAnimationType(Enum):
20
+ """ Flyout animation type """
21
+ PULL_UP = 0
22
+ DROP_DOWN = 1
23
+ SLIDE_LEFT = 2
24
+ SLIDE_RIGHT = 3
25
+ FADE_IN = 4
26
+ NONE = 5
27
+
28
+
29
+ class IconWidget(QWidget):
30
+
31
+ def __init__(self, icon, parent=None):
32
+ super().__init__(parent=parent)
33
+ self.setFixedSize(36, 54)
34
+ self.icon = icon
35
+
36
+ def paintEvent(self, e):
37
+ if not self.icon:
38
+ return
39
+
40
+ painter = QPainter(self)
41
+ painter.setRenderHints(QPainter.Antialiasing |
42
+ QPainter.SmoothPixmapTransform)
43
+
44
+ rect = QRectF(8, (self.height()-20)/2, 20, 20)
45
+ drawIcon(self.icon, painter, rect)
46
+
47
+
48
+ class FlyoutViewBase(QWidget):
49
+ """ Flyout view base class """
50
+
51
+ def __init__(self, parent=None):
52
+ super().__init__(parent=parent)
53
+
54
+ def addWidget(self, widget: QWidget, stretch=0, align=Qt.AlignLeft):
55
+ raise NotImplementedError
56
+
57
+ def backgroundColor(self):
58
+ return QColor(40, 40, 40) if isDarkTheme() else QColor(248, 248, 248)
59
+
60
+ def borderColor(self):
61
+ return QColor(0, 0, 0, 45) if isDarkTheme() else QColor(0, 0, 0, 17)
62
+
63
+ def paintEvent(self, e):
64
+ painter = QPainter(self)
65
+ painter.setRenderHints(QPainter.Antialiasing)
66
+
67
+ painter.setBrush(self.backgroundColor())
68
+ painter.setPen(self.borderColor())
69
+
70
+ rect = self.rect().adjusted(1, 1, -1, -1)
71
+ painter.drawRoundedRect(rect, 8, 8)
72
+
73
+
74
+ class FlyoutView(FlyoutViewBase):
75
+ """ Flyout view """
76
+
77
+ closed = Signal()
78
+
79
+ def __init__(self, title: str, content: str, icon: Union[FluentIconBase, QIcon, str] = None,
80
+ image: Union[str, QPixmap, QImage] = None, isClosable=False, parent=None):
81
+ super().__init__(parent=parent)
82
+ """
83
+ Parameters
84
+ ----------
85
+ title: str
86
+ the title of teaching tip
87
+
88
+ content: str
89
+ the content of teaching tip
90
+
91
+ icon: InfoBarIcon | FluentIconBase | QIcon | str
92
+ the icon of teaching tip
93
+
94
+ image: str | QPixmap | QImage
95
+ the image of teaching tip
96
+
97
+ isClosable: bool
98
+ whether to show the close button
99
+
100
+ parent: QWidget
101
+ parent widget
102
+ """
103
+ self.icon = icon
104
+ self.title = title
105
+ self.image = image
106
+ self.content = content
107
+ self.isClosable = isClosable
108
+
109
+ self.vBoxLayout = QVBoxLayout(self)
110
+ self.viewLayout = QHBoxLayout()
111
+ self.widgetLayout = QVBoxLayout()
112
+
113
+ self.titleLabel = QLabel(title, self)
114
+ self.contentLabel = QLabel(content, self)
115
+ self.iconWidget = IconWidget(icon, self)
116
+ self.imageLabel = ImageLabel(self)
117
+ self.closeButton = TransparentToolButton(FluentIcon.CLOSE, self)
118
+
119
+ self.__initWidgets()
120
+
121
+ def __initWidgets(self):
122
+ self.imageLabel.setImage(self.image)
123
+
124
+ self.closeButton.setFixedSize(32, 32)
125
+ self.closeButton.setIconSize(QSize(12, 12))
126
+ self.closeButton.setVisible(self.isClosable)
127
+ self.titleLabel.setVisible(bool(self.title))
128
+ self.contentLabel.setVisible(bool(self.content))
129
+ self.iconWidget.setHidden(self.icon is None)
130
+
131
+ self.closeButton.clicked.connect(self.closed)
132
+
133
+ self.titleLabel.setObjectName('titleLabel')
134
+ self.contentLabel.setObjectName('contentLabel')
135
+ FluentStyleSheet.TEACHING_TIP.apply(self)
136
+
137
+ self.__initLayout()
138
+
139
+ def __initLayout(self):
140
+ self.vBoxLayout.setContentsMargins(1, 1, 1, 1)
141
+ self.widgetLayout.setContentsMargins(0, 8, 0, 8)
142
+ self.viewLayout.setSpacing(4)
143
+ self.widgetLayout.setSpacing(0)
144
+ self.vBoxLayout.setSpacing(0)
145
+
146
+ # add icon widget
147
+ if not self.title or not self.content:
148
+ self.iconWidget.setFixedHeight(36)
149
+
150
+ self.vBoxLayout.addLayout(self.viewLayout)
151
+ self.viewLayout.addWidget(self.iconWidget, 0, Qt.AlignTop)
152
+
153
+ # add text
154
+ self._adjustText()
155
+ self.widgetLayout.addWidget(self.titleLabel)
156
+ self.widgetLayout.addWidget(self.contentLabel)
157
+ self.viewLayout.addLayout(self.widgetLayout)
158
+
159
+ # add close button
160
+ self.closeButton.setVisible(self.isClosable)
161
+ self.viewLayout.addWidget(
162
+ self.closeButton, 0, Qt.AlignRight | Qt.AlignTop)
163
+
164
+ # adjust content margins
165
+ margins = QMargins(6, 5, 6, 5)
166
+ margins.setLeft(20 if not self.icon else 5)
167
+ margins.setRight(20 if not self.isClosable else 6)
168
+ self.viewLayout.setContentsMargins(margins)
169
+
170
+ # add image
171
+ self._adjustImage()
172
+ self._addImageToLayout()
173
+
174
+ def addWidget(self, widget: QWidget, stretch=0, align=Qt.AlignLeft):
175
+ """ add widget to view """
176
+ self.widgetLayout.addSpacing(8)
177
+ self.widgetLayout.addWidget(widget, stretch, align)
178
+
179
+ def _addImageToLayout(self):
180
+ self.imageLabel.setBorderRadius(8, 8, 0, 0)
181
+ self.imageLabel.setHidden(self.imageLabel.isNull())
182
+ self.vBoxLayout.insertWidget(0, self.imageLabel)
183
+
184
+ def _adjustText(self):
185
+ w = min(900, QApplication.screenAt(
186
+ QCursor.pos()).geometry().width() - 200)
187
+
188
+ # adjust title
189
+ chars = max(min(w / 10, 120), 30)
190
+ self.titleLabel.setText(TextWrap.wrap(self.title, chars, False)[0])
191
+
192
+ # adjust content
193
+ chars = max(min(w / 9, 120), 30)
194
+ self.contentLabel.setText(TextWrap.wrap(self.content, chars, False)[0])
195
+
196
+ def _adjustImage(self):
197
+ w = self.vBoxLayout.sizeHint().width() - 2
198
+ self.imageLabel.scaledToWidth(w)
199
+
200
+ def showEvent(self, e):
201
+ super().showEvent(e)
202
+ self._adjustImage()
203
+ self.adjustSize()
204
+
205
+
206
+ class Flyout(QWidget):
207
+ """ Flyout """
208
+
209
+ closed = Signal()
210
+
211
+ def __init__(self, view: FlyoutViewBase, parent=None, isDeleteOnClose=True, isMacInputMethodEnabled=False):
212
+ super().__init__(parent=parent)
213
+ self.view = view
214
+ self.hBoxLayout = QHBoxLayout(self)
215
+ self.aniManager = None # type: FlyoutAnimationManager
216
+ self.isDeleteOnClose = isDeleteOnClose
217
+ self.isMacInputMethodEnabled = isMacInputMethodEnabled
218
+
219
+ self.hBoxLayout.setContentsMargins(15, 8, 15, 20)
220
+ self.hBoxLayout.addWidget(self.view)
221
+ self.setShadowEffect()
222
+
223
+ self.setAttribute(Qt.WA_TranslucentBackground)
224
+
225
+ if sys.platform != "darwin" or not isMacInputMethodEnabled:
226
+ self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint |
227
+ Qt.NoDropShadowWindowHint)
228
+ else:
229
+ self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint)
230
+ QApplication.instance().installEventFilter(self)
231
+
232
+ def eventFilter(self, watched, event):
233
+ if sys.platform == "darwin" and self.isMacInputMethodEnabled:
234
+ if self.isVisible() and event.type() == QEvent.MouseButtonPress:
235
+ if not self.rect().contains(self.mapFromGlobal(event.globalPos())):
236
+ self.close()
237
+
238
+ return super().eventFilter(watched, event)
239
+
240
+ def setShadowEffect(self, blurRadius=35, offset=(0, 8)):
241
+ """ add shadow to dialog """
242
+ color = QColor(0, 0, 0, 80 if isDarkTheme() else 30)
243
+ self.shadowEffect = QGraphicsDropShadowEffect(self.view)
244
+ self.shadowEffect.setBlurRadius(blurRadius)
245
+ self.shadowEffect.setOffset(*offset)
246
+ self.shadowEffect.setColor(color)
247
+ self.view.setGraphicsEffect(None)
248
+ self.view.setGraphicsEffect(self.shadowEffect)
249
+
250
+ def closeEvent(self, e):
251
+ if self.isDeleteOnClose:
252
+ self.deleteLater()
253
+
254
+ super().closeEvent(e)
255
+ self.closed.emit()
256
+
257
+ def showEvent(self, e):
258
+ # fixes #780
259
+ self.activateWindow()
260
+ super().showEvent(e)
261
+
262
+ def exec(self, pos: QPoint, aniType=FlyoutAnimationType.PULL_UP):
263
+ """ show calendar view """
264
+ self.aniManager = FlyoutAnimationManager.make(aniType, self)
265
+ self.show()
266
+ self.aniManager.exec(pos)
267
+
268
+ @classmethod
269
+ def make(cls, view: FlyoutViewBase, target: Union[QWidget, QPoint] = None, parent=None,
270
+ aniType=FlyoutAnimationType.PULL_UP, isDeleteOnClose=True, isMacInputMethodEnabled=False):
271
+ """ create and show a flyout
272
+
273
+ Parameters
274
+ ----------
275
+ view: FlyoutViewBase
276
+ flyout view
277
+
278
+ target: QWidget | QPoint
279
+ the target widget or position to show flyout
280
+
281
+ parent: QWidget
282
+ parent window
283
+
284
+ aniType: FlyoutAnimationType
285
+ flyout animation type
286
+
287
+ isDeleteOnClose: bool
288
+ whether delete flyout automatically when flyout is closed
289
+ """
290
+ w = cls(view, parent, isDeleteOnClose, isMacInputMethodEnabled)
291
+
292
+ if target is None:
293
+ return w
294
+
295
+ # show flyout first so that we can get the correct size
296
+ w.show()
297
+
298
+ # move flyout to the top of target
299
+ if isinstance(target, QWidget):
300
+ target = FlyoutAnimationManager.make(aniType, w).position(target)
301
+
302
+ w.exec(target, aniType)
303
+ return w
304
+
305
+ @classmethod
306
+ def create(cls, title: str, content: str, icon: Union[FluentIconBase, QIcon, str] = None,
307
+ image: Union[str, QPixmap, QImage] = None, isClosable=False, target: Union[QWidget, QPoint] = None,
308
+ parent=None, aniType=FlyoutAnimationType.PULL_UP, isDeleteOnClose=True, isMacInputMethodEnabled=False):
309
+ """ create and show a flyout using the default view
310
+
311
+ Parameters
312
+ ----------
313
+ title: str
314
+ the title of teaching tip
315
+
316
+ content: str
317
+ the content of teaching tip
318
+
319
+ icon: InfoBarIcon | FluentIconBase | QIcon | str
320
+ the icon of teaching tip
321
+
322
+ image: str | QPixmap | QImage
323
+ the image of teaching tip
324
+
325
+ isClosable: bool
326
+ whether to show the close button
327
+
328
+ target: QWidget | QPoint
329
+ the target widget or position to show flyout
330
+
331
+ parent: QWidget
332
+ parent window
333
+
334
+ aniType: FlyoutAnimationType
335
+ flyout animation type
336
+
337
+ isDeleteOnClose: bool
338
+ whether delete flyout automatically when flyout is closed
339
+ """
340
+ view = FlyoutView(title, content, icon, image, isClosable)
341
+ w = cls.make(view, target, parent, aniType, isDeleteOnClose, isMacInputMethodEnabled)
342
+ view.closed.connect(w.close)
343
+ return w
344
+
345
+ def fadeOut(self):
346
+ self.fadeOutAni = QPropertyAnimation(self, b'windowOpacity', self)
347
+ self.fadeOutAni.finished.connect(self.close)
348
+ self.fadeOutAni.setStartValue(1)
349
+ self.fadeOutAni.setEndValue(0)
350
+ self.fadeOutAni.setDuration(120)
351
+ self.fadeOutAni.start()
352
+
353
+
354
+ class FlyoutAnimationManager(QObject):
355
+ """ Flyout animation manager """
356
+
357
+ managers = {}
358
+
359
+ def __init__(self, flyout: Flyout):
360
+ super().__init__()
361
+ self.flyout = flyout
362
+ self.aniGroup = QParallelAnimationGroup(self)
363
+ self.slideAni = QPropertyAnimation(flyout, b'pos', self)
364
+ self.opacityAni = QPropertyAnimation(flyout, b'windowOpacity', self)
365
+
366
+ self.slideAni.setDuration(187)
367
+ self.opacityAni.setDuration(187)
368
+
369
+ self.opacityAni.setStartValue(0)
370
+ self.opacityAni.setEndValue(1)
371
+
372
+ self.slideAni.setEasingCurve(QEasingCurve.OutQuad)
373
+ self.opacityAni.setEasingCurve(QEasingCurve.OutQuad)
374
+ self.aniGroup.addAnimation(self.slideAni)
375
+ self.aniGroup.addAnimation(self.opacityAni)
376
+
377
+ @classmethod
378
+ def register(cls, name):
379
+ """ register menu animation manager
380
+
381
+ Parameters
382
+ ----------
383
+ name: Any
384
+ the name of manager, it should be unique
385
+ """
386
+ def wrapper(Manager):
387
+ if name not in cls.managers:
388
+ cls.managers[name] = Manager
389
+
390
+ return Manager
391
+
392
+ return wrapper
393
+
394
+ def exec(self, pos: QPoint):
395
+ """ start animation """
396
+ raise NotImplementedError
397
+
398
+ def _adjustPosition(self, pos):
399
+ rect = getCurrentScreenGeometry()
400
+ w, h = self.flyout.sizeHint().width() + 5, self.flyout.sizeHint().height()
401
+ x = max(rect.left(), min(pos.x(), rect.right() - w))
402
+ y = max(rect.top(), min(pos.y() - 4, rect.bottom() - h + 5))
403
+ return QPoint(x, y)
404
+
405
+ def position(self, target: QWidget):
406
+ """ return the top left position relative to the target """
407
+ raise NotImplementedError
408
+
409
+ @classmethod
410
+ def make(cls, aniType: FlyoutAnimationType, flyout: Flyout) -> "FlyoutAnimationManager":
411
+ """ mask animation manager """
412
+ if aniType not in cls.managers:
413
+ raise ValueError(f'`{aniType}` is an invalid animation type.')
414
+
415
+ return cls.managers[aniType](flyout)
416
+
417
+
418
+ @FlyoutAnimationManager.register(FlyoutAnimationType.PULL_UP)
419
+ class PullUpFlyoutAnimationManager(FlyoutAnimationManager):
420
+ """ Pull up flyout animation manager """
421
+
422
+ def position(self, target: QWidget):
423
+ w = self.flyout
424
+ pos = target.mapToGlobal(QPoint())
425
+ x = pos.x() + target.width()//2 - w.sizeHint().width()//2
426
+ y = pos.y() - w.sizeHint().height() + w.layout().contentsMargins().bottom()
427
+ return QPoint(x, y)
428
+
429
+ def exec(self, pos: QPoint):
430
+ pos = self._adjustPosition(pos)
431
+ self.slideAni.setStartValue(pos+QPoint(0, 8))
432
+ self.slideAni.setEndValue(pos)
433
+ self.aniGroup.start()
434
+
435
+
436
+ @FlyoutAnimationManager.register(FlyoutAnimationType.DROP_DOWN)
437
+ class DropDownFlyoutAnimationManager(FlyoutAnimationManager):
438
+ """ Drop down flyout animation manager """
439
+
440
+ def position(self, target: QWidget):
441
+ w = self.flyout
442
+ pos = target.mapToGlobal(QPoint(0, target.height()))
443
+ x = pos.x() + target.width()//2 - w.sizeHint().width()//2
444
+ y = pos.y() - w.layout().contentsMargins().top() + 8
445
+ return QPoint(x, y)
446
+
447
+ def exec(self, pos: QPoint):
448
+ pos = self._adjustPosition(pos)
449
+ self.slideAni.setStartValue(pos-QPoint(0, 8))
450
+ self.slideAni.setEndValue(pos)
451
+ self.aniGroup.start()
452
+
453
+
454
+ @FlyoutAnimationManager.register(FlyoutAnimationType.SLIDE_LEFT)
455
+ class SlideLeftFlyoutAnimationManager(FlyoutAnimationManager):
456
+ """ Slide left flyout animation manager """
457
+
458
+ def position(self, target: QWidget):
459
+ w = self.flyout
460
+ pos = target.mapToGlobal(QPoint(0, 0))
461
+ x = pos.x() - w.sizeHint().width() + 8
462
+ y = pos.y() - w.sizeHint().height()//2 + target.height()//2 + \
463
+ w.layout().contentsMargins().top()
464
+ return QPoint(x, y)
465
+
466
+ def exec(self, pos: QPoint):
467
+ pos = self._adjustPosition(pos)
468
+ self.slideAni.setStartValue(pos+QPoint(8, 0))
469
+ self.slideAni.setEndValue(pos)
470
+ self.aniGroup.start()
471
+
472
+
473
+ @FlyoutAnimationManager.register(FlyoutAnimationType.SLIDE_RIGHT)
474
+ class SlideRightFlyoutAnimationManager(FlyoutAnimationManager):
475
+ """ Slide right flyout animation manager """
476
+
477
+ def position(self, target: QWidget):
478
+ w = self.flyout
479
+ pos = target.mapToGlobal(QPoint(0, 0))
480
+ x = pos.x() + target.width() - 8
481
+ y = pos.y() - w.sizeHint().height()//2 + target.height()//2 + \
482
+ w.layout().contentsMargins().top()
483
+ return QPoint(x, y)
484
+
485
+ def exec(self, pos: QPoint):
486
+ pos = self._adjustPosition(pos)
487
+ self.slideAni.setStartValue(pos-QPoint(8, 0))
488
+ self.slideAni.setEndValue(pos)
489
+ self.aniGroup.start()
490
+
491
+
492
+ @FlyoutAnimationManager.register(FlyoutAnimationType.FADE_IN)
493
+ class FadeInFlyoutAnimationManager(FlyoutAnimationManager):
494
+ """ Fade in flyout animation manager """
495
+
496
+ def position(self, target: QWidget):
497
+ w = self.flyout
498
+ pos = target.mapToGlobal(QPoint())
499
+ x = pos.x() + target.width()//2 - w.sizeHint().width()//2
500
+ y = pos.y() - w.sizeHint().height() + w.layout().contentsMargins().bottom()
501
+ return QPoint(x, y)
502
+
503
+ def exec(self, pos: QPoint):
504
+ self.flyout.move(self._adjustPosition(pos))
505
+ self.aniGroup.removeAnimation(self.slideAni)
506
+ self.aniGroup.start()
507
+
508
+
509
+
510
+ @FlyoutAnimationManager.register(FlyoutAnimationType.NONE)
511
+ class DummyFlyoutAnimationManager(FlyoutAnimationManager):
512
+ """ Dummy flyout animation manager """
513
+
514
+ def exec(self, pos: QPoint):
515
+ """ start animation """
516
+ self.flyout.move(self._adjustPosition(pos))
517
+
518
+ def position(self, target: QWidget):
519
+ """ return the top left position relative to the target """
520
+ m = self.flyout.hBoxLayout.contentsMargins()
521
+ return target.mapToGlobal(QPoint(-m.left(), -self.flyout.sizeHint().height()+m.bottom()-8))
@@ -0,0 +1,49 @@
1
+ import sys
2
+
3
+ if sys.platform != "win32" or sys.getwindowsversion().build < 22000:
4
+ from fframelesswindow import FramelessWindow
5
+ else:
6
+ from ctypes.wintypes import MSG
7
+
8
+ import win32con
9
+ from PySide6.QtCore import QPoint, QEvent, Qt
10
+ from PySide6.QtGui import QCursor, QMouseEvent
11
+ from PySide6.QtWidgets import QApplication
12
+
13
+ from fframelesswindow import AcrylicWindow as Window
14
+ from fframelesswindow.titlebar.title_bar_buttons import TitleBarButtonState
15
+
16
+
17
+ class FramelessWindow(Window):
18
+ """ Frameless window """
19
+
20
+ def __init__(self, parent=None):
21
+ super().__init__(parent)
22
+ self.windowEffect.setMicaEffect(self.winId())
23
+
24
+ def nativeEvent(self, eventType, message):
25
+ """ Handle the Windows message """
26
+ msg = MSG.from_address(message.__int__())
27
+ if not msg.hWnd:
28
+ return super().nativeEvent(eventType, message)
29
+
30
+ if msg.message == win32con.WM_NCHITTEST and self._isResizeEnabled:
31
+ if self._isHoverMaxBtn():
32
+ self.titleBar.maxBtn.setState(TitleBarButtonState.HOVER)
33
+ return True, win32con.HTMAXBUTTON
34
+
35
+ elif msg.message in [0x2A2, win32con.WM_MOUSELEAVE]:
36
+ self.titleBar.maxBtn.setState(TitleBarButtonState.NORMAL)
37
+ elif msg.message in [win32con.WM_NCLBUTTONDOWN, win32con.WM_NCLBUTTONDBLCLK] and self._isHoverMaxBtn():
38
+ e = QMouseEvent(QEvent.MouseButtonPress, QPoint(), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
39
+ QApplication.sendEvent(self.titleBar.maxBtn, e)
40
+ return True, 0
41
+ elif msg.message in [win32con.WM_NCLBUTTONUP, win32con.WM_NCRBUTTONUP] and self._isHoverMaxBtn():
42
+ e = QMouseEvent(QEvent.MouseButtonRelease, QPoint(), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
43
+ QApplication.sendEvent(self.titleBar.maxBtn, e)
44
+
45
+ return super().nativeEvent(eventType, message)
46
+
47
+ def _isHoverMaxBtn(self):
48
+ pos = QCursor.pos() - self.geometry().topLeft() - self.titleBar.pos()
49
+ return self.titleBar.childAt(pos) is self.titleBar.maxBtn
@@ -0,0 +1,53 @@
1
+ # coding:utf-8
2
+ from typing import Union
3
+
4
+ from PySide6.QtCore import Property
5
+ from PySide6.QtGui import QIcon, QPainter
6
+ from PySide6.QtWidgets import QWidget
7
+
8
+ from ...common.icon import FluentIconBase, drawIcon, toQIcon
9
+ from ...common.overload import singledispatchmethod
10
+
11
+
12
+ class IconWidget(QWidget):
13
+ """ Icon widget
14
+
15
+ Constructors
16
+ ------------
17
+ * IconWidget(`parent`: QWidget = None)
18
+ * IconWidget(`icon`: QIcon | str | FluentIconBase, `parent`: QWidget = None)
19
+ """
20
+
21
+ @singledispatchmethod
22
+ def __init__(self, parent=None):
23
+ super().__init__(parent)
24
+ self.setIcon(QIcon())
25
+
26
+ @__init__.register
27
+ def _(self, icon: FluentIconBase, parent: QWidget = None):
28
+ self.__init__(parent)
29
+ self.setIcon(icon)
30
+
31
+ @__init__.register
32
+ def _(self, icon: QIcon, parent: QWidget = None):
33
+ self.__init__(parent)
34
+ self.setIcon(icon)
35
+
36
+ @__init__.register
37
+ def _(self, icon: str, parent: QWidget = None):
38
+ self.__init__(parent)
39
+ self.setIcon(icon)
40
+
41
+ def getIcon(self):
42
+ return toQIcon(self._icon)
43
+
44
+ def setIcon(self, icon: Union[str, QIcon, FluentIconBase]):
45
+ self._icon = icon
46
+ self.update()
47
+
48
+ def paintEvent(self, e):
49
+ painter = QPainter(self)
50
+ painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
51
+ drawIcon(self._icon, painter, self.rect())
52
+
53
+ icon = Property(QIcon, getIcon, setIcon)