Skip to content

优化鼠标光标 & 添加按钮等悬停效果#5757

Open
CiiLu wants to merge 12 commits into
HMCL-dev:mainfrom
CiiLu:bg
Open

优化鼠标光标 & 添加按钮等悬停效果#5757
CiiLu wants to merge 12 commits into
HMCL-dev:mainfrom
CiiLu:bg

Conversation

@CiiLu

@CiiLu CiiLu commented Mar 8, 2026

Copy link
Copy Markdown
Contributor

@Glavo

Glavo commented Mar 8, 2026

Copy link
Copy Markdown
Member

感觉改之后很多地方没有足够的空间显示阴影,看着很奇怪。

@Glavo

Glavo commented Apr 6, 2026

Copy link
Copy Markdown
Member

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the hover animation logic by moving it from RipplerContainer to JFXRippler, introducing a hoverOverlay rectangle and implementing mouse event handlers for background interpolation. The review feedback recommends using Animation or Timeline instead of Transition to prevent visual jumps during rapid mouse movements and suggests simplifying the interpolateBackground method for better efficiency.

Comment thread HMCL/src/main/java/com/jfoenix/controls/JFXRippler.java Outdated
Comment thread HMCL/src/main/java/com/jfoenix/controls/JFXRippler.java
Comment thread HMCL/src/main/java/com/jfoenix/controls/JFXRippler.java
@Glavo

Glavo commented Apr 17, 2026

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b3d2410e06

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread HMCL/src/main/java/com/jfoenix/controls/JFXRippler.java
@Glavo

Glavo commented Apr 27, 2026

Copy link
Copy Markdown
Member

/gemini reivew

@gemini-code-assist

Copy link
Copy Markdown
Contributor

您好,@Glavo。看起来您可能输入了 /gemini reivew,这似乎是一个拼写错误。如果您想请求我进行代码审查,请使用正确的命令 /gemini review。谢谢!

@Glavo

Glavo commented Apr 27, 2026

Copy link
Copy Markdown
Member
image

很多控件都是方形的涟漪,很奇怪。

@Glavo

Glavo commented May 7, 2026

Copy link
Copy Markdown
Member

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request migrates hover animation logic from RipplerContainer to JFXRippler, introducing a hoverOverlay rectangle and mouse event handlers to manage hover states. Feedback focuses on improving the component's reusability by removing hardcoded theme dependencies, correcting the animation interpolators for a more standard UI feel, and optimizing performance in the resetRippler method to prevent excessive object allocation.

Comment on lines +146 to +147
Color onSurface = Themes.getColorScheme().getOnSurface();
hoverOverlay.setFill(Color.color(onSurface.getRed(), onSurface.getGreen(), onSurface.getBlue(), 0.04));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The JFXRippler class now has a direct dependency on org.jackhuang.hmcl.theme.Themes. As a general-purpose UI component, it should ideally remain agnostic of the application's specific theme logic. Hardcoding this dependency makes the component less reusable and breaks the separation of concerns. Consider using a CSS-stylable property or a dedicated hoverFill property that can be configured from the outside.

Comment thread HMCL/src/main/java/com/jfoenix/controls/JFXRippler.java
hoverOverlay.setY(bounds.getMinY() + diffMinY - snappedTopInset());
hoverOverlay.setWidth(control.getLayoutBounds().getWidth());
hoverOverlay.setHeight(control.getLayoutBounds().getHeight());
hoverOverlay.setClip(getMask());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

resetRippler is called frequently during layout changes. Calling getMask() here on every pulse is inefficient because it creates a new Node object each time. This can lead to excessive object allocation and GC pressure during UI transitions (e.g., when the control is resizing). Consider caching the mask node or only updating it when the mask type or significant bounds change.

@CiiLu CiiLu changed the title 悬停效果改为在 JFXRippler 中实现 优化鼠标光标 & 添加按钮等悬停效果 May 16, 2026
CiiLu added 3 commits May 16, 2026 18:22
# Conflicts:
#	HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java
@Minecraft269

