diff --git a/app/src/main/java/io/xpipe/app/browser/BrowserSessionTabsComp.java b/app/src/main/java/io/xpipe/app/browser/BrowserSessionTabsComp.java index e0687036b..5eba37c95 100644 --- a/app/src/main/java/io/xpipe/app/browser/BrowserSessionTabsComp.java +++ b/app/src/main/java/io/xpipe/app/browser/BrowserSessionTabsComp.java @@ -2,8 +2,10 @@ package io.xpipe.app.browser; import io.xpipe.app.comp.Comp; import io.xpipe.app.comp.SimpleComp; +import io.xpipe.app.comp.base.LoadingIconComp; import io.xpipe.app.comp.base.PrettyImageHelper; import io.xpipe.app.core.App; +import io.xpipe.app.core.AppFontSizes; import io.xpipe.app.core.AppI18n; import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.util.BooleanScope; @@ -417,26 +419,15 @@ public class BrowserSessionTabsComp extends SimpleComp { }); if (tabModel.getIcon() != null) { - var ring = new RingProgressIndicator(0, false); - ring.setMinSize(16, 16); - ring.setPrefSize(16, 16); - ring.setMaxSize(16, 16); - ring.progressProperty() - .bind(Bindings.createDoubleBinding( - () -> tabModel.getBusy().get() - && !AppPrefs.get().performanceMode().get() - ? -1d - : 0, - PlatformThread.sync(tabModel.getBusy()), - AppPrefs.get().performanceMode())); - + var loading = new LoadingIconComp(tabModel.getBusy(), AppFontSizes::base).createRegion(); + loading.setPrefWidth(16); + loading.setPrefHeight(16); var image = tabModel.getIcon(); var logo = PrettyImageHelper.ofFixedSizeSquare(image, 16).createRegion(); - tab.graphicProperty() .bind(Bindings.createObjectBinding( () -> { - return tabModel.getBusy().get() ? ring : logo; + return tabModel.getBusy().get() ? loading : logo; }, PlatformThread.sync(tabModel.getBusy()))); } @@ -496,6 +487,7 @@ public class BrowserSessionTabsComp extends SimpleComp { if (newValue != null) { Platform.runLater(() -> { Label l = (Label) tabs.lookup("#" + id + " .tab-label"); + l.setGraphicTextGap(7); var w = l.maxWidthProperty(); l.minWidthProperty().bind(w); l.prefWidthProperty().bind(w); diff --git a/app/src/main/java/io/xpipe/app/comp/base/LoadingIconComp.java b/app/src/main/java/io/xpipe/app/comp/base/LoadingIconComp.java new file mode 100644 index 000000000..16d2afb10 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/comp/base/LoadingIconComp.java @@ -0,0 +1,76 @@ +package io.xpipe.app.comp.base; + +import atlantafx.base.controls.RingProgressIndicator; +import io.xpipe.app.comp.Comp; +import io.xpipe.app.comp.CompStructure; +import io.xpipe.app.comp.SimpleComp; +import io.xpipe.app.comp.SimpleCompStructure; +import io.xpipe.app.core.AppFontSizes; +import io.xpipe.app.prefs.AppPrefs; +import io.xpipe.app.util.PlatformThread; +import io.xpipe.app.util.ThreadHelper; +import javafx.animation.AnimationTimer; +import javafx.application.Platform; +import javafx.beans.binding.Bindings; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; + +import java.util.function.Consumer; +import java.util.function.Function; + +public class LoadingIconComp extends SimpleComp { + + private static final char[] chars = new char[] {'⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'}; + + private final ObservableValue show; + private final Consumer fontSize; + + public LoadingIconComp(ObservableValue show, Consumer fontSize) {this.show = show; + this.fontSize = fontSize; + } + + @Override + protected Region createSimple() { + var label = new Label(); + fontSize.accept(label); + label.setAlignment(Pos.CENTER); + label.setText(Character.toString(chars[0])); + label.getStyleClass().add("loading-icon-comp"); + + var timer = new AnimationTimer() { + + long init = 0; + int index = 0; + + @Override + public void handle(long now) { + if (init == 0) { + init = now; + } + + var nowMs = now; + if ((nowMs - init) > 300 * 1_000_000L) { + label.setText(Character.toString(chars[index])); + init = nowMs; + index = (index + 1) % chars.length; + } + } + }; + + show.subscribe(val -> { + if (val) { + timer.start(); + } else { + timer.stop(); + } + }); + + return label; + } +} diff --git a/app/src/main/java/io/xpipe/app/comp/base/LoadingOverlayComp.java b/app/src/main/java/io/xpipe/app/comp/base/LoadingOverlayComp.java index 5c06b1d2c..41c6ed435 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/LoadingOverlayComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/LoadingOverlayComp.java @@ -3,6 +3,7 @@ package io.xpipe.app.comp.base; import io.xpipe.app.comp.Comp; import io.xpipe.app.comp.CompStructure; import io.xpipe.app.comp.SimpleCompStructure; +import io.xpipe.app.core.AppFontSizes; import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.util.PlatformThread; import io.xpipe.app.util.ThreadHelper; @@ -37,9 +38,7 @@ public class LoadingOverlayComp extends Comp> { var compStruc = comp.createStructure(); var r = compStruc.get(); - var loading = new RingProgressIndicator(0, false); - loading.progressProperty().bind(progress); - loading.visibleProperty().bind(Bindings.not(AppPrefs.get().performanceMode())); + var loading = new LoadingIconComp(showLoading, AppFontSizes::xxxl).createRegion(); var loadingOverlay = new StackPane(loading); loadingOverlay.getStyleClass().add("loading-comp"); diff --git a/app/src/main/java/io/xpipe/app/core/AppFontSizes.java b/app/src/main/java/io/xpipe/app/core/AppFontSizes.java index 29be5ffad..c0345518b 100644 --- a/app/src/main/java/io/xpipe/app/core/AppFontSizes.java +++ b/app/src/main/java/io/xpipe/app/core/AppFontSizes.java @@ -42,7 +42,7 @@ public class AppFontSizes { } public static void xxxl(Node node) { - apply(node, AppFontSizes::getXxl); + apply(node, AppFontSizes::getXxxl); } public static void title(Node node) { diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalDockComp.java b/app/src/main/java/io/xpipe/app/terminal/TerminalDockComp.java index c45b28e7a..f7e0145e4 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalDockComp.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalDockComp.java @@ -1,12 +1,14 @@ package io.xpipe.app.terminal; import io.xpipe.app.comp.SimpleComp; +import io.xpipe.app.comp.base.LoadingIconComp; import io.xpipe.app.core.AppFontSizes; import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.window.AppMainWindow; import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.util.PlatformThread; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableBooleanValue; import javafx.beans.value.ObservableValue; @@ -66,10 +68,8 @@ public class TerminalDockComp extends SimpleComp { stack.pseudoClassStateChanged(PseudoClass.getPseudoClass("empty"), true); label.textProperty().bind(AppI18n.observable("terminalStarting")); if (!AppPrefs.get().performanceMode().get()) { - var i = new RingProgressIndicator(-1.0, false); - i.setMaxWidth(10); - i.setMaxHeight(10); - label.setGraphic(i); + var l = new LoadingIconComp(new SimpleBooleanProperty(true), AppFontSizes::sm).createRegion(); + label.setGraphic(l); } } });