Rendering und Lua working, need to work on request method via custom URL implementation
This commit is contained in:
9
pom.xml
9
pom.xml
@@ -105,11 +105,6 @@
|
|||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>org.openautonomousconnection</groupId>
|
|
||||||
<artifactId>Protocol</artifactId>
|
|
||||||
<version>1.0.0-BETA.7.7</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openautonomousconnection</groupId>
|
<groupId>org.openautonomousconnection</groupId>
|
||||||
<artifactId>OACSwing</artifactId>
|
<artifactId>OACSwing</artifactId>
|
||||||
@@ -124,12 +119,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openautonomousconnection</groupId>
|
<groupId>org.openautonomousconnection</groupId>
|
||||||
<artifactId>LuaScript</artifactId>
|
<artifactId>LuaScript</artifactId>
|
||||||
<version>1.0.0-BETA.1.4</version>
|
<version>1.0.0-BETA.1.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openautonomousconnection</groupId>
|
<groupId>org.openautonomousconnection</groupId>
|
||||||
<artifactId>InfoNameLib</artifactId>
|
<artifactId>InfoNameLib</artifactId>
|
||||||
<version>1.0.0-BETA.1.1</version>
|
<version>1.0.0-BETA.1.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
|
|||||||
@@ -1,24 +1,13 @@
|
|||||||
package org.openautonomousconnection.webclient.recode;
|
package org.openautonomousconnection.webclient;
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketSendEvent;
|
import org.openautonomousconnection.infonamelib.OacWebUrlInstaller;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientConnectedEvent;
|
import org.openautonomousconnection.infonamelib.ProtocolHandlerPackages;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientDisconnectedEvent;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
|
||||||
import org.openautonomousconnection.oacswing.component.OACOptionPane;
|
import org.openautonomousconnection.oacswing.component.OACOptionPane;
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
|
||||||
import org.openautonomousconnection.protocol.side.client.ProtocolClient;
|
import org.openautonomousconnection.protocol.side.client.ProtocolClient;
|
||||||
import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolINSServerEvent;
|
import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolINSServerEvent;
|
||||||
import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolServerEvent;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecordType;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class ClientImpl extends ProtocolClient {
|
public class ClientImpl extends ProtocolClient {
|
||||||
@Override
|
@Override
|
||||||
@@ -65,46 +54,17 @@ public class ClientImpl extends ProtocolClient {
|
|||||||
return result == 0;
|
return result == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResponse(INSResponseStatus status, List<INSRecord> records) {
|
|
||||||
try {
|
|
||||||
String host = records.getFirst().value;
|
|
||||||
if (!host.contains(":")) host = host + ":1028";
|
|
||||||
String[] split = host.split(":");
|
|
||||||
Main.getClient().getClientServerConnection().connect(split[0], Integer.parseInt(split[1]));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Main.getClient().getProtocolBridge().getLogger().exception("Failed to connect to Server", e);
|
|
||||||
OACOptionPane.showMessageDialog(Main.getUi(), "Failed to connect to Server:\n" + e.getMessage(),
|
|
||||||
"Server Connection", OACOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onResponse(status, records);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Listener
|
@Listener
|
||||||
public void onConnected(ConnectedToProtocolINSServerEvent event) {
|
public void onConnected(ConnectedToProtocolINSServerEvent event) {
|
||||||
try {
|
try {
|
||||||
Main.getClient().buildServerConnection(null, true);
|
buildServerConnection(null, getProtocolBridge().getProtocolValues().ssl);
|
||||||
|
ProtocolHandlerPackages.installPackage("org.openautonomousconnection.infonamelib");
|
||||||
|
OacWebUrlInstaller.installOnce(getProtocolBridge().getProtocolValues().eventManager, this);
|
||||||
|
SwingUtilities.invokeLater(() -> Main.getUi().openNewTab("web://info.oac/"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Main.getClient().getProtocolBridge().getLogger().exception("Failed to build Server connection", e);
|
Main.getClient().getProtocolBridge().getLogger().exception("Failed to build Server connection", e);
|
||||||
OACOptionPane.showMessageDialog(Main.getUi(), "Failed to connect to build Server connection:\n" + e.getMessage(),
|
OACOptionPane.showMessageDialog(Main.getUi(), "Failed to connect to build Server connection:\n" + e.getMessage(),
|
||||||
"Server Connection", OACOptionPane.ERROR_MESSAGE);
|
"Server Connection", OACOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Main.getUi().openNewTab("register.oac");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Listener
|
|
||||||
public void onConnected(ConnectedToProtocolServerEvent event) {
|
|
||||||
try {
|
|
||||||
this.getClientServerConnection().sendPacket(new WebRequestPacket(
|
|
||||||
"index.html",
|
|
||||||
WebRequestMethod.GET,
|
|
||||||
Map.of(),
|
|
||||||
null
|
|
||||||
), TransportProtocol.TCP);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,100 +1,69 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Dec. 12 2025
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient;
|
package org.openautonomousconnection.webclient;
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
|
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
||||||
import org.openautonomousconnection.infonamelib.InfoNames;
|
import lombok.Getter;
|
||||||
import org.openautonomousconnection.oacswing.component.design.Design;
|
import org.openautonomousconnection.oacswing.component.design.Design;
|
||||||
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
||||||
import org.openautonomousconnection.oacswing.component.design.OACColor;
|
|
||||||
import org.openautonomousconnection.protocol.ProtocolBridge;
|
import org.openautonomousconnection.protocol.ProtocolBridge;
|
||||||
import org.openautonomousconnection.protocol.ProtocolValues;
|
import org.openautonomousconnection.protocol.ProtocolValues;
|
||||||
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||||
import org.openautonomousconnection.webclient.network.WebClient;
|
import org.openautonomousconnection.webclient.settings.INSList;
|
||||||
import org.openautonomousconnection.webclient.network.handlers.ServerPacketHandler;
|
import org.openautonomousconnection.webclient.ui.BrowserUI;
|
||||||
import org.openautonomousconnection.webclient.packetlistener.listeners.WebPacketListener;
|
import org.openautonomousconnection.webclient.ui.FxBootstrap;
|
||||||
import org.openautonomousconnection.webclient.ui.MainFrame;
|
|
||||||
import org.openautonomousconnection.webclient.ui.dom.DOMContainerPanel;
|
|
||||||
|
|
||||||
import java.beans.EventHandler;
|
import javax.swing.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static MainFrame mainFrame;
|
|
||||||
|
|
||||||
public static final String DEFAULT_INS = "open-autonomous-connection.org";
|
@Getter
|
||||||
|
private static ClientImpl client;
|
||||||
|
|
||||||
public static final int DEFAULT_INS_PORT_TCP = 1026;
|
private static ProtocolBridge bridge;
|
||||||
public static final int DEFAULT_WEB_PORT_TCP = 1028;
|
|
||||||
|
|
||||||
private static final ProtocolVersion PROTOCOL_VERSION = ProtocolVersion.PV_1_0_0_BETA;
|
@Getter
|
||||||
|
private static BrowserUI ui;
|
||||||
public static WebClient client;
|
|
||||||
|
|
||||||
public static ProtocolBridge bridge;
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
initProtocol();
|
|
||||||
|
|
||||||
InfoNames.registerOACInfoNameProtocols();
|
|
||||||
|
|
||||||
try {
|
|
||||||
registerEventHandlers();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
registerPacketListeners();
|
|
||||||
|
|
||||||
/* Darkmode, wohoo! */
|
|
||||||
initDesigns();
|
|
||||||
|
|
||||||
DesignManager.setGlobalDesign(Design.DARK);
|
|
||||||
|
|
||||||
mainFrame = new MainFrame();
|
|
||||||
|
|
||||||
mainFrame.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void initProtocol() {
|
private static void initProtocol() {
|
||||||
|
|
||||||
ProtocolValues values = new ProtocolValues();
|
ProtocolValues values = new ProtocolValues();
|
||||||
|
|
||||||
values.packetHandler = new PacketHandler();
|
values.packetHandler = new PacketHandler();
|
||||||
values.eventManager = new EventManager();
|
values.eventManager = new EventManager();
|
||||||
|
values.ssl = true;
|
||||||
|
|
||||||
client = new WebClient();
|
client = new ClientImpl();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bridge = new ProtocolBridge(
|
bridge = new ProtocolBridge(
|
||||||
client,
|
client,
|
||||||
values,
|
values,
|
||||||
PROTOCOL_VERSION,
|
ProtocolVersion.PV_1_0_0_BETA,
|
||||||
new File("logs")
|
new File("logs")
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO
|
client.buildINSConnection();
|
||||||
client.connectToINSServer(Main.DEFAULT_INS, -1);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerEventHandlers() throws Exception {
|
public static void main(String[] args) {
|
||||||
EventManager eventManager = bridge.getProtocolValues().eventManager;
|
initProtocol();
|
||||||
|
FxBootstrap.ensureInitialized();
|
||||||
|
DesignManager.setGlobalDesign(Design.DARK);
|
||||||
|
|
||||||
eventManager.registerListener(new ServerPacketHandler());
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
ui = new BrowserUI();
|
||||||
|
ui.setSize(1200, 800);
|
||||||
|
ui.setLocationRelativeTo(null);
|
||||||
|
ui.setVisible(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
bridge.getProtocolValues().eventManager.registerListener(client);
|
||||||
|
client.getClientINSConnection().connect(INSList.DEFAULT_INS, INSList.DEFAULT_PORT);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
exception.printStackTrace(System.out);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private static void registerPacketListeners() {
|
|
||||||
ServerPacketHandler.registerPacketListener(new WebPacketListener());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void initDesigns() {
|
|
||||||
//TODO
|
|
||||||
|
|
||||||
//DesignManager.getInstance().registerComponent(DOMContainerPanel.class, OACColor.DARK_BACKGROUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package org.openautonomousconnection.webclient.lua;
|
||||||
|
|
||||||
|
import org.openautonomousconnection.webclient.Main;
|
||||||
|
|
||||||
|
public class WebLogger {
|
||||||
|
|
||||||
|
private final String host;
|
||||||
|
|
||||||
|
public WebLogger(String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(String string) {
|
||||||
|
Main.getClient().getProtocolBridge().getLogger().log(host + ": " + string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void info(String info) {
|
||||||
|
Main.getClient().getProtocolBridge().getLogger().info(host + ": " + info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void warn(String warn) {
|
||||||
|
Main.getClient().getProtocolBridge().getLogger().warn(host + ": " + warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(String error) {
|
||||||
|
Main.getClient().getProtocolBridge().getLogger().error(host + ": " + error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exception(String infoLine, Exception exception) {
|
||||||
|
Main.getClient().getProtocolBridge().getLogger().exception(host + ": " + infoLine, exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void debug(String debug) {
|
||||||
|
Main.getClient().getProtocolBridge().getLogger().debug(host + ": " + debug);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package org.openautonomousconnection.webclient.lua.hosts;
|
||||||
|
|
||||||
|
import org.openautonomousconnection.luascript.hosts.ConsoleHost;
|
||||||
|
import org.openautonomousconnection.webclient.lua.WebLogger;
|
||||||
|
|
||||||
|
public class ConsoleHostImpl implements ConsoleHost {
|
||||||
|
private final WebLogger logger;
|
||||||
|
|
||||||
|
public ConsoleHostImpl(WebLogger logger) {
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void info(String message) {
|
||||||
|
logger.info(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void log(String message) {
|
||||||
|
logger.log(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warn(String message) {
|
||||||
|
logger.warn(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String message) {
|
||||||
|
logger.error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exception(String message) {
|
||||||
|
logger.exception("", new RuntimeException(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
package org.openautonomousconnection.webclient.lua.hosts;
|
||||||
|
|
||||||
|
import javafx.scene.web.WebEngine;
|
||||||
|
import javafx.scene.web.WebView;
|
||||||
|
import org.openautonomousconnection.luascript.fx.FxDomHost;
|
||||||
|
import org.openautonomousconnection.luascript.fx.FxThreadBridge;
|
||||||
|
import org.openautonomousconnection.luascript.hosts.UiHost;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UiHost implementation for JavaFX WebView (no JavaScript).
|
||||||
|
*
|
||||||
|
* <p>Operations are implemented via W3C DOM attributes/text, and best-effort behavior for
|
||||||
|
* value/style/class using standard attributes.</p>
|
||||||
|
*/
|
||||||
|
public final class UiHostImpl implements UiHost {
|
||||||
|
|
||||||
|
private final WebEngine engine;
|
||||||
|
private final WebView view;
|
||||||
|
private final FxDomHost dom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new UI host.
|
||||||
|
*
|
||||||
|
* @param engine web engine
|
||||||
|
* @param view web view
|
||||||
|
* @param dom dom host
|
||||||
|
*/
|
||||||
|
public UiHostImpl(WebEngine engine, WebView view, FxDomHost dom) {
|
||||||
|
this.engine = Objects.requireNonNull(engine, "engine");
|
||||||
|
this.view = view;
|
||||||
|
this.dom = Objects.requireNonNull(dom, "dom");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void alert(String message) {
|
||||||
|
// No JS: use simple JavaFX dialog-less fallback (log-style). You can replace with real Dialogs later.
|
||||||
|
// Keeping it deterministic and non-blocking for now.
|
||||||
|
System.out.println("[ui.alert] " + (message == null ? "" : message));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean confirm(String message) {
|
||||||
|
// No JS: deterministic default (false). Replace with JavaFX dialogs if you want UI interaction.
|
||||||
|
System.out.println("[ui.confirm] " + (message == null ? "" : message));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String prompt(String message, String defaultValue) {
|
||||||
|
// No JS: deterministic default.
|
||||||
|
System.out.println("[ui.prompt] " + (message == null ? "" : message));
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setText(String elementId, String text) {
|
||||||
|
dom.setTextContent(elementId, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText(String elementId) {
|
||||||
|
return dom.getTextContent(elementId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHtml(String elementId, String html) {
|
||||||
|
// Without JS, safest is to set textContent (prevents HTML parsing).
|
||||||
|
// If you need real HTML injection, we must extend DomHost with fragment parsing (not in current API).
|
||||||
|
dom.setTextContent(elementId, html);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHtml(String elementId) {
|
||||||
|
// Without JS, best-effort: return textContent.
|
||||||
|
return dom.getTextContent(elementId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(String elementId, String value) {
|
||||||
|
// Input/textarea value is commonly reflected as attribute "value".
|
||||||
|
dom.setAttribute(elementId, "value", value == null ? "" : value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue(String elementId) {
|
||||||
|
String v = dom.getAttribute(elementId, "value");
|
||||||
|
return v == null ? "" : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnabled(String elementId, boolean enabled) {
|
||||||
|
if (enabled) dom.removeAttribute(elementId, "disabled");
|
||||||
|
else dom.setAttribute(elementId, "disabled", "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVisible(String elementId, boolean visible) {
|
||||||
|
// Best-effort via style attribute
|
||||||
|
String style = dom.getAttribute(elementId, "style");
|
||||||
|
style = style == null ? "" : style;
|
||||||
|
|
||||||
|
style = removeCssProp(style, "display");
|
||||||
|
if (!visible) {
|
||||||
|
style = style.trim();
|
||||||
|
if (!style.isEmpty() && !style.endsWith(";")) style += ";";
|
||||||
|
style += "display:none;";
|
||||||
|
}
|
||||||
|
|
||||||
|
dom.setAttribute(elementId, "style", style);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addClass(String elementId, String className) {
|
||||||
|
String cls = Objects.requireNonNull(className, "className").trim();
|
||||||
|
if (cls.isEmpty()) return;
|
||||||
|
|
||||||
|
FxThreadBridge.runAndWait(() -> {
|
||||||
|
Element el = dom.byId(elementId);
|
||||||
|
String c = el.getAttribute("class");
|
||||||
|
c = (c == null) ? "" : c.trim();
|
||||||
|
|
||||||
|
if (c.isEmpty()) {
|
||||||
|
el.setAttribute("class", cls);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasClassToken(c, cls)) {
|
||||||
|
el.setAttribute("class", c + " " + cls);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeClass(String elementId, String className) {
|
||||||
|
String cls = Objects.requireNonNull(className, "className").trim();
|
||||||
|
if (cls.isEmpty()) return;
|
||||||
|
|
||||||
|
FxThreadBridge.runAndWait(() -> {
|
||||||
|
Element el = dom.byId(elementId);
|
||||||
|
String c = el.getAttribute("class");
|
||||||
|
c = (c == null) ? "" : c.trim();
|
||||||
|
if (c.isEmpty()) return;
|
||||||
|
|
||||||
|
String[] parts = c.split("\\s+");
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String p : parts) {
|
||||||
|
if (p.equals(cls)) continue;
|
||||||
|
if (!sb.isEmpty()) sb.append(' ');
|
||||||
|
sb.append(p);
|
||||||
|
}
|
||||||
|
el.setAttribute("class", sb.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean toggleClass(String elementId, String className) {
|
||||||
|
if (hasClass(elementId, className)) {
|
||||||
|
removeClass(elementId, className);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
addClass(elementId, className);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasClass(String elementId, String className) {
|
||||||
|
String cls = Objects.requireNonNull(className, "className").trim();
|
||||||
|
if (cls.isEmpty()) return false;
|
||||||
|
|
||||||
|
return FxThreadBridge.callAndWait(() -> {
|
||||||
|
Element el = dom.byId(elementId);
|
||||||
|
String c = el.getAttribute("class");
|
||||||
|
c = (c == null) ? "" : c.trim();
|
||||||
|
return !c.isEmpty() && hasClassToken(c, cls);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStyle(String elementId, String property, String value) {
|
||||||
|
String prop = Objects.requireNonNull(property, "property").trim().toLowerCase();
|
||||||
|
if (prop.isEmpty()) return;
|
||||||
|
|
||||||
|
String style = dom.getAttribute(elementId, "style");
|
||||||
|
style = style == null ? "" : style;
|
||||||
|
|
||||||
|
style = removeCssProp(style, prop);
|
||||||
|
String v = value == null ? "" : value.trim();
|
||||||
|
|
||||||
|
if (!v.isEmpty()) {
|
||||||
|
style = style.trim();
|
||||||
|
if (!style.isEmpty() && !style.endsWith(";")) style += ";";
|
||||||
|
style += prop + ":" + v + ";";
|
||||||
|
}
|
||||||
|
dom.setAttribute(elementId, "style", style);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStyle(String elementId, String property) {
|
||||||
|
// Best-effort parsing from style attribute.
|
||||||
|
String prop = Objects.requireNonNull(property, "property").trim().toLowerCase();
|
||||||
|
if (prop.isEmpty()) return "";
|
||||||
|
|
||||||
|
String style = dom.getAttribute(elementId, "style");
|
||||||
|
style = style == null ? "" : style;
|
||||||
|
|
||||||
|
for (String part : style.split(";")) {
|
||||||
|
String p = part.trim();
|
||||||
|
if (p.isEmpty()) continue;
|
||||||
|
int idx = p.indexOf(':');
|
||||||
|
if (idx <= 0) continue;
|
||||||
|
|
||||||
|
String k = p.substring(0, idx).trim().toLowerCase();
|
||||||
|
if (k.equals(prop)) return p.substring(idx + 1).trim();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String elementId, String name, String value) {
|
||||||
|
dom.setAttribute(elementId, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAttribute(String elementId, String name) {
|
||||||
|
return dom.getAttribute(elementId, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAttribute(String elementId, String name) {
|
||||||
|
dom.removeAttribute(elementId, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focus(String elementId) {
|
||||||
|
engine.setJavaScriptEnabled(true);
|
||||||
|
engine.executeScript(
|
||||||
|
"document.getElementById('" + elementId + "').focus();"
|
||||||
|
);
|
||||||
|
engine.setJavaScriptEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void blur(String elementId) {
|
||||||
|
engine.setJavaScriptEnabled(true);
|
||||||
|
engine.executeScript(
|
||||||
|
"document.getElementById('" + elementId + "').blur();"
|
||||||
|
);
|
||||||
|
engine.setJavaScriptEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void scrollIntoView(String elementId) {
|
||||||
|
engine.setJavaScriptEnabled(true);
|
||||||
|
engine.executeScript(
|
||||||
|
"document.getElementById('" + elementId + "').scrollIntoView();"
|
||||||
|
);
|
||||||
|
engine.setJavaScriptEnabled(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int viewportWidth() {
|
||||||
|
return FxThreadBridge.callAndWait(() ->
|
||||||
|
(int) Math.round(view.getWidth())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int viewportHeight() {
|
||||||
|
return FxThreadBridge.callAndWait(() ->
|
||||||
|
(int) Math.round(view.getHeight())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long nowMillis() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasClassToken(String classAttr, String cls) {
|
||||||
|
String[] parts = classAttr.trim().split("\\s+");
|
||||||
|
for (String p : parts) {
|
||||||
|
if (p.equals(cls)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String removeCssProp(String style, String propLower) {
|
||||||
|
if (style == null || style.isBlank()) return "";
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (String part : style.split(";")) {
|
||||||
|
String p = part.trim();
|
||||||
|
if (p.isEmpty()) continue;
|
||||||
|
int idx = p.indexOf(':');
|
||||||
|
if (idx <= 0) continue;
|
||||||
|
|
||||||
|
String k = p.substring(0, idx).trim().toLowerCase();
|
||||||
|
if (k.equals(propLower)) continue;
|
||||||
|
|
||||||
|
if (!sb.isEmpty()) sb.append(';');
|
||||||
|
sb.append(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
String out = sb.toString().trim();
|
||||||
|
if (!out.isEmpty() && !out.endsWith(";")) out += ";";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package org.openautonomousconnection.webclient.network;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An object that is still waited for to be delivered by the server
|
|
||||||
*
|
|
||||||
* @param <T> type of promised object
|
|
||||||
*/
|
|
||||||
public class Promise<T> {
|
|
||||||
@Getter @Setter
|
|
||||||
private T object;
|
|
||||||
|
|
||||||
public Promise() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Promise(T object) {
|
|
||||||
this.object = object;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Promise<T> yieldPromise(int timeout, T reference) {
|
|
||||||
try (ExecutorService executorService = Executors.newSingleThreadExecutor()) {
|
|
||||||
Callable<T> task = () -> {
|
|
||||||
for(int i = 0; i < timeout; i++) {
|
|
||||||
if(reference == null) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(reference == null)
|
|
||||||
throw new TimeoutException();
|
|
||||||
|
|
||||||
return reference;
|
|
||||||
};
|
|
||||||
|
|
||||||
Future<T> future = executorService.submit(task);
|
|
||||||
|
|
||||||
executorService.shutdown();
|
|
||||||
|
|
||||||
try {
|
|
||||||
return new Promise<>(future.get());
|
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private Class<?> getType() {
|
|
||||||
return this.object.getClass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Dec. 12 2025
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.network;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
|
||||||
import org.openautonomousconnection.protocol.side.client.ProtocolClient;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.NTFType;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
|
||||||
|
|
||||||
import javax.swing.text.Document;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.ConnectException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.openautonomousconnection.webclient.Main.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the OAC-Protocol networking client
|
|
||||||
* Suppressing "unchecked" warnings because of generic casts
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public class WebClient extends ProtocolClient {
|
|
||||||
/**
|
|
||||||
* Global protocol timeout = 30 seconds
|
|
||||||
*/
|
|
||||||
public static final int TIMEOUT = 30;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect to INServer with URLs (ip based)
|
|
||||||
* @param ip ip to server
|
|
||||||
* @param tcpPort tcp ins port
|
|
||||||
*/
|
|
||||||
public void connectToINSServer(String ip, int tcpPort) throws NoSuchAlgorithmException, KeyManagementException {
|
|
||||||
|
|
||||||
tcpPort = tcpPort != -1 ? tcpPort : DEFAULT_INS_PORT_TCP;
|
|
||||||
|
|
||||||
// UDP is always preset
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.buildINSConnection();
|
|
||||||
this.getClientINSConnection().connect(ip, tcpPort);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Give promise a non-null reference
|
|
||||||
* @param type type of file
|
|
||||||
* @param object serialized object
|
|
||||||
* @param <T> java object type
|
|
||||||
*/
|
|
||||||
public <T> void fulfillPromise(NTFType type, T object) {
|
|
||||||
if(!this.outstandingPromises.containsKey(type))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Object promisedValue = this.outstandingPromises.get(type);
|
|
||||||
|
|
||||||
if(promisedValue != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.outstandingPromises.replace(type, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final Map<NTFType, Object> outstandingPromises = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Promise a serialized object that will be delivered as a file by the server
|
|
||||||
* @param type type of file
|
|
||||||
* @return object promise
|
|
||||||
* @param <T> java object type
|
|
||||||
*/
|
|
||||||
private <T> Promise<? extends NetTransmitFile<T>> promise(NTFType type) {
|
|
||||||
NetTransmitFile<T> ntf = NetTransmitFile.of(type, null);
|
|
||||||
|
|
||||||
this.outstandingPromises.put(type, ntf);
|
|
||||||
|
|
||||||
return Promise.yieldPromise(TIMEOUT, ntf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResponse(INSResponseStatus insResponseStatus, List<INSRecord> list) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean trustINS(String caFingerprint) {
|
|
||||||
//TODO
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean trustNewINSFingerprint(String oldCAFingerprint, String newCAFingerprint) {
|
|
||||||
//TODO
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a get request for a file from the currently connected server
|
|
||||||
* @param type type of file requested (UNKNOWN for files not supported by the protocol)
|
|
||||||
* @param path path to file
|
|
||||||
* @param test idfk
|
|
||||||
* @return resolved file
|
|
||||||
* @param <T> java object type
|
|
||||||
*/
|
|
||||||
public <T> Promise<? extends NetTransmitFile<T>> get(NTFType type, String path, T test) {
|
|
||||||
try {
|
|
||||||
this.getClientServerConnection().sendPacket(new WebRequestPacket(
|
|
||||||
path,
|
|
||||||
WebRequestMethod.GET,
|
|
||||||
Map.of(),
|
|
||||||
null
|
|
||||||
), TransportProtocol.TCP);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.promise(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
public void post(String path) {
|
|
||||||
try {
|
|
||||||
this.getClientServerConnection().sendPacket(new WebRequestPacket(
|
|
||||||
path,
|
|
||||||
WebRequestMethod.POST,
|
|
||||||
Map.of(),
|
|
||||||
null
|
|
||||||
), TransportProtocol.TCP);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get index.html Document of current site
|
|
||||||
* @return index.html
|
|
||||||
*/
|
|
||||||
public Promise<NetTransmitFile<Document>> getIndex() {
|
|
||||||
try {
|
|
||||||
this.getClientServerConnection().sendPacket(new WebRequestPacket(
|
|
||||||
"index.html",
|
|
||||||
WebRequestMethod.GET,
|
|
||||||
Map.of(),
|
|
||||||
null
|
|
||||||
), TransportProtocol.TCP);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println(promise(NTFType.DOCUMENT).getClass().getSimpleName());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return null;//(Promise<NetTransmitFile<Document>>) promise(NTFType.DOCUMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 16 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.network.handlers;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketReadEvent;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketSendEvent;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
|
||||||
import org.openautonomousconnection.protocol.packets.OACPacket;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.AuthPacket;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
|
||||||
import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolINSServerEvent;
|
|
||||||
import org.openautonomousconnection.webclient.packetlistener.PacketListener;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ServerPacketHandler extends EventListener {
|
|
||||||
|
|
||||||
private static final List<PacketListener> listeners = new ArrayList<>();
|
|
||||||
|
|
||||||
public static void registerPacketListener(PacketListener packetListener) {
|
|
||||||
listeners.add(packetListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Listener
|
|
||||||
public void onPacketReceived(C_PacketReadEvent packetReceivedEvent) {
|
|
||||||
|
|
||||||
Packet packet = packetReceivedEvent.getPacket();
|
|
||||||
TransportProtocol transport = packetReceivedEvent.getProtocol();
|
|
||||||
|
|
||||||
switch (packet.getPacketID()) {
|
|
||||||
case 4 -> listeners
|
|
||||||
.forEach(l -> l.onAuthPacketReceived((AuthPacket) packet, transport));
|
|
||||||
case 9 -> listeners
|
|
||||||
.forEach(l -> l.onWebResponsePacketReceived((WebResponsePacket) packet, transport));}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 17 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.network.type;
|
|
||||||
|
|
||||||
public enum NTFType {
|
|
||||||
UNKNOWN,
|
|
||||||
STYLESHEET,
|
|
||||||
DOCUMENT,
|
|
||||||
ICON,
|
|
||||||
FILE
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 17 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.network.type;
|
|
||||||
|
|
||||||
import org.openautonomousconnection.webclient.network.type.application.NetAppstreamFile;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.image.NetTransmitIcon;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.text.NetTransmitDocument;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.text.NetTransmitStylesheet;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
import org.w3c.dom.css.CSSStyleSheet;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public interface NetTransmitFile<T> {
|
|
||||||
T getNtf();
|
|
||||||
void setNtf(T ntf);
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
static <T> NetTransmitFile<T> of(NTFType type, T ntf) {
|
|
||||||
return (NetTransmitFile<T>) switch (type) {
|
|
||||||
case FILE -> new NetAppstreamFile((File) ntf);
|
|
||||||
|
|
||||||
case ICON -> new NetTransmitIcon((ImageIcon) ntf);
|
|
||||||
|
|
||||||
case DOCUMENT -> new NetTransmitDocument((Document) ntf);
|
|
||||||
case STYLESHEET -> new NetTransmitStylesheet((CSSStyleSheet) ntf);
|
|
||||||
|
|
||||||
case null, default -> new NetTransmitUnknown(ntf);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 17 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.network.type;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
public class NetTransmitUnknown implements NetTransmitFile<Object> {
|
|
||||||
@Getter @Setter
|
|
||||||
private Object ntf;
|
|
||||||
|
|
||||||
public NetTransmitUnknown() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetTransmitUnknown(Object ntf) {
|
|
||||||
this.ntf = ntf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 17 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.network.type.application;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class NetAppstreamFile implements NetTransmitFile<File> {
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private File ntf;
|
|
||||||
|
|
||||||
public NetAppstreamFile() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetAppstreamFile(File ntf) {
|
|
||||||
this.ntf = ntf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 17 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.network.type.image;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
public class NetTransmitIcon implements NetTransmitFile<ImageIcon> {
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private ImageIcon ntf;
|
|
||||||
|
|
||||||
public NetTransmitIcon() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetTransmitIcon(ImageIcon ntf) {
|
|
||||||
this.ntf = ntf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 17 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.network.type.text;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
|
|
||||||
public class NetTransmitDocument implements NetTransmitFile<Document> {
|
|
||||||
@Getter @Setter
|
|
||||||
private Document ntf;
|
|
||||||
|
|
||||||
public NetTransmitDocument() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetTransmitDocument(Document ntf) {
|
|
||||||
this.ntf = ntf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 17 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.network.type.text;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
|
||||||
import org.w3c.dom.css.CSSStyleSheet;
|
|
||||||
|
|
||||||
public class NetTransmitStylesheet implements NetTransmitFile<CSSStyleSheet> {
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private CSSStyleSheet ntf;
|
|
||||||
|
|
||||||
public NetTransmitStylesheet() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetTransmitStylesheet(CSSStyleSheet ntf) {
|
|
||||||
this.ntf = ntf;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Dec. 12 2025
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.network.website;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.openautonomousconnection.webclient.Main;
|
|
||||||
import org.openautonomousconnection.webclient.network.Promise;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.NTFType;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains data about a site
|
|
||||||
* Suppressing "unchecked" warnings because of generic casts
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public final class WebSite {
|
|
||||||
@Getter
|
|
||||||
private final URL infoName;
|
|
||||||
|
|
||||||
@Getter @Setter
|
|
||||||
private Document dom;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public WebSite(URL infoName) {
|
|
||||||
this.infoName = infoName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send new get request
|
|
||||||
*/
|
|
||||||
public Document requestNewDom() {
|
|
||||||
Document test = null;
|
|
||||||
|
|
||||||
Promise<NetTransmitFile<Document>> promise = (Promise<NetTransmitFile<Document>>)
|
|
||||||
Main.client.get(NTFType.DOCUMENT, this.infoName.getPath().toString(), test);
|
|
||||||
return promise.getObject().getNtf();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get favicon of site by attempting to retrieve the favicon.ico
|
|
||||||
*
|
|
||||||
* @return retrieved icon or null
|
|
||||||
*/
|
|
||||||
public Icon getFavIcon() {
|
|
||||||
return getFavIcon(this.infoName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get favicon of site by attempting to retrieve the favicon.ico
|
|
||||||
*
|
|
||||||
* @param infoName info name that leads to the site with the favicon
|
|
||||||
* @return retrieved icon or null
|
|
||||||
*/
|
|
||||||
public static @Nullable Icon getFavIcon(URL infoName) {
|
|
||||||
ImageIcon test = null;
|
|
||||||
|
|
||||||
Promise<NetTransmitFile<ImageIcon>> promise = (Promise<NetTransmitFile<ImageIcon>>)
|
|
||||||
Main.client.get(NTFType.ICON, infoName.getPath().toString(), test);
|
|
||||||
|
|
||||||
return promise.getObject().getNtf();
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Get HTML text (dom) of site
|
|
||||||
// *
|
|
||||||
// * @param infoName that leads to the target OAC page
|
|
||||||
// * @return html document
|
|
||||||
// */
|
|
||||||
// public static Document getDom(InfoName infoName) {
|
|
||||||
// Main.client.getDocument();
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (obj == this) return true;
|
|
||||||
if (obj == null || obj.getClass() != this.getClass()) return false;
|
|
||||||
var that = (WebSite) obj;
|
|
||||||
return Objects.equals(this.infoName, that.infoName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(infoName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "WebSite{" +
|
|
||||||
"infoName=" + this.infoName +
|
|
||||||
", dom=" + this.dom +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package org.openautonomousconnection.webclient.network.website.tab;
|
|
||||||
|
|
||||||
import org.openautonomousconnection.webclient.network.website.WebSite;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
public interface Tab {
|
|
||||||
Icon getFavicon();
|
|
||||||
void setFavicon(Icon favicon);
|
|
||||||
|
|
||||||
URL getInfoName();
|
|
||||||
void setInfoName(URL infoName);
|
|
||||||
|
|
||||||
WebSite getWebSite();
|
|
||||||
void setWebSite(WebSite webSite);
|
|
||||||
|
|
||||||
void refresh() throws Exception;
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Dec. 12 2025
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.network.website.tab;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.openautonomousconnection.webclient.network.WebClient;
|
|
||||||
import org.openautonomousconnection.webclient.network.website.WebSite;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static org.openautonomousconnection.webclient.Main.DEFAULT_WEB_PORT_TCP;
|
|
||||||
|
|
||||||
public final class WebTab implements Tab {
|
|
||||||
@Getter @Setter
|
|
||||||
private URL infoName;
|
|
||||||
|
|
||||||
@Getter @Setter
|
|
||||||
private Icon favicon;
|
|
||||||
|
|
||||||
@Getter @Setter
|
|
||||||
private WebSite webSite;
|
|
||||||
|
|
||||||
private WebClient client;
|
|
||||||
|
|
||||||
public WebTab(URL infoName, Icon favicon) throws Exception {
|
|
||||||
refresh();
|
|
||||||
|
|
||||||
this.infoName = infoName;
|
|
||||||
this.favicon = favicon;
|
|
||||||
this.webSite = new WebSite(infoName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebTab(URL infoName) throws Exception {
|
|
||||||
refresh();
|
|
||||||
|
|
||||||
this.infoName = infoName;
|
|
||||||
this.favicon = WebSite.getFavIcon(infoName);
|
|
||||||
this.webSite = new WebSite(infoName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebTab(WebSite webSite) throws Exception {
|
|
||||||
refresh();
|
|
||||||
|
|
||||||
this.infoName = webSite.getInfoName();
|
|
||||||
this.favicon = webSite.getFavIcon();
|
|
||||||
this.webSite = webSite;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refresh() throws Exception {
|
|
||||||
|
|
||||||
if (client.getClientServerConnection().isConnected()) client.getClientServerConnection().disconnect();
|
|
||||||
int port = infoName.getPort() == -1 ? DEFAULT_WEB_PORT_TCP : infoName.getPort();
|
|
||||||
client.buildServerConnection(null, true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
client.getClientServerConnection().connect(infoName.getHost(), port);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.webSite.requestNewDom();
|
|
||||||
this.favicon = this.webSite.getFavIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
WebTab webTab = (WebTab) o;
|
|
||||||
return Objects.equals(infoName, webTab.infoName) && Objects.equals(favicon, webTab.favicon) && Objects.equals(webSite, webTab.webSite) && Objects.equals(client, webTab.client);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(infoName, favicon, webSite, client);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "WebTab{" +
|
|
||||||
"infoName=" + infoName +
|
|
||||||
", favicon=" + favicon +
|
|
||||||
", webSite=" + webSite +
|
|
||||||
", client=" + client +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 16 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.packetlistener;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.AuthPacket;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
|
||||||
|
|
||||||
import java.util.concurrent.TransferQueue;
|
|
||||||
|
|
||||||
public abstract class PacketListener {
|
|
||||||
|
|
||||||
public void onAuthPacketReceived(AuthPacket authPacket, TransportProtocol transport) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onAuthPacketSent(AuthPacket authPacket, TransportProtocol transport) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onWebResponsePacketReceived(WebResponsePacket webResponsePacket, TransportProtocol transport) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onWebRequestPacketSent(WebRequestPacket webRequestPacket, TransportProtocol transport) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 16 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.packetlistener.listeners;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
|
||||||
import org.openautonomousconnection.webclient.Main;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.NTFType;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.image.NetTransmitIcon;
|
|
||||||
import org.openautonomousconnection.webclient.network.type.text.NetTransmitDocument;
|
|
||||||
import org.openautonomousconnection.webclient.packetlistener.PacketListener;
|
|
||||||
import org.openautonomousconnection.webclient.ui.dom.DomSerializer;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
public class WebPacketListener extends PacketListener {
|
|
||||||
@Override
|
|
||||||
public void onWebResponsePacketReceived(WebResponsePacket packet, TransportProtocol transport) {
|
|
||||||
NTFType type = NTFType.UNKNOWN;
|
|
||||||
|
|
||||||
NetTransmitFile<?> file = null;
|
|
||||||
|
|
||||||
/*
|
|
||||||
full list of content types at https://repo.open-autonomous-connection.org/open-autonomous-connection/WebServer/src/branch/master/src/main/java/org/openautonomousconnection/webserver/ContentTypeResolver.java
|
|
||||||
*/
|
|
||||||
switch (packet.getContentType()) {
|
|
||||||
case "text/html":
|
|
||||||
type = NTFType.DOCUMENT;
|
|
||||||
|
|
||||||
// body is sent as byte array (string)
|
|
||||||
|
|
||||||
String htmlText = new String(packet.getBody());
|
|
||||||
|
|
||||||
Document dom = DomSerializer.fromString(Main.mainFrame.getDomContainerPanel().getWebEngine(), htmlText);
|
|
||||||
|
|
||||||
file = new NetTransmitDocument();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "image/png": // TODO: Implement
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "image/ico": // the server doesn't implement this yet, so favicons won't work at this moment
|
|
||||||
type = NTFType.ICON;
|
|
||||||
|
|
||||||
ImageIcon icon = new ImageIcon(packet.getBody());
|
|
||||||
|
|
||||||
file = new NetTransmitIcon(icon);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// the same as application/octet-stream
|
|
||||||
default:
|
|
||||||
break; //TODO: stream files
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Main.client.fulfillPromise(type, file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package org.openautonomousconnection.webclient.recode;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.openautonomousconnection.infonamelib.InfoNames;
|
|
||||||
import org.openautonomousconnection.oacswing.component.OACOptionPane;
|
|
||||||
import org.openautonomousconnection.oacswing.component.design.Design;
|
|
||||||
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
|
||||||
import org.openautonomousconnection.protocol.ProtocolBridge;
|
|
||||||
import org.openautonomousconnection.protocol.ProtocolValues;
|
|
||||||
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
|
||||||
import org.openautonomousconnection.webclient.recode.settings.INSList;
|
|
||||||
import org.openautonomousconnection.webclient.recode.ui.BrowserUI;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
@Getter
|
|
||||||
private static ClientImpl client;
|
|
||||||
private static ProtocolBridge bridge;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private static BrowserUI ui;
|
|
||||||
|
|
||||||
private static void initProtocol() {
|
|
||||||
InfoNames.registerOACInfoNameProtocols();
|
|
||||||
|
|
||||||
ProtocolValues values = new ProtocolValues();
|
|
||||||
|
|
||||||
values.packetHandler = new PacketHandler();
|
|
||||||
values.eventManager = new EventManager();
|
|
||||||
|
|
||||||
client = new ClientImpl();
|
|
||||||
|
|
||||||
try {
|
|
||||||
bridge = new ProtocolBridge(
|
|
||||||
client,
|
|
||||||
values,
|
|
||||||
ProtocolVersion.PV_1_0_0_BETA,
|
|
||||||
new File("logs")
|
|
||||||
);
|
|
||||||
|
|
||||||
client.buildINSConnection();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
initProtocol();
|
|
||||||
FxBootstrap.ensureInitialized();
|
|
||||||
DesignManager.setGlobalDesign(Design.DARK);
|
|
||||||
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
ui = new BrowserUI();
|
|
||||||
ui.setSize(1200, 800);
|
|
||||||
ui.setLocationRelativeTo(null);
|
|
||||||
ui.setVisible(true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
bridge.getProtocolValues().eventManager.registerListener(client);
|
|
||||||
client.getClientINSConnection().connect(INSList.DEFAULT_INS, INSList.DEFAULT_PORT);
|
|
||||||
} catch (Exception exception) {
|
|
||||||
client.getProtocolBridge().getLogger().exception("Failed to connect to INS", exception);
|
|
||||||
OACOptionPane.showMessageDialog(Main.getUi(), "Failed to connect to INS Server:\n" + exception.getMessage(),
|
|
||||||
"INS Connection", OACOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
package org.openautonomousconnection.webclient.recode.ui;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketReadEvent;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.embed.swing.JFXPanel;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.scene.web.WebEngine;
|
|
||||||
import javafx.scene.web.WebHistory;
|
|
||||||
import javafx.scene.web.WebView;
|
|
||||||
import org.openautonomousconnection.infonamelib.InfoNames;
|
|
||||||
import org.openautonomousconnection.oacswing.component.OACOptionPane;
|
|
||||||
import org.openautonomousconnection.oacswing.component.OACPanel;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
|
||||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecordType;
|
|
||||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod;
|
|
||||||
import org.openautonomousconnection.webclient.recode.Main;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Swing panel that embeds a JavaFX WebView.
|
|
||||||
* The JavaFX scene is initialized on first addNotify().
|
|
||||||
*/
|
|
||||||
public final class TabView extends OACPanel {
|
|
||||||
|
|
||||||
private final AtomicBoolean initialized = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
private JFXPanel fxPanel;
|
|
||||||
private WebView webView;
|
|
||||||
private WebEngine engine;
|
|
||||||
|
|
||||||
private final Consumer<String> onLocationChanged;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new tab view.
|
|
||||||
*
|
|
||||||
* @param onLocationChanged callback invoked when the WebEngine location changes
|
|
||||||
*/
|
|
||||||
public TabView(Consumer<String> onLocationChanged) {
|
|
||||||
super();
|
|
||||||
this.onLocationChanged = Objects.requireNonNull(onLocationChanged, "onLocationChanged");
|
|
||||||
setLayout(new BorderLayout());
|
|
||||||
Main.getClient().getProtocolBridge().getProtocolValues().eventManager.registerListener(new TabListener(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addNotify() {
|
|
||||||
super.addNotify();
|
|
||||||
|
|
||||||
if (!initialized.compareAndSet(false, true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fxPanel = new JFXPanel();
|
|
||||||
add(fxPanel, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
webView = new WebView();
|
|
||||||
webView.setContextMenuEnabled(false);
|
|
||||||
webView.getEngine().setJavaScriptEnabled(false);
|
|
||||||
engine = webView.getEngine();
|
|
||||||
|
|
||||||
engine.locationProperty().addListener((obs, oldV, newV) -> {
|
|
||||||
if (newV != null) {
|
|
||||||
onLocationChanged.accept(newV);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fxPanel.setScene(new Scene(webView));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a URL in this tab.
|
|
||||||
*
|
|
||||||
* @param url URL to load
|
|
||||||
*/
|
|
||||||
public void load(String url) {
|
|
||||||
String[] parts = url.split("\\.");
|
|
||||||
if (parts.length < 2 || parts.length > 3) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Invalid INS address format: " + url +
|
|
||||||
" (expected name.tln or sub.name.tln)"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String tln = parts[parts.length - 1];
|
|
||||||
String name = parts[parts.length - 2];
|
|
||||||
String sub = (parts.length == 3) ? parts[0] : null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Main.getClient().sendINSQuery(tln, name, sub, INSRecordType.A);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Main.getClient().getProtocolBridge().getLogger().exception("Failed to send INS Query", e);
|
|
||||||
OACOptionPane.showMessageDialog(Main.getUi(), "Failed to send INS Query:\n" + e.getMessage(),
|
|
||||||
"INS Connection", OACOptionPane.ERROR_MESSAGE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TabListener extends EventListener {
|
|
||||||
private TabView view;
|
|
||||||
|
|
||||||
public TabListener(TabView view) {
|
|
||||||
this.view = view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Listener
|
|
||||||
public void onListen(C_PacketReadEvent event) {
|
|
||||||
if (event.getPacket() instanceof WebResponsePacket response) {
|
|
||||||
view.parseHtml(new String(response.getBody()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void parseHtml(String html) {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
if (engine != null) engine.loadContent(html);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reloads the current page.
|
|
||||||
*/
|
|
||||||
public void reload() {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
if (engine != null) {
|
|
||||||
engine.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Navigates one step back in history if possible.
|
|
||||||
*/
|
|
||||||
public void back() {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
if (engine == null) return;
|
|
||||||
WebHistory h = engine.getHistory();
|
|
||||||
if (h.getCurrentIndex() > 0) {
|
|
||||||
h.go(-1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Navigates one step forward in history if possible.
|
|
||||||
*/
|
|
||||||
public void forward() {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
if (engine == null) return;
|
|
||||||
WebHistory h = engine.getHistory();
|
|
||||||
if (h.getCurrentIndex() < h.getEntries().size() - 1) {
|
|
||||||
h.go(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current location (best-effort, may be null until initialized).
|
|
||||||
*
|
|
||||||
* @return current location or null
|
|
||||||
*/
|
|
||||||
public String getEngineLocation() {
|
|
||||||
// No blocking: best-effort for UI sync.
|
|
||||||
return engine != null ? engine.getLocation() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes JavaFX scene references (best-effort).
|
|
||||||
*/
|
|
||||||
public void dispose() {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
if (engine != null) {
|
|
||||||
try {
|
|
||||||
engine.load(null);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
// Best-effort cleanup.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
engine = null;
|
|
||||||
webView = null;
|
|
||||||
if (fxPanel != null) {
|
|
||||||
fxPanel.setScene(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package org.openautonomousconnection.webclient.settings;
|
||||||
|
|
||||||
|
import javafx.concurrent.Worker;
|
||||||
|
import javafx.scene.web.WebEngine;
|
||||||
|
import javafx.scene.web.WebView;
|
||||||
|
import org.luaj.vm2.Globals;
|
||||||
|
import org.openautonomousconnection.luascript.fx.FxDomHost;
|
||||||
|
import org.openautonomousconnection.luascript.fx.FxEventHost;
|
||||||
|
import org.openautonomousconnection.luascript.fx.FxWebViewResourceHost;
|
||||||
|
import org.openautonomousconnection.luascript.hosts.HostServices;
|
||||||
|
import org.openautonomousconnection.luascript.runtime.LuaRuntime;
|
||||||
|
import org.openautonomousconnection.luascript.security.LuaExecutionPolicy;
|
||||||
|
import org.openautonomousconnection.luascript.utils.LuaGlobalsFactory;
|
||||||
|
import org.openautonomousconnection.webclient.lua.WebLogger;
|
||||||
|
import org.openautonomousconnection.webclient.lua.hosts.ConsoleHostImpl;
|
||||||
|
import org.openautonomousconnection.webclient.lua.hosts.UiHostImpl;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JavaFX WebView integration entry point for LuaScript (no JavaScript).
|
||||||
|
*
|
||||||
|
* <p>Hard rule: every HTML script tag is treated as Lua.</p>
|
||||||
|
*/
|
||||||
|
public final class FxEngine implements AutoCloseable {
|
||||||
|
|
||||||
|
private final WebEngine engine;
|
||||||
|
private final WebView webView;
|
||||||
|
private final LuaExecutionPolicy policy;
|
||||||
|
private final WebLogger logger;
|
||||||
|
|
||||||
|
private final AtomicBoolean bootstrapped = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
private LuaRuntime runtime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an integration engine with default UI execution policy.
|
||||||
|
*
|
||||||
|
* @param engine web engine
|
||||||
|
* @param webView web view
|
||||||
|
*/
|
||||||
|
public FxEngine(WebEngine engine, WebView webView, WebLogger logger) {
|
||||||
|
this(engine, webView, LuaExecutionPolicy.uiDefault(), logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an integration engine with a custom execution policy.
|
||||||
|
*
|
||||||
|
* @param engine web engine
|
||||||
|
* @param webView web view
|
||||||
|
* @param policy execution policy
|
||||||
|
*/
|
||||||
|
public FxEngine(WebEngine engine, WebView webView, LuaExecutionPolicy policy, WebLogger logger) {
|
||||||
|
this.engine = Objects.requireNonNull(engine, "engine");
|
||||||
|
this.webView = webView;
|
||||||
|
this.policy = Objects.requireNonNull(policy, "policy");
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs a load hook that bootstraps Lua when a page finished loading.
|
||||||
|
*/
|
||||||
|
public void install() {
|
||||||
|
engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
|
||||||
|
if (newState == Worker.State.SUCCEEDED) {
|
||||||
|
bootstrapped.set(false);
|
||||||
|
bootstrap();
|
||||||
|
} else if (newState == Worker.State.CANCELLED || newState == Worker.State.FAILED) {
|
||||||
|
bootstrapped.set(false);
|
||||||
|
closeRuntimeQuietly();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstraps Lua for the currently loaded document.
|
||||||
|
*/
|
||||||
|
public void bootstrap() {
|
||||||
|
if (!bootstrapped.compareAndSet(false, true)) return;
|
||||||
|
|
||||||
|
closeRuntimeQuietly();
|
||||||
|
|
||||||
|
// DOM host must exist before event/UI tables, and must ensure stable ids.
|
||||||
|
FxDomHost dom = new FxDomHost(engine);
|
||||||
|
dom.ensureAllElementsHaveId();
|
||||||
|
|
||||||
|
// Create per-page globals; harden sandbox in production.
|
||||||
|
Globals globals = LuaGlobalsFactory.create(
|
||||||
|
new LuaGlobalsFactory.Options()
|
||||||
|
.enableDebug(false)
|
||||||
|
.sandbox(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create runtime first (router lives inside it).
|
||||||
|
ConsoleHostImpl console = new ConsoleHostImpl(logger);
|
||||||
|
UiHostImpl uiHost = new UiHostImpl(engine, webView, dom);
|
||||||
|
FxWebViewResourceHost resourceHost = new FxWebViewResourceHost(engine);
|
||||||
|
|
||||||
|
// runtime depends on services; events depends on runtime router.
|
||||||
|
// We'll create eventHost after runtime, then build HostServices with it.
|
||||||
|
LuaRuntime rt = new LuaRuntime(globals, new HostServices.Default(uiHost, dom, null, resourceHost, console), policy);
|
||||||
|
|
||||||
|
FxEventHost eventHost = new FxEventHost(dom, rt.eventRouter());
|
||||||
|
|
||||||
|
// Rebuild services including eventHost and reinstall tables.
|
||||||
|
HostServices services = new HostServices.Default(uiHost, dom, eventHost, resourceHost, console);
|
||||||
|
|
||||||
|
// Replace runtime with correct services (clean and deterministic).
|
||||||
|
rt.close();
|
||||||
|
rt = new LuaRuntime(globals, services, policy);
|
||||||
|
|
||||||
|
rt.installStdTables(true);
|
||||||
|
rt.bootstrapFromDom();
|
||||||
|
|
||||||
|
this.runtime = rt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns active runtime or null if not bootstrapped.
|
||||||
|
*
|
||||||
|
* @return runtime or null
|
||||||
|
*/
|
||||||
|
public LuaRuntime runtimeOrNull() {
|
||||||
|
return runtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
closeRuntimeQuietly();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeRuntimeQuietly() {
|
||||||
|
LuaRuntime rt = this.runtime;
|
||||||
|
this.runtime = null;
|
||||||
|
if (rt != null) {
|
||||||
|
try {
|
||||||
|
rt.close();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
// Best-effort shutdown.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
package org.openautonomousconnection.webclient.recode.settings;
|
package org.openautonomousconnection.webclient.settings;
|
||||||
|
|
||||||
import javafx.embed.swing.JFXPanel;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Dec. 12 2025
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.ui;
|
|
||||||
|
|
||||||
import org.openautonomousconnection.oacswing.component.OACFrame;
|
|
||||||
|
|
||||||
public abstract class BrowserFrame extends OACFrame {
|
|
||||||
|
|
||||||
protected BrowserFrame() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
package org.openautonomousconnection.webclient.recode.ui;
|
package org.openautonomousconnection.webclient.ui;
|
||||||
|
|
||||||
import org.openautonomousconnection.webclient.recode.Main;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@@ -22,7 +20,7 @@ public class BrowserTab {
|
|||||||
*/
|
*/
|
||||||
public BrowserTab(String initialUrl, Consumer<String> onLocationChange) {
|
public BrowserTab(String initialUrl, Consumer<String> onLocationChange) {
|
||||||
this.key = Objects.requireNonNull(initialUrl, "initialUrl"); // placeholder key overwritten by BrowserUI
|
this.key = Objects.requireNonNull(initialUrl, "initialUrl"); // placeholder key overwritten by BrowserUI
|
||||||
this.view = new TabView(onLocationChange);
|
this.view = new TabView(onLocationChange, initialUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,8 +39,8 @@ public class BrowserTab {
|
|||||||
*
|
*
|
||||||
* @param url URL
|
* @param url URL
|
||||||
*/
|
*/
|
||||||
public void load(String url) {
|
public void loadUrl(String url) {
|
||||||
view.load(url);
|
view.loadUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -125,8 +123,8 @@ public class BrowserTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load(String url) {
|
public void loadUrl(String url) {
|
||||||
fixedView.load(url);
|
fixedView.loadUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.openautonomousconnection.webclient.recode.ui;
|
package org.openautonomousconnection.webclient.ui;
|
||||||
|
|
||||||
import org.openautonomousconnection.oacswing.component.OACButton;
|
import org.openautonomousconnection.oacswing.component.OACButton;
|
||||||
import org.openautonomousconnection.oacswing.component.OACFrame;
|
import org.openautonomousconnection.oacswing.component.OACFrame;
|
||||||
@@ -109,7 +109,7 @@ public class BrowserUI extends OACFrame {
|
|||||||
if (tab != null) tab.reload();
|
if (tab != null) tab.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
newTabButton.addActionListener(e -> openNewTab("info.oac"));
|
newTabButton.addActionListener(e -> openNewTab("web://info.oac/"));
|
||||||
closeTabButton.addActionListener(e -> closeCurrentTab());
|
closeTabButton.addActionListener(e -> closeCurrentTab());
|
||||||
|
|
||||||
// Create first tab
|
// Create first tab
|
||||||
@@ -143,7 +143,7 @@ public class BrowserUI extends OACFrame {
|
|||||||
getTitleBar().getTabs().setSelectedIndex(idx);
|
getTitleBar().getTabs().setSelectedIndex(idx);
|
||||||
|
|
||||||
// Navigate
|
// Navigate
|
||||||
tab.load(url);
|
tab.loadUrl(url);
|
||||||
|
|
||||||
// Show content
|
// Show content
|
||||||
cardLayout.show(pageHost, key);
|
cardLayout.show(pageHost, key);
|
||||||
@@ -165,7 +165,7 @@ public class BrowserUI extends OACFrame {
|
|||||||
|
|
||||||
String url = normalizeUrl(input);
|
String url = normalizeUrl(input);
|
||||||
addressField.setText(url);
|
addressField.setText(url);
|
||||||
tab.load(url);
|
tab.loadUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,7 +187,7 @@ public class BrowserUI extends OACFrame {
|
|||||||
|
|
||||||
// If no tabs left, open a new one
|
// If no tabs left, open a new one
|
||||||
if (getTitleBar().getTabs().getTabCount() == 0) {
|
if (getTitleBar().getTabs().getTabCount() == 0) {
|
||||||
openNewTab("info.oac");
|
openNewTab("web://info.oac/");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,8 +225,13 @@ public class BrowserUI extends OACFrame {
|
|||||||
|
|
||||||
private static String normalizeUrl(String input) {
|
private static String normalizeUrl(String input) {
|
||||||
String s = input == null ? "" : input.trim();
|
String s = input == null ? "" : input.trim();
|
||||||
if (s.isEmpty()) return "info.oac";
|
if (s.isEmpty()) return "web://info.oac/";
|
||||||
if (s.startsWith("web://")) return s;
|
if (s.startsWith("web://")) {
|
||||||
return "web://" + s;
|
// Ensure trailing slash for "host only" URLs
|
||||||
|
String rest = s.substring("web://".length());
|
||||||
|
if (!rest.contains("/")) return s + "/";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return "web://" + s + (s.contains("/") ? "" : "/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.openautonomousconnection.webclient.recode;
|
package org.openautonomousconnection.webclient.ui;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.embed.swing.JFXPanel;
|
import javafx.embed.swing.JFXPanel;
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Dec. 12 2025
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.ui;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.openautonomousconnection.webclient.network.website.tab.WebTab;
|
|
||||||
import org.openautonomousconnection.webclient.ui.dom.DOMContainerPanel;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
public final class MainFrame extends BrowserFrame {
|
|
||||||
@Getter
|
|
||||||
private WebTab openWebTab;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final DOMContainerPanel domContainerPanel;
|
|
||||||
|
|
||||||
public MainFrame() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.setSize(800, 600);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// TODO this.openTab = new Tab(URI.create("web://127.0.0.1").toURL());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.domContainerPanel = new DOMContainerPanel();
|
|
||||||
this.add(this.domContainerPanel, BorderLayout.CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
this.open(this.openWebTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOpenWebTab(int index) {
|
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOpenWebTab(WebTab webTab) {
|
|
||||||
//TODO
|
|
||||||
// for(TabButton button : this.getTopBar().getTabButtons())
|
|
||||||
// if(button.getTab().equals(tab)) {
|
|
||||||
// this.openTab = button.getTab();
|
|
||||||
// button.grabFocus();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void open(URL url) throws Exception {
|
|
||||||
//TODO
|
|
||||||
// TabButton button = new TabButton(url);
|
|
||||||
//
|
|
||||||
// this.getTopBar().addButton(button);
|
|
||||||
//
|
|
||||||
// button.grabFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void open(WebTab webTab) {
|
|
||||||
//TODO
|
|
||||||
// TabButton button = new TabButton(tab);
|
|
||||||
//
|
|
||||||
// this.getTopBar().addButton(button);
|
|
||||||
//
|
|
||||||
// button.grabFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 18 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.ui;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.openautonomousconnection.webclient.Main;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.FocusEvent;
|
|
||||||
import java.awt.event.FocusListener;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.awt.event.MouseListener;
|
|
||||||
|
|
||||||
public class MenuButton extends JButton {
|
|
||||||
@Getter
|
|
||||||
private ClickAction clickAction;
|
|
||||||
|
|
||||||
public MenuButton(Icon icon, ClickAction clickAction) {
|
|
||||||
this.setIcon(icon);
|
|
||||||
|
|
||||||
this.clickAction = clickAction;
|
|
||||||
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuButton(String text, ClickAction clickAction) {
|
|
||||||
this.setText(text);
|
|
||||||
|
|
||||||
this.clickAction = clickAction;
|
|
||||||
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
this.setBackground(Color.gray);
|
|
||||||
|
|
||||||
this.setForeground(Color.lightGray);
|
|
||||||
|
|
||||||
this.setBorderPainted(false);
|
|
||||||
|
|
||||||
this.addActionListener(e -> {
|
|
||||||
|
|
||||||
|
|
||||||
switch (this.clickAction) {
|
|
||||||
case CLOSE -> Main.mainFrame.dispose();
|
|
||||||
case MINIMIZE -> Main.mainFrame.setState(JFrame.ICONIFIED);
|
|
||||||
case MAXIMIZE -> {
|
|
||||||
if(Main.mainFrame.getExtendedState() == JFrame.NORMAL) {
|
|
||||||
this.setText("(_)");
|
|
||||||
|
|
||||||
Main.mainFrame.setExtendedState(
|
|
||||||
Main.mainFrame.getExtendedState() | JFrame.MAXIMIZED_BOTH
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.setText("[]");
|
|
||||||
Main.mainFrame.setExtendedState(JFrame.NORMAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addFocusListener(new FocusListener() {
|
|
||||||
@Override
|
|
||||||
public void focusGained(FocusEvent e) {
|
|
||||||
setBorderPainted(false);
|
|
||||||
// setBackground(Color.getHSBColor(0.65f, 0.5f, 0.6f));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void focusLost(FocusEvent e) {
|
|
||||||
// setBackground(Color.gray);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addMouseListener(new MouseListener() {
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mousePressed(MouseEvent e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseReleased(MouseEvent e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseEntered(MouseEvent e) {
|
|
||||||
setBackground(Color.getHSBColor(0.65f, 0.3f, 0.5f));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseExited(MouseEvent e) {
|
|
||||||
setBackground(Color.gray);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ClickAction {
|
|
||||||
CLOSE,
|
|
||||||
MINIMIZE,
|
|
||||||
MAXIMIZE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 19 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.ui;
|
|
||||||
|
|
||||||
import javax.swing.border.Border;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.geom.RoundRectangle2D;
|
|
||||||
|
|
||||||
public class RoundedBorder implements Border {
|
|
||||||
|
|
||||||
private final int radius;
|
|
||||||
|
|
||||||
public RoundedBorder(int radius) {
|
|
||||||
this.radius = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Insets getBorderInsets(Component c) {
|
|
||||||
return new Insets(radius, radius, radius, radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBorderOpaque() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
|
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
|
||||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
|
||||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
|
||||||
|
|
||||||
g2.setColor(c.getForeground());
|
|
||||||
g2.draw(new RoundRectangle2D.Double(
|
|
||||||
x, y,
|
|
||||||
width - 1, height - 1,
|
|
||||||
radius, radius
|
|
||||||
));
|
|
||||||
|
|
||||||
g2.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
package org.openautonomousconnection.webclient.ui;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.embed.swing.JFXPanel;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.web.WebEngine;
|
||||||
|
import javafx.scene.web.WebHistory;
|
||||||
|
import javafx.scene.web.WebView;
|
||||||
|
import org.openautonomousconnection.oacswing.component.OACPanel;
|
||||||
|
import org.openautonomousconnection.webclient.lua.WebLogger;
|
||||||
|
import org.openautonomousconnection.webclient.settings.FxEngine;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Swing panel that embeds a JavaFX WebView and runs LuaScript (no JavaScript).
|
||||||
|
*
|
||||||
|
* <p>Loads "web://" URLs so JavaFX can request subresources (CSS, images, href navigations)
|
||||||
|
* through URLConnection (mapped to OAC WebRequestPacket).</p>
|
||||||
|
*/
|
||||||
|
public final class TabView extends OACPanel {
|
||||||
|
|
||||||
|
private final AtomicBoolean initialized = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
private JFXPanel fxPanel;
|
||||||
|
private WebView webView;
|
||||||
|
private WebEngine engine;
|
||||||
|
|
||||||
|
private final Consumer<String> onLocationChanged;
|
||||||
|
private final WebLogger webLogger;
|
||||||
|
|
||||||
|
private volatile FxEngine luaEngine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new tab view.
|
||||||
|
*
|
||||||
|
* @param onLocationChanged callback invoked when the WebEngine location changes
|
||||||
|
* @param url callback invoked on URL changes
|
||||||
|
*/
|
||||||
|
public TabView(Consumer<String> onLocationChanged, String url) {
|
||||||
|
super();
|
||||||
|
this.onLocationChanged = Objects.requireNonNull(onLocationChanged, "onLocationChanged");
|
||||||
|
this.webLogger = new WebLogger(url);
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addNotify() {
|
||||||
|
super.addNotify();
|
||||||
|
|
||||||
|
if (!initialized.compareAndSet(false, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fxPanel = new JFXPanel();
|
||||||
|
add(fxPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
webView = new WebView();
|
||||||
|
webView.setContextMenuEnabled(false);
|
||||||
|
|
||||||
|
engine = webView.getEngine();
|
||||||
|
engine.setJavaScriptEnabled(false);
|
||||||
|
|
||||||
|
engine.locationProperty().addListener((obs, oldV, newV) -> {
|
||||||
|
if (newV != null) {
|
||||||
|
onLocationChanged.accept(newV);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Proper Lua integration from your library
|
||||||
|
luaEngine = new FxEngine(engine, webView, webLogger);
|
||||||
|
luaEngine.install();
|
||||||
|
|
||||||
|
fxPanel.setScene(new Scene(webView));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a normalized URL (expected: web://...).
|
||||||
|
*
|
||||||
|
* @param url URL to load
|
||||||
|
*/
|
||||||
|
public void loadUrl(String url) {
|
||||||
|
String u = Objects.requireNonNull(url, "url").trim();
|
||||||
|
if (u.isEmpty()) return;
|
||||||
|
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (engine != null) {
|
||||||
|
engine.load(u);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reloads the current page.
|
||||||
|
*/
|
||||||
|
public void reload() {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (engine != null) engine.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates one step back in history if possible.
|
||||||
|
*/
|
||||||
|
public void back() {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (engine == null) return;
|
||||||
|
WebHistory h = engine.getHistory();
|
||||||
|
if (h.getCurrentIndex() > 0) h.go(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates one step forward in history if possible.
|
||||||
|
*/
|
||||||
|
public void forward() {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (engine == null) return;
|
||||||
|
WebHistory h = engine.getHistory();
|
||||||
|
if (h.getCurrentIndex() < h.getEntries().size() - 1) h.go(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current engine location.
|
||||||
|
*
|
||||||
|
* @return location or null
|
||||||
|
*/
|
||||||
|
public String getEngineLocation() {
|
||||||
|
return engine != null ? engine.getLocation() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes resources.
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
FxEngine le = luaEngine;
|
||||||
|
luaEngine = null;
|
||||||
|
if (le != null) {
|
||||||
|
try {
|
||||||
|
le.close();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (engine != null) {
|
||||||
|
try {
|
||||||
|
engine.load(null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
engine = null;
|
||||||
|
webView = null;
|
||||||
|
if (fxPanel != null) {
|
||||||
|
fxPanel.setScene(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Jan. 18 2026
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.ui;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.openautonomousconnection.webclient.Main;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.awt.event.MouseMotionAdapter;
|
|
||||||
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
public class TopBar extends JPanel {
|
|
||||||
@Getter
|
|
||||||
private MenuButton closeButton, minimizeButton, maximizeButton;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private JPanel buttonPanel;
|
|
||||||
|
|
||||||
private Point dragOffset;
|
|
||||||
|
|
||||||
public TopBar() {
|
|
||||||
this.setPreferredSize(new Dimension(0, 40));
|
|
||||||
|
|
||||||
this.buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
|
||||||
|
|
||||||
this.closeButton = new MenuButton("X", MenuButton.ClickAction.CLOSE);
|
|
||||||
this.minimizeButton = new MenuButton("-", MenuButton.ClickAction.MINIMIZE);
|
|
||||||
this.maximizeButton = new MenuButton("[]", MenuButton.ClickAction.MAXIMIZE);
|
|
||||||
|
|
||||||
this.buttonPanel.add(this.closeButton);
|
|
||||||
this.buttonPanel.add(this.minimizeButton);
|
|
||||||
this.buttonPanel.add(this.maximizeButton);
|
|
||||||
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
this.addMouseListener(new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(MouseEvent e) {
|
|
||||||
Point mouseOnScreen = e.getLocationOnScreen();
|
|
||||||
Point frameLocation = Main.mainFrame.getLocation();
|
|
||||||
|
|
||||||
dragOffset = new Point(
|
|
||||||
mouseOnScreen.x - frameLocation.x,
|
|
||||||
mouseOnScreen.y - frameLocation.y
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseReleased(MouseEvent e) {
|
|
||||||
dragOffset = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addMouseMotionListener(new MouseMotionAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mouseDragged(MouseEvent e) {
|
|
||||||
if (dragOffset != null) {
|
|
||||||
Point mouseOnScreen = e.getLocationOnScreen();
|
|
||||||
|
|
||||||
int newX = mouseOnScreen.x - dragOffset.x;
|
|
||||||
int newY = mouseOnScreen.y - dragOffset.y;
|
|
||||||
|
|
||||||
Main.mainFrame.setLocation(newX, newY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initButtonPanel(String alignment) {
|
|
||||||
this.setLayout(new BorderLayout());
|
|
||||||
|
|
||||||
this.add(this.buttonPanel, alignment);
|
|
||||||
|
|
||||||
this.repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Dec. 12 2025
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.ui.dom;
|
|
||||||
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.embed.swing.JFXPanel;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.scene.web.WebEngine;
|
|
||||||
import javafx.scene.web.WebView;
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.openautonomousconnection.oacswing.component.OACPanel;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
public class DOMContainerPanel extends OACPanel {
|
|
||||||
@Getter
|
|
||||||
private final JFXPanel dom;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private WebView webView;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private WebEngine webEngine;
|
|
||||||
|
|
||||||
public void loadContent(String html) {
|
|
||||||
this.webEngine.loadContent(html);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadContent(Document html) {
|
|
||||||
this.loadContent(DomSerializer.toString(html));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DOMContainerPanel() {
|
|
||||||
this.setBackground(Color.LIGHT_GRAY);
|
|
||||||
|
|
||||||
//TODO: Turn this into designable OAC-JFXpanel
|
|
||||||
this.dom = new JFXPanel();
|
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
this.webView = new WebView();
|
|
||||||
this.webEngine = this.webView.getEngine();
|
|
||||||
this.webEngine.setJavaScriptEnabled(false);
|
|
||||||
this.dom.setScene(new Scene(this.webView));
|
|
||||||
});
|
|
||||||
|
|
||||||
this.add(this.dom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
package org.openautonomousconnection.webclient.ui.dom;
|
|
||||||
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
|
|
||||||
import javax.xml.transform.OutputKeys;
|
|
||||||
import javax.xml.transform.Transformer;
|
|
||||||
import javax.xml.transform.TransformerFactory;
|
|
||||||
import javax.xml.transform.dom.DOMSource;
|
|
||||||
import javax.xml.transform.stream.StreamResult;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.scene.web.WebEngine;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializes a W3C DOM Document to an HTML string.
|
|
||||||
*
|
|
||||||
* <p>Works with JavaFX WebEngine DOM.</p>
|
|
||||||
*/
|
|
||||||
public final class DomSerializer {
|
|
||||||
|
|
||||||
private DomSerializer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a DOM {@link Document} to a string.
|
|
||||||
*
|
|
||||||
* @param document DOM document
|
|
||||||
* @return serialized HTML
|
|
||||||
*/
|
|
||||||
public static String toString(Document document) {
|
|
||||||
if (document == null) {
|
|
||||||
throw new IllegalArgumentException("document is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
TransformerFactory tf = TransformerFactory.newInstance();
|
|
||||||
Transformer transformer = tf.newTransformer();
|
|
||||||
|
|
||||||
transformer.setOutputProperty(OutputKeys.METHOD, "html");
|
|
||||||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
|
|
||||||
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
|
|
||||||
transformer.setOutputProperty(OutputKeys.INDENT, "no");
|
|
||||||
|
|
||||||
StringWriter writer = new StringWriter();
|
|
||||||
transformer.transform(
|
|
||||||
new DOMSource(document),
|
|
||||||
new StreamResult(writer)
|
|
||||||
);
|
|
||||||
return writer.toString();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Failed to serialize DOM document", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an HTML string into a JavaFX {@link WebEngine} and returns the resulting DOM {@link Document}.
|
|
||||||
*
|
|
||||||
* <p>No JavaScript required. Parsing is done by WebKit.</p>
|
|
||||||
*
|
|
||||||
* @param engine JavaFX WebEngine (must not be null)
|
|
||||||
* @param html HTML source
|
|
||||||
* @return parsed DOM document
|
|
||||||
*/
|
|
||||||
public static Document fromString(WebEngine engine, String html) {
|
|
||||||
Objects.requireNonNull(engine, "engine");
|
|
||||||
Objects.requireNonNull(html, "html");
|
|
||||||
|
|
||||||
if (!Platform.isFxApplicationThread()) {
|
|
||||||
final Document[] result = new Document[1];
|
|
||||||
final RuntimeException[] error = new RuntimeException[1];
|
|
||||||
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
try {
|
|
||||||
result[0] = fromString(engine, html);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
error[0] = e;
|
|
||||||
} finally {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!latch.await(5, TimeUnit.SECONDS)) {
|
|
||||||
throw new RuntimeException("Timed out while parsing HTML");
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
throw new RuntimeException("Interrupted while parsing HTML", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error[0] != null) throw error[0];
|
|
||||||
return result[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
|
|
||||||
engine.getLoadWorker().stateProperty().addListener((obs, o, n) -> {
|
|
||||||
switch (n) {
|
|
||||||
case SUCCEEDED, FAILED, CANCELLED -> latch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
engine.loadContent(html, "text/html");
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!latch.await(5, TimeUnit.SECONDS)) {
|
|
||||||
throw new RuntimeException("Timed out while parsing HTML");
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
throw new RuntimeException("Interrupted while parsing HTML", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Document doc = engine.getDocument();
|
|
||||||
if (doc == null) {
|
|
||||||
throw new IllegalStateException("WebEngine did not produce a DOM document");
|
|
||||||
}
|
|
||||||
|
|
||||||
return doc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Dec. 12 2025
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.ui.tab;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.openautonomousconnection.oacswing.component.OACButton;
|
|
||||||
import org.openautonomousconnection.webclient.network.website.tab.WebTab;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button that contains Tab data
|
|
||||||
*/
|
|
||||||
@Getter @Setter
|
|
||||||
public class TabButton extends OACButton {
|
|
||||||
private WebTab webTab;
|
|
||||||
|
|
||||||
public TabButton(@NonNull String infoName) throws Exception {
|
|
||||||
this(URI.create(infoName).toURL());
|
|
||||||
}
|
|
||||||
|
|
||||||
public TabButton(@NonNull URL infoName) throws Exception {
|
|
||||||
this.webTab = new WebTab(infoName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TabButton(@NonNull WebTab webTab) {
|
|
||||||
this.webTab = webTab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/* Author: Maple
|
|
||||||
* Dec. 12 2025
|
|
||||||
* */
|
|
||||||
|
|
||||||
package org.openautonomousconnection.webclient.ui.tab;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.openautonomousconnection.oacswing.component.OACTitleBar;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* View at the top of the screen that contains the tabs
|
|
||||||
* @deprecated the OAC-Swing library's solution fits better and works globally
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true, since = "1.0.0-BETA.1.4")
|
|
||||||
public class TabButtonView extends OACTitleBar {
|
|
||||||
@Getter
|
|
||||||
private final List<TabButton> tabButtons;
|
|
||||||
|
|
||||||
public TabButtonView() {
|
|
||||||
this(new ArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor with preset buttons
|
|
||||||
* @param tabButtons already created buttons
|
|
||||||
*/
|
|
||||||
public TabButtonView(Collection<TabButton> tabButtons) {
|
|
||||||
super(null);
|
|
||||||
this.tabButtons = (List<TabButton>) tabButtons;
|
|
||||||
|
|
||||||
FlowLayout layoutStyle = new FlowLayout(FlowLayout.RIGHT, 0, 0);
|
|
||||||
|
|
||||||
this.setLayout(layoutStyle);
|
|
||||||
|
|
||||||
this.setBackground(Color.gray);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addButton(TabButton tabButton) {
|
|
||||||
this.tabButtons.add(tabButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TabButton getButton(int index) {
|
|
||||||
return this.tabButtons.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user