Copy link
Copy Markdown

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors hover effects and animations across several UI components, primarily removing explicit hand cursor settings from multiple Java classes and CSS styles, and cleaning up hover transition logic in RipplerContainer. Instead, hover animations are centralized in JFXRippler using a new hoverOverlay rectangle. Feedback on these changes suggests adding a null check for hoverOverlay in the mouse event handler to prevent a potential NullPointerException, and optimizing resetRippler by avoiding setting a clip mask when the mask type is a rectangle to reduce GC pressure.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +139 to +148
mouseEventHandler = event -> {
if (coverAnimation != null) {
coverAnimation.stop();
}

boolean isEntered = event.getEventType() == MouseEvent.MOUSE_ENTERED;
coverAnimation = new Timeline(new KeyFrame(Motion.SHORT4,
new KeyValue(hoverOverlay.opacityProperty(), isEntered ? 1 : 0, isEntered ? Motion.EASE_IN : Motion.EASE_OUT)));
coverAnimation.play();
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

在启用动画的分支中,如果 hoverOverlaynull,调用 hoverOverlay.opacityProperty() 将会抛出 NullPointerException。虽然 hoverOverlaycreateRippleUI() 中进行了初始化,但为了保持与其他方法(如 interpolateBackgroundresetRippler)中对 hoverOverlay 进行空检查的一致性,建议在此处也添加空检查。

Suggested change
mouseEventHandler = event -> {
if (coverAnimation != null) {
coverAnimation.stop();
}
boolean isEntered = event.getEventType() == MouseEvent.MOUSE_ENTERED;
coverAnimation = new Timeline(new KeyFrame(Motion.SHORT4,
new KeyValue(hoverOverlay.opacityProperty(), isEntered ? 1 : 0, isEntered ? Motion.EASE_IN : Motion.EASE_OUT)));
coverAnimation.play();
};
mouseEventHandler = event -> {
if (hoverOverlay == null) {
return;
}
if (coverAnimation != null) {
coverAnimation.stop();
}
boolean isEntered = event.getEventType() == MouseEvent.MOUSE_ENTERED;
coverAnimation = new Timeline(new KeyFrame(Motion.SHORT4,
new KeyValue(hoverOverlay.opacityProperty(), isEntered ? 1 : 0, isEntered ? Motion.EASE_IN : Motion.EASE_OUT)));
coverAnimation.play();
};

Comment on lines +611 to +620
if (hoverOverlay != null && control != null) {
Bounds bounds = control.getBoundsInParent();
double diffMinX = Math.abs(control.getBoundsInLocal().getMinX() - control.getLayoutBounds().getMinX());
double diffMinY = Math.abs(control.getBoundsInLocal().getMinY() - control.getLayoutBounds().getMinY());
hoverOverlay.setX(bounds.getMinX() + diffMinX - snappedLeftInset());
hoverOverlay.setY(bounds.getMinY() + diffMinY - snappedTopInset());
hoverOverlay.setWidth(control.getLayoutBounds().getWidth());
hoverOverlay.setHeight(control.getLayoutBounds().getHeight());
hoverOverlay.setClip(getMask());
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

getMaskType() == RipplerMask.RECT 时,hoverOverlay 本身已经是一个大小完全相同的矩形,因此不需要再设置裁剪遮罩(Clip)。在频繁触发的 resetRippler() 中,每次都创建并设置新的 Rectangle 遮罩会带来不必要的性能开销和 GC 压力。建议在遮罩类型为 RECT 时将 Clip 设为 null 以进行优化。

        if (hoverOverlay != null && control != null) {
            Bounds bounds = control.getBoundsInParent();
            double diffMinX = Math.abs(control.getBoundsInLocal().getMinX() - control.getLayoutBounds().getMinX());
            double diffMinY = Math.abs(control.getBoundsInLocal().getMinY() - control.getLayoutBounds().getMinY());
            hoverOverlay.setX(bounds.getMinX() + diffMinX - snappedLeftInset());
            hoverOverlay.setY(bounds.getMinY() + diffMinY - snappedTopInset());
            hoverOverlay.setWidth(control.getLayoutBounds().getWidth());
            hoverOverlay.setHeight(control.getLayoutBounds().getHeight());
            if (getMaskType() == RipplerMask.RECT) {
                hoverOverlay.setClip(null);
            } else {
                hoverOverlay.setClip(getMask());
            }
        }

@ChouChiu

Copy link
Copy Markdown

/gemini review

其实你这样不断的在 pr 下触发 ai 审查很 spam,我的 inbox 快被这些信息塞爆了

@gemini-code-assist

Copy link
Copy Markdown
Contributor

您好,@ChouChiu。非常抱歉给您的收件箱带来了困扰。作为 AI 助手,我仅在被明确调用时才会参与讨论。如果您认为当前的 PR 讨论已经足够,请随时忽略后续的自动回复。如果您有任何关于代码本身的问题或需要进一步的审查,请随时告知。

@CiiLu

CiiLu commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

其实你这样不断的在 pr 下触发 ai 审查很 spam,我的 inbox 快被这些信息塞爆了

是这样的

@Minecraft269

Copy link
Copy Markdown

/gemini review

其实你这样不断的在 pr 下触发 ai 审查很 spam,我的 inbox 快被这些信息塞爆了

我已认识到我的错误 现在开始我不会在没有请求审查的时候进行 /gemini review

@ChouChiu

ChouChiu commented Jun 27, 2026

Copy link
Copy Markdown

/gemini review

其实你这样不断的在 pr 下触发 ai 审查很 spam,我的 inbox 快被这些信息塞爆了

我已认识到我的错误 现在开始我不会在没有请求审查的时候进行 /gemini review

虽然上面这条评论以及这条评论本身或许也有些 spam 的意味,但我不想纠结于此。我更想指出的是,我理解你是抱着为社区做贡献的初衷,但以下行为确实已经构成了困扰:

  1. 在 PR 下反复触发 AI 审查,例如:
    优化鼠标光标 & 添加按钮等悬停效果 #5757 (comment)
    [Feature] 模组等的更新日志以及 HTMLRenderer 的完善 #6098 (comment)
    [Enhancement] 原理图管理优化  #6094 (comment)

  2. 频繁 @ 他人(Ping)。据我所知,Mention 功能一般用于比较紧急的场景,随意使用会形成骚扰;尤其是在安装 GitHub Mobile 后,这些 @ 会直接推送到手机,而不仅仅是留在 inbox 里。相关例子:
    Add UI scale setting to personalization page #6061 (comment)
    [Feature] 让HMCL可以识别到JIJ的打包形式并以FlowPane的形式显示,并支持搜索和导出JIJ信息,且在崩溃时若有JIJ信息则一并打包至压缩包内 #5383 (comment)

这些操作,包括 PR 审查、进度追踪等,本应由项目维护人员负责,而不是由我们这样的 Outsider 越俎代庖。你的行为会向至少 98 人的 inbox 和邮箱发送通知,实实在在地造成了骚扰。

这条评论也不是旨在贬低或者什么,你也有在社区中发表了有价值的意见,例如:
#6199 (comment)

@Minecraft269

Minecraft269 commented Jun 27, 2026

Copy link
Copy Markdown

虽然上面这条评论以及这条评论本身或许也有些 spam 的意味

总得回复吧?

  1. 在 PR 下反复触发 AI 审查,例如:
    优化鼠标光标 & 添加按钮等悬停效果 #5757 (comment)

我先不说 (就在本PR)

[Feature] 模组等的更新日志以及 HTMLRenderer 的完善 #6098 (comment)
[Enhancement] 原理图管理优化  #6094 (comment)

这些是没有什么明确性的拒绝及在公测群内反应

  1. 频繁 @ 他人(Ping)。据我所知,Mention 功能一般用于比较紧急的场景,随意使用会形成骚扰;尤其是在安装 GitHub Mobile 后,这些 @ 会直接推送到手机,而不仅仅是留在 inbox 里。相关例子:
    Add UI scale setting to personalization page #6061 (comment)

这个我有频繁@他人吗? 就@了一次诶

[Feature] 让HMCL可以识别到JIJ的打包形式并以FlowPane的形式显示,并支持搜索和导出JIJ信息,且在崩溃时若有JIJ信息则一并打包至压缩包内 #5383 (comment)

我和这位贡献者是有聊天方式的 所以请不要见怪不怪

这些操作,包括 PR 审查、进度追踪等,本应由项目维护人员负责,而不是由我们这样的 Outsider 越俎代庖。你的行为会向至少 98 人的 inbox 和邮箱发送通知,实实在在地造成了骚扰。

我无法反驳

这条评论也不是旨在贬低或者什么,你也有在社区中发表了有价值的意见,例如: #6199 (comment)

但是我指出一些我个人的观点: 1.基本上 我的一些动作是在公测群里有反馈的若有什么不合理行为我不会继续下去(就像我之前说想把每个停更太久的PR ping下author) 2.反复触发审查并不是我的个人行为 包括主要维护者(Glavo)
在内都会对一个PR进行重复审查 3.过去的事就过去了 个人的行为可以改正 而不是在旧伤上继续将伤口扩大 需要让伤口愈合(长记性) 4.我的行为基本上都有在公测群里反映过所以并不会产生什么影响 且如果有 可以使用
image

(如果有什么说错的地方请见谅(因为我不怎么会表达 但是最后也谢谢你指出我的错误))

@3gf8jv4dv

3gf8jv4dv commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

2.反复触发审查并不是我的个人行为 包括主要维护者(Glavo)在内都会对一个PR进行重复审查

其他的 ChouChiu 说的比较透了。关于你说的这一点,我个人的看法是,对自己的 PR 召唤 AI review 无可厚非 (#6235#6238 就是良好的例子);对于其他人的 PR,除非你是维护者,或者你和 PR 作者认识/经过沟通,否则不应该主动召唤 AI review。这本质上还是礼节问题。

此外,也不是所有 PR 都一定要 AI 来 review。一些比较简单、规模小、扫两眼就能确认的 PR 就没有用牛刀的必要。如果反复召唤 AI 导致一个小 PR 堆积了几十甚至上百的评论,那也算是 spam。

Edit 1: 润色

@Minecraft269

Copy link
Copy Markdown

2.反复触发审查并不是我的个人行为 包括主要维护者(Glavo)在内都会对一个PR进行重复审查

其他的 ChouChiu 说的比较透了。关于你说的这一点,我个人的看法是,对自己的 PR 召唤 AI review 无可厚非,也无可指摘 (#6235#6238 就是良好的例子);对于其他人的 PR,除非你是维护者,或者你和 PR 作者认识/经过沟通,否则不应该主动召唤 AI review。这本质上还是礼节问题。

此外,也不是所有 PR 都一定要 AI 来 review。一些比较简单、规模小、扫两眼就能确认的 PR 就没有用牛刀的必要。如果反复召唤 AI 导致一个小 PR 堆积了几十甚至上百的评论,那也算是 spam。

thanks 指正 but

或者你和 PR 作者认识/经过沟通

这是在公测群里有聊过的 但是 除了这个 #6061 但又说回来 #1771 情况不差不多吗?

我觉得这个话题可以就此而止了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants