Skip to content

Commit 886319a

Browse files
authored
避免在代码中直接引用 Controllers.getStage() (HMCL-dev#6431)
1 parent 3320f6c commit 886319a

33 files changed

Lines changed: 238 additions & 186 deletions

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ private static void checkConfigInTempDir() {
266266
@Override
267267
public void stop() throws Exception {
268268
Controllers.onApplicationStop();
269-
FileSaver.shutdown();
269+
SettingsManager.shutdown();
270270
LOG.shutdown();
271271
}
272272

HMCL/src/main/java/org/jackhuang/hmcl/setting/JsonSettingFile.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,14 @@ private LoadResult<T> result(T value, SettingFileAccess access) {
156156
///
157157
/// @param value the settings object to observe
158158
void installAutoSave(T value) {
159-
value.addListener(source -> save(value));
159+
value.addListener(observable -> {
160+
if (value.shouldSaveImmediately(observable)) {
161+
save(value);
162+
value.setSavePending(false);
163+
} else {
164+
value.setSavePending(true);
165+
}
166+
});
160167
}
161168

162169
/// Saves a settings object.

HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherState.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.google.gson.annotations.JsonAdapter;
2121
import com.google.gson.annotations.SerializedName;
22+
import javafx.beans.Observable;
2223
import javafx.beans.property.DoubleProperty;
2324
import javafx.beans.property.ObjectProperty;
2425
import javafx.beans.property.SimpleDoubleProperty;
@@ -54,6 +55,15 @@ public LauncherState() {
5455
register();
5556
}
5657

58+
/// Returns whether the changed field should trigger automatic persistence immediately.
59+
@Override
60+
public boolean shouldSaveImmediately(Observable observable) {
61+
return observable != x
62+
&& observable != y
63+
&& observable != width
64+
&& observable != height;
65+
}
66+
5767
/// The schema used by this launcher state file.
5868
@SerializedName(JsonSchema.PROPERTY_SCHEMA)
5969
private final ObjectProperty<JsonSchema> schema = new SimpleObjectProperty<>(CURRENT_SCHEMA);

HMCL/src/main/java/org/jackhuang/hmcl/setting/SettingsManager.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ public final class SettingsManager {
5555
private SettingsManager() {
5656
}
5757

58+
/// Saves deferred launcher state changes and shutdown file saver.
59+
public static void shutdown() {
60+
savePendingChanges();
61+
FileSaver.shutdown();
62+
}
63+
64+
/// Saves deferred launcher state changes.
65+
public static void savePendingChanges() {
66+
if (launcherState != null && launcherState.isSavable() && launcherState.isSavePending()) {
67+
launcherState.setSavePending(false);
68+
STATE_FILE.save(launcherState);
69+
}
70+
}
71+
5872
/// The local directory storing per-workspace configuration files.
5973
private static final Path LOCAL_CONFIG_FILES_DIRECTORY = Metadata.HMCL_LOCAL_HOME.resolve("config");
6074

HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java

Lines changed: 88 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@
3636
import javafx.scene.control.Label;
3737
import javafx.scene.layout.Region;
3838
import javafx.scene.paint.Color;
39-
import javafx.stage.Screen;
40-
import javafx.stage.Stage;
41-
import javafx.stage.StageStyle;
39+
import javafx.stage.*;
4240
import javafx.util.Duration;
4341
import org.jackhuang.hmcl.Launcher;
4442
import org.jackhuang.hmcl.Metadata;
@@ -73,6 +71,7 @@
7371
import org.jetbrains.annotations.Nullable;
7472

7573
import java.io.IOException;
74+
import java.nio.file.Path;
7675
import java.time.LocalDate;
7776
import java.util.List;
7877
import java.util.concurrent.CompletableFuture;
@@ -97,12 +96,12 @@ public final class Controllers {
9796
public static final int MIN_CONTENT_HEIGHT = 450 + 2 + 40; // bg height + border width*2 + toolbar height
9897
public static final int MIN_WIDTH = MIN_CONTENT_WIDTH + CUSTOM_DECORATION_SHADOW_EXTENT;
9998
public static final int MIN_HEIGHT = MIN_CONTENT_HEIGHT + CUSTOM_DECORATION_SHADOW_EXTENT;
100-
public static final Screen SCREEN = Screen.getPrimary();
99+
public static final Rectangle2D PRIMARY_SCREEN_BOUNDS = Screen.getPrimary().getBounds();
101100
private static InvalidationListener stageSizeChangeListener;
102-
private static DoubleProperty stageX = new SimpleDoubleProperty();
103-
private static DoubleProperty stageY = new SimpleDoubleProperty();
104-
private static DoubleProperty stageWidth = new SimpleDoubleProperty();
105-
private static DoubleProperty stageHeight = new SimpleDoubleProperty();
101+
private static final DoubleProperty contentX = new SimpleDoubleProperty();
102+
private static final DoubleProperty contentY = new SimpleDoubleProperty();
103+
private static final DoubleProperty contentWidth = new SimpleDoubleProperty();
104+
private static final DoubleProperty contentHeight = new SimpleDoubleProperty();
106105

107106
private static Scene scene;
108107
private static Stage stage;
@@ -133,12 +132,16 @@ public interface ThrowingRunnable {
133132
void run() throws Exception;
134133
}
135134

136-
public static Scene getScene() {
137-
return scene;
135+
public static @Nullable Stage getStage() {
136+
return stage;
138137
}
139138

140-
public static Stage getStage() {
141-
return stage;
139+
public static ReadOnlyDoubleProperty windowWidthProperty() {
140+
return contentWidth;
141+
}
142+
143+
public static ReadOnlyDoubleProperty windowHeightProperty() {
144+
return contentHeight;
142145
}
143146

144147
@FXThread
@@ -214,64 +217,8 @@ public static DecoratorController getDecorator() {
214217
return decorator;
215218
}
216219

217-
public static void saveWindowStates() {
218-
saveWindowBounds();
219-
}
220-
221-
private static void saveWindowBounds() {
222-
if (stageX != null) {
223-
state().setX(toContentX(stageX.get()) / SCREEN.getBounds().getWidth());
224-
}
225-
if (stageY != null) {
226-
state().setY(toContentY(stageY.get()) / SCREEN.getBounds().getHeight());
227-
}
228-
if (stageHeight != null) {
229-
state().setHeight(toContentHeight(stageHeight.get()));
230-
}
231-
if (stageWidth != null) {
232-
state().setWidth(toContentWidth(stageWidth.get()));
233-
}
234-
}
235-
236220
public static void onApplicationStop() {
237221
stageSizeChangeListener = null;
238-
saveWindowBounds();
239-
stageX = null;
240-
stageY = null;
241-
stageHeight = null;
242-
stageWidth = null;
243-
}
244-
245-
private static double toContentX(double stageX) {
246-
return stageX + CUSTOM_DECORATION_SHADOW_SIZE;
247-
}
248-
249-
private static double toContentY(double stageY) {
250-
return stageY + CUSTOM_DECORATION_SHADOW_SIZE;
251-
}
252-
253-
private static double toStageX(double contentX) {
254-
return contentX - CUSTOM_DECORATION_SHADOW_SIZE;
255-
}
256-
257-
private static double toStageY(double contentY) {
258-
return contentY - CUSTOM_DECORATION_SHADOW_SIZE;
259-
}
260-
261-
private static double toContentWidth(double stageWidth) {
262-
return Math.max(0.0, stageWidth - CUSTOM_DECORATION_SHADOW_EXTENT);
263-
}
264-
265-
private static double toContentHeight(double stageHeight) {
266-
return Math.max(0.0, stageHeight - CUSTOM_DECORATION_SHADOW_EXTENT);
267-
}
268-
269-
private static double toStageWidth(double contentWidth) {
270-
return contentWidth + CUSTOM_DECORATION_SHADOW_EXTENT;
271-
}
272-
273-
private static double toStageHeight(double contentHeight) {
274-
return contentHeight + CUSTOM_DECORATION_SHADOW_EXTENT;
275222
}
276223

277224
public static void initialize(Stage stage) {
@@ -285,60 +232,22 @@ public static void initialize(Stage stage) {
285232
LOG.info("Enable sub-pixel antialiasing");
286233
System.getProperties().put("prism.lcdtext", "true");
287234
} else if ("gray".equalsIgnoreCase(fontAntiAliasing)
288-
|| OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS && SCREEN.getOutputScaleX() > 1) {
235+
|| OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS && Screen.getPrimary().getOutputScaleX() > 1) {
289236
LOG.info("Disable sub-pixel antialiasing");
290237
System.getProperties().put("prism.lcdtext", "false");
291238
}
292239
}
293240

294241
Controllers.stage = stage;
295242

296-
stageSizeChangeListener = o -> {
297-
ReadOnlyDoubleProperty sourceProperty = (ReadOnlyDoubleProperty) o;
298-
DoubleProperty targetProperty;
299-
switch (sourceProperty.getName()) {
300-
case "x": {
301-
targetProperty = stageX;
302-
break;
303-
}
304-
case "y": {
305-
targetProperty = stageY;
306-
break;
307-
}
308-
case "width": {
309-
targetProperty = stageWidth;
310-
break;
311-
}
312-
case "height": {
313-
targetProperty = stageHeight;
314-
break;
315-
}
316-
default: {
317-
targetProperty = null;
318-
}
319-
}
320-
321-
if (targetProperty != null
322-
&& Controllers.stage != null
323-
&& !Controllers.stage.isIconified()
324-
// https://github.com/HMCL-dev/HMCL/issues/4290
325-
&& (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS ||
326-
!Controllers.stage.isFullScreen() && !Controllers.stage.isMaximized())
327-
) {
328-
targetProperty.set(sourceProperty.get());
329-
}
330-
};
331-
332-
WeakInvalidationListener weakListener = new WeakInvalidationListener(stageSizeChangeListener);
333-
334243
double initContentWidth = Math.max(MIN_CONTENT_WIDTH, state().getWidth());
335244
double initContentHeight = Math.max(MIN_CONTENT_HEIGHT, state().getHeight());
336-
double initWidth = toStageWidth(initContentWidth);
337-
double initHeight = toStageHeight(initContentHeight);
245+
double initWidth = initContentWidth + CUSTOM_DECORATION_SHADOW_EXTENT;
246+
double initHeight = initContentHeight + CUSTOM_DECORATION_SHADOW_EXTENT;
338247

339248
{
340-
double initContentX = state().getX() * SCREEN.getBounds().getWidth();
341-
double initContentY = state().getY() * SCREEN.getBounds().getHeight();
249+
double initContentX = state().getX() * PRIMARY_SCREEN_BOUNDS.getWidth();
250+
double initContentY = state().getY() * PRIMARY_SCREEN_BOUNDS.getHeight();
342251

343252
boolean invalid = true;
344253
double border = 20D;
@@ -355,24 +264,65 @@ public static void initialize(Stage stage) {
355264
}
356265

357266
if (invalid) {
358-
initContentX = (0.5D - initContentWidth / SCREEN.getBounds().getWidth() / 2)
359-
* SCREEN.getBounds().getWidth();
360-
initContentY = (0.5D - initContentHeight / SCREEN.getBounds().getHeight() / 2)
361-
* SCREEN.getBounds().getHeight();
267+
initContentX = (0.5D - initContentWidth / PRIMARY_SCREEN_BOUNDS.getWidth() / 2)
268+
* PRIMARY_SCREEN_BOUNDS.getWidth();
269+
initContentY = (0.5D - initContentHeight / PRIMARY_SCREEN_BOUNDS.getHeight() / 2)
270+
* PRIMARY_SCREEN_BOUNDS.getHeight();
362271
}
363272

364-
double initX = toStageX(initContentX);
365-
double initY = toStageY(initContentY);
273+
double initX = initContentX - CUSTOM_DECORATION_SHADOW_SIZE;
274+
double initY = initContentY - CUSTOM_DECORATION_SHADOW_SIZE;
366275
stage.setX(initX);
367276
stage.setY(initY);
368-
stageX.set(initX);
369-
stageY.set(initY);
277+
contentX.set(initContentX);
278+
contentY.set(initContentY);
370279
}
371280

372281
stage.setHeight(initHeight);
373282
stage.setWidth(initWidth);
374-
stageHeight.set(initHeight);
375-
stageWidth.set(initWidth);
283+
contentHeight.set(initContentHeight);
284+
contentWidth.set(initContentWidth);
285+
286+
stageSizeChangeListener = o -> {
287+
ReadOnlyDoubleProperty property = (ReadOnlyDoubleProperty) o;
288+
Stage currentStage = property.getBean() instanceof Stage s ? s : null;
289+
if (currentStage == null)
290+
return;
291+
292+
boolean saveState = !currentStage.isIconified()
293+
// https://github.com/HMCL-dev/HMCL/issues/4290
294+
&& (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS
295+
|| !currentStage.isFullScreen() && !currentStage.isMaximized());
296+
297+
switch (property.getName()) {
298+
case "x" -> {
299+
double value = property.get() + CUSTOM_DECORATION_SHADOW_SIZE;
300+
contentX.set(value);
301+
if (saveState)
302+
state().setX(value / PRIMARY_SCREEN_BOUNDS.getWidth());
303+
}
304+
case "y" -> {
305+
double value = property.get() + CUSTOM_DECORATION_SHADOW_SIZE;
306+
contentY.set(value);
307+
if (saveState)
308+
state().setY(value / PRIMARY_SCREEN_BOUNDS.getHeight());
309+
}
310+
case "width" -> {
311+
double value = Math.max(MIN_CONTENT_WIDTH, property.get() - CUSTOM_DECORATION_SHADOW_EXTENT);
312+
contentWidth.set(value);
313+
if (saveState)
314+
state().setWidth(value);
315+
}
316+
case "height" -> {
317+
double value = Math.max(MIN_CONTENT_HEIGHT, property.get() - CUSTOM_DECORATION_SHADOW_EXTENT);
318+
contentHeight.set(value);
319+
if (saveState)
320+
state().setHeight(value);
321+
}
322+
}
323+
};
324+
325+
WeakInvalidationListener weakListener = new WeakInvalidationListener(stageSizeChangeListener);
376326
stage.xProperty().addListener(weakListener);
377327
stage.yProperty().addListener(weakListener);
378328
stage.heightProperty().addListener(weakListener);
@@ -585,7 +535,7 @@ public static void confirm(String text, String title, MessageType type, Runnable
585535

586536
/// Shows a warning that confirms backing up a read-only settings file before overwriting it.
587537
///
588-
/// @param text the file-specific read-only warning
538+
/// @param text the file-specific read-only warning
589539
/// @param overwrite the action that backs up and overwrites the file
590540
public static void confirmBackupAndOverwrite(String text, ThrowingRunnable overwrite) {
591541
dialog(new MessageDialogPane.Builder(
@@ -694,6 +644,22 @@ public static void showToast(String content) {
694644
decorator.showToast(content);
695645
}
696646

647+
public static @Nullable Path showDialog(DirectoryChooser directoryChooser) {
648+
return FileUtils.toPath(directoryChooser.showDialog(stage));
649+
}
650+
651+
public static @Nullable Path showOpenDialog(FileChooser fileChooser) {
652+
return FileUtils.toPath(fileChooser.showOpenDialog(stage));
653+
}
654+
655+
public static @Nullable Path showSaveDialog(FileChooser fileChooser) {
656+
return FileUtils.toPath(fileChooser.showSaveDialog(stage));
657+
}
658+
659+
public static @Nullable List<Path> showOpenMultipleDialog(FileChooser fileChooser) {
660+
return FileUtils.toPaths(fileChooser.showOpenMultipleDialog(stage));
661+
}
662+
697663
public static void onHyperlinkAction(String href) {
698664
if (href.startsWith("hmcl://")) {
699665
switch (href) {

HMCL/src/main/java/org/jackhuang/hmcl/ui/UpgradeDialog.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@
4444
public final class UpgradeDialog extends JFXDialogLayout {
4545

4646
public UpgradeDialog(RemoteVersion remoteVersion, Runnable updateRunnable) {
47-
maxWidthProperty().bind(Controllers.getScene().widthProperty().multiply(0.7));
48-
maxHeightProperty().bind(Controllers.getScene().heightProperty().multiply(0.7));
47+
maxWidthProperty().bind(Controllers.windowWidthProperty().multiply(0.7));
48+
maxHeightProperty().bind(Controllers.windowHeightProperty().multiply(0.7));
4949

5050
setHeading(new Label(i18n("update.changelog")));
5151
setBody(new JFXSpinner());

HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import org.jackhuang.hmcl.ui.DialogController;
4444
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
4545
import org.jackhuang.hmcl.util.StringUtils;
46-
import org.jackhuang.hmcl.util.io.FileUtils;
4746
import org.jackhuang.hmcl.util.skin.InvalidSkinException;
4847
import org.jackhuang.hmcl.util.skin.NormalizedSkin;
4948
import org.jetbrains.annotations.Nullable;
@@ -151,7 +150,7 @@ public Task<?> uploadSkin() {
151150
FileChooser chooser = new FileChooser();
152151
chooser.setTitle(i18n("account.skin.upload"));
153152
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("account.skin.file"), "*.png"));
154-
Path selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage()));
153+
Path selectedFile = Controllers.showOpenDialog(chooser);
155154
if (selectedFile == null) {
156155
return null;
157156
}

0 commit comments

Comments
 (0)