RinUI 0.1.4.1__tar.gz → 0.1.5.2__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.
- {rinui-0.1.4.1/RinUI.egg-info → rinui-0.1.5.2}/PKG-INFO +1 -1
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/__init__.py +1 -1
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Text/SpinBox.qml +1 -1
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/core/launcher.py +9 -5
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/core/theme.py +0 -2
- rinui-0.1.5.2/RinUI/core/window.py +235 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/windows/CtrlBtn.qml +5 -1
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/windows/FluentWindowBase.qml +1 -1
- {rinui-0.1.4.1 → rinui-0.1.5.2/RinUI.egg-info}/PKG-INFO +1 -1
- {rinui-0.1.4.1 → rinui-0.1.5.2}/pyproject.toml +1 -1
- rinui-0.1.4.1/RinUI/core/window.py +0 -214
- {rinui-0.1.4.1 → rinui-0.1.5.2}/LICENSE +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/MANIFEST.in +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/README.md +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/assets/fonts/FluentSystemIcons-Index.js +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/assets/fonts/FluentSystemIcons-Resizable.ttf +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/assets/img/default_app_icon.png +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Base.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/Button.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/CheckBox.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/ComboBox.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/DropDownButton.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/Hyperlink.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/PillButton.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/RadioButton.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/RoundButton.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/Slider.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/Switch.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/ToggleButton.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/BasicInput/ToolButton.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/ContextMenu.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/DateAndTime/DatePicker.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/DateAndTime/PickerView.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/DateAndTime/TimePicker.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/DialogsAndFlyouts/Dialog.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/DialogsAndFlyouts/DialogButtonBox.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/DialogsAndFlyouts/Flyout.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/DialogsAndFlyouts/Popup.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/FocusIndicator.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/IconWidget.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Indicator.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Layout/Expander.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Layout/SettingExpander.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Layout/SettingItem.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/ListAndCollections/Clip.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/ListAndCollections/Frame.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/ListAndCollections/ListView.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/ListAndCollections/ListViewDelegate.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/ListAndCollections/SettingCard.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/ListAndCollections/TableView.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/ListAndCollections/TableViewDelegate.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Media/Avatar.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/MenusAndToolbars/Menu.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/MenusAndToolbars/MenuBar.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/MenusAndToolbars/MenuItem.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/MenusAndToolbars/MenuItemGroup.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/MenusAndToolbars/MenuSeparator.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/MenusAndToolbars/TextInputMenu.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/MenusAndToolbars/ToolSeparator.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Navigation/ErrorPage.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Navigation/NavigationBar.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Navigation/NavigationItem.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Navigation/NavigationSubItem.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Navigation/NavigationView.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Navigation/Segmented.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Navigation/SegmentedItem.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Navigation/SelectorBar.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Navigation/SelectorBarItem.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/ScrollBar.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/ScrollView.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Shadow.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/StatusAndInfo/BusyIndicator.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/StatusAndInfo/InfoBadge.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/StatusAndInfo/InfoBar.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/StatusAndInfo/ProgressBar.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/StatusAndInfo/ProgressRing.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/StatusAndInfo/Toast.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/StatusAndInfo/ToolTip.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Text/Text.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Text/TextArea.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Text/TextField.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/Text/TextInput.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/components/qmldir +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/core/__init__.py +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/core/config.py +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/core/translator.py +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/hooks/__init__.py +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/hooks/hook-RinUI.py +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/languages/en_US.qm +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/languages/en_US.ts +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/languages/zh_CN.qm +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/languages/zh_CN.ts +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/qmldir +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/themes/Appearance.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/themes/Colors.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/themes/dark.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/themes/light.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/themes/qmldir +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/themes/theme.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/themes/utils.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/utils/Animation.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/utils/FloatLayer.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/utils/FontIconLoader.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/utils/Position.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/utils/Severity.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/utils/Typography.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/utils/qmldir +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/windows/FluentPage.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/windows/FluentWindow.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/windows/TitleBar.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/windows/WindowManager.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/windows/qmldir +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/windows/window/ApplicationWindow.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI/windows/window/Window.qml +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI.egg-info/SOURCES.txt +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI.egg-info/dependency_links.txt +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI.egg-info/entry_points.txt +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI.egg-info/requires.txt +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/RinUI.egg-info/top_level.txt +0 -0
- {rinui-0.1.4.1 → rinui-0.1.5.2}/setup.cfg +0 -0
@@ -1,9 +1,9 @@
|
|
1
|
-
import os
|
2
1
|
import sys
|
3
2
|
from typing import Union
|
4
3
|
|
5
4
|
from PySide6.QtCore import QCoreApplication, QUrl, QObject
|
6
5
|
from PySide6.QtGui import QIcon
|
6
|
+
from PySide6.QtQuick import QQuickWindow
|
7
7
|
from PySide6.QtWidgets import QApplication
|
8
8
|
from PySide6.QtQml import QQmlApplicationEngine
|
9
9
|
from pathlib import Path
|
@@ -20,6 +20,7 @@ class RinUIWindow:
|
|
20
20
|
:param qml_path: str or Path, QML file path (eg = "path/to/main.qml")
|
21
21
|
"""
|
22
22
|
super().__init__()
|
23
|
+
self.windows = None
|
23
24
|
if hasattr(self, "_initialized") and self._initialized:
|
24
25
|
return
|
25
26
|
|
@@ -54,9 +55,9 @@ class RinUIWindow:
|
|
54
55
|
|
55
56
|
if qml_path is None:
|
56
57
|
raise ValueError("QML path must be provided to load the window.")
|
57
|
-
self.qml_path = qml_path
|
58
|
+
self.qml_path = Path(qml_path)
|
58
59
|
|
59
|
-
if
|
60
|
+
if self.qml_path.exists():
|
60
61
|
self.engine.addImportPath(RINUI_PATH)
|
61
62
|
else:
|
62
63
|
raise FileNotFoundError(f"Cannot find RinUI module: {RINUI_PATH}")
|
@@ -73,8 +74,10 @@ class RinUIWindow:
|
|
73
74
|
|
74
75
|
# 窗口设置
|
75
76
|
self.root_window = self.engine.rootObjects()[0]
|
77
|
+
self.windows = [self.root_window] + self.root_window.findChildren(QQuickWindow)
|
76
78
|
|
77
|
-
self.
|
79
|
+
for window in self.windows:
|
80
|
+
self.theme_manager.set_window(window)
|
78
81
|
|
79
82
|
# 窗口句柄管理
|
80
83
|
self._window_handle_setup()
|
@@ -91,7 +94,7 @@ class RinUIWindow:
|
|
91
94
|
|
92
95
|
from .window import WinEventFilter, WinEventManager
|
93
96
|
|
94
|
-
self.win_event_filter = WinEventFilter(self.
|
97
|
+
self.win_event_filter = WinEventFilter(self.windows)
|
95
98
|
self.win_event_manager = WinEventManager()
|
96
99
|
|
97
100
|
app_instance = QApplication.instance()
|
@@ -106,6 +109,7 @@ class RinUIWindow:
|
|
106
109
|
:return:
|
107
110
|
"""
|
108
111
|
app_instance = QApplication.instance()
|
112
|
+
path = Path(path).as_posix()
|
109
113
|
if app_instance:
|
110
114
|
app_instance.setWindowIcon(QIcon(path)) # 设置应用程序图标
|
111
115
|
self.root_window.setProperty('icon', QUrl.fromLocalFile(path))
|
@@ -0,0 +1,235 @@
|
|
1
|
+
import platform
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from PySide6.QtCore import QAbstractNativeEventFilter, QByteArray, QObject, Slot
|
5
|
+
import ctypes
|
6
|
+
from ctypes import wintypes
|
7
|
+
|
8
|
+
import win32con
|
9
|
+
from PySide6.QtQuick import QQuickWindow
|
10
|
+
from win32gui import ReleaseCapture, GetWindowPlacement, ShowWindow
|
11
|
+
from win32con import SW_MAXIMIZE, SW_RESTORE
|
12
|
+
from win32api import SendMessage
|
13
|
+
|
14
|
+
from RinUI.core.config import is_windows
|
15
|
+
|
16
|
+
# 定义 Windows 类型
|
17
|
+
ULONG_PTR = ctypes.c_ulong if ctypes.sizeof(ctypes.c_void_p) == 4 else ctypes.c_ulonglong
|
18
|
+
LONG = ctypes.c_long
|
19
|
+
|
20
|
+
|
21
|
+
# 自定义结构体 MONITORINFO
|
22
|
+
class MONITORINFO(ctypes.Structure):
|
23
|
+
_fields_ = [
|
24
|
+
('cbSize', wintypes.DWORD),
|
25
|
+
('rcMonitor', wintypes.RECT),
|
26
|
+
('rcWork', wintypes.RECT),
|
27
|
+
('dwFlags', wintypes.DWORD)
|
28
|
+
]
|
29
|
+
|
30
|
+
|
31
|
+
class MSG(ctypes.Structure):
|
32
|
+
_fields_ = [
|
33
|
+
("hwnd", ctypes.c_void_p),
|
34
|
+
("message", wintypes.UINT),
|
35
|
+
("wParam", wintypes.WPARAM),
|
36
|
+
("lParam", wintypes.LPARAM),
|
37
|
+
("time", wintypes.DWORD),
|
38
|
+
("pt", wintypes.POINT),
|
39
|
+
]
|
40
|
+
|
41
|
+
|
42
|
+
user32 = ctypes.windll.user32
|
43
|
+
|
44
|
+
# 定义必要的 Windows 常量
|
45
|
+
WM_NCCALCSIZE = 0x0083
|
46
|
+
WM_NCHITTEST = 0x0084
|
47
|
+
WM_SYSCOMMAND = 0x0112
|
48
|
+
WM_GETMINMAXINFO = 0x0024
|
49
|
+
|
50
|
+
WS_CAPTION = 0x00C00000
|
51
|
+
WS_THICKFRAME = 0x00040000
|
52
|
+
|
53
|
+
SC_MINIMIZE = 0xF020
|
54
|
+
SC_MAXIMIZE = 0xF030
|
55
|
+
SC_RESTORE = 0xF120
|
56
|
+
|
57
|
+
|
58
|
+
class MINMAXINFO(ctypes.Structure):
|
59
|
+
_fields_ = [
|
60
|
+
("ptReserved", wintypes.POINT),
|
61
|
+
("ptMaxSize", wintypes.POINT),
|
62
|
+
("ptMaxPosition", wintypes.POINT),
|
63
|
+
("ptMinTrackSize", wintypes.POINT),
|
64
|
+
("ptMaxTrackSize", wintypes.POINT),
|
65
|
+
]
|
66
|
+
|
67
|
+
|
68
|
+
class WinEventManager(QObject):
|
69
|
+
@Slot(QObject, result=int)
|
70
|
+
def getWindowId(self, window):
|
71
|
+
"""获取窗口的句柄"""
|
72
|
+
print(f"GetWindowId: {window.winId()}")
|
73
|
+
return int(window.winId())
|
74
|
+
|
75
|
+
@Slot(int)
|
76
|
+
def dragWindowEvent(self, hwnd: int):
|
77
|
+
""" 在Windows 用原生方法拖动"""
|
78
|
+
if not is_windows() or type(hwnd) is not int or hwnd == 0:
|
79
|
+
print(
|
80
|
+
f"Use Qt method to drag window on: {platform.system()}"
|
81
|
+
if not is_windows() else f"Invalid window handle: {hwnd}"
|
82
|
+
)
|
83
|
+
return
|
84
|
+
|
85
|
+
ReleaseCapture()
|
86
|
+
SendMessage(
|
87
|
+
hwnd,
|
88
|
+
win32con.WM_SYSCOMMAND,
|
89
|
+
win32con.SC_MOVE | win32con.HTCAPTION, 0
|
90
|
+
)
|
91
|
+
|
92
|
+
@Slot(int)
|
93
|
+
def maximizeWindow(self, hwnd):
|
94
|
+
"""在Windows上最大化或还原窗口"""
|
95
|
+
if not is_windows() or type(hwnd) is not int or hwnd == 0:
|
96
|
+
print(
|
97
|
+
f"Use Qt method to drag window on: {platform.system()}"
|
98
|
+
if not is_windows() else f"Invalid window handle: {hwnd}"
|
99
|
+
)
|
100
|
+
return
|
101
|
+
|
102
|
+
try:
|
103
|
+
placement = GetWindowPlacement(hwnd)
|
104
|
+
current_state = placement[1]
|
105
|
+
|
106
|
+
if current_state == SW_MAXIMIZE:
|
107
|
+
ShowWindow(hwnd, SW_RESTORE)
|
108
|
+
else:
|
109
|
+
ShowWindow(hwnd, SW_MAXIMIZE)
|
110
|
+
|
111
|
+
except Exception as e:
|
112
|
+
print(f"Error toggling window state: {e}")
|
113
|
+
|
114
|
+
|
115
|
+
class WinEventFilter(QAbstractNativeEventFilter):
|
116
|
+
def __init__(self, windows: list):
|
117
|
+
super().__init__()
|
118
|
+
self.windows = windows # 接受多个窗口
|
119
|
+
self.hwnds = {} # 用于存储每个窗口的 hwnd
|
120
|
+
self.resize_border = 8
|
121
|
+
|
122
|
+
for window in self.windows:
|
123
|
+
window.visibleChanged.connect(self._on_visible_changed)
|
124
|
+
if window.isVisible():
|
125
|
+
self._init_window_handle(window)
|
126
|
+
|
127
|
+
def _on_visible_changed(self, visible: bool):
|
128
|
+
for window in self.windows:
|
129
|
+
if visible and self.hwnds.get(window) is None:
|
130
|
+
self._init_window_handle(window)
|
131
|
+
|
132
|
+
def _init_window_handle(self, window: QQuickWindow):
|
133
|
+
hwnd = int(window.winId())
|
134
|
+
self.hwnds[window] = hwnd
|
135
|
+
self.set_window_styles(window)
|
136
|
+
|
137
|
+
def set_window_styles(self, window: QQuickWindow):
|
138
|
+
hwnd = self.hwnds.get(window)
|
139
|
+
if hwnd is None:
|
140
|
+
return
|
141
|
+
|
142
|
+
style = user32.GetWindowLongPtrW(hwnd, -16) # GWL_STYLE
|
143
|
+
style |= WS_CAPTION | WS_THICKFRAME
|
144
|
+
user32.SetWindowLongPtrW(hwnd, -16, style) # GWL_STYLE
|
145
|
+
|
146
|
+
# 重绘
|
147
|
+
user32.SetWindowPos(hwnd, 0, 0, 0, 0, 0,
|
148
|
+
0x0002 | 0x0001 | 0x0040) # SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED
|
149
|
+
|
150
|
+
def nativeEventFilter(self, eventType: QByteArray, message):
|
151
|
+
if eventType != b"windows_generic_MSG":
|
152
|
+
return False, 0
|
153
|
+
|
154
|
+
try:
|
155
|
+
message_addr = int(message)
|
156
|
+
except:
|
157
|
+
buf = memoryview(message)
|
158
|
+
message_addr = ctypes.addressof(ctypes.c_char.from_buffer(buf))
|
159
|
+
|
160
|
+
# 直接使用内存地址访问 MSG 字段
|
161
|
+
hwnd = ctypes.c_void_p.from_address(message_addr).value
|
162
|
+
message_id = wintypes.UINT.from_address(message_addr + ctypes.sizeof(ctypes.c_void_p)).value
|
163
|
+
wParam = wintypes.WPARAM.from_address(message_addr + 2 * ctypes.sizeof(ctypes.c_void_p)).value
|
164
|
+
lParam = wintypes.LPARAM.from_address(message_addr + 3 * ctypes.sizeof(ctypes.c_void_p)).value
|
165
|
+
|
166
|
+
# 遍历每个窗口,检查哪个窗口收到了消息
|
167
|
+
for window in self.windows:
|
168
|
+
hwnd_window = self.hwnds.get(window)
|
169
|
+
if hwnd_window == hwnd:
|
170
|
+
if message_id == WM_NCHITTEST:
|
171
|
+
x = ctypes.c_short(lParam & 0xFFFF).value
|
172
|
+
y = ctypes.c_short((lParam >> 16) & 0xFFFF).value
|
173
|
+
|
174
|
+
rect = wintypes.RECT()
|
175
|
+
user32.GetWindowRect(hwnd_window, ctypes.byref(rect))
|
176
|
+
left, top, right, bottom = rect.left, rect.top, rect.right, rect.bottom
|
177
|
+
border = self.resize_border
|
178
|
+
|
179
|
+
if left <= x < left + border:
|
180
|
+
if top <= y < top + border:
|
181
|
+
return True, 13 # HTTOPLEFT
|
182
|
+
elif bottom - border <= y < bottom:
|
183
|
+
return True, 16 # HTBOTTOMLEFT
|
184
|
+
else:
|
185
|
+
return True, 10 # HTLEFT
|
186
|
+
elif right - border <= x < right:
|
187
|
+
if top <= y < top + border:
|
188
|
+
return True, 14 # HTTOPRIGHT
|
189
|
+
elif bottom - border <= y < bottom:
|
190
|
+
return True, 17 # HTBOTTOMRIGHT
|
191
|
+
else:
|
192
|
+
return True, 11 # HTRIGHT
|
193
|
+
elif top <= y < top + border:
|
194
|
+
return True, 12 # HTTOP
|
195
|
+
elif bottom - border <= y < bottom:
|
196
|
+
return True, 15 # HTBOTTOM
|
197
|
+
|
198
|
+
# 其他区域不处理
|
199
|
+
return False, 0
|
200
|
+
|
201
|
+
# 移除标题栏
|
202
|
+
elif message_id == WM_NCCALCSIZE and wParam:
|
203
|
+
return True, 0
|
204
|
+
|
205
|
+
# 支持动画
|
206
|
+
elif message_id == WM_SYSCOMMAND:
|
207
|
+
return False, 0
|
208
|
+
|
209
|
+
# 处理 WM_GETMINMAXINFO 消息以支持 Snap 功能
|
210
|
+
elif message_id == WM_GETMINMAXINFO:
|
211
|
+
# 获取屏幕工作区大小
|
212
|
+
monitor = user32.MonitorFromWindow(hwnd_window, 2) # MONITOR_DEFAULTTONEAREST
|
213
|
+
|
214
|
+
# 使用自定义的 MONITORINFO 结构
|
215
|
+
monitor_info = MONITORINFO()
|
216
|
+
monitor_info.cbSize = ctypes.sizeof(MONITORINFO)
|
217
|
+
monitor_info.dwFlags = 0
|
218
|
+
user32.GetMonitorInfoW(monitor, ctypes.byref(monitor_info))
|
219
|
+
|
220
|
+
# 获取 MINMAXINFO 结构
|
221
|
+
minmax_info = MINMAXINFO.from_address(lParam)
|
222
|
+
|
223
|
+
# 设置最大化位置和大小
|
224
|
+
minmax_info.ptMaxPosition.x = monitor_info.rcWork.left - monitor_info.rcMonitor.left
|
225
|
+
minmax_info.ptMaxPosition.y = monitor_info.rcWork.top - monitor_info.rcMonitor.top
|
226
|
+
minmax_info.ptMaxSize.x = monitor_info.rcWork.right - monitor_info.rcWork.left
|
227
|
+
minmax_info.ptMaxSize.y = monitor_info.rcWork.bottom - monitor_info.rcWork.top
|
228
|
+
|
229
|
+
# 设置最小跟踪大小
|
230
|
+
minmax_info.ptMinTrackSize.x = 200 # 最小宽度
|
231
|
+
minmax_info.ptMinTrackSize.y = 150 # 最小高度
|
232
|
+
|
233
|
+
return True, 0
|
234
|
+
|
235
|
+
return False, 0
|
@@ -18,7 +18,7 @@ ApplicationWindow {
|
|
18
18
|
color: "transparent"
|
19
19
|
|
20
20
|
// 自定义属性
|
21
|
-
property var icon:
|
21
|
+
property var icon: undefined // 图标
|
22
22
|
property alias titleEnabled: titleBar.titleEnabled
|
23
23
|
property int titleBarHeight: Theme.currentTheme.appearance.dialogTitleBarHeight
|
24
24
|
|
@@ -1,214 +0,0 @@
|
|
1
|
-
import platform
|
2
|
-
|
3
|
-
from PySide6.QtCore import QAbstractNativeEventFilter, QByteArray, QObject, Slot
|
4
|
-
import ctypes
|
5
|
-
from ctypes import wintypes
|
6
|
-
|
7
|
-
import win32con
|
8
|
-
from win32gui import ReleaseCapture, GetWindowPlacement, ShowWindow
|
9
|
-
from win32con import SW_MAXIMIZE, SW_RESTORE
|
10
|
-
from win32api import SendMessage
|
11
|
-
|
12
|
-
from RinUI.core.config import is_windows
|
13
|
-
|
14
|
-
# 定义 Windows 类型
|
15
|
-
ULONG_PTR = ctypes.c_ulong if ctypes.sizeof(ctypes.c_void_p) == 4 else ctypes.c_ulonglong
|
16
|
-
LONG = ctypes.c_long
|
17
|
-
|
18
|
-
|
19
|
-
# 自定义结构体 MONITORINFO
|
20
|
-
class MONITORINFO(ctypes.Structure):
|
21
|
-
_fields_ = [
|
22
|
-
('cbSize', wintypes.DWORD),
|
23
|
-
('rcMonitor', wintypes.RECT),
|
24
|
-
('rcWork', wintypes.RECT),
|
25
|
-
('dwFlags', wintypes.DWORD)
|
26
|
-
]
|
27
|
-
|
28
|
-
|
29
|
-
class MSG(ctypes.Structure):
|
30
|
-
_fields_ = [
|
31
|
-
("hwnd", ctypes.c_void_p),
|
32
|
-
("message", wintypes.UINT),
|
33
|
-
("wParam", wintypes.WPARAM),
|
34
|
-
("lParam", wintypes.LPARAM),
|
35
|
-
("time", wintypes.DWORD),
|
36
|
-
("pt", wintypes.POINT),
|
37
|
-
]
|
38
|
-
|
39
|
-
|
40
|
-
user32 = ctypes.windll.user32
|
41
|
-
|
42
|
-
# 定义必要的 Windows 常量
|
43
|
-
WM_NCCALCSIZE = 0x0083
|
44
|
-
WM_NCHITTEST = 0x0084
|
45
|
-
WM_SYSCOMMAND = 0x0112
|
46
|
-
WM_GETMINMAXINFO = 0x0024
|
47
|
-
|
48
|
-
WS_CAPTION = 0x00C00000
|
49
|
-
WS_THICKFRAME = 0x00040000
|
50
|
-
|
51
|
-
SC_MINIMIZE = 0xF020
|
52
|
-
SC_MAXIMIZE = 0xF030
|
53
|
-
SC_RESTORE = 0xF120
|
54
|
-
|
55
|
-
|
56
|
-
class MINMAXINFO(ctypes.Structure):
|
57
|
-
_fields_ = [
|
58
|
-
("ptReserved", wintypes.POINT),
|
59
|
-
("ptMaxSize", wintypes.POINT),
|
60
|
-
("ptMaxPosition", wintypes.POINT),
|
61
|
-
("ptMinTrackSize", wintypes.POINT),
|
62
|
-
("ptMaxTrackSize", wintypes.POINT),
|
63
|
-
]
|
64
|
-
|
65
|
-
|
66
|
-
class WinEventManager(QObject):
|
67
|
-
@Slot(QObject, result=int)
|
68
|
-
def getWindowId(self, window):
|
69
|
-
"""获取窗口的句柄"""
|
70
|
-
print(f"GetWindowId: {window.winId()}")
|
71
|
-
return int(window.winId())
|
72
|
-
|
73
|
-
@Slot(int)
|
74
|
-
def dragWindowEvent(self, hwnd: int):
|
75
|
-
""" 在Windows 用原生方法拖动"""
|
76
|
-
if not is_windows() or type(hwnd) is not int or hwnd == 0:
|
77
|
-
print(
|
78
|
-
f"Use Qt method to drag window on: {platform.system()}"
|
79
|
-
if not is_windows() else f"Invalid window handle: {hwnd}"
|
80
|
-
)
|
81
|
-
return
|
82
|
-
|
83
|
-
ReleaseCapture()
|
84
|
-
SendMessage(
|
85
|
-
hwnd,
|
86
|
-
win32con.WM_SYSCOMMAND,
|
87
|
-
win32con.SC_MOVE | win32con.HTCAPTION, 0
|
88
|
-
)
|
89
|
-
|
90
|
-
@Slot(int)
|
91
|
-
def maximizeWindow(self, hwnd):
|
92
|
-
"""在Windows上最大化或还原窗口"""
|
93
|
-
if not is_windows() or type(hwnd) is not int or hwnd == 0:
|
94
|
-
print(
|
95
|
-
f"Use Qt method to drag window on: {platform.system()}"
|
96
|
-
if not is_windows() else f"Invalid window handle: {hwnd}"
|
97
|
-
)
|
98
|
-
return
|
99
|
-
|
100
|
-
try:
|
101
|
-
placement = GetWindowPlacement(hwnd)
|
102
|
-
current_state = placement[1]
|
103
|
-
|
104
|
-
if current_state == SW_MAXIMIZE:
|
105
|
-
ShowWindow(hwnd, SW_RESTORE)
|
106
|
-
else:
|
107
|
-
ShowWindow(hwnd, SW_MAXIMIZE)
|
108
|
-
|
109
|
-
except Exception as e:
|
110
|
-
print(f"Error toggling window state: {e}")
|
111
|
-
|
112
|
-
|
113
|
-
class WinEventFilter(QAbstractNativeEventFilter):
|
114
|
-
def __init__(self, window):
|
115
|
-
super().__init__()
|
116
|
-
self.window = window
|
117
|
-
self.hwnd = int(window.winId())
|
118
|
-
self.resize_border = 8 # resize 边框宽度
|
119
|
-
|
120
|
-
self.set_window_styles()
|
121
|
-
|
122
|
-
def set_window_styles(self):
|
123
|
-
"""设置必要的窗口样式以启用原生窗口行为"""
|
124
|
-
style = user32.GetWindowLongPtrW(self.hwnd, -16) # GWL_STYLE
|
125
|
-
|
126
|
-
style |= WS_CAPTION | WS_THICKFRAME
|
127
|
-
user32.SetWindowLongPtrW(self.hwnd, -16, style) # GWL_STYLE
|
128
|
-
|
129
|
-
# 重绘
|
130
|
-
user32.SetWindowPos(self.hwnd, 0, 0, 0, 0, 0,
|
131
|
-
0x0002 | 0x0001 | 0x0040) # SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED
|
132
|
-
|
133
|
-
def nativeEventFilter(self, eventType: QByteArray, message):
|
134
|
-
if eventType != b"windows_generic_MSG":
|
135
|
-
return False, 0
|
136
|
-
|
137
|
-
try:
|
138
|
-
message_addr = int(message)
|
139
|
-
except:
|
140
|
-
buf = memoryview(message)
|
141
|
-
message_addr = ctypes.addressof(ctypes.c_char.from_buffer(buf))
|
142
|
-
|
143
|
-
# 直接使用内存地址访问 MSG 字段
|
144
|
-
hwnd = ctypes.c_void_p.from_address(message_addr).value
|
145
|
-
message_id = wintypes.UINT.from_address(message_addr + ctypes.sizeof(ctypes.c_void_p)).value
|
146
|
-
wParam = wintypes.WPARAM.from_address(message_addr + 2 * ctypes.sizeof(ctypes.c_void_p)).value
|
147
|
-
lParam = wintypes.LPARAM.from_address(message_addr + 3 * ctypes.sizeof(ctypes.c_void_p)).value
|
148
|
-
|
149
|
-
if message_id == WM_NCHITTEST:
|
150
|
-
x = ctypes.c_short(lParam & 0xFFFF).value
|
151
|
-
y = ctypes.c_short((lParam >> 16) & 0xFFFF).value
|
152
|
-
|
153
|
-
rect = wintypes.RECT()
|
154
|
-
user32.GetWindowRect(self.hwnd, ctypes.byref(rect))
|
155
|
-
left, top, right, bottom = rect.left, rect.top, rect.right, rect.bottom
|
156
|
-
border = self.resize_border
|
157
|
-
|
158
|
-
if left <= x < left + border:
|
159
|
-
if top <= y < top + border:
|
160
|
-
return True, 13 # HTTOPLEFT
|
161
|
-
elif bottom - border <= y < bottom:
|
162
|
-
return True, 16 # HTBOTTOMLEFT
|
163
|
-
else:
|
164
|
-
return True, 10 # HTLEFT
|
165
|
-
elif right - border <= x < right:
|
166
|
-
if top <= y < top + border:
|
167
|
-
return True, 14 # HTTOPRIGHT
|
168
|
-
elif bottom - border <= y < bottom:
|
169
|
-
return True, 17 # HTBOTTOMRIGHT
|
170
|
-
else:
|
171
|
-
return True, 11 # HTRIGHT
|
172
|
-
elif top <= y < top + border:
|
173
|
-
return True, 12 # HTTOP
|
174
|
-
elif bottom - border <= y < bottom:
|
175
|
-
return True, 15 # HTBOTTOM
|
176
|
-
|
177
|
-
# 其他区域不处理
|
178
|
-
return False, 0
|
179
|
-
|
180
|
-
# 移除标题栏
|
181
|
-
elif message_id == WM_NCCALCSIZE and wParam:
|
182
|
-
return True, 0
|
183
|
-
|
184
|
-
# 支持动画
|
185
|
-
elif message_id == WM_SYSCOMMAND:
|
186
|
-
return False, 0
|
187
|
-
|
188
|
-
# 处理 WM_GETMINMAXINFO 消息以支持 Snap 功能
|
189
|
-
elif message_id == WM_GETMINMAXINFO:
|
190
|
-
# 获取屏幕工作区大小
|
191
|
-
monitor = user32.MonitorFromWindow(self.hwnd, 2) # MONITOR_DEFAULTTONEAREST
|
192
|
-
|
193
|
-
# 使用自定义的 MONITORINFO 结构
|
194
|
-
monitor_info = MONITORINFO()
|
195
|
-
monitor_info.cbSize = ctypes.sizeof(MONITORINFO)
|
196
|
-
monitor_info.dwFlags = 0
|
197
|
-
user32.GetMonitorInfoW(monitor, ctypes.byref(monitor_info))
|
198
|
-
|
199
|
-
# 获取 MINMAXINFO 结构
|
200
|
-
minmax_info = MINMAXINFO.from_address(lParam)
|
201
|
-
|
202
|
-
# 设置最大化位置和大小
|
203
|
-
minmax_info.ptMaxPosition.x = monitor_info.rcWork.left - monitor_info.rcMonitor.left
|
204
|
-
minmax_info.ptMaxPosition.y = monitor_info.rcWork.top - monitor_info.rcMonitor.top
|
205
|
-
minmax_info.ptMaxSize.x = monitor_info.rcWork.right - monitor_info.rcWork.left
|
206
|
-
minmax_info.ptMaxSize.y = monitor_info.rcWork.bottom - monitor_info.rcWork.top
|
207
|
-
|
208
|
-
# 设置最小跟踪大小
|
209
|
-
minmax_info.ptMinTrackSize.x = 200 # 最小宽度
|
210
|
-
minmax_info.ptMinTrackSize.y = 150 # 最小高度
|
211
|
-
|
212
|
-
return True, 0
|
213
|
-
|
214
|
-
return False, 0
|
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
|
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
|