Add file browser icons

This commit is contained in:
crschnick
2023-04-01 21:10:17 +00:00
parent a516541c8d
commit 94a6b8d5cc
1217 changed files with 3128 additions and 70 deletions

View File

@@ -4,6 +4,7 @@ package io.xpipe.app.browser;
import atlantafx.base.theme.Styles;
import atlantafx.base.theme.Tweaks;
import io.xpipe.app.browser.icon.FileIconManager;
import io.xpipe.app.comp.base.LazyTextFieldComp;
import io.xpipe.app.fxcomps.impl.PrettyImageComp;
import io.xpipe.app.fxcomps.util.PlatformThread;
@@ -295,20 +296,17 @@ final class FileListComp extends AnchorPane {
HBox.setHgrow(textField, Priority.ALWAYS);
setGraphic(box);
if (!isDirectory) {
img.set("file_drag_icon.png");
} else {
img.set("folder_closed.svg");
}
var isParentLink = getTableRow()
.getItem()
.equals(fileList.getFileSystemModel().getCurrentParentDirectory());
img.set(FileIconManager.getFileIcon(isParentLink ? fileList.getFileSystemModel().getCurrentDirectory() : getTableRow().getItem(), isParentLink));
pseudoClassStateChanged(FOLDER, isDirectory);
var fileName = getTableRow()
.getItem()
.equals(fileList.getFileSystemModel().getCurrentParentDirectory())
var fileName = isParentLink
? ".."
: FileNames.getFileName(fullPath);
var hidden = getTableRow().getItem().isHidden() || fileName.startsWith(".");
var hidden = !isParentLink && (getTableRow().getItem().isHidden() || fileName.startsWith("."));
getTableRow().pseudoClassStateChanged(HIDDEN, hidden);
text.set(fileName);

View File

@@ -1,5 +1,6 @@
package io.xpipe.app.browser;
import io.xpipe.app.browser.icon.FileIcons;
import io.xpipe.app.comp.base.ListBoxViewComp;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.impl.LabelComp;

View File

@@ -0,0 +1,29 @@
package io.xpipe.app.browser.icon;
import io.xpipe.core.store.FileSystem;
import java.util.Arrays;
public interface FileIconFactory {
class SimpleFile extends IconVariant implements FileIconFactory {
private final String[] endings;
public SimpleFile(String lightIcon, String darkIcon, String... endings) {
super(lightIcon, darkIcon);
this.endings = endings;
}
@Override
public String getIcon(FileSystem.FileEntry entry) {
if (entry.isDirectory()) {
return null;
}
return Arrays.stream(endings).anyMatch(ending -> entry.getPath().endsWith(ending)) ? getIcon() : null;
}
}
String getIcon(FileSystem.FileEntry entry);
}

View File

@@ -0,0 +1,131 @@
package io.xpipe.app.browser.icon;
import io.xpipe.app.core.AppImages;
import io.xpipe.app.core.AppResources;
import io.xpipe.core.store.FileSystem;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class FileIconManager {
private static final List<FileIconFactory> factories = new ArrayList<>();
private static final List<FolderIconFactory> folderFactories = new ArrayList<>();
private static boolean loaded;
static {
AppResources.with(AppResources.XPIPE_MODULE, "browser_icons/file_list.txt", path -> {
try (var reader =
new BufferedReader(new InputStreamReader(Files.newInputStream(path), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
var split = line.split("\\|");
var id = split[0].trim();
var filter = Arrays.stream(split[1].split(","))
.map(s -> {
var r = s.trim();
if (r.startsWith(".")) {
return r;
}
if (r.contains(".")) {
return r;
}
return "." + r;
})
.toList();
var darkIcon = split[2].trim();
var lightIcon = split.length > 3 ? split[3].trim() : darkIcon;
factories.add(new FileIconFactory.SimpleFile(lightIcon, darkIcon, filter.toArray(String[]::new)));
}
}
});
folderFactories.addAll(List.of(new FolderIconFactory.SimpleDirectory(
new IconVariant("default_root_folder.svg"), new IconVariant("default_root_folder_opened.svg"), "")));
AppResources.with(AppResources.XPIPE_MODULE, "browser_icons/folder_list.txt", path -> {
try (var reader =
new BufferedReader(new InputStreamReader(Files.newInputStream(path), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
var split = line.split("\\|");
var id = split[0].trim();
var filter = Arrays.stream(split[1].split(","))
.map(s -> {
var r = s.trim();
if (r.startsWith(".")) {
return r;
}
if (r.contains(".")) {
return r;
}
return "." + r;
})
.toList();
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;
folderFactories.add(new FolderIconFactory.SimpleDirectory(
new IconVariant(lightClosedIcon, closedIcon),
new IconVariant(lightOpenIcon, openIcon),
filter.toArray(String[]::new)));
}
}
});
}
private static void loadIfNecessary() {
if (!loaded) {
AppImages.loadDirectory(AppResources.XPIPE_MODULE, "browser_icons");
loaded = true;
}
}
public static String getFileIcon(FileSystem.FileEntry entry, boolean open) {
if (entry == null) {
return null;
}
loadIfNecessary();
if (!entry.isDirectory()) {
for (var f : factories) {
var icon = f.getIcon(entry);
if (icon != null) {
return getIconPath(icon);
}
}
} else {
for (var f : folderFactories) {
var icon = f.getIcon(entry, open);
if (icon != null) {
return getIconPath(icon);
}
}
}
return entry.isDirectory() ? (open ? "default_folder_opened.svg" : "default_folder.svg") : "default_file.svg";
}
public static String getParentLinkIcon() {
loadIfNecessary();
return "default_folder_opened.svg";
}
private static String getIconPath(String name) {
return name;
}
}

View File

@@ -1,4 +1,4 @@
package io.xpipe.app.browser;
package io.xpipe.app.browser.icon;
import io.xpipe.app.fxcomps.impl.PrettyImageComp;
import io.xpipe.core.store.FileSystem;
@@ -11,10 +11,6 @@ public class FileIcons {
}
public static String getIcon(FileSystem.FileEntry entry) {
if (!entry.isDirectory()) {
return "app:file_drag_icon.png";
} else {
return "app:folder_closed.svg";
}
return FileIconManager.getFileIcon(entry, false);
}
}

View File

@@ -0,0 +1,36 @@
package io.xpipe.app.browser.icon;
import io.xpipe.core.impl.FileNames;
import io.xpipe.core.store.FileSystem;
import java.util.Arrays;
public interface FolderIconFactory {
class SimpleDirectory implements FolderIconFactory {
private final IconVariant closed;
private final IconVariant open;
private final String[] names;
public SimpleDirectory(IconVariant closed, IconVariant open, String... names) {
this.closed = closed;
this.open = open;
this.names = names;
}
@Override
public String getIcon(FileSystem.FileEntry entry, boolean open) {
if (!entry.isDirectory()) {
return null;
}
return Arrays.stream(names).anyMatch(name -> FileNames.getFileName(entry.getPath())
.equalsIgnoreCase(name))
? (open ? this.open.getIcon() : this.closed.getIcon())
: null;
}
}
String getIcon(FileSystem.FileEntry entry, boolean open);
}

View File

@@ -0,0 +1,27 @@
package io.xpipe.app.browser.icon;
import io.xpipe.app.prefs.AppPrefs;
public class IconVariant {
private final String lightIcon;
private final String darkIcon;
public IconVariant(String icon) {
this(icon, icon);
}
public IconVariant(String lightIcon, String darkIcon) {
this.lightIcon = lightIcon;
this.darkIcon = darkIcon;
}
protected final String getIcon() {
var t = AppPrefs.get() != null ? AppPrefs.get().theme.getValue() : null;
if (t == null) {
return lightIcon;
}
return t.getTheme().isDarkMode() ? darkIcon : lightIcon;
}
}

View File

@@ -25,38 +25,39 @@ public class AppImages {
public static void init() {
TrackEvent.info("Loading images ...");
for (var module : AppExtensionManager.getInstance().getContentModules()) {
AppResources.with(module.getName(), "img", basePath -> {
if (!Files.exists(basePath)) {
return;
}
var simpleName = FilenameUtils.getExtension(module.getName());
String defaultPrefix = simpleName + ":";
Files.walkFileTree(basePath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
var relativeFileName = FilenameUtils.separatorsToUnix(basePath.relativize(file).toString());
try {
if (FilenameUtils.getExtension(file.toString()).equals("svg")) {
var s = Files.readString(file);
svgImages.put(
defaultPrefix + relativeFileName,
s);
} else {
images.put(
defaultPrefix + relativeFileName,
loadImage(file));
}
} catch (IOException ex) {
ErrorEvent.fromThrowable(ex).omitted(true).build().handle();
}
return FileVisitResult.CONTINUE;
}
});
});
loadDirectory(module.getName(), "img");
}
}
public static void loadDirectory(String module, String dir) {
AppResources.with(module, dir, basePath -> {
if (!Files.exists(basePath)) {
return;
}
var simpleName = FilenameUtils.getExtension(module);
String defaultPrefix = simpleName + ":";
Files.walkFileTree(basePath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
var relativeFileName = FilenameUtils.separatorsToUnix(
basePath.relativize(file).toString());
try {
if (FilenameUtils.getExtension(file.toString()).equals("svg")) {
var s = Files.readString(file);
svgImages.put(defaultPrefix + relativeFileName, s);
} else {
images.put(defaultPrefix + relativeFileName, loadImage(file));
}
} catch (IOException ex) {
ErrorEvent.fromThrowable(ex).omitted(true).build().handle();
}
return FileVisitResult.CONTINUE;
}
});
});
}
public static String svgImage(String file) {
if (file == null) {
return "";

View File

@@ -9,6 +9,7 @@ import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.css.Size;
import javafx.css.SizeUnits;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
@@ -55,24 +56,38 @@ public class SvgComp {
return;
}
var regularExpression = Pattern.compile("<svg.+?width=\"([^\s]+)\"", Pattern.DOTALL);
var matcher = regularExpression.matcher(val);
if (!matcher.find()) {
var dim = getDimensions(val);
if (dim == null) {
return;
}
var width = matcher.group(1);
regularExpression = Pattern.compile("<svg.+?height=\"([^\s]+)\"", Pattern.DOTALL);
matcher = regularExpression.matcher(val);
matcher.find();
var height = matcher.group(1);
var widthInteger = parseSize(width).pixels();
var heightInteger = parseSize(height).pixels();
widthProperty.set((int) Math.ceil(widthInteger));
heightProperty.set((int) Math.ceil(heightInteger));
widthProperty.set((int) Math.ceil(dim.getX()));
heightProperty.set((int) Math.ceil(dim.getY()));
});
return new SvgComp(widthProperty, heightProperty, content);
}
private static Point2D getDimensions(String val) {
var regularExpression = Pattern.compile("<svg[^>]+?width=\"([^\s]+)\"", Pattern.DOTALL);
var matcher = regularExpression.matcher(val);
if (!matcher.find()) {
var viewBox = Pattern.compile(
"<svg.+?viewBox=\"([\\d.]+)\\s+([\\d.]+)\\s+([\\d.]+)\\s+([\\d.]+)\"", Pattern.DOTALL);
matcher = viewBox.matcher(val);
if (matcher.find()) {
return new Point2D(parseSize(matcher.group(3)).pixels(), parseSize(matcher.group(4)).pixels());
}
}
var width = matcher.group(1);
regularExpression = Pattern.compile("<svg.+?height=\"([^\s]+)\"", Pattern.DOTALL);
matcher = regularExpression.matcher(val);
matcher.find();
var height = matcher.group(1);
return new Point2D(parseSize(width).pixels(), parseSize(height).pixels());
}
private String getHtml(String content) {
return "<html><body style='margin: 0; padding: 0; border: none;' >" + content + "</body></html>";
}

