diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java index 69564b876..4ef791110 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java @@ -2,9 +2,12 @@ package org.cryptomator.common.vaults; import javafx.application.Platform; import javafx.beans.Observable; +import javafx.beans.property.DoubleProperty; import javafx.beans.property.LongProperty; import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleLongProperty; +import javafx.beans.value.ObservableValue; import javafx.concurrent.ScheduledService; import javafx.concurrent.Task; import javafx.util.Duration; @@ -28,6 +31,7 @@ public class VaultStats { private final ScheduledService> updateService; private final LongProperty bytesPerSecondRead = new SimpleLongProperty(); private final LongProperty bytesPerSecondWritten = new SimpleLongProperty(); + private final DoubleProperty cacheHitRate = new SimpleDoubleProperty(); @Inject VaultStats(AtomicReference fs, ObjectProperty state, ExecutorService executor) { @@ -55,6 +59,17 @@ public class VaultStats { assert Platform.isFxApplicationThread(); bytesPerSecondRead.set(stats.map(CryptoFileSystemStats::pollBytesRead).orElse(0l)); bytesPerSecondWritten.set(stats.map(CryptoFileSystemStats::pollBytesWritten).orElse(0l)); + cacheHitRate.set(stats.map(this::getCacheHitRate).orElse(0.0)); + } + + private double getCacheHitRate(CryptoFileSystemStats stats) { + long accesses = stats.pollChunkCacheAccesses(); + long hits = stats.pollChunkCacheHits(); + if (accesses == 0) { + return 0.0; + } else { + return hits / (double) accesses; + } } private class UpdateStatsService extends ScheduledService> { @@ -94,4 +109,13 @@ public class VaultStats { public long getBytesPerSecondWritten() { return bytesPerSecondWritten.get(); } + + public DoubleProperty cacheHitRateProperty() { + return cacheHitRate; + } + + public double getCacheHitRate() { + return cacheHitRate.get(); + } + } diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java index 3a3b47d1b..52108a12a 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java @@ -28,7 +28,7 @@ public enum FxmlFile { UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), // UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), // VAULT_OPTIONS("/fxml/vault_options.fxml"), // - VAULT_STATISTICS("/fxml/vault_statistics.fxml"), // + VAULT_STATISTICS("/fxml/stats.fxml"), // WRONGFILEALERT("/fxml/wrongfilealert.fxml"); private final String ressourcePathString; diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/WeakBindings.java b/main/ui/src/main/java/org/cryptomator/ui/common/WeakBindings.java index 70111dd38..34c90d212 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/WeakBindings.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/WeakBindings.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.common; +import javafx.beans.binding.DoubleBinding; import javafx.beans.binding.LongBinding; import javafx.beans.binding.StringBinding; import javafx.beans.value.ObservableObjectValue; @@ -50,4 +51,23 @@ public final class WeakBindings { }; } + /** + * Create a new DoubleBinding that listens to changes from the given observable without being strongly referenced by it. + * + * @param observable The observable + * @return a DoubleBinding weakly referenced from the given observable + */ + public static DoubleBinding bindDouble(ObservableValue observable) { + return new DoubleBinding() { + { + bind(observable); + } + + @Override + protected double computeValue() { + return observable.getValue().doubleValue(); + } + }; + } + } diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java index eca71c38e..cc9c98c87 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java @@ -20,7 +20,7 @@ import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.migration.MigrationComponent; import org.cryptomator.ui.removevault.RemoveVaultComponent; import org.cryptomator.ui.vaultoptions.VaultOptionsComponent; -import org.cryptomator.ui.vaultstatistics.VaultStatisticsComponent; +import org.cryptomator.ui.stats.VaultStatisticsComponent; import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent; import javax.inject.Provider; diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java index e42b902e7..7f64cf7e5 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java @@ -6,7 +6,7 @@ import javafx.fxml.FXML; import org.cryptomator.common.vaults.Vault; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.VaultService; -import org.cryptomator.ui.vaultstatistics.VaultStatisticsComponent; +import org.cryptomator.ui.stats.VaultStatisticsComponent; import javax.inject.Inject; diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsComponent.java b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsComponent.java similarity index 94% rename from main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsComponent.java rename to main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsComponent.java index 915f936f9..18c596793 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsComponent.java +++ b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsComponent.java @@ -1,4 +1,4 @@ -package org.cryptomator.ui.vaultstatistics; +package org.cryptomator.ui.stats; import dagger.BindsInstance; import dagger.Lazy; diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsController.java b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsController.java similarity index 59% rename from main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsController.java rename to main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsController.java index 46ee16fb1..f8914c476 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsController.java @@ -1,13 +1,17 @@ -package org.cryptomator.ui.vaultstatistics; +package org.cryptomator.ui.stats; import javafx.animation.Animation; import javafx.animation.KeyFrame; import javafx.animation.Timeline; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.DoubleBinding; import javafx.beans.binding.LongBinding; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; +import javafx.scene.chart.AreaChart; import javafx.scene.chart.LineChart; +import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart.Data; import javafx.scene.chart.XYChart.Series; import javafx.stage.Stage; @@ -18,13 +22,14 @@ import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.WeakBindings; import javax.inject.Inject; +import java.util.Arrays; import java.util.ResourceBundle; @VaultStatisticsScoped public class VaultStatisticsController implements FxController { - private static final int IO_SAMPLING_STEPS = 100; - private static final double IO_SAMPLING_INTERVAL = 0.5; + private static final int IO_SAMPLING_STEPS = 30; + private static final double IO_SAMPLING_INTERVAL = 1; private final VaultStats stats; private final Series readData; @@ -32,19 +37,27 @@ public class VaultStatisticsController implements FxController { private final Timeline ioAnimation; private final LongBinding bpsRead; private final LongBinding bpsWritten; + private final DoubleBinding cacheHitRate; + private final DoubleBinding cacheHitDregrees; + private final DoubleBinding cacheHitPercentage; - public LineChart lineGraph; + public AreaChart readChart; + public AreaChart writeChart; + public NumberAxis readChartXAxis; + public NumberAxis readChartYAxis; + public NumberAxis writeChartXAxis; + public NumberAxis writeChartYAxis; @Inject public VaultStatisticsController(@VaultStatisticsWindow Stage window, @VaultStatisticsWindow Vault vault, ResourceBundle resourceBundle) { this.stats = vault.getStats(); + this.readData = new Series<>(); + this.writeData = new Series<>(); this.bpsRead = WeakBindings.bindLong(stats.bytesPerSecondReadProperty()); this.bpsWritten = WeakBindings.bindLong(stats.bytesPerSecondWrittenProperty()); - - this.readData = new Series<>(); - readData.setName(resourceBundle.getString("vaultstatistics.readDataLabel")); - this.writeData = new Series<>(); - writeData.setName(resourceBundle.getString("vaultstatistics.writtenDataLabel")); + this.cacheHitRate = WeakBindings.bindDouble(stats.cacheHitRateProperty()); + this.cacheHitDregrees = cacheHitRate.multiply(-270); + this.cacheHitPercentage = cacheHitRate.multiply(100); this.ioAnimation = new Timeline(); //TODO Research better timer ioAnimation.getKeyFrames().add(new KeyFrame(Duration.seconds(IO_SAMPLING_INTERVAL), new IoSamplingAnimationHandler(readData, writeData))); @@ -60,14 +73,18 @@ public class VaultStatisticsController implements FxController { @FXML public void initialize() { - lineGraph.getData().addAll(readData, writeData); + readChart.getData().addAll(readData); + writeChart.getData().addAll(writeData); } private class IoSamplingAnimationHandler implements EventHandler { private static final double BYTES_TO_MEGABYTES_FACTOR = 1.0 / IO_SAMPLING_INTERVAL / 1024.0 / 1024.0; + + private long step = IO_SAMPLING_STEPS; private final Series decryptedBytesRead; private final Series encryptedBytesWrite; + private final long[] maxBuf = new long[IO_SAMPLING_STEPS]; public IoSamplingAnimationHandler(Series readData, Series writeData) { this.decryptedBytesRead = readData; @@ -82,23 +99,28 @@ public class VaultStatisticsController implements FxController { @Override public void handle(ActionEvent event) { - // move all values one step: - for (int i = 0; i < IO_SAMPLING_STEPS - 1; i++) { - int j = i + 1; - Number tmp = decryptedBytesRead.getData().get(j).getYValue(); - decryptedBytesRead.getData().get(i).setYValue(tmp); + final long currentStep = step++; + final long decBytes = stats.bytesPerSecondReadProperty().get(); + final long encBytes = stats.bytesPerSecondWrittenProperty().get(); - tmp = encryptedBytesWrite.getData().get(j).getYValue(); - encryptedBytesWrite.getData().get(i).setYValue(tmp); - } + maxBuf[(int) currentStep % IO_SAMPLING_STEPS] = Math.max(decBytes, encBytes); + long allTimeMax = Arrays.stream(maxBuf).max().orElse(0l); + + // remove oldest value: + decryptedBytesRead.getData().remove(0); + encryptedBytesWrite.getData().remove(0); // add latest value: - final long decBytes = stats.bytesPerSecondReadProperty().get(); - final double decMb = decBytes * BYTES_TO_MEGABYTES_FACTOR; - final long encBytes = stats.bytesPerSecondWrittenProperty().get(); - final double encMb = encBytes * BYTES_TO_MEGABYTES_FACTOR; - decryptedBytesRead.getData().get(IO_SAMPLING_STEPS - 1).setYValue(decMb); - encryptedBytesWrite.getData().get(IO_SAMPLING_STEPS - 1).setYValue(encMb); + decryptedBytesRead.getData().add(new Data<>(currentStep, decBytes)); + encryptedBytesWrite.getData().add(new Data<>(currentStep, encBytes)); + + // adjust ranges: + readChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS); + readChartXAxis.setUpperBound(currentStep); + readChartYAxis.setUpperBound(allTimeMax); + writeChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS); + writeChartXAxis.setUpperBound(currentStep); + writeChartYAxis.setUpperBound(allTimeMax); } } @@ -119,4 +141,20 @@ public class VaultStatisticsController implements FxController { public long getBpsWritten() { return bpsWritten.get(); } + + public DoubleBinding cacheHitPercentageProperty() { + return cacheHitPercentage; + } + + public double getCacheHitPercentage() { + return cacheHitPercentage.get(); + } + + public DoubleBinding cacheHitDregreesProperty() { + return cacheHitDregrees; + } + + public double getCacheHitDregrees() { + return cacheHitDregrees.get(); + } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsModule.java b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsModule.java similarity index 90% rename from main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsModule.java rename to main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsModule.java index 4acad0869..1d3b1eeab 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsModule.java @@ -1,4 +1,4 @@ -package org.cryptomator.ui.vaultstatistics; +package org.cryptomator.ui.stats; import dagger.Binds; import dagger.Module; @@ -38,7 +38,7 @@ abstract class VaultStatisticsModule { @VaultStatisticsScoped static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle, @VaultStatisticsWindow Vault vault) { Stage stage = factory.create(); - stage.setTitle(String.format(resourceBundle.getString("vaultstatistics.title"), vault.getDisplayableName())); + stage.setTitle(String.format(resourceBundle.getString("stats.title"), vault.getDisplayableName())); stage.setResizable(false); var weakStage = new WeakReference<>(stage); vault.stateProperty().addListener(new ChangeListener<>() { @@ -60,7 +60,7 @@ abstract class VaultStatisticsModule { @FxmlScene(FxmlFile.VAULT_STATISTICS) @VaultStatisticsScoped static Scene provideVaultStatisticsScene(@VaultStatisticsWindow FXMLLoaderFactory fxmlLoaders) { - return fxmlLoaders.createScene("/fxml/vault_statistics.fxml"); + return fxmlLoaders.createScene("/fxml/stats.fxml"); } // ------------------ diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsScoped.java b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsScoped.java similarity index 85% rename from main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsScoped.java rename to main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsScoped.java index 842e7e508..43de85b54 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsScoped.java +++ b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsScoped.java @@ -1,4 +1,4 @@ -package org.cryptomator.ui.vaultstatistics; +package org.cryptomator.ui.stats; import javax.inject.Scope; import java.lang.annotation.Documented; diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsWindow.java b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsWindow.java similarity index 85% rename from main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsWindow.java rename to main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsWindow.java index 4fe9174a1..2cd3ca37f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsWindow.java +++ b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsWindow.java @@ -1,4 +1,4 @@ -package org.cryptomator.ui.vaultstatistics; +package org.cryptomator.ui.stats; import javax.inject.Qualifier; import java.lang.annotation.Documented; diff --git a/main/ui/src/main/resources/css/dark_theme.css b/main/ui/src/main/resources/css/dark_theme.css index 71195116d..5450664ab 100644 --- a/main/ui/src/main/resources/css/dark_theme.css +++ b/main/ui/src/main/resources/css/dark_theme.css @@ -874,59 +874,52 @@ * I/O Statistics * * * ******************************************************************************/ -.chart { + +.cache-arc-background { + -fx-fill: transparent; + -fx-stroke: MUTED_BG; + -fx-stroke-type: centered; + -fx-stroke-width: 12; + -fx-stroke-line-cap: butt; +} + +.cache-arc-foreground { + -fx-fill: transparent; + -fx-stroke: PRIMARY; + -fx-stroke-type: centered; + -fx-stroke-width: 12; + -fx-stroke-line-cap: butt; +} + +.chart.io-stats { -fx-padding: 10px; + -fx-horizontal-grid-lines-visible: false; + -fx-horizontal-zero-line-visible: false; + -fx-vertical-grid-lines-visible: false; + -fx-vertical-zero-line-visible: false; +} + +.axis.io-stats { + -fx-tick-mark-visible: false; + -fx-minor-tick-visible: false; + -fx-tick-labels-visible: false; } .chart-plot-background { - -fx-background-color: MAIN_BG; - -fx-padding: 20px; + -fx-background-color: transparent; } -/* content */ - -.chart-content { - -fx-padding: 10px; - -fx-text-fill: TEXT_FILL; - -fx-tick-label-fill: GRAY_3; - -fx-minor-tick-visible: false -} -.chart-horizontal-grid-lines { - -fx-stroke: PRIMARY_L2; -} .chart-vertical-zero-line, -.chart-horizontal-zero-line { - -fx-stroke: PRIMARY_L2; -} -.chart-series-line { - -fx-stroke-width: 2px; -} +.chart-horizontal-zero-line, .chart-alternative-row-fill { - -fx-fill: GRAY_3; -fx-stroke: transparent; -fx-stroke-width: 0; } -.axis { - -fx-tick-label-fill: TEXT_FILL; - -fx-tick-length: 20; - -fx-minor-tick-length: 10; -} -.axis-label { - -fx-text-fill: TEXT_FILL; -} -.default-color0.chart-series-line { -fx-stroke: PRIMARY; } -.default-color1.chart-series-line { -fx-stroke: RED_5 ; } -/* legend */ - -.chart-legend { - -fx-text-fill: TEXT_FILL; - -fx-background-color: transparent; - -fx-padding: 0.4em; +.default-color0.chart-series-area-line { + -fx-stroke: PRIMARY; } -.chart-line-symbol { - -fx-background-radius: 5px; - -fx-padding: 5px; +.default-color0.chart-series-area-fill { + -fx-fill: linear-gradient(to bottom, PRIMARY, transparent); + -fx-stroke: transparent; } -.default-color0.chart-line-symbol { -fx-background-color: PRIMARY; } -.default-color1.chart-line-symbol { -fx-background-color: RED_5; } diff --git a/main/ui/src/main/resources/css/light_theme.css b/main/ui/src/main/resources/css/light_theme.css index 855e48ec0..cc0e848ab 100644 --- a/main/ui/src/main/resources/css/light_theme.css +++ b/main/ui/src/main/resources/css/light_theme.css @@ -884,48 +884,52 @@ /* content */ -.chart-content { + +.cache-arc-background { + -fx-fill: transparent; + -fx-stroke: MUTED_BG; + -fx-stroke-type: centered; + -fx-stroke-width: 12; + -fx-stroke-line-cap: butt; +} + +.cache-arc-foreground { + -fx-fill: transparent; + -fx-stroke: PRIMARY; + -fx-stroke-type: centered; + -fx-stroke-width: 12; + -fx-stroke-line-cap: butt; +} + +.chart.io-stats { -fx-padding: 10px; - -fx-text-fill: TEXT_FILL; - -fx-tick-label-fill: GRAY_3; - -fx-minor-tick-visible: false + -fx-horizontal-grid-lines-visible: false; + -fx-horizontal-zero-line-visible: false; + -fx-vertical-grid-lines-visible: false; + -fx-vertical-zero-line-visible: false; } -.chart-horizontal-grid-lines { - -fx-stroke: PRIMARY_D2; + +.axis.io-stats { + -fx-tick-mark-visible: false; + -fx-minor-tick-visible: false; + -fx-tick-labels-visible: false; } + +.chart-plot-background { + -fx-background-color: transparent; +} + .chart-vertical-zero-line, -.chart-horizontal-zero-line { - -fx-stroke: PRIMARY_D2; -} -.chart-series-line { - -fx-stroke-width: 2px; -} +.chart-horizontal-zero-line, .chart-alternative-row-fill { - -fx-fill: GRAY_8; -fx-stroke: transparent; -fx-stroke-width: 0; } -.axis { - -fx-tick-label-fill: TEXT_FILL; - -fx-tick-length: 20; - -fx-minor-tick-length: 10; -} -.axis-label { - -fx-text-fill: TEXT_FILL; -} -.default-color0.chart-series-line { -fx-stroke: PRIMARY; } -.default-color1.chart-series-line { -fx-stroke: RED_5 ; } -/* legend */ - -.chart-legend { - -fx-text-fill: TEXT_FILL; - -fx-background-color: transparent; - -fx-padding: 0.4em; +.default-color0.chart-series-area-line { + -fx-stroke: PRIMARY; } -.chart-line-symbol { - -fx-background-radius: 5px; - -fx-padding: 5px; +.default-color0.chart-series-area-fill { + -fx-fill: linear-gradient(to bottom, PRIMARY, transparent); + -fx-stroke: transparent; } -.default-color0.chart-line-symbol { -fx-background-color: PRIMARY; } -.default-color1.chart-line-symbol { -fx-background-color: RED_5; } diff --git a/main/ui/src/main/resources/fxml/stats.fxml b/main/ui/src/main/resources/fxml/stats.fxml new file mode 100644 index 000000000..d0cef21c4 --- /dev/null +++ b/main/ui/src/main/resources/fxml/stats.fxml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/ui/src/main/resources/fxml/vault_statistics.fxml b/main/ui/src/main/resources/fxml/vault_statistics.fxml deleted file mode 100644 index 5de2d45e6..000000000 --- a/main/ui/src/main/resources/fxml/vault_statistics.fxml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/main/ui/src/main/resources/i18n/strings.properties b/main/ui/src/main/resources/i18n/strings.properties index 0f1cee1cf..3681c281c 100644 --- a/main/ui/src/main/resources/i18n/strings.properties +++ b/main/ui/src/main/resources/i18n/strings.properties @@ -161,12 +161,10 @@ preferences.donationKey.getDonationKey=Get a donation key preferences.about=About # Vault Statistics -vaultstatistics.title=Statistics for %s -vaultstatistics.xAxisTimeLabel=Seconds -vaultstatistics.yAxisThroughputLabel=Throughput in KiB/s -vaultstatistics.throughputTitle=Read and Writes -vaultstatistics.readDataLabel=Read Data -vaultstatistics.writtenDataLabel=Written Data +stats.title=Statistics for %s +stats.readDataLabel=Read Data +stats.writtenDataLabel=Written Data +stats.cacheHitRate=Cache Hit Rate # Main Window main.closeBtn.tooltip=Close