Rework icon loading

This commit is contained in:
crschnick 2024-09-27 10:39:18 +00:00
parent b093295c67
commit c7c7321aff
17 changed files with 83 additions and 77 deletions

View file

@ -84,7 +84,7 @@ public class BrowserNavBar extends Comp<BrowserNavBar.Structure> {
var graphic = Bindings.createStringBinding( var graphic = Bindings.createStringBinding(
() -> { () -> {
return model.getCurrentDirectory() != null return model.getCurrentDirectory() != null
? FileIconManager.getFileIcon(model.getCurrentDirectory(), false) ? FileIconManager.getFileIcon(model.getCurrentDirectory())
: null; : null;
}, },
model.getCurrentPath()); model.getCurrentPath());

View file

@ -65,11 +65,11 @@ public class BrowserEntry {
if (fileType != null) { if (fileType != null) {
return fileType.getIcon(); return fileType.getIcon();
} else if (directoryType != null) { } else if (directoryType != null) {
return directoryType.getIcon(rawFileEntry, false); return directoryType.getIcon(rawFileEntry);
} else { } else {
return rawFileEntry != null && rawFileEntry.resolved().getKind() == FileKind.DIRECTORY return rawFileEntry != null && rawFileEntry.resolved().getKind() == FileKind.DIRECTORY
? "default_folder.svg" ? "browser/default_folder.svg"
: "default_file.svg"; : "browser/default_file.svg";
} }
} }

View file

@ -143,7 +143,7 @@ public class BrowserQuickAccessContextMenu extends ContextMenu {
// Use original name, not the link target // Use original name, not the link target
browserEntry.getRawFileEntry().getName(), browserEntry.getRawFileEntry().getName(),
PrettyImageHelper.ofFixedSize( PrettyImageHelper.ofFixedSize(
FileIconManager.getFileIcon(browserEntry.getRawFileEntry(), false), 24, 24) FileIconManager.getFileIcon(browserEntry.getRawFileEntry()), 24, 24)
.createRegion()); .createRegion());
createMenu(); createMenu();
addInputListeners(); addInputListeners();

View file

@ -42,8 +42,8 @@ public abstract class BrowserIconDirectoryType {
} }
@Override @Override
public String getIcon(FileEntry entry, boolean open) { public String getIcon(FileEntry entry) {
return open ? "default_root_folder_opened.svg" : "default_root_folder.svg"; return "browser/default_root_folder.svg";
} }
}); });
@ -60,16 +60,12 @@ public abstract class BrowserIconDirectoryType {
}) })
.collect(Collectors.toSet()); .collect(Collectors.toSet());
var closedIcon = split[2].trim(); var closedIcon = "browser/" + split[2].trim();
var openIcon = split[3].trim(); var lightClosedIcon = split.length > 4 ? "browser/" + split[4].trim() : closedIcon;
var lightClosedIcon = split.length > 4 ? split[4].trim() : closedIcon;
var lightOpenIcon = split.length > 4 ? split[5].trim() : openIcon;
ALL.add(new Simple( ALL.add(new Simple(
id, id,
new IconVariant(lightClosedIcon, closedIcon), new IconVariant(lightClosedIcon, closedIcon),
new IconVariant(lightOpenIcon, openIcon),
filter)); filter));
} }
} }
@ -84,7 +80,7 @@ public abstract class BrowserIconDirectoryType {
public abstract boolean matches(FileEntry entry); public abstract boolean matches(FileEntry entry);
public abstract String getIcon(FileEntry entry, boolean open); public abstract String getIcon(FileEntry entry);
public static class Simple extends BrowserIconDirectoryType { public static class Simple extends BrowserIconDirectoryType {
@ -92,13 +88,11 @@ public abstract class BrowserIconDirectoryType {
private final String id; private final String id;
private final IconVariant closed; private final IconVariant closed;
private final IconVariant open;
private final Set<String> names; private final Set<String> names;
public Simple(String id, IconVariant closed, IconVariant open, Set<String> names) { public Simple(String id, IconVariant closed, Set<String> names) {
this.id = id; this.id = id;
this.closed = closed; this.closed = closed;
this.open = open;
this.names = names; this.names = names;
} }
@ -113,8 +107,8 @@ public abstract class BrowserIconDirectoryType {
} }
@Override @Override
public String getIcon(FileEntry entry, boolean open) { public String getIcon(FileEntry entry) {
return open ? this.open.getIcon() : this.closed.getIcon(); return this.closed.getIcon();
} }
} }
} }

