编程值更改后,菜单栏的本机撤消角色将不起作用



代码

我正在为菜单栏构建一个应用程序菜单,我正在使用角色来自动化一些预定义的功能,比如:

{
label: 'File',
submenu: []
},
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' }
]
},
{
label: 'View',
submenu: []
},
...

问题

下面的代码部分呈现菜单并像魔术一样工作:{ role: 'undo' },但仅用于正常操作。

但是,当您以编程方式替换表单控件(textbox、texarea等(的值,然后对该表单控件进行一些手动更改时,Undo功能将不再工作。

我可能错过了什么?这是虫子还是什么?以下是使用的软件版本。

环境

版本
软件
电子12.2.3
节点.js14.16.0
MacOS10.13.6

由于Electron的角色只是Chrome功能的扩展,真正的测试是看看在不使用Electron时是否会发生同样的情况。

我已经在标准html代码库和Electron代码库中测试了您的问题。

标准HTML代码库

在下面的文件中,我有两个文本字段。一个是输入字段,另一个是提供反馈,只是为了确定。

button是将一些text动态地附加到输入字段的末尾。

测试:

  1. 在输入字段中键入一些文本
  2. 尝试撤消重做功能。// Success
    • Windows:Ctrl-Z和Ctrl-Y
    • MacOS:Cmd-Z和Shift-Cmd-Z
  3. 现在单击button以附加一些文本
  4. 再次尝试撤消重做// Failure

动态添加文本后,撤消和重做不再有效。为什么?我不知道为什么,但这似乎是Chrome的标准功能。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Undo / Redo Test</title>
</head>
<body>
<div>
<label for="input">Input: </label>
<input type="text" id="input">
<input type="button" id="button" value="Append 'text'">
</div>
<div>
<label for="output">Output: </label>
<input type="text" id="output" disabled>
</div>
</body>
<script>
let input = document.getElementById('input');
let output = document.getElementById('output');
input.addEventListener('keyup', () => {
output.value = input.value;
});
document.getElementById('button').addEventListener('click', () => {
let value = input.value + ' text';
input.value = value;
output.value = value;
});
</script>
</html>

电子代码库

现在,让我们将完全相同的html代码移到Electron应用程序中,这样我们就可以使用Edit->Undo/Redo菜单。

通过键盘快捷键进行测试会产生与上述相同的结果。

让我们用菜单测试一下:

  1. 在输入字段中键入一些文本
  2. 选择edit->undoredo功能。// Succees
  3. 现在单击button以附加一些文本
  4. 尝试edit->undoredo// Failure

结果相同。动态添加文本后,撤消和重做将不再有效。标准HTML代码库的反映。

main.js(主线程(

'use strict';
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const nodePath = require("path");
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
fullscreen: false,
resizable: true,
movable: true,
minimizable: true,
maximizable: true,
enableLargerThanScreen: true,
closable: true,
focusable: true,
fullscreenable: true,
frame: true,
hasShadow: true,
backgroundColor: '#fff',
show: false,
icon: nodePath.join(__dirname, 'icon.png'),
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
worldSafeExecuteJavaScript: true,
enableRemoteModule: false,
devTools: (! electronApp.isPackaged),
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});

preload.js(主线程(

'use strict';
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);

index.html(渲染线程(

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Undo / Redo Test</title>
</head>
<body>
<div>
<label for="input">Input: </label>
<input type="text" id="input">
<input type="button" id="button" value="Append 'text'">
</div>
<div>
<label for="output">Output: </label>
<input type="text" id="output" disabled>
</div>
</body>
<script>
let input = document.getElementById('input');
let output = document.getElementById('output');
input.addEventListener('keyup', () => {
output.value = input.value;
});
document.getElementById('button').addEventListener('click', () => {
let value = input.value + ' text';
input.value = value;
output.value = value;
});
</script>
</html>

测试使用:

  • 电子:17.1.0
  • 节点:16.13.0
  • 铬:98.04.4758.102

测试日期:

  • Windows:10
  • MacOS:10.15.7

Memento&命令设计模式

您可以尝试将undoredoexecCommand之类的东西挂钩,但我认为,如果您想做得好,使其具有可扩展性和可读性,那么您应该真正按照Memento和Command设计模式来实现一些东西。

尽管需要进行一些设置工作,但一旦工作,它就可以应用于页面上的任何<form>字段,而不仅仅是<input type="text"><textarea>

但为什么要到此为止,让它也适用于页面或应用程序范围的功能(在可以应用它的地方(。例如:"删除"、"重命名"、"移动"、"发布"、"设置更新"等

相关内容

最新更新