diff --git a/app/common/icons.py b/app/common/icons.py index ae394a15..05885c78 100644 --- a/app/common/icons.py +++ b/app/common/icons.py @@ -59,6 +59,7 @@ class Icon(FluentIconBase, Enum): TEXTCHECK = 'TextCheck' DOCUMENT = 'Document' ARROWREPEAT = "ArrowRepeat" + QUESTION_CIRCLE = 'QuestionCircle' def path(self, theme=Theme.AUTO): return f'./app/resource/icons/{self.value}_{getIconColor(theme)}.svg' diff --git a/app/components/profile_level_icon_widget.py b/app/components/profile_level_icon_widget.py index 0358d5a3..1417f6fd 100644 --- a/app/components/profile_level_icon_widget.py +++ b/app/components/profile_level_icon_widget.py @@ -10,6 +10,7 @@ from app.common.qfluentwidgets import (ProgressRing, ToolTipFilter, ToolTipPosition, isDarkTheme, themeColor, FlyoutViewBase, TextWrap, FlyoutAnimationType) from app.components.color_label import ColorLabel +from app.components.tool_tip import CustomToolTip from app.common.style_sheet import StyleSheet @@ -139,13 +140,15 @@ def updateAramInfo(self, info): if not self.mFlyout or not info: return - self.mFlyout.updateInfo(info) + self.mFlyout.view.updateInfo(info) def enterEvent(self, a0): if not self.aramInfo or self.mFlyout: return - self.mFlyout = AramFlyout(self.aramInfo, self) + view = AramFlyoutView(self.aramInfo) + + self.mFlyout = CustomToolTip(view, self) self.mFlyout.show() super().enterEvent(a0) @@ -158,118 +161,6 @@ def leaveEvent(self, a0): self.mFlyout = None -class AramFlyout(QWidget): - def __init__(self, info, target: QWidget, parent=None): - super().__init__(parent) - self.target = target - - self.hBoxLayout = QHBoxLayout(self) - self.view = AramFlyoutView(info, parent) - self.setWindowFlags(Qt.ToolTip | Qt.FramelessWindowHint | - Qt.NoDropShadowWindowHint) - self.setAttribute(Qt.WA_TranslucentBackground) - - self.hBoxLayout.addWidget(self.view) - self.hBoxLayout.setContentsMargins(15, 8, 15, 20) - - self.__initShadowEffect() - self.__initAnimation() - - def __initShadowEffect(self, blurRadius=35, offset=(0, 8)): - color = QColor(0, 0, 0, 80 if isDarkTheme() else 30) - - self.shadowEffect = QGraphicsDropShadowEffect(self.view) - self.shadowEffect.setBlurRadius(blurRadius) - self.shadowEffect.setOffset(*offset) - self.shadowEffect.setColor(color) - - self.view.setGraphicsEffect(None) - self.view.setGraphicsEffect(self.shadowEffect) - - def __initAnimation(self): - self.inOpacityAni = QPropertyAnimation( - self, b'windowOpacity', self.parent()) - self.inSlideAni = QPropertyAnimation(self, b'pos', self.parent()) - - self.inOpacityAni.setDuration(187) - self.inSlideAni.setDuration(187) - - self.inOpacityAni.setEasingCurve(QEasingCurve.InOutQuad) - self.inSlideAni.setEasingCurve(QEasingCurve.InOutQuad) - - self.inOpacityAni.setStartValue(0) - self.inOpacityAni.setEndValue(1) - - self.inAniGroup = QParallelAnimationGroup(self) - self.inAniGroup.addAnimation(self.inOpacityAni) - self.inAniGroup.addAnimation(self.inSlideAni) - - self.outAniGroup = QParallelAnimationGroup(self) - - self.outOpacityAni = QPropertyAnimation( - self, b'windowOpacity', self.parent()) - self.outOpacityAni.setEasingCurve(QEasingCurve.InOutQuad) - self.outOpacityAni.setStartValue(1) - self.outOpacityAni.setEndValue(0) - self.outOpacityAni.setDuration(120) - - self.outSlideAni = QPropertyAnimation(self, b'pos', self.parent()) - self.outSlideAni.setEasingCurve(QEasingCurve.InOutQuad) - self.outSlideAni.setDuration(120) - - self.outAniGroup.addAnimation(self.outOpacityAni) - self.outAniGroup.addAnimation(self.outSlideAni) - - self.outAniGroup.finished.connect(self.close) - - def showEvent(self, e): - pos = self.getPosition() - - if self.animationType == FlyoutAnimationType.SLIDE_LEFT: - self.inSlideAni.setStartValue(pos + QPoint(10, 0)) - else: - self.inSlideAni.setStartValue(pos - QPoint(10, 0)) - - self.inSlideAni.setEndValue(pos) - self.inAniGroup.start() - - return super().showEvent(e) - - def fadeOut(self): - self.outSlideAni.setStartValue(self.pos()) - - if self.animationType == FlyoutAnimationType.SLIDE_LEFT: - self.outSlideAni.setEndValue(self.pos() + QPoint(10, 0)) - else: - self.outSlideAni.setEndValue(self.pos() - QPoint(10, 0)) - - self.outAniGroup.finished.connect(self.close) - self.outAniGroup.start() - - def getPosition(self): - pos = self.target.mapToGlobal(QPoint()) - x, y = pos.x(), pos.y() - - hintWidth = self.sizeHint().width() - hintHeight = self.sizeHint().height() - - x += self.target.width() // 2 - hintWidth // 2 - y += self.target.height() // 2 - hintHeight // 2 - - dx = -hintWidth // 2 - 45 - - self.animationType = FlyoutAnimationType.SLIDE_LEFT - - if x + dx < -15: - self.animationType = FlyoutAnimationType.SLIDE_RIGHT - dx = -dx - - return QPoint(x + dx, y) - - def updateInfo(self, info): - self.view.updateInfo(info) - - class AramFlyoutView(FlyoutViewBase): def __init__(self, info, parent=None): super().__init__(parent=parent) diff --git a/app/components/tool_tip.py b/app/components/tool_tip.py new file mode 100644 index 00000000..241f5494 --- /dev/null +++ b/app/components/tool_tip.py @@ -0,0 +1,147 @@ + +from PyQt5.QtCore import ( + Qt, QPoint, QPropertyAnimation, QParallelAnimationGroup, QEasingCurve, + QSize) +from PyQt5.QtGui import QColor +from PyQt5.QtWidgets import (QWidget, QHBoxLayout, + QGraphicsDropShadowEffect) + +from app.common.qfluentwidgets import (isDarkTheme, TransparentToolButton, + FlyoutAnimationType, FluentIcon) +from app.common.icons import Icon + + +class CustomToolTip(QWidget): + def __init__(self, view: QWidget, target: QWidget, parent=None): + super().__init__(parent) + self.target = target + + self.hBoxLayout = QHBoxLayout(self) + self.view = view + self.setWindowFlags(Qt.ToolTip | Qt.FramelessWindowHint | + Qt.NoDropShadowWindowHint) + self.setAttribute(Qt.WA_TranslucentBackground) + + self.hBoxLayout.addWidget(self.view) + self.hBoxLayout.setContentsMargins(15, 8, 15, 20) + + self.__initShadowEffect() + self.__initAnimation() + + def __initShadowEffect(self, blurRadius=35, offset=(0, 8)): + color = QColor(0, 0, 0, 80 if isDarkTheme() else 30) + + self.shadowEffect = QGraphicsDropShadowEffect(self.view) + self.shadowEffect.setBlurRadius(blurRadius) + self.shadowEffect.setOffset(*offset) + self.shadowEffect.setColor(color) + + self.view.setGraphicsEffect(None) + self.view.setGraphicsEffect(self.shadowEffect) + + def __initAnimation(self): + self.inOpacityAni = QPropertyAnimation( + self, b'windowOpacity', self.parent()) + self.inSlideAni = QPropertyAnimation(self, b'pos', self.parent()) + + self.inOpacityAni.setDuration(187) + self.inSlideAni.setDuration(187) + + self.inOpacityAni.setEasingCurve(QEasingCurve.InOutQuad) + self.inSlideAni.setEasingCurve(QEasingCurve.InOutQuad) + + self.inOpacityAni.setStartValue(0) + self.inOpacityAni.setEndValue(1) + + self.inAniGroup = QParallelAnimationGroup(self) + self.inAniGroup.addAnimation(self.inOpacityAni) + self.inAniGroup.addAnimation(self.inSlideAni) + + self.outAniGroup = QParallelAnimationGroup(self) + + self.outOpacityAni = QPropertyAnimation( + self, b'windowOpacity', self.parent()) + self.outOpacityAni.setEasingCurve(QEasingCurve.InOutQuad) + self.outOpacityAni.setStartValue(1) + self.outOpacityAni.setEndValue(0) + self.outOpacityAni.setDuration(120) + + self.outSlideAni = QPropertyAnimation(self, b'pos', self.parent()) + self.outSlideAni.setEasingCurve(QEasingCurve.InOutQuad) + self.outSlideAni.setDuration(120) + + self.outAniGroup.addAnimation(self.outOpacityAni) + self.outAniGroup.addAnimation(self.outSlideAni) + + self.outAniGroup.finished.connect(self.close) + + def showEvent(self, e): + pos = self.getPosition() + + if self.animationType == FlyoutAnimationType.SLIDE_LEFT: + self.inSlideAni.setStartValue(pos + QPoint(10, 0)) + else: + self.inSlideAni.setStartValue(pos - QPoint(10, 0)) + + self.inSlideAni.setEndValue(pos) + self.inAniGroup.start() + + return super().showEvent(e) + + def fadeOut(self): + self.outSlideAni.setStartValue(self.pos()) + + if self.animationType == FlyoutAnimationType.SLIDE_LEFT: + self.outSlideAni.setEndValue(self.pos() + QPoint(10, 0)) + else: + self.outSlideAni.setEndValue(self.pos() - QPoint(10, 0)) + + self.outAniGroup.finished.connect(self.close) + self.outAniGroup.start() + + def getPosition(self): + pos = self.target.mapToGlobal(QPoint()) + x, y = pos.x(), pos.y() + + hintWidth = self.sizeHint().width() + hintHeight = self.sizeHint().height() + + x += self.target.width() // 2 - hintWidth // 2 + y += self.target.height() // 2 - hintHeight // 2 + + dx = -hintWidth // 2 - 45 + + self.animationType = FlyoutAnimationType.SLIDE_LEFT + + if x + dx < -15: + self.animationType = FlyoutAnimationType.SLIDE_RIGHT + dx = -dx + + return QPoint(x + dx, y) + + +class HelpButton(TransparentToolButton): + def __init__(self, view: QWidget, parent: QWidget = None): + super().__init__(Icon.QUESTION_CIRCLE, parent=parent) + + self.setFixedSize(QSize(26, 26)) + self.setFixedSize(QSize(16, 16)) + + self.view = view + self.mToolTip = None + + def enterEvent(self, e): + if self.mToolTip: + return + + self.mToolTip = CustomToolTip(self.view, self) + self.mToolTip.show() + + return super().enterEvent(e) + + def leaveEvent(self, a0): + if not self.mToolTip: + return + + self.mToolTip.fadeOut() + self.mToolTip = None diff --git a/app/resource/i18n/Seraphine.zh_CN.qm b/app/resource/i18n/Seraphine.zh_CN.qm index 352ef0eb..eb57fc63 100644 Binary files a/app/resource/i18n/Seraphine.zh_CN.qm and b/app/resource/i18n/Seraphine.zh_CN.qm differ diff --git a/app/resource/i18n/Seraphine.zh_CN.ts b/app/resource/i18n/Seraphine.zh_CN.ts index c92abb2b..714b529e 100644 --- a/app/resource/i18n/Seraphine.zh_CN.ts +++ b/app/resource/i18n/Seraphine.zh_CN.ts @@ -42,37 +42,37 @@ AramFlyoutView - + Damage Dealt: 造成伤害: - + Damage Received: 承受伤害: - + Healing Increase: 治疗效果: - + Shield Increase: 护盾效果: - + Ability Haste: 技能急速: - + Tenacity: 韧性: - + Powered by: jddld.com 数据来源:jddld.com @@ -141,12 +141,12 @@ 已启用,自动禁用: - + Disabled 未启用 - + Enable: 启用自动 Ban: @@ -156,70 +156,79 @@ 若队友预选该英雄,则空 Ban: - + Ban after a delay of seconds: 在进入禁用阶段后 Ban 人的秒数: - + Default Configurations 默认设置 - + Default champions: 默认禁用英雄: - + Choose 选择 - + Rank Configurations 按照位置设置 - + Top: 上路: - + Juggle: 打野: - + Mid: 中路: - + Bottom: 下路: - + Support: 辅助: - + Prevent banning champions picked by teammates: 若队友预选该英雄,则空 Ban: - + Reset 恢复默认 - + Enabled 已启用 + + + Default settings must be set. + +If champions set by lane are not available, default settings will be used. + 必须设置默认英雄 + +若非排位模式或按位置设置英雄不可用,则将使用默认设置 + AutoSelectChampionCard @@ -234,7 +243,7 @@ 已启用,自动选择: - + Disabled 未启用 @@ -244,7 +253,7 @@ 将要自动亮起的英雄: - + Enable: 启用自动亮起: @@ -254,7 +263,7 @@ 在时间结束后确定选择(更换亮起英雄后无效) - + Completed before timeout: 在时间结束时确定选择: @@ -279,55 +288,64 @@ 默认设置 - + Default champions: 默认亮起英雄: - + Choose 选择 - + Rank Configurations 按照位置设置 - + Top: 上路: - + Juggle: 打野: - + Mid: 中路: - + Bottom: 下路: - + Support: 辅助: - + Enabled 已启用 - + Reset 恢复默认 + + + Default settings must be set. + +If champions set by lane are not available, default settings will be used. + 必须设置默认英雄 + +若非排位模式或按位置设置英雄不可用,则将使用默认设置 + AuxiliaryInterface @@ -941,17 +959,17 @@ GameTab - + remake 重开 - + win 胜利 - + lose 失败 @@ -1142,7 +1160,7 @@ 对局信息 - + Start LOL 启动游戏 @@ -1162,12 +1180,12 @@ 客户端已连接 - + Invalid path 路径非法 - + Please set the correct directory of the LOL client in the setting page 请在设置页面中设置正确的 LOL 客户端路径 @@ -1177,52 +1195,52 @@ 启动页 - + Start LOL successfully 启动客户端成功 - + Home 游戏大厅 - + Selecting Champions 英雄选择 - + Gaming 游戏中 - + Waiting for status 等待游戏结果 - + End of game 游戏结束 - + Lobby 房间组队中 - + Ready check 匹配确认 - + Match making 匹配中 - + Exception occurred 😥 程序出现异常 😥 @@ -1232,12 +1250,12 @@ 战绩查询 👀 - + Exit 直接退出 - + Minimize 最小化到任务栏 @@ -1247,7 +1265,7 @@ 退出 - + Do you wish to exit? 你第一次点击了关闭按钮 @@ -1288,17 +1306,17 @@ 客户端信息请求失败 - + Blue Team 蓝色方 - + Red Team 红色方 - + Waiting reconnect 等待重新连接 @@ -1338,22 +1356,22 @@ 请确保能连接至 GitHub - + ( - + ) - + , - + Choose action for close button (you can modify it at any time in the settings page) 请选择点击关闭按钮的默认行为 @@ -1983,82 +2001,82 @@ 客户端路径 - + Auto-start LOL 自动启动游戏 - + Launch LOL client upon opening Seraphine automatically 启动 Seraphine 时自动启动 LOL 客户端 - + Personalization 个性化 - + Application theme 应用主题 - + Change the appearance of Seraphine 调整 Seraphine 的外观主题 - + Light 浅色 - + Dark 深色 - + Use system setting 跟随系统设置 - + Theme color 主题色 - + Change the theme color of Seraphine 调整 Seraphine 的主题色 - + Interface zoom 界面缩放 - + Change the size of widgets and fonts 调整部件和字体的大小 - + Updated successfully 更新成功 - + Configuration takes effect after restart 设置在重启软件后生效 - + Language 语言 - + Set your preferred language for Seraphine 选择 Seraphine 所使用的语言 @@ -2089,52 +2107,52 @@ 在对局详情界面中显示段位图标,启动该选项将影响加载该界面的速度 - + About 关于 - + Provide feedback 提供反馈 - + Help us improve Seraphine by providing feedback 通过提供反馈帮助我们改善 Seraphine - + Copyright 版权所有 - + Version 当前版本 - + Delete 删除 - + Delete cache 清除缓存 - + Delete all game resources (Apply it when game resources update) 删除所有游戏资源的缓存(建议在游戏资源有更新时使用) - + View GitHub 查看 GitHub - + Really? 真的要删除吗? @@ -2146,32 +2164,32 @@ 这有可能会消耗更多的时间 - + Confirm delete 确定删除 - + Mica effect 云母效果 - + Apply semi transparent to windows and surfaces (only available on Win11) 窗口和表面显示半透明(仅在 Win11 上可用) - + Minimize to tray on close 最小化到任务栏托盘 - + Minimize to system tray when clicking close 点击右上角关闭时将程序最小化到托盘 - + Settings have been applied 设置已应用 @@ -2186,12 +2204,12 @@ 打开此选项后,当你在排位时,对局信息界面将只显示排位模式对局战绩 - + Check for updates 检查更新 - + Automatically check for updates when software starts 在 Seraphine 启动时自动检查更新 @@ -2203,20 +2221,20 @@ Minimize windows during game activities - 游戏进行时最小化 Seraphine 窗口 + 游戏进行时最小化 Seraphine 窗口 Reduce CPU usage for rendering UI during gaming - 在游戏时通过避免渲染窗口以减少 CPU 使用 + 在游戏时通过避免渲染窗口以减少 CPU 使用 - + Log Level 日志等级 - + The level of logging for Seraphine (take effect after restart) 修改 Seraphine 记录日志的等级(重启后生效) @@ -2226,12 +2244,12 @@ HTTP 代理 - + Using a proxy when connecting to GitHub 连接 GitHub 时启用 HTTP 代理 - + Update 软件更新 @@ -2251,17 +2269,17 @@ 该值越大数据加载速度越快,但越可能引起客户端闪退 - + Game tabs color 对局卡片颜色 - + Change the color of game tabs 改变对局卡片提示胜利 / 失败的颜色 - + HTTP proxy HTTP 代理 @@ -2271,22 +2289,22 @@ 在对局详情界面中显示段位图标,启动该选项将影响加载该界面的速度 - + Open 打开文件夹 - + Log file 日志文件 - + Open log directory 打开日志文件夹 - + Game resources will be downloaded again when they are used by Seraphine, which will cost more time 游戏资源将会在它们要被 Seraphine 使用时重新下载 @@ -2425,12 +2443,12 @@ when they are used by Seraphine, which will cost more time SummonerInfoView - + Ranked Solo / Duo 单 / 双排 - + Ranked Flex 灵活排位 diff --git a/app/resource/icons/QuestionCircle_black.svg b/app/resource/icons/QuestionCircle_black.svg new file mode 100644 index 00000000..bcf8c728 --- /dev/null +++ b/app/resource/icons/QuestionCircle_black.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/app/resource/icons/QuestionCircle_white.svg b/app/resource/icons/QuestionCircle_white.svg new file mode 100644 index 00000000..e910b09d --- /dev/null +++ b/app/resource/icons/QuestionCircle_white.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/app/view/auxiliary_interface.py b/app/view/auxiliary_interface.py index 81272bff..09d4a999 100644 --- a/app/view/auxiliary_interface.py +++ b/app/view/auxiliary_interface.py @@ -9,7 +9,7 @@ qconfig, IndicatorPosition, InfoBar, InfoBarPosition, SpinBox, ExpandGroupSettingCard, TransparentToolButton, FluentIcon, Flyout, FlyoutAnimationType, TeachingTip, - MessageBox, CheckBox) + MessageBox, CheckBox, ToolTipFilter, ToolTipPosition) from PyQt5.QtCore import Qt, pyqtSignal, QEvent, QSize from PyQt5.QtWidgets import (QWidget, QLabel, QCompleter, QVBoxLayout, QHBoxLayout, QGridLayout, @@ -1208,6 +1208,8 @@ def __init__(self, title, content=None, self.defaultCfgWidget = QWidget(self.view) self.defaultCfgLayout = QGridLayout(self.defaultCfgWidget) self.defaultHintLabel = QLabel(self.tr("Default Configurations")) + self.helpLayout = QHBoxLayout() + self.helpButotn = TransparentToolButton(Icon.QUESTION_CIRCLE) self.defaultLabel = QLabel(self.tr("Default champions: ")) self.defaultChampions = ChampionsCard() @@ -1251,6 +1253,14 @@ def __initWidget(self): self.defaultHintLabel.setStyleSheet("font: bold") self.rankLabel.setStyleSheet("font: bold") + self.helpButotn.setFixedSize(QSize(26, 26)) + self.helpButotn.setIconSize(QSize(16, 16)) + + self.helpButotn.setToolTip(self.tr( + "Default settings must be set.\n\nIf champions set by lane are not available, default settings will be used.")) + self.helpButotn.installEventFilter(ToolTipFilter( + self.helpButotn, 0, ToolTipPosition.RIGHT)) + # 逻辑是,必须要设置默认,才能设置具体分路和启动功能 selected = qconfig.get(self.defaultChampionsConfigItem) != [] checked = qconfig.get(self.enableConfigItem) @@ -1286,8 +1296,13 @@ def __initLayout(self): self.defaultCfgLayout.setContentsMargins(48, 18, 44, 18) self.defaultCfgLayout.setSizeConstraint(QHBoxLayout.SetMinimumSize) - self.defaultCfgLayout.addWidget( - self.defaultHintLabel, 0, 0, Qt.AlignLeft) + self.helpLayout.setContentsMargins(0, 0, 0, 0) + self.helpLayout.setSpacing(10) + self.helpLayout.addWidget(self.defaultHintLabel) + self.helpLayout.addWidget(self.helpButotn) + + self.defaultCfgLayout.addLayout( + self.helpLayout, 0, 0, Qt.AlignLeft) self.defaultCfgLayout.addWidget( self.defaultLabel, 1, 0, Qt.AlignLeft) @@ -1457,6 +1472,8 @@ def __init__(self, title, content=None, self.defaultCfgWidget = QWidget(self.view) self.defaultCfgLayout = QGridLayout(self.defaultCfgWidget) self.defaultHintLabel = QLabel(self.tr("Default Configurations")) + self.helpLayout = QHBoxLayout() + self.helpButotn = TransparentToolButton(Icon.QUESTION_CIRCLE) self.defaultLabel = QLabel(self.tr("Default champions: ")) self.defaultChampions = ChampionsCard() @@ -1508,6 +1525,14 @@ def __initWidget(self): delayTime = qconfig.get(self.delayTimeConfigItem) friendlyEnabled = qconfig.get(self.friendlyConfigItem) + self.helpButotn.setFixedSize(QSize(26, 26)) + self.helpButotn.setIconSize(QSize(16, 16)) + + self.helpButotn.setToolTip(self.tr( + "Default settings must be set.\n\nIf champions set by lane are not available, default settings will be used.")) + self.helpButotn.installEventFilter(ToolTipFilter( + self.helpButotn, 0, ToolTipPosition.RIGHT)) + for ty in ['default', 'top', 'jug', 'mid', 'bot', 'sup']: button: PushButton = getattr(self, f"{ty}SelectButton") button.setMinimumWidth(100) @@ -1545,8 +1570,13 @@ def __initLayout(self): self.defaultCfgLayout.setContentsMargins(48, 18, 44, 18) self.defaultCfgLayout.setSizeConstraint(QHBoxLayout.SetMinimumSize) - self.defaultCfgLayout.addWidget( - self.defaultHintLabel, 0, 0, Qt.AlignLeft) + self.helpLayout.setContentsMargins(0, 0, 0, 0) + self.helpLayout.setSpacing(10) + self.helpLayout.addWidget(self.defaultHintLabel) + self.helpLayout.addWidget(self.helpButotn) + + self.defaultCfgLayout.addLayout( + self.helpLayout, 0, 0, Qt.AlignLeft) self.defaultCfgLayout.addWidget( self.defaultLabel, 1, 0, Qt.AlignLeft)