RinUI 0.1.0__py3-none-any.whl → 0.1.1__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 (114) hide show
  1. RinUI/__init__.py +4 -4
  2. RinUI/assets/fonts/FluentSystemIcons-Index.js +5255 -5255
  3. RinUI/components/Base.qml +78 -78
  4. RinUI/components/BasicInput/Button.qml +146 -146
  5. RinUI/components/BasicInput/CheckBox.qml +98 -98
  6. RinUI/components/BasicInput/ComboBox.qml +159 -159
  7. RinUI/components/BasicInput/DropDownButton.qml +20 -20
  8. RinUI/components/BasicInput/Hyperlink.qml +17 -17
  9. RinUI/components/BasicInput/PillButton.qml +10 -10
  10. RinUI/components/BasicInput/RadioButton.qml +94 -94
  11. RinUI/components/BasicInput/RoundButton.qml +26 -26
  12. RinUI/components/BasicInput/Slider.qml +212 -212
  13. RinUI/components/BasicInput/Switch.qml +101 -101
  14. RinUI/components/BasicInput/ToggleButton.qml +10 -10
  15. RinUI/components/BasicInput/ToolButton.qml +30 -30
  16. RinUI/components/ContextMenu.qml +183 -183
  17. RinUI/components/DateAndTime/DatePicker.qml +142 -140
  18. RinUI/components/DateAndTime/PickerView.qml +223 -223
  19. RinUI/components/DateAndTime/TimePicker.qml +114 -114
  20. RinUI/components/DialogsAndFlyouts/Dialog.qml +106 -106
  21. RinUI/components/DialogsAndFlyouts/DialogButtonBox.qml +47 -47
  22. RinUI/components/DialogsAndFlyouts/Flyout.qml +87 -87
  23. RinUI/components/DialogsAndFlyouts/Popup.qml +110 -106
  24. RinUI/components/FocusIndicator.qml +33 -33
  25. RinUI/components/IconWidget.qml +52 -52
  26. RinUI/components/Indicator.qml +89 -89
  27. RinUI/components/Layout/Expander.qml +159 -159
  28. RinUI/components/Layout/SettingExpander.qml +66 -66
  29. RinUI/components/Layout/SettingItem.qml +70 -70
  30. RinUI/components/ListAndCollections/Clip.qml +21 -21
  31. RinUI/components/ListAndCollections/Frame.qml +43 -43
  32. RinUI/components/ListAndCollections/ListView.qml +104 -104
  33. RinUI/components/ListAndCollections/ListViewDelegate.qml +82 -82
  34. RinUI/components/ListAndCollections/SettingCard.qml +72 -72
  35. RinUI/components/ListAndCollections/TableView.qml +81 -81
  36. RinUI/components/ListAndCollections/TableViewDelegate.qml +88 -88
  37. RinUI/components/Media/Avatar.qml +82 -82
  38. RinUI/components/MenusAndToolbars/Menu.qml +150 -150
  39. RinUI/components/MenusAndToolbars/MenuBar.qml +42 -42
  40. RinUI/components/MenusAndToolbars/MenuItem.qml +131 -131
  41. RinUI/components/MenusAndToolbars/MenuItemGroup.qml +43 -43
  42. RinUI/components/MenusAndToolbars/MenuSeparator.qml +13 -13
  43. RinUI/components/MenusAndToolbars/TextInputMenu.qml +37 -37
  44. RinUI/components/MenusAndToolbars/ToolSeparator.qml +16 -16
  45. RinUI/components/Navigation/ErrorPage.qml +48 -48
  46. RinUI/components/Navigation/NavigationBar.qml +179 -179
  47. RinUI/components/Navigation/NavigationItem.qml +193 -193
  48. RinUI/components/Navigation/NavigationSubItem.qml +103 -103
  49. RinUI/components/Navigation/NavigationView.qml +228 -227
  50. RinUI/components/Navigation/Segmented.qml +16 -16
  51. RinUI/components/Navigation/SegmentedItem.qml +107 -107
  52. RinUI/components/Navigation/SelectorBar.qml +12 -12
  53. RinUI/components/Navigation/SelectorBarItem.qml +88 -88
  54. RinUI/components/ScrollBar.qml +204 -204
  55. RinUI/components/ScrollView.qml +12 -12
  56. RinUI/components/Shadow.qml +47 -47
  57. RinUI/components/StatusAndInfo/InfoBadge.qml +77 -77
  58. RinUI/components/StatusAndInfo/InfoBar.qml +256 -251
  59. RinUI/components/StatusAndInfo/ProgressBar.qml +126 -126
  60. RinUI/components/StatusAndInfo/ProgressRing.qml +149 -0
  61. RinUI/components/StatusAndInfo/Toast.qml +236 -236
  62. RinUI/components/StatusAndInfo/ToolTip.qml +93 -93
  63. RinUI/components/Text/SpinBox.qml +150 -133
  64. RinUI/components/Text/Text.qml +44 -44
  65. RinUI/components/Text/TextArea.qml +117 -117
  66. RinUI/components/Text/TextField.qml +113 -113
  67. RinUI/components/Text/TextInput.qml +47 -47
  68. RinUI/components/qmldir +80 -80
  69. RinUI/core/__init__.py +4 -4
  70. RinUI/core/config.py +129 -129
  71. RinUI/core/launcher.py +129 -129
  72. RinUI/core/theme.py +339 -339
  73. RinUI/core/translator.py +25 -25
  74. RinUI/hooks/__init__.py +3 -3
  75. RinUI/hooks/hook-RinUI.py +3 -3
  76. RinUI/qmldir +108 -104
  77. RinUI/themes/Appearance.qml +36 -0
  78. RinUI/themes/Colors.qml +36 -0
  79. RinUI/themes/dark.qml +145 -145
  80. RinUI/themes/light.qml +145 -145
  81. RinUI/themes/qmldir +9 -6
  82. RinUI/themes/theme.qml +151 -149
  83. RinUI/themes/utils.qml +37 -37
  84. RinUI/utils/Animation.qml +12 -12
  85. RinUI/utils/FloatLayer.qml +132 -123
  86. RinUI/utils/FontIconLoader.qml +13 -13
  87. RinUI/utils/Position.qml +19 -19
  88. RinUI/utils/Severity.qml +13 -13
  89. RinUI/utils/Typography.qml +17 -17
  90. RinUI/utils/qmldir +4 -4
  91. RinUI/windows/CtrlBtn.qml +118 -118
  92. RinUI/windows/FluentPage.qml +92 -92
  93. RinUI/windows/FluentWindow.qml +31 -30
  94. RinUI/windows/FluentWindowBase.qml +158 -158
  95. RinUI/windows/TitleBar.qml +135 -135
  96. RinUI/windows/qmldir +7 -7
  97. RinUI/windows/window/ApplicationWindow.qml +8 -8
  98. RinUI/windows/window/Window.qml +118 -118
  99. {rinui-0.1.0.data → rinui-0.1.1.data}/data/LICENSE +21 -21
  100. {rinui-0.1.0.data → rinui-0.1.1.data}/data/README.md +100 -100
  101. {rinui-0.1.0.dist-info → rinui-0.1.1.dist-info}/METADATA +116 -115
  102. rinui-0.1.1.dist-info/RECORD +112 -0
  103. {rinui-0.1.0.dist-info → rinui-0.1.1.dist-info}/WHEEL +1 -1
  104. {rinui-0.1.0.dist-info → rinui-0.1.1.dist-info/licenses}/LICENSE +21 -21
  105. RinUI/__pycache__/__init__.cpython-38.pyc +0 -0
  106. RinUI/config/rin_ui.json +0 -8
  107. RinUI/core/__pycache__/__init__.cpython-38.pyc +0 -0
  108. RinUI/core/__pycache__/config.cpython-38.pyc +0 -0
  109. RinUI/core/__pycache__/launcher.cpython-38.pyc +0 -0
  110. RinUI/core/__pycache__/theme.cpython-38.pyc +0 -0
  111. RinUI/core/__pycache__/translator.cpython-38.pyc +0 -0
  112. rinui-0.1.0.dist-info/RECORD +0 -116
  113. {rinui-0.1.0.dist-info → rinui-0.1.1.dist-info}/entry_points.txt +0 -0
  114. {rinui-0.1.0.dist-info → rinui-0.1.1.dist-info}/top_level.txt +0 -0