View File

@@ -80,7 +80,10 @@ public class AppUpdater {
lastUpdateCheckResult.addListener((c, o, n) -> {
downloadedUpdate.setValue(null);
});
refreshUpdateCheckSilent();
if (XPipeDistributionType.get().checkForUpdateOnStartup()) {
refreshUpdateCheckSilent();
}
}
private static void event(String msg) {

View File

@@ -1,6 +1,5 @@
package io.xpipe.app.util;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.core.util.ModuleHelper;
import io.xpipe.core.util.XPipeInstallation;
@@ -9,13 +8,13 @@ public interface XPipeDistributionType {
XPipeDistributionType DEVELOPMENT = new XPipeDistributionType() {
@Override
public boolean supportsUpdate() {
return true;
public boolean checkForUpdateOnStartup() {
return false;
}
@Override
public void performUpdateAction() {
TrackEvent.info("Development mode update executed");
public boolean supportsUpdate() {
return true;
}
@Override
@@ -26,12 +25,14 @@ public interface XPipeDistributionType {
XPipeDistributionType PORTABLE = new XPipeDistributionType() {
@Override
public boolean supportsUpdate() {
public boolean checkForUpdateOnStartup() {
return false;
}
@Override
public void performUpdateAction() {}
public boolean supportsUpdate() {
return false;
}
@Override
public String getName() {
@@ -41,13 +42,13 @@ public interface XPipeDistributionType {
XPipeDistributionType INSTALLATION = new XPipeDistributionType() {
@Override
public boolean supportsUpdate() {
public boolean checkForUpdateOnStartup() {
return true;
}
@Override
public void performUpdateAction() {
TrackEvent.info("Update action called");
public boolean supportsUpdate() {
return true;
}
@Override
@@ -68,9 +69,9 @@ public interface XPipeDistributionType {
}
}
boolean supportsUpdate();
boolean checkForUpdateOnStartup();
void performUpdateAction();
boolean supportsUpdate();
String getName();
}

View File

@@ -34,6 +34,7 @@ open module io.xpipe.app {
exports io.xpipe.app.fxcomps.augment;
exports io.xpipe.app.test;
exports io.xpipe.app.browser;
exports io.xpipe.app.browser.icon;
requires com.sun.jna;
requires com.sun.jna.platform;