Skip to content

Commit 9f2bfe5

Browse files
authored
write clipboard on ^C and ^X (#42)
1 parent 3bf3501 commit 9f2bfe5

File tree

4 files changed

+75
-1
lines changed

4 files changed

+75
-1
lines changed

page/keycode.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { getInputElement } from './focus'
22
import Module from './module'
33

4+
const isApple = navigator.userAgent.includes('Mac OS X')
5+
46
function extract(event: KeyboardEvent): [string, string, number] | undefined {
57
const { key, code, shiftKey, altKey, ctrlKey, metaKey } = event
68
// Host IME
@@ -17,14 +19,22 @@ export function processKey(key: string, code: string, modifiers: number, isRelea
1719
}
1820

1921
export function keyEvent(event: KeyboardEvent) {
20-
if (!getInputElement()) {
22+
const input = getInputElement()
23+
if (!input) {
2124
return
2225
}
2326
const extracted = extract(event)
2427
if (!extracted) {
2528
return
2629
}
2730
const isRelease = event.type === 'keyup'
31+
// Write clipboard for Ctrl/Cmd + C/X
32+
if (!isRelease && ['c', 'x'].includes(extracted[0]) && ((isApple && extracted[2] === (1 << 6)) || (!isApple && extracted[2] === (1 << 2)))) {
33+
const selectedText = input.value.substring(input.selectionStart ?? 0, input.selectionEnd ?? 0)
34+
if (selectedText) {
35+
Module.ccall('write_clipboard', 'void', ['string'], [selectedText])
36+
}
37+
}
2838
if (processKey(...extracted, isRelease)) {
2939
event.preventDefault()
3040
}

src/fcitx.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "../fcitx5/src/modules/clipboard/clipboard_public.h"
12
#include "../wasmfrontend/wasmfrontend.h"
23
#include "../webkeyboard/webkeyboard.h"
34
#include "event_js.h"
@@ -18,6 +19,7 @@ namespace fcitx {
1819
std::unique_ptr<Instance> instance;
1920
WasmFrontend *frontend;
2021
WebKeyboard *ui;
22+
AddonInstance *clipboard;
2123

2224
void notify_main_async(const std::string &str);
2325

@@ -142,6 +144,10 @@ EMSCRIPTEN_KEEPALIVE void scroll(int start, int count) {
142144
ui->scroll(start, count);
143145
}
144146

147+
EMSCRIPTEN_KEEPALIVE void write_clipboard(const char *text) {
148+
clipboard->call<IClipboard::setClipboardV2>("", text, false);
149+
}
150+
145151
EMSCRIPTEN_KEEPALIVE void init(const char *locale, bool worker, bool touch) {
146152
if (instance) {
147153
return;
@@ -175,6 +181,7 @@ EMSCRIPTEN_KEEPALIVE void init(const char *locale, bool worker, bool touch) {
175181
instance->initialize(); // Unnecessary to call exec.
176182
frontend = dynamic_cast<WasmFrontend *>(addonMgr.addon("wasmfrontend"));
177183
ui = dynamic_cast<WebKeyboard *>(addonMgr.addon("webkeyboard"));
184+
clipboard = addonMgr.addon("clipboard", true);
178185
}
179186

180187
EMSCRIPTEN_KEEPALIVE void reload(const char *locale, bool touch) {

tests/test-clipboard.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { Page } from '@playwright/test'
2+
import { expect, test } from '@playwright/test'
3+
import { browserName, expectPanelHidden, init } from './util'
4+
5+
function openClipboard(page: Page) {
6+
return page.keyboard.press('Control+;')
7+
}
8+
9+
test('Clipboard', async ({ page }) => {
10+
test.skip(process.platform === 'linux' && browserName(page) === 'webkit')
11+
await init(page)
12+
const CONTROL = process.platform === 'darwin' ? 'Meta' : 'Control'
13+
14+
const textarea = page.locator('textarea')
15+
await textarea.click()
16+
await textarea.fill('abc')
17+
18+
// Cut
19+
await page.keyboard.down('Shift')
20+
await page.keyboard.press('ArrowLeft')
21+
await page.keyboard.up('Shift')
22+
await page.keyboard.down(CONTROL)
23+
await page.keyboard.press('x')
24+
await page.keyboard.up(CONTROL)
25+
26+
// Copy
27+
await page.keyboard.down('Shift')
28+
await page.keyboard.press('ArrowLeft')
29+
await page.keyboard.press('ArrowLeft')
30+
await page.keyboard.up('Shift')
31+
await page.keyboard.down(CONTROL)
32+
await page.keyboard.press('c')
33+
await page.keyboard.up(CONTROL)
34+
35+
await openClipboard(page)
36+
const text = page.locator('.fcitx-text')
37+
const auxDown = page.locator('.fcitx-aux-down')
38+
await expect(text).toHaveCount(2)
39+
await expect(text.nth(0)).toHaveText('ab')
40+
await expect(text.nth(1)).toHaveText('c')
41+
await expect(auxDown).not.toBeVisible()
42+
43+
await page.keyboard.press('2')
44+
await expect(textarea).toHaveValue('c')
45+
46+
await openClipboard(page)
47+
await page.keyboard.press('Backspace')
48+
await expectPanelHidden(page)
49+
50+
await openClipboard(page)
51+
await expect(page.locator('.fcitx-aux-down')).toHaveText('No clipboard history.')
52+
await expect(text).toHaveCount(0)
53+
})

tests/util.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ export async function init(page: Page) {
88
})
99
}
1010

11+
export function browserName(page: Page) {
12+
return page.context().browser()!.browserType().name()
13+
}
14+
1115
export async function getBox(locator: Locator) {
1216
return (await locator.boundingBox())!
1317
}

0 commit comments

Comments
 (0)