RinUI/core/theme.py CHANGED
@@ -1,339 +1,339 @@
1
- import ctypes
2
- import platform
3
- import time
4
-
5
- from PySide6.QtCore import QObject, Signal, Slot, QThread
6
-
7
- from .config import DEFAULT_CONFIG, RinConfig, is_win10, is_windows, is_win11, BackdropEffect
8
- import sys
9
- import darkdetect
10
-
11
-
12
- def check_darkdetect_support():
13
- system = platform.system()
14
- if system == "Darwin":
15
- mac_ver = platform.mac_ver()[0]
16
- major, minor, *_ = map(int, mac_ver.split("."))
17
- return (major == 10 and minor >= 14) or major > 10
18
-
19
- elif system == "Windows":
20
- return platform.release() >= "10"
21
- else:
22
- return False
23
-
24
-
25
- ACCENT_STATES = {
26
- "acrylic": 3,
27
- "mica": 2,
28
- "tabbed": 4,
29
- "none": 0
30
- }
31
-
32
- ACCENT_SUPPORT = {
33
- "acrylic": is_win10(),
34
- "mica": is_win11(),
35
- "tabbed": is_win10(),
36
- "none": True
37
- }
38
-
39
-
40
- class ACCENT_POLICY(ctypes.Structure):
41
- _fields_ = [
42
- ("AccentState", ctypes.c_int),
43
- ("AccentFlags", ctypes.c_int),
44
- ("GradientColor", ctypes.c_int),
45
- ("AnimationId", ctypes.c_int),
46
- ]
47
-
48
-
49
- class WINDOWCOMPOSITIONATTRIBDATA(ctypes.Structure):
50
- _fields_ = [
51
- ("Attrib", ctypes.c_int),
52
- ("pvData", ctypes.c_void_p),
53
- ("cbData", ctypes.c_size_t),
54
- ]
55
-
56
-
57
- class ThemeListener(QThread):
58
- """
59
- 监听系统颜色模式
60
- """
61
- themeChanged = Signal(str)
62
-
63
- def run(self):
64
- last_theme = darkdetect.theme()
65
- while True:
66
- current_theme = darkdetect.theme()
67
- if current_theme != last_theme:
68
- last_theme = current_theme
69
- self.themeChanged.emit(current_theme)
70
- print(f"Theme changed: {current_theme}")
71
- time.sleep(1)
72
-
73
- def stop(self):
74
- self.terminate()
75
-
76
-
77
- class ThemeManager(QObject):
78
- themeChanged = Signal(str)
79
- backdropChanged = Signal(str)
80
- windows = [] # 窗口句柄们(
81
- _instance = None
82
-
83
- # DWM 常量保持不变
84
- DWMWA_USE_IMMERSIVE_DARK_MODE = 20
85
- DWMWA_WINDOW_CORNER_PREFERENCE = 33
86
- DWMWA_NCRENDERING_POLICY = 2
87
- DWMNCRENDERINGPOLICY_ENABLED = 2
88
- DWMWA_SYSTEMBACKDROP_TYPE = 38
89
- WCA_ACCENT_POLICY = 19
90
-
91
- # 圆角
92
- DWMWCP_DEFAULT = 0
93
- DWMWCP_DONOTROUND = 1
94
- DWMWCP_ROUND = 2
95
- DWMWCP_ROUNDSMALL = 3
96
-
97
- def clean_up(self):
98
- """
99
- 清理资源并停止主题监听。
100
- """
101
- if self.listener:
102
- RinConfig.save_config()
103
- print("Save config.")
104
- self.listener.stop()
105
- self.listener.wait() # 等待线程结束
106
- print("Theme listener stopped.")
107
-
108
- def __new__(cls, *args, **kwargs):
109
- """
110
- 单例管理,共享主题状态
111
- :param args:
112
- :param kwargs:
113
- """
114
- if cls._instance is None:
115
- cls._instance = super().__new__(cls)
116
- return cls._instance
117
-
118
- def __init__(self):
119
- if hasattr(self, "_initialized") and self._initialized:
120
- return
121
- self._initialized = True
122
- super().__init__()
123
- self.theme_dict = {
124
- "Light": 0,
125
- "Dark": 1
126
- }
127
-
128
- self.listener = None # 监听线程
129
- self.current_theme = DEFAULT_CONFIG["theme"]["current_theme"] # 当前主题
130
- self.is_darkdetect_supported = check_darkdetect_support()
131
-
132
- try:
133
- self.current_theme = RinConfig["theme"]["current_theme"]
134
- except Exception as e:
135
- print(f"Failed to load config because of {e}, using default config")
136
-
137
- # self.hwnd = None # 窗口句柄
138
-
139
- self.start_listener()
140
-
141
- def start_listener(self):
142
- if not self.is_darkdetect_supported:
143
- print("darkdetect not supported on this platform")
144
- return
145
- self.listener = ThemeListener()
146
- self.listener.themeChanged.connect(self._handle_system_theme)
147
- self.listener.start()
148
-
149
- def set_window(self, window): # 绑定窗口句柄
150
- hwnd = int(window.winId())
151
- self.windows.append(hwnd)
152
- print(f"Window handle set: {hwnd}")
153
-
154
- def _handle_system_theme(self):
155
- if self.current_theme == "Auto":
156
- self._update_window_theme()
157
- self.themeChanged.emit(self._actual_theme())
158
- else:
159
- # 保持当前背景效果不变
160
- self._update_window_theme()
161
-
162
- @Slot(str)
163
- def apply_backdrop_effect(self, effect_type: str):
164
- """
165
- 应用背景效果
166
- :param effect_type: str, 背景效果类型(acrylic, mica, tabbed, none)
167
- """
168
- self._update_window_theme()
169
- if not is_windows() or not self.windows:
170
- print(f"Cannot apply effect \"{effect_type}\" on this platform")
171
- return -2 # 非 windows或未绑定窗口
172
- self.backdropChanged.emit(effect_type)
173
-
174
- accent_state = ACCENT_STATES.get(effect_type, 0)
175
- if not ACCENT_SUPPORT.get(effect_type, False):
176
- print(f"Effect \"{effect_type}\" not supported on this platform")
177
- return -1 # 效果不支持
178
-
179
- for hwnd in self.windows:
180
- if is_win11():
181
- ctypes.windll.dwmapi.DwmSetWindowAttribute(
182
- hwnd,
183
- self.DWMWA_SYSTEMBACKDROP_TYPE,
184
- ctypes.byref(ctypes.c_int(accent_state)),
185
- ctypes.sizeof(ctypes.c_int)
186
- )
187
- elif is_win10() and effect_type == BackdropEffect.Acrylic.value:
188
- self._apply_win10_effect(effect_type, hwnd)
189
-
190
- RinConfig["backdrop_effect"] = effect_type
191
- print(
192
- f"Applied \"{effect_type.strip().capitalize()}\" effect with "
193
- f"{platform.system() + '11' if is_win11() else '10'}"
194
- )
195
- return 0 # 成功
196
-
197
- def _apply_win10_effect(self, effect_type, hwnd):
198
- """
199
- 应用 Windows 10 背景效果
200
- :param effect_type: str, 背景效果类型(acrylic, tabbed(actually blur)
201
- """
202
- backdrop_color = RinConfig["win10_feat"]["backdrop_dark" if self.is_dark_theme() else "backdrop_light"]
203
-
204
- accent = ACCENT_POLICY()
205
- accent.AccentState = ACCENT_STATES[effect_type]
206
- accent.AccentFlags = 2
207
- accent.GradientColor = backdrop_color
208
- data = WINDOWCOMPOSITIONATTRIBDATA()
209
- data.Attrib = self.WCA_ACCENT_POLICY
210
- data.pvData = ctypes.cast(ctypes.pointer(accent), ctypes.c_void_p)
211
- data.cbData = ctypes.sizeof(accent)
212
-
213
- try:
214
- set_window_composition = ctypes.windll.user32.SetWindowCompositionAttribute
215
- set_window_composition(hwnd, ctypes.byref(data))
216
- except Exception as e:
217
- print(f"Failed to apply acrylic on Win10: {e}")
218
-
219
- def apply_window_effects(self): # 启用圆角阴影
220
- if sys.platform != "win32" or not self.windows:
221
- return
222
-
223
- dwm = ctypes.windll.dwmapi
224
-
225
- # 启用非客户端渲染策略(让窗口边框具备阴影)
226
- ncrp = ctypes.c_int(self.DWMNCRENDERINGPOLICY_ENABLED)
227
- for hwnd in self.windows:
228
- dwm.DwmSetWindowAttribute(
229
- hwnd,
230
- self.DWMWA_NCRENDERING_POLICY,
231
- ctypes.byref(ncrp),
232
- ctypes.sizeof(ncrp)
233
- )
234
-
235
- # 启用圆角效果
236
- corner_preference = ctypes.c_int(self.DWMWCP_ROUND)
237
- dwm.DwmSetWindowAttribute(
238
- hwnd,
239
- self.DWMWA_WINDOW_CORNER_PREFERENCE,
240
- ctypes.byref(corner_preference),
241
- ctypes.sizeof(corner_preference)
242
- )
243
- print("Enabled Rounded and Shadows")
244
-
245
- def _update_window_theme(self): # 更新窗口的颜色模式
246
- if sys.platform != "win32" or not self.windows:
247
- return
248
- actual_theme = self._actual_theme()
249
- for hwnd in self.windows:
250
- if is_win11():
251
- ctypes.windll.dwmapi.DwmSetWindowAttribute(
252
- hwnd,
253
- self.DWMWA_USE_IMMERSIVE_DARK_MODE,
254
- ctypes.byref(ctypes.c_int(self.theme_dict[actual_theme])),
255
- ctypes.sizeof(ctypes.c_int)
256
- )
257
- elif is_win10() and RinConfig["backdrop_effect"] == BackdropEffect.Acrylic.value:
258
- self._apply_win10_effect(RinConfig["backdrop_effect"], hwnd)
259
- else:
260
- print(f"Cannot apply backdrop on {platform.system()}")
261
-
262
- print(f"Window theme updated to {actual_theme}")
263
-
264
- def is_dark_theme(self):
265
- """是否为暗黑主题"""
266
- return self._actual_theme() == "Dark"
267
-
268
- def _actual_theme(self):
269
- """实际应用的主题"""
270
- if self.current_theme == "Auto":
271
- return darkdetect.theme() if self.is_darkdetect_supported else "Light"
272
- return self.current_theme
273
-
274
- @Slot(str)
275
- def toggle_theme(self, theme: str): # 切换主题
276
- if theme not in ["Auto", "Light", "Dark"]: # 三状态
277
- return
278
- if self.current_theme != theme:
279
- print(f"Switching to '{theme}' theme")
280
- self.current_theme = theme
281
- RinConfig["theme"]["current_theme"] = theme
282
- self._update_window_theme()
283
- self.themeChanged.emit(self._actual_theme())
284
-
285
- @Slot(result=str)
286
- def get_theme(self):
287
- return self._actual_theme()
288
-
289
- @Slot(result=str)
290
- def get_theme_name(self):
291
- """获取当前主题名称"""
292
- return self.current_theme
293
-
294
- @Slot(str)
295
- def receive(self, message):
296
- print(message)
297
-
298
- @Slot(result=str)
299
- def get_backdrop_effect(self):
300
- """获取当前背景效果"""
301
- return RinConfig["backdrop_effect"]
302
-
303
- @Slot(str)
304
- def set_theme_color(self, color):
305
- """设置当前主题颜色"""
306
- RinConfig["theme_color"] = color
307
- RinConfig.save_config()
308
-
309
- @Slot(result=str)
310
- def get_theme_color(self):
311
- """获取当前主题颜色"""
312
- return RinConfig["theme_color"]
313
-
314
- @Slot(QObject, result=int)
315
- def getWindowId(self, window):
316
- """获取窗口的句柄"""
317
- print(f"GetWindowId: {window.winId()}")
318
- return int(window.winId())
319
-
320
- @Slot(int)
321
- def dragWindowEvent(self, hwnd):
322
- """ 在Windows 用原生方法拖动"""
323
- if not is_windows() or hwnd not in self.windows:
324
- print(
325
- f"Use Qt method to drag window on: {platform.system()}"
326
- if not is_windows() else f"Invalid window handle: {hwnd}"
327
- )
328
- return
329
-
330
- import win32con
331
- from win32gui import ReleaseCapture
332
- from win32api import SendMessage
333
-
334
- ReleaseCapture()
335
- SendMessage(
336
- hwnd,
337
- win32con.WM_SYSCOMMAND,
338
- win32con.SC_MOVE | win32con.HTCAPTION, 0
339
- )
1
+ import ctypes
2
+ import platform
3
+ import time
4
+
5
+ from PySide6.QtCore import QObject, Signal, Slot, QThread
6
+
7
+ from .config import DEFAULT_CONFIG, RinConfig, is_win10, is_windows, is_win11, BackdropEffect
8
+ import sys
9
+ import darkdetect
10
+
11
+
12
+ def check_darkdetect_support():
13
+ system = platform.system()
14
+ if system == "Darwin":
15
+ mac_ver = platform.mac_ver()[0]
16
+ major, minor, *_ = map(int, mac_ver.split("."))
17
+ return (major == 10 and minor >= 14) or major > 10
18
+
19
+ elif system == "Windows":
20
+ return platform.release() >= "10"
21
+ else:
22
+ return False
23
+
24
+
25
+ ACCENT_STATES = {
26
+ "acrylic": 3,
27
+ "mica": 2,
28
+ "tabbed": 4,
29
+ "none": 0
30
+ }
31
+
32
+ ACCENT_SUPPORT = {
33
+ "acrylic": is_win10(),
34
+ "mica": is_win11(),
35
+ "tabbed": is_win10(),
36
+ "none": True
37
+ }
38
+
39
+
40
+ class ACCENT_POLICY(ctypes.Structure):
41
+ _fields_ = [
42
+ ("AccentState", ctypes.c_int),
43
+ ("AccentFlags", ctypes.c_int),
44
+ ("GradientColor", ctypes.c_int),
45
+ ("AnimationId", ctypes.c_int),
46
+ ]
47
+
48
+
49
+ class WINDOWCOMPOSITIONATTRIBDATA(ctypes.Structure):
50
+ _fields_ = [
51
+ ("Attrib", ctypes.c_int),
52
+ ("pvData", ctypes.c_void_p),
53
+ ("cbData", ctypes.c_size_t),
54
+ ]
55
+
56
+
57
+ class ThemeListener(QThread):
58
+ """
59
+ 监听系统颜色模式
60
+ """
61
+ themeChanged = Signal(str)
62
+
63
+ def run(self):
64
+ last_theme = darkdetect.theme()
65
+ while True:
66
+ current_theme = darkdetect.theme()
67
+ if current_theme != last_theme:
68
+ last_theme = current_theme
69
+ self.themeChanged.emit(current_theme)
70
+ print(f"Theme changed: {current_theme}")
71
+ time.sleep(1)
72
+
73
+ def stop(self):
74
+ self.terminate()
75
+
76
+
77
+ class ThemeManager(QObject):
78
+ themeChanged = Signal(str)
79
+ backdropChanged = Signal(str)
80
+ windows = [] # 窗口句柄们(
81
+ _instance = None
82
+
83
+ # DWM 常量保持不变
84
+ DWMWA_USE_IMMERSIVE_DARK_MODE = 20
85
+ DWMWA_WINDOW_CORNER_PREFERENCE = 33
86
+ DWMWA_NCRENDERING_POLICY = 2
87
+ DWMNCRENDERINGPOLICY_ENABLED = 2
88
+ DWMWA_SYSTEMBACKDROP_TYPE = 38
89
+ WCA_ACCENT_POLICY = 19
90
+
91
+ # 圆角
92
+ DWMWCP_DEFAULT = 0
93
+ DWMWCP_DONOTROUND = 1
94
+ DWMWCP_ROUND = 2
95
+ DWMWCP_ROUNDSMALL = 3
96
+
97
+ def clean_up(self):
98
+ """
99
+ 清理资源并停止主题监听。
100
+ """
101
+ if self.listener:
102
+ RinConfig.save_config()
103
+ print("Save config.")
104
+ self.listener.stop()
105
+ self.listener.wait() # 等待线程结束
106
+ print("Theme listener stopped.")
107
+
108
+ def __new__(cls, *args, **kwargs):
109
+ """
110
+ 单例管理,共享主题状态
111
+ :param args:
112
+ :param kwargs:
113
+ """
114
+ if cls._instance is None:
115
+ cls._instance = super().__new__(cls)
116
+ return cls._instance
117
+
118
+ def __init__(self):
119
+ if hasattr(self, "_initialized") and self._initialized:
120
+ return
121
+ self._initialized = True
122
+ super().__init__()
123
+ self.theme_dict = {
124
+ "Light": 0,
125
+ "Dark": 1
126
+ }
127
+
128
+ self.listener = None # 监听线程
129
+ self.current_theme = DEFAULT_CONFIG["theme"]["current_theme"] # 当前主题
130
+ self.is_darkdetect_supported = check_darkdetect_support()
131
+
132
+ try:
133
+ self.current_theme = RinConfig["theme"]["current_theme"]
134
+ except Exception as e:
135
+ print(f"Failed to load config because of {e}, using default config")
136
+
137
+ # self.hwnd = None # 窗口句柄
138
+
139
+ self.start_listener()
140
+
141
+ def start_listener(self):
142
+ if not self.is_darkdetect_supported:
143
+ print("darkdetect not supported on this platform")
144
+ return
145
+ self.listener = ThemeListener()
146
+ self.listener.themeChanged.connect(self._handle_system_theme)
147
+ self.listener.start()
148
+
149
+ def set_window(self, window): # 绑定窗口句柄
150
+ hwnd = int(window.winId())
151
+ self.windows.append(hwnd)
152
+ print(f"Window handle set: {hwnd}")
153
+
154
+ def _handle_system_theme(self):
155
+ if self.current_theme == "Auto":
156
+ self._update_window_theme()
157
+ self.themeChanged.emit(self._actual_theme())
158
+ else:
159
+ # 保持当前背景效果不变
160
+ self._update_window_theme()
161
+
162
+ @Slot(str)
163
+ def apply_backdrop_effect(self, effect_type: str):
164
+ """
165
+ 应用背景效果
166
+ :param effect_type: str, 背景效果类型(acrylic, mica, tabbed, none)
167
+ """
168
+ self._update_window_theme()
169
+ if not is_windows() or not self.windows:
170
+ print(f"Cannot apply effect \"{effect_type}\" on this platform")
171
+ return -2 # 非 windows或未绑定窗口
172
+ self.backdropChanged.emit(effect_type)
173
+
174
+ accent_state = ACCENT_STATES.get(effect_type, 0)
175
+ if not ACCENT_SUPPORT.get(effect_type, False):
176
+ print(f"Effect \"{effect_type}\" not supported on this platform")
177
+ return -1 # 效果不支持
178
+
179
+ for hwnd in self.windows:
180
+ if is_win11():
181
+ ctypes.windll.dwmapi.DwmSetWindowAttribute(
182
+ hwnd,
183
+ self.DWMWA_SYSTEMBACKDROP_TYPE,
184
+ ctypes.byref(ctypes.c_int(accent_state)),
185
+ ctypes.sizeof(ctypes.c_int)
186
+ )
187
+ elif is_win10() and effect_type == BackdropEffect.Acrylic.value:
188
+ self._apply_win10_effect(effect_type, hwnd)
189
+
190
+ RinConfig["backdrop_effect"] = effect_type
191
+ print(
192
+ f"Applied \"{effect_type.strip().capitalize()}\" effect with "
193
+ f"{platform.system() + '11' if is_win11() else '10'}"
194
+ )
195
+ return 0 # 成功
196
+
197
+ def _apply_win10_effect(self, effect_type, hwnd):
198
+ """
199
+ 应用 Windows 10 背景效果
200
+ :param effect_type: str, 背景效果类型(acrylic, tabbed(actually blur)
201
+ """
202
+ backdrop_color = RinConfig["win10_feat"]["backdrop_dark" if self.is_dark_theme() else "backdrop_light"]
203
+
204
+ accent = ACCENT_POLICY()
205
+ accent.AccentState = ACCENT_STATES[effect_type]
206
+ accent.AccentFlags = 2
207
+ accent.GradientColor = backdrop_color
208
+ data = WINDOWCOMPOSITIONATTRIBDATA()
209
+ data.Attrib = self.WCA_ACCENT_POLICY
210
+ data.pvData = ctypes.cast(ctypes.pointer(accent), ctypes.c_void_p)
211
+ data.cbData = ctypes.sizeof(accent)
212
+
213
+ try:
214
+ set_window_composition = ctypes.windll.user32.SetWindowCompositionAttribute
215
+ set_window_composition(hwnd, ctypes.byref(data))
216
+ except Exception as e:
217
+ print(f"Failed to apply acrylic on Win10: {e}")
218
+
219
+ def apply_window_effects(self): # 启用圆角阴影
220
+ if sys.platform != "win32" or not self.windows:
221
+ return
222
+
223
+ dwm = ctypes.windll.dwmapi
224
+
225
+ # 启用非客户端渲染策略(让窗口边框具备阴影)
226
+ ncrp = ctypes.c_int(self.DWMNCRENDERINGPOLICY_ENABLED)
227
+ for hwnd in self.windows:
228
+ dwm.DwmSetWindowAttribute(
229
+ hwnd,
230
+ self.DWMWA_NCRENDERING_POLICY,
231
+ ctypes.byref(ncrp),
232
+ ctypes.sizeof(ncrp)
233
+ )
234
+
235
+ # 启用圆角效果
236
+ corner_preference = ctypes.c_int(self.DWMWCP_ROUND)
237
+ dwm.DwmSetWindowAttribute(
238
+ hwnd,
239
+ self.DWMWA_WINDOW_CORNER_PREFERENCE,
240
+ ctypes.byref(corner_preference),
241
+ ctypes.sizeof(corner_preference)
242
+ )
243
+ print("Enabled Rounded and Shadows")
244
+
245
+ def _update_window_theme(self): # 更新窗口的颜色模式
246
+ if sys.platform != "win32" or not self.windows:
247
+ return
248
+ actual_theme = self._actual_theme()
249
+ for hwnd in self.windows:
250
+ if is_win11():
251
+ ctypes.windll.dwmapi.DwmSetWindowAttribute(
252
+ hwnd,
253
+ self.DWMWA_USE_IMMERSIVE_DARK_MODE,
254
+ ctypes.byref(ctypes.c_int(self.theme_dict[actual_theme])),
255
+ ctypes.sizeof(ctypes.c_int)
256
+ )
257
+ elif is_win10() and RinConfig["backdrop_effect"] == BackdropEffect.Acrylic.value:
258
+ self._apply_win10_effect(RinConfig["backdrop_effect"], hwnd)
259
+ else:
260
+ print(f"Cannot apply backdrop on {platform.system()}")
261
+
262
+ print(f"Window theme updated to {actual_theme}")
263
+
264
+ def is_dark_theme(self):
265
+ """是否为暗黑主题"""
266
+ return self._actual_theme() == "Dark"
267
+
268
+ def _actual_theme(self):
269
+ """实际应用的主题"""
270
+ if self.current_theme == "Auto":
271
+ return darkdetect.theme() if self.is_darkdetect_supported else "Light"
272
+ return self.current_theme
273
+
274
+ @Slot(str)
275
+ def toggle_theme(self, theme: str): # 切换主题
276
+ if theme not in ["Auto", "Light", "Dark"]: # 三状态
277
+ return
278
+ if self.current_theme != theme:
279
+ print(f"Switching to '{theme}' theme")
280
+ self.current_theme = theme
281
+ RinConfig["theme"]["current_theme"] = theme
282
+ self._update_window_theme()
283
+ self.themeChanged.emit(self._actual_theme())
284
+
285
+ @Slot(result=str)
286
+ def get_theme(self):
287
+ return self._actual_theme()
288
+
289
+ @Slot(result=str)
290
+ def get_theme_name(self):
291
+ """获取当前主题名称"""
292
+ return self.current_theme
293
+
294
+ @Slot(str)
295
+ def receive(self, message):
296
+ print(message)
297
+
298
+ @Slot(result=str)
299
+ def get_backdrop_effect(self):
300
+ """获取当前背景效果"""
301
+ return RinConfig["backdrop_effect"]
302
+
303
+ @Slot(str)
304
+ def set_theme_color(self, color):
305
+ """设置当前主题颜色"""
306
+ RinConfig["theme_color"] = color
307
+ RinConfig.save_config()
308
+
309
+ @Slot(result=str)
310
+ def get_theme_color(self):
311
+ """获取当前主题颜色"""
312
+ return RinConfig["theme_color"]
313
+
314
+ @Slot(QObject, result=int)
315
+ def getWindowId(self, window):
316
+ """获取窗口的句柄"""
317
+ print(f"GetWindowId: {window.winId()}")
318
+ return int(window.winId())
319
+
320
+ @Slot(int)
321
+ def dragWindowEvent(self, hwnd):
322
+ """ 在Windows 用原生方法拖动"""
323
+ if not is_windows() or hwnd not in self.windows:
324
+ print(
325
+ f"Use Qt method to drag window on: {platform.system()}"
326
+ if not is_windows() else f"Invalid window handle: {hwnd}"
327
+ )
328
+ return
329
+
330
+ import win32con
331
+ from win32gui import ReleaseCapture
332
+ from win32api import SendMessage
333
+
334
+ ReleaseCapture()
335
+ SendMessage(
336
+ hwnd,
337
+ win32con.WM_SYSCOMMAND,
338
+ win32con.SC_MOVE | win32con.HTCAPTION, 0
339
+ )