monkeyqt 0.2.2__tar.gz → 0.2.4__tar.gz
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.
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/PKG-INFO +1 -1
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/__init__.py +7 -2
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/basic/button.py +21 -4
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/basic/select_card.py +57 -8
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/layout/__init__.py +2 -1
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/layout/box.py +100 -3
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/layout/window.py +113 -10
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt.egg-info/PKG-INFO +1 -1
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/LICENSE +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/README.md +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/common/enums.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/basic/__init__.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/basic/checkbox.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/data/__init__.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/data/avatar.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/data/console.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/data/data_table.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/data/image_compare.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/data/image_split.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/data/preview_dialogs.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/data/pro_table.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/data/table.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/feedback/__init__.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/feedback/alert.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/feedback/card.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/feedback/progress_bar.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/feedback/progress_ring.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/feedback/tooltip.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/form/auth.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/form/captcha.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/form/combobox.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/form/date_picker.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/form/form.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/form/input.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/form/multicombobox.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/form/slider.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/form/switch.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/form/upload.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/layout/page.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/layout/scroll_area.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/layout/widget.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/layout/window_shell.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/navigation/__init__.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/navigation/avatar_menu.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/navigation/breadcrumb.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/navigation/dropdown.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/navigation/history.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/navigation/pagination.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/navigation/segmented.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/navigation/sidebar.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/navigation/tabs.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/components/navigation/topbar.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/core/icons.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/core/theme.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/core/theme_data.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/designer/plugins.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/icons.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/__init__.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/__main__.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/adapter.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/components/__init__.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/components/theme_selector.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/engine.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/gallery.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/manager.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/names.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/palette_theme.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/style_utils.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/tokens.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt/themes/unified_qss.py +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt.egg-info/SOURCES.txt +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt.egg-info/dependency_links.txt +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt.egg-info/requires.txt +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/monkeyqt.egg-info/top_level.txt +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/pyproject.toml +0 -0
- {monkeyqt-0.2.2 → monkeyqt-0.2.4}/setup.cfg +0 -0
|
@@ -33,6 +33,8 @@ from .components.form.multicombobox import MkMultiComboBox
|
|
|
33
33
|
|
|
34
34
|
# Data
|
|
35
35
|
from .components.data.avatar import MkAvatar
|
|
36
|
+
from .components.data.table import MkTable
|
|
37
|
+
from .components.data.data_table import MkDataTable
|
|
36
38
|
from .components.data.pro_table import MkProTable
|
|
37
39
|
from .components.data.image_compare import MkImageCompare
|
|
38
40
|
from .components.data.image_split import MkImageSplit
|
|
@@ -56,7 +58,7 @@ from .components.feedback.tooltip import (
|
|
|
56
58
|
# Layout
|
|
57
59
|
from .components.layout.window import MkTitleBar, MkWindow
|
|
58
60
|
from .components.layout.window_shell import MkWindowShell
|
|
59
|
-
from .components.layout.box import MkQVBoxLayout, MkQHBoxLayout, MkVBox, MkHBox
|
|
61
|
+
from .components.layout.box import MkQVBoxLayout, MkQHBoxLayout, MkResponsiveBox, MkVBox, MkHBox
|
|
60
62
|
from .components.layout.widget import MkQWidget, MkWidget
|
|
61
63
|
from .components.layout.scroll_area import MkScrollArea, MkStackedWidget
|
|
62
64
|
from .components.layout.page import MkPage
|
|
@@ -79,7 +81,7 @@ from .themes import (
|
|
|
79
81
|
modern_scrollbar_qss,
|
|
80
82
|
)
|
|
81
83
|
|
|
82
|
-
__version__ = "0.2.
|
|
84
|
+
__version__ = "0.2.4"
|
|
83
85
|
|
|
84
86
|
__all__ = [
|
|
85
87
|
"MkButton",
|
|
@@ -113,6 +115,8 @@ __all__ = [
|
|
|
113
115
|
"MkMessage",
|
|
114
116
|
"MkUpload",
|
|
115
117
|
"MkAvatar",
|
|
118
|
+
"MkTable",
|
|
119
|
+
"MkDataTable",
|
|
116
120
|
"MkProTable",
|
|
117
121
|
"MkImageCompare",
|
|
118
122
|
"MkImageSplit",
|
|
@@ -133,6 +137,7 @@ __all__ = [
|
|
|
133
137
|
"MkWindowShell",
|
|
134
138
|
"MkQVBoxLayout",
|
|
135
139
|
"MkQHBoxLayout",
|
|
140
|
+
"MkResponsiveBox",
|
|
136
141
|
"MkVBox",
|
|
137
142
|
"MkHBox",
|
|
138
143
|
"MkQWidget",
|
|
@@ -214,10 +214,23 @@ class MkButton(QPushButton):
|
|
|
214
214
|
qss += f"\nQPushButton {{ {size_qss} }}"
|
|
215
215
|
_MkThemeEngine.apply_style_sheet(self, qss)
|
|
216
216
|
|
|
217
|
+
def sizeHint(self):
|
|
218
|
+
hint = super().sizeHint()
|
|
219
|
+
fm = self.fontMetrics()
|
|
220
|
+
text_w = fm.horizontalAdvance(self.text()) if self.text() else 0
|
|
221
|
+
icon_w = self.iconSize().width() if not self.icon().isNull() else 0
|
|
222
|
+
spacing = 8 if (icon_w and text_w) else 0
|
|
223
|
+
padding_h = 28 if text_w else 0
|
|
224
|
+
w = max(hint.width(), icon_w + text_w + spacing + padding_h)
|
|
225
|
+
h = max(hint.height(), 36)
|
|
226
|
+
from PySide6.QtCore import QSize
|
|
227
|
+
return QSize(w, h)
|
|
228
|
+
|
|
217
229
|
def paintEvent(self, event):
|
|
218
230
|
t = ThemeEngine
|
|
219
231
|
painter = QPainter(self)
|
|
220
232
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
|
233
|
+
painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)
|
|
221
234
|
rect = self.rect()
|
|
222
235
|
primary = t.get("--primary", "#409EFF")
|
|
223
236
|
bg = t.get("--bg", "#FFFFFF")
|
|
@@ -411,7 +424,7 @@ class MkButton(QPushButton):
|
|
|
411
424
|
icon_sz = self.iconSize()
|
|
412
425
|
icon_w = icon_sz.width()
|
|
413
426
|
icon_h = icon_sz.height()
|
|
414
|
-
spacing =
|
|
427
|
+
spacing = 8 if text else 0
|
|
415
428
|
fm = painter.fontMetrics()
|
|
416
429
|
text_w = fm.horizontalAdvance(text) if text else 0
|
|
417
430
|
total_w = icon_w + spacing + text_w
|
|
@@ -419,8 +432,7 @@ class MkButton(QPushButton):
|
|
|
419
432
|
start_x = rect.left() + (rect.width() - total_w) // 2
|
|
420
433
|
icon_y = rect.top() + (rect.height() - icon_h) // 2
|
|
421
434
|
|
|
422
|
-
|
|
423
|
-
painter.drawPixmap(start_x, icon_y, pixmap)
|
|
435
|
+
icon.paint(painter, QRect(start_x, icon_y, icon_w, icon_h))
|
|
424
436
|
|
|
425
437
|
if text:
|
|
426
438
|
text_rect = QRect(start_x + icon_w + spacing, rect.top(), text_w + 10, rect.height())
|
|
@@ -482,7 +494,7 @@ class MkButton(QPushButton):
|
|
|
482
494
|
def setIcon(self, icon):
|
|
483
495
|
if isinstance(icon, str):
|
|
484
496
|
try:
|
|
485
|
-
from
|
|
497
|
+
from monkeyqt_icons import Ph
|
|
486
498
|
pm = Ph.pixmap(icon, size=16, color=self._get_button_text_color())
|
|
487
499
|
if pm and not pm.isNull():
|
|
488
500
|
icon = QIcon(pm)
|
|
@@ -491,11 +503,15 @@ class MkButton(QPushButton):
|
|
|
491
503
|
elif hasattr(icon, "_ph_spec"):
|
|
492
504
|
self._ph_icon_spec = getattr(icon, "_ph_spec", None)
|
|
493
505
|
try:
|
|
506
|
+
from monkeyqt_icons import Ph
|
|
494
507
|
spec = dict(self._ph_icon_spec)
|
|
495
508
|
orig_color = spec.get("color")
|
|
496
509
|
if orig_color in (None, "auto", "default", "fg", "foreground", "currentcolor"):
|
|
497
510
|
spec["color"] = self._get_button_text_color()
|
|
498
511
|
icon = Ph.icon(**spec)
|
|
512
|
+
if "size" in spec and isinstance(spec["size"], int):
|
|
513
|
+
from PySide6.QtCore import QSize
|
|
514
|
+
self.setIconSize(QSize(spec["size"], spec["size"]))
|
|
499
515
|
except Exception:
|
|
500
516
|
pass
|
|
501
517
|
super().setIcon(icon)
|
|
@@ -504,6 +520,7 @@ class MkButton(QPushButton):
|
|
|
504
520
|
self._update_style()
|
|
505
521
|
if getattr(self, "_ph_icon_spec", None):
|
|
506
522
|
try:
|
|
523
|
+
from monkeyqt_icons import Ph
|
|
507
524
|
spec = dict(self._ph_icon_spec)
|
|
508
525
|
orig_color = spec.get("color")
|
|
509
526
|
if orig_color in (None, "auto", "default", "fg", "foreground", "currentcolor"):
|
|
@@ -281,6 +281,7 @@ class MkSelectItem(QFrame):
|
|
|
281
281
|
|
|
282
282
|
self.setCursor(Qt.CursorShape.PointingHandCursor)
|
|
283
283
|
self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
|
|
284
|
+
self.setMinimumWidth(0)
|
|
284
285
|
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
|
285
286
|
|
|
286
287
|
self._init_ui()
|
|
@@ -311,6 +312,10 @@ class MkSelectItem(QFrame):
|
|
|
311
312
|
|
|
312
313
|
# 3. 文本列(主标题 + 副标题/指标)
|
|
313
314
|
self.text_container = QWidget(self)
|
|
315
|
+
self.text_container.setMinimumWidth(0)
|
|
316
|
+
text_policy = self.text_container.sizePolicy()
|
|
317
|
+
text_policy.setHorizontalPolicy(QSizePolicy.Policy.Ignored)
|
|
318
|
+
self.text_container.setSizePolicy(text_policy)
|
|
314
319
|
_MkThemeEngine.apply_style_sheet(self.text_container, "background: transparent;")
|
|
315
320
|
text_layout = QVBoxLayout(self.text_container)
|
|
316
321
|
text_layout.setContentsMargins(0, 0, 0, 0)
|
|
@@ -321,6 +326,10 @@ class MkSelectItem(QFrame):
|
|
|
321
326
|
title_font.setPointSize(10)
|
|
322
327
|
title_font.setWeight(QFont.Weight.Normal)
|
|
323
328
|
self.lbl_title.setFont(title_font)
|
|
329
|
+
self.lbl_title.setMinimumWidth(0)
|
|
330
|
+
title_policy = self.lbl_title.sizePolicy()
|
|
331
|
+
title_policy.setHorizontalPolicy(QSizePolicy.Policy.Ignored)
|
|
332
|
+
self.lbl_title.setSizePolicy(title_policy)
|
|
324
333
|
_MkThemeEngine.apply_style_sheet(self.lbl_title, "background: transparent;")
|
|
325
334
|
text_layout.addWidget(self.lbl_title)
|
|
326
335
|
|
|
@@ -328,6 +337,10 @@ class MkSelectItem(QFrame):
|
|
|
328
337
|
sub_font = QFont()
|
|
329
338
|
sub_font.setPointSize(9)
|
|
330
339
|
self.lbl_subtitle.setFont(sub_font)
|
|
340
|
+
self.lbl_subtitle.setMinimumWidth(0)
|
|
341
|
+
subtitle_policy = self.lbl_subtitle.sizePolicy()
|
|
342
|
+
subtitle_policy.setHorizontalPolicy(QSizePolicy.Policy.Ignored)
|
|
343
|
+
self.lbl_subtitle.setSizePolicy(subtitle_policy)
|
|
331
344
|
_MkThemeEngine.apply_style_sheet(self.lbl_subtitle, "background: transparent;")
|
|
332
345
|
self.lbl_subtitle.setVisible(bool(self._subtitle_text))
|
|
333
346
|
text_layout.addWidget(self.lbl_subtitle)
|
|
@@ -376,6 +389,26 @@ class MkSelectItem(QFrame):
|
|
|
376
389
|
if self._actions_visible_on_hover:
|
|
377
390
|
self.actions_container.setVisible(False)
|
|
378
391
|
|
|
392
|
+
def resizeEvent(self, event):
|
|
393
|
+
super().resizeEvent(event)
|
|
394
|
+
self._update_elided_text()
|
|
395
|
+
|
|
396
|
+
def _update_elided_text(self):
|
|
397
|
+
"""Fit long labels into narrow cards without growing the scroll area."""
|
|
398
|
+
for label, source in (
|
|
399
|
+
(self.lbl_title, self._title_text),
|
|
400
|
+
(self.lbl_subtitle, self._subtitle_text),
|
|
401
|
+
):
|
|
402
|
+
available = max(0, label.width() - 2)
|
|
403
|
+
display = label.fontMetrics().elidedText(
|
|
404
|
+
source,
|
|
405
|
+
Qt.TextElideMode.ElideRight,
|
|
406
|
+
available,
|
|
407
|
+
)
|
|
408
|
+
if label.text() != display:
|
|
409
|
+
label.setText(display)
|
|
410
|
+
label.setToolTip(source if display != source else "")
|
|
411
|
+
|
|
379
412
|
def _update_indicator(self):
|
|
380
413
|
"""更新圆点或图标展示"""
|
|
381
414
|
if self._dot_color:
|
|
@@ -558,12 +591,12 @@ class MkSelectItem(QFrame):
|
|
|
558
591
|
|
|
559
592
|
def set_title(self, title: str):
|
|
560
593
|
self._title_text = title
|
|
561
|
-
self.
|
|
594
|
+
self._update_elided_text()
|
|
562
595
|
|
|
563
596
|
def set_subtitle(self, subtitle: str):
|
|
564
597
|
self._subtitle_text = subtitle
|
|
565
|
-
self.lbl_subtitle.setText(subtitle)
|
|
566
598
|
self.lbl_subtitle.setVisible(bool(subtitle))
|
|
599
|
+
self._update_elided_text()
|
|
567
600
|
self.setMinimumHeight(56)
|
|
568
601
|
|
|
569
602
|
def set_dot_color(self, color: Optional[str]):
|
|
@@ -729,11 +762,11 @@ class MkSelectCard(QFrame):
|
|
|
729
762
|
|
|
730
763
|
def __init__(
|
|
731
764
|
self,
|
|
732
|
-
title: str = "
|
|
765
|
+
title: str = "训练任务与模型",
|
|
733
766
|
icon: Optional[str] = "cube",
|
|
734
767
|
show_select_all: bool = True,
|
|
735
|
-
select_all_text: str = "
|
|
736
|
-
badge_pattern: str = "{count}
|
|
768
|
+
select_all_text: str = "全选 / 取消全选",
|
|
769
|
+
badge_pattern: str = "{count} 已选",
|
|
737
770
|
max_list_height: Optional[int] = None,
|
|
738
771
|
parent: Optional[QWidget] = None,
|
|
739
772
|
):
|
|
@@ -752,6 +785,8 @@ class MkSelectCard(QFrame):
|
|
|
752
785
|
self._updating_select_all = False
|
|
753
786
|
|
|
754
787
|
self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
|
|
788
|
+
self.setMinimumWidth(0)
|
|
789
|
+
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
|
755
790
|
self._init_ui()
|
|
756
791
|
ThemeEngine.instance().themeChanged.connect(self._apply_style)
|
|
757
792
|
self._apply_style()
|
|
@@ -775,10 +810,13 @@ class MkSelectCard(QFrame):
|
|
|
775
810
|
hdr_font.setPointSize(11)
|
|
776
811
|
hdr_font.setWeight(QFont.Weight.Bold)
|
|
777
812
|
self.lbl_hdr_title.setFont(hdr_font)
|
|
813
|
+
self.lbl_hdr_title.setMinimumWidth(0)
|
|
814
|
+
header_title_policy = self.lbl_hdr_title.sizePolicy()
|
|
815
|
+
header_title_policy.setHorizontalPolicy(QSizePolicy.Policy.Ignored)
|
|
816
|
+
self.lbl_hdr_title.setSizePolicy(header_title_policy)
|
|
778
817
|
_MkThemeEngine.apply_style_sheet(self.lbl_hdr_title, "background: transparent;")
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
self.header_layout.addStretch()
|
|
818
|
+
# 标题本身承担弹性空间,避免独立 stretch 在窄屏下把标题和计数压成 0px。
|
|
819
|
+
self.header_layout.addWidget(self.lbl_hdr_title, stretch=1)
|
|
782
820
|
|
|
783
821
|
# 右侧简约文本计数(参考图三: "1 selected",告别蓝色胶囊)
|
|
784
822
|
self.lbl_badge = QLabel(self._badge_pattern.format(count=0), self)
|
|
@@ -787,6 +825,10 @@ class MkSelectCard(QFrame):
|
|
|
787
825
|
badge_font.setWeight(QFont.Weight.Normal)
|
|
788
826
|
self.lbl_badge.setFont(badge_font)
|
|
789
827
|
self.lbl_badge.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
828
|
+
self.lbl_badge.setMinimumWidth(0)
|
|
829
|
+
badge_policy = self.lbl_badge.sizePolicy()
|
|
830
|
+
badge_policy.setHorizontalPolicy(QSizePolicy.Policy.Minimum)
|
|
831
|
+
self.lbl_badge.setSizePolicy(badge_policy)
|
|
790
832
|
self.header_layout.addWidget(self.lbl_badge)
|
|
791
833
|
|
|
792
834
|
self._card_layout.addLayout(self.header_layout)
|
|
@@ -802,11 +844,18 @@ class MkSelectCard(QFrame):
|
|
|
802
844
|
self.scroll_area = QScrollArea(self)
|
|
803
845
|
self.scroll_area.setWidgetResizable(True)
|
|
804
846
|
self.scroll_area.setFrameShape(QFrame.Shape.NoFrame)
|
|
847
|
+
self.scroll_area.setMinimumWidth(0)
|
|
848
|
+
self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
849
|
+
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
|
805
850
|
if self._max_list_height:
|
|
806
851
|
self.scroll_area.setMaximumHeight(self._max_list_height)
|
|
807
852
|
|
|
808
853
|
self.list_container = QWidget()
|
|
809
854
|
self.list_container.setObjectName("MkSelectListContainer")
|
|
855
|
+
self.list_container.setMinimumWidth(0)
|
|
856
|
+
list_policy = self.list_container.sizePolicy()
|
|
857
|
+
list_policy.setHorizontalPolicy(QSizePolicy.Policy.Ignored)
|
|
858
|
+
self.list_container.setSizePolicy(list_policy)
|
|
810
859
|
self.list_layout = QVBoxLayout(self.list_container)
|
|
811
860
|
self.list_layout.setContentsMargins(0, 0, 0, 0)
|
|
812
861
|
self.list_layout.setSpacing(6)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from .box import MkQVBoxLayout, MkQHBoxLayout, MkVBox, MkHBox
|
|
2
|
+
from .box import MkQVBoxLayout, MkQHBoxLayout, MkResponsiveBox, MkVBox, MkHBox
|
|
3
3
|
from .widget import MkQWidget, MkWidget
|
|
4
4
|
from .window import MkTitleBar, MkWindow
|
|
5
5
|
from .window_shell import MkWindowShell
|
|
@@ -9,6 +9,7 @@ from .page import MkPage
|
|
|
9
9
|
__all__ = [
|
|
10
10
|
"MkQVBoxLayout",
|
|
11
11
|
"MkQHBoxLayout",
|
|
12
|
+
"MkResponsiveBox",
|
|
12
13
|
"MkVBox",
|
|
13
14
|
"MkHBox",
|
|
14
15
|
"MkQWidget",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from PySide6.QtCore import Qt, Property, QRectF, Signal
|
|
3
5
|
from PySide6.QtGui import QPainter, QColor, QPen, QBrush
|
|
4
|
-
from PySide6.QtWidgets import QFrame, QVBoxLayout, QHBoxLayout, QWidget
|
|
6
|
+
from PySide6.QtWidgets import QBoxLayout, QFrame, QVBoxLayout, QHBoxLayout, QWidget
|
|
5
7
|
|
|
6
8
|
from monkeyqt.themes.engine import ThemeEngine
|
|
7
9
|
from monkeyqt.themes.style_utils import qcolor
|
|
@@ -211,13 +213,33 @@ class MkQHBoxLayout(QFrame):
|
|
|
211
213
|
支持自动填充/自适应大小、外边距(padding)、内间距(spacing/gap)、背景颜色、圆角以及可控制显示/隐藏的边框线。
|
|
212
214
|
"""
|
|
213
215
|
|
|
214
|
-
|
|
216
|
+
responsiveChanged = Signal(bool)
|
|
217
|
+
|
|
218
|
+
def __init__(
|
|
219
|
+
self,
|
|
220
|
+
widgets=None,
|
|
221
|
+
spacing=8,
|
|
222
|
+
padding=12,
|
|
223
|
+
border=False,
|
|
224
|
+
border_width=1,
|
|
225
|
+
radius=6,
|
|
226
|
+
bg_color=None,
|
|
227
|
+
parent=None,
|
|
228
|
+
breakpoint: Optional[int] = None,
|
|
229
|
+
compact_spacing: Optional[int] = None,
|
|
230
|
+
):
|
|
215
231
|
super().__init__(parent)
|
|
216
232
|
self._padding = padding
|
|
217
233
|
self._border = border
|
|
218
234
|
self._border_width = border_width
|
|
219
235
|
self._radius = radius
|
|
220
236
|
self._bg_color = bg_color
|
|
237
|
+
self._breakpoint = max(1, int(breakpoint)) if breakpoint else None
|
|
238
|
+
self._wide_spacing = int(spacing)
|
|
239
|
+
self._compact_spacing = (
|
|
240
|
+
self._wide_spacing if compact_spacing is None else int(compact_spacing)
|
|
241
|
+
)
|
|
242
|
+
self._is_compact = False
|
|
221
243
|
|
|
222
244
|
self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
|
|
223
245
|
self.setStyleSheet(f"""
|
|
@@ -241,6 +263,51 @@ class MkQHBoxLayout(QFrame):
|
|
|
241
263
|
|
|
242
264
|
ThemeEngine.instance().themeChanged.connect(self.set_theme_style)
|
|
243
265
|
|
|
266
|
+
def showEvent(self, event):
|
|
267
|
+
super().showEvent(event)
|
|
268
|
+
self._sync_responsive_direction()
|
|
269
|
+
|
|
270
|
+
def resizeEvent(self, event):
|
|
271
|
+
super().resizeEvent(event)
|
|
272
|
+
self._sync_responsive_direction(event.size().width())
|
|
273
|
+
|
|
274
|
+
def _sync_responsive_direction(self, width: Optional[int] = None):
|
|
275
|
+
if self._breakpoint is None:
|
|
276
|
+
compact = False
|
|
277
|
+
else:
|
|
278
|
+
compact = (self.width() if width is None else width) < self._breakpoint
|
|
279
|
+
|
|
280
|
+
if compact == self._is_compact:
|
|
281
|
+
return
|
|
282
|
+
|
|
283
|
+
self._is_compact = compact
|
|
284
|
+
self._layout.setDirection(
|
|
285
|
+
QBoxLayout.Direction.TopToBottom
|
|
286
|
+
if compact
|
|
287
|
+
else QBoxLayout.Direction.LeftToRight
|
|
288
|
+
)
|
|
289
|
+
self._layout.setSpacing(
|
|
290
|
+
self._compact_spacing if compact else self._wide_spacing
|
|
291
|
+
)
|
|
292
|
+
self._layout.invalidate()
|
|
293
|
+
self.updateGeometry()
|
|
294
|
+
self.responsiveChanged.emit(compact)
|
|
295
|
+
|
|
296
|
+
def set_breakpoint(self, breakpoint: Optional[int]):
|
|
297
|
+
self._breakpoint = max(1, int(breakpoint)) if breakpoint else None
|
|
298
|
+
self._sync_responsive_direction()
|
|
299
|
+
|
|
300
|
+
def is_compact(self) -> bool:
|
|
301
|
+
return self._is_compact
|
|
302
|
+
|
|
303
|
+
@Property(int)
|
|
304
|
+
def breakpoint(self):
|
|
305
|
+
return self._breakpoint or 0
|
|
306
|
+
|
|
307
|
+
@breakpoint.setter
|
|
308
|
+
def breakpoint(self, value):
|
|
309
|
+
self.set_breakpoint(value)
|
|
310
|
+
|
|
244
311
|
def set_theme_style(self, style_name: str = None):
|
|
245
312
|
self.update()
|
|
246
313
|
|
|
@@ -391,6 +458,36 @@ class MkQHBoxLayout(QFrame):
|
|
|
391
458
|
self.update()
|
|
392
459
|
|
|
393
460
|
|
|
461
|
+
class MkResponsiveBox(MkQHBoxLayout):
|
|
462
|
+
"""Horizontal container that stacks its children below a width breakpoint."""
|
|
463
|
+
|
|
464
|
+
def __init__(
|
|
465
|
+
self,
|
|
466
|
+
widgets=None,
|
|
467
|
+
spacing=8,
|
|
468
|
+
padding=12,
|
|
469
|
+
border=False,
|
|
470
|
+
border_width=1,
|
|
471
|
+
radius=6,
|
|
472
|
+
bg_color=None,
|
|
473
|
+
parent=None,
|
|
474
|
+
breakpoint=900,
|
|
475
|
+
compact_spacing=None,
|
|
476
|
+
):
|
|
477
|
+
super().__init__(
|
|
478
|
+
widgets=widgets,
|
|
479
|
+
spacing=spacing,
|
|
480
|
+
padding=padding,
|
|
481
|
+
border=border,
|
|
482
|
+
border_width=border_width,
|
|
483
|
+
radius=radius,
|
|
484
|
+
bg_color=bg_color,
|
|
485
|
+
parent=parent,
|
|
486
|
+
breakpoint=breakpoint,
|
|
487
|
+
compact_spacing=compact_spacing,
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
|
|
394
491
|
# 便捷别名
|
|
395
492
|
MkVBox = MkQVBoxLayout
|
|
396
493
|
MkHBox = MkQHBoxLayout
|
|
@@ -28,8 +28,8 @@ class MkTitleBarCloseButton(QPushButton):
|
|
|
28
28
|
- Windowed mode: Smooth rounded top-right corner matching window border-radius (default 8px).
|
|
29
29
|
- Maximized mode: Straight 90-degree right angle (0px) honoring Fitts's Law.
|
|
30
30
|
2. Pixel-perfect seamless fitting:
|
|
31
|
-
-
|
|
32
|
-
background
|
|
31
|
+
- Clears alpha only for translucent fallback windows; DWM-clipped opaque
|
|
32
|
+
windows preserve the titlebar background to avoid black corner pixels.
|
|
33
33
|
- High-DPI anti-aliased geometry drawing.
|
|
34
34
|
"""
|
|
35
35
|
def __init__(self, titlebar, parent=None):
|
|
@@ -61,12 +61,16 @@ class MkTitleBarCloseButton(QPushButton):
|
|
|
61
61
|
win = self.window()
|
|
62
62
|
if win and hasattr(win, "isMaximized") and win.isMaximized():
|
|
63
63
|
return 0.0
|
|
64
|
+
if win and getattr(win, "_use_native_corner_surface", False):
|
|
65
|
+
return 0.0
|
|
64
66
|
p_win = getattr(self._titlebar, "parent_window", None)
|
|
65
67
|
if p_win:
|
|
66
68
|
if hasattr(p_win, "isMaximized") and p_win.isMaximized():
|
|
67
69
|
return 0.0
|
|
68
70
|
if getattr(p_win, "_current_is_max_state", False):
|
|
69
71
|
return 0.0
|
|
72
|
+
if getattr(p_win, "_use_native_corner_surface", False):
|
|
73
|
+
return 0.0
|
|
70
74
|
if hasattr(p_win, "_border_radius"):
|
|
71
75
|
return float(p_win._border_radius)
|
|
72
76
|
|
|
@@ -116,14 +120,25 @@ class MkTitleBarCloseButton(QPushButton):
|
|
|
116
120
|
|
|
117
121
|
if self._is_hovered or self._is_pressed:
|
|
118
122
|
bg_color = QColor("#c42b1c" if self._is_pressed else "#e81123")
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
host_window = getattr(self._titlebar, "parent_window", None)
|
|
124
|
+
native_corner_surface = bool(
|
|
125
|
+
getattr(host_window, "_use_native_corner_surface", False)
|
|
126
|
+
)
|
|
127
|
+
# Clearing to transparent is required by the legacy alpha-window
|
|
128
|
+
# path, but creates opaque black pixels in a DWM-clipped surface.
|
|
129
|
+
# The native path keeps the already-painted titlebar background.
|
|
130
|
+
if not native_corner_surface:
|
|
131
|
+
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Source)
|
|
132
|
+
painter.fillRect(self.rect(), QColor(0, 0, 0, 0))
|
|
122
133
|
|
|
123
134
|
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceOver)
|
|
124
|
-
if r <= 0.0:
|
|
135
|
+
if r <= 0.0 or native_corner_surface:
|
|
136
|
+
# On Windows 11 DWM-clipped surfaces, the close button fills the full
|
|
137
|
+
# corner rectangle so DWM clips the window boundary natively without
|
|
138
|
+
# exposing any underlying titlebar background sliver.
|
|
125
139
|
painter.fillRect(self.rect(), bg_color)
|
|
126
140
|
else:
|
|
141
|
+
# Translucent fallback windows on legacy systems require manual corner curve
|
|
127
142
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
|
|
128
143
|
path = QPainterPath()
|
|
129
144
|
path.moveTo(0, 0)
|
|
@@ -754,6 +769,11 @@ class MkWindow(QMainWindow):
|
|
|
754
769
|
self._auto_content_container = None
|
|
755
770
|
self._close_behavior = "close" # "close" or "hide"
|
|
756
771
|
self._border_radius = 8
|
|
772
|
+
# Windows 11 can let DWM clip an opaque frameless window. This avoids
|
|
773
|
+
# the layered-window trails seen while shrinking a translucent window,
|
|
774
|
+
# while older platforms keep the original alpha-based corner path.
|
|
775
|
+
self._use_native_corner_surface = self._supports_native_corner_surface()
|
|
776
|
+
self._native_frame_synced = False
|
|
757
777
|
self._normal_geometry = None
|
|
758
778
|
self._is_maximizing = False
|
|
759
779
|
|
|
@@ -806,10 +826,23 @@ class MkWindow(QMainWindow):
|
|
|
806
826
|
self.setWindowFlags(Qt.WindowType.Window)
|
|
807
827
|
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, False)
|
|
808
828
|
|
|
829
|
+
@staticmethod
|
|
830
|
+
def _supports_native_corner_surface() -> bool:
|
|
831
|
+
if sys.platform != "win32":
|
|
832
|
+
return False
|
|
833
|
+
try:
|
|
834
|
+
# DWMWA_WINDOW_CORNER_PREFERENCE is supported from build 22000.
|
|
835
|
+
return sys.getwindowsversion().build >= 22000
|
|
836
|
+
except (AttributeError, OSError):
|
|
837
|
+
return False
|
|
838
|
+
|
|
809
839
|
def init_custom_frame(self):
|
|
810
840
|
# Custom frameless behavior
|
|
811
841
|
self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.Window)
|
|
812
|
-
self.setAttribute(
|
|
842
|
+
self.setAttribute(
|
|
843
|
+
Qt.WidgetAttribute.WA_TranslucentBackground,
|
|
844
|
+
not self._use_native_corner_surface,
|
|
845
|
+
)
|
|
813
846
|
|
|
814
847
|
# 1. Root outer layout to support padding for drop shadow
|
|
815
848
|
self._root_widget = QWidget(self)
|
|
@@ -887,6 +920,11 @@ class MkWindow(QMainWindow):
|
|
|
887
920
|
super().setCentralWidget(self._root_widget)
|
|
888
921
|
|
|
889
922
|
self.update_style()
|
|
923
|
+
if self._use_native_corner_surface and not self._enable_native_corners(False):
|
|
924
|
+
# Unsupported DWM implementations (older systems, compatibility
|
|
925
|
+
# layers) retain the original translucent corner implementation.
|
|
926
|
+
self._use_native_corner_surface = False
|
|
927
|
+
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, True)
|
|
890
928
|
|
|
891
929
|
def set_preset(self, preset: str):
|
|
892
930
|
self._preset = preset
|
|
@@ -1541,8 +1579,7 @@ class MkWindow(QMainWindow):
|
|
|
1541
1579
|
return None
|
|
1542
1580
|
|
|
1543
1581
|
def _enable_native_corners(self, is_max: bool | None = None):
|
|
1544
|
-
|
|
1545
|
-
if sys.platform == "win32":
|
|
1582
|
+
if getattr(self, "_use_native_corner_surface", False):
|
|
1546
1583
|
try:
|
|
1547
1584
|
import ctypes
|
|
1548
1585
|
hwnd = getattr(self, "_mk_cached_hwnd", None)
|
|
@@ -1556,7 +1593,14 @@ class MkWindow(QMainWindow):
|
|
|
1556
1593
|
# 2 = DWMWCP_ROUND (Standard Windows 11 rounded corners)
|
|
1557
1594
|
# 3 = DWMWCP_ROUNDSMALL (Restrained Windows 11 rounded corners)
|
|
1558
1595
|
pref = ctypes.c_int(1 if is_max else 2)
|
|
1559
|
-
ctypes.windll.dwmapi.DwmSetWindowAttribute(
|
|
1596
|
+
result = ctypes.windll.dwmapi.DwmSetWindowAttribute(
|
|
1597
|
+
hwnd,
|
|
1598
|
+
33,
|
|
1599
|
+
ctypes.byref(pref),
|
|
1600
|
+
ctypes.sizeof(pref),
|
|
1601
|
+
)
|
|
1602
|
+
if result != 0:
|
|
1603
|
+
return False
|
|
1560
1604
|
|
|
1561
1605
|
border_color = ctypes.c_uint(0xFFFFFFFE) # DWMWA_COLOR_NONE
|
|
1562
1606
|
ctypes.windll.dwmapi.DwmSetWindowAttribute(
|
|
@@ -1565,8 +1609,58 @@ class MkWindow(QMainWindow):
|
|
|
1565
1609
|
ctypes.byref(border_color),
|
|
1566
1610
|
ctypes.sizeof(border_color),
|
|
1567
1611
|
)
|
|
1612
|
+
return True
|
|
1613
|
+
except Exception:
|
|
1614
|
+
return False
|
|
1615
|
+
return False
|
|
1616
|
+
|
|
1617
|
+
def _notify_native_frame_changed(self) -> bool:
|
|
1618
|
+
"""Force one Win32 non-client recalculation after the first show."""
|
|
1619
|
+
if sys.platform != "win32" or not self._use_native_corner_surface:
|
|
1620
|
+
return False
|
|
1621
|
+
try:
|
|
1622
|
+
hwnd = getattr(self, "_mk_cached_hwnd", None)
|
|
1623
|
+
if hwnd is None:
|
|
1624
|
+
hwnd = int(self.winId())
|
|
1625
|
+
self._mk_cached_hwnd = hwnd
|
|
1626
|
+
|
|
1627
|
+
set_window_pos = ctypes.windll.user32.SetWindowPos
|
|
1628
|
+
set_window_pos.argtypes = [
|
|
1629
|
+
wintypes.HWND,
|
|
1630
|
+
wintypes.HWND,
|
|
1631
|
+
ctypes.c_int,
|
|
1632
|
+
ctypes.c_int,
|
|
1633
|
+
ctypes.c_int,
|
|
1634
|
+
ctypes.c_int,
|
|
1635
|
+
wintypes.UINT,
|
|
1636
|
+
]
|
|
1637
|
+
set_window_pos.restype = wintypes.BOOL
|
|
1638
|
+
|
|
1639
|
+
# SWP_FRAMECHANGED sends WM_NCCALCSIZE even though position and
|
|
1640
|
+
# dimensions stay unchanged. This is the frame refresh otherwise
|
|
1641
|
+
# observed only after the first maximize/restore cycle.
|
|
1642
|
+
flags = 0x0001 | 0x0002 | 0x0004 | 0x0010 | 0x0020
|
|
1643
|
+
res = bool(set_window_pos(hwnd, 0, 0, 0, 0, 0, flags))
|
|
1644
|
+
try:
|
|
1645
|
+
ctypes.windll.user32.RedrawWindow(hwnd, None, None, 0x0401) # RDW_FRAME | RDW_INVALIDATE
|
|
1568
1646
|
except Exception:
|
|
1569
1647
|
pass
|
|
1648
|
+
return res
|
|
1649
|
+
except Exception:
|
|
1650
|
+
return False
|
|
1651
|
+
|
|
1652
|
+
def _refresh_native_chrome(self):
|
|
1653
|
+
"""Re-apply DWM clipping after the HWND has entered the shown state."""
|
|
1654
|
+
if not getattr(self, "_use_native_corner_surface", False):
|
|
1655
|
+
return
|
|
1656
|
+
try:
|
|
1657
|
+
is_max = self.isMaximized()
|
|
1658
|
+
if self.titlebar:
|
|
1659
|
+
self.titlebar.set_maximized_state(is_max)
|
|
1660
|
+
self._notify_native_frame_changed()
|
|
1661
|
+
self._enable_native_corners(is_max)
|
|
1662
|
+
except RuntimeError:
|
|
1663
|
+
pass
|
|
1570
1664
|
|
|
1571
1665
|
def _apply_window_state_immediate(self, is_max: bool, force: bool = False):
|
|
1572
1666
|
try:
|
|
@@ -1682,6 +1776,15 @@ class MkWindow(QMainWindow):
|
|
|
1682
1776
|
self._normal_geometry = self._get_safe_normal_geometry(self.geometry())
|
|
1683
1777
|
is_max = True if getattr(self, "_is_maximizing", False) else self.isMaximized()
|
|
1684
1778
|
self._apply_window_state_immediate(is_max, force=True)
|
|
1779
|
+
if self._use_native_corner_surface:
|
|
1780
|
+
# A custom frameless HWND needs one explicit non-client refresh on
|
|
1781
|
+
# first show. Re-apply the DWM preference afterwards because frame
|
|
1782
|
+
# recalculation may recreate its visible corner state.
|
|
1783
|
+
if not self._native_frame_synced:
|
|
1784
|
+
self._native_frame_synced = self._notify_native_frame_changed()
|
|
1785
|
+
self._enable_native_corners(is_max)
|
|
1786
|
+
QTimer.singleShot(0, self._refresh_native_chrome)
|
|
1787
|
+
QTimer.singleShot(80, self._refresh_native_chrome)
|
|
1685
1788
|
|
|
1686
1789
|
def resizeEvent(self, event):
|
|
1687
1790
|
super().resizeEvent(event)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|