Przeglądaj źródła

Feature/simple spinning (#1728)

* Define discrete rotation animation
* New class FontAwesome5Spinner extending FontAwesome5IconView, animated
* New class AutoAnimator to play Animation on a Node conditionally
* Replace all occurences of Progress Indicator with FontAwesome5Spinner
* Spin spinner icon in processing vault state
* remove undocumented progress indicator styling
Armin Schrenk 3 lat temu
rodzic
commit
c81ef1c109

+ 1 - 0
src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java

@@ -76,6 +76,7 @@ public class CreateNewVaultPasswordController implements FxController {
 	private final BooleanProperty readyToCreateVault;
 	private final ObjectBinding<ContentDisplay> createVaultButtonState;
 
+	/* FXML */
 	public ToggleGroup recoveryKeyChoice;
 	public Toggle showRecoveryKey;
 	public Toggle skipRecoveryKey;

+ 21 - 0
src/main/java/org/cryptomator/ui/common/Animations.java

@@ -1,11 +1,17 @@
 package org.cryptomator.ui.common;
 
+import javafx.animation.Animation;
+import javafx.animation.Interpolator;
 import javafx.animation.KeyFrame;
 import javafx.animation.KeyValue;
+import javafx.animation.RotateTransition;
+import javafx.animation.SequentialTransition;
 import javafx.animation.Timeline;
 import javafx.beans.value.WritableValue;
+import javafx.scene.Node;
 import javafx.stage.Window;
 import javafx.util.Duration;
+import java.util.stream.IntStream;
 
 public class Animations {
 
@@ -33,4 +39,19 @@ public class Animations {
 		);
 	}
 
+	public static SequentialTransition createDiscrete360Rotation(Node toAnimate) {
+		var animation = new SequentialTransition(IntStream.range(0, 8).mapToObj(i -> Animations.createDiscrete45Rotation()).toArray(Animation[]::new));
+		animation.setCycleCount(Animation.INDEFINITE);
+		animation.setNode(toAnimate);
+		return animation;
+	}
+
+	private static RotateTransition createDiscrete45Rotation() {
+		var animation = new RotateTransition(Duration.millis(100));
+		animation.setInterpolator(Interpolator.DISCRETE);
+		animation.setByAngle(45);
+		animation.setCycleCount(1);
+		return animation;
+	}
+
 }

+ 108 - 0
src/main/java/org/cryptomator/ui/common/AutoAnimator.java

@@ -0,0 +1,108 @@
+package org.cryptomator.ui.common;
+
+import com.tobiasdiez.easybind.EasyBind;
+import com.tobiasdiez.easybind.Subscription;
+
+import javafx.animation.Animation;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.value.ObservableValue;
+
+/**
+ * Animation which starts and stops automatically based on an observable condition.
+ * <p>
+ * During creation the consumer can optionally define actions to be executed everytime before the animation starts and after it stops.
+ * The automatic playback of the animation based on the condition can be stopped by calling {@link #deactivateCondition()}. To reactivate it, {@link #activateCondition()} must be called.
+ */
+public class AutoAnimator<T extends Animation> {
+
+
+	private final T animation;
+	private final ObservableValue<Boolean> condition;
+	private final Runnable beforeStart;
+	private final Runnable afterStop;
+
+	private Subscription sub;
+
+	AutoAnimator(T animation, ObservableValue<Boolean> condition, Runnable beforeStart, Runnable afterStop) {
+		this.animation = animation;
+		this.condition = condition;
+		this.beforeStart = beforeStart;
+		this.afterStop = afterStop;
+
+		activateCondition();
+	}
+
+	public void playFromStart() {
+		beforeStart.run();
+		animation.playFromStart();
+	}
+
+	public void stop() {
+		animation.stop();
+		afterStop.run();
+	}
+
+	/**
+	 * Deactivates activation on the condition.
+	 * No-op if condition is already deactivated.
+	 */
+	public void deactivateCondition() {
+		if (sub != null) {
+			sub.unsubscribe();
+		}
+	}
+
+	/**
+	 * Activates the condition
+	 * No-op if condition is already activated.
+	 */
+	public void activateCondition() {
+		if (sub == null) {
+			this.sub = EasyBind.subscribe(condition, this::togglePlay);
+		}
+	}
+
+	private void togglePlay(boolean play) {
+		if (play) {
+			this.playFromStart();
+		} else {
+			this.stop();
+		}
+	}
+
+	public static Builder animate(Animation animation) {
+		return new Builder(animation);
+	}
+
+	public static class Builder {
+
+		private Animation animation;
+		private ObservableValue<Boolean> condition = new SimpleBooleanProperty(true);
+		private Runnable beforeStart = () -> {};
+		private Runnable afterStop = () -> {};
+
+		private Builder(Animation animation) {
+			this.animation = animation;
+		}
+
+		public Builder onCondition(ObservableValue<Boolean> condition) {
+			this.condition = condition;
+			return this;
+		}
+
+		public Builder beforeStart(Runnable beforeStart) {
+			this.beforeStart = beforeStart;
+			return this;
+		}
+
+		public Builder afterStop(Runnable afterStop) {
+			this.afterStop = afterStop;
+			return this;
+		}
+
+		public AutoAnimator build() {
+			return new AutoAnimator(animation, condition, beforeStart, afterStop);
+		}
+
+	}
+}

+ 36 - 0
src/main/java/org/cryptomator/ui/controls/FontAwesome5Spinner.java

@@ -0,0 +1,36 @@
+package org.cryptomator.ui.controls;
+
+import org.cryptomator.ui.common.Animations;
+import org.cryptomator.ui.common.AutoAnimator;
+
+import javafx.beans.NamedArg;
+import javafx.beans.value.ObservableValue;
+import java.util.Optional;
+
+/**
+ * An animated progress spinner using the {@link FontAwesome5IconView} with the spinner glyph.
+ * <p>
+ * Using the default constructor, the animation is always played if the icon is visible. To animate on other conditions, use the constructor with the "spinning" property.
+ */
+public class FontAwesome5Spinner extends FontAwesome5IconView {
+
+	private AutoAnimator animator;
+
+	public FontAwesome5Spinner() {
+		new FontAwesome5Spinner(Optional.empty());
+	}
+
+	public FontAwesome5Spinner(@NamedArg("spinning") ObservableValue<Boolean> spinning) {
+		new FontAwesome5Spinner(Optional.of(spinning));
+	}
+
+	private FontAwesome5Spinner(Optional<ObservableValue<Boolean>> animateCondition) {
+		setGlyph(FontAwesome5Icon.SPINNER);
+		var animation = Animations.createDiscrete360Rotation(this);
+		this.animator = AutoAnimator.animate(animation) //
+				.afterStop(() -> setRotate(0)) //
+				.onCondition(animateCondition.orElse(visibleProperty())) //
+				.build();
+	}
+
+}

+ 3 - 0
src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/PassphraseEntryController.java

@@ -5,6 +5,7 @@ import org.cryptomator.common.vaults.Vault;
 import org.cryptomator.ui.common.FxController;
 import org.cryptomator.ui.common.UserInteractionLock;
 import org.cryptomator.ui.common.WeakBindings;
+import org.cryptomator.ui.controls.FontAwesome5IconView;
 import org.cryptomator.ui.controls.NiceSecurePasswordField;
 import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent;
 import org.cryptomator.ui.keyloading.KeyLoading;
@@ -59,8 +60,10 @@ public class PassphraseEntryController implements FxController {
 	private final BooleanProperty unlockButtonDisabled;
 	private final StringBinding vaultName;
 
+	/* FXML */
 	public NiceSecurePasswordField passwordField;
 	public CheckBox savePasswordCheckbox;
+	public FontAwesome5IconView unlockInProgressView;
 	public ImageView face;
 	public ImageView leftArm;
 	public ImageView rightArm;

+ 17 - 0
src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java

@@ -1,10 +1,14 @@
 package org.cryptomator.ui.mainwindow;
 
 import com.tobiasdiez.easybind.EasyBind;
+import com.tobiasdiez.easybind.Subscription;
 import org.cryptomator.common.vaults.Vault;
 import org.cryptomator.common.vaults.VaultState;
+import org.cryptomator.ui.common.Animations;
+import org.cryptomator.ui.common.AutoAnimator;
 import org.cryptomator.ui.common.FxController;
 import org.cryptomator.ui.controls.FontAwesome5Icon;
+import org.cryptomator.ui.controls.FontAwesome5IconView;
 import org.cryptomator.ui.fxapp.FxApplication;
 
 import javax.inject.Inject;
@@ -22,6 +26,12 @@ public class VaultDetailController implements FxController {
 	private final Binding<FontAwesome5Icon> glyph;
 	private final BooleanBinding anyVaultSelected;
 
+	private AutoAnimator spinAnimation;
+
+	/* FXML */
+	public FontAwesome5IconView vaultStateView;
+
+
 	@Inject
 	VaultDetailController(ObjectProperty<Vault> vault, FxApplication application) {
 		this.vault = vault;
@@ -32,6 +42,13 @@ public class VaultDetailController implements FxController {
 		this.anyVaultSelected = vault.isNotNull();
 	}
 
+	public void initialize() {
+		this.spinAnimation = AutoAnimator.animate(Animations.createDiscrete360Rotation(vaultStateView)) //
+				.onCondition(EasyBind.select(vault).selectObject(Vault::stateProperty).map(VaultState.Value.PROCESSING::equals)) //
+				.afterStop(() -> vaultStateView.setRotate(0)) //
+				.build();
+	}
+
 	// TODO deduplicate w/ VaultListCellController
 	private FontAwesome5Icon getGlyphForVaultState(VaultState.Value state) {
 		if (state != null) {

+ 16 - 0
src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java

@@ -3,8 +3,11 @@ package org.cryptomator.ui.mainwindow;
 import com.tobiasdiez.easybind.EasyBind;
 import org.cryptomator.common.vaults.Vault;
 import org.cryptomator.common.vaults.VaultState;
+import org.cryptomator.ui.common.Animations;
+import org.cryptomator.ui.common.AutoAnimator;
 import org.cryptomator.ui.common.FxController;
 import org.cryptomator.ui.controls.FontAwesome5Icon;
+import org.cryptomator.ui.controls.FontAwesome5IconView;
 
 import javax.inject.Inject;
 import javafx.beans.binding.Binding;
@@ -17,6 +20,11 @@ public class VaultListCellController implements FxController {
 	private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
 	private final Binding<FontAwesome5Icon> glyph;
 
+	private AutoAnimator spinAnimation;
+
+	/* FXML */
+	public FontAwesome5IconView vaultStateView;
+
 	@Inject
 	VaultListCellController() {
 		this.glyph = EasyBind.select(vault) //
@@ -24,6 +32,13 @@ public class VaultListCellController implements FxController {
 				.map(this::getGlyphForVaultState);
 	}
 
+	public void initialize() {
+		this.spinAnimation = AutoAnimator.animate(Animations.createDiscrete360Rotation(vaultStateView)) //
+				.onCondition(EasyBind.select(vault).selectObject(Vault::stateProperty).map(VaultState.Value.PROCESSING::equals)) //
+				.afterStop(() -> vaultStateView.setRotate(0)) //
+				.build();
+	}
+
 	// TODO deduplicate w/ VaultDetailController
 	private FontAwesome5Icon getGlyphForVaultState(VaultState.Value state) {
 		if (state != null) {
@@ -59,4 +74,5 @@ public class VaultListCellController implements FxController {
 	public void setVault(Vault value) {
 		vault.set(value);
 	}
+
 }

+ 3 - 0
src/main/java/org/cryptomator/ui/migration/MigrationRunController.java

@@ -66,7 +66,10 @@ public class MigrationRunController implements FxController {
 	private final Lazy<Scene> capabilityErrorScene;
 	private final BooleanProperty migrationButtonDisabled;
 	private final DoubleProperty migrationProgress;
+
 	private volatile double volatileMigrationProgress = -1.0;
+
+	/* FXML */
 	public NiceSecurePasswordField passwordField;
 
 	@Inject

+ 2 - 0
src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java

@@ -26,6 +26,8 @@ public class UpdatesPreferencesController implements FxController {
 	private final ReadOnlyStringProperty latestVersion;
 	private final ReadOnlyStringProperty currentVersion;
 	private final BooleanBinding updateAvailable;
+
+	/* FXML */
 	public CheckBox checkForUpdatesCheckbox;
 
 	@Inject

+ 2 - 0
src/main/java/org/cryptomator/ui/quit/QuitController.java

@@ -30,6 +30,8 @@ public class QuitController implements FxController {
 	private final ExecutorService executorService;
 	private final VaultService vaultService;
 	private final AtomicReference<QuitResponse> quitResponse = new AtomicReference<>();
+
+	/* FXML */
 	public Button lockAndQuitButton;
 
 	@Inject

+ 1 - 0
src/main/java/org/cryptomator/ui/unlock/UnlockSuccessController.java

@@ -32,6 +32,7 @@ public class UnlockSuccessController implements FxController {
 	private final ObjectProperty<ContentDisplay> revealButtonState;
 	private final BooleanProperty revealButtonDisabled;
 
+	/* FXML */
 	public CheckBox rememberChoiceCheckbox;
 
 	@Inject

+ 4 - 69
src/main/resources/css/dark_theme.css

@@ -583,6 +583,10 @@
 	-fx-fill: white;
 }
 
+.button:default:disabled .glyph-icon {
+	-fx-fill: TEXT_FILL_MUTED;
+}
+
 .button:default .label {
 	-fx-text-fill: white;
 }
@@ -803,75 +807,6 @@
 	-fx-scale-shape: false;
 }
 
-/*******************************************************************************
- *                                                                             *
- * ProgressIndicator                                                           *
- * Derived from aquafx-project.com, (C) Claudine Zillmann, see NOTICE.md       *
- *                                                                             *
- ******************************************************************************/
-
-.progress-indicator {
-	-fx-indeterminate-segment-count: 12;
-	-fx-spin-enabled: true;
-}
-
-.progress-indicator:indeterminate > .spinner {
-	-fx-padding: 0.083333em;
-}
-
-.progress-indicator:indeterminate .segment {
-	-fx-background-color: PROGRESS_INDICATOR_BEGIN, PROGRESS_INDICATOR_END;
-	-fx-background-insets: 0, 0.5;
-}
-
-.progress-indicator:indeterminate .segment0 {
-	-fx-shape: "m 12.007729,4.9541827 c -0.49762,0.7596865 0.893181,1.6216808 1.327833,0.7666252 L 15.456199,2.0477574 C 15.942094,1.2061627 14.61426,0.43953765 14.128365,1.2811324 z";
-}
-
-.progress-indicator:indeterminate .segment1 {
-	-fx-shape: "m 9.2224559,4.62535 c -0.051108,0.9067177 1.5843581,0.957826 1.5332501,0 l 0,-4.24127319 c 0,-0.9717899 -1.5332501,-0.9717899 -1.5332501,0 z";
-}
-
-.progress-indicator:indeterminate .segment2 {
-	-fx-shape: "M 8.0465401,4.9030617 C 8.5441601,5.6627485 7.1533584,6.5247425 6.7187068,5.6696872 L 4.5980702,1.9966363 C 4.1121752,1.1550418 5.4400085,0.38841683 5.9259035,1.2300114 z";
-}
-
-.progress-indicator:indeterminate .segment3 {
-	-fx-shape: "M 5.7330066,6.5305598 C 6.5579512,6.9103162 5.8366865,8.3790371 5.0144939,7.8850315 L 1.2677551,5.8974832 C 0.409277,5.4420823 1.1277888,4.0876101 1.9862674,4.5430105 z";
-}
-
-.progress-indicator:indeterminate .segment4 {
-	-fx-shape: "m 0.42171041,9.2083842 c -0.90671825,-0.051108 -0.95782608,1.5843588 0,1.5332498 l 4.24127319,0 c 0.9717899,0 0.9717899,-1.5332498 0,-1.5332498 z";
-}
-
-.progress-indicator:indeterminate .segment5 {
-	-fx-shape: "M 5.7330066,13.443113 C 6.5579512,13.063356 5.8366865,11.594635 5.0144939,12.088641 L 1.2677551,14.076189 C 0.409277,14.53159 1.1277888,15.886062 1.9862674,15.430662 z";
-}
-
-.progress-indicator:indeterminate .segment6 {
-	-fx-shape: "M 8.0465401,15.070611 C 8.5441601,14.310924 7.1533584,13.44893 6.7187068,14.303985 l -2.1206366,3.673051 c -0.485895,0.841595 0.8419383,1.60822 1.3278333,0.766625 z";
-}
-
-.progress-indicator:indeterminate .segment7 {
-	-fx-shape: "m 9.2224559,19.539943 c -0.051108,0.906718 1.5843581,0.957826 1.5332501,0 l 0,-4.241273 c 0,-0.97179 -1.5332501,-0.97179 -1.5332501,0 z";
-}
-
-.progress-indicator:indeterminate .segment8 {
-	-fx-shape: "m 12.10997,15.070611 c -0.49762,-0.759687 0.893182,-1.621681 1.327834,-0.766626 l 2.120636,3.673051 c 0.485895,0.841595 -0.841938,1.60822 -1.327833,0.766625 z";
-}
-
-.progress-indicator:indeterminate .segment9 {
-	-fx-shape: "m 14.423504,13.443113 c -0.824945,-0.379757 -0.10368,-1.848478 0.718512,-1.354472 l 3.746739,1.987548 c 0.858478,0.455401 0.139967,1.809873 -0.718512,1.354473 z";
-}
-
-.progress-indicator:indeterminate .segment10 {
-	-fx-shape: "m 15.372451,9.2445322 c -0.906719,-0.051108 -0.957826,1.5843588 0,1.5332498 l 4.241273,0 c 0.97179,0 0.97179,-1.5332498 0,-1.5332498 z";
-}
-
-.progress-indicator:indeterminate .segment11 {
-	-fx-shape: "m 14.321262,6.5816808 c -0.824944,0.3797564 -0.10368,1.8484772 0.718513,1.3544717 L 18.786514,5.9486042 C 19.644992,5.4932031 18.92648,4.1387308 18.068001,4.5941315 z";
-}
-
 /*******************************************************************************
  *                                                                             *
  * ProgressBar                                                                 *

+ 4 - 69
src/main/resources/css/light_theme.css

@@ -581,6 +581,10 @@
 	-fx-fill: white;
 }
 
+.button:default:disabled .glyph-icon {
+	-fx-fill: TEXT_FILL_MUTED;
+}
+
 .button:default .label {
 	-fx-text-fill: white;
 }
@@ -801,75 +805,6 @@
 	-fx-scale-shape: false;
 }
 
-/*******************************************************************************
- *                                                                             *
- * ProgressIndicator                                                           *
- * Derived from aquafx-project.com, (C) Claudine Zillmann, see NOTICE.md       *
- *                                                                             *
- ******************************************************************************/
-
-.progress-indicator {
-	-fx-indeterminate-segment-count: 12;
-	-fx-spin-enabled: true;
-}
-
-.progress-indicator:indeterminate > .spinner {
-	-fx-padding: 0.083333em;
-}
-
-.progress-indicator:indeterminate .segment {
-	-fx-background-color: PROGRESS_INDICATOR_BEGIN, PROGRESS_INDICATOR_END;
-	-fx-background-insets: 0, 0.5;
-}
-
-.progress-indicator:indeterminate .segment0 {
-	-fx-shape: "m 12.007729,4.9541827 c -0.49762,0.7596865 0.893181,1.6216808 1.327833,0.7666252 L 15.456199,2.0477574 C 15.942094,1.2061627 14.61426,0.43953765 14.128365,1.2811324 z";
-}
-
-.progress-indicator:indeterminate .segment1 {
-	-fx-shape: "m 9.2224559,4.62535 c -0.051108,0.9067177 1.5843581,0.957826 1.5332501,0 l 0,-4.24127319 c 0,-0.9717899 -1.5332501,-0.9717899 -1.5332501,0 z";
-}
-
-.progress-indicator:indeterminate .segment2 {
-	-fx-shape: "M 8.0465401,4.9030617 C 8.5441601,5.6627485 7.1533584,6.5247425 6.7187068,5.6696872 L 4.5980702,1.9966363 C 4.1121752,1.1550418 5.4400085,0.38841683 5.9259035,1.2300114 z";
-}
-
-.progress-indicator:indeterminate .segment3 {
-	-fx-shape: "M 5.7330066,6.5305598 C 6.5579512,6.9103162 5.8366865,8.3790371 5.0144939,7.8850315 L 1.2677551,5.8974832 C 0.409277,5.4420823 1.1277888,4.0876101 1.9862674,4.5430105 z";
-}
-
-.progress-indicator:indeterminate .segment4 {
-	-fx-shape: "m 0.42171041,9.2083842 c -0.90671825,-0.051108 -0.95782608,1.5843588 0,1.5332498 l 4.24127319,0 c 0.9717899,0 0.9717899,-1.5332498 0,-1.5332498 z";
-}
-
-.progress-indicator:indeterminate .segment5 {
-	-fx-shape: "M 5.7330066,13.443113 C 6.5579512,13.063356 5.8366865,11.594635 5.0144939,12.088641 L 1.2677551,14.076189 C 0.409277,14.53159 1.1277888,15.886062 1.9862674,15.430662 z";
-}
-
-.progress-indicator:indeterminate .segment6 {
-	-fx-shape: "M 8.0465401,15.070611 C 8.5441601,14.310924 7.1533584,13.44893 6.7187068,14.303985 l -2.1206366,3.673051 c -0.485895,0.841595 0.8419383,1.60822 1.3278333,0.766625 z";
-}
-
-.progress-indicator:indeterminate .segment7 {
-	-fx-shape: "m 9.2224559,19.539943 c -0.051108,0.906718 1.5843581,0.957826 1.5332501,0 l 0,-4.241273 c 0,-0.97179 -1.5332501,-0.97179 -1.5332501,0 z";
-}
-
-.progress-indicator:indeterminate .segment8 {
-	-fx-shape: "m 12.10997,15.070611 c -0.49762,-0.759687 0.893182,-1.621681 1.327834,-0.766626 l 2.120636,3.673051 c 0.485895,0.841595 -0.841938,1.60822 -1.327833,0.766625 z";
-}
-
-.progress-indicator:indeterminate .segment9 {
-	-fx-shape: "m 14.423504,13.443113 c -0.824945,-0.379757 -0.10368,-1.848478 0.718512,-1.354472 l 3.746739,1.987548 c 0.858478,0.455401 0.139967,1.809873 -0.718512,1.354473 z";
-}
-
-.progress-indicator:indeterminate .segment10 {
-	-fx-shape: "m 15.372451,9.2445322 c -0.906719,-0.051108 -0.957826,1.5843588 0,1.5332498 l 4.241273,0 c 0.97179,0 0.97179,-1.5332498 0,-1.5332498 z";
-}
-
-.progress-indicator:indeterminate .segment11 {
-	-fx-shape: "m 14.321262,6.5816808 c -0.824944,0.3797564 -0.10368,1.8484772 0.718513,1.3544717 L 18.786514,5.9486042 C 19.644992,5.4932031 18.92648,4.1387308 18.068001,4.5941315 z";
-}
-
 /*******************************************************************************
  *                                                                             *
  * ProgressBar                                                                 *

+ 2 - 2
src/main/resources/fxml/addvault_new_password.fxml

@@ -4,11 +4,11 @@
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.ButtonBar?>
 <?import javafx.scene.control.Label?>
-<?import javafx.scene.control.ProgressIndicator?>
 <?import javafx.scene.control.RadioButton?>
 <?import javafx.scene.control.ToggleGroup?>
 <?import javafx.scene.layout.Region?>
 <?import javafx.scene.layout.VBox?>
+<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
 <VBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
 	  fx:controller="org.cryptomator.ui.addvaultwizard.CreateNewVaultPasswordController"
@@ -41,7 +41,7 @@
 				<Button text="%addvaultwizard.new.createVaultBtn" ButtonBar.buttonData="NEXT_FORWARD" onAction="#next" defaultButton="true" disable="${!controller.readyToCreateVault}"
 						contentDisplay="${controller.createVaultButtonState}">
 					<graphic>
-						<ProgressIndicator progress="-1" prefWidth="12" prefHeight="12"/>
+						<FontAwesome5Spinner fx:id="spinner" glyphSize="12" />
 					</graphic>
 				</Button>
 			</buttons>

+ 2 - 2
src/main/resources/fxml/migration_run.fxml

@@ -7,8 +7,8 @@
 <?import javafx.scene.control.ButtonBar?>
 <?import javafx.scene.control.Label?>
 <?import javafx.scene.control.ProgressBar?>
-<?import javafx.scene.control.ProgressIndicator?>
 <?import javafx.scene.layout.VBox?>
+<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
 <VBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
 	  fx:controller="org.cryptomator.ui.migration.MigrationRunController"
@@ -37,7 +37,7 @@
 					<Button text="%migration.run.startMigrationBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#migrate" contentDisplay="${controller.migrateButtonContentDisplay}"
 							disable="${controller.migrationButtonDisabled}">
 						<graphic>
-							<ProgressIndicator progress="-1" prefWidth="12" prefHeight="12"/>
+							<FontAwesome5Spinner glyphSize="12" />
 						</graphic>
 					</Button>
 				</buttons>

+ 2 - 2
src/main/resources/fxml/preferences_updates.fxml

@@ -6,8 +6,8 @@
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.CheckBox?>
 <?import javafx.scene.control.Hyperlink?>
-<?import javafx.scene.control.ProgressIndicator?>
 <?import javafx.scene.layout.VBox?>
+<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
 <VBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
 	  fx:controller="org.cryptomator.ui.preferences.UpdatesPreferencesController"
@@ -26,7 +26,7 @@
 		<VBox alignment="CENTER" spacing="6">
 			<Button text="%preferences.updates.checkNowBtn" defaultButton="true" onAction="#checkNow" contentDisplay="${controller.checkForUpdatesButtonState}">
 				<graphic>
-					<ProgressIndicator progress="-1" prefWidth="12" prefHeight="12"/>
+					<FontAwesome5Spinner fx:id="spinner" glyphSize="12"/>
 				</graphic>
 			</Button>
 

+ 2 - 2
src/main/resources/fxml/quit.fxml

@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <?import org.cryptomator.ui.controls.FontAwesome5IconView?>
+<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
 <?import javafx.geometry.Insets?>
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.ButtonBar?>
 <?import javafx.scene.control.Label?>
-<?import javafx.scene.control.ProgressIndicator?>
 <?import javafx.scene.layout.HBox?>
 <?import javafx.scene.layout.StackPane?>
 <?import javafx.scene.layout.VBox?>
@@ -35,7 +35,7 @@
 					<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" cancelButton="true" onAction="#cancel"/>
 					<Button fx:id="lockAndQuitButton" text="%quit.lockAndQuit" ButtonBar.buttonData="FINISH" onAction="#lockAndQuit" contentDisplay="TEXT_ONLY">
 						<graphic>
-							<ProgressIndicator progress="-1" prefWidth="12" prefHeight="12"/>
+							<FontAwesome5Spinner glyphSize="12"/>
 						</graphic>
 					</Button>
 				</buttons>

+ 2 - 2
src/main/resources/fxml/unlock_enter_password.fxml

@@ -6,12 +6,12 @@
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.ButtonBar?>
 <?import javafx.scene.control.CheckBox?>
-<?import javafx.scene.control.ProgressIndicator?>
 <?import javafx.scene.image.Image?>
 <?import javafx.scene.image.ImageView?>
 <?import javafx.scene.layout.HBox?>
 <?import javafx.scene.layout.StackPane?>
 <?import javafx.scene.layout.VBox?>
+<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
 <VBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
 	  fx:controller="org.cryptomator.ui.keyloading.masterkeyfile.PassphraseEntryController"
@@ -58,7 +58,7 @@
 					<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#cancel" disable="${controller.userInteractionDisabled}"/>
 					<Button text="%unlock.unlockBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#unlock" contentDisplay="${controller.unlockButtonContentDisplay}" disable="${controller.unlockButtonDisabled}">
 						<graphic>
-							<ProgressIndicator progress="-1" prefWidth="12" prefHeight="12"/>
+							<FontAwesome5Spinner glyphSize="12" />
 						</graphic>
 					</Button>
 				</buttons>

+ 2 - 2
src/main/resources/fxml/unlock_success.fxml

@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <?import org.cryptomator.ui.controls.FontAwesome5IconView?>
+<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
 <?import org.cryptomator.ui.controls.FormattedLabel?>
 <?import javafx.geometry.Insets?>
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.ButtonBar?>
 <?import javafx.scene.control.CheckBox?>
-<?import javafx.scene.control.ProgressIndicator?>
 <?import javafx.scene.layout.HBox?>
 <?import javafx.scene.layout.StackPane?>
 <?import javafx.scene.layout.VBox?>
@@ -39,7 +39,7 @@
 					<Button text="%generic.button.done" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
 					<Button text="%unlock.success.revealBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#revealAndClose" contentDisplay="${controller.revealButtonState}" disable="${controller.revealButtonDisabled}">
 						<graphic>
-							<ProgressIndicator progress="-1" prefWidth="12" prefHeight="12"/>
+							<FontAwesome5Spinner glyphSize="12"/>
 						</graphic>
 					</Button>
 				</buttons>

+ 1 - 1
src/main/resources/fxml/vault_detail.fxml

@@ -22,7 +22,7 @@
 		<HBox spacing="12" visible="${controller.anyVaultSelected}" managed="${controller.anyVaultSelected}">
 			<StackPane alignment="CENTER">
 				<Circle styleClass="glyph-icon-primary" radius="16"/>
-				<FontAwesome5IconView styleClass="glyph-icon-white" glyph="${controller.glyph}" HBox.hgrow="NEVER" glyphSize="16"/>
+				<FontAwesome5IconView fx:id="vaultStateView" styleClass="glyph-icon-white" glyph="${controller.glyph}" HBox.hgrow="NEVER" glyphSize="16"/>
 			</StackPane>
 			<VBox spacing="4" HBox.hgrow="ALWAYS">
 				<HBox spacing="12">

+ 1 - 1
src/main/resources/fxml/vault_list_cell.fxml

@@ -19,7 +19,7 @@
 	</padding>
 	<children>
 		<VBox alignment="CENTER" minWidth="20">
-			<FontAwesome5IconView glyph="${controller.glyph}" HBox.hgrow="NEVER" glyphSize="16"/>
+			<FontAwesome5IconView fx:id="vaultStateView" glyph="${controller.glyph}" HBox.hgrow="NEVER" glyphSize="16"/>
 		</VBox>
 		<VBox spacing="4" HBox.hgrow="ALWAYS">
 			<Label styleClass="header-label" text="${controller.vault.displayName}"/>