mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-24 16:40:25 +00:00
Reformat [stage]
This commit is contained in:
parent
5ce9538633
commit
8f49c35aca
201 changed files with 1335 additions and 820 deletions
|
@ -1,7 +1,5 @@
|
|||
package io.xpipe.app.beacon;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import io.xpipe.app.core.AppResources;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
|
@ -10,6 +8,9 @@ import io.xpipe.app.util.MarkdownHelper;
|
|||
import io.xpipe.beacon.BeaconConfig;
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import io.xpipe.core.util.XPipeInstallation;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -22,16 +23,22 @@ import java.util.concurrent.Executors;
|
|||
public class AppBeaconServer {
|
||||
|
||||
private static AppBeaconServer INSTANCE;
|
||||
|
||||
@Getter
|
||||
private final int port;
|
||||
|
||||
@Getter
|
||||
private final boolean propertyPort;
|
||||
|
||||
private boolean running;
|
||||
private HttpServer server;
|
||||
|
||||
@Getter
|
||||
private final Set<BeaconSession> sessions = new HashSet<>();
|
||||
|
||||
@Getter
|
||||
private final Set<BeaconShellSession> shellSessions = new HashSet<>();
|
||||
|
||||
@Getter
|
||||
private String localAuthSecret;
|
||||
|
||||
|
@ -122,8 +129,7 @@ public class AppBeaconServer {
|
|||
"openapi.yaml", "misc/openapi.yaml",
|
||||
"markdown.css", "misc/github-markdown-dark.css",
|
||||
"highlight.min.js", "misc/highlight.min.js",
|
||||
"github-dark.min.css", "misc/github-dark.min.css"
|
||||
);
|
||||
"github-dark.min.css", "misc/github-dark.min.css");
|
||||
resourceMap.forEach((s, s2) -> {
|
||||
server.createContext("/" + s, exchange -> {
|
||||
handleResource(exchange, s2);
|
||||
|
@ -145,7 +151,7 @@ public class AppBeaconServer {
|
|||
});
|
||||
}
|
||||
var body = resources.get(resource).getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(200,body.length);
|
||||
exchange.sendResponseHeaders(200, body.length);
|
||||
try (var out = exchange.getResponseBody()) {
|
||||
out.write(body);
|
||||
}
|
||||
|
@ -154,19 +160,21 @@ public class AppBeaconServer {
|
|||
private void handleCatchAll(HttpExchange exchange) throws IOException {
|
||||
if (notFoundHtml == null) {
|
||||
AppResources.with(AppResources.XPIPE_MODULE, "misc/api.md", file -> {
|
||||
notFoundHtml = MarkdownHelper.toHtml(Files.readString(file), head -> {
|
||||
return head + "\n" +
|
||||
"<link rel=\"stylesheet\" href=\"markdown.css\">" + "\n" +
|
||||
"<link rel=\"stylesheet\" href=\"github-dark.min.css\">" + "\n" +
|
||||
"<script src=\"highlight.min.js\"></script>" + "\n" +
|
||||
"<script>hljs.highlightAll();</script>";
|
||||
}, s -> {
|
||||
return "<div style=\"max-width: 800px;margin: auto;\">" + s + "</div>";
|
||||
});
|
||||
notFoundHtml = MarkdownHelper.toHtml(
|
||||
Files.readString(file),
|
||||
head -> {
|
||||
return head + "\n" + "<link rel=\"stylesheet\" href=\"markdown.css\">"
|
||||
+ "\n" + "<link rel=\"stylesheet\" href=\"github-dark.min.css\">"
|
||||
+ "\n" + "<script src=\"highlight.min.js\"></script>"
|
||||
+ "\n" + "<script>hljs.highlightAll();</script>";
|
||||
},
|
||||
s -> {
|
||||
return "<div style=\"max-width: 800px;margin: auto;\">" + s + "</div>";
|
||||
});
|
||||
});
|
||||
}
|
||||
var body = notFoundHtml.getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(200,body.length);
|
||||
exchange.sendResponseHeaders(200, body.length);
|
||||
try (var out = exchange.getResponseBody()) {
|
||||
out.write(body);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package io.xpipe.app.beacon;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.beacon.*;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -18,10 +19,12 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
|||
|
||||
private final BeaconInterface<T> beaconInterface;
|
||||
|
||||
public BeaconRequestHandler(BeaconInterface<T> beaconInterface) {this.beaconInterface = beaconInterface;}
|
||||
public BeaconRequestHandler(BeaconInterface<T> beaconInterface) {
|
||||
this.beaconInterface = beaconInterface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
public void handle(HttpExchange exchange) {
|
||||
if (!AppPrefs.get().disableApiAuthentication().get() && beaconInterface.requiresAuthentication()) {
|
||||
var auth = exchange.getRequestHeaders().getFirst("Authorization");
|
||||
if (auth == null) {
|
||||
|
@ -30,7 +33,10 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
|||
}
|
||||
|
||||
var token = auth.replace("Bearer ", "");
|
||||
var session = AppBeaconServer.get().getSessions().stream().filter(s -> s.getToken().equals(token)).findFirst().orElse(null);
|
||||
var session = AppBeaconServer.get().getSessions().stream()
|
||||
.filter(s -> s.getToken().equals(token))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (session == null) {
|
||||
writeError(exchange, new BeaconClientErrorResponse("Unknown token"), 403);
|
||||
return;
|
||||
|
@ -47,8 +53,11 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
|||
try (InputStream is = exchange.getRequestBody()) {
|
||||
var tree = JacksonMapper.getDefault().readTree(is);
|
||||
TrackEvent.trace("Parsed raw request:\n" + tree.toPrettyString());
|
||||
var emptyRequestClass = tree.isEmpty() && beaconInterface.getRequestClass().getDeclaredFields().length == 0;
|
||||
object = emptyRequestClass ? createDefaultRequest(beaconInterface) : JacksonMapper.getDefault().treeToValue(tree, beaconInterface.getRequestClass());
|
||||
var emptyRequestClass =
|
||||
tree.isEmpty() && beaconInterface.getRequestClass().getDeclaredFields().length == 0;
|
||||
object = emptyRequestClass
|
||||
? createDefaultRequest(beaconInterface)
|
||||
: JacksonMapper.getDefault().treeToValue(tree, beaconInterface.getRequestClass());
|
||||
TrackEvent.trace("Parsed request object:\n" + object);
|
||||
}
|
||||
response = beaconInterface.handle(exchange, object);
|
||||
|
@ -62,7 +71,8 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
|||
writeError(exchange, new BeaconServerErrorResponse(cause), 500);
|
||||
return;
|
||||
} catch (IOException ex) {
|
||||
// Handle serialization errors as normal exceptions and other IO exceptions as assuming that the connection is broken
|
||||
// Handle serialization errors as normal exceptions and other IO exceptions as assuming that the connection
|
||||
// is broken
|
||||
if (!ex.getClass().getName().contains("jackson")) {
|
||||
ErrorEvent.fromThrowable(ex).omit().expected().handle();
|
||||
} else {
|
||||
|
@ -76,32 +86,32 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var emptyResponseClass = beaconInterface.getResponseClass().getDeclaredFields().length == 0;
|
||||
if (!emptyResponseClass && response != null) {
|
||||
TrackEvent.trace("Sending response:\n" + object);
|
||||
var tree = JacksonMapper.getDefault().valueToTree(response);
|
||||
TrackEvent.trace("Sending raw response:\n" + tree.toPrettyString());
|
||||
var bytes = tree.toPrettyString().getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(200, bytes.length);
|
||||
try (OutputStream os = exchange.getResponseBody()) {
|
||||
os.write(bytes);
|
||||
}
|
||||
} else {
|
||||
exchange.sendResponseHeaders(200, -1);
|
||||
try {
|
||||
var emptyResponseClass = beaconInterface.getResponseClass().getDeclaredFields().length == 0;
|
||||
if (!emptyResponseClass && response != null) {
|
||||
TrackEvent.trace("Sending response:\n" + object);
|
||||
var tree = JacksonMapper.getDefault().valueToTree(response);
|
||||
TrackEvent.trace("Sending raw response:\n" + tree.toPrettyString());
|
||||
var bytes = tree.toPrettyString().getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(200, bytes.length);
|
||||
try (OutputStream os = exchange.getResponseBody()) {
|
||||
os.write(bytes);
|
||||
}
|
||||
} catch (IOException ioException) {
|
||||
ErrorEvent.fromThrowable(ioException).omit().expected().handle();
|
||||
} catch (Throwable other) {
|
||||
ErrorEvent.fromThrowable(other).handle();
|
||||
writeError(exchange, new BeaconServerErrorResponse(other), 500);
|
||||
return;
|
||||
} else {
|
||||
exchange.sendResponseHeaders(200, -1);
|
||||
}
|
||||
} catch (IOException ioException) {
|
||||
ErrorEvent.fromThrowable(ioException).omit().expected().handle();
|
||||
} catch (Throwable other) {
|
||||
ErrorEvent.fromThrowable(other).handle();
|
||||
writeError(exchange, new BeaconServerErrorResponse(other), 500);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeError(HttpExchange exchange, Object errorMessage, int code) {
|
||||
try {
|
||||
var bytes = JacksonMapper.getDefault().writeValueAsString(errorMessage).getBytes(StandardCharsets.UTF_8);
|
||||
var bytes =
|
||||
JacksonMapper.getDefault().writeValueAsString(errorMessage).getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(code, bytes.length);
|
||||
try (OutputStream os = exchange.getResponseBody()) {
|
||||
os.write(bytes);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.xpipe.app.beacon;
|
||||
|
||||
import io.xpipe.beacon.BeaconClientInformation;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.xpipe.app.beacon;
|
|||
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.util.AskpassAlert;
|
||||
import io.xpipe.app.util.SecretManager;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.AskpassExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AskpassExchangeImpl extends AskpassExchange {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
public Object handle(HttpExchange exchange, Request msg) {
|
||||
if (msg.getRequest() == null) {
|
||||
var r = AskpassAlert.queryRaw(msg.getPrompt(), null);
|
||||
return Response.builder().value(r.getSecret()).build();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
|
@ -8,6 +7,8 @@ import io.xpipe.beacon.BeaconServerException;
|
|||
import io.xpipe.beacon.api.ConnectionQueryExchange;
|
||||
import io.xpipe.core.store.StorePath;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -16,7 +17,7 @@ import java.util.regex.Pattern;
|
|||
public class ConnectionQueryExchangeImpl extends ConnectionQueryExchange {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
public Object handle(HttpExchange exchange, Request msg) {
|
||||
var catMatcher = Pattern.compile(toRegex("all connections/" + msg.getCategoryFilter()));
|
||||
var conMatcher = Pattern.compile(toRegex(msg.getConnectionFilter()));
|
||||
|
||||
|
@ -31,7 +32,9 @@ public class ConnectionQueryExchangeImpl extends ConnectionQueryExchange {
|
|||
continue;
|
||||
}
|
||||
|
||||
var cat = DataStorage.get().getStoreCategoryIfPresent(storeEntry.getCategoryUuid()).orElse(null);
|
||||
var cat = DataStorage.get()
|
||||
.getStoreCategoryIfPresent(storeEntry.getCategoryUuid())
|
||||
.orElse(null);
|
||||
if (cat == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -46,11 +49,18 @@ public class ConnectionQueryExchangeImpl extends ConnectionQueryExchange {
|
|||
|
||||
var mapped = new ArrayList<QueryResponse>();
|
||||
for (DataStoreEntry e : found) {
|
||||
var names = DataStorage.get().getStorePath(DataStorage.get().getStoreCategoryIfPresent(e.getCategoryUuid()).orElseThrow()).getNames();
|
||||
var names = DataStorage.get()
|
||||
.getStorePath(DataStorage.get()
|
||||
.getStoreCategoryIfPresent(e.getCategoryUuid())
|
||||
.orElseThrow())
|
||||
.getNames();
|
||||
var cat = new StorePath(names.subList(1, names.size()));
|
||||
var obj = ConnectionQueryExchange.QueryResponse.builder()
|
||||
.uuid(e.getUuid()).category(cat).connection(DataStorage.get()
|
||||
.getStorePath(e)).type(e.getProvider().getId()).build();
|
||||
.uuid(e.getUuid())
|
||||
.category(cat)
|
||||
.connection(DataStorage.get().getStorePath(e))
|
||||
.type(e.getProvider().getId())
|
||||
.build();
|
||||
mapped.add(obj);
|
||||
}
|
||||
return Response.builder().found(mapped).build();
|
||||
|
@ -86,20 +96,16 @@ public class ConnectionQueryExchangeImpl extends ConnectionQueryExchange {
|
|||
}
|
||||
break;
|
||||
case '*':
|
||||
if (inClass == 0)
|
||||
sb.append(".*");
|
||||
else
|
||||
sb.append('*');
|
||||
if (inClass == 0) sb.append(".*");
|
||||
else sb.append('*');
|
||||
break;
|
||||
case '?':
|
||||
if (inClass == 0)
|
||||
sb.append('.');
|
||||
else
|
||||
sb.append('?');
|
||||
if (inClass == 0) sb.append('.');
|
||||
else sb.append('?');
|
||||
break;
|
||||
case '[':
|
||||
inClass++;
|
||||
firstIndexInClass = i+1;
|
||||
firstIndexInClass = i + 1;
|
||||
sb.append('[');
|
||||
break;
|
||||
case ']':
|
||||
|
@ -115,15 +121,12 @@ public class ConnectionQueryExchangeImpl extends ConnectionQueryExchange {
|
|||
case '$':
|
||||
case '@':
|
||||
case '%':
|
||||
if (inClass == 0 || (firstIndexInClass == i && ch == '^'))
|
||||
sb.append('\\');
|
||||
if (inClass == 0 || (firstIndexInClass == i && ch == '^')) sb.append('\\');
|
||||
sb.append(ch);
|
||||
break;
|
||||
case '!':
|
||||
if (firstIndexInClass == i)
|
||||
sb.append('^');
|
||||
else
|
||||
sb.append('!');
|
||||
if (firstIndexInClass == i) sb.append('^');
|
||||
else sb.append('!');
|
||||
break;
|
||||
case '{':
|
||||
inGroup++;
|
||||
|
@ -134,10 +137,8 @@ public class ConnectionQueryExchangeImpl extends ConnectionQueryExchange {
|
|||
sb.append(')');
|
||||
break;
|
||||
case ',':
|
||||
if (inGroup > 0)
|
||||
sb.append('|');
|
||||
else
|
||||
sb.append(',');
|
||||
if (inGroup > 0) sb.append('|');
|
||||
else sb.append(',');
|
||||
break;
|
||||
default:
|
||||
sb.append(ch);
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.DaemonFocusExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DaemonFocusExchangeImpl extends DaemonFocusExchange {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
|
||||
OperationMode.switchUp(OperationMode.map(msg.getMode()));
|
||||
return Response.builder().build();
|
||||
}
|
||||
OperationMode.switchUp(OperationMode.map(msg.getMode()));
|
||||
return Response.builder().build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.DaemonModeExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DaemonModeExchangeImpl extends DaemonModeExchange {
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
public Object handle(HttpExchange exchange, Request msg)
|
||||
throws BeaconClientException {
|
||||
// Wait for startup
|
||||
while (OperationMode.get() == null) {
|
||||
ThreadHelper.sleep(100);
|
||||
|
@ -21,11 +23,11 @@ public class DaemonModeExchangeImpl extends DaemonModeExchange {
|
|||
if (!mode.isSupported()) {
|
||||
throw new BeaconClientException("Unsupported mode: " + msg.getMode().getDisplayName() + ". Supported: "
|
||||
+ String.join(
|
||||
", ",
|
||||
OperationMode.getAll().stream()
|
||||
.filter(OperationMode::isSupported)
|
||||
.map(OperationMode::getId)
|
||||
.toList()));
|
||||
", ",
|
||||
OperationMode.getAll().stream()
|
||||
.filter(OperationMode::isSupported)
|
||||
.map(OperationMode::getId)
|
||||
.toList()));
|
||||
}
|
||||
|
||||
OperationMode.switchToSyncIfPossible(mode);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.launcher.LauncherInput;
|
||||
import io.xpipe.app.util.PlatformState;
|
||||
|
@ -8,11 +7,14 @@ import io.xpipe.beacon.BeaconClientException;
|
|||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.DaemonOpenExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DaemonOpenExchangeImpl extends DaemonOpenExchange {
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
public Object handle(HttpExchange exchange, Request msg)
|
||||
throws BeaconServerException {
|
||||
if (msg.getArguments().isEmpty()) {
|
||||
if (!OperationMode.switchToSyncIfPossible(OperationMode.GUI)) {
|
||||
throw new BeaconServerException(PlatformState.getLastError());
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.DaemonStatusExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DaemonStatusExchangeImpl extends DaemonStatusExchange {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request body) throws IOException, BeaconClientException, BeaconServerException {
|
||||
public Object handle(HttpExchange exchange, Request body) {
|
||||
String mode;
|
||||
if (OperationMode.get() == null) {
|
||||
mode = "none";
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.DaemonStopExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DaemonStopExchangeImpl extends DaemonStopExchange {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
public Object handle(HttpExchange exchange, Request msg) {
|
||||
ThreadHelper.runAsync(() -> {
|
||||
ThreadHelper.sleep(1000);
|
||||
OperationMode.close();
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.AppProperties;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.DaemonVersionExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DaemonVersionExchangeImpl extends DaemonVersionExchange {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
public Object handle(HttpExchange exchange, Request msg) {
|
||||
var jvmVersion = System.getProperty("java.vm.vendor") + " "
|
||||
+ System.getProperty("java.vm.name") + " ("
|
||||
+ System.getProperty("java.vm.version") + ")";
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.beacon.AppBeaconServer;
|
||||
import io.xpipe.app.beacon.BeaconSession;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
|
@ -10,13 +8,16 @@ import io.xpipe.beacon.BeaconClientException;
|
|||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.HandshakeExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class HandshakeExchangeImpl extends HandshakeExchange {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request body) throws IOException, BeaconClientException, BeaconServerException {
|
||||
public Object handle(HttpExchange exchange, Request body)
|
||||
throws BeaconClientException {
|
||||
if (!checkAuth(body.getAuth())) {
|
||||
throw new BeaconClientException("Authentication failed");
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.beacon.AppBeaconServer;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.ShellExecExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -15,9 +16,13 @@ public class ShellExecExchangeImpl extends ShellExecExchange {
|
|||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
var e = DataStorage.get().getStoreEntryIfPresent(msg.getConnection()).orElseThrow(() -> new IllegalArgumentException("Unknown connection"));
|
||||
var existing = AppBeaconServer.get().getShellSessions().stream().filter(beaconShellSession -> beaconShellSession.getEntry().equals(e)).findFirst();
|
||||
public Object handle(HttpExchange exchange, Request msg) {
|
||||
var e = DataStorage.get()
|
||||
.getStoreEntryIfPresent(msg.getConnection())
|
||||
.orElseThrow(() -> new IllegalArgumentException("Unknown connection"));
|
||||
var existing = AppBeaconServer.get().getShellSessions().stream()
|
||||
.filter(beaconShellSession -> beaconShellSession.getEntry().equals(e))
|
||||
.findFirst();
|
||||
if (existing.isEmpty()) {
|
||||
throw new BeaconClientException("No shell session active for connection");
|
||||
}
|
||||
|
@ -30,6 +35,10 @@ public class ShellExecExchangeImpl extends ShellExecExchange {
|
|||
command.accumulateStderr(s -> err.set(s));
|
||||
exitCode = command.getExitCode();
|
||||
}
|
||||
return Response.builder().stdout(out.get()).stderr(err.get()).exitCode(exitCode).build();
|
||||
return Response.builder()
|
||||
.stdout(out.get())
|
||||
.stderr(err.get())
|
||||
.exitCode(exitCode)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.beacon.AppBeaconServer;
|
||||
import io.xpipe.app.beacon.BeaconShellSession;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
|
@ -8,6 +7,8 @@ import io.xpipe.beacon.BeaconClientException;
|
|||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.ShellStartExchange;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -16,13 +17,17 @@ public class ShellStartExchangeImpl extends ShellStartExchange {
|
|||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
var e = DataStorage.get().getStoreEntryIfPresent(msg.getConnection()).orElseThrow(() -> new IllegalArgumentException("Unknown connection"));
|
||||
public Object handle(HttpExchange exchange, Request msg) {
|
||||
var e = DataStorage.get()
|
||||
.getStoreEntryIfPresent(msg.getConnection())
|
||||
.orElseThrow(() -> new IllegalArgumentException("Unknown connection"));
|
||||
if (!(e.getStore() instanceof ShellStore s)) {
|
||||
throw new BeaconClientException("Not a shell connection");
|
||||
}
|
||||
|
||||
var existing = AppBeaconServer.get().getShellSessions().stream().filter(beaconShellSession -> beaconShellSession.getEntry().equals(e)).findFirst();
|
||||
var existing = AppBeaconServer.get().getShellSessions().stream()
|
||||
.filter(beaconShellSession -> beaconShellSession.getEntry().equals(e))
|
||||
.findFirst();
|
||||
if (existing.isPresent()) {
|
||||
return Response.builder().build();
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.beacon.AppBeaconServer;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.ShellStopExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -14,9 +15,13 @@ public class ShellStopExchangeImpl extends ShellStopExchange {
|
|||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
var e = DataStorage.get().getStoreEntryIfPresent(msg.getConnection()).orElseThrow(() -> new IllegalArgumentException("Unknown connection"));
|
||||
var existing = AppBeaconServer.get().getShellSessions().stream().filter(beaconShellSession -> beaconShellSession.getEntry().equals(e)).findFirst();
|
||||
public Object handle(HttpExchange exchange, Request msg) {
|
||||
var e = DataStorage.get()
|
||||
.getStoreEntryIfPresent(msg.getConnection())
|
||||
.orElseThrow(() -> new IllegalArgumentException("Unknown connection"));
|
||||
var existing = AppBeaconServer.get().getShellSessions().stream()
|
||||
.filter(beaconShellSession -> beaconShellSession.getEntry().equals(e))
|
||||
.findFirst();
|
||||
if (existing.isPresent()) {
|
||||
existing.get().getControl().close();
|
||||
AppBeaconServer.get().getShellSessions().remove(existing.get());
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.util.TerminalLauncherManager;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.TerminalLaunchExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TerminalLaunchExchangeImpl extends TerminalLaunchExchange {
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
public Object handle(HttpExchange exchange, Request msg)
|
||||
throws BeaconClientException {
|
||||
var r = TerminalLauncherManager.performLaunch(msg.getRequest());
|
||||
return Response.builder().targetFile(r).build();
|
||||
}
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.util.TerminalLauncherManager;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.TerminalWaitExchange;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TerminalWaitExchangeImpl extends TerminalWaitExchange {
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
public Object handle(HttpExchange exchange, Request msg)
|
||||
throws BeaconClientException, BeaconServerException {
|
||||
TerminalLauncherManager.waitForCompletion(msg.getRequest());
|
||||
return Response.builder().build();
|
||||
}
|
||||
|
|
|
@ -8,11 +8,13 @@ import io.xpipe.app.util.ThreadHelper;
|
|||
import io.xpipe.core.process.ProcessControlProvider;
|
||||
import io.xpipe.core.store.FileSystem;
|
||||
import io.xpipe.core.util.FailableRunnable;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.input.DataFormat;
|
||||
import javafx.scene.input.Dragboard;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.Value;
|
||||
|
||||
|
@ -46,8 +48,7 @@ public class BrowserClipboard {
|
|||
}
|
||||
|
||||
List<File> data = (List<File>) clipboard.getData(DataFlavor.javaFileListFlavor);
|
||||
var files =
|
||||
data.stream().map(f -> f.toPath()).toList();
|
||||
var files = data.stream().map(f -> f.toPath()).toList();
|
||||
if (files.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -57,7 +58,8 @@ public class BrowserClipboard {
|
|||
entries.add(LocalFileSystem.getLocalBrowserEntry(file));
|
||||
}
|
||||
|
||||
currentCopyClipboard.setValue(new Instance(UUID.randomUUID(), null, entries, BrowserFileTransferMode.COPY));
|
||||
currentCopyClipboard.setValue(
|
||||
new Instance(UUID.randomUUID(), null, entries, BrowserFileTransferMode.COPY));
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).expected().omit().handle();
|
||||
}
|
||||
|
@ -66,7 +68,8 @@ public class BrowserClipboard {
|
|||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static ClipboardContent startDrag(FileSystem.FileEntry base, List<BrowserEntry> selected, BrowserFileTransferMode mode) {
|
||||
public static ClipboardContent startDrag(
|
||||
FileSystem.FileEntry base, List<BrowserEntry> selected, BrowserFileTransferMode mode) {
|
||||
if (selected.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -21,14 +21,14 @@ public class BrowserFileOpener {
|
|||
key,
|
||||
new BooleanScope(model.getBusy()).exclusive(),
|
||||
() -> {
|
||||
return entry.getFileSystem().openInput(file);
|
||||
return entry.getFileSystem().openInput(file);
|
||||
},
|
||||
(size) -> {
|
||||
if (model.isClosed()) {
|
||||
return OutputStream.nullOutputStream();
|
||||
}
|
||||
|
||||
return entry.getFileSystem().openOutput(file, size);
|
||||
return entry.getFileSystem().openOutput(file, size);
|
||||
},
|
||||
s -> FileOpener.openWithAnyApplication(s));
|
||||
}
|
||||
|
@ -42,14 +42,14 @@ public class BrowserFileOpener {
|
|||
key,
|
||||
new BooleanScope(model.getBusy()).exclusive(),
|
||||
() -> {
|
||||
return entry.getFileSystem().openInput(file);
|
||||
return entry.getFileSystem().openInput(file);
|
||||
},
|
||||
(size) -> {
|
||||
if (model.isClosed()) {
|
||||
return OutputStream.nullOutputStream();
|
||||
}
|
||||
|
||||
return entry.getFileSystem().openOutput(file, size);
|
||||
return entry.getFileSystem().openOutput(file, size);
|
||||
},
|
||||
s -> FileOpener.openInDefaultApplication(s));
|
||||
}
|
||||
|
@ -68,15 +68,14 @@ public class BrowserFileOpener {
|
|||
key,
|
||||
new BooleanScope(model.getBusy()).exclusive(),
|
||||
() -> {
|
||||
return entry.getFileSystem().openInput(file);
|
||||
|
||||
return entry.getFileSystem().openInput(file);
|
||||
},
|
||||
(size) -> {
|
||||
if (model.isClosed()) {
|
||||
return OutputStream.nullOutputStream();
|
||||
}
|
||||
|
||||
return entry.getFileSystem().openOutput(file, size);
|
||||
return entry.getFileSystem().openOutput(file, size);
|
||||
},
|
||||
FileOpener::openInTextEditor);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.browser.fs.OpenFileSystemModel;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.impl.TextFieldComp;
|
||||
import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
||||
import io.xpipe.app.util.InputHelper;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.geometry.Pos;
|
||||
|
@ -16,6 +16,8 @@ import javafx.scene.input.KeyCode;
|
|||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
public class BrowserFilterComp extends Comp<BrowserFilterComp.Structure> {
|
||||
|
@ -40,7 +42,8 @@ public class BrowserFilterComp extends Comp<BrowserFilterComp.Structure> {
|
|||
button.fire();
|
||||
keyEvent.consume();
|
||||
});
|
||||
new TooltipAugment<>("app.search", new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN)).augment(button);
|
||||
new TooltipAugment<>("app.search", new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN))
|
||||
.augment(button);
|
||||
text.focusedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue && filterString.getValue() == null) {
|
||||
if (button.isFocused()) {
|
||||
|
@ -110,7 +113,7 @@ public class BrowserFilterComp extends Comp<BrowserFilterComp.Structure> {
|
|||
button.minWidthProperty().bind(text.heightProperty());
|
||||
button.maxHeightProperty().bind(text.heightProperty());
|
||||
button.maxWidthProperty().bind(text.heightProperty());
|
||||
return new Structure(box, (TextField) text, button);
|
||||
return new Structure(box, text, button);
|
||||
}
|
||||
|
||||
public record Structure(HBox box, TextField textField, Button toggleButton) implements CompStructure<HBox> {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.browser.file.BrowserContextMenu;
|
||||
import io.xpipe.app.browser.fs.OpenFileSystemModel;
|
||||
import io.xpipe.app.browser.icon.FileIconManager;
|
||||
|
@ -13,6 +12,7 @@ import io.xpipe.app.fxcomps.impl.TextFieldComp;
|
|||
import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
@ -28,6 +28,8 @@ import javafx.scene.layout.HBox;
|
|||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
public class BrowserNavBar extends Comp<BrowserNavBar.Structure> {
|
||||
|
@ -94,8 +96,8 @@ public class BrowserNavBar extends Comp<BrowserNavBar.Structure> {
|
|||
homeButton.getStyleClass().add(Styles.LEFT_PILL);
|
||||
homeButton.getStyleClass().add("path-graphic-button");
|
||||
new ContextMenuAugment<>(event -> event.getButton() == MouseButton.PRIMARY, null, () -> {
|
||||
return model.getInOverview().get() ? null : new BrowserContextMenu(model, null, false);
|
||||
})
|
||||
return model.getInOverview().get() ? null : new BrowserContextMenu(model, null, false);
|
||||
})
|
||||
.augment(new SimpleCompStructure<>(homeButton));
|
||||
|
||||
var historyButton = new Button(null, new FontIcon("mdi2h-history"));
|
||||
|
@ -103,7 +105,8 @@ public class BrowserNavBar extends Comp<BrowserNavBar.Structure> {
|
|||
historyButton.getStyleClass().add(Styles.RIGHT_PILL);
|
||||
new ContextMenuAugment<>(event -> event.getButton() == MouseButton.PRIMARY, null, this::createContextMenu)
|
||||
.augment(new SimpleCompStructure<>(historyButton));
|
||||
new TooltipAugment<>("history", new KeyCodeCombination(KeyCode.H, KeyCombination.ALT_DOWN)).augment(historyButton);
|
||||
new TooltipAugment<>("history", new KeyCodeCombination(KeyCode.H, KeyCombination.ALT_DOWN))
|
||||
.augment(historyButton);
|
||||
|
||||
var breadcrumbs = new BrowserBreadcrumbBar(model).grow(false, true);
|
||||
|
||||
|
@ -114,7 +117,8 @@ public class BrowserNavBar extends Comp<BrowserNavBar.Structure> {
|
|||
event.consume();
|
||||
});
|
||||
breadcrumbsRegion.setFocusTraversable(false);
|
||||
breadcrumbsRegion.visibleProperty()
|
||||
breadcrumbsRegion
|
||||
.visibleProperty()
|
||||
.bind(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return !pathRegion.isFocused()
|
||||
|
|
|
@ -11,11 +11,13 @@ import io.xpipe.app.issue.ErrorEvent;
|
|||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.store.FileSystem;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -68,8 +70,9 @@ public class BrowserOverviewComp extends SimpleComp {
|
|||
var rootsOverview = new BrowserFileOverviewComp(model, FXCollections.observableArrayList(roots), false);
|
||||
var rootsPane = new SimpleTitledPaneComp(AppI18n.observable("roots"), rootsOverview);
|
||||
|
||||
var recent = new DerivedObservableList<>(model.getSavedState().getRecentDirectories(), true).mapped(
|
||||
s -> FileSystem.FileEntry.ofDirectory(model.getFileSystem(), s.getDirectory())).getList();
|
||||
var recent = new DerivedObservableList<>(model.getSavedState().getRecentDirectories(), true)
|
||||
.mapped(s -> FileSystem.FileEntry.ofDirectory(model.getFileSystem(), s.getDirectory()))
|
||||
.getList();
|
||||
var recentOverview = new BrowserFileOverviewComp(model, recent, true);
|
||||
var recentPane = new SimpleTitledPaneComp(AppI18n.observable("recent"), recentOverview);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import io.xpipe.app.fxcomps.Comp;
|
|||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ObservableList;
|
||||
|
@ -18,6 +19,7 @@ import javafx.scene.control.OverrunStyle;
|
|||
import javafx.scene.image.Image;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import io.xpipe.app.browser.file.BrowserContextMenu;
|
||||
import io.xpipe.app.browser.file.BrowserFileListCompEntry;
|
||||
import io.xpipe.app.browser.fs.OpenFileSystemModel;
|
||||
|
@ -12,10 +11,13 @@ import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
|||
import io.xpipe.app.fxcomps.impl.LabelComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.util.HumanReadableFormat;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.scene.control.ToolBar;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
|
||||
|
@ -57,8 +59,10 @@ public class BrowserStatusBarComp extends SimpleComp {
|
|||
var transferred = HumanReadableFormat.progressByteCount(p.getTransferred());
|
||||
var all = HumanReadableFormat.byteCount(p.getTotal());
|
||||
var name = (p.getName() != null ? " @ " + p.getName() + " " : "");
|
||||
var time = p.getTotal() > 50_000_000 && p.elapsedTime().compareTo(Duration.of(200, ChronoUnit.MILLIS)) > 0 ? " | "
|
||||
+ HumanReadableFormat.duration(p.expectedTimeRemaining()) : " | ...";
|
||||
var time =
|
||||
p.getTotal() > 50_000_000 && p.elapsedTime().compareTo(Duration.of(200, ChronoUnit.MILLIS)) > 0
|
||||
? " | " + HumanReadableFormat.duration(p.expectedTimeRemaining())
|
||||
: " | ...";
|
||||
return transferred + " / " + all + name + time;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import io.xpipe.app.fxcomps.augment.DragOverPseudoClassAugment;
|
|||
import io.xpipe.app.fxcomps.impl.*;
|
||||
import io.xpipe.app.fxcomps.util.DerivedObservableList;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.geometry.Insets;
|
||||
|
@ -18,6 +19,7 @@ import javafx.scene.input.Dragboard;
|
|||
import javafx.scene.input.TransferMode;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -46,7 +48,9 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
var backgroundStack =
|
||||
new StackComp(List.of(background)).grow(true, true).styleClass("download-background");
|
||||
|
||||
var binding = new DerivedObservableList<>(syncItems, true).mapped(item -> item.getBrowserEntry()).getList();
|
||||
var binding = new DerivedObservableList<>(syncItems, true)
|
||||
.mapped(item -> item.getBrowserEntry())
|
||||
.getList();
|
||||
var list = new BrowserSelectionListComp(
|
||||
binding,
|
||||
entry -> Bindings.createStringBinding(
|
||||
|
@ -57,9 +61,15 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
if (sourceItem.isEmpty()) {
|
||||
return "?";
|
||||
}
|
||||
var name = entry.getModel() == null || sourceItem.get().downloadFinished().get()
|
||||
? "Local"
|
||||
: entry.getModel().getFileSystemModel().getName();
|
||||
var name = entry.getModel() == null
|
||||
|| sourceItem
|
||||
.get()
|
||||
.downloadFinished()
|
||||
.get()
|
||||
? "Local"
|
||||
: entry.getModel()
|
||||
.getFileSystemModel()
|
||||
.getName();
|
||||
return entry.getFileName() + " (" + name + ")";
|
||||
},
|
||||
syncAllDownloaded))
|
||||
|
|
|
@ -9,6 +9,7 @@ import io.xpipe.app.browser.session.BrowserSessionModel;
|
|||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
import io.xpipe.app.util.ShellTemp;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.Property;
|
||||
|
@ -17,6 +18,7 @@ import javafx.beans.property.SimpleObjectProperty;
|
|||
import javafx.beans.value.ObservableBooleanValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import lombok.Value;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
|
@ -95,7 +97,8 @@ public class BrowserTransferModel {
|
|||
}
|
||||
|
||||
var item = new Item(null, name, entry, path);
|
||||
item.progress.setValue(BrowserTransferProgress.finished(entry.getFileName(), entry.getRawFileEntry().getSize()));
|
||||
item.progress.setValue(BrowserTransferProgress.finished(
|
||||
entry.getFileName(), entry.getRawFileEntry().getSize()));
|
||||
items.add(item);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
@ -155,8 +158,7 @@ public class BrowserTransferModel {
|
|||
Path localFile;
|
||||
Property<BrowserTransferProgress> progress;
|
||||
|
||||
public Item(
|
||||
OpenFileSystemModel openFileSystemModel, String name, BrowserEntry browserEntry, Path localFile) {
|
||||
public Item(OpenFileSystemModel openFileSystemModel, String name, BrowserEntry browserEntry, Path localFile) {
|
||||
this.openFileSystemModel = openFileSystemModel;
|
||||
this.name = name;
|
||||
this.browserEntry = browserEntry;
|
||||
|
|
|
@ -32,7 +32,7 @@ public class BrowserTransferProgress {
|
|||
|
||||
public Duration elapsedTime() {
|
||||
var now = Instant.now();
|
||||
var elapsed = Duration.between(start,now);
|
||||
var elapsed = Duration.between(start, now);
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package io.xpipe.app.browser;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.browser.session.BrowserSessionModel;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.ListBoxViewComp;
|
||||
|
@ -18,6 +16,7 @@ import io.xpipe.app.fxcomps.util.BindingsHelper;
|
|||
import io.xpipe.app.fxcomps.util.DerivedObservableList;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
@ -31,6 +30,9 @@ import javafx.scene.layout.Priority;
|
|||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import atlantafx.base.theme.Styles;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BrowserWelcomeComp extends SimpleComp {
|
||||
|
@ -65,18 +67,20 @@ public class BrowserWelcomeComp extends SimpleComp {
|
|||
return new VBox(hbox);
|
||||
}
|
||||
|
||||
var list = new DerivedObservableList<>(state.getEntries(), true).filtered(e -> {
|
||||
var entry = DataStorage.get().getStoreEntryIfPresent(e.getUuid());
|
||||
if (entry.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
var list = new DerivedObservableList<>(state.getEntries(), true)
|
||||
.filtered(e -> {
|
||||
var entry = DataStorage.get().getStoreEntryIfPresent(e.getUuid());
|
||||
if (entry.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entry.get().getValidity().isUsable()) {
|
||||
return false;
|
||||
}
|
||||
if (!entry.get().getValidity().isUsable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).getList();
|
||||
return true;
|
||||
})
|
||||
.getList();
|
||||
var empty = Bindings.createBooleanBinding(() -> list.isEmpty(), list);
|
||||
|
||||
var headerBinding = BindingsHelper.flatMap(empty, b -> {
|
||||
|
|
|
@ -7,10 +7,12 @@ import io.xpipe.app.fxcomps.util.BindingsHelper;
|
|||
import io.xpipe.app.util.BooleanScope;
|
||||
import io.xpipe.app.util.LicenseProvider;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.List;
|
||||
|
|
|
@ -33,9 +33,8 @@ public final class BrowserContextMenu extends ContextMenu {
|
|||
private static List<BrowserEntry> resolveIfNeeded(BrowserAction action, List<BrowserEntry> selected) {
|
||||
return action.automaticallyResolveLinks()
|
||||
? selected.stream()
|
||||
.map(browserEntry -> new BrowserEntry(
|
||||
browserEntry.getRawFileEntry().resolved(),
|
||||
browserEntry.getModel()))
|
||||
.map(browserEntry ->
|
||||
new BrowserEntry(browserEntry.getRawFileEntry().resolved(), browserEntry.getModel()))
|
||||
.toList()
|
||||
: selected;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package io.xpipe.app.browser.file;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.browser.action.BrowserAction;
|
||||
import io.xpipe.app.comp.base.LazyTextFieldComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
|
@ -15,6 +13,7 @@ import io.xpipe.core.process.OsType;
|
|||
import io.xpipe.core.store.FileKind;
|
||||
import io.xpipe.core.store.FileNames;
|
||||
import io.xpipe.core.store.FileSystem;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
|
@ -38,6 +37,9 @@ import javafx.scene.layout.HBox;
|
|||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import atlantafx.base.theme.Styles;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
|
@ -159,16 +161,18 @@ public final class BrowserFileListComp extends SimpleComp {
|
|||
private void prepareTableShortcuts(TableView<BrowserEntry> table) {
|
||||
table.setOnKeyPressed(event -> {
|
||||
var selected = fileList.getSelection();
|
||||
var action = BrowserAction.getFlattened(fileList.getFileSystemModel(), selected).stream().filter(
|
||||
browserAction -> browserAction.isApplicable(fileList.getFileSystemModel(), selected) &&
|
||||
browserAction.isActive(fileList.getFileSystemModel(), selected)).filter(
|
||||
browserAction -> browserAction.getShortcut() != null).filter(browserAction -> browserAction.getShortcut().match(event)).findAny();
|
||||
var action = BrowserAction.getFlattened(fileList.getFileSystemModel(), selected).stream()
|
||||
.filter(browserAction -> browserAction.isApplicable(fileList.getFileSystemModel(), selected)
|
||||
&& browserAction.isActive(fileList.getFileSystemModel(), selected))
|
||||
.filter(browserAction -> browserAction.getShortcut() != null)
|
||||
.filter(browserAction -> browserAction.getShortcut().match(event))
|
||||
.findAny();
|
||||
action.ifPresent(browserAction -> {
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
browserAction.execute(fileList.getFileSystemModel(), selected);
|
||||
});
|
||||
event.consume();
|
||||
});
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
browserAction.execute(fileList.getFileSystemModel(), selected);
|
||||
});
|
||||
event.consume();
|
||||
});
|
||||
if (action.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
@ -326,7 +330,9 @@ public final class BrowserFileListComp extends SimpleComp {
|
|||
Platform.runLater(() -> {
|
||||
var newItems = new ArrayList<>(fileList.getShown().getValue());
|
||||
|
||||
var hasModifiedDate = newItems.size() == 0 || newItems.stream().anyMatch(entry -> entry.getRawFileEntry().getDate() != null);
|
||||
var hasModifiedDate = newItems.size() == 0
|
||||
|| newItems.stream()
|
||||
.anyMatch(entry -> entry.getRawFileEntry().getDate() != null);
|
||||
if (!hasModifiedDate) {
|
||||
table.getColumns().remove(mtimeCol);
|
||||
} else {
|
||||
|
@ -336,7 +342,10 @@ public final class BrowserFileListComp extends SimpleComp {
|
|||
}
|
||||
|
||||
if (fileList.getFileSystemModel().getFileSystem() != null) {
|
||||
var shell = fileList.getFileSystemModel().getFileSystem().getShell().orElseThrow();
|
||||
var shell = fileList.getFileSystemModel()
|
||||
.getFileSystem()
|
||||
.getShell()
|
||||
.orElseThrow();
|
||||
var hasAttributes = !OsType.WINDOWS.equals(shell.getOsType());
|
||||
if (!hasAttributes) {
|
||||
table.getColumns().remove(modeCol);
|
||||
|
@ -357,8 +366,10 @@ public final class BrowserFileListComp extends SimpleComp {
|
|||
if (!Objects.equals(lastDir.get(), currentDirectory)) {
|
||||
TableViewSkin<?> skin = (TableViewSkin<?>) table.getSkin();
|
||||
if (skin != null) {
|
||||
VirtualFlow<?> flow = (VirtualFlow<?>) skin.getChildren().get(1);
|
||||
ScrollBar vbar = (ScrollBar) flow.getChildrenUnmodifiable().get(2);
|
||||
VirtualFlow<?> flow =
|
||||
(VirtualFlow<?>) skin.getChildren().get(1);
|
||||
ScrollBar vbar =
|
||||
(ScrollBar) flow.getChildrenUnmodifiable().get(2);
|
||||
if (vbar.getValue() != 0.0) {
|
||||
table.scrollTo(0);
|
||||
}
|
||||
|
@ -541,7 +552,8 @@ public final class BrowserFileListComp extends SimpleComp {
|
|||
var selected = fileList.getSelection();
|
||||
// Only show one menu across all selected entries
|
||||
if (selected.size() > 0 && selected.getLast() == getTableRow().getItem()) {
|
||||
var cm = new BrowserContextMenu(fileList.getFileSystemModel(), getTableRow().getItem(), false);
|
||||
var cm = new BrowserContextMenu(
|
||||
fileList.getFileSystemModel(), getTableRow().getItem(), false);
|
||||
ContextMenuHelper.toggleShow(cm, this, Side.RIGHT);
|
||||
event.consume();
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@ package io.xpipe.app.browser.file;
|
|||
import io.xpipe.app.browser.BrowserClipboard;
|
||||
import io.xpipe.app.browser.BrowserSelectionListComp;
|
||||
import io.xpipe.core.store.FileKind;
|
||||
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.TableView;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.input.*;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -154,7 +156,13 @@ public class BrowserFileListCompEntry {
|
|||
var target = item != null && item.getRawFileEntry().getKind() == FileKind.DIRECTORY
|
||||
? item.getRawFileEntry()
|
||||
: model.getFileSystemModel().getCurrentDirectory();
|
||||
model.getFileSystemModel().dropFilesIntoAsync(target, files.stream().map(browserEntry -> browserEntry.getRawFileEntry()).toList(), db.getMode());
|
||||
model.getFileSystemModel()
|
||||
.dropFilesIntoAsync(
|
||||
target,
|
||||
files.stream()
|
||||
.map(browserEntry -> browserEntry.getRawFileEntry())
|
||||
.toList(),
|
||||
db.getMode());
|
||||
event.setDropCompleted(true);
|
||||
event.consume();
|
||||
}
|
||||
|
@ -180,7 +188,10 @@ public class BrowserFileListCompEntry {
|
|||
|
||||
var selected = model.getSelection();
|
||||
Dragboard db = row.startDragAndDrop(TransferMode.COPY);
|
||||
db.setContent(BrowserClipboard.startDrag(model.getFileSystemModel().getCurrentDirectory(), selected, event.isAltDown() ? BrowserFileTransferMode.MOVE : BrowserFileTransferMode.NORMAL));
|
||||
db.setContent(BrowserClipboard.startDrag(
|
||||
model.getFileSystemModel().getCurrentDirectory(),
|
||||
selected,
|
||||
event.isAltDown() ? BrowserFileTransferMode.MOVE : BrowserFileTransferMode.NORMAL));
|
||||
|
||||
Image image = BrowserSelectionListComp.snapshot(selected);
|
||||
db.setDragView(image, -20, 15);
|
||||
|
|
|
@ -5,12 +5,14 @@ import io.xpipe.app.issue.ErrorEvent;
|
|||
import io.xpipe.core.store.FileKind;
|
||||
import io.xpipe.core.store.FileNames;
|
||||
import io.xpipe.core.store.FileSystem;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -54,7 +56,8 @@ public final class BrowserFileListModel {
|
|||
|
||||
public void setAll(Stream<FileSystem.FileEntry> newFiles) {
|
||||
try (var s = newFiles) {
|
||||
var l = s.filter(entry -> entry != null).map(entry -> new BrowserEntry(entry, this))
|
||||
var l = s.filter(entry -> entry != null)
|
||||
.map(entry -> new BrowserEntry(entry, this))
|
||||
.toList();
|
||||
all.setValue(l);
|
||||
refreshShown();
|
||||
|
@ -90,9 +93,7 @@ public final class BrowserFileListModel {
|
|||
path -> path.getRawFileEntry().resolved().getKind() != FileKind.DIRECTORY);
|
||||
var comp = comparatorProperty.getValue();
|
||||
|
||||
Comparator<BrowserEntry> us = comp != null
|
||||
? dirsFirst.thenComparing(comp)
|
||||
: dirsFirst;
|
||||
Comparator<BrowserEntry> us = comp != null ? dirsFirst.thenComparing(comp) : dirsFirst;
|
||||
return us;
|
||||
}
|
||||
|
||||
|
@ -120,7 +121,11 @@ public final class BrowserFileListModel {
|
|||
try {
|
||||
fileSystemModel.getFileSystem().move(fullPath, newFullPath);
|
||||
fileSystemModel.refresh();
|
||||
var b = all.getValue().stream().filter(browserEntry -> browserEntry.getRawFileEntry().getPath().equals(newFullPath)).findFirst().orElse(old);
|
||||
var b = all.getValue().stream()
|
||||
.filter(browserEntry ->
|
||||
browserEntry.getRawFileEntry().getPath().equals(newFullPath))
|
||||
.findFirst()
|
||||
.orElse(old);
|
||||
return b;
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
|
|
|
@ -36,8 +36,7 @@ public class BrowserFileOverviewComp extends SimpleComp {
|
|||
var icon = BrowserIcons.createIcon(entry);
|
||||
var graphic = new HorizontalComp(List.of(
|
||||
icon,
|
||||
new BrowserQuickAccessButtonComp(
|
||||
() -> new BrowserEntry(entry, model.getFileList()), model)));
|
||||
new BrowserQuickAccessButtonComp(() -> new BrowserEntry(entry, model.getFileList()), model)));
|
||||
var l = new Button(entry.getPath(), graphic.createRegion());
|
||||
l.setGraphicTextGap(1);
|
||||
l.setOnAction(event -> {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package io.xpipe.app.browser.file;
|
||||
|
||||
public enum BrowserFileTransferMode {
|
||||
|
||||
NORMAL,
|
||||
COPY,
|
||||
MOVE
|
||||
|
|
|
@ -28,9 +28,12 @@ public class BrowserFileTransferOperation {
|
|||
|
||||
BrowserAlerts.FileConflictChoice lastConflictChoice;
|
||||
|
||||
public BrowserFileTransferOperation(FileSystem.FileEntry target, List<FileSystem.FileEntry> files, BrowserFileTransferMode transferMode, boolean checkConflicts,
|
||||
Consumer<BrowserTransferProgress> progress
|
||||
) {
|
||||
public BrowserFileTransferOperation(
|
||||
FileSystem.FileEntry target,
|
||||
List<FileSystem.FileEntry> files,
|
||||
BrowserFileTransferMode transferMode,
|
||||
boolean checkConflicts,
|
||||
Consumer<BrowserTransferProgress> progress) {
|
||||
this.target = target;
|
||||
this.files = files;
|
||||
this.transferMode = transferMode;
|
||||
|
@ -38,7 +41,12 @@ public class BrowserFileTransferOperation {
|
|||
this.progress = progress;
|
||||
}
|
||||
|
||||
public static BrowserFileTransferOperation ofLocal(FileSystem.FileEntry target, List<Path> files, BrowserFileTransferMode transferMode, boolean checkConflicts, Consumer<BrowserTransferProgress> progress) {
|
||||
public static BrowserFileTransferOperation ofLocal(
|
||||
FileSystem.FileEntry target,
|
||||
List<Path> files,
|
||||
BrowserFileTransferMode transferMode,
|
||||
boolean checkConflicts,
|
||||
Consumer<BrowserTransferProgress> progress) {
|
||||
var entries = files.stream()
|
||||
.map(path -> {
|
||||
if (!Files.exists(path)) {
|
||||
|
@ -60,11 +68,7 @@ public class BrowserFileTransferOperation {
|
|||
this.progress.accept(progress);
|
||||
}
|
||||
|
||||
private boolean handleChoice(
|
||||
FileSystem fileSystem,
|
||||
String target,
|
||||
boolean multiple)
|
||||
throws Exception {
|
||||
private boolean handleChoice(FileSystem fileSystem, String target, boolean multiple) throws Exception {
|
||||
if (lastConflictChoice == BrowserAlerts.FileConflictChoice.CANCEL) {
|
||||
return false;
|
||||
}
|
||||
|
@ -101,15 +105,15 @@ public class BrowserFileTransferOperation {
|
|||
return true;
|
||||
}
|
||||
|
||||
public void execute()
|
||||
throws Exception {
|
||||
public void execute() throws Exception {
|
||||
if (files.isEmpty()) {
|
||||
updateProgress(BrowserTransferProgress.empty());
|
||||
return;
|
||||
}
|
||||
|
||||
var same = files.getFirst().getFileSystem().equals(target.getFileSystem());
|
||||
var doesMove = transferMode == BrowserFileTransferMode.MOVE || (same && transferMode == BrowserFileTransferMode.NORMAL);
|
||||
var doesMove = transferMode == BrowserFileTransferMode.MOVE
|
||||
|| (same && transferMode == BrowserFileTransferMode.NORMAL);
|
||||
if (doesMove) {
|
||||
if (!BrowserAlerts.showMoveAlert(files, target)) {
|
||||
return;
|
||||
|
@ -132,8 +136,7 @@ public class BrowserFileTransferOperation {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleSingleOnSameFileSystem(FileSystem.FileEntry source)
|
||||
throws Exception {
|
||||
private void handleSingleOnSameFileSystem(FileSystem.FileEntry source) throws Exception {
|
||||
// Prevent dropping directory into itself
|
||||
if (source.getPath().equals(target.getPath())) {
|
||||
return;
|
||||
|
@ -163,8 +166,7 @@ public class BrowserFileTransferOperation {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleSingleAcrossFileSystems(FileSystem.FileEntry source)
|
||||
throws Exception {
|
||||
private void handleSingleAcrossFileSystems(FileSystem.FileEntry source) throws Exception {
|
||||
if (target.getKind() != FileKind.DIRECTORY) {
|
||||
throw new IllegalStateException("Target " + target.getPath() + " is not a directory");
|
||||
}
|
||||
|
@ -214,9 +216,7 @@ public class BrowserFileTransferOperation {
|
|||
} else if (sourceFile.getKind() == FileKind.FILE) {
|
||||
if (checkConflicts
|
||||
&& !handleChoice(
|
||||
target.getFileSystem(),
|
||||
targetFile,
|
||||
files.size() > 1 || flatFiles.size() > 1)) {
|
||||
target.getFileSystem(), targetFile, files.size() > 1 || flatFiles.size() > 1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.xpipe.app.browser.fs.OpenFileSystemModel;
|
|||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||
import io.xpipe.app.util.InputHelper;
|
||||
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
|
|
@ -8,6 +8,7 @@ import io.xpipe.app.util.InputHelper;
|
|||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.store.FileKind;
|
||||
import io.xpipe.core.store.FileSystem;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.geometry.Side;
|
||||
|
@ -19,6 +20,7 @@ import javafx.scene.input.KeyCode;
|
|||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -139,8 +141,10 @@ public class BrowserQuickAccessContextMenu extends ContextMenu {
|
|||
this.browserEntry = browserEntry;
|
||||
this.menu = new Menu(
|
||||
// Use original name, not the link target
|
||||
browserEntry.getRawFileEntry().getName(), PrettyImageHelper.ofFixedRasterized(
|
||||
FileIconManager.getFileIcon(browserEntry.getRawFileEntry(), false), 24, 24).createRegion());
|
||||
browserEntry.getRawFileEntry().getName(),
|
||||
PrettyImageHelper.ofFixedRasterized(
|
||||
FileIconManager.getFileIcon(browserEntry.getRawFileEntry(), false), 24, 24)
|
||||
.createRegion());
|
||||
createMenu();
|
||||
addInputListeners();
|
||||
}
|
||||
|
@ -243,7 +247,8 @@ public class BrowserQuickAccessContextMenu extends ContextMenu {
|
|||
if (contextMenu != null) {
|
||||
contextMenu.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
||||
keyBasedNavigation = true;
|
||||
if (event.getCode().equals(KeyCode.SPACE) || event.getCode().equals(KeyCode.ENTER)) {
|
||||
if (event.getCode().equals(KeyCode.SPACE)
|
||||
|| event.getCode().equals(KeyCode.ENTER)) {
|
||||
expandBrowserActionMenuKey = true;
|
||||
} else {
|
||||
expandBrowserActionMenuKey = false;
|
||||
|
|
|
@ -66,7 +66,8 @@ public class FileSystemHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static String resolveDirectoryPath(OpenFileSystemModel model, String path, boolean allowRewrite) throws Exception {
|
||||
public static String resolveDirectoryPath(OpenFileSystemModel model, String path, boolean allowRewrite)
|
||||
throws Exception {
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -97,7 +98,8 @@ public class FileSystemHelper {
|
|||
return FileNames.toDirectory(resolved);
|
||||
}
|
||||
|
||||
public static void validateDirectoryPath(OpenFileSystemModel model, String path, boolean verifyExists) throws Exception {
|
||||
public static void validateDirectoryPath(OpenFileSystemModel model, String path, boolean verifyExists)
|
||||
throws Exception {
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,6 @@ public class LocalFileSystem {
|
|||
|
||||
public static BrowserEntry getLocalBrowserEntry(Path file) throws Exception {
|
||||
var e = getLocalFileEntry(file);
|
||||
return new BrowserEntry(e,null);
|
||||
return new BrowserEntry(e, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.xpipe.app.browser.fs;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import io.xpipe.app.browser.BrowserFilterComp;
|
||||
import io.xpipe.app.browser.BrowserNavBar;
|
||||
import io.xpipe.app.browser.BrowserOverviewComp;
|
||||
|
@ -17,6 +16,7 @@ import io.xpipe.app.fxcomps.augment.ContextMenuAugment;
|
|||
import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
||||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||
import io.xpipe.app.util.InputHelper;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.MenuButton;
|
||||
|
@ -28,6 +28,8 @@ import javafx.scene.layout.HBox;
|
|||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -54,13 +56,15 @@ public class OpenFileSystemComp extends SimpleComp {
|
|||
var root = new VBox();
|
||||
var overview = new Button(null, new FontIcon("mdi2m-monitor"));
|
||||
overview.setOnAction(e -> model.cdAsync(null));
|
||||
new TooltipAugment<>("overview", new KeyCodeCombination(KeyCode.HOME, KeyCombination.ALT_DOWN)).augment(overview);
|
||||
new TooltipAugment<>("overview", new KeyCodeCombination(KeyCode.HOME, KeyCombination.ALT_DOWN))
|
||||
.augment(overview);
|
||||
overview.disableProperty().bind(model.getInOverview());
|
||||
overview.setAccessibleText("System overview");
|
||||
InputHelper.onKeyCombination(root, new KeyCodeCombination(KeyCode.HOME, KeyCombination.ALT_DOWN), true, keyEvent -> {
|
||||
overview.fire();
|
||||
keyEvent.consume();
|
||||
});
|
||||
InputHelper.onKeyCombination(
|
||||
root, new KeyCodeCombination(KeyCode.HOME, KeyCombination.ALT_DOWN), true, keyEvent -> {
|
||||
overview.fire();
|
||||
keyEvent.consume();
|
||||
});
|
||||
|
||||
var backBtn = BrowserAction.byId("back", model, List.of()).toButton(root, model, List.of());
|
||||
var forthBtn = BrowserAction.byId("forward", model, List.of()).toButton(root, model, List.of());
|
||||
|
@ -95,12 +99,12 @@ public class OpenFileSystemComp extends SimpleComp {
|
|||
refreshBtn,
|
||||
terminalBtn,
|
||||
menuButton);
|
||||
squaredSize(navBar.get(),overview,true);
|
||||
squaredSize(navBar.get(),backBtn,true);
|
||||
squaredSize(navBar.get(),forthBtn,true);
|
||||
squaredSize(navBar.get(),refreshBtn,true);
|
||||
squaredSize(navBar.get(),terminalBtn,true);
|
||||
squaredSize(navBar.get(),menuButton,false);
|
||||
squaredSize(navBar.get(), overview, true);
|
||||
squaredSize(navBar.get(), backBtn, true);
|
||||
squaredSize(navBar.get(), forthBtn, true);
|
||||
squaredSize(navBar.get(), refreshBtn, true);
|
||||
squaredSize(navBar.get(), terminalBtn, true);
|
||||
squaredSize(navBar.get(), menuButton, false);
|
||||
|
||||
var content = createFileListContent();
|
||||
root.getChildren().addAll(topBar, content);
|
||||
|
@ -111,26 +115,30 @@ public class OpenFileSystemComp extends SimpleComp {
|
|||
}
|
||||
});
|
||||
|
||||
InputHelper.onKeyCombination(root, new KeyCodeCombination(KeyCode.F, KeyCombination.CONTROL_DOWN), true, keyEvent -> {
|
||||
filter.toggleButton().fire();
|
||||
filter.textField().requestFocus();
|
||||
keyEvent.consume();
|
||||
});
|
||||
InputHelper.onKeyCombination(root, new KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN), true, keyEvent -> {
|
||||
navBar.textField().requestFocus();
|
||||
keyEvent.consume();
|
||||
});
|
||||
InputHelper.onKeyCombination(root, new KeyCodeCombination(KeyCode.H, KeyCombination.ALT_DOWN), true, keyEvent -> {
|
||||
navBar.historyButton().fire();
|
||||
keyEvent.consume();
|
||||
});
|
||||
InputHelper.onKeyCombination(root, new KeyCodeCombination(KeyCode.UP, KeyCombination.ALT_DOWN), true, keyEvent -> {
|
||||
var p = model.getCurrentParentDirectory();
|
||||
if (p != null) {
|
||||
model.cdAsync(p.getPath());
|
||||
}
|
||||
keyEvent.consume();
|
||||
});
|
||||
InputHelper.onKeyCombination(
|
||||
root, new KeyCodeCombination(KeyCode.F, KeyCombination.CONTROL_DOWN), true, keyEvent -> {
|
||||
filter.toggleButton().fire();
|
||||
filter.textField().requestFocus();
|
||||
keyEvent.consume();
|
||||
});
|
||||
InputHelper.onKeyCombination(
|
||||
root, new KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN), true, keyEvent -> {
|
||||
navBar.textField().requestFocus();
|
||||
keyEvent.consume();
|
||||
});
|
||||
InputHelper.onKeyCombination(
|
||||
root, new KeyCodeCombination(KeyCode.H, KeyCombination.ALT_DOWN), true, keyEvent -> {
|
||||
navBar.historyButton().fire();
|
||||
keyEvent.consume();
|
||||
});
|
||||
InputHelper.onKeyCombination(
|
||||
root, new KeyCodeCombination(KeyCode.UP, KeyCombination.ALT_DOWN), true, keyEvent -> {
|
||||
var p = model.getCurrentParentDirectory();
|
||||
if (p != null) {
|
||||
model.cdAsync(p.getPath());
|
||||
}
|
||||
keyEvent.consume();
|
||||
});
|
||||
return root;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,10 @@ import io.xpipe.core.process.ShellDialects;
|
|||
import io.xpipe.core.process.ShellOpenFunction;
|
||||
import io.xpipe.core.store.*;
|
||||
import io.xpipe.core.util.FailableConsumer;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
|
@ -345,7 +347,8 @@ public final class OpenFileSystemModel extends BrowserSessionTab<FileSystemStore
|
|||
}
|
||||
|
||||
startIfNeeded();
|
||||
var op = BrowserFileTransferOperation.ofLocal(entry, files,BrowserFileTransferMode.COPY,true, progress::setValue);
|
||||
var op = BrowserFileTransferOperation.ofLocal(
|
||||
entry, files, BrowserFileTransferMode.COPY, true, progress::setValue);
|
||||
op.execute();
|
||||
refreshSync();
|
||||
});
|
||||
|
@ -353,8 +356,7 @@ public final class OpenFileSystemModel extends BrowserSessionTab<FileSystemStore
|
|||
}
|
||||
|
||||
public void dropFilesIntoAsync(
|
||||
FileSystem.FileEntry target, List<FileSystem.FileEntry> files, BrowserFileTransferMode mode
|
||||
) {
|
||||
FileSystem.FileEntry target, List<FileSystem.FileEntry> files, BrowserFileTransferMode mode) {
|
||||
// We don't have to do anything in this case
|
||||
if (files.isEmpty()) {
|
||||
return;
|
||||
|
@ -367,7 +369,7 @@ public final class OpenFileSystemModel extends BrowserSessionTab<FileSystemStore
|
|||
}
|
||||
|
||||
startIfNeeded();
|
||||
var op = new BrowserFileTransferOperation(target, files, mode,true, progress::setValue);
|
||||
var op = new BrowserFileTransferOperation(target, files, mode, true, progress::setValue);
|
||||
op.execute();
|
||||
refreshSync();
|
||||
});
|
||||
|
|
|
@ -10,10 +10,12 @@ import io.xpipe.app.util.ThreadHelper;
|
|||
import io.xpipe.core.store.FileNames;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
import io.xpipe.core.util.FailableFunction;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
|
|
|
@ -11,8 +11,10 @@ import io.xpipe.app.util.ThreadHelper;
|
|||
import io.xpipe.core.store.FileNames;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
import io.xpipe.core.util.FailableFunction;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
|
@ -9,8 +9,8 @@ import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
|||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
|
||||
import io.xpipe.app.util.InputHelper;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
|
|
@ -3,8 +3,8 @@ package io.xpipe.app.comp.base;
|
|||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
|
|
@ -43,9 +43,11 @@ public class DropdownComp extends Comp<CompStructure<Button>> {
|
|||
.map(menuItem -> menuItem.getGraphic().visibleProperty())
|
||||
.toList();
|
||||
button.visibleProperty()
|
||||
.bind(Bindings.createBooleanBinding(() -> {
|
||||
return l.stream().anyMatch(booleanObservableValue -> booleanObservableValue.getValue());
|
||||
}, l.toArray(ObservableValue[]::new)));
|
||||
.bind(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return l.stream().anyMatch(booleanObservableValue -> booleanObservableValue.getValue());
|
||||
},
|
||||
l.toArray(ObservableValue[]::new)));
|
||||
|
||||
var graphic = new FontIcon("mdi2c-chevron-double-down");
|
||||
button.fontProperty().subscribe(c -> {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package io.xpipe.app.comp.base;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||
import io.xpipe.app.fxcomps.impl.TextAreaComp;
|
||||
import io.xpipe.app.util.FileOpener;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
@ -13,6 +13,8 @@ import javafx.scene.control.TextArea;
|
|||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
|
||||
|
@ -50,21 +52,23 @@ public class IntegratedTextAreaComp extends Comp<IntegratedTextAreaComp.Structur
|
|||
|
||||
@Override
|
||||
public Structure createBase() {
|
||||
var fileDrop = new FileDropOverlayComp<>(new Comp<TextAreaStructure>() {
|
||||
@Override
|
||||
public TextAreaStructure createBase() {
|
||||
var textArea = new TextAreaComp(value, lazy).createStructure();
|
||||
var copyButton = createOpenButton();
|
||||
var pane = new AnchorPane(copyButton);
|
||||
pane.setPickOnBounds(false);
|
||||
AnchorPane.setTopAnchor(copyButton, 10.0);
|
||||
AnchorPane.setRightAnchor(copyButton, 10.0);
|
||||
var fileDrop = new FileDropOverlayComp<>(
|
||||
new Comp<TextAreaStructure>() {
|
||||
@Override
|
||||
public TextAreaStructure createBase() {
|
||||
var textArea = new TextAreaComp(value, lazy).createStructure();
|
||||
var copyButton = createOpenButton();
|
||||
var pane = new AnchorPane(copyButton);
|
||||
pane.setPickOnBounds(false);
|
||||
AnchorPane.setTopAnchor(copyButton, 10.0);
|
||||
AnchorPane.setRightAnchor(copyButton, 10.0);
|
||||
|
||||
var c = new StackPane();
|
||||
c.getChildren().addAll(textArea.get(), pane);
|
||||
return new TextAreaStructure(c, textArea.getTextArea());
|
||||
}
|
||||
}, paths -> value.setValue(Files.readString(paths.getFirst())));
|
||||
var c = new StackPane();
|
||||
c.getChildren().addAll(textArea.get(), pane);
|
||||
return new TextAreaStructure(c, textArea.getTextArea());
|
||||
}
|
||||
},
|
||||
paths -> value.setValue(Files.readString(paths.getFirst())));
|
||||
var struc = fileDrop.createStructure();
|
||||
return new Structure(struc.get(), struc.getCompStructure().getTextArea());
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import io.xpipe.app.fxcomps.CompStructure;
|
|||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.util.DerivedObservableList;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package io.xpipe.app.comp.base;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||
import io.xpipe.app.util.FileOpener;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.scene.control.Button;
|
||||
|
@ -12,6 +12,8 @@ import javafx.scene.control.TextArea;
|
|||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
|
||||
|
@ -20,20 +22,15 @@ public class MarkdownEditorComp extends Comp<MarkdownEditorComp.Structure> {
|
|||
private final Property<String> value;
|
||||
private final String identifier;
|
||||
|
||||
public MarkdownEditorComp(
|
||||
Property<String> value, String identifier) {
|
||||
public MarkdownEditorComp(Property<String> value, String identifier) {
|
||||
this.value = value;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
private Button createOpenButton() {
|
||||
return new IconButtonComp(
|
||||
"mdal-edit",
|
||||
() -> FileOpener.openString(
|
||||
identifier + ".md",
|
||||
this,
|
||||
value.getValue(),
|
||||
(s) -> {
|
||||
"mdal-edit",
|
||||
() -> FileOpener.openString(identifier + ".md", this, value.getValue(), (s) -> {
|
||||
Platform.runLater(() -> value.setValue(s));
|
||||
}))
|
||||
.styleClass("edit-button")
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
package io.xpipe.app.comp.base;
|
||||
|
||||
import atlantafx.base.controls.ModalPane;
|
||||
import atlantafx.base.layout.ModalBox;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.core.AppFont;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.geometry.Insets;
|
||||
|
@ -19,6 +17,10 @@ import javafx.scene.input.KeyEvent;
|
|||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import atlantafx.base.controls.ModalPane;
|
||||
import atlantafx.base.layout.ModalBox;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import lombok.Value;
|
||||
|
||||
public class ModalOverlayComp extends SimpleComp {
|
||||
|
|
|
@ -159,7 +159,10 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
|
|||
}
|
||||
|
||||
{
|
||||
var b = new IconButtonComp("mdi2c-code-json", () -> Hyperlinks.open("http://localhost:" + AppPrefs.get().httpServerPort().getValue()))
|
||||
var b = new IconButtonComp(
|
||||
"mdi2c-code-json",
|
||||
() -> Hyperlinks.open("http://localhost:"
|
||||
+ AppPrefs.get().httpServerPort().getValue()))
|
||||
.tooltipKey("api")
|
||||
.apply(simpleBorders)
|
||||
.accessibleTextKey("api");
|
||||
|
|
|
@ -8,12 +8,14 @@ import io.xpipe.app.fxcomps.util.LabelGraphic;
|
|||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
|
@ -34,7 +36,11 @@ public class StoreToggleComp extends SimpleComp {
|
|||
private ObservableValue<Boolean> customVisibility = new SimpleBooleanProperty(true);
|
||||
|
||||
public static <T extends DataStore> StoreToggleComp simpleToggle(
|
||||
String nameKey, ObservableValue<LabelGraphic> graphic, StoreSection section, Function<T, Boolean> initial, BiConsumer<T, Boolean> setter) {
|
||||
String nameKey,
|
||||
ObservableValue<LabelGraphic> graphic,
|
||||
StoreSection section,
|
||||
Function<T, Boolean> initial,
|
||||
BiConsumer<T, Boolean> setter) {
|
||||
return new StoreToggleComp(
|
||||
nameKey,
|
||||
graphic,
|
||||
|
@ -49,8 +55,9 @@ public class StoreToggleComp extends SimpleComp {
|
|||
public static <T extends DataStore> StoreToggleComp enableToggle(
|
||||
String nameKey, StoreSection section, Function<T, Boolean> initial, BiConsumer<T, Boolean> setter) {
|
||||
var val = new SimpleBooleanProperty();
|
||||
ObservableValue<LabelGraphic> g = val.map(aBoolean -> aBoolean ?
|
||||
new LabelGraphic.IconGraphic("mdi2c-circle-slice-8") : new LabelGraphic.IconGraphic("mdi2p-power"));
|
||||
ObservableValue<LabelGraphic> g = val.map(aBoolean -> aBoolean
|
||||
? new LabelGraphic.IconGraphic("mdi2c-circle-slice-8")
|
||||
: new LabelGraphic.IconGraphic("mdi2p-power"));
|
||||
var t = new StoreToggleComp(
|
||||
nameKey,
|
||||
g,
|
||||
|
@ -68,17 +75,29 @@ public class StoreToggleComp extends SimpleComp {
|
|||
}
|
||||
|
||||
public static <T extends DataStore> StoreToggleComp childrenToggle(
|
||||
String nameKey, boolean graphic, StoreSection section, Function<T, Boolean> initial, BiConsumer<T, Boolean> setter) {
|
||||
String nameKey,
|
||||
boolean graphic,
|
||||
StoreSection section,
|
||||
Function<T, Boolean> initial,
|
||||
BiConsumer<T, Boolean> setter) {
|
||||
var val = new SimpleBooleanProperty();
|
||||
ObservableValue<LabelGraphic> g = graphic ? val.map(aBoolean -> aBoolean ?
|
||||
new LabelGraphic.IconGraphic("mdi2c-circle-slice-8") : new LabelGraphic.IconGraphic("mdi2c-circle-half-full")) : null;
|
||||
var t = new StoreToggleComp(nameKey, g, section,
|
||||
new SimpleBooleanProperty(initial.apply(section.getWrapper().getEntry().getStore().asNeeded())), v -> {
|
||||
Platform.runLater(() -> {
|
||||
setter.accept(section.getWrapper().getEntry().getStore().asNeeded(), v);
|
||||
StoreViewState.get().toggleStoreListUpdate();
|
||||
});
|
||||
});
|
||||
ObservableValue<LabelGraphic> g = graphic
|
||||
? val.map(aBoolean -> aBoolean
|
||||
? new LabelGraphic.IconGraphic("mdi2c-circle-slice-8")
|
||||
: new LabelGraphic.IconGraphic("mdi2c-circle-half-full"))
|
||||
: null;
|
||||
var t = new StoreToggleComp(
|
||||
nameKey,
|
||||
g,
|
||||
section,
|
||||
new SimpleBooleanProperty(
|
||||
initial.apply(section.getWrapper().getEntry().getStore().asNeeded())),
|
||||
v -> {
|
||||
Platform.runLater(() -> {
|
||||
setter.accept(section.getWrapper().getEntry().getStore().asNeeded(), v);
|
||||
StoreViewState.get().toggleStoreListUpdate();
|
||||
});
|
||||
});
|
||||
t.tooltipKey("showAllChildren");
|
||||
t.value.subscribe((newValue) -> {
|
||||
val.set(newValue);
|
||||
|
@ -86,7 +105,12 @@ public class StoreToggleComp extends SimpleComp {
|
|||
return t;
|
||||
}
|
||||
|
||||
public StoreToggleComp(String nameKey, ObservableValue<LabelGraphic> graphic, StoreSection section, boolean initial, Consumer<Boolean> onChange) {
|
||||
public StoreToggleComp(
|
||||
String nameKey,
|
||||
ObservableValue<LabelGraphic> graphic,
|
||||
StoreSection section,
|
||||
boolean initial,
|
||||
Consumer<Boolean> onChange) {
|
||||
this.nameKey = nameKey;
|
||||
this.graphic = graphic;
|
||||
this.section = section;
|
||||
|
@ -94,7 +118,12 @@ public class StoreToggleComp extends SimpleComp {
|
|||
this.onChange = onChange;
|
||||
}
|
||||
|
||||
public StoreToggleComp(String nameKey, ObservableValue<LabelGraphic> graphic, StoreSection section, BooleanProperty initial, Consumer<Boolean> onChange) {
|
||||
public StoreToggleComp(
|
||||
String nameKey,
|
||||
ObservableValue<LabelGraphic> graphic,
|
||||
StoreSection section,
|
||||
BooleanProperty initial,
|
||||
Consumer<Boolean> onChange) {
|
||||
this.nameKey = nameKey;
|
||||
this.graphic = graphic;
|
||||
this.section = section;
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
package io.xpipe.app.comp.base;
|
||||
|
||||
import atlantafx.base.controls.ToggleSwitch;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.fxcomps.util.LabelGraphic;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import atlantafx.base.controls.ToggleSwitch;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
|
||||
|
@ -44,8 +46,9 @@ public class ToggleSwitchComp extends SimpleComp {
|
|||
s.textProperty().bind(PlatformThread.sync(name));
|
||||
}
|
||||
if (graphic != null) {
|
||||
s.graphicProperty().bind(PlatformThread.sync(graphic.map(labelGraphic -> labelGraphic.createGraphicNode())));
|
||||
s.pseudoClassStateChanged(PseudoClass.getPseudoClass("has-graphic"),true);
|
||||
s.graphicProperty()
|
||||
.bind(PlatformThread.sync(graphic.map(labelGraphic -> labelGraphic.createGraphicNode())));
|
||||
s.pseudoClassStateChanged(PseudoClass.getPseudoClass("has-graphic"), true);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -39,8 +39,7 @@ public class StoreCreationMenu {
|
|||
"addScript", "mdi2s-script-text-outline", DataStoreProvider.CreationCategory.SCRIPT, "script"));
|
||||
|
||||
menu.getItems()
|
||||
.add(category(
|
||||
"addService", "mdi2c-cloud-braces", DataStoreProvider.CreationCategory.SERVICE, null));
|
||||
.add(category("addService", "mdi2c-cloud-braces", DataStoreProvider.CreationCategory.SERVICE, null));
|
||||
|
||||
menu.getItems()
|
||||
.add(category(
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package io.xpipe.app.comp.store;
|
||||
|
||||
import atlantafx.base.layout.InputGroup;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.comp.base.LoadingOverlayComp;
|
||||
import io.xpipe.app.core.*;
|
||||
import io.xpipe.app.ext.ActionProvider;
|
||||
|
@ -22,6 +20,7 @@ import io.xpipe.app.storage.DataStorage;
|
|||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.update.XPipeDistributionType;
|
||||
import io.xpipe.app.util.*;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.value.ObservableDoubleValue;
|
||||
|
@ -34,6 +33,9 @@ import javafx.scene.input.MouseButton;
|
|||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import atlantafx.base.layout.InputGroup;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.nio.file.Files;
|
||||
|
@ -207,9 +209,11 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
protected Region createButtonBar() {
|
||||
var list = new DerivedObservableList<>(wrapper.getActionProviders(), false);
|
||||
var buttons = list.mapped(actionProvider -> {
|
||||
var button = buildButton(actionProvider);
|
||||
return button != null ? button.createRegion() : null;
|
||||
}).filtered(region -> region != null).getList();
|
||||
var button = buildButton(actionProvider);
|
||||
return button != null ? button.createRegion() : null;
|
||||
})
|
||||
.filtered(region -> region != null)
|
||||
.getList();
|
||||
|
||||
var ig = new InputGroup();
|
||||
Runnable update = () -> {
|
||||
|
@ -236,28 +240,31 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
return null;
|
||||
}
|
||||
|
||||
var button =
|
||||
new IconButtonComp(cs.getIcon(wrapper.getEntry().ref()), leaf != null ? () -> {
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
wrapper.runAction(leaf.createAction(wrapper.getEntry().ref()), leaf.showBusy());
|
||||
});
|
||||
} : null);
|
||||
var button = new IconButtonComp(
|
||||
cs.getIcon(wrapper.getEntry().ref()),
|
||||
leaf != null
|
||||
? () -> {
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
wrapper.runAction(
|
||||
leaf.createAction(wrapper.getEntry().ref()), leaf.showBusy());
|
||||
});
|
||||
}
|
||||
: null);
|
||||
if (branch != null) {
|
||||
button.apply(new ContextMenuAugment<>(mouseEvent -> mouseEvent.getButton() == MouseButton.PRIMARY,keyEvent -> false,() -> {
|
||||
var cm = ContextMenuHelper.create();
|
||||
branch.getChildren().forEach(childProvider -> {
|
||||
var menu = buildMenuItemForAction(childProvider);
|
||||
if (menu != null) {
|
||||
cm.getItems().add(menu);
|
||||
}
|
||||
});
|
||||
return cm;
|
||||
}));
|
||||
button.apply(new ContextMenuAugment<>(
|
||||
mouseEvent -> mouseEvent.getButton() == MouseButton.PRIMARY, keyEvent -> false, () -> {
|
||||
var cm = ContextMenuHelper.create();
|
||||
branch.getChildren().forEach(childProvider -> {
|
||||
var menu = buildMenuItemForAction(childProvider);
|
||||
if (menu != null) {
|
||||
cm.getItems().add(menu);
|
||||
}
|
||||
});
|
||||
return cm;
|
||||
}));
|
||||
}
|
||||
button.accessibleText(
|
||||
cs.getName(wrapper.getEntry().ref()).getValue());
|
||||
button.apply(new TooltipAugment<>(
|
||||
cs.getName(wrapper.getEntry().ref()), null));
|
||||
button.accessibleText(cs.getName(wrapper.getEntry().ref()).getValue());
|
||||
button.apply(new TooltipAugment<>(cs.getName(wrapper.getEntry().ref()), null));
|
||||
return button;
|
||||
}
|
||||
|
||||
|
@ -284,7 +291,9 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (p.getLeafDataStoreCallSite() != null && p.getLeafDataStoreCallSite().isSystemAction() && !hasSep) {
|
||||
if (p.getLeafDataStoreCallSite() != null
|
||||
&& p.getLeafDataStoreCallSite().isSystemAction()
|
||||
&& !hasSep) {
|
||||
if (contextMenu.getItems().size() > 0) {
|
||||
contextMenu.getItems().add(new SeparatorMenuItem());
|
||||
}
|
||||
|
@ -312,8 +321,8 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
contextMenu.getItems().add(browse);
|
||||
|
||||
var copyId = new MenuItem(AppI18n.get("copyId"), new FontIcon("mdi2c-content-copy"));
|
||||
copyId.setOnAction(
|
||||
event -> ClipboardHelper.copyText(wrapper.getEntry().getUuid().toString()));
|
||||
copyId.setOnAction(event ->
|
||||
ClipboardHelper.copyText(wrapper.getEntry().getUuid().toString()));
|
||||
contextMenu.getItems().add(copyId);
|
||||
}
|
||||
|
||||
|
@ -343,12 +352,16 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
.getList()
|
||||
.forEach(storeCategoryWrapper -> {
|
||||
MenuItem m = new MenuItem();
|
||||
m.textProperty().setValue(" ".repeat(storeCategoryWrapper.getDepth()) + storeCategoryWrapper.getName().getValue());
|
||||
m.textProperty()
|
||||
.setValue(" ".repeat(storeCategoryWrapper.getDepth())
|
||||
+ storeCategoryWrapper.getName().getValue());
|
||||
m.setOnAction(event -> {
|
||||
wrapper.moveTo(storeCategoryWrapper.getCategory());
|
||||
event.consume();
|
||||
});
|
||||
if (storeCategoryWrapper.getParent() == null || storeCategoryWrapper.equals(wrapper.getCategory().getValue())) {
|
||||
if (storeCategoryWrapper.getParent() == null
|
||||
|| storeCategoryWrapper.equals(
|
||||
wrapper.getCategory().getValue())) {
|
||||
m.setDisable(true);
|
||||
}
|
||||
|
||||
|
@ -386,10 +399,17 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
section.get().getAllChildren().getList().forEach(other -> {
|
||||
var ow = other.getWrapper();
|
||||
var op = ow.getEntry().getProvider();
|
||||
MenuItem m = new MenuItem(ow.getName().getValue(),
|
||||
op != null ? PrettyImageHelper.ofFixedSizeSquare(op.getDisplayIconFileName(ow.getEntry().getStore()),
|
||||
16).createRegion() : null);
|
||||
if (other.getWrapper().equals(wrapper) || ow.getEntry().getUuid().equals(wrapper.getEntry().getOrderBefore())) {
|
||||
MenuItem m = new MenuItem(
|
||||
ow.getName().getValue(),
|
||||
op != null
|
||||
? PrettyImageHelper.ofFixedSizeSquare(
|
||||
op.getDisplayIconFileName(
|
||||
ow.getEntry().getStore()),
|
||||
16)
|
||||
.createRegion()
|
||||
: null);
|
||||
if (other.getWrapper().equals(wrapper)
|
||||
|| ow.getEntry().getUuid().equals(wrapper.getEntry().getOrderBefore())) {
|
||||
m.setDisable(true);
|
||||
}
|
||||
m.setOnAction(event -> {
|
||||
|
@ -436,9 +456,7 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
: new MenuItem(null, new FontIcon(icon));
|
||||
|
||||
var proRequired = p.getProFeatureId() != null
|
||||
&& !LicenseProvider.get()
|
||||
.getFeature(p.getProFeatureId())
|
||||
.isSupported();
|
||||
&& !LicenseProvider.get().getFeature(p.getProFeatureId()).isSupported();
|
||||
if (proRequired) {
|
||||
item.setDisable(true);
|
||||
item.textProperty().bind(Bindings.createStringBinding(() -> name.getValue() + " (Pro)", name));
|
||||
|
@ -448,7 +466,9 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
Menu menu = item instanceof Menu m ? m : null;
|
||||
|
||||
if (branch != null) {
|
||||
var items = branch.getChildren().stream().map(c -> buildMenuItemForAction(c)).toList();
|
||||
var items = branch.getChildren().stream()
|
||||
.map(c -> buildMenuItemForAction(c))
|
||||
.toList();
|
||||
menu.getItems().addAll(items);
|
||||
return menu;
|
||||
} else if (leaf.canLinkTo()) {
|
||||
|
@ -462,18 +482,16 @@ public abstract class StoreEntryComp extends SimpleComp {
|
|||
menu.getItems().add(run);
|
||||
|
||||
var sc = new MenuItem(null, new FontIcon("mdi2c-code-greater-than"));
|
||||
var url = "xpipe://action/" + p.getId() + "/"
|
||||
+ wrapper.getEntry().getUuid();
|
||||
var url = "xpipe://action/" + p.getId() + "/" + wrapper.getEntry().getUuid();
|
||||
sc.textProperty().bind(AppI18n.observable("base.createShortcut"));
|
||||
sc.setOnAction(event -> {
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
DesktopShortcuts.create(
|
||||
url,
|
||||
wrapper.nameProperty().getValue() + " ("
|
||||
+ p
|
||||
.getLeafDataStoreCallSite()
|
||||
.getName(wrapper.getEntry().ref())
|
||||
.getValue() + ")");
|
||||
+ p.getLeafDataStoreCallSite()
|
||||
.getName(wrapper.getEntry().ref())
|
||||
.getValue() + ")");
|
||||
});
|
||||
});
|
||||
menu.getItems().add(sc);
|
||||
|
|
|
@ -18,8 +18,14 @@ public class StoreEntryListComp extends SimpleComp {
|
|||
|
||||
private Comp<?> createList() {
|
||||
var content = new ListBoxViewComp<>(
|
||||
StoreViewState.get().getCurrentTopLevelSection().getShownChildren().getList(),
|
||||
StoreViewState.get().getCurrentTopLevelSection().getAllChildren().getList(),
|
||||
StoreViewState.get()
|
||||
.getCurrentTopLevelSection()
|
||||
.getShownChildren()
|
||||
.getList(),
|
||||
StoreViewState.get()
|
||||
.getCurrentTopLevelSection()
|
||||
.getAllChildren()
|
||||
.getList(),
|
||||
(StoreSection e) -> {
|
||||
var custom = StoreSection.customSection(e, true).hgrow();
|
||||
return new HorizontalComp(List.of(Comp.hspacer(8), custom, Comp.hspacer(10)))
|
||||
|
@ -50,16 +56,21 @@ public class StoreEntryListComp extends SimpleComp {
|
|||
var map = new LinkedHashMap<Comp<?>, ObservableValue<Boolean>>();
|
||||
map.put(
|
||||
createList(),
|
||||
Bindings.not(Bindings.isEmpty(
|
||||
StoreViewState.get().getCurrentTopLevelSection().getShownChildren().getList())));
|
||||
Bindings.not(Bindings.isEmpty(StoreViewState.get()
|
||||
.getCurrentTopLevelSection()
|
||||
.getShownChildren()
|
||||
.getList())));
|
||||
|
||||
map.put(new StoreIntroComp(), showIntro);
|
||||
map.put(
|
||||
new StoreNotFoundComp(),
|
||||
Bindings.and(
|
||||
Bindings.not(Bindings.isEmpty(StoreViewState.get().getAllEntries().getList())),
|
||||
Bindings.isEmpty(
|
||||
StoreViewState.get().getCurrentTopLevelSection().getShownChildren().getList())));
|
||||
Bindings.not(Bindings.isEmpty(
|
||||
StoreViewState.get().getAllEntries().getList())),
|
||||
Bindings.isEmpty(StoreViewState.get()
|
||||
.getCurrentTopLevelSection()
|
||||
.getShownChildren()
|
||||
.getList())));
|
||||
return new MultiContentComp(map).createRegion();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
|||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
@ -23,6 +24,7 @@ import javafx.scene.layout.Priority;
|
|||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.TextAlignment;
|
||||
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
public class StoreEntryListStatusComp extends SimpleComp {
|
||||
|
@ -55,17 +57,28 @@ public class StoreEntryListStatusComp extends SimpleComp {
|
|||
label.textProperty().bind(name);
|
||||
label.getStyleClass().add("name");
|
||||
|
||||
var all = StoreViewState.get().getAllEntries().filtered(
|
||||
storeEntryWrapper -> {
|
||||
var rootCategory = storeEntryWrapper.getCategory().getValue().getRoot();
|
||||
var inRootCategory = StoreViewState.get().getActiveCategory().getValue().getRoot().equals(rootCategory);
|
||||
// Sadly the all binding does not update when the individual visibility of entries changes
|
||||
// But it is good enough.
|
||||
var showProvider = !storeEntryWrapper.getEntry().getValidity().isUsable() ||
|
||||
storeEntryWrapper.getEntry().getProvider().shouldShow(storeEntryWrapper);
|
||||
return inRootCategory && showProvider;
|
||||
},
|
||||
StoreViewState.get().getActiveCategory());
|
||||
var all = StoreViewState.get()
|
||||
.getAllEntries()
|
||||
.filtered(
|
||||
storeEntryWrapper -> {
|
||||
var rootCategory =
|
||||
storeEntryWrapper.getCategory().getValue().getRoot();
|
||||
var inRootCategory = StoreViewState.get()
|
||||
.getActiveCategory()
|
||||
.getValue()
|
||||
.getRoot()
|
||||
.equals(rootCategory);
|
||||
// Sadly the all binding does not update when the individual visibility of entries changes
|
||||
// But it is good enough.
|
||||
var showProvider =
|
||||
!storeEntryWrapper.getEntry().getValidity().isUsable()
|
||||
|| storeEntryWrapper
|
||||
.getEntry()
|
||||
.getProvider()
|
||||
.shouldShow(storeEntryWrapper);
|
||||
return inRootCategory && showProvider;
|
||||
},
|
||||
StoreViewState.get().getActiveCategory());
|
||||
var shownList = all.filtered(
|
||||
storeEntryWrapper -> {
|
||||
return storeEntryWrapper.matchesFilter(
|
||||
|
|
|
@ -9,8 +9,10 @@ import io.xpipe.app.storage.DataStoreCategory;
|
|||
import io.xpipe.app.storage.DataStoreColor;
|
||||
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;
|
||||
|
@ -26,7 +28,8 @@ public class StoreEntryWrapper {
|
|||
private final BooleanProperty disabled = new SimpleBooleanProperty();
|
||||
private final BooleanProperty busy = new SimpleBooleanProperty();
|
||||
private final Property<DataStoreEntry.Validity> validity = new SimpleObjectProperty<>();
|
||||
private final ListProperty<ActionProvider> actionProviders = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||
private final ListProperty<ActionProvider> actionProviders =
|
||||
new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||
private final Property<ActionProvider> defaultActionProvider = new SimpleObjectProperty<>();
|
||||
private final BooleanProperty deletable = new SimpleBooleanProperty();
|
||||
private final BooleanProperty expanded = new SimpleBooleanProperty();
|
||||
|
@ -50,8 +53,8 @@ public class StoreEntryWrapper {
|
|||
.getApplicableClass()
|
||||
.isAssignableFrom(entry.getStore().getClass());
|
||||
})
|
||||
.sorted(Comparator.comparing(
|
||||
actionProvider -> actionProvider.getLeafDataStoreCallSite().isSystemAction()))
|
||||
.sorted(Comparator.comparing(actionProvider ->
|
||||
actionProvider.getLeafDataStoreCallSite().isSystemAction()))
|
||||
.forEach(dataStoreActionProvider -> {
|
||||
actionProviders.add(dataStoreActionProvider);
|
||||
});
|
||||
|
@ -67,7 +70,7 @@ public class StoreEntryWrapper {
|
|||
|
||||
public void orderBefore(StoreEntryWrapper other) {
|
||||
ThreadHelper.runAsync(() -> {
|
||||
DataStorage.get().orderBefore(getEntry(),other.getEntry());
|
||||
DataStorage.get().orderBefore(getEntry(), other.getEntry());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -155,10 +158,11 @@ public class StoreEntryWrapper {
|
|||
defaultActionProvider.setValue(null);
|
||||
} else {
|
||||
var defaultProvider = ActionProvider.ALL.stream()
|
||||
.filter(e -> entry.getStore() != null && e.getDefaultDataStoreCallSite() != null
|
||||
.filter(e -> entry.getStore() != null
|
||||
&& e.getDefaultDataStoreCallSite() != null
|
||||
&& e.getDefaultDataStoreCallSite()
|
||||
.getApplicableClass()
|
||||
.isAssignableFrom(entry.getStore().getClass())
|
||||
.getApplicableClass()
|
||||
.isAssignableFrom(entry.getStore().getClass())
|
||||
&& e.getDefaultDataStoreCallSite().isApplicable(entry.ref()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
@ -167,11 +171,10 @@ public class StoreEntryWrapper {
|
|||
try {
|
||||
var newProviders = ActionProvider.ALL.stream()
|
||||
.filter(dataStoreActionProvider -> {
|
||||
return showActionProvider(dataStoreActionProvider);
|
||||
return showActionProvider(dataStoreActionProvider);
|
||||
})
|
||||
.sorted(Comparator.comparing(
|
||||
actionProvider -> actionProvider.getLeafDataStoreCallSite() != null &&
|
||||
actionProvider.getLeafDataStoreCallSite().isSystemAction()))
|
||||
.sorted(Comparator.comparing(actionProvider -> actionProvider.getLeafDataStoreCallSite() != null
|
||||
&& actionProvider.getLeafDataStoreCallSite().isSystemAction()))
|
||||
.toList();
|
||||
if (!actionProviders.equals(newProviders)) {
|
||||
actionProviders.setAll(newProviders);
|
||||
|
@ -186,14 +189,15 @@ public class StoreEntryWrapper {
|
|||
var leaf = p.getLeafDataStoreCallSite();
|
||||
if (leaf != null) {
|
||||
return (entry.getValidity().isUsable() || (!leaf.requiresValidStore() && entry.getProvider() != null))
|
||||
&& leaf.getApplicableClass().isAssignableFrom(entry.getStore().getClass())
|
||||
&& leaf
|
||||
.isApplicable(entry.ref());
|
||||
&& leaf.getApplicableClass()
|
||||
.isAssignableFrom(entry.getStore().getClass())
|
||||
&& leaf.isApplicable(entry.ref());
|
||||
}
|
||||
|
||||
|
||||
var branch = p.getBranchDataStoreCallSite();
|
||||
if (branch != null && entry.getStore() != null && branch.getApplicableClass().isAssignableFrom(entry.getStore().getClass())) {
|
||||
if (branch != null
|
||||
&& entry.getStore() != null
|
||||
&& branch.getApplicableClass().isAssignableFrom(entry.getStore().getClass())) {
|
||||
return branch.getChildren().stream().anyMatch(child -> {
|
||||
return showActionProvider(child);
|
||||
});
|
||||
|
@ -223,7 +227,7 @@ public class StoreEntryWrapper {
|
|||
entry.notifyUpdate(true, false);
|
||||
if (found != null) {
|
||||
var act = found.getDefaultDataStoreCallSite().createAction(entry.ref());
|
||||
runAction(act,found.getDefaultDataStoreCallSite().showBusy());
|
||||
runAction(act, found.getDefaultDataStoreCallSite().showBusy());
|
||||
} else {
|
||||
entry.setExpanded(!entry.isExpanded());
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.xpipe.app.comp.store;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.core.AppFont;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
|
@ -8,6 +7,7 @@ import io.xpipe.app.fxcomps.impl.PrettySvgComp;
|
|||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.ScanAlert;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
|
@ -17,6 +17,8 @@ import javafx.scene.layout.HBox;
|
|||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
public class StoreIntroComp extends SimpleComp {
|
||||
|
@ -58,7 +60,6 @@ public class StoreIntroComp extends SimpleComp {
|
|||
return v;
|
||||
}
|
||||
|
||||
|
||||
private Region createImportIntro() {
|
||||
var title = new Label();
|
||||
title.textProperty().bind(AppI18n.observable("importConnectionsTitle"));
|
||||
|
|
|
@ -4,8 +4,8 @@ import io.xpipe.app.comp.base.SideSplitPaneComp;
|
|||
import io.xpipe.app.core.AppActionLinkDetector;
|
||||
import io.xpipe.app.core.AppLayoutModel;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
|
||||
import io.xpipe.app.util.InputHelper;
|
||||
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
|
@ -24,10 +24,11 @@ public class StoreLayoutComp extends SimpleComp {
|
|||
struc.getLeft().setMinWidth(260);
|
||||
struc.getLeft().setMaxWidth(500);
|
||||
struc.get().getStyleClass().add("store-layout");
|
||||
InputHelper.onKeyCombination(struc.get(),new KeyCodeCombination(KeyCode.V, KeyCombination.SHORTCUT_DOWN), true, keyEvent -> {
|
||||
AppActionLinkDetector.detectOnPaste();
|
||||
keyEvent.consume();
|
||||
});
|
||||
InputHelper.onKeyCombination(
|
||||
struc.get(), new KeyCodeCombination(KeyCode.V, KeyCombination.SHORTCUT_DOWN), true, keyEvent -> {
|
||||
AppActionLinkDetector.detectOnPaste();
|
||||
keyEvent.consume();
|
||||
});
|
||||
return struc.get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.xpipe.app.comp.store;
|
||||
|
||||
import atlantafx.base.controls.Popover;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.DialogComp;
|
||||
import io.xpipe.app.comp.base.MarkdownEditorComp;
|
||||
|
@ -11,12 +10,15 @@ import io.xpipe.app.fxcomps.CompStructure;
|
|||
import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.scene.control.Button;
|
||||
|
||||
import atlantafx.base.controls.Popover;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
|
@ -24,7 +26,9 @@ public class StoreNotesComp extends Comp<StoreNotesComp.Structure> {
|
|||
|
||||
private final StoreEntryWrapper wrapper;
|
||||
|
||||
public StoreNotesComp(StoreEntryWrapper wrapper) {this.wrapper = wrapper;}
|
||||
public StoreNotesComp(StoreEntryWrapper wrapper) {
|
||||
this.wrapper = wrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Structure createBase() {
|
||||
|
@ -36,7 +40,8 @@ public class StoreNotesComp extends Comp<StoreNotesComp.Structure> {
|
|||
.styleClass("notes-button")
|
||||
.grow(false, true)
|
||||
.hide(BindingsHelper.map(n, s -> s.getCommited() == null && s.getCurrent() == null))
|
||||
.createStructure().get();
|
||||
.createStructure()
|
||||
.get();
|
||||
button.prefWidthProperty().bind(button.heightProperty());
|
||||
|
||||
var prop = new SimpleStringProperty(n.getValue().getCurrent());
|
||||
|
@ -73,12 +78,16 @@ public class StoreNotesComp extends Comp<StoreNotesComp.Structure> {
|
|||
|
||||
private Popover createPopover(AtomicReference<Popover> ref, Property<String> prop) {
|
||||
var n = wrapper.getNotes();
|
||||
var md = new MarkdownEditorComp(prop, "notes-" + wrapper.getName().getValue()).prefWidth(600).prefHeight(600).createStructure();
|
||||
var md = new MarkdownEditorComp(prop, "notes-" + wrapper.getName().getValue())
|
||||
.prefWidth(600)
|
||||
.prefHeight(600)
|
||||
.createStructure();
|
||||
var dialog = new DialogComp() {
|
||||
|
||||
@Override
|
||||
protected void finish() {
|
||||
n.setValue(new StoreNotes(n.getValue().getCurrent(), n.getValue().getCurrent()));
|
||||
n.setValue(
|
||||
new StoreNotes(n.getValue().getCurrent(), n.getValue().getCurrent()));
|
||||
ref.get().hide();
|
||||
}
|
||||
|
||||
|
@ -90,8 +99,9 @@ public class StoreNotesComp extends Comp<StoreNotesComp.Structure> {
|
|||
@Override
|
||||
public Comp<?> bottom() {
|
||||
return new ButtonComp(AppI18n.observable("delete"), () -> {
|
||||
n.setValue(new StoreNotes(null, null));
|
||||
}).hide(BindingsHelper.map(n, v -> v.getCommited() == null));
|
||||
n.setValue(new StoreNotes(null, null));
|
||||
})
|
||||
.hide(BindingsHelper.map(n, v -> v.getCommited() == null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -114,7 +124,8 @@ public class StoreNotesComp extends Comp<StoreNotesComp.Structure> {
|
|||
popover.setTitle(wrapper.getName().getValue());
|
||||
popover.showingProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue) {
|
||||
n.setValue(new StoreNotes(n.getValue().getCommited(), n.getValue().getCommited()));
|
||||
n.setValue(
|
||||
new StoreNotes(n.getValue().getCommited(), n.getValue().getCommited()));
|
||||
DataStorage.get().saveAsync();
|
||||
ref.set(null);
|
||||
}
|
||||
|
@ -136,7 +147,6 @@ public class StoreNotesComp extends Comp<StoreNotesComp.Structure> {
|
|||
return popover;
|
||||
}
|
||||
|
||||
|
||||
public record Structure(Popover popover, Button button) implements CompStructure<Button> {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,12 +6,14 @@ import io.xpipe.app.fxcomps.util.DerivedObservableList;
|
|||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
import javafx.beans.value.ObservableStringValue;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -39,7 +41,8 @@ public class StoreSection {
|
|||
if (wrapper != null) {
|
||||
this.showDetails = Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
return wrapper.getExpanded().get() || allChildren.getList().isEmpty();
|
||||
return wrapper.getExpanded().get()
|
||||
|| allChildren.getList().isEmpty();
|
||||
},
|
||||
wrapper.getExpanded(),
|
||||
allChildren.getList());
|
||||
|
@ -85,7 +88,12 @@ public class StoreSection {
|
|||
}
|
||||
seen.add(wrapper);
|
||||
|
||||
var found = list.getList().stream().filter(section -> section.getWrapper().getEntry().getUuid().equals(wrapper.getEntry().getOrderBefore())).findFirst();
|
||||
var found = list.getList().stream()
|
||||
.filter(section -> section.getWrapper()
|
||||
.getEntry()
|
||||
.getUuid()
|
||||
.equals(wrapper.getEntry().getOrderBefore()))
|
||||
.findFirst();
|
||||
if (found.isPresent()) {
|
||||
return count(found.get().getWrapper(), seen);
|
||||
} else {
|
||||
|
@ -99,7 +107,8 @@ public class StoreSection {
|
|||
var mappedSortMode = BindingsHelper.flatMap(
|
||||
category,
|
||||
storeCategoryWrapper -> storeCategoryWrapper != null ? storeCategoryWrapper.getSortMode() : null);
|
||||
return list.sorted((o1, o2) -> {
|
||||
return list.sorted(
|
||||
(o1, o2) -> {
|
||||
var current = mappedSortMode.getValue();
|
||||
if (current != null) {
|
||||
return comp.thenComparing(current.comparator())
|
||||
|
@ -117,7 +126,8 @@ public class StoreSection {
|
|||
Predicate<StoreEntryWrapper> entryFilter,
|
||||
ObservableStringValue filterString,
|
||||
ObservableValue<StoreCategoryWrapper> category) {
|
||||
var topLevel = all.filtered(section -> {
|
||||
var topLevel = all.filtered(
|
||||
section -> {
|
||||
return DataStorage.get().isRootEntry(section.getEntry());
|
||||
},
|
||||
category,
|
||||
|
@ -128,11 +138,15 @@ public class StoreSection {
|
|||
var shown = ordered.filtered(
|
||||
section -> {
|
||||
// matches filter
|
||||
return (filterString == null || section.matchesFilter(filterString.get())) &&
|
||||
return (filterString == null || section.matchesFilter(filterString.get()))
|
||||
&&
|
||||
// matches selector
|
||||
(section.anyMatches(entryFilter)) &&
|
||||
(section.anyMatches(entryFilter))
|
||||
&&
|
||||
// same category
|
||||
(category == null || category.getValue() == null || showInCategory(category.getValue(), section.getWrapper()));
|
||||
(category == null
|
||||
|| category.getValue() == null
|
||||
|| showInCategory(category.getValue(), section.getWrapper()));
|
||||
},
|
||||
category,
|
||||
filterString);
|
||||
|
@ -148,23 +162,31 @@ public class StoreSection {
|
|||
ObservableStringValue filterString,
|
||||
ObservableValue<StoreCategoryWrapper> category) {
|
||||
if (e.getEntry().getValidity() == DataStoreEntry.Validity.LOAD_FAILED) {
|
||||
return new StoreSection(e, new DerivedObservableList<>(
|
||||
FXCollections.observableArrayList(), true), new DerivedObservableList<>(
|
||||
FXCollections.observableArrayList(), true), depth);
|
||||
return new StoreSection(
|
||||
e,
|
||||
new DerivedObservableList<>(FXCollections.observableArrayList(), true),
|
||||
new DerivedObservableList<>(FXCollections.observableArrayList(), true),
|
||||
depth);
|
||||
}
|
||||
|
||||
var allChildren = all.filtered(other -> {
|
||||
// Legacy implementation that does not use children caches. Use for testing
|
||||
// if (true) return DataStorage.get()
|
||||
// .getDisplayParent(other.getEntry())
|
||||
// .map(found -> found.equals(e.getEntry()))
|
||||
// .orElse(false);
|
||||
var allChildren = all.filtered(
|
||||
other -> {
|
||||
// Legacy implementation that does not use children caches. Use for testing
|
||||
// if (true) return DataStorage.get()
|
||||
// .getDisplayParent(other.getEntry())
|
||||
// .map(found -> found.equals(e.getEntry()))
|
||||
// .orElse(false);
|
||||
|
||||
// is children. This check is fast as the children are cached in the storage
|
||||
return DataStorage.get().getStoreChildren(e.getEntry()).contains(other.getEntry()) &&
|
||||
// show provider
|
||||
(!other.getEntry().getValidity().isUsable() || other.getEntry().getProvider().shouldShow(other));
|
||||
}, e.getPersistentState(), e.getCache(), StoreViewState.get().getEntriesListChangeObservable());
|
||||
// is children. This check is fast as the children are cached in the storage
|
||||
return DataStorage.get().getStoreChildren(e.getEntry()).contains(other.getEntry())
|
||||
&&
|
||||
// show provider
|
||||
(!other.getEntry().getValidity().isUsable()
|
||||
|| other.getEntry().getProvider().shouldShow(other));
|
||||
},
|
||||
e.getPersistentState(),
|
||||
e.getCache(),
|
||||
StoreViewState.get().getEntriesListChangeObservable());
|
||||
var l = new ArrayList<>(parents);
|
||||
l.add(e);
|
||||
var cached = allChildren.mapped(c -> create(l, c, depth + 1, all, entryFilter, filterString, category));
|
||||
|
@ -172,14 +194,23 @@ public class StoreSection {
|
|||
var filtered = ordered.filtered(
|
||||
section -> {
|
||||
// matches filter
|
||||
return (filterString == null || section.matchesFilter(filterString.get()) || l.stream().anyMatch(p -> p.matchesFilter(filterString.get()))) &&
|
||||
return (filterString == null
|
||||
|| section.matchesFilter(filterString.get())
|
||||
|| l.stream().anyMatch(p -> p.matchesFilter(filterString.get())))
|
||||
&&
|
||||
// matches selector
|
||||
section.anyMatches(entryFilter) &&
|
||||
section.anyMatches(entryFilter)
|
||||
&&
|
||||
// matches category
|
||||
// Prevent updates for children on category switching by checking depth
|
||||
(category == null || category.getValue() == null || showInCategory(category.getValue(), section.getWrapper()) || depth > 0) &&
|
||||
(category == null
|
||||
|| category.getValue() == null
|
||||
|| showInCategory(category.getValue(), section.getWrapper())
|
||||
|| depth > 0)
|
||||
&&
|
||||
// not root
|
||||
// If this entry is already shown as root due to a different category than parent, don't show it
|
||||
// If this entry is already shown as root due to a different category than parent, don't
|
||||
// show it
|
||||
// again here
|
||||
!DataStorage.get().isRootEntry(section.getWrapper().getEntry());
|
||||
},
|
||||
|
|
|
@ -9,6 +9,7 @@ import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
|||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.scene.control.Button;
|
||||
|
@ -126,14 +127,16 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
|
||||
// Optimization for large sections. If there are more than 20 children, only add the nodes to the scene if the
|
||||
// section is actually expanded
|
||||
var listSections = section.getShownChildren().filtered(
|
||||
storeSection -> section.getAllChildren().getList().size() <= 20
|
||||
|| section.getWrapper().getExpanded().get(),
|
||||
section.getWrapper().getExpanded(),
|
||||
section.getAllChildren().getList());
|
||||
var content = new ListBoxViewComp<>(listSections.getList(), section.getAllChildren().getList(), (StoreSection e) -> {
|
||||
return StoreSection.customSection(e, false).apply(GrowAugment.create(true, false));
|
||||
})
|
||||
var listSections = section.getShownChildren()
|
||||
.filtered(
|
||||
storeSection -> section.getAllChildren().getList().size() <= 20
|
||||
|| section.getWrapper().getExpanded().get(),
|
||||
section.getWrapper().getExpanded(),
|
||||
section.getAllChildren().getList());
|
||||
var content = new ListBoxViewComp<>(
|
||||
listSections.getList(), section.getAllChildren().getList(), (StoreSection e) -> {
|
||||
return StoreSection.customSection(e, false).apply(GrowAugment.create(true, false));
|
||||
})
|
||||
.minHeight(0)
|
||||
.hgrow();
|
||||
|
||||
|
@ -152,7 +155,8 @@ public class StoreSectionComp extends Comp<CompStructure<VBox>> {
|
|||
.apply(struc -> struc.get().setFillHeight(true))
|
||||
.hide(Bindings.or(
|
||||
Bindings.not(section.getWrapper().getExpanded()),
|
||||
Bindings.size(section.getShownChildren().getList()).isEqualTo(0)))));
|
||||
Bindings.size(section.getShownChildren().getList())
|
||||
.isEqualTo(0)))));
|
||||
return full.styleClass("store-entry-section-comp")
|
||||
.apply(struc -> {
|
||||
struc.get().setFillWidth(true);
|
||||
|
|
|
@ -9,6 +9,7 @@ import io.xpipe.app.fxcomps.impl.IconButtonComp;
|
|||
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
|
||||
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||
import io.xpipe.app.storage.DataStoreColor;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
@ -129,14 +130,17 @@ public class StoreSectionMiniComp extends Comp<CompStructure<VBox>> {
|
|||
// Optimization for large sections. If there are more than 20 children, only add the nodes to the scene if the
|
||||
// section is actually expanded
|
||||
var listSections = section.getWrapper() != null
|
||||
? section.getShownChildren().filtered(
|
||||
storeSection -> section.getAllChildren().getList().size() <= 20 || expanded.get(),
|
||||
expanded,
|
||||
section.getAllChildren().getList())
|
||||
? section.getShownChildren()
|
||||
.filtered(
|
||||
storeSection ->
|
||||
section.getAllChildren().getList().size() <= 20 || expanded.get(),
|
||||
expanded,
|
||||
section.getAllChildren().getList())
|
||||
: section.getShownChildren();
|
||||
var content = new ListBoxViewComp<>(listSections.getList(), section.getAllChildren().getList(), (StoreSection e) -> {
|
||||
return new StoreSectionMiniComp(e, this.augment, this.action, this.condensedStyle);
|
||||
})
|
||||
var content = new ListBoxViewComp<>(
|
||||
listSections.getList(), section.getAllChildren().getList(), (StoreSection e) -> {
|
||||
return new StoreSectionMiniComp(e, this.augment, this.action, this.condensedStyle);
|
||||
})
|
||||
.minHeight(0)
|
||||
.hgrow();
|
||||
|
||||
|
|
|
@ -9,9 +9,11 @@ import io.xpipe.app.storage.DataStorage;
|
|||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.storage.StorageListener;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.collections.FXCollections;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -86,22 +88,26 @@ public class StoreViewState {
|
|||
currentTopLevelSection =
|
||||
StoreSection.createTopLevel(allEntries, storeEntryWrapper -> true, filter, activeCategory);
|
||||
} catch (Exception exception) {
|
||||
currentTopLevelSection =
|
||||
new StoreSection(null,
|
||||
new DerivedObservableList<>(FXCollections.observableArrayList(), true),
|
||||
new DerivedObservableList<>(FXCollections.observableArrayList(), true),
|
||||
0);
|
||||
currentTopLevelSection = new StoreSection(
|
||||
null,
|
||||
new DerivedObservableList<>(FXCollections.observableArrayList(), true),
|
||||
new DerivedObservableList<>(FXCollections.observableArrayList(), true),
|
||||
0);
|
||||
ErrorEvent.fromThrowable(exception).handle();
|
||||
}
|
||||
}
|
||||
|
||||
private void initContent() {
|
||||
allEntries.getList().setAll(FXCollections.observableArrayList(DataStorage.get().getStoreEntries().stream()
|
||||
.map(StoreEntryWrapper::new)
|
||||
.toList()));
|
||||
categories.getList().setAll(FXCollections.observableArrayList(DataStorage.get().getStoreCategories().stream()
|
||||
.map(StoreCategoryWrapper::new)
|
||||
.toList()));
|
||||
allEntries
|
||||
.getList()
|
||||
.setAll(FXCollections.observableArrayList(DataStorage.get().getStoreEntries().stream()
|
||||
.map(StoreEntryWrapper::new)
|
||||
.toList()));
|
||||
categories
|
||||
.getList()
|
||||
.setAll(FXCollections.observableArrayList(DataStorage.get().getStoreCategories().stream()
|
||||
.map(StoreCategoryWrapper::new)
|
||||
.toList()));
|
||||
|
||||
activeCategory.addListener((observable, oldValue, newValue) -> {
|
||||
DataStorage.get().setSelectedCategory(newValue.getCategory());
|
||||
|
@ -274,12 +280,16 @@ public class StoreViewState {
|
|||
public Optional<StoreSection> getParentSectionForWrapper(StoreEntryWrapper wrapper) {
|
||||
StoreSection current = getCurrentTopLevelSection();
|
||||
while (true) {
|
||||
var child = current.getAllChildren().getList().stream().filter(section -> section.getWrapper().equals(wrapper)).findFirst();
|
||||
var child = current.getAllChildren().getList().stream()
|
||||
.filter(section -> section.getWrapper().equals(wrapper))
|
||||
.findFirst();
|
||||
if (child.isPresent()) {
|
||||
return Optional.of(current);
|
||||
}
|
||||
|
||||
var traverse = current.getAllChildren().getList().stream().filter(section -> section.anyMatches(w -> w.equals(wrapper))).findFirst();
|
||||
var traverse = current.getAllChildren().getList().stream()
|
||||
.filter(section -> section.anyMatches(w -> w.equals(wrapper)))
|
||||
.findFirst();
|
||||
if (traverse.isPresent()) {
|
||||
current = traverse.get();
|
||||
} else {
|
||||
|
@ -339,7 +349,9 @@ public class StoreViewState {
|
|||
.compareToIgnoreCase(o2.nameProperty().getValue());
|
||||
}
|
||||
};
|
||||
return categories.filtered(cat -> root == null || cat.getRoot().equals(root)).sorted(comparator);
|
||||
return categories
|
||||
.filtered(cat -> root == null || cat.getRoot().equals(root))
|
||||
.sorted(comparator);
|
||||
}
|
||||
|
||||
public StoreCategoryWrapper getAllConnectionsCategory() {
|
||||
|
|
|
@ -6,9 +6,11 @@ import io.xpipe.app.issue.TrackEvent;
|
|||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.update.XPipeDistributionType;
|
||||
import io.xpipe.app.util.LicenseProvider;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
|
|
|
@ -5,11 +5,13 @@ import io.xpipe.app.launcher.LauncherInput;
|
|||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.DataFormat;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AppActionLinkDetector {
|
||||
|
||||
@Setter
|
||||
private static String lastDetectedAction;
|
||||
|
||||
private static String getClipboardAction() {
|
||||
|
@ -34,10 +36,6 @@ public class AppActionLinkDetector {
|
|||
LauncherInput.handle(List.of(content));
|
||||
}
|
||||
|
||||
public static void setLastDetectedAction(String s) {
|
||||
lastDetectedAction = s;
|
||||
}
|
||||
|
||||
public static void detectOnFocus() {
|
||||
var content = getClipboardAction();
|
||||
if (content == null) {
|
||||
|
|
|
@ -9,10 +9,10 @@ import io.xpipe.app.util.PlatformState;
|
|||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.desktop.*;
|
||||
import java.util.List;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class AppDesktopIntegration {
|
||||
|
||||
|
@ -26,10 +26,10 @@ public class AppDesktopIntegration {
|
|||
Desktop.getDesktop().addAppEventListener(new SystemSleepListener() {
|
||||
@Override
|
||||
public void systemAboutToSleep(SystemSleepEvent e) {
|
||||
if (AppPrefs.get() != null &&
|
||||
AppPrefs.get().lockVaultOnHibernation().get() &&
|
||||
AppPrefs.get().getLockCrypt().get() != null &&
|
||||
!AppPrefs.get().getLockCrypt().get().isBlank()) {
|
||||
if (AppPrefs.get() != null
|
||||
&& AppPrefs.get().lockVaultOnHibernation().get()
|
||||
&& AppPrefs.get().getLockCrypt().get() != null
|
||||
&& !AppPrefs.get().getLockCrypt().get().isBlank()) {
|
||||
// If we run this at the same time as the system is sleeping, there might be exceptions
|
||||
// because the platform does not like being shut down while sleeping
|
||||
// This hopefully assures that it will be run later, probably on system wake
|
||||
|
@ -70,7 +70,9 @@ public class AppDesktopIntegration {
|
|||
|
||||
// Set dock icon explicitly on mac
|
||||
// This is necessary in case XPipe was started through a script as it will have no icon otherwise
|
||||
if (AppProperties.get().isDeveloperMode() && AppLogs.get().isWriteToSysout() && Taskbar.isTaskbarSupported()) {
|
||||
if (AppProperties.get().isDeveloperMode()
|
||||
&& AppLogs.get().isWriteToSysout()
|
||||
&& Taskbar.isTaskbarSupported()) {
|
||||
try {
|
||||
var iconUrl = Main.class.getResourceAsStream("resources/img/logo/padded/logo_128x128.png");
|
||||
if (iconUrl != null) {
|
||||
|
|
|
@ -88,7 +88,8 @@ public class AppExtensionManager {
|
|||
}
|
||||
|
||||
if (!AppProperties.get().isFullVersion()) {
|
||||
var localInstallation = XPipeInstallation.getLocalDefaultInstallationBasePath(AppProperties.get().isStaging() || AppProperties.get().isLocatePtb());
|
||||
var localInstallation = XPipeInstallation.getLocalDefaultInstallationBasePath(
|
||||
AppProperties.get().isStaging() || AppProperties.get().isLocatePtb());
|
||||
Path p = Path.of(localInstallation);
|
||||
if (!Files.exists(p)) {
|
||||
throw new IllegalStateException(
|
||||
|
|
|
@ -10,10 +10,12 @@ import io.xpipe.app.util.OptionsBuilder;
|
|||
import io.xpipe.app.util.Translatable;
|
||||
import io.xpipe.core.util.ModuleHelper;
|
||||
import io.xpipe.core.util.XPipeInstallation;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.Value;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
|
|
@ -19,14 +19,13 @@ import lombok.extern.jackson.Jacksonized;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class AppLayoutModel {
|
||||
|
||||
private static AppLayoutModel INSTANCE;
|
||||
|
||||
@Getter
|
||||
private final SavedState savedState;
|
||||
|
||||
@Getter
|
||||
private final List<Entry> entries;
|
||||
|
||||
private final Property<Entry> selected;
|
||||
|
@ -51,10 +50,6 @@ public class AppLayoutModel {
|
|||
INSTANCE = null;
|
||||
}
|
||||
|
||||
public Property<Entry> getSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void selectBrowser() {
|
||||
selected.setValue(entries.getFirst());
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import io.xpipe.app.prefs.AppPrefs;
|
|||
import io.xpipe.app.prefs.CloseBehaviourAlert;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.geometry.Rectangle2D;
|
||||
|
@ -16,17 +17,18 @@ import javafx.scene.input.KeyEvent;
|
|||
import javafx.scene.layout.Region;
|
||||
import javafx.stage.Screen;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class AppMainWindow {
|
||||
|
||||
|
|
|
@ -2,8 +2,10 @@ package io.xpipe.app.core;
|
|||
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
||||
import javafx.application.Preloader;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.xpipe.app.issue.ErrorEvent;
|
|||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.core.util.ModuleHelper;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
|
||||
|
@ -100,7 +101,8 @@ public class AppProperties {
|
|||
locatePtb = Optional.ofNullable(System.getProperty("io.xpipe.app.locator.usePtbInstallation"))
|
||||
.map(Boolean::parseBoolean)
|
||||
.orElse(false);
|
||||
locatorVersionCheck = Optional.ofNullable(System.getProperty("io.xpipe.app.locator.disableInstallationVersionCheck"))
|
||||
locatorVersionCheck = Optional.ofNullable(
|
||||
System.getProperty("io.xpipe.app.locator.disableInstallationVersionCheck"))
|
||||
.map(s -> !Boolean.parseBoolean(s))
|
||||
.orElse(true);
|
||||
isTest = isJUnitTest();
|
||||
|
|
|
@ -260,8 +260,8 @@ public class AppTheme {
|
|||
public static final Theme CUSTOM = new DerivedTheme("custom", "primer", "Custom", new PrimerDark());
|
||||
|
||||
// Also include your custom theme here
|
||||
public static final List<Theme> ALL =
|
||||
List.of(PRIMER_LIGHT, PRIMER_DARK, NORD_LIGHT, NORD_DARK, CUPERTINO_LIGHT, CUPERTINO_DARK, DRACULA, MOCHA);
|
||||
public static final List<Theme> ALL = List.of(
|
||||
PRIMER_LIGHT, PRIMER_DARK, NORD_LIGHT, NORD_DARK, CUPERTINO_LIGHT, CUPERTINO_DARK, DRACULA, MOCHA);
|
||||
protected final String id;
|
||||
|
||||
@Getter
|
||||
|
|
|
@ -7,6 +7,7 @@ import io.xpipe.app.prefs.AppPrefs;
|
|||
import io.xpipe.app.util.InputHelper;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.css.PseudoClass;
|
||||
|
|
|
@ -81,8 +81,8 @@ public class AppAvCheck {
|
|||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return WindowsRegistry.local().valueExists(
|
||||
WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Bitdefender", "InstallDir");
|
||||
return WindowsRegistry.local()
|
||||
.valueExists(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Bitdefender", "InstallDir");
|
||||
}
|
||||
},
|
||||
MALWAREBYTES("Malwarebytes") {
|
||||
|
@ -93,7 +93,8 @@ public class AppAvCheck {
|
|||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return WindowsRegistry.local().valueExists(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Malwarebytes", "id");
|
||||
return WindowsRegistry.local()
|
||||
.valueExists(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Malwarebytes", "id");
|
||||
}
|
||||
},
|
||||
MCAFEE("McAfee") {
|
||||
|
@ -104,7 +105,8 @@ public class AppAvCheck {
|
|||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return WindowsRegistry.local().valueExists(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\McAfee", "mi");
|
||||
return WindowsRegistry.local()
|
||||
.valueExists(WindowsRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\McAfee", "mi");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import io.xpipe.app.issue.ErrorEvent;
|
|||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.app.update.UpdateChangelogAlert;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
|
||||
import javafx.stage.Stage;
|
||||
|
||||
public class GuiMode extends PlatformMode {
|
||||
|
|
|
@ -194,8 +194,9 @@ public abstract class OperationMode {
|
|||
|
||||
public static void restart() {
|
||||
OperationMode.executeAfterShutdown(() -> {
|
||||
var loc = AppProperties.get().isDevelopmentEnvironment() ?
|
||||
XPipeInstallation.getLocalDefaultInstallationBasePath() : XPipeInstallation.getCurrentInstallationBasePath().toString();
|
||||
var loc = AppProperties.get().isDevelopmentEnvironment()
|
||||
? XPipeInstallation.getLocalDefaultInstallationBasePath()
|
||||
: XPipeInstallation.getCurrentInstallationBasePath().toString();
|
||||
var exec = XPipeInstallation.createExternalAsyncLaunchCommand(loc, XPipeDaemonMode.GUI, "", true);
|
||||
LocalShell.getShell().executeSimpleCommand(exec);
|
||||
});
|
||||
|
|
|
@ -45,7 +45,6 @@ public interface ActionProvider {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
default BranchDataStoreCallSite<?> getBranchDataStoreCallSite() {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ public abstract class DataStorageExtensionProvider {
|
|||
return ALL;
|
||||
}
|
||||
|
||||
public void storageInit() throws Exception {}
|
||||
public void storageInit() {}
|
||||
|
||||
public static class Loader implements ModuleLayerLoader {
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import io.xpipe.app.storage.DataStorage;
|
|||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.Property;
|
||||
|
@ -29,9 +30,9 @@ public interface DataStoreProvider {
|
|||
default boolean shouldShow(StoreEntryWrapper w) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
default void onChildrenRefresh(DataStoreEntry entry) {}
|
||||
|
||||
|
||||
default ObservableBooleanValue busy(StoreEntryWrapper wrapper) {
|
||||
return new SimpleBooleanProperty(false);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import io.xpipe.core.store.StatefulDataStore;
|
|||
public interface EnabledParentStoreProvider extends DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public default StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) {
|
||||
default StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) {
|
||||
if (sec.getWrapper().getValidity().getValue() != DataStoreEntry.Validity.COMPLETE) {
|
||||
return StoreEntryComp.create(sec.getWrapper(), null, preferLarge);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import io.xpipe.core.store.StatefulDataStore;
|
|||
public interface EnabledStoreProvider extends DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public default StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) {
|
||||
default StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) {
|
||||
if (sec.getWrapper().getValidity().getValue() != DataStoreEntry.Validity.COMPLETE) {
|
||||
return StoreEntryComp.create(sec.getWrapper(), null, preferLarge);
|
||||
}
|
||||
|
|
|
@ -40,8 +40,9 @@ public interface SingletonSessionStoreProvider extends DataStoreProvider {
|
|||
enabled.set(s.isSessionEnabled());
|
||||
});
|
||||
|
||||
ObservableValue<LabelGraphic> g = enabled.map(aBoolean -> aBoolean ?
|
||||
new LabelGraphic.IconGraphic("mdi2c-circle-slice-8") : new LabelGraphic.IconGraphic("mdi2p-power"));
|
||||
ObservableValue<LabelGraphic> g = enabled.map(aBoolean -> aBoolean
|
||||
? new LabelGraphic.IconGraphic("mdi2c-circle-slice-8")
|
||||
: new LabelGraphic.IconGraphic("mdi2p-power"));
|
||||
var t = new StoreToggleComp(null, g, sec, enabled, aBoolean -> {
|
||||
SingletonSessionStore<?> s = sec.getWrapper().getEntry().getStore().asNeeded();
|
||||
if (s.isSessionEnabled() != aBoolean) {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package io.xpipe.app.fxcomps;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.augment.Augment;
|
||||
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.app.fxcomps.impl.TooltipAugment;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Insets;
|
||||
|
@ -18,6 +18,8 @@ import javafx.scene.layout.Priority;
|
|||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import atlantafx.base.controls.Spacer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
|
|
@ -6,12 +6,14 @@ import io.xpipe.app.fxcomps.CompStructure;
|
|||
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.util.Translatable;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.xpipe.app.fxcomps.impl;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.browser.session.BrowserChooserComp;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
|
@ -16,6 +15,7 @@ import io.xpipe.app.storage.DataStorage;
|
|||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.FileNames;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
@ -23,6 +23,8 @@ import javafx.beans.value.ObservableValue;
|
|||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.nio.file.Files;
|
||||
|
@ -44,7 +46,6 @@ public class ContextualFileReferenceChoiceComp extends Comp<CompStructure<HBox>>
|
|||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
|
||||
public <T extends FileSystemStore> ContextualFileReferenceChoiceComp(
|
||||
Property<DataStoreEntryRef<T>> fileSystem, Property<String> filePath) {
|
||||
this.fileSystem = new SimpleObjectProperty<>();
|
||||
|
|
|
@ -77,7 +77,8 @@ public class DataStoreChoiceComp<T extends DataStore> extends SimpleComp {
|
|||
Predicate<StoreEntryWrapper> applicable = storeEntryWrapper -> {
|
||||
var e = storeEntryWrapper.getEntry();
|
||||
|
||||
if (e.equals(self) || DataStorage.get().getStoreParentHierarchy(e).contains(self)) {
|
||||
if (e.equals(self)
|
||||
|| DataStorage.get().getStoreParentHierarchy(e).contains(self)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import io.xpipe.app.fxcomps.util.DerivedObservableList;
|
|||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.util.ContextMenuHelper;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.css.PseudoClass;
|
||||
|
@ -25,6 +26,7 @@ import javafx.scene.control.MenuItem;
|
|||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
@ -77,12 +79,14 @@ public class StoreCategoryComp extends SimpleComp {
|
|||
showing.bind(cm.showingProperty());
|
||||
return cm;
|
||||
}));
|
||||
var shownList = new DerivedObservableList<>(category.getContainedEntries(), true).filtered(
|
||||
storeEntryWrapper -> {
|
||||
return storeEntryWrapper.matchesFilter(
|
||||
StoreViewState.get().getFilterString().getValue());
|
||||
},
|
||||
StoreViewState.get().getFilterString()).getList();
|
||||
var shownList = new DerivedObservableList<>(category.getContainedEntries(), true)
|
||||
.filtered(
|
||||
storeEntryWrapper -> {
|
||||
return storeEntryWrapper.matchesFilter(
|
||||
StoreViewState.get().getFilterString().getValue());
|
||||
},
|
||||
StoreViewState.get().getFilterString())
|
||||
.getList();
|
||||
var count = new CountComp<>(shownList, category.getContainedEntries(), string -> "(" + string + ")");
|
||||
var hover = new SimpleBooleanProperty();
|
||||
var focus = new SimpleBooleanProperty();
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.xpipe.app.core.AppProperties;
|
|||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
@ -13,6 +14,7 @@ import javafx.scene.Node;
|
|||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.web.WebView;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.xpipe.app.core.AppI18n;
|
|||
import io.xpipe.app.fxcomps.CompStructure;
|
||||
import io.xpipe.app.fxcomps.augment.Augment;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.control.Tooltip;
|
||||
|
|
|
@ -8,6 +8,7 @@ import javafx.beans.value.ObservableValue;
|
|||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -133,13 +134,15 @@ public class DerivedObservableList<T> {
|
|||
var l1 = this.<V>createNewDerived();
|
||||
Runnable runnable = () -> {
|
||||
cache.keySet().removeIf(t -> !getList().contains(t));
|
||||
l1.setContent(list.stream().map(v -> {
|
||||
if (!cache.containsKey(v)) {
|
||||
cache.put(v, map.apply(v));
|
||||
}
|
||||
l1.setContent(list.stream()
|
||||
.map(v -> {
|
||||
if (!cache.containsKey(v)) {
|
||||
cache.put(v, map.apply(v));
|
||||
}
|
||||
|
||||
return cache.get(v);
|
||||
}).toList());
|
||||
return cache.get(v);
|
||||
})
|
||||
.toList());
|
||||
};
|
||||
runnable.run();
|
||||
list.addListener((ListChangeListener<? super T>) c -> {
|
||||
|
@ -160,17 +163,16 @@ public class DerivedObservableList<T> {
|
|||
}
|
||||
|
||||
public DerivedObservableList<T> filtered(Predicate<T> predicate, Observable... observables) {
|
||||
return filtered(
|
||||
Bindings.createObjectBinding(
|
||||
() -> {
|
||||
return new Predicate<>() {
|
||||
@Override
|
||||
public boolean test(T v) {
|
||||
return predicate.test(v);
|
||||
}
|
||||
};
|
||||
},
|
||||
Arrays.stream(observables).filter(Objects::nonNull).toArray(Observable[]::new)));
|
||||
return filtered(Bindings.createObjectBinding(
|
||||
() -> {
|
||||
return new Predicate<>() {
|
||||
@Override
|
||||
public boolean test(T v) {
|
||||
return predicate.test(v);
|
||||
}
|
||||
};
|
||||
},
|
||||
Arrays.stream(observables).filter(Objects::nonNull).toArray(Observable[]::new)));
|
||||
}
|
||||
|
||||
public DerivedObservableList<T> filtered(ObservableValue<Predicate<T>> predicate) {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package io.xpipe.app.fxcomps.util;
|
||||
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.Node;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
|
|
@ -11,6 +11,7 @@ import io.xpipe.app.fxcomps.SimpleComp;
|
|||
import io.xpipe.app.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.app.util.LicenseRequiredException;
|
||||
import io.xpipe.app.util.PlatformState;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
@ -24,6 +25,7 @@ import javafx.scene.layout.Region;
|
|||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -252,7 +254,8 @@ public class ErrorHandlerComp extends SimpleComp {
|
|||
}
|
||||
|
||||
if (!event.isDisableDefaultActions() || event.getCustomActions().isEmpty()) {
|
||||
for (var action : List.of(ErrorAction.automaticallyReport(), ErrorAction.reportOnGithub(), ErrorAction.ignore())) {
|
||||
for (var action :
|
||||
List.of(ErrorAction.automaticallyReport(), ErrorAction.reportOnGithub(), ErrorAction.ignore())) {
|
||||
var ac = createActionComp(action);
|
||||
actionBox.getChildren().add(ac);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import io.xpipe.beacon.api.DaemonOpenExchange;
|
|||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.util.XPipeDaemonMode;
|
||||
import io.xpipe.core.util.XPipeInstallation;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import picocli.CommandLine;
|
||||
|
||||
|
@ -83,24 +84,33 @@ public class LauncherCommand implements Callable<Integer> {
|
|||
private void checkStart() {
|
||||
try {
|
||||
var port = AppBeaconServer.get().getPort();
|
||||
var client = BeaconClient.tryEstablishConnection(port, BeaconClientInformation.Daemon.builder().build());
|
||||
var client = BeaconClient.tryEstablishConnection(
|
||||
port, BeaconClientInformation.Daemon.builder().build());
|
||||
if (client.isPresent()) {
|
||||
client.get().performRequest(DaemonFocusExchange.Request.builder().mode(getEffectiveMode()).build());
|
||||
if (!inputs.isEmpty()) {
|
||||
client.get().performRequest(
|
||||
DaemonOpenExchange.Request.builder().arguments(inputs).build());
|
||||
}
|
||||
client.get()
|
||||
.performRequest(DaemonFocusExchange.Request.builder()
|
||||
.mode(getEffectiveMode())
|
||||
.build());
|
||||
if (!inputs.isEmpty()) {
|
||||
client.get()
|
||||
.performRequest(DaemonOpenExchange.Request.builder()
|
||||
.arguments(inputs)
|
||||
.build());
|
||||
}
|
||||
|
||||
if (OsType.getLocal().equals(OsType.MACOS)) {
|
||||
Desktop.getDesktop().setOpenURIHandler(e -> {
|
||||
try {
|
||||
client.get().performRequest(DaemonOpenExchange.Request.builder().arguments(List.of(e.getURI().toString())).build());
|
||||
} catch (Exception ex) {
|
||||
ErrorEvent.fromThrowable(ex).expected().omit().handle();
|
||||
}
|
||||
});
|
||||
ThreadHelper.sleep(1000);
|
||||
}
|
||||
if (OsType.getLocal().equals(OsType.MACOS)) {
|
||||
Desktop.getDesktop().setOpenURIHandler(e -> {
|
||||
try {
|
||||
client.get()
|
||||
.performRequest(DaemonOpenExchange.Request.builder()
|
||||
.arguments(List.of(e.getURI().toString()))
|
||||
.build());
|
||||
} catch (Exception ex) {
|
||||
ErrorEvent.fromThrowable(ex).expected().omit().handle();
|
||||
}
|
||||
});
|
||||
ThreadHelper.sleep(1000);
|
||||
}
|
||||
TrackEvent.info("Another instance is already running on this port. Quitting ...");
|
||||
OperationMode.halt(1);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import io.xpipe.app.ext.ActionProvider;
|
|||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.net.URI;
|
||||
|
@ -33,11 +34,11 @@ public abstract class LauncherInput {
|
|||
}
|
||||
});
|
||||
|
||||
// var requiresPlatform = all.stream().anyMatch(launcherInput -> launcherInput.requiresJavaFXPlatform());
|
||||
// if (requiresPlatform) {
|
||||
// OperationMode.switchToSyncIfPossible(OperationMode.GUI);
|
||||
// }
|
||||
// var hasGui = OperationMode.get() == OperationMode.GUI;
|
||||
// var requiresPlatform = all.stream().anyMatch(launcherInput -> launcherInput.requiresJavaFXPlatform());
|
||||
// if (requiresPlatform) {
|
||||
// OperationMode.switchToSyncIfPossible(OperationMode.GUI);
|
||||
// }
|
||||
// var hasGui = OperationMode.get() == OperationMode.GUI;
|
||||
|
||||
all.forEach(launcherInput -> {
|
||||
try {
|
||||
|
|
|
@ -15,12 +15,14 @@ import io.xpipe.app.util.PasswordLockSecretValue;
|
|||
import io.xpipe.core.util.InPlaceSecretValue;
|
||||
import io.xpipe.core.util.ModuleHelper;
|
||||
import io.xpipe.core.util.XPipeInstallation;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
import javafx.beans.value.ObservableDoubleValue;
|
||||
import javafx.beans.value.ObservableStringValue;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
|
||||
|
@ -120,8 +122,8 @@ public class AppPrefs {
|
|||
private final StringProperty lockCrypt =
|
||||
mapVaultSpecific(new SimpleStringProperty(), "workspaceLock", String.class);
|
||||
|
||||
final Property<Integer> httpServerPort =
|
||||
mapVaultSpecific(new SimpleObjectProperty<>(XPipeInstallation.getDefaultBeaconPort()), "httpServerPort", Integer.class);
|
||||
final Property<Integer> httpServerPort = mapVaultSpecific(
|
||||
new SimpleObjectProperty<>(XPipeInstallation.getDefaultBeaconPort()), "httpServerPort", Integer.class);
|
||||
final StringProperty apiKey =
|
||||
mapVaultSpecific(new SimpleStringProperty(UUID.randomUUID().toString()), "apiKey", String.class);
|
||||
final BooleanProperty disableApiAuthentication =
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue