Skip to content

Commit 2ba3611

Browse files
committed
Merge remote-tracking branch 'origin/main' into mod-download-list-page-skin-refactor
2 parents 7e6caf7 + 886319a commit 2ba3611

92 files changed

Lines changed: 6226 additions & 753 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gemini/config.yaml

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
name: PR Size Label
2+
3+
on:
4+
pull_request_target:
5+
types:
6+
- opened
7+
- reopened
8+
- synchronize
9+
workflow_dispatch:
10+
11+
12+
permissions:
13+
contents: read
14+
issues: write
15+
pull-requests: write
16+
17+
jobs:
18+
Label:
19+
runs-on: ubuntu-latest
20+
21+
steps:
22+
- name: Label PR by filtered changed lines
23+
uses: actions/github-script@v7
24+
with:
25+
github-token: ${{ secrets.GITHUB_TOKEN }}
26+
script: |
27+
const owner = context.repo.owner
28+
const repo = context.repo.repo
29+
const ignoredFilePatterns = [
30+
/(?:^|\/)\.cnb\/.+\.ya?ml$/i,
31+
/(?:^|\/)\.gemini\/.+\.ya?ml$/i,
32+
/(?:^|\/)\.gitee\/.+\.ya?ml$/i,
33+
/(?:^|\/)\.github\/.+\.ya?ml$/i,
34+
/(?:^|\/)\.idea\/.+\.xml$/i,
35+
/(?:^|\/)\.editorconfig$/i,
36+
/(?:^|\/)\.gitattributes$/i,
37+
/(?:^|\/)\.gitignore$/i,
38+
/(?:^|\/).+\.kts$/i,
39+
/(?:^|\/).+\.md$/i,
40+
/(?:^|\/)LICENSE(?:_.*)?$/i,
41+
/(?:^|\/)gradlew(?:\.bat|\.ps1)?$/i,
42+
/(?:^|\/)gradle\/wrapper\/gradle-wrapper\.properties$/i,
43+
/(?:^|\/)lang\/.+\.properties$/i,
44+
/(?:^|\/)l10n\/.+\.properties$/i,
45+
/(?:^|\/)checkstyle\.xml$/i,
46+
/(?:^|\/).+\.(?:png|jpe?g|gif|ico|svg|json|ttf|woff2?|webp|avif|jxl|heic|mp4|webm|mp3|ogg|wav|txt|toml)$/i
47+
]
48+
49+
async function processPR(issue_number) {
50+
const files = await github.paginate(github.rest.pulls.listFiles, {
51+
owner,
52+
repo,
53+
pull_number: issue_number,
54+
per_page: 100,
55+
})
56+
57+
const countedFiles = []
58+
const ignoredFiles = []
59+
let totalChanges = 0
60+
61+
for (const file of files) {
62+
const ignored = ignoredFilePatterns.some(pattern => pattern.test(file.filename))
63+
if (ignored) {
64+
ignoredFiles.push(file.filename)
65+
continue
66+
}
67+
68+
const fileChanges = file.changes ?? ((file.additions || 0) + (file.deletions || 0))
69+
totalChanges += fileChanges
70+
countedFiles.push(`${file.filename} (${fileChanges})`)
71+
}
72+
73+
let targetLabel
74+
if (totalChanges <= 9) targetLabel = '1+'
75+
else if (totalChanges <= 39) targetLabel = '10+'
76+
else if (totalChanges <= 99) targetLabel = '40+'
77+
else if (totalChanges <= 499) targetLabel = '100+'
78+
else if (totalChanges <= 999) targetLabel = '500+'
79+
else if (totalChanges <= 1999) targetLabel = '1000+'
80+
else if (totalChanges <= 4999) targetLabel = '2000+'
81+
else targetLabel = '5000+'
82+
83+
const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
84+
owner,
85+
repo,
86+
issue_number,
87+
per_page: 100,
88+
})
89+
90+
const sizeLabels = currentLabels
91+
.map(label => label.name)
92+
.filter(name => /^\d+\+$/.test(name))
93+
94+
for (const name of sizeLabels) {
95+
if (name !== targetLabel) {
96+
await github.rest.issues.removeLabel({
97+
owner,
98+
repo,
99+
issue_number,
100+
name,
101+
}).catch(() => {})
102+
}
103+
}
104+
105+
if (!currentLabels.some(label => label.name === targetLabel)) {
106+
await github.rest.issues.addLabels({
107+
owner,
108+
repo,
109+
issue_number,
110+
labels: [targetLabel],
111+
})
112+
}
113+
114+
core.info(`Pull request #${issue_number} has ${totalChanges} counted changed lines, applied ${targetLabel}.`)
115+
core.info(`Ignored files: ${ignoredFiles.length ? ignoredFiles.join(', ') : 'none'}`)
116+
core.info(`Counted files: ${countedFiles.length ? countedFiles.join(', ') : 'none'}`)
117+
}
118+
119+
if (context.eventName === 'workflow_dispatch') {
120+
core.info('Manual trigger detected. Fetching all open pull requests...')
121+
122+
const openPRs = await github.paginate(github.rest.pulls.list, {
123+
owner,
124+
repo,
125+
state: 'open',
126+
per_page: 100,
127+
})
128+
129+
const unlabelledPRs = openPRs.filter(pr => {
130+
return !pr.labels.some(label => /^\d+\+$/.test(label.name))
131+
})
132+
133+
core.info(`Found ${openPRs.length} open pull request(s), ${unlabelledPRs.length} of them lack a size label.`)
134+
135+
for (const pr of unlabelledPRs) {
136+
core.info(`----------------------------------------`)
137+
core.info(`Processing unlabelled pull request #${pr.number}: ${pr.title}`)
138+
await processPR(pr.number)
139+
}
140+
} else {
141+
const pr = context.payload.pull_request
142+
if (!pr) {
143+
core.info('No pull request found in payload, skipping.')
144+
return
145+
}
146+
await processPR(pr.number)
147+
}

AGENTS.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ These rules apply to all Java code written or modified in this repository.
44

55
## Nullability
66

7-
- Annotate every class with JetBrains Annotations `@NotNullByDefault`.
8-
- Any type, field, parameter, return value, local variable, or generic type argument that may be `null` must be explicitly annotated with `@Nullable`.
9-
- Nullability must never be implicit.
7+
- Every class declared in a newly added Java source file must be annotated with JetBrains Annotations `@NotNullByDefault`.
8+
- When writing or modifying Java code, any type, field, parameter, return value, local variable, or generic type argument that may be `null` must be explicitly annotated with `@Nullable`.
9+
- Nullability in code being written or modified must never be implicit.
1010

1111
## Immutability
1212

@@ -15,6 +15,8 @@ These rules apply to all Java code written or modified in this repository.
1515

1616
## Documentation
1717

18+
Apply the following requirements when writing or modifying code. Do not use them as code-review criteria.
19+
1820
- Every class, field, and method must have documentation.
1921
- Documentation must use `///` Markdown-style Javadoc comments.
2022
- Keep documentation accurate and specific to the actual behavior, constraints, and side effects.

HMCL/build.gradle.kts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ if (buildNumber != null) {
5252
}
5353
}
5454

55-
val embedResources by configurations.registering
55+
val embedResources = configurations.register("embedResources")
5656

