Skip to content

Commit

Permalink
Some Fixes related to copy/paste functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
kia committed Feb 8, 2023
1 parent 768a2ac commit 223cee4
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 22 deletions.
17 changes: 12 additions & 5 deletions src/main/java/eu/mihosoft/monacofx/ClipboardBridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
* Bridge between javascript code and java to add and use system clipboard functionality.
Expand All @@ -54,7 +55,7 @@ public void copy(JSObject jsSelection) {
int endLineNumber = getNumber(jsSelection, "endLineNumber") - 1;
int endColumn = getNumber(jsSelection, "endColumn") - 1;
String originText = document.getText();
String[] lines = originText.split("\n");
String[] lines = originText.split("\n", -1);
StringBuilder copyText = new StringBuilder();
if (startLineNumber == endLineNumber) {
copyText = new StringBuilder(lines[startLineNumber].substring(startColumn, endColumn));
Expand Down Expand Up @@ -112,14 +113,20 @@ private String addPasteString(JSObject jsSelection, String pasteString, String o
private void calcNewCursorPosition(JSObject position, String string) {
int lineNumber = getNumber(position, "lineNumber");
int column = getNumber(position, "column");
long count = string.lines().count() - 1;
long count = string.split("\n", -1).length - 1;
position.setMember("lineNumber", lineNumber + count);
position.setMember("column", column + string.lines().skip(count).findFirst().get().length());

Optional<String> lastLine = string.lines().skip(count).findFirst();
if (lastLine.isPresent()) {
position.setMember("column", column + lastLine.get().length());
} else {
position.setMember("column", column + string.length());
}
}

private int getNumber(JSObject selection, String startLineNumber) {
private int getNumber(JSObject selection, String numberName) {
int number = 0;
Object selectionMember = selection.getMember(startLineNumber);
Object selectionMember = selection.getMember(numberName);
if (selectionMember instanceof Integer) {
number = (Integer) selectionMember;
} else if (selectionMember instanceof Double) {
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/eu/mihosoft/monacofx/Document.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ public String getLanguage() {
* @param text the text in editor is replaced byt this text
*/
public void updateText(String text) {
window.call("updateText", text);
try {
window.call("updateText", text);
} catch (JSException jsException) {
// in some cases the window object gets lost. why? this is a workaround
window = (JSObject) engine.executeScript("window");
window.call("updateText", text);
}
}
}
44 changes: 33 additions & 11 deletions src/main/java/eu/mihosoft/monacofx/MonacoFX.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,23 @@
*/
package eu.mihosoft.monacofx;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.concurrent.Worker;
import javafx.event.ActionEvent;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Region;
import javafx.scene.robot.Robot;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.util.Callback;
import javafx.util.Duration;
import netscape.javascript.JSException;
import netscape.javascript.JSObject;

Expand All @@ -59,6 +63,9 @@ public class MonacoFX extends Region {
private final SystemClipboardWrapper systemClipboardWrapper;
private boolean readOnly;

private Worker.State workerState;
private Timeline oneSecondWonder;

public MonacoFX() {
view = new WebView();

Expand All @@ -71,6 +78,7 @@ public MonacoFX() {
systemClipboardWrapper = new SystemClipboardWrapper();
ClipboardBridge clipboardBridge = new ClipboardBridge(getEditor().getDocument(), systemClipboardWrapper);
engine.getLoadWorker().stateProperty().addListener((o, old, state) -> {
workerState = state;
if (state == Worker.State.SUCCEEDED) {
AtomicBoolean jsDone = new AtomicBoolean(false);
AtomicInteger attempts = new AtomicInteger();
Expand Down Expand Up @@ -105,16 +113,30 @@ public MonacoFX() {
}
});
engine.load(url);
waitForLoad();
addClipboardFunctions();
}

/**
* wait a bit
*/
private void waitForLoad() {
oneSecondWonder = new Timeline(new KeyFrame(Duration.seconds(1), (ActionEvent event) -> {
if ( Worker.State.SUCCEEDED == workerState) {
oneSecondWonder.stop();
}
}));
oneSecondWonder.setCycleCount(15);
oneSecondWonder.play();
}

public void reload() {
engine.reload();
setReadonly(isReadOnly());
}

private void addClipboardFunctions() {
addEventFilter(KeyEvent.KEY_PRESSED, event -> systemClipboardWrapper.handleCopyCutKeyEvent(event, (a) -> getSelectionObject()));
addEventFilter(KeyEvent.KEY_PRESSED, event -> systemClipboardWrapper.handleCopyCutKeyEvent(event, (a) -> getSelectionObject(), readOnly));
}

private Object getSelectionObject() {
Expand All @@ -130,11 +152,10 @@ private static void pressArrowKey(Robot r, KeyCode keyCode, int count) {

@Override
public void requestFocus() {
getWebEngine().getLoadWorker().stateProperty().addListener((o, old, state) -> {
if (state == Worker.State.SUCCEEDED) {
super.requestFocus();
getWebEngine().executeScript("setTimeout(() => { editorView.focus();}, 1200);");
}
executeJavaScriptLambda(null, param -> {
super.requestFocus();
getWebEngine().executeScript("setTimeout(() => { editorView.focus();}, 1200);");
return null;
});
}

Expand Down Expand Up @@ -248,22 +269,22 @@ private void executeJavaScriptLambda(Object parameter , Callback<Object, Void> c
if (Worker.State.SUCCEEDED == stateProperty.getValue()) {
callback.call(parameter);
} else {
getWebEngine().getLoadWorker().stateProperty().addListener((o, old, state) -> {
ChangeListener<Worker.State> stateChangeListener = (o, old, state) -> {
if (Worker.State.SUCCEEDED == state) {
JSObject window = (JSObject) engine.executeScript("window");
AtomicBoolean jsDone = new AtomicBoolean(false);
AtomicInteger attempts = new AtomicInteger();
Thread thread = new Thread(() -> {
while (!jsDone.get()) {
// check if JS execution is done.
Platform.runLater(() -> {
JSObject window = (JSObject) engine.executeScript("window");
Object jsEditorObj = window.call("getEditorView");
if (jsEditorObj instanceof JSObject) {
callback.call(parameter);
jsDone.set(true);
}
});
if(attempts.getAndIncrement()> 10) {
if (attempts.getAndIncrement() > 10) {
throw new RuntimeException(
"Cannot initialize editor (JS execution not complete). Max number of attempts reached."
);
Expand All @@ -279,7 +300,8 @@ private void executeJavaScriptLambda(Object parameter , Callback<Object, Void> c
});
thread.start();
}
});
};
stateProperty.addListener(stateChangeListener);
}
}
}
18 changes: 13 additions & 5 deletions src/main/java/eu/mihosoft/monacofx/SystemClipboardWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class SystemClipboardWrapper {
private final KeyCodeCombination KEY_CODE_CTRL_C = new KeyCodeCombination(KeyCode.C, KeyCombination.SHORTCUT_DOWN);
private final KeyCodeCombination KEY_CODE_CTRL_X = new KeyCodeCombination(KeyCode.X, KeyCombination.SHORTCUT_DOWN);
private final KeyCodeCombination KEY_CODE_CTRL_INSERT = new KeyCodeCombination(KeyCode.INSERT, KeyCombination.SHORTCUT_DOWN);
private final KeyCodeCombination KEY_CODE_CTRL_DELETE = new KeyCodeCombination(KeyCode.DELETE, KeyCombination.SHORTCUT_DOWN);

/**
* Puts the text into clipboard.
Expand All @@ -56,17 +57,24 @@ public void putString(String text) {
/**
* When ever KeyEvent.KEY_PRESSED with 'Ctrl x' or 'Ctrl c' happens the passed string obj is copied in
* to the clipboard
* @param event key event
*
* @param event key event
* @param getSelectionObjectCallBack
* @param readOnly
*/
public void handleCopyCutKeyEvent(KeyEvent event, Callback<Void, Object> getSelectionObjectCallBack) {
public void handleCopyCutKeyEvent(KeyEvent event, Callback<Void, Object> getSelectionObjectCallBack, boolean readOnly) {
if (event.getEventType().getName().equals("KEY_PRESSED")
&& KEY_CODE_CTRL_X.match(event)
|| (KEY_CODE_CTRL_C.match(event))
|| (KEY_CODE_CTRL_INSERT.match(event))) {
|| KEY_CODE_CTRL_C.match(event)
|| KEY_CODE_CTRL_DELETE.match(event)
|| KEY_CODE_CTRL_INSERT.match(event)) {
Object obj = getSelectionObjectCallBack.call(null);
String selectedText = String.valueOf(obj);
if (selectedText.isEmpty()) {
if ((readOnly && KEY_CODE_CTRL_X.match(event))
|| (readOnly && KEY_CODE_CTRL_INSERT.match(event))
|| (readOnly && KEY_CODE_CTRL_DELETE.match(event))
|| selectedText.isEmpty()
) {
event.consume();
} else {
Platform.runLater(() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@
}
]);

const model = editorView.getModel();
model.setEOL(monaco.editor.EndOfLineSequence.LF);

editorView.addAction({
id: 'editor.action.clipboardCopyAction',
Expand Down

0 comments on commit 223cee4

Please sign in to comment.