From 3107cb5368679fec07d716a9e1ed4641bfc6efc2 Mon Sep 17 00:00:00 2001 From: Jonatan Heyman Date: Thu, 9 Jan 2025 17:03:51 +0100 Subject: [PATCH] Add right click context menu and improve application menu - Add right-click context menu with undo/redo/cut/copy/paste/select all options, as well as Delete block and Move block to another buffer - Add Delete Block and Move Block to another buffer in the application menu - Change so that we use our custom "undo" event instead of relying on the default undo event, since there were cases when the default undo didn't trigger the CodeMirror undo reliably --- docs/changelog.md | 2 + electron/main/index.ts | 6 ++- electron/main/menu.js | 79 +++++++++++++++++++++++++++++++-------- electron/preload/index.js | 4 ++ src/common/constants.js | 3 ++ src/components/App.vue | 6 ++- src/components/Editor.vue | 32 ++++++++++++---- src/editor/editor.js | 6 ++- webapp/bridge.js | 4 ++ 9 files changed, 115 insertions(+), 27 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4402d20..e3cfb77 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,8 @@ Here are the most notable changes in each release. For a more detailed list of c ## 2.1.0 (not yet released) - Added support for moving the current block to another (or new) buffer. Pressing `Ctrl/Cmd+S` will now pop up a dialog where you can search for and select another buffer to which the block will be moved. It's also possible to select to create a brand new buffer to which the block will be moved. +- Add right click context menu with undo/redo/cut/copy/paste/select all as well as Delete Block and Move block to another buffer. +- Add File menu item for switching buffer - When deleting a block, the cursor will now end up at the beginning of the next block, instead of at the end of the previous block. - Added support for the following languages: * Elixir diff --git a/electron/main/index.ts b/electron/main/index.ts index a2fefbc..32effaa 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -5,7 +5,7 @@ import fs from "fs" import { WINDOW_CLOSE_EVENT, SETTINGS_CHANGE_EVENT } from '@/src/common/constants' -import { menu, getTrayMenu } from './menu' +import { menu, getTrayMenu, getEditorContextMenu } from './menu' import CONFIG from "../config" import { isDev, isLinux, isMac, isWindows } from '../detect-platform'; import { initializeAutoUpdate, checkForUpdates } from './auto-update'; @@ -376,6 +376,10 @@ ipcMain.handle("setWindowTitle", (event, title) => { win?.setTitle(title) }) +ipcMain.handle("showEditorContextMenu", () => { + getEditorContextMenu(win).popup({window:win}); +}) + // Initialize note/file library async function initFileLibrary(win) { await migrateBufferFileToLibrary(app) diff --git a/electron/main/menu.js b/electron/main/menu.js index 2e8a54b..353c2fe 100644 --- a/electron/main/menu.js +++ b/electron/main/menu.js @@ -1,10 +1,51 @@ const { app, Menu } = require("electron") -import { OPEN_SETTINGS_EVENT, REDO_EVENT, MOVE_BLOCK_EVENT } from '@/src/common/constants' +import { OPEN_SETTINGS_EVENT, UNDO_EVENT, REDO_EVENT, MOVE_BLOCK_EVENT, DELETE_BLOCK_EVENT, CHANGE_BUFFER_EVENT } from '@/src/common/constants' import { openAboutWindow } from "./about"; import { quit } from "./index" const isMac = process.platform === "darwin" + +const undoMenuItem = { + label: 'Undo', + accelerator: 'CommandOrControl+z', + click: (menuItem, window, event) => { + window?.webContents.send(UNDO_EVENT) + }, +} + +const redoMenuItem = { + label: 'Redo', + accelerator: 'CommandOrControl+Shift+z', + click: (menuItem, window, event) => { + window?.webContents.send(REDO_EVENT) + }, +} + +const deleteBlockMenuItem = { + label: 'Delete block', + accelerator: 'CommandOrControl+Shift+D', + click: (menuItem, window, event) => { + window?.webContents.send(DELETE_BLOCK_EVENT) + }, +} + +const moveBlockMenuItem = { + label: 'Move block to another buffer…', + accelerator: 'CommandOrControl+S', + click: (menuItem, window, event) => { + window?.webContents.send(MOVE_BLOCK_EVENT) + }, +} + +const changeBufferMenuItem = { + label: 'Switch buffer…', + accelerator: 'CommandOrControl+P', + click: (menuItem, window, event) => { + window?.webContents.send(CHANGE_BUFFER_EVENT) + }, +} + const template = [ // { role: 'appMenu' } ...(isMac ? [{ @@ -18,6 +59,7 @@ const template = [ }, }, { type: 'separator' }, + changeBufferMenuItem, { label: 'Settings', click: (menuItem, window, event) => { @@ -37,6 +79,7 @@ const template = [ }] : [{ role: 'fileMenu', submenu: [ + changeBufferMenuItem, { label: 'Settings', click: (menuItem, window, event) => { @@ -62,22 +105,11 @@ const template = [ { label: 'Edit', submenu: [ - { role: 'undo' }, - { - label: 'Redo', - accelerator: 'CommandOrControl+Shift+z', - click: (menuItem, window, event) => { - window?.webContents.send(REDO_EVENT) - }, - }, + undoMenuItem, + redoMenuItem, { type: 'separator' }, - { - label: 'Move block to another buffer...', - accelerator: 'CommandOrControl+S', - click: (menuItem, window, event) => { - window?.webContents.send(MOVE_BLOCK_EVENT) - }, - }, + deleteBlockMenuItem, + moveBlockMenuItem, { type: 'separator' }, { role: 'cut' }, { role: 'copy' }, @@ -185,3 +217,18 @@ export function getTrayMenu(win) { ]) } +export function getEditorContextMenu(win) { + return Menu.buildFromTemplate([ + undoMenuItem, + redoMenuItem, + {type: 'separator'}, + {role: 'cut'}, + {role: 'copy'}, + {role: 'paste'}, + {type: 'separator'}, + {role: 'selectAll'}, + {type: 'separator'}, + deleteBlockMenuItem, + moveBlockMenuItem, + ]) +} diff --git a/electron/preload/index.js b/electron/preload/index.js index 31e2a1e..ce2aae8 100644 --- a/electron/preload/index.js +++ b/electron/preload/index.js @@ -48,6 +48,10 @@ contextBridge.exposeInMainWorld("heynote", { off(event, callback) { ipcRenderer.off(event, callback) }, + + invoke(event, ...args) { + return ipcRenderer.invoke(event, ...args) + } }, buffer: { diff --git a/src/common/constants.js b/src/common/constants.js index 7d8e3f9..90d9ac0 100644 --- a/src/common/constants.js +++ b/src/common/constants.js @@ -5,7 +5,10 @@ export const WINDOW_CLOSE_EVENT = "window-close" export const OPEN_SETTINGS_EVENT = "open-settings" export const SETTINGS_CHANGE_EVENT = "settings-change" export const REDO_EVENT = "redo" +export const UNDO_EVENT = "undo" export const MOVE_BLOCK_EVENT = "move-block" +export const DELETE_BLOCK_EVENT = "delete-block" +export const CHANGE_BUFFER_EVENT = "change-buffer" export const UPDATE_AVAILABLE_EVENT = "update-available" export const UPDATE_NOT_AVAILABLE_EVENT = "update-not-available" diff --git a/src/components/App.vue b/src/components/App.vue index 8bb2437..fa20814 100644 --- a/src/components/App.vue +++ b/src/components/App.vue @@ -7,7 +7,7 @@ import { useSettingsStore } from "../stores/settings-store" import { useEditorCacheStore } from '../stores/editor-cache' - import { OPEN_SETTINGS_EVENT, MOVE_BLOCK_EVENT } from '@/src/common/constants' + import { OPEN_SETTINGS_EVENT, MOVE_BLOCK_EVENT, CHANGE_BUFFER_EVENT } from '@/src/common/constants' import StatusBar from './StatusBar.vue' import Editor from './Editor.vue' @@ -48,6 +48,10 @@ window.heynote.mainProcess.on(MOVE_BLOCK_EVENT, (path) => { this.openMoveToBufferSelector() }) + + window.heynote.mainProcess.on(CHANGE_BUFFER_EVENT, () => { + this.openBufferSelector() + }) }, beforeUnmount() { diff --git a/src/components/Editor.vue b/src/components/Editor.vue index d661a0d..9dfde36 100644 --- a/src/components/Editor.vue +++ b/src/components/Editor.vue @@ -5,7 +5,7 @@ import { useErrorStore } from "../stores/error-store" import { useHeynoteStore } from "../stores/heynote-store.js" import { useEditorCacheStore } from "../stores/editor-cache" - import { REDO_EVENT, WINDOW_CLOSE_EVENT } from '@/src/common/constants'; + import { REDO_EVENT, WINDOW_CLOSE_EVENT, DELETE_BLOCK_EVENT, UNDO_EVENT } from '@/src/common/constants'; const NUM_EDITOR_INSTANCES = 5 @@ -22,7 +22,6 @@ syntaxTreeDebugContent: null, editor: null, onWindowClose: null, - onRedo: null, } }, @@ -38,15 +37,25 @@ [this.editor.path, this.editor.getContent()], ]) } + window.heynote.mainProcess.on(WINDOW_CLOSE_EVENT, this.onWindowClose) - this.onRedo = () => { + window.heynote.mainProcess.on(UNDO_EVENT, () => { + if (this.editor) { + toRaw(this.editor).undo() + } + }) + + window.heynote.mainProcess.on(REDO_EVENT, () => { if (this.editor) { toRaw(this.editor).redo() } - } + }) - window.heynote.mainProcess.on(WINDOW_CLOSE_EVENT, this.onWindowClose) - window.heynote.mainProcess.on(REDO_EVENT, this.onRedo) + window.heynote.mainProcess.on(DELETE_BLOCK_EVENT, () => { + if (this.editor) { + toRaw(this.editor).deleteActiveBlock() + } + }) // if debugSyntaxTree prop is set, display syntax tree for debugging if (this.debugSyntaxTree) { @@ -70,7 +79,9 @@ beforeUnmount() { window.heynote.mainProcess.off(WINDOW_CLOSE_EVENT, this.onWindowClose) - window.heynote.mainProcess.off(REDO_EVENT, this.onRedo) + window.heynote.mainProcess.off(UNDO_EVENT) + window.heynote.mainProcess.off(REDO_EVENT) + window.heynote.mainProcess.off(DELETE_BLOCK_EVENT) this.editorCacheStore.tearDown(); }, @@ -138,13 +149,18 @@ focus() { toRaw(this.editor).focus() }, + + onContextMenu(event) { + event.preventDefault() + window.heynote.mainProcess.invoke("showEditorContextMenu") + }, }, }