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(
() -> {
return model.getCurrentDirectory() != null
? FileIconManager.getFileIcon(model.getCurrentDirectory(), false)
? FileIconManager.getFileIcon(model.getCurrentDirectory())
: null;
},
model.getCurrentPath());

View file

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

View file

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

View file

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

View file

@ -47,8 +47,8 @@ public abstract class BrowserIconFileType {
return "." + r;
})
.collect(Collectors.toSet());
var darkIcon = split[2].trim();
var lightIcon = split.length > 3 ? split[3].trim() : darkIcon;
var darkIcon = "browser/" + split[2].trim();
var lightIcon = (split.length > 3 ? "browser/" + split[3].trim() : darkIcon);
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 static Comp<?> createDefaultFileIcon() {
return PrettyImageHelper.ofFixedSizeSquare("default_file.svg", 24);
return PrettyImageHelper.ofFixedSizeSquare("browser/default_file.svg", 24);
}
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) {
@ -19,6 +19,6 @@ public class BrowserIcons {
}
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;
import io.xpipe.app.resources.AppImages;
import io.xpipe.app.resources.AppResources;
import io.xpipe.core.store.FileEntry;
import io.xpipe.core.store.FileKind;
@ -13,12 +11,11 @@ public class FileIconManager {
if (!loaded) {
BrowserIconFileType.loadDefinitions();
BrowserIconDirectoryType.loadDefinitions();
AppImages.loadDirectory(AppResources.XPIPE_MODULE, "img/browser", true, false);
loaded = true;
}
}
public static synchronized String getFileIcon(FileEntry entry, boolean open) {
public static synchronized String getFileIcon(FileEntry entry) {
if (entry == null) {
return null;
}
@ -33,13 +30,13 @@ public class FileIconManager {
} else {
for (var f : BrowserIconDirectoryType.getAll()) {
if (f.matches(r)) {
return f.getIcon(r, open);
return f.getIcon(r);
}
}
}
return r.getKind() == FileKind.DIRECTORY
? (open ? "default_folder_opened.svg" : "default_folder.svg")
: "default_file.svg";
return "browser/" + (r.getKind() == FileKind.DIRECTORY
? "default_folder.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.DataStoreEntry;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import lombok.Getter;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
@Getter
public class StoreEntryWrapper {
@ -40,7 +40,8 @@ public class StoreEntryWrapper {
private final Property<StoreCategoryWrapper> category = new SimpleObjectProperty<>();
private final Property<String> summary = new SimpleObjectProperty<>();
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) {
this.entry = entry;
@ -138,7 +139,8 @@ public class StoreEntryWrapper {
}
color.setValue(entry.getColor());
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);
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) {
var leaf = p.getLeafDataStoreCallSite();
if (leaf != null) {

View file

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

View file

@ -27,7 +27,6 @@ import java.util.List;
public class StoreIconChoiceDialogComp extends SimpleComp {
public static void show(DataStoreEntry entry) {
SystemIcons.load();
var window = AppWindowHelper.sideWindow(
AppI18n.get("chooseCustomIcon"), stage -> new StoreIconChoiceDialogComp(entry, stage), false, null);
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.impl.PrettyImageHelper;
import io.xpipe.app.fxcomps.impl.TooltipAugment;
import io.xpipe.app.resources.SystemIcons;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import lombok.AllArgsConstructor;
import org.kordamp.ikonli.javafx.FontIcon;
@ -24,12 +21,7 @@ public class StoreIconComp extends SimpleComp {
@Override
protected Region createSimple() {
var icon = Bindings.createStringBinding(
() -> {
return getImage();
},
wrapper.getIcon());
var imageComp = PrettyImageHelper.ofFixedSize(icon, w, h);
var imageComp = PrettyImageHelper.ofFixedSize(wrapper.getIconFile(), w, h);
var storeIcon = imageComp.createRegion();
if (wrapper.getValidity().getValue().isUsable()) {
new TooltipAugment<>(wrapper.getEntry().getProvider().displayName(), null).augment(storeIcon);
@ -67,19 +59,4 @@ public class StoreIconComp extends SimpleComp {
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;
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.window.AppWindowHelper;
import io.xpipe.app.resources.AppImages;
import io.xpipe.app.resources.AppResources;
import io.xpipe.app.util.PlatformState;
import io.xpipe.app.util.WindowsRegistry;
import io.xpipe.core.process.OsType;
import javafx.geometry.Insets;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import lombok.Getter;
import java.nio.file.Files;
@ -44,7 +44,6 @@ public class AppAvCheck {
PlatformState.initPlatformOrThrow();
AppStyle.init();
AppImages.init();
var a = AppWindowHelper.showBlockingAlert(alert -> {
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.core.App;
import io.xpipe.app.core.AppGreetings;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.core.check.AppPtbCheck;
import io.xpipe.app.core.window.AppMainWindow;
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.util.NativeBridge;
import io.xpipe.app.util.ThreadHelper;
import javafx.stage.Stage;
public class GuiMode extends PlatformMode {
@ -39,6 +39,7 @@ public class GuiMode extends PlatformMode {
AppGreetings.showIfNeeded();
AppPtbCheck.check();
NativeBridge.init();
AppLayoutModel.init();
TrackEvent.info("Waiting for window setup completion ...");
PlatformThread.runLaterIfNeededBlocking(() -> {

View file

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

View file

@ -216,7 +216,6 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
selected.getValue().getStore());
}
SystemIcons.load();
return "app:system/"
+ 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.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import org.apache.commons.io.FilenameUtils;
import java.awt.image.BufferedImage;
@ -16,6 +14,8 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
@ -37,6 +37,7 @@ public class AppImages {
}
public static void loadDirectory(String module, String dir, boolean loadImages, boolean loadSvgs) {
var start = Instant.now();
AppResources.with(module, dir, basePath -> {
if (!Files.exists(basePath)) {
return;
@ -49,12 +50,17 @@ public class AppImages {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
var relativeFileName = FilenameUtils.separatorsToUnix(
basePath.relativize(file).toString());
var key = defaultPrefix + relativeFileName;
if (images.containsKey(key) || svgImages.containsKey(key)) {
return FileVisitResult.CONTINUE;
}
try {
if (FilenameUtils.getExtension(file.toString()).equals("svg") && loadSvgs) {
var s = Files.readString(file);
svgImages.put(defaultPrefix + relativeFileName, s);
svgImages.put(key, s);
} else if (loadImages) {
images.put(defaultPrefix + relativeFileName, loadImage(file));
images.put(key, loadImage(file));
}
} catch (IOException ex) {
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) {

View file

@ -172,6 +172,20 @@ public class DataStoreEntry extends StorageElement {
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() {
if (icon != null && SystemIcons.getForId(icon).isEmpty()) {
icon = null;