优化更新

This commit is contained in:
2026-09-23 16:51:40 +08:00
parent cd4ffbfa90
commit 68e8eb91a7
15 changed files with 1858 additions and 193 deletions
+70 -24
View File
@@ -7,13 +7,36 @@ from PyQt6.QtGui import (
QDrag, QPixmap, QPainter, QColor, QIcon, QPen, QKeySequence,
QFont, QFontMetrics, QTextLayout, QTextOption,
)
from PyQt6.QtCore import Qt, QMimeData, QByteArray, QSize, QPoint, QFileInfo, QPropertyAnimation, QEasingCurve
from PyQt6.QtCore import Qt, QMimeData, QByteArray, QSize, QPoint, QFileInfo
from db import database
from ui.flow_layout import ITEM_W
import ui.theme as theme
import ui.dialog_style as dialog_style
import shortcut_target
_icon_provider = QFileIconProvider()
# 图标缓存:同一文件路径在刷新/重建条目时避免重复读磁盘提取图标
_icon_cache: dict[str, QIcon] = {}
_ICON_CACHE_MAX = 512
def _cached_icon(key: str, loader) -> QIcon:
icon = _icon_cache.get(key)
if icon is None:
icon = loader()
if len(_icon_cache) >= _ICON_CACHE_MAX:
_icon_cache.clear()
_icon_cache[key] = icon
return icon
def get_icon_width() -> int:
"""设置中的图标宽度(限制在 48~120),ItemWidget 尺寸以此为准。"""
try:
w = int(database.get_setting("icon_width", str(ITEM_W)))
except Exception:
w = ITEM_W
return max(48, min(120, w))
def _wrapped_line_count(text: str, font: QFont, width_px: int) -> int:
@@ -56,21 +79,26 @@ def two_line_display_text(text: str, font: QFont, width_px: int) -> str:
def extract_icon(path: str) -> QIcon:
try:
icon = _icon_provider.icon(QFileInfo(path))
if not icon.isNull():
px = icon.pixmap(QSize(36, 36))
if not px.isNull() and px.width() > 4:
return icon
except Exception:
pass
ext = os.path.splitext(path)[1].lower()
color = "#4a9eff"
if ext == ".exe": return qta.icon("fa5s.desktop", color=color)
if ext == ".lnk": return qta.icon("fa5s.link", color=color)
if ext in (".bat", ".cmd"): return qta.icon("fa5s.terminal", color=color)
if ext == ".url": return qta.icon("fa5s.globe", color=color)
return qta.icon("fa5s.file", color=color)
key = os.path.normcase(path)
def _load() -> QIcon:
try:
icon = _icon_provider.icon(QFileInfo(path))
if not icon.isNull():
px = icon.pixmap(QSize(64, 64))
if not px.isNull() and px.width() > 4:
return icon
except Exception:
pass
ext = os.path.splitext(path)[1].lower()
color = "#4a9eff"
if ext == ".exe": return qta.icon("fa5s.desktop", color=color)
if ext == ".lnk": return qta.icon("fa5s.link", color=color)
if ext in (".bat", ".cmd"): return qta.icon("fa5s.terminal", color=color)
if ext == ".url": return qta.icon("fa5s.globe", color=color)
return qta.icon("fa5s.file", color=color)
return _cached_icon(key, _load)
class ItemWidget(QWidget):
@@ -79,10 +107,14 @@ class ItemWidget(QWidget):
def __init__(self, item_data: dict, parent=None):
super().__init__(parent)
self.item_data = item_data
self.setFixedSize(68, 76)
# 尺寸跟随设置中的「图标宽度」,高度 = 宽度 + 文本区
w = get_icon_width()
self._icon_px = max(24, int(w * 0.53))
self.setFixedSize(w, w + 8)
self.setCursor(Qt.CursorShape.PointingHandCursor)
self._drag_start_pos = QPoint()
self._selected = False
self._hovered = False
self._build_ui()
def showEvent(self, event):
@@ -95,12 +127,12 @@ class ItemWidget(QWidget):
def _build_ui(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(4, 6, 4, 4)
layout.setContentsMargins(4, 6, 4, 2)
layout.setSpacing(3)
icon = extract_icon(self.item_data["path"])
self.icon_label = QLabel()
self.icon_label.setPixmap(icon.pixmap(QSize(36, 36)))
self.icon_label.setPixmap(icon.pixmap(QSize(self._icon_px, self._icon_px)))
self.icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.icon_label.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
@@ -133,17 +165,31 @@ class ItemWidget(QWidget):
def matches(self, keyword: str) -> bool:
return keyword.lower() in self.item_data["name"].lower()
# ── 选中高亮 ─────────────────────────────────────────
# ── 选中/悬停高亮 ─────────────────────────────────────
def _set_selected(self, val: bool):
self._selected = val
self.update()
def enterEvent(self, event):
self._hovered = True
self.update()
super().enterEvent(event)
def leaveEvent(self, event):
self._hovered = False
self.update()
super().leaveEvent(event)
def paintEvent(self, event):
if self._selected:
if self._selected or self._hovered:
p = QPainter(self)
p.setRenderHint(QPainter.RenderHint.Antialiasing)
p.setBrush(QColor(74, 158, 255, 50))
p.setPen(QPen(QColor("#4a9eff"), 1.5))
if self._selected:
p.setBrush(QColor(74, 158, 255, 50))
p.setPen(QPen(QColor("#4a9eff"), 1.5))
else:
p.setBrush(QColor(128, 128, 128, 36))
p.setPen(Qt.PenStyle.NoPen)
p.drawRoundedRect(1, 1, self.width() - 2, self.height() - 2, 6, 6)
p.end()
super().paintEvent(event)
@@ -305,7 +351,7 @@ class ItemWidget(QWidget):
conn.commit()
conn.close()
self.item_data["path"] = store
self.icon_label.setPixmap(extract_icon(store).pixmap(QSize(36, 36)))
self.icon_label.setPixmap(extract_icon(store).pixmap(QSize(self._icon_px, self._icon_px)))
self._update_name_display()
def _delete_item(self, skip_confirm: bool = False, skip_refresh: bool = False):