View file

@ -47,8 +47,8 @@ public abstract class BrowserIconFileType {
return "." + r; return "." + r;
}) })
.collect(Collectors.toSet()); .collect(Collectors.toSet());
var darkIcon = split[2].trim(); var darkIcon = "browser/" + split[2].trim();
var lightIcon = split.length > 3 ? split[3].trim() : darkIcon; var lightIcon = (split.length > 3 ? "browser/" + split[3].trim() : darkIcon);
ALL.add(new BrowserIconFileType.Simple(id, lightIcon, darkIcon, filter)); ALL.add(new BrowserIconFileType.Simple(id, lightIcon, darkIcon, filter));
} }
} }

View file

@ -7,11 +7,11 @@ import io.xpipe.core.store.FileEntry;
public class BrowserIcons { public class BrowserIcons {
public static Comp<?> createDefaultFileIcon() { public static Comp<?> createDefaultFileIcon() {
return PrettyImageHelper.ofFixedSizeSquare("default_file.svg", 24); return PrettyImageHelper.ofFixedSizeSquare("browser/default_file.svg", 24);
} }
public static Comp<?> createDefaultDirectoryIcon() { public static Comp<?> createDefaultDirectoryIcon() {
return PrettyImageHelper.ofFixedSizeSquare("default_folder.svg", 24); return PrettyImageHelper.ofFixedSizeSquare("browser/default_folder.svg", 24);
} }
public static Comp<?> createIcon(BrowserIconFileType type) { public static Comp<?> createIcon(BrowserIconFileType type) {
@ -19,6 +19,6 @@ public class BrowserIcons {
} }
public static Comp<?> createIcon(FileEntry entry) { public static Comp<?> createIcon(FileEntry entry) {
return PrettyImageHelper.ofFixedSizeSquare(FileIconManager.getFileIcon(entry, false), 24); return PrettyImageHelper.ofFixedSizeSquare(FileIconManager.getFileIcon(entry), 24);
} }
} }

View file

@ -1,7 +1,5 @@
package io.xpipe.app.browser.icon; package io.xpipe.app.browser.icon;
import io.xpipe.app.resources.AppImages;
import io.xpipe.app.resources.AppResources;
import io.xpipe.core.store.FileEntry; import io.xpipe.core.store.FileEntry;
import io.xpipe.core.store.FileKind; import io.xpipe.core.store.FileKind;
@ -13,12 +11,11 @@ public class FileIconManager {
if (!loaded) { if (!loaded) {
BrowserIconFileType.loadDefinitions(); BrowserIconFileType.loadDefinitions();
BrowserIconDirectoryType.loadDefinitions(); BrowserIconDirectoryType.loadDefinitions();
AppImages.loadDirectory(AppResources.XPIPE_MODULE, "img/browser", true, false);
loaded = true; loaded = true;
} }
} }
public static synchronized String getFileIcon(FileEntry entry, boolean open) { public static synchronized String getFileIcon(FileEntry entry) {
if (entry == null) { if (entry == null) {
return null; return null;
} }
@ -33,13 +30,13 @@ public class FileIconManager {
} else { } else {
for (var f : BrowserIconDirectoryType.getAll()) { for (var f : BrowserIconDirectoryType.getAll()) {
if (f.matches(r)) { if (f.matches(r)) {
return f.getIcon(r, open); return f.getIcon(r);
} }
} }
} }
return r.getKind() == FileKind.DIRECTORY return "browser/" + (r.getKind() == FileKind.DIRECTORY
? (open ? "default_folder_opened.svg" : "default_folder.svg") ? "default_folder.svg"
: "default_file.svg"; : "default_file.svg");
} }
} }

View file

@ -9,15 +9,15 @@ import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory; import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import javafx.beans.property.*; import javafx.beans.property.*;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import lombok.Getter; import lombok.Getter;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
@Getter @Getter
public class StoreEntryWrapper { public class StoreEntryWrapper {
@ -40,7 +40,8 @@ public class StoreEntryWrapper {
private final Property<StoreCategoryWrapper> category = new SimpleObjectProperty<>(); private final Property<StoreCategoryWrapper> category = new SimpleObjectProperty<>();
private final Property<String> summary = new SimpleObjectProperty<>(); private final Property<String> summary = new SimpleObjectProperty<>();
private final Property<StoreNotes> notes; private final Property<StoreNotes> notes;
private final Property<String> icon = new SimpleObjectProperty<>(); private final Property<String> customIcon = new SimpleObjectProperty<>();
private final Property<String> iconFile = new SimpleObjectProperty<>();
public StoreEntryWrapper(DataStoreEntry entry) { public StoreEntryWrapper(DataStoreEntry entry) {
this.entry = entry; this.entry = entry;
@ -138,7 +139,8 @@ public class StoreEntryWrapper {
} }
color.setValue(entry.getColor()); color.setValue(entry.getColor());
notes.setValue(new StoreNotes(entry.getNotes(), entry.getNotes())); notes.setValue(new StoreNotes(entry.getNotes(), entry.getNotes()));
icon.setValue(entry.getIcon()); customIcon.setValue(entry.getIcon());
iconFile.setValue(getEffectiveIconFile());
busy.setValue(entry.getBusyCounter().get() != 0); busy.setValue(entry.getBusyCounter().get() != 0);
deletable.setValue(entry.getConfiguration().isDeletable() deletable.setValue(entry.getConfiguration().isDeletable()
@ -192,6 +194,20 @@ public class StoreEntryWrapper {
} }
} }
private String getEffectiveIconFile() {
if (disabledProperty().get()) {
return "disabled_icon.png";
}
if (getCustomIcon().getValue() == null) {
return getEntry()
.getProvider()
.getDisplayIconFileName(getEntry().getStore());
}
return "app:system/" + getCustomIcon().getValue() + ".svg";
}
private boolean showActionProvider(ActionProvider p) { private boolean showActionProvider(ActionProvider p) {
var leaf = p.getLeafDataStoreCallSite(); var leaf = p.getLeafDataStoreCallSite();
if (leaf != null) { if (leaf != null) {

View file

@ -137,7 +137,7 @@ public class StoreIconChoiceComp extends SimpleComp {
} }
root.setText(icon.getDisplayName()); root.setText(icon.getDisplayName());
image.set(icon.getIconName() + ".svg"); image.set("app:system/" + icon.getIconName() + ".svg");
setGraphic(root); setGraphic(root);
} }
} }

View file

@ -27,7 +27,6 @@ import java.util.List;
public class StoreIconChoiceDialogComp extends SimpleComp { public class StoreIconChoiceDialogComp extends SimpleComp {
public static void show(DataStoreEntry entry) { public static void show(DataStoreEntry entry) {
SystemIcons.load();
var window = AppWindowHelper.sideWindow( var window = AppWindowHelper.sideWindow(
AppI18n.get("chooseCustomIcon"), stage -> new StoreIconChoiceDialogComp(entry, stage), false, null); AppI18n.get("chooseCustomIcon"), stage -> new StoreIconChoiceDialogComp(entry, stage), false, null);
window.initModality(Modality.APPLICATION_MODAL); window.initModality(Modality.APPLICATION_MODAL);

View file

@ -3,15 +3,12 @@ package io.xpipe.app.comp.store;
import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.impl.PrettyImageHelper; import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
import io.xpipe.app.fxcomps.impl.TooltipAugment; import io.xpipe.app.fxcomps.impl.TooltipAugment;
import io.xpipe.app.resources.SystemIcons;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.input.MouseButton; import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.javafx.FontIcon;
@ -24,12 +21,7 @@ public class StoreIconComp extends SimpleComp {
@Override @Override
protected Region createSimple() { protected Region createSimple() {
var icon = Bindings.createStringBinding( var imageComp = PrettyImageHelper.ofFixedSize(wrapper.getIconFile(), w, h);
() -> {
return getImage();
},
wrapper.getIcon());
var imageComp = PrettyImageHelper.ofFixedSize(icon, w, h);
var storeIcon = imageComp.createRegion(); var storeIcon = imageComp.createRegion();
if (wrapper.getValidity().getValue().isUsable()) { if (wrapper.getValidity().getValue().isUsable()) {
new TooltipAugment<>(wrapper.getEntry().getProvider().displayName(), null).augment(storeIcon); new TooltipAugment<>(wrapper.getEntry().getProvider().displayName(), null).augment(storeIcon);
@ -67,19 +59,4 @@ public class StoreIconComp extends SimpleComp {
return stack; return stack;
} }
private String getImage() {
if (wrapper.disabledProperty().get()) {
return "disabled_icon.png";
}
if (wrapper.getIcon().getValue() == null) {
return wrapper.getEntry()
.getProvider()
.getDisplayIconFileName(wrapper.getEntry().getStore());
}
SystemIcons.load();
return "app:system/" + wrapper.getIcon().getValue() + ".svg";
}
} }

View file

@ -1,20 +1,20 @@
package io.xpipe.app.core.check; package io.xpipe.app.core.check;
import io.xpipe.app.comp.base.MarkdownComp; import io.xpipe.app.comp.base.MarkdownComp;
import io.xpipe.app.core.*; import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.core.AppState;
import io.xpipe.app.core.AppStyle;
import io.xpipe.app.core.mode.OperationMode; import io.xpipe.app.core.mode.OperationMode;
import io.xpipe.app.core.window.AppWindowHelper; import io.xpipe.app.core.window.AppWindowHelper;
import io.xpipe.app.resources.AppImages;
import io.xpipe.app.resources.AppResources; import io.xpipe.app.resources.AppResources;
import io.xpipe.app.util.PlatformState; import io.xpipe.app.util.PlatformState;
import io.xpipe.app.util.WindowsRegistry; import io.xpipe.app.util.WindowsRegistry;
import io.xpipe.core.process.OsType; import io.xpipe.core.process.OsType;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType; import javafx.scene.control.ButtonType;
import lombok.Getter; import lombok.Getter;
import java.nio.file.Files; import java.nio.file.Files;
@ -44,7 +44,6 @@ public class AppAvCheck {
PlatformState.initPlatformOrThrow(); PlatformState.initPlatformOrThrow();
AppStyle.init(); AppStyle.init();
AppImages.init();
var a = AppWindowHelper.showBlockingAlert(alert -> { var a = AppWindowHelper.showBlockingAlert(alert -> {
alert.setTitle(AppI18n.get("antivirusNoticeTitle")); alert.setTitle(AppI18n.get("antivirusNoticeTitle"));

View file

@ -4,6 +4,7 @@ import io.xpipe.app.browser.file.LocalFileSystem;
import io.xpipe.app.browser.icon.FileIconManager; import io.xpipe.app.browser.icon.FileIconManager;
import io.xpipe.app.core.App; import io.xpipe.app.core.App;
import io.xpipe.app.core.AppGreetings; import io.xpipe.app.core.AppGreetings;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.core.check.AppPtbCheck; import io.xpipe.app.core.check.AppPtbCheck;
import io.xpipe.app.core.window.AppMainWindow; import io.xpipe.app.core.window.AppMainWindow;
import io.xpipe.app.fxcomps.util.PlatformThread; import io.xpipe.app.fxcomps.util.PlatformThread;
@ -12,7 +13,6 @@ import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.update.UpdateChangelogAlert; import io.xpipe.app.update.UpdateChangelogAlert;
import io.xpipe.app.util.NativeBridge; import io.xpipe.app.util.NativeBridge;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import javafx.stage.Stage; import javafx.stage.Stage;
public class GuiMode extends PlatformMode { public class GuiMode extends PlatformMode {
@ -39,6 +39,7 @@ public class GuiMode extends PlatformMode {
AppGreetings.showIfNeeded(); AppGreetings.showIfNeeded();
AppPtbCheck.check(); AppPtbCheck.check();
NativeBridge.init(); NativeBridge.init();
AppLayoutModel.init();
TrackEvent.info("Waiting for window setup completion ..."); TrackEvent.info("Waiting for window setup completion ...");
PlatformThread.runLaterIfNeededBlocking(() -> { PlatformThread.runLaterIfNeededBlocking(() -> {

View file

@ -9,7 +9,6 @@ import io.xpipe.app.resources.AppImages;
import io.xpipe.app.update.UpdateAvailableAlert; import io.xpipe.app.update.UpdateAvailableAlert;
import io.xpipe.app.util.PlatformState; import io.xpipe.app.util.PlatformState;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import javafx.application.Application; import javafx.application.Application;
public abstract class PlatformMode extends OperationMode { public abstract class PlatformMode extends OperationMode {
@ -30,11 +29,13 @@ public abstract class PlatformMode extends OperationMode {
PlatformState.initPlatformOrThrow(); PlatformState.initPlatformOrThrow();
// Check if we can load system fonts or fail // Check if we can load system fonts or fail
AppFontLoadingCheck.check(); AppFontLoadingCheck.check();
// Can be loaded async
var imageThread = ThreadHelper.runFailableAsync(() -> {
AppImages.init();
});
AppFont.init(); AppFont.init();
AppTheme.init(); AppTheme.init();
AppStyle.init(); AppStyle.init();
AppImages.init();
AppLayoutModel.init();
TrackEvent.info("Finished essential component initialization before platform"); TrackEvent.info("Finished essential component initialization before platform");
TrackEvent.info("Launching application ..."); TrackEvent.info("Launching application ...");
@ -57,6 +58,7 @@ public abstract class PlatformMode extends OperationMode {
} }
StoreViewState.init(); StoreViewState.init();
imageThread.join();
} }
@Override @Override

View file

@ -216,7 +216,6 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
selected.getValue().getStore()); selected.getValue().getStore());
} }
SystemIcons.load();
return "app:system/" return "app:system/"
+ selected.getValue().get().getIcon() + ".svg"; + selected.getValue().get().getIcon() + ".svg";
}, },

View file

@ -3,10 +3,8 @@ package io.xpipe.app.resources;
import io.xpipe.app.core.AppExtensionManager; import io.xpipe.app.core.AppExtensionManager;
import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.issue.TrackEvent;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.WritableImage; import javafx.scene.image.WritableImage;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -16,6 +14,8 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor; import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -37,6 +37,7 @@ public class AppImages {
} }
public static void loadDirectory(String module, String dir, boolean loadImages, boolean loadSvgs) { public static void loadDirectory(String module, String dir, boolean loadImages, boolean loadSvgs) {
var start = Instant.now();
AppResources.with(module, dir, basePath -> { AppResources.with(module, dir, basePath -> {
if (!Files.exists(basePath)) { if (!Files.exists(basePath)) {
return; return;
@ -49,12 +50,17 @@ public class AppImages {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
var relativeFileName = FilenameUtils.separatorsToUnix( var relativeFileName = FilenameUtils.separatorsToUnix(
basePath.relativize(file).toString()); basePath.relativize(file).toString());
var key = defaultPrefix + relativeFileName;
if (images.containsKey(key) || svgImages.containsKey(key)) {
return FileVisitResult.CONTINUE;
}
try { try {
if (FilenameUtils.getExtension(file.toString()).equals("svg") && loadSvgs) { if (FilenameUtils.getExtension(file.toString()).equals("svg") && loadSvgs) {
var s = Files.readString(file); var s = Files.readString(file);
svgImages.put(defaultPrefix + relativeFileName, s); svgImages.put(key, s);
} else if (loadImages) { } else if (loadImages) {
images.put(defaultPrefix + relativeFileName, loadImage(file)); images.put(key, loadImage(file));
} }
} catch (IOException ex) { } catch (IOException ex) {
ErrorEvent.fromThrowable(ex).omitted(true).build().handle(); ErrorEvent.fromThrowable(ex).omitted(true).build().handle();
@ -63,6 +69,8 @@ public class AppImages {
} }
}); });
}); });
var elapsed = Duration.between(start, Instant.now());
TrackEvent.trace("Loaded images in " + module + ":" + dir + " in " + elapsed.toMillis() + " ms");
} }
public static String svgImage(String file) { public static String svgImage(String file) {

View file

@ -172,6 +172,20 @@ public class DataStoreEntry extends StorageElement {
return entry; return entry;
} }
public String getEffectiveIconFile() {
if (getValidity() == Validity.LOAD_FAILED) {
return "disabled_icon.png";
}
if (icon == null) {
return getProvider()
.getDisplayIconFileName(getStore());
}
return "app:system/" + icon + ".svg";
}
void refreshIcon() { void refreshIcon() {
if (icon != null && SystemIcons.getForId(icon).isEmpty()) { if (icon != null && SystemIcons.getForId(icon).isEmpty()) {
icon = null; icon = null;