mirror of https://github.com/badvision/jace.git
269 lines
9.3 KiB
Java
269 lines
9.3 KiB
Java
/*
|
|
* To change this license header, choose License Headers in Project Properties.
|
|
* To change this template file, choose Tools | Templates
|
|
* and open the template in the editor.
|
|
*/
|
|
package jace;
|
|
|
|
import com.sun.glass.ui.Application;
|
|
import jace.cheat.MetaCheat;
|
|
import jace.core.Card;
|
|
import jace.core.Computer;
|
|
import jace.core.Keyboard;
|
|
import jace.library.MediaCache;
|
|
import jace.library.MediaConsumer;
|
|
import jace.library.MediaConsumerParent;
|
|
import jace.library.MediaEntry;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.net.URI;
|
|
import java.net.URL;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
import java.util.concurrent.ScheduledFuture;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
import javafx.event.EventHandler;
|
|
import javafx.fxml.FXML;
|
|
import javafx.geometry.Insets;
|
|
import javafx.scene.control.Label;
|
|
import javafx.scene.effect.DropShadow;
|
|
import javafx.scene.image.ImageView;
|
|
import javafx.scene.input.DragEvent;
|
|
import javafx.scene.input.KeyEvent;
|
|
import javafx.scene.input.MouseEvent;
|
|
import javafx.scene.input.TransferMode;
|
|
import javafx.scene.layout.AnchorPane;
|
|
import javafx.scene.layout.Background;
|
|
import javafx.scene.layout.BackgroundFill;
|
|
import javafx.scene.layout.CornerRadii;
|
|
import javafx.scene.layout.HBox;
|
|
import javafx.scene.layout.StackPane;
|
|
import javafx.scene.paint.Color;
|
|
import javafx.stage.Stage;
|
|
|
|
/**
|
|
*
|
|
* @author blurry
|
|
*/
|
|
public class JaceUIController {
|
|
|
|
@FXML
|
|
private URL location;
|
|
|
|
@FXML
|
|
private AnchorPane rootPane;
|
|
|
|
@FXML
|
|
private StackPane stackPane;
|
|
|
|
@FXML
|
|
private HBox notificationBox;
|
|
|
|
@FXML
|
|
private ImageView appleScreen;
|
|
|
|
Computer computer;
|
|
|
|
@FXML
|
|
void initialize() {
|
|
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
|
assert stackPane != null : "fx:id=\"stackPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
|
assert notificationBox != null : "fx:id=\"notificationBox\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
|
assert appleScreen != null : "fx:id=\"appleScreen\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
|
appleScreen.fitWidthProperty().bind(rootPane.widthProperty());
|
|
appleScreen.fitHeightProperty().bind(rootPane.heightProperty());
|
|
rootPane.setOnDragEntered(this::processDragEnteredEvent);
|
|
rootPane.setOnDragExited(this::processDragExitedEvent);
|
|
}
|
|
|
|
public void connectComputer(Computer computer, Stage primaryStage) {
|
|
this.computer = computer;
|
|
appleScreen.setImage(computer.getVideo().getFrameBuffer());
|
|
EventHandler<KeyEvent> keyboardHandler = computer.getKeyboard().getListener();
|
|
primaryStage.setOnShowing(evt -> computer.getKeyboard().resetState());
|
|
rootPane.setFocusTraversable(true);
|
|
rootPane.setOnKeyPressed(keyboardHandler);
|
|
rootPane.setOnKeyReleased(keyboardHandler);
|
|
rootPane.requestFocus();
|
|
}
|
|
|
|
private void processDragEnteredEvent(DragEvent evt) {
|
|
MediaEntry media = null;
|
|
if (evt.getDragboard().hasFiles()) {
|
|
media = MediaCache.getMediaFromFile(getDraggedFile(evt.getDragboard().getFiles()));
|
|
} else if (evt.getDragboard().hasUrl()) {
|
|
media = MediaCache.getMediaFromUrl(evt.getDragboard().getUrl());
|
|
} else if (evt.getDragboard().hasString()) {
|
|
String path = evt.getDragboard().getString();
|
|
try {
|
|
URI.create(path);
|
|
media = MediaCache.getMediaFromUrl(path);
|
|
} catch (IllegalArgumentException ex) {
|
|
File f = new File(path);
|
|
if (f.exists()) {
|
|
media = MediaCache.getMediaFromFile(f);
|
|
}
|
|
}
|
|
}
|
|
if (media != null) {
|
|
evt.acceptTransferModes(TransferMode.LINK, TransferMode.COPY);
|
|
startDragEvent(media);
|
|
}
|
|
}
|
|
|
|
private void processDragExitedEvent(DragEvent evt) {
|
|
endDragEvent();
|
|
}
|
|
|
|
private File getDraggedFile(List<File> files) {
|
|
if (files == null || files.isEmpty()) {
|
|
return null;
|
|
}
|
|
for (File f : files) {
|
|
if (f.exists()) {
|
|
return f;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
HBox drivePanel;
|
|
|
|
private void startDragEvent(MediaEntry media) {
|
|
List<MediaConsumer> consumers = getMediaConsumers();
|
|
drivePanel = new HBox();
|
|
consumers.stream()
|
|
.filter((consumer) -> (consumer.isAccepted(media, media.files.get(0))))
|
|
.forEach((consumer) -> {
|
|
Label icon = consumer.getIcon().orElse(null);
|
|
if (icon == null) {
|
|
return;
|
|
}
|
|
icon.setTextFill(Color.WHITE);
|
|
icon.setPadding(new Insets(2.0));
|
|
drivePanel.getChildren().add(icon);
|
|
icon.setOnDragOver(event -> {
|
|
event.acceptTransferModes(TransferMode.ANY);
|
|
event.consume();
|
|
});
|
|
icon.setOnDragDropped(event -> {
|
|
System.out.println("Dropping media on " + icon.getText());
|
|
try {
|
|
computer.pause();
|
|
consumer.insertMedia(media, media.files.get(0));
|
|
computer.resume();
|
|
event.setDropCompleted(true);
|
|
event.consume();
|
|
} catch (IOException ex) {
|
|
Logger.getLogger(JaceUIController.class.getName()).log(Level.SEVERE, null, ex);
|
|
}
|
|
endDragEvent();
|
|
});
|
|
});
|
|
stackPane.getChildren().add(drivePanel);
|
|
drivePanel.setLayoutX(10);
|
|
drivePanel.setLayoutY(10);
|
|
}
|
|
|
|
private void endDragEvent() {
|
|
stackPane.getChildren().remove(drivePanel);
|
|
drivePanel.getChildren().stream().forEach((n) -> {
|
|
n.setOnDragDropped(null);
|
|
});
|
|
}
|
|
|
|
private List<MediaConsumer> getMediaConsumers() {
|
|
List<MediaConsumer> consumers = new ArrayList<>();
|
|
for (Optional<Card> card : computer.memory.getAllCards()) {
|
|
card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent -> {
|
|
consumers.addAll(Arrays.asList(((MediaConsumerParent) parent).getConsumers()));
|
|
});
|
|
}
|
|
return consumers;
|
|
}
|
|
|
|
Map<Label, Long> iconTTL = new ConcurrentHashMap<>();
|
|
|
|
void addIndicator(Label icon) {
|
|
addIndicator(icon, 250);
|
|
}
|
|
|
|
void addIndicator(Label icon, long TTL) {
|
|
if (!iconTTL.containsKey(icon)) {
|
|
Application.invokeLater(() -> {
|
|
if (!notificationBox.getChildren().contains(icon)) {
|
|
notificationBox.getChildren().add(icon);
|
|
}
|
|
});
|
|
}
|
|
trackTTL(icon, TTL);
|
|
}
|
|
|
|
void removeIndicator(Label icon) {
|
|
Application.invokeLater(() -> {
|
|
notificationBox.getChildren().remove(icon);
|
|
iconTTL.remove(icon);
|
|
});
|
|
}
|
|
|
|
ScheduledExecutorService notificationExecutor = Executors.newSingleThreadScheduledExecutor();
|
|
ScheduledFuture ttlCleanupTask = null;
|
|
|
|
private void trackTTL(Label icon, long TTL) {
|
|
iconTTL.put(icon, System.currentTimeMillis() + TTL);
|
|
|
|
if (ttlCleanupTask == null || ttlCleanupTask.isCancelled()) {
|
|
ttlCleanupTask = notificationExecutor.scheduleWithFixedDelay(this::processTTL, 1, 100, TimeUnit.MILLISECONDS);
|
|
}
|
|
}
|
|
|
|
private void processTTL() {
|
|
Long now = System.currentTimeMillis();
|
|
iconTTL.keySet().stream()
|
|
.filter((icon) -> (iconTTL.get(icon) <= now))
|
|
.forEach((icon) -> {
|
|
removeIndicator(icon);
|
|
});
|
|
if (iconTTL.isEmpty()) {
|
|
ttlCleanupTask.cancel(true);
|
|
ttlCleanupTask = null;
|
|
}
|
|
}
|
|
|
|
public void addMouseListener(EventHandler<MouseEvent> handler) {
|
|
appleScreen.addEventHandler(MouseEvent.ANY, handler);
|
|
}
|
|
|
|
public void removeMouseListener(EventHandler<MouseEvent> handler) {
|
|
appleScreen.removeEventHandler(MouseEvent.ANY, handler);
|
|
}
|
|
|
|
Label currentNotification = null;
|
|
public void displayNotification(String message) {
|
|
Label oldNotification = currentNotification;
|
|
Label notification = new Label(message);
|
|
currentNotification = notification;
|
|
notification.setEffect(new DropShadow(2.0, Color.BLACK));
|
|
notification.setTextFill(Color.WHITE);
|
|
notification.setBackground(new Background(new BackgroundFill(Color.rgb(0,0,80, 0.7), new CornerRadii(5.0), new Insets(-5.0))));
|
|
Application.invokeLater(() -> {
|
|
stackPane.getChildren().remove(oldNotification);
|
|
stackPane.getChildren().add(notification);
|
|
});
|
|
|
|
notificationExecutor.schedule(()->{
|
|
Application.invokeLater(() -> {
|
|
stackPane.getChildren().remove(notification);
|
|
});
|
|
}, 4, TimeUnit.SECONDS);
|
|
}
|
|
}
|