5757
dependencies {
5858
implementation(project(":HMCLCore"))
@@ -166,7 +166,7 @@ val hmclProperties = buildList {
166166
}
167167

168168
val hmclPropertiesFile = layout.buildDirectory.file("hmcl.properties")
169-
val createPropertiesFile by tasks.registering {
169+
val createPropertiesFile = tasks.register("createPropertiesFile") {
170170
outputs.file(hmclPropertiesFile)
171171
hmclProperties.forEach { (k, v) -> inputs.property(k, v) }
172172

@@ -258,7 +258,7 @@ tasks.processResources {
258258

259259
fun artifactFile(ext: String) = jarPath.resolveSibling(jarPath.nameWithoutExtension + '.' + ext)
260260

261-
val makeExecutables by tasks.registering {
261+
val makeExecutables = tasks.register("makeExecutables") {
262262
val extensions = listOf("exe", "sh")
263263

264264
dependsOn(tasks.jar)
@@ -286,7 +286,7 @@ val makeExecutables by tasks.registering {
286286
}
287287
}
288288

289-
val makeDeb by tasks.registering(CreateDeb::class) {
289+
val makeDeb = tasks.register("makeDeb", CreateDeb::class) {
290290
dependsOn(makeExecutables)
291291

292292
val debFile = layout.file(provider { artifactFile("deb") })
@@ -438,19 +438,19 @@ tasks.register<CheckTranslations>("checkTranslations") {
438438

439439
val generatedDir = layout.buildDirectory.dir("generated")
440440

441-
val upsideDownTranslate by tasks.registering(UpsideDownTranslate::class) {
441+
val upsideDownTranslate = tasks.register<UpsideDownTranslate>("upsideDownTranslate") {
442442
inputFile.set(layout.projectDirectory.file("src/main/resources/assets/lang/I18N.properties"))
443443
outputFile.set(generatedDir.map { it.file("generated/i18n/I18N_en_Qabs.properties") })
444444
}
445445

446-
val createLanguageList by tasks.registering(CreateLanguageList::class) {
446+
val createLanguageList = tasks.register<CreateLanguageList>("createLanguageList") {
447447
resourceBundleDir.set(layout.projectDirectory.dir("src/main/resources/assets/lang"))
448448
resourceBundleBaseName.set("I18N")
449449
additionalLanguages.set(listOf("en-Qabs"))
450450
outputFile.set(generatedDir.map { it.file("languages.json") })
451451
}
452452

453-
val createLocaleNamesResourceBundle by tasks.registering(CreateLocaleNamesResourceBundle::class) {
453+
val createLocaleNamesResourceBundle = tasks.register<CreateLocaleNamesResourceBundle>("createLocaleNamesResourceBundle") {
454454
dependsOn(createLanguageList)
455455

456456
languagesFile.set(createLanguageList.flatMap { it.outputFile })

HMCL/src/main/java/com/jfoenix/controls/JFXPasswordField.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import java.util.ArrayList;
3939
import java.util.Collections;
4040
import java.util.List;
41-
import java.util.Locale;
4241

4342
import static org.jackhuang.hmcl.ui.FXUtils.useJFXContextMenu;
4443

@@ -68,14 +67,18 @@ protected Skin<?> createDefaultSkin() {
6867

6968
private void initialize() {
7069
this.getStyleClass().add(DEFAULT_STYLE_CLASS);
71-
if ("dalvik".equals(System.getProperty("java.vm.name").toLowerCase(Locale.ROOT))) {
72-
this.setStyle("-fx-skin: \"com.jfoenix.android.skins.JFXPasswordFieldSkinAndroid\";");
73-
}
74-
75-
7670
useJFXContextMenu(this);
7771
}
7872

73+
/// Prevents the legacy JFoenix skin from continuously requesting another layout pass.
74+
// https://github.com/HMCL-dev/HMCL/issues/5822
75+
// TODO: This method may no longer be needed after we update JFXTextFieldSkin
76+
@Override
77+
protected void layoutChildren() {
78+
super.layoutChildren();
79+
this.setNeedsLayout(false);
80+
}
81+
7982
/**
8083
* Initialize the style class to 'jfx-password-field'.
8184
* <p>
@@ -294,4 +297,3 @@ public StyleableBooleanProperty getStyleableProperty(JFXPasswordField control) {
294297
}
295298

296299
}
297-

HMCL/src/main/java/org/jackhuang/hmcl/EntryPoint.java

Lines changed: 13 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ public static void main(String[] args) {
5252

5353
checkWine();
5454

55-
setupJavaFXVMOptions();
56-
5755
if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) {
5856
System.getProperties().putIfAbsent("apple.awt.application.appearance", "system");
5957
if (!isInsideMacAppBundle())
@@ -74,71 +72,6 @@ public static void exit(int exitCode) {
7472
System.exit(exitCode);
7573
}
7674

77-
private static void setupJavaFXVMOptions() {
78-
if ("true".equalsIgnoreCase(System.getenv("HMCL_FORCE_GPU"))) {
79-
LOG.info("HMCL_FORCE_GPU: true");
80-
System.getProperties().putIfAbsent("prism.forceGPU", "true");
81-
}
82-
83-
String animationFrameRate = System.getenv("HMCL_ANIMATION_FRAME_RATE");
84-
if (animationFrameRate != null) {
85-
LOG.info("HMCL_ANIMATION_FRAME_RATE: " + animationFrameRate);
86-
87-
try {
88-
if (Integer.parseInt(animationFrameRate) <= 0)
89-
throw new NumberFormatException(animationFrameRate);
90-
91-
System.getProperties().putIfAbsent("javafx.animation.pulse", animationFrameRate);
92-
} catch (NumberFormatException e) {
93-
LOG.warning("Invalid animation frame rate: " + animationFrameRate);
94-
}
95-
}
96-
97-
String uiScale = System.getProperty("hmcl.uiScale", System.getenv("HMCL_UI_SCALE"));
98-
if (uiScale != null) {
99-
uiScale = uiScale.trim();
100-
101-
LOG.info("HMCL_UI_SCALE: " + uiScale);
102-
103-
try {
104-
float scaleValue;
105-
if (uiScale.endsWith("%")) {
106-
scaleValue = Integer.parseInt(uiScale.substring(0, uiScale.length() - 1)) / 100.0f;
107-
} else if (uiScale.endsWith("dpi") || uiScale.endsWith("DPI")) {
108-
scaleValue = Integer.parseInt(uiScale.substring(0, uiScale.length() - 3)) / 96.0f;
109-
} else {
110-
scaleValue = Float.parseFloat(uiScale);
111-
}
112-
113-
float lowerBound;
114-
float upperBound;
115-
116-
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
117-
// JavaFX behavior may be abnormal when the DPI scaling factor is too high
118-
lowerBound = 0.25f;
119-
upperBound = 4f;
120-
} else {
121-
lowerBound = 0.01f;
122-
upperBound = 10f;
123-
}
124-
125-
if (scaleValue >= lowerBound && scaleValue <= upperBound) {
126-
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
127-
System.getProperties().putIfAbsent("glass.win.uiScale", uiScale);
128-
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) {
129-
LOG.warning("macOS does not support setting UI scale, so it will be ignored");
130-
} else {
131-
System.getProperties().putIfAbsent("glass.gtk.uiScale", uiScale);
132-
}
133-
} else {
134-
LOG.warning("UI scale out of range: " + uiScale);
135-
}
136-
} catch (Throwable e) {
137-
LOG.warning("Invalid UI scale: " + uiScale);
138-
}
139-
}
140-
}
141-
14275
private static void createHMCLDirectories() {
14376
if (!Files.isDirectory(Metadata.HMCL_LOCAL_HOME)) {
14477
try {
@@ -225,15 +158,8 @@ private static void verifyJavaFX() {
225158

226159
private static void checkWine() {
227160
if (OperatingSystem.isRunningUnderWine()) {
228-
SwingUtils.initLookAndFeel();
229161
LOG.warning("HMCL is running under Wine or its distributions!");
230-
231-
int result = JOptionPane.showOptionDialog(null, i18n("fatal.wine_warning"), i18n("message.warning"), JOptionPane.OK_CANCEL_OPTION,
232-
JOptionPane.WARNING_MESSAGE, null, null, null);
233-
234-
if (result == JOptionPane.CANCEL_OPTION || result == JOptionPane.CLOSED_OPTION) {
235-
exit(1);
236-
}
162+
showWarning(i18n("fatal.wine_warning"));
237163
}
238164
}
239165

@@ -273,10 +199,21 @@ private static void enableUnsafeMemoryAccess() {
273199
}
274200
}
275201

202+
static void showWarning(String message) {
203+
SwingUtils.initLookAndFeel();
204+
205+
int result = JOptionPane.showOptionDialog(null, message, i18n("message.warning"), JOptionPane.OK_CANCEL_OPTION,
206+
JOptionPane.WARNING_MESSAGE, null, null, null);
207+
208+
if (result == JOptionPane.CANCEL_OPTION || result == JOptionPane.CLOSED_OPTION) {
209+
exit(1);
210+
}
211+
}
212+
276213
/**
277214
* Indicates that a fatal error has occurred, and that the application cannot start.
278215
*/
279-
private static void showErrorAndExit(String message) {
216+
static void showErrorAndExit(String message) {
280217
SwingUtils.showErrorDialog(message);
281218
exit(1);
282219
}

0 commit comments

Comments
 (0)