diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index a82e89ba7..abb1b4a92 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,7 +1,14 @@ name: Bug Report description: Create a report to help us improve -labels: ["type:bug"] +type: "Bug" body: + - type: input + id: summary + attributes: + label: Summary + placeholder: Please summarize your problem. + validations: + required: true - type: checkboxes id: terms attributes: @@ -11,13 +18,6 @@ body: required: true - label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md) required: true - - type: input - id: summary - attributes: - label: Summary - placeholder: Please summarize your problem. - validations: - required: true - type: textarea id: software-versions attributes: @@ -97,4 +97,4 @@ body: id: further-info attributes: label: Anything else? - description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue? + description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue? \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 652f27234..826f3410a 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -1,7 +1,14 @@ name: Feature Request description: Suggest an idea for this project -labels: ["type:feature-request"] +type: "Feature" body: + - type: input + id: summary + attributes: + label: Summary + placeholder: Please summarize your feature request. + validations: + required: true - type: checkboxes id: terms attributes: @@ -11,13 +18,6 @@ body: required: true - label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md) required: true - - type: input - id: summary - attributes: - label: Summary - placeholder: Please summarize your feature request. - validations: - required: true - type: textarea id: motivation attributes: diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index 16c5bc530..fa021d441 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -28,7 +28,7 @@ env: jobs: build: name: Build Debian Package - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - id: versions diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java b/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java index 2ffda4d73..a25384c78 100644 --- a/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java +++ b/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java @@ -76,8 +76,10 @@ public class ReadmeGenerator { input.chars().forEachOrdered(c -> { if (c < 128) { sb.append((char) c); + } else if (c <= 0xFF) { + sb.append("\\'").append(String.format("%02X", c)); } else if (c < 0xFFFF) { - sb.append("\\u").append(c); + sb.append("\\uc1\\u").append(c); } }); } diff --git a/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java b/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java index 5107fe740..837bea012 100644 --- a/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java +++ b/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java @@ -38,7 +38,7 @@ public class Dialogs { .setMessageKey("removeVault.message") // .setDescriptionKey("removeVault.description") // .setIcon(FontAwesome5Icon.QUESTION) // - .setOkButtonKey("removeVault.confirmBtn") // + .setOkButtonKey("generic.button.remove") // .setCancelButtonKey("generic.button.cancel") // .setOkAction(stage -> { LOG.debug("Removing vault {}.", vault.getDisplayName()); @@ -54,7 +54,7 @@ public class Dialogs { .setMessageKey("removeCert.message") // .setDescriptionKey("removeCert.description") // .setIcon(FontAwesome5Icon.QUESTION) // - .setOkButtonKey("removeCert.confirmBtn") // + .setOkButtonKey("generic.button.remove") // .setCancelButtonKey("generic.button.cancel") // .setOkAction(stage -> { settings.licenseKey.set(null); diff --git a/src/main/java/org/cryptomator/ui/dialogs/SimpleDialog.java b/src/main/java/org/cryptomator/ui/dialogs/SimpleDialog.java index 84d9e4f75..08f77849e 100644 --- a/src/main/java/org/cryptomator/ui/dialogs/SimpleDialog.java +++ b/src/main/java/org/cryptomator/ui/dialogs/SimpleDialog.java @@ -31,8 +31,9 @@ public class SimpleDialog { FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController( // new SimpleDialogController(resolveText(builder.messageKey, null), // resolveText(builder.descriptionKey, null), // - builder.icon, resolveText(builder.okButtonKey, null), // - resolveText(builder.cancelButtonKey, null), // + builder.icon, // + resolveText(builder.okButtonKey, null), // + builder.cancelButtonKey != null ? resolveText(builder.cancelButtonKey, null) : null, // () -> builder.okAction.accept(dialogStage), // () -> builder.cancelAction.accept(dialogStage)), // Scene::new, builder.resourceBundle); @@ -67,7 +68,6 @@ public class SimpleDialog { private String descriptionKey; private String okButtonKey; private String cancelButtonKey; - private FontAwesome5Icon icon; private Consumer okAction = Stage::close; private Consumer cancelAction = Stage::close; @@ -128,7 +128,6 @@ public class SimpleDialog { Objects.requireNonNull(messageKey, "SimpleDialog messageKey must be set."); Objects.requireNonNull(descriptionKey, "SimpleDialog descriptionKey must be set."); Objects.requireNonNull(okButtonKey, "SimpleDialog okButtonKey must be set."); - Objects.requireNonNull(cancelButtonKey, "SimpleDialog cancelButtonKey must be set."); try { return new SimpleDialog(this); diff --git a/src/main/java/org/cryptomator/ui/dialogs/SimpleDialogController.java b/src/main/java/org/cryptomator/ui/dialogs/SimpleDialogController.java index 0eee1b308..bbf590145 100644 --- a/src/main/java/org/cryptomator/ui/dialogs/SimpleDialogController.java +++ b/src/main/java/org/cryptomator/ui/dialogs/SimpleDialogController.java @@ -14,6 +14,7 @@ public class SimpleDialogController implements FxController { private final String cancelButtonText; private final Runnable okAction; private final Runnable cancelAction; + private final boolean cancelButtonVisible; public SimpleDialogController(String message, String description, FontAwesome5Icon icon, String okButtonText, String cancelButtonText, Runnable okAction, Runnable cancelAction) { this.message = message; @@ -23,6 +24,11 @@ public class SimpleDialogController implements FxController { this.cancelButtonText = cancelButtonText; this.okAction = okAction; this.cancelAction = cancelAction; + this.cancelButtonVisible = cancelButtonText != null && !cancelButtonText.isEmpty(); + } + + public boolean isCancelButtonVisible() { + return cancelButtonVisible; } public String getMessage() { diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java index 71e7592f6..f94d882fa 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java @@ -165,7 +165,7 @@ public class ReceiveKeyController implements FxController { var vaultKeyUri = hubConfig.URIs.API.resolve("vaults/" + vaultId + "/access-token"); var request = HttpRequest.newBuilder(vaultKeyUri) // .header("Authorization", "Bearer " + bearerToken) // - .header("deviceId", deviceId) // + .header("Hub-Device-ID", deviceId) // .GET() // .timeout(REQ_TIMEOUT) // .build(); diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java index 13412dd27..18db5e5ac 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java @@ -19,9 +19,11 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.fxml.FXML; +import javafx.geometry.Rectangle2D; import javafx.scene.layout.StackPane; import javafx.stage.Screen; import javafx.stage.Stage; +import javafx.stage.WindowEvent; @MainWindowScoped public class MainWindowController implements FxController { @@ -68,18 +70,15 @@ public class MainWindowController implements FxController { int y = settings.windowYPosition.get(); int width = settings.windowWidth.get(); int height = settings.windowHeight.get(); - if (windowPositionSaved(x, y, width, height) ) { - if(isWithinDisplayBounds(x, y, width, height)) { //use stored window position - window.setX(x); - window.setY(y); - window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth())); - window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight())); - } else if(isWithinDisplayBounds((int) window.getX(), (int) window.getY(), width, height)) { //just reset position of upper left corner, keep window size - window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth())); - window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight())); - } //else reset window completely + if (windowPositionSaved(x, y, width, height)) { + window.setX(x); + window.setY(y); + window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth())); + window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight())); } + window.setOnShowing(this::checkDisplayBounds); + settings.windowXPosition.bind(window.xProperty()); settings.windowYPosition.bind(window.yProperty()); settings.windowWidth.bind(window.widthProperty()); @@ -90,6 +89,39 @@ public class MainWindowController implements FxController { return x != 0 || y != 0 || width != 0 || height != 0; } + private void checkDisplayBounds(WindowEvent windowEvent) { + int x = settings.windowXPosition.get(); + int y = settings.windowYPosition.get(); + int width = settings.windowWidth.get(); + int height = settings.windowHeight.get(); + + // 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(x); + window.setY(y); + window.setWidth(width); + window.setHeight(height); + } + + Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds(); + if (!isWithinDisplayBounds(x, y, width, height)) { //use stored window position + LOG.debug("Resetting window position due to insufficient screen overlap"); + var centeredX = (primaryScreenBounds.getWidth() - window.getMinWidth()) / 2; + var centeredY = (primaryScreenBounds.getHeight() - window.getMinHeight()) / 2; + //check if we can keep width and height + if (isWithinDisplayBounds((int) centeredX, (int) centeredY, width, height)) { + //if so, keep window size + window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth())); + window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight())); + } + //reset position of upper left corner + window.setX(centeredX); + window.setY(centeredY); + } + } + private boolean isWithinDisplayBounds(int x, int y, int width, int height) { // define a rect which is inset on all sides from the window's rect: final int shrinkedX = x + 20; // 20px left diff --git a/src/main/resources/css/dark_theme.css b/src/main/resources/css/dark_theme.css index abb6efe57..4d3db3968 100644 --- a/src/main/resources/css/dark_theme.css +++ b/src/main/resources/css/dark_theme.css @@ -359,6 +359,12 @@ -fx-background-color: PRIMARY; } +.notification-debug:hover .notification-label, +.notification-update:hover .notification-label, +.notification-support:hover .notification-label { + -fx-underline:true; +} + /******************************************************************************* * * * ScrollBar * diff --git a/src/main/resources/css/light_theme.css b/src/main/resources/css/light_theme.css index 516dd0b26..11cb1a9df 100644 --- a/src/main/resources/css/light_theme.css +++ b/src/main/resources/css/light_theme.css @@ -358,6 +358,12 @@ -fx-background-color: PRIMARY; } +.notification-debug:hover .notification-label, +.notification-update:hover .notification-label, +.notification-support:hover .notification-label { + -fx-underline:true; +} + /******************************************************************************* * * * ScrollBar * diff --git a/src/main/resources/fxml/simple_dialog.fxml b/src/main/resources/fxml/simple_dialog.fxml index 32ad63abf..0e9b01776 100644 --- a/src/main/resources/fxml/simple_dialog.fxml +++ b/src/main/resources/fxml/simple_dialog.fxml @@ -1,16 +1,16 @@ - - - - - - - - - + + + + + + + + + -