From aedbefc38a266664340c75b24d6b2ef1ef69bde1 Mon Sep 17 00:00:00 2001 From: Rexbas Date: Tue, 18 Jul 2023 20:03:19 +0200 Subject: [PATCH 1/4] Reset window position when out of display bounds --- .../ui/mainwindow/ResizeController.java | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java b/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java index 1b3f1b69f..eec2ac183 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java @@ -57,8 +57,8 @@ public class ResizeController implements FxController { settings.displayConfiguration.set(getMonitorSizes()); return; } else { - if (didDisplayConfigurationChange()) { - //If the position is illegal, then the window appears on the main screen in the middle of the window. + if (didDisplayConfigurationChange() || !isWithinDisplayBounds()) { + // If the position is illegal, then the window appears on the main screen in the middle of the window. Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds(); window.setX((primaryScreenBounds.getWidth() - window.getMinWidth()) / 2); window.setY((primaryScreenBounds.getHeight() - window.getMinHeight()) / 2); @@ -86,6 +86,35 @@ public class ResizeController implements FxController { return configurationHasChanged; } + private boolean isWithinDisplayBounds() { + // (x1, y1) is the top left corner of the window, (x2, y2) is the bottom right corner + final double slack = 10; + final double width = window.getWidth() - 2 * slack; + final double height = window.getHeight() - 2 * slack; + final double x1 = window.getX() + slack; + final double y1 = window.getY() + slack; + final double x2 = x1 + width; + final double y2 = y1 + height; + + final ObservableList screens = Screen.getScreensForRectangle(x1, y1, width, height); + + // Find the total visible area of the window + double visibleArea = 0; + for (Screen screen : screens) { + Rectangle2D bounds = screen.getVisualBounds(); + + double xOverlap = Math.min(x2, bounds.getMaxX()) - Math.max(x1, bounds.getMinX()); + double yOverlap = Math.min(y2, bounds.getMaxY()) - Math.max(y1, bounds.getMinY()); + + visibleArea += xOverlap * yOverlap; + } + + final double windowArea = width * height; + + // Within bounds if the visible area matches the window area + return visibleArea == windowArea; + } + private String getMonitorSizes() { ObservableList screens = Screen.getScreens(); StringBuilder sb = new StringBuilder(); From 094a7c6a207109c2a66c90020e1e1a0956284518 Mon Sep 17 00:00:00 2001 From: Rexbas Date: Tue, 18 Jul 2023 20:08:13 +0200 Subject: [PATCH 2/4] Check display bounds onShowing event --- .../ui/mainwindow/ResizeController.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java b/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java index eec2ac183..8afcdc153 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java @@ -15,6 +15,7 @@ import javafx.scene.input.MouseEvent; import javafx.scene.layout.Region; import javafx.stage.Screen; import javafx.stage.Stage; +import javafx.stage.WindowEvent; @MainWindow public class ResizeController implements FxController { @@ -71,6 +72,9 @@ public class ResizeController implements FxController { window.setY(settings.windowYPosition.get()); } } + + window.setOnShowing(this::checkDisplayBounds); + savePositionalSettings(); } @@ -115,6 +119,18 @@ public class ResizeController implements FxController { return visibleArea == windowArea; } + private void checkDisplayBounds(WindowEvent evt) { + if (!isWithinDisplayBounds()) { + // If the position is illegal, then the window appears on the main screen in the middle of the window. + Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds(); + window.setX((primaryScreenBounds.getWidth() - window.getMinWidth()) / 2); + window.setY((primaryScreenBounds.getHeight() - window.getMinHeight()) / 2); + window.setWidth(window.getMinWidth()); + window.setHeight(window.getMinHeight()); + savePositionalSettings(); + } + } + private String getMonitorSizes() { ObservableList screens = Screen.getScreens(); StringBuilder sb = new StringBuilder(); @@ -212,5 +228,4 @@ public class ResizeController implements FxController { public boolean isShowResizingArrows() { return showResizingArrows.get(); } - } \ No newline at end of file From 745d435a579a8413c450e228598a4193083b7a19 Mon Sep 17 00:00:00 2001 From: Rexbas Date: Tue, 18 Jul 2023 20:15:36 +0200 Subject: [PATCH 3/4] Remove displayConfiguration from settings --- .../cryptomator/common/settings/Settings.java | 4 -- .../common/settings/SettingsJson.java | 3 -- .../ui/mainwindow/ResizeController.java | 44 +++---------------- 3 files changed, 5 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/cryptomator/common/settings/Settings.java b/src/main/java/org/cryptomator/common/settings/Settings.java index fd9f01904..4e0e0df97 100644 --- a/src/main/java/org/cryptomator/common/settings/Settings.java +++ b/src/main/java/org/cryptomator/common/settings/Settings.java @@ -65,7 +65,6 @@ public class Settings { public final IntegerProperty windowYPosition; public final IntegerProperty windowWidth; public final IntegerProperty windowHeight; - public final StringProperty displayConfiguration; public final StringProperty language; public final StringProperty mountService; public final StringProperty lastUpdateCheck; @@ -103,7 +102,6 @@ public class Settings { this.windowYPosition = new SimpleIntegerProperty(this, "windowYPosition", json.windowYPosition); this.windowWidth = new SimpleIntegerProperty(this, "windowWidth", json.windowWidth); this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight); - this.displayConfiguration = new SimpleStringProperty(this, "displayConfiguration", json.displayConfiguration); this.language = new SimpleStringProperty(this, "language", json.language); this.mountService = new SimpleStringProperty(this, "mountService", json.mountService); this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck); @@ -131,7 +129,6 @@ public class Settings { windowYPosition.addListener(this::somethingChanged); windowWidth.addListener(this::somethingChanged); windowHeight.addListener(this::somethingChanged); - displayConfiguration.addListener(this::somethingChanged); language.addListener(this::somethingChanged); mountService.addListener(this::somethingChanged); lastUpdateCheck.addListener(this::somethingChanged); @@ -186,7 +183,6 @@ public class Settings { json.windowYPosition = windowYPosition.get(); json.windowWidth = windowWidth.get(); json.windowHeight = windowHeight.get(); - json.displayConfiguration = displayConfiguration.get(); json.language = language.get(); json.mountService = mountService.get(); json.lastUpdateCheck = lastUpdateCheck.get(); diff --git a/src/main/java/org/cryptomator/common/settings/SettingsJson.java b/src/main/java/org/cryptomator/common/settings/SettingsJson.java index 977e1d3cd..2c7c963da 100644 --- a/src/main/java/org/cryptomator/common/settings/SettingsJson.java +++ b/src/main/java/org/cryptomator/common/settings/SettingsJson.java @@ -31,9 +31,6 @@ class SettingsJson { @JsonProperty("theme") UiTheme theme = Settings.DEFAULT_THEME; - @JsonProperty("displayConfiguration") - String displayConfiguration; - @JsonProperty("keychainProvider") String keychainProvider = Settings.DEFAULT_KEYCHAIN_PROVIDER; diff --git a/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java b/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java index 8afcdc153..9cda85cdb 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java @@ -6,7 +6,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.collections.ObservableList; import javafx.fxml.FXML; @@ -54,42 +53,20 @@ public class ResizeController implements FxController { public void initialize() { LOG.trace("init ResizeController"); - if (neverTouched()) { - settings.displayConfiguration.set(getMonitorSizes()); - return; - } else { - if (didDisplayConfigurationChange() || !isWithinDisplayBounds()) { - // If the position is illegal, then the window appears on the main screen in the middle of the window. - Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds(); - window.setX((primaryScreenBounds.getWidth() - window.getMinWidth()) / 2); - window.setY((primaryScreenBounds.getHeight() - window.getMinHeight()) / 2); - window.setWidth(window.getMinWidth()); - window.setHeight(window.getMinHeight()); - } else { - window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight()); - window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth()); - window.setX(settings.windowXPosition.get()); - window.setY(settings.windowYPosition.get()); - } + if (!neverTouched()) { + window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight()); + window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth()); + window.setX(settings.windowXPosition.get()); + window.setY(settings.windowYPosition.get()); } window.setOnShowing(this::checkDisplayBounds); - - savePositionalSettings(); } private boolean neverTouched() { return (settings.windowHeight.get() == 0) && (settings.windowWidth.get() == 0) && (settings.windowXPosition.get() == 0) && (settings.windowYPosition.get() == 0); } - private boolean didDisplayConfigurationChange() { - String currentDisplayConfiguration = getMonitorSizes(); - String settingsDisplayConfiguration = settings.displayConfiguration.get(); - boolean configurationHasChanged = !settingsDisplayConfiguration.equals(currentDisplayConfiguration); - if (configurationHasChanged) settings.displayConfiguration.set(currentDisplayConfiguration); - return configurationHasChanged; - } - private boolean isWithinDisplayBounds() { // (x1, y1) is the top left corner of the window, (x2, y2) is the bottom right corner final double slack = 10; @@ -131,17 +108,6 @@ public class ResizeController implements FxController { } } - private String getMonitorSizes() { - ObservableList screens = Screen.getScreens(); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < screens.size(); i++) { - Rectangle2D screenBounds = screens.get(i).getBounds(); - if (!sb.isEmpty()) sb.append(" "); - sb.append("displayId: " + i + ", " + screenBounds.getWidth() + "x" + screenBounds.getHeight() + ";"); - } - return sb.toString(); - } - private void startResize(MouseEvent evt) { origX = window.getX(); origY = window.getY(); From aa14d79642249cfd4ca12bc3e455b1012bd2fdc3 Mon Sep 17 00:00:00 2001 From: Rexbas Date: Tue, 22 Aug 2023 20:28:17 +0200 Subject: [PATCH 4/4] Restore a closed minimized window at (-32000, -32000) to the last saved position Fixes #3079, second scenario. --- .../cryptomator/ui/mainwindow/ResizeController.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java b/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java index 9cda85cdb..b136fa55c 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/ResizeController.java @@ -97,6 +97,17 @@ public class ResizeController implements FxController { } private void checkDisplayBounds(WindowEvent evt) { + + // Minimizing a window in Windows and closing it could result in an out of bounds position at (x, y) = (-32000, -32000) + // See https://devblogs.microsoft.com/oldnewthing/20041028-00/?p=37453 + // If the position is (-32000, -32000), restore to the last saved position + if (window.getX() == -32000 && window.getY() == -32000) { + window.setX(settings.windowXPosition.get()); + window.setY(settings.windowYPosition.get()); + window.setWidth(settings.windowWidth.get()); + window.setHeight(settings.windowHeight.get()); + } + if (!isWithinDisplayBounds()) { // If the position is illegal, then the window appears on the main screen in the middle of the window. Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds();