|
@@ -19,14 +19,68 @@ enum RawInputModifiers {
|
|
|
PenBarrelButton = 2048
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+* This is a hack to handle Mozilla clipboard events in a more convinient way for framework users.
|
|
|
+* In the browser, events go in order KeyDown -> Paste -> KeyUp.
|
|
|
+* On KeyDown we trigger Avalonia handlers, which might execute readClipboardText.
|
|
|
+* When readClipboardText was executed, we mark ClipboardState as Pending and setup clipboard promise,
|
|
|
+* which will un-handle KeyDown event, basically allowing browser to pass a Paste event properly.
|
|
|
+* On actual Paste event we execute promise callbacks, resuming async operation, and returning pasted text to the app.
|
|
|
+* Note #1, on every KeyUp event we will reset all the state and reject pending promises if any, as this event it expected to come after Paste.
|
|
|
+* Note #2, whole this code will be executed only on legacy browsers like Mozilla, where clipboard.readText is not available.
|
|
|
+* Note #3, with all of these hacks Clipboard.ReadText will still work only on actual "paste" gesture initiated by user.
|
|
|
+* */
|
|
|
+enum ClipboardState {
|
|
|
+ None,
|
|
|
+ Ready,
|
|
|
+ Pending
|
|
|
+}
|
|
|
+
|
|
|
export class InputHelper {
|
|
|
+ static clipboardState: ClipboardState = ClipboardState.None;
|
|
|
+ static resolveClipboard?: any;
|
|
|
+ static rejectClipboard?: any;
|
|
|
+
|
|
|
+ public static initializeBackgroundHandlers() {
|
|
|
+ if (this.clipboardState !== ClipboardState.None) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ globalThis.addEventListener("paste", (args: any) => {
|
|
|
+ if (this.clipboardState === ClipboardState.Pending) {
|
|
|
+ this.resolveClipboard(args.clipboardData.getData("text"));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.clipboardState = ClipboardState.Ready;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static async readClipboardText(): Promise<string> {
|
|
|
+ if (globalThis.navigator.clipboard.readText) {
|
|
|
+ return await globalThis.navigator.clipboard.readText();
|
|
|
+ } else {
|
|
|
+ try {
|
|
|
+ return await new Promise<any>((resolve, reject) => {
|
|
|
+ this.clipboardState = ClipboardState.Pending;
|
|
|
+ this.resolveClipboard = resolve;
|
|
|
+ this.rejectClipboard = reject;
|
|
|
+ });
|
|
|
+ } finally {
|
|
|
+ this.clipboardState = ClipboardState.Ready;
|
|
|
+ this.resolveClipboard = null;
|
|
|
+ this.rejectClipboard = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
public static subscribeKeyEvents(
|
|
|
element: HTMLInputElement,
|
|
|
keyDownCallback: (code: string, key: string, modifiers: RawInputModifiers) => boolean,
|
|
|
keyUpCallback: (code: string, key: string, modifiers: RawInputModifiers) => boolean) {
|
|
|
const keyDownHandler = (args: KeyboardEvent) => {
|
|
|
if (keyDownCallback(args.code, args.key, this.getModifiers(args))) {
|
|
|
- args.preventDefault();
|
|
|
+ if (this.clipboardState !== ClipboardState.Pending) {
|
|
|
+ args.preventDefault();
|
|
|
+ }
|
|
|
}
|
|
|
};
|
|
|
element.addEventListener("keydown", keyDownHandler);
|
|
@@ -35,6 +89,9 @@ export class InputHelper {
|
|
|
if (keyUpCallback(args.code, args.key, this.getModifiers(args))) {
|
|
|
args.preventDefault();
|
|
|
}
|
|
|
+ if (this.rejectClipboard) {
|
|
|
+ this.rejectClipboard();
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
element.addEventListener("keyup", keyUpHandler);
|