优化程序
This commit is contained in:
1 parent
68e8eb91a7
commit
6df29c9254
27 files changed
+1974
-203
No files matched your search
+139
-9
@@ -432,6 +432,8 @@ class PanelWindow(QWidget):
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
self._apply_win32_blur(theme.name() == "dark")
|
||||
# 亚克力方形底靠 DWM 圆角裁剪盖掉;窗口重建后属性丢失,每次 show 重应用
|
||||
self._apply_win11_round_corners()
|
||||
if not self._screen_hooked:
|
||||
wh = self.windowHandle()
|
||||
if wh is not None:
|
||||
@@ -571,6 +573,8 @@ class PanelWindow(QWidget):
|
||||
每项: (tooltip, qtawesome_icon, callback)
|
||||
排除重启/退出。"""
|
||||
return [
|
||||
("新建桌面文件框", "fa5s.window-restore", self._create_file_box),
|
||||
("新建文件夹框", "fa5s.folder-open", self._create_folder_box),
|
||||
("微信多开", "fa5b.weixin", self._open_wechat_multi),
|
||||
("管理员运行 CMD", "fa5s.terminal", self._open_admin_cmd),
|
||||
("管理员运行 PowerShell", "fa5b.windows", self._open_admin_powershell),
|
||||
@@ -840,7 +844,7 @@ class PanelWindow(QWidget):
|
||||
f"""
|
||||
QWidget#container {{
|
||||
background: {t['panel_bg']};
|
||||
border-radius: 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid {t['panel_border']};
|
||||
}}
|
||||
"""
|
||||
@@ -964,7 +968,7 @@ class PanelWindow(QWidget):
|
||||
f"""
|
||||
QWidget#bottom_bar {{
|
||||
background: {bar_bg2};
|
||||
border-radius: 0 0 12px 12px;
|
||||
border-radius: 0 0 8px 8px;
|
||||
border-top: 1px solid {t['panel_border']};
|
||||
}}
|
||||
QPushButton {{ border:none; background:transparent; border-radius:4px; }}
|
||||
@@ -993,6 +997,14 @@ class PanelWindow(QWidget):
|
||||
if item and item.widget():
|
||||
item.widget()._apply_theme()
|
||||
|
||||
# ✅ 主题变化时通知所有 FileBox 重绘
|
||||
try:
|
||||
from ui.box import FileBox
|
||||
for b in FileBox._instances:
|
||||
b._apply_theme() # 重设 QSS(container + title_bar 都会刷新)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _apply_tooltip_theme(self, t: dict, is_dark: bool):
|
||||
"""让所有控件 tooltip 跟随主题(含快捷栏按钮)。"""
|
||||
from PyQt6.QtGui import QPalette, QColor
|
||||
@@ -1093,6 +1105,54 @@ class PanelWindow(QWidget):
|
||||
dlg = WechatMultiDialog(self)
|
||||
dlg.exec()
|
||||
|
||||
# ── FileBoxes ─────────────────────────────────────────
|
||||
_file_boxes: list = []
|
||||
|
||||
def _create_file_box(self):
|
||||
"""新建一个空的拖入卡片文件框。"""
|
||||
self._create_box_common(title="文件框", folder_path="")
|
||||
|
||||
def _create_folder_box(self):
|
||||
"""新建文件夹框 — 先让用户选文件夹。"""
|
||||
from PyQt6.QtWidgets import QFileDialog
|
||||
folder = QFileDialog.getExistingDirectory(self, "选择文件夹", os.path.expanduser("~"))
|
||||
if not folder: return
|
||||
title = os.path.basename(folder) or folder
|
||||
self._create_box_common(title=title, folder_path=folder)
|
||||
|
||||
def _create_box_common(self, title: str, folder_path: str):
|
||||
from ui.box import FileBox
|
||||
# 注册 panel 引用 —— FileBox 在 mousePress 时会 raise_ 主面板
|
||||
FileBox._panel_ref = self
|
||||
screen = QApplication.primaryScreen().availableGeometry()
|
||||
idx = len(self._file_boxes)
|
||||
x = screen.center().x() - 120 + idx * 20
|
||||
y = screen.center().y() - 160 + idx * 20
|
||||
new_id = database.add_file_box(title, x=x, y=y, w=240, h=320)
|
||||
database.update_file_box(new_id, folder_path=folder_path) # 文件夹框模式
|
||||
box = FileBox({
|
||||
"id": new_id, "title": title, "x": x, "y": y,
|
||||
"w": 240, "h": 320, "folder_path": folder_path,
|
||||
})
|
||||
self._file_boxes.append(box)
|
||||
box.show(); box.raise_(); box.activateWindow()
|
||||
# 主面板始终在 FileBox 之上
|
||||
self.raise_()
|
||||
|
||||
def _load_file_boxes(self):
|
||||
"""启动时从 DB 恢复所有文件框。"""
|
||||
from ui.box import FileBox
|
||||
FileBox._panel_ref = self
|
||||
for d in database.get_file_boxes():
|
||||
box = FileBox(d)
|
||||
self._file_boxes.append(box)
|
||||
box.show()
|
||||
|
||||
def _close_all_file_boxes(self):
|
||||
for b in list(self._file_boxes):
|
||||
b.close()
|
||||
self._file_boxes.clear()
|
||||
|
||||
def _toggle_theme(self):
|
||||
theme.set_theme("light" if theme.name() == "dark" else "dark")
|
||||
self._apply_theme()
|
||||
@@ -1210,6 +1270,18 @@ class PanelWindow(QWidget):
|
||||
self._open_url_in_browser("https://www.doubao.com/chat/", "豆包")
|
||||
|
||||
def refresh_groups(self):
|
||||
# 同步刷新所有分组联动的 FileBox(双向联动)
|
||||
try:
|
||||
from ui.box import FileBox
|
||||
for b in FileBox._instances:
|
||||
if b._is_group_linked:
|
||||
b.refresh_items()
|
||||
# 如果分组被删了 → 自动解除关联
|
||||
if not database.get_items(b._group_id):
|
||||
pass # 分组可能空了,正常
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 记录当前折叠状态
|
||||
collapsed_ids = set()
|
||||
for i in range(self.groups_layout.count() - 1):
|
||||
@@ -1308,6 +1380,8 @@ class PanelWindow(QWidget):
|
||||
self.setGeometry(geo)
|
||||
self.show()
|
||||
self._apply_win32_blur(theme.name() == "dark")
|
||||
# setWindowFlags 重建了原生窗口,DWM 圆角属性丢失,重新应用
|
||||
self._apply_win11_round_corners()
|
||||
ball = getattr(self, "_ball_ref", None)
|
||||
if ball is not None:
|
||||
ball.set_stays_on_top(on_top)
|
||||
@@ -1653,7 +1727,8 @@ class PanelWindow(QWidget):
|
||||
if event.buttons() & Qt.MouseButton.LeftButton and self._win_drag_pos:
|
||||
self._dock_state = "none"
|
||||
self._is_collapsed = False
|
||||
self.move(gp - self._win_drag_pos)
|
||||
raw_pos = gp - self._win_drag_pos
|
||||
self.move(self._magnetic_snap(raw_pos))
|
||||
return True
|
||||
# 悬停时更新光标
|
||||
self._update_cursor(gp)
|
||||
@@ -1676,7 +1751,8 @@ class PanelWindow(QWidget):
|
||||
if event.buttons() & Qt.MouseButton.LeftButton and self._win_drag_pos:
|
||||
self._dock_state = "none"
|
||||
self._is_collapsed = False
|
||||
self.move(event.globalPosition().toPoint() - self._win_drag_pos)
|
||||
raw_pos = event.globalPosition().toPoint() - self._win_drag_pos
|
||||
self.move(self._magnetic_snap(raw_pos))
|
||||
return
|
||||
self._update_cursor(event.globalPosition().toPoint())
|
||||
|
||||
@@ -1764,6 +1840,39 @@ class PanelWindow(QWidget):
|
||||
self.show_near(self._ball_ref.pos(), self._ball_ref.width())
|
||||
|
||||
# ── 边缘吸附与收起 ─────────────────────────────────────
|
||||
_SNAP_THRESHOLD = 30 # 拖拽中磁吸触发阈值(像素)
|
||||
|
||||
def _magnetic_snap(self, pos: QPoint) -> QPoint:
|
||||
"""拖拽中实时磁吸:窗口接近边缘时自动吸附到边缘,给用户"磁性"手感。"""
|
||||
screen = QApplication.screenAt(pos)
|
||||
if not screen:
|
||||
screen = QApplication.primaryScreen()
|
||||
geom = screen.availableGeometry()
|
||||
x, y, w, h = pos.x(), pos.y(), self.width(), self.height()
|
||||
t = self._SNAP_THRESHOLD
|
||||
|
||||
# 优先吸附距离最近的边缘
|
||||
top_dist = abs(y - geom.top())
|
||||
bottom_dist = abs((y + h) - geom.bottom())
|
||||
left_dist = abs(x - geom.left())
|
||||
right_dist = abs((x + w) - geom.right())
|
||||
min_dist = min(top_dist, bottom_dist, left_dist, right_dist)
|
||||
|
||||
if min_dist <= t:
|
||||
if min_dist == top_dist:
|
||||
y = geom.top()
|
||||
elif min_dist == bottom_dist:
|
||||
y = geom.bottom() - h
|
||||
elif min_dist == left_dist:
|
||||
x = geom.left()
|
||||
else:
|
||||
x = geom.right() - w
|
||||
|
||||
# 限制在屏幕范围内
|
||||
x = max(geom.left(), min(x, geom.right() - w))
|
||||
y = max(geom.top(), min(y, geom.bottom() - h))
|
||||
return QPoint(x, y)
|
||||
|
||||
def _check_dock_state(self):
|
||||
if self._pinned:
|
||||
self._dock_state = "none"
|
||||
@@ -1772,10 +1881,11 @@ class PanelWindow(QWidget):
|
||||
screen = QApplication.screenAt(self.geometry().center())
|
||||
if not screen:
|
||||
screen = QApplication.primaryScreen()
|
||||
screen_geom = screen.geometry()
|
||||
# 用 availableGeometry 排除任务栏区域,避免吸附到任务栏上
|
||||
screen_geom = screen.availableGeometry()
|
||||
|
||||
x, y, w, h = self.x(), self.y(), self.width(), self.height()
|
||||
threshold = 20
|
||||
threshold = 30 # 吸附阈值(像素)
|
||||
|
||||
# 检测顶部吸附
|
||||
if abs(y - screen_geom.top()) <= threshold:
|
||||
@@ -1803,7 +1913,7 @@ class PanelWindow(QWidget):
|
||||
if not is_hovered:
|
||||
screen = QApplication.screenAt(cursor_pos)
|
||||
if screen:
|
||||
screen_geom = screen.geometry()
|
||||
screen_geom = screen.availableGeometry()
|
||||
tolerance = 3
|
||||
if self._dock_state == "top" and cursor_pos.y() <= screen_geom.top() + tolerance:
|
||||
if geom.left() <= cursor_pos.x() <= geom.right():
|
||||
@@ -1831,7 +1941,7 @@ class PanelWindow(QWidget):
|
||||
screen = QApplication.screenAt(self.geometry().center())
|
||||
if not screen:
|
||||
screen = QApplication.primaryScreen()
|
||||
screen_geom = screen.geometry()
|
||||
screen_geom = screen.availableGeometry()
|
||||
|
||||
x, y, w, h = self.x(), self.y(), self.width(), self.height()
|
||||
target_x, target_y = x, y
|
||||
@@ -1854,7 +1964,7 @@ class PanelWindow(QWidget):
|
||||
screen = QApplication.screenAt(self.geometry().center())
|
||||
if not screen:
|
||||
screen = QApplication.primaryScreen()
|
||||
screen_geom = screen.geometry()
|
||||
screen_geom = screen.availableGeometry()
|
||||
|
||||
x, y, w, h = self.x(), self.y(), self.width(), self.height()
|
||||
target_x, target_y = x, y
|
||||
@@ -1881,6 +1991,26 @@ class PanelWindow(QWidget):
|
||||
self._dock_anim.finished.connect(self.raise_)
|
||||
self._dock_anim.start()
|
||||
|
||||
def _apply_win11_round_corners(self):
|
||||
"""Windows 11: DWMWA_WINDOW_CORNER_PREFERENCE(33) = DWMWCP_ROUND(2)。
|
||||
|
||||
必须调用:亚克力合成层(SetWindowCompositionAttribute)作用于**整个方形窗口**,
|
||||
QSS border-radius 只能裁 Qt 内容层,裁不掉 OS 层的方形亚克力 →
|
||||
四角圆弧外露出方形底座("圆角下面垫着直角"的根因)。
|
||||
DWMWCP_ROUND 让 DWM 在 OS 层物理裁剪窗口四角,亚克力方形一起被裁掉。
|
||||
setWindowFlags 重建窗口后 DWM 属性会丢,需在 showEvent / 置顶切换后重应用。"""
|
||||
if sys.platform != "win32":
|
||||
return
|
||||
try:
|
||||
import ctypes
|
||||
hwnd = int(self.winId())
|
||||
pref = ctypes.c_int(2) # DWMWCP_ROUND
|
||||
ctypes.windll.dwmapi.DwmSetWindowAttribute(
|
||||
hwnd, 33, ctypes.byref(pref), ctypes.sizeof(pref)
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _apply_win32_blur(self, is_dark: bool):
|
||||
if sys.platform != "win32":
|
||||
return
|
||||
|
||||
Reference in new issue
